Skip to content

Commit 1212294

Browse files
committed
feat(kotlin): add browse helpers
1 parent f29dfa7 commit 1212294

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed

clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/exception/AlgoliaRuntimeException.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,12 @@ public class AlgoliaRetryException(
5454
public class AlgoliaWaitException(
5555
message: String? = null,
5656
) : AlgoliaRuntimeException(message)
57+
58+
/**
59+
* Exception thrown when an error occurs during an iterable helper execution.
60+
*
61+
* @param message the detail message
62+
*/
63+
public class AlgoliaIterableException(
64+
message: String? = null,
65+
) : AlgoliaRuntimeException(message)

clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SearchClient.kt

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.algolia.client.extensions
22

33
import com.algolia.client.api.SearchClient
44
import com.algolia.client.exception.AlgoliaApiException
5+
import com.algolia.client.extensions.internal.*
56
import com.algolia.client.extensions.internal.DisjunctiveFaceting
67
import com.algolia.client.extensions.internal.buildRestrictionString
78
import com.algolia.client.extensions.internal.encodeKeySHA256
@@ -583,3 +584,100 @@ public suspend fun SearchClient.searchDisjunctiveFaceting(
583584
val responses = searchForHits(queries, requestOptions = requestOptions)
584585
return helper.mergeResponses(responses)
585586
}
587+
588+
/**
589+
* Helper: Returns an iterator on top of the `browse` method.
590+
*
591+
* @param indexName The index in which to perform the request.
592+
* @param params The `browse` parameters.
593+
* @param validate The function to validate the response. Default is to check if the cursor is not null.
594+
* @param aggregator The function to aggregate the response.
595+
* @param requestOptions The requestOptions to send along with the query, they will be merged with
596+
* the transporter requestOptions. (optional)
597+
*/
598+
public suspend fun SearchClient.browseObjects(
599+
indexName: String,
600+
params: BrowseParamsObject,
601+
validate: (BrowseResponse) -> Boolean = { response -> response.cursor != null },
602+
aggregator: ((BrowseResponse) -> Unit),
603+
requestOptions: RequestOptions? = null,
604+
): BrowseResponse {
605+
return createIterable(
606+
execute = { previousResponse ->
607+
browse(indexName, params.copy(cursor = previousResponse?.cursor), requestOptions)
608+
},
609+
validate = validate,
610+
aggregator = aggregator,
611+
)
612+
}
613+
614+
/**
615+
* Helper: Returns an iterator on top of the `browse` method.
616+
*
617+
* @param indexName The index in which to perform the request.
618+
* @param searchRulesParams The search rules request parameters
619+
* @param validate The function to validate the response. Default is to check if the cursor is not null.
620+
* @param requestOptions The requestOptions to send along with the query, they will be merged with
621+
* the transporter requestOptions. (optional)
622+
*/
623+
public suspend fun SearchClient.browseRules(
624+
indexName: String,
625+
searchRulesParams: SearchRulesParams,
626+
validate: ((SearchRulesResponse) -> Boolean)? = null,
627+
aggregator: (SearchRulesResponse) -> Unit,
628+
requestOptions: RequestOptions? = null,
629+
): SearchRulesResponse {
630+
return createIterable(
631+
execute = { previousResponse ->
632+
searchRules(
633+
indexName,
634+
searchRulesParams.copy(
635+
page = if (previousResponse != null) (previousResponse.page + 1) else 0,
636+
hitsPerPage = searchRulesParams.hitsPerPage ?: 1000
637+
),
638+
requestOptions
639+
)
640+
},
641+
validate = validate ?: { response -> response.page + 1 >= response.nbPages },
642+
aggregator = aggregator,
643+
)
644+
}
645+
646+
/**
647+
* Helper: Returns an iterator on top of the `browse` method.
648+
*
649+
* @param indexName The index in which to perform the request.
650+
* @param searchSynonymsParams The search synonyms request parameters
651+
* @param validate The function to validate the response. Default is to check if the cursor is not null.
652+
* @param requestOptions The requestOptions to send along with the query, they will be merged with
653+
* the transporter requestOptions. (optional)
654+
*/
655+
public suspend fun SearchClient.browseSynonyms(
656+
indexName: String,
657+
searchSynonymsParams: SearchSynonymsParams,
658+
validate: ((SearchSynonymsResponse) -> Boolean)? = null,
659+
aggregator: (SearchSynonymsResponse) -> Unit,
660+
requestOptions: RequestOptions? = null,
661+
): SearchSynonymsResponse {
662+
val hitsPerPage = 1000
663+
var page = searchSynonymsParams.page ?: 0
664+
665+
return createIterable(
666+
execute = { _ ->
667+
try {
668+
searchSynonyms(
669+
indexName,
670+
searchSynonymsParams = searchSynonymsParams.copy(
671+
page = page,
672+
hitsPerPage = hitsPerPage
673+
),
674+
requestOptions
675+
)
676+
} finally {
677+
page += 1
678+
}
679+
},
680+
validate = validate ?: { response -> response.nbHits < hitsPerPage },
681+
aggregator = aggregator,
682+
)
683+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.algolia.client.extensions.internal
2+
3+
import com.algolia.client.exception.AlgoliaIterableException
4+
import kotlinx.coroutines.delay
5+
import kotlin.time.Duration
6+
7+
public data class IterableError<T>(
8+
public val validate: (T) -> Boolean,
9+
public val message: ((T) -> String)? = null
10+
)
11+
12+
public suspend fun <T> createIterable(
13+
execute: suspend (T?) -> T,
14+
validate: (T) -> Boolean,
15+
aggregator: ((T) -> Unit)? = null,
16+
timeout: () -> Duration = { Duration.ZERO },
17+
error: IterableError<T>? = null
18+
): T {
19+
suspend fun executor(previousResponse: T? = null): T {
20+
val response = execute(previousResponse)
21+
22+
if (aggregator != null) {
23+
aggregator(response)
24+
}
25+
26+
if (validate(response)) {
27+
return response
28+
}
29+
30+
if (error != null && error.validate(response)) {
31+
val message = error.message?.invoke(response) ?: "An error occurred"
32+
throw AlgoliaIterableException(message)
33+
}
34+
35+
delay(timeout().inWholeMilliseconds)
36+
37+
return executor(response)
38+
}
39+
40+
return executor()
41+
}

0 commit comments

Comments
 (0)