From ff405534ce25391f1a3db680fe646c3da82b966d Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Tue, 27 Feb 2024 17:38:45 +0100 Subject: [PATCH 1/3] feat(cts): add helpers to the specs (#2605) --- .../Models/Common/SecuredApiKeyRestriction.cs | 38 --- .../Common/SecuredApiKeyRestrictionHelper.cs | 10 +- .../algoliasearch/Utils/ApiKeyOperation.cs | 20 -- .../algoliasearch/Utils/ClientExtensions.cs | 2 +- .../com/algolia/utils/ApiKeyOperation.java | 7 - .../client/extensions/ApiKeyOperation.kt | 10 - .../algolia/client/extensions/SearchClient.kt | 6 +- .../extensions/SecuredAPIKeyRestriction.kt | 26 -- .../extensions/internal/SearchClient.kt | 14 +- .../com/algolia/client/TestSecureApiKey.kt | 4 +- .../algoliasearch/http/helpers.py | 63 +--- .../extension/ApiKeyOperation.scala | 9 - .../algoliasearch/extension/package.scala | 2 +- .../Search/Extra/APIKeyOperation.swift | 14 - .../Sources/Search/Extra/SearchHelpers.swift | 2 +- ...> SecuredApiKeyRestrictionExtension.swift} | 42 +-- .../codegen/AlgoliaCSharpGenerator.java | 1 + .../algolia/codegen/AlgoliaDartGenerator.java | 1 + .../algolia/codegen/AlgoliaGoGenerator.java | 1 + .../algolia/codegen/AlgoliaJavaGenerator.java | 13 +- .../codegen/AlgoliaJavascriptGenerator.java | 1 + .../codegen/AlgoliaKotlinGenerator.java | 1 + .../algolia/codegen/AlgoliaPhpGenerator.java | 9 + .../codegen/AlgoliaPythonGenerator.java | 1 + .../algolia/codegen/AlgoliaRubyGenerator.java | 9 + .../codegen/AlgoliaScalaGenerator.java | 1 + .../codegen/AlgoliaSwiftGenerator.java | 1 + .../cts/lambda/DynamicTemplateLambda.java | 4 +- .../codegen/cts/tests/TestsRequest.java | 55 ++-- .../com/algolia/codegen/utils/Helpers.java | 5 + .../csharp/Playground/Playgrounds/Search.cs | 2 +- playground/python/app/search.py | 1 - scripts/buildClients.ts | 8 +- scripts/buildSpecs.ts | 19 +- scripts/common.ts | 18 +- scripts/cts/generate.ts | 6 +- scripts/generate.ts | 15 +- scripts/install.sh | 2 +- scripts/playground.ts | 3 - scripts/pre-gen/generateOpenapitools.ts | 12 +- scripts/snippets/generate.ts | 6 +- scripts/spinners.ts | 4 +- scripts/types.ts | 3 + .../search/helpers/generateSecuredApiKey.yml | 71 +++++ specs/search/helpers/waitForApiKey.yml | 40 +++ specs/search/spec.yml | 9 + templates/go/api.mustache | 259 +-------------- templates/go/search_helpers.mustache | 295 ++++++++++++++++++ templates/go/tests/requests/requests.mustache | 25 +- templates/java/api_helpers.mustache | 8 +- .../java/tests/requests/requests.mustache | 2 +- .../client/model/clientMethodProps.mustache | 32 +- .../tests/requests/helpers.mustache | 4 +- templates/python/api.mustache | 3 +- templates/python/imports.mustache | 2 +- templates/python/search_helpers.mustache | 6 +- .../python/tests/requests/helpers.mustache | 10 +- .../python/tests/requests/requests.mustache | 4 +- .../output/csharp/src/SecuredApiKeysTests.cs | 14 +- .../handwritten/SecuredAPIKeyHelpers.swift | 9 +- 60 files changed, 614 insertions(+), 650 deletions(-) delete mode 100644 clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestriction.cs delete mode 100644 clients/algoliasearch-client-csharp/algoliasearch/Utils/ApiKeyOperation.cs delete mode 100644 clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/utils/ApiKeyOperation.java delete mode 100644 clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/ApiKeyOperation.kt delete mode 100644 clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SecuredAPIKeyRestriction.kt delete mode 100644 clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/ApiKeyOperation.scala delete mode 100644 clients/algoliasearch-client-swift/Sources/Search/Extra/APIKeyOperation.swift rename clients/algoliasearch-client-swift/Sources/Search/Extra/{SecuredAPIKeyRestriction.swift => SecuredApiKeyRestrictionExtension.swift} (55%) create mode 100644 specs/search/helpers/generateSecuredApiKey.yml create mode 100644 specs/search/helpers/waitForApiKey.yml create mode 100644 templates/go/search_helpers.mustache diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestriction.cs b/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestriction.cs deleted file mode 100644 index bae4306070..0000000000 --- a/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestriction.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; -using Algolia.Search.Models.Search; - -namespace Algolia.Search.Models.Common; - -public partial class SecuredApiKeyRestriction -{ - /// - /// Search query parameters - /// - public IndexSettingsAsSearchParams Query { get; set; } - - /// - /// A Unix timestamp used to define the expiration date of the API key. - /// - [JsonPropertyName("validUntil")] - public long? ValidUntil { get; set; } - - /// - /// List of index names that can be queried. - /// - [JsonPropertyName("restrictIndices")] - public List RestrictIndices { get; set; } - - /// - /// IPv4 network allowed to use the generated key. This is used for more protection against API key leaking and reuse. - /// - [JsonPropertyName("restrictSources")] - public List RestrictSources { get; set; } - - /// - /// Specify a user identifier. This is often used with rate limits. - /// - [JsonPropertyName("userToken")] - public string UserToken { get; set; } - -} diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs b/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs index 347d1415c0..272fb54710 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs @@ -4,12 +4,12 @@ using Algolia.Search.Http; using Algolia.Search.Utils; -namespace Algolia.Search.Models.Common; +namespace Algolia.Search.Models.Search; /// /// Secured Api Key restrictions /// -public partial class SecuredApiKeyRestriction +public partial class SecuredAPIKeyRestrictions { /// @@ -19,12 +19,12 @@ public partial class SecuredApiKeyRestriction public string ToQueryString() { string restrictionQuery = null; - if (Query != null) + if (SearchParams != null) { - restrictionQuery = ToQueryString(Query); + restrictionQuery = ToQueryString(SearchParams); } - var restrictions = ToQueryString(this, nameof(Query)); + var restrictions = ToQueryString(this, nameof(SearchParams)); var array = new[] { restrictionQuery, restrictions }; return string.Join("&", array.Where(s => !string.IsNullOrEmpty(s))); diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Utils/ApiKeyOperation.cs b/clients/algoliasearch-client-csharp/algoliasearch/Utils/ApiKeyOperation.cs deleted file mode 100644 index 407d5bd4e5..0000000000 --- a/clients/algoliasearch-client-csharp/algoliasearch/Utils/ApiKeyOperation.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Algolia.Search.Utils; - -/// -/// ApiKey operations -/// -public enum ApiKeyOperation -{ - /// - /// Add a new ApiKey - /// - Add, - /// - /// Delete an existing ApiKey - /// - Delete, - /// - /// Update an existing ApiKey - /// - Update, -} diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs b/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs index 8433a38175..b59ca3a2a1 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs @@ -268,7 +268,7 @@ public static IEnumerable BrowseSynonyms(this SearchClient client, s /// Restriction to add the key /// public static string GenerateSecuredApiKey(this SearchClient client, string parentApiKey, - SecuredApiKeyRestriction restriction) + SecuredAPIKeyRestrictions restriction) { var queryParams = restriction.ToQueryString(); var hash = HmacShaHelper.GetHash(parentApiKey, queryParams); diff --git a/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/utils/ApiKeyOperation.java b/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/utils/ApiKeyOperation.java deleted file mode 100644 index a1db4e6805..0000000000 --- a/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/utils/ApiKeyOperation.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.algolia.utils; - -public enum ApiKeyOperation { - ADD, - DELETE, - UPDATE, -} diff --git a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/ApiKeyOperation.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/ApiKeyOperation.kt deleted file mode 100644 index f5dd78d28d..0000000000 --- a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/ApiKeyOperation.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.algolia.client.extensions - -/** - * Enum for the operations that can be performed. - */ -public enum class ApiKeyOperation { - Create, - Update, - Delete -} diff --git a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SearchClient.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SearchClient.kt index db58c64f57..94f82734a0 100644 --- a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SearchClient.kt +++ b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SearchClient.kt @@ -41,7 +41,7 @@ public suspend fun SearchClient.waitForApiKey( requestOptions: RequestOptions? = null, ) { when (operation) { - ApiKeyOperation.Create -> waitKeyCreation( + ApiKeyOperation.Add -> waitKeyCreation( key = key, maxRetries = maxRetries, timeout = timeout, @@ -311,7 +311,7 @@ public suspend fun SearchClient.replaceAllObjects( * @param restriction Restriction to add the key * @throws Exception if an error occurs during the encoding */ -public fun SearchClient.generateSecuredApiKey(parentAPIKey: String, restriction: SecuredAPIKeyRestriction): String { +public fun SearchClient.generateSecuredApiKey(parentAPIKey: String, restriction: SecuredAPIKeyRestrictions): String { val restrictionString = buildRestrictionString(restriction) val hash = encodeKeySHA256(parentAPIKey, restrictionString) return "$hash$restrictionString".encodeBase64() @@ -322,7 +322,7 @@ public fun SearchClient.generateSecuredApiKey(parentAPIKey: String, restriction: * * @param apiKey The secured API Key to check. * @return Duration left before the secured API key expires. - * @throws IllegalArgumentException if [apiKey] doesn't have a [SecuredAPIKeyRestriction.validUntil]. + * @throws IllegalArgumentException if [apiKey] doesn't have a [SecuredAPIKeyRestrictions.validUntil]. */ public fun securedApiKeyRemainingValidity(apiKey: String): Duration { val decoded = apiKey.decodeBase64String() diff --git a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SecuredAPIKeyRestriction.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SecuredAPIKeyRestriction.kt deleted file mode 100644 index 1e30aeb518..0000000000 --- a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/SecuredAPIKeyRestriction.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.algolia.client.extensions - -import com.algolia.client.model.search.SearchParamsObject -import kotlinx.datetime.Instant - -/** - * Create restrictions for an API key. - * - * @param query A mapping of search parameters that will be forced at query time. - * @param restrictIndices List of index names that can be queried. - * @param restrictSources IPv4 network allowed using the generated key. This is used for more protection against - * the api key leaking and reuse. - * @param validUntil A Unix timestamp used to define the expiration date of the API key. - * @param userToken Specify a user identifier. This is often used with rate limits. By default, rate limits will only - * use the IP. This can be an issue when several of your end users are using the same IP. To avoid that, you can set a - * userToken query parameter when generating the key. When set, a unique user will be identified by their IP + user_token - * instead of only by their IP. This allows you to restrict a single user from performing a maximum of N API calls per hour, - * even if he shares his IP with another user. - */ -public data class SecuredAPIKeyRestriction( - val query: SearchParamsObject? = null, - val restrictIndices: List? = null, - val restrictSources: List? = null, - val validUntil: Instant? = null, - val userToken: String? = null -) diff --git a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/internal/SearchClient.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/internal/SearchClient.kt index e017d24fef..ef176120dc 100644 --- a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/internal/SearchClient.kt +++ b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/internal/SearchClient.kt @@ -1,20 +1,20 @@ package com.algolia.client.extensions.internal import com.algolia.client.api.SearchClient -import com.algolia.client.extensions.SecuredAPIKeyRestriction import com.algolia.client.model.search.SearchParamsObject +import com.algolia.client.model.search.SecuredAPIKeyRestrictions import io.ktor.http.* import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive /** - * Builds a restriction string based on provided [SecuredAPIKeyRestriction]. + * Builds a restriction string based on provided [SecuredAPIKeyRestrictions]. */ -internal fun SearchClient.buildRestrictionString(restriction: SecuredAPIKeyRestriction): String { +internal fun SearchClient.buildRestrictionString(restriction: SecuredAPIKeyRestrictions): String { return Parameters.build { - restriction.query?.let { query -> - val json = options.json.encodeToJsonElement(SearchParamsObject.serializer(), query).jsonObject + restriction.searchParams?.let { searchParams -> + val json = options.json.encodeToJsonElement(SearchParamsObject.serializer(), searchParams).jsonObject json.forEach { (key, element) -> when (element) { is JsonArray -> appendAll(key, element.jsonPrimitive.content.map { it.toString() }) @@ -23,8 +23,8 @@ internal fun SearchClient.buildRestrictionString(restriction: SecuredAPIKeyRestr } } restriction.restrictIndices?.let { append("restrictIndices", it.joinToString(";")) } - restriction.restrictSources?.let { append("restrictSources", it.joinToString(";")) } + restriction.restrictSources?.let { append("restrictSources", it) } restriction.userToken?.let { append("userToken", it) } - restriction.validUntil?.let { append("validUntil", it.toEpochMilliseconds().toString()) } + restriction.validUntil?.let { append("validUntil", it.toString()) } }.formUrlEncode() } diff --git a/clients/algoliasearch-client-kotlin/client/src/commonTest/kotlin/com/algolia/client/TestSecureApiKey.kt b/clients/algoliasearch-client-kotlin/client/src/commonTest/kotlin/com/algolia/client/TestSecureApiKey.kt index 0d86691edc..e061cd633d 100644 --- a/clients/algoliasearch-client-kotlin/client/src/commonTest/kotlin/com/algolia/client/TestSecureApiKey.kt +++ b/clients/algoliasearch-client-kotlin/client/src/commonTest/kotlin/com/algolia/client/TestSecureApiKey.kt @@ -1,7 +1,7 @@ package com.algolia.client import com.algolia.client.api.SearchClient -import com.algolia.client.extensions.SecuredAPIKeyRestriction +import com.algolia.client.extensions.SecuredAPIKeyRestrictions import com.algolia.client.extensions.generateSecuredApiKey import com.algolia.client.extensions.securedApiKeyRemainingValidity import com.algolia.client.model.search.SearchParamsObject @@ -15,7 +15,7 @@ class TestSecureApiKey { @Test fun securedApiKey() { val parentAPIKey = "SearchOnlyApiKeyKeptPrivate" - val restriction = SecuredAPIKeyRestriction( + val restriction = SecuredAPIKeyRestrictions( query = SearchParamsObject(filters = "_tags:user_42"), validUntil = Clock.System.now() + 2.days, ) diff --git a/clients/algoliasearch-client-python/algoliasearch/http/helpers.py b/clients/algoliasearch-client-python/algoliasearch/http/helpers.py index 4acb59e6a4..c6ec476498 100644 --- a/clients/algoliasearch-client-python/algoliasearch/http/helpers.py +++ b/clients/algoliasearch-client-python/algoliasearch/http/helpers.py @@ -1,12 +1,7 @@ # coding: utf-8 from asyncio import sleep -from json import loads -from typing import Any, Callable, Dict, List, Optional, Protocol, Self, TypeVar, Union - -from pydantic import BaseModel - -from algoliasearch.search.models.search_params_object import SearchParamsObject +from typing import Callable, Protocol, TypeVar T = TypeVar("T") @@ -57,59 +52,3 @@ async def retry(prev: T = None) -> T: return await retry(resp) return await retry() - - -class SecuredApiKeyRestrictions(BaseModel): - """ - SecuredApiKeyRestrictions - """ - - search_params: SearchParamsObject = SearchParamsObject() - valid_until: Optional[int] = 0 - restrict_indices: Optional[Union[List[str], str]] = None - restrict_sources: Optional[str] = None - user_token: Optional[str] = None - - model_config = {"populate_by_name": True, "validate_assignment": True} - - def to_json(self) -> str: - return self.model_dump_json(by_alias=True, exclude_unset=True) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Create an instance of SecuredApiKeyRestrictions from a JSON string""" - return cls.from_dict(loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - * Fields in `self.additional_properties` are added to the output dict. - """ - return self.model_dump( - by_alias=True, - exclude={}, - exclude_none=True, - ) - - @classmethod - def from_dict(cls, obj: Dict) -> Self: - """Create an instance of SecuredApiKeyRestrictions from a dict""" - if obj is None: - return None - if not isinstance(obj, dict): - return cls.model_validate(obj) - return cls.model_validate( - { - "search_params": SearchParamsObject.from_dict(obj.get("search_params")), - "valid_until": int(obj.get("valid_until")), - "restrict_indices": obj.get("restrict_indices"), - "restrict_sources": obj.get("restrict_sources"), - "user_token": obj.get("user_token"), - } - ) diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/ApiKeyOperation.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/ApiKeyOperation.scala deleted file mode 100644 index f2dd585976..0000000000 --- a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/ApiKeyOperation.scala +++ /dev/null @@ -1,9 +0,0 @@ -package algoliasearch.extension - -sealed trait ApiKeyOperation - -object ApiKeyOperation { - case object Create extends ApiKeyOperation - case object Update extends ApiKeyOperation - case object Delete extends ApiKeyOperation -} diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/package.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/package.scala index c33534d672..023332d213 100644 --- a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/package.scala +++ b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/extension/package.scala @@ -34,7 +34,7 @@ package object extension { requestOptions: Option[RequestOptions] = None )(implicit ec: ExecutionContext): Future[Any] = { operation match { - case ApiKeyOperation.Create => + case ApiKeyOperation.Add => client.waitKeyCreation(key, maxRetries, delay, requestOptions) case ApiKeyOperation.Update => client.waitKeyUpdate(key, apiKey.get, maxRetries, delay, requestOptions) diff --git a/clients/algoliasearch-client-swift/Sources/Search/Extra/APIKeyOperation.swift b/clients/algoliasearch-client-swift/Sources/Search/Extra/APIKeyOperation.swift deleted file mode 100644 index 7b1912be2d..0000000000 --- a/clients/algoliasearch-client-swift/Sources/Search/Extra/APIKeyOperation.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// APIKeyOperation.swift -// -// -// Created by Algolia on 26/02/2024. -// - -import Foundation - -public enum ApiKeyOperation: String { - case add - case update - case delete -} diff --git a/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchHelpers.swift b/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchHelpers.swift index ea06a39cdc..74b0e94b4a 100644 --- a/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchHelpers.swift +++ b/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchHelpers.swift @@ -445,7 +445,7 @@ public extension SearchClient { /// - returns: String? func generateSecuredApiKey( parentApiKey: String, - with restriction: SecuredAPIKeyRestriction = SecuredAPIKeyRestriction() + with restriction: SecuredAPIKeyRestrictions = SecuredAPIKeyRestrictions() ) throws -> String? { let queryParams = try restriction.toURLEncodedString() let hash = queryParams.hmac256(withKey: parentApiKey) diff --git a/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredAPIKeyRestriction.swift b/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift similarity index 55% rename from clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredAPIKeyRestriction.swift rename to clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift index 935b1dee2c..dd8135fdbd 100644 --- a/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredAPIKeyRestriction.swift +++ b/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift @@ -1,54 +1,30 @@ -// -// SecuredAPIKeyRestriction.swift -// -// -// Created by Algolia on 26/02/2024. -// - -import Core import Foundation +import Core -public struct SecuredAPIKeyRestriction { - let searchParams: SearchParamsObject? - let validUntil: TimeInterval? - let restrictIndices: [String]? - let restrictSources: [String]? - let userToken: String? - - public init( - searchParams: SearchParamsObject? = nil, - validUntil: TimeInterval? = nil, - restrictIndices: [String]? = nil, - restrictSources: [String]? = nil, - userToken: String? = nil - ) { - self.searchParams = searchParams - self.validUntil = validUntil - self.restrictIndices = restrictIndices - self.restrictSources = restrictSources - self.userToken = userToken - } - - func toURLEncodedString() throws -> String { +public extension SecuredAPIKeyRestrictions { + func toURLEncodedString() throws -> String { var queryDictionary: [String: Any] = [:] if let searchParams { - let searchParamsData = try CodableHelper.jsonEncoder.encode(self.searchParams) + let searchParamsData = try CodableHelper.jsonEncoder.encode(searchParams) guard let searchParamsDictionary = try JSONSerialization.jsonObject( with: searchParamsData, options: .fragmentsAllowed ) as? [String: Any] else { - throw AlgoliaError.runtimeError("Unable to encode Secured API Key Restrictions search parameters") + throw AlgoliaError.runtimeError("Unable to encode Secured API Key restrictions search parameters") } queryDictionary = searchParamsDictionary } + if let filters { + queryDictionary["filters"] = filters + } if let validUntil { queryDictionary["validUntil"] = Int(validUntil) } if let restrictIndices { - queryDictionary["restrictIndices"] = restrictIndices + queryDictionary["restrictIndices"] = restrictIndices.joined(separator: ",") } if let restrictSources { queryDictionary["restrictSources"] = restrictSources diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaCSharpGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaCSharpGenerator.java index 7d35933e5e..431cde929c 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaCSharpGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaCSharpGenerator.java @@ -151,6 +151,7 @@ public void processOpenAPI(OpenAPI openAPI) { @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); GenericPropagator.propagateGenericsToOperations(operations, models); return operations; } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaDartGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaDartGenerator.java index 5cedf8ce8a..3cb99c75ee 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaDartGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaDartGenerator.java @@ -133,6 +133,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { OperationsMap operationsMap = super.postProcessOperationsWithModels(objs, allModels); + Helpers.removeHelpers(operationsMap); return support.clearOneOfFromApiImports(operationsMap); } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java index b6d102e82f..37fe797aff 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java @@ -129,6 +129,7 @@ public Map postProcessAllModels(Map objs) @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); GenericPropagator.propagateGenericsToOperations(operations, models); return operations; } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java index b77d30fdc8..18778b31f0 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java @@ -3,7 +3,9 @@ import com.algolia.codegen.exceptions.*; import com.algolia.codegen.utils.*; import com.algolia.codegen.utils.OneOf; +import com.google.common.collect.ImmutableMap.Builder; import com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Mustache.Lambda; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.Schema; @@ -50,7 +52,6 @@ public void processOpts() { supportingFiles.add(new SupportingFile("build_config.mustache", invokerFolder, "BuildConfig.java")); supportingFiles.add(new SupportingFile("gradle.properties.mustache", "", "gradle.properties")); additionalProperties.put("isSearchClient", client.equals("search")); - additionalProperties.put("lambda.type-to-name", (Mustache.Lambda) (fragment, writer) -> writer.write(typeToName(fragment.execute()))); try { additionalProperties.put("packageVersion", Helpers.getClientConfigField("java", "packageVersion")); @@ -89,6 +90,7 @@ public Map postProcessAllModels(Map objs) @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); GenericPropagator.propagateGenericsToOperations(operations, models); return operations; } @@ -119,4 +121,13 @@ public String toEnumVarName(String value, String datatype) { private String typeToName(String content) { return content.trim().replace("<", "Of").replace(">", "").replace(",", "").replace(" ", ""); } + + @Override + protected Builder addMustacheLambdas() { + Builder lambdas = super.addMustacheLambdas(); + + lambdas.put("type-to-name", (Mustache.Lambda) (fragment, writer) -> writer.write(typeToName(fragment.execute()))); + + return lambdas; + } } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java index 109982fae8..7f3644e2ed 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java @@ -178,6 +178,7 @@ public Map postProcessAllModels(Map objs) @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { OperationsMap results = super.postProcessOperationsWithModels(objs, allModels); + Helpers.removeHelpers(results); setDefaultGeneratorOptions(); try { diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java index 0f03d8bfa9..9c8aab456d 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java @@ -187,6 +187,7 @@ private static void jsonParent(Map models) { @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); GenericPropagator.propagateGenericsToOperations(operations, models); return operations; } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java index 9b0bcc4bc7..5f916bf918 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java @@ -9,6 +9,8 @@ import org.openapitools.codegen.CodegenOperation; import org.openapitools.codegen.SupportingFile; import org.openapitools.codegen.languages.PhpClientCodegen; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.OperationsMap; public class AlgoliaPhpGenerator extends PhpClientCodegen { @@ -70,6 +72,13 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation return Helpers.specifyCustomRequest(super.fromOperation(path, httpMethod, operation, servers)); } + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { + OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); + return operations; + } + @Override public String getComposerPackageName() { return "algolia/algoliasearch-client-php"; diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java index f8e0f6bd1e..b63e8bb3a7 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java @@ -117,6 +117,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); List> imports = operations.getImports(); diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaRubyGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaRubyGenerator.java index 804d5e810d..6e00d4b5e3 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaRubyGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaRubyGenerator.java @@ -8,6 +8,8 @@ import java.util.*; import org.openapitools.codegen.*; import org.openapitools.codegen.languages.RubyClientCodegen; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.OperationsMap; public class AlgoliaRubyGenerator extends RubyClientCodegen { @@ -74,6 +76,13 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation return Helpers.specifyCustomRequest(super.fromOperation(path, httpMethod, operation, servers)); } + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { + OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); + return operations; + } + @Override public String modelFileFolder() { return outputFolder + "/lib/" + gemName + "/models/" + CLIENT; diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java index e0af02f4ec..f131a38259 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java @@ -137,6 +137,7 @@ public Map postProcessAllModels(Map objs) @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); GenericPropagator.propagateGenericsToOperations(operations, models); return operations; } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaSwiftGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaSwiftGenerator.java index 8b52e80ae9..4594c03709 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaSwiftGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaSwiftGenerator.java @@ -220,6 +220,7 @@ public Map postProcessAllModels(Map objs) @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + Helpers.removeHelpers(operations); GenericPropagator.propagateGenericsToOperations(operations, models); return operations; } diff --git a/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicTemplateLambda.java b/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicTemplateLambda.java index 7aa7f4000b..688ac6f4fa 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicTemplateLambda.java +++ b/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicTemplateLambda.java @@ -16,8 +16,8 @@ public class DynamicTemplateLambda implements Mustache.Lambda { - private TemplatingExecutor executor; - private TemplatingEngineAdapter adaptor; + private final TemplatingExecutor executor; + private final TemplatingEngineAdapter adaptor; public DynamicTemplateLambda(DefaultCodegen generator) { // we can't access the default template manager, so we have to create our own diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java index 5e460a4ec4..66429c6a2b 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java @@ -60,7 +60,13 @@ public void run(Map models, Map for (Map.Entry entry : operations.entrySet()) { String operationId = entry.getKey(); + CodegenOperation ope = entry.getValue(); + boolean isHelper = (boolean) ope.vendorExtensions.getOrDefault("x-helper", false); if (!cts.containsKey(operationId)) { + if (isHelper) { + continue; + } + throw new CTSException( "operationId '" + operationId + @@ -85,52 +91,45 @@ public void run(Map models, Map test.put("testIndex", i); try { - CodegenOperation ope = entry.getValue(); test.put("isGeneric", (boolean) ope.vendorExtensions.getOrDefault("x-is-generic", false)); if (Helpers.CUSTOM_METHODS.contains(ope.operationIdOriginal)) { test.put("isCustomRequest", true); } - // We check on the spec if body parameters should be present in the CTS - // If so, we change the `null` default to an empty object, so we know if - // tests are properly written - if (ope.bodyParams.size() != 0 && req.request.body == null) { - req.request.body = "{}"; - } + if (req.request != null && !isHelper) { + // We check on the spec if body parameters should be present in the CTS + // If so, we change the `null` default to an empty object, so we know if + // tests are properly written + if (ope.bodyParams.size() != 0 && req.request.body == null) { + req.request.body = "{}"; + } - // For golang, jsonassert expect % to be formatted, we need to escape them - if (language.equals("go") && req.request.body != null) { - req.request.body = req.request.body.replace("%", "%%"); - } + // For golang, jsonassert expect % to be formatted, we need to escape them + if (language.equals("go") && req.request.body != null) { + req.request.body = req.request.body.replace("%", "%%"); + } - // For dart, same thing but for $ - if (language.equals("dart") && req.request.body != null) { - req.request.body = req.request.body.replace("$", "\\$"); - } + // For dart, same thing but for $ + if (language.equals("dart") && req.request.body != null) { + req.request.body = req.request.body.replace("$", "\\$"); + } - // In a case of a `GET` or `DELETE` request, we want to assert if the body - // is correctly parsed (absent from the payload) - if (req.request.method.equals("GET") || req.request.method.equals("DELETE")) { - test.put("assertNullBody", true); + // In a case of a `GET` or `DELETE` request, we want to assert if the body + // is correctly parsed (absent from the payload) + if (req.request.method.equals("GET") || req.request.method.equals("DELETE")) { + test.put("assertNullBody", true); + } } if (req.response != null) { bundle.put("hasE2E", true); test.put("response", req.response); - - if (req.response.statusCode == 0) { - throw new CTSException( - "operationId '" + - operationId + - "' has a 'response' field in order to generate e2e tests but is missing the" + - " 'statusCode' parameter" - ); - } } test.put("request", req.request); test.put("hasParameters", req.parameters.size() != 0); test.put("hasOperationParams", ope.hasParams); + test.put("isHelper", isHelper); if (req.requestOptions != null) { test.put("hasRequestOptions", true); diff --git a/generators/src/main/java/com/algolia/codegen/utils/Helpers.java b/generators/src/main/java/com/algolia/codegen/utils/Helpers.java index cd29d41fe9..c2549cde57 100644 --- a/generators/src/main/java/com/algolia/codegen/utils/Helpers.java +++ b/generators/src/main/java/com/algolia/codegen/utils/Helpers.java @@ -13,6 +13,7 @@ import org.openapitools.codegen.CodegenOperation; import org.openapitools.codegen.CodegenServer; import org.openapitools.codegen.CodegenServerVariable; +import org.openapitools.codegen.model.OperationsMap; public class Helpers { @@ -150,6 +151,10 @@ public static void generateServers(List servers, Map (boolean) entry.vendorExtensions.getOrDefault("x-helper", false)); + } + /** * Get the current version of the given client from the * `clients/algoliasearch-client-javascript/packages/${client}/package.json` file, defaults to diff --git a/playground/csharp/Playground/Playgrounds/Search.cs b/playground/csharp/Playground/Playgrounds/Search.cs index 004d9ee23b..01a022aac2 100644 --- a/playground/csharp/Playground/Playgrounds/Search.cs +++ b/playground/csharp/Playground/Playgrounds/Search.cs @@ -201,7 +201,7 @@ await PlaygroundHelper.Start("Deleting API Key", async () => Console.WriteLine("--- Generate Secured API Keys `GenerateSecuredApiKeys` ---"); var generateSecuredApiKeys = _client.GenerateSecuredApiKey(_configuration.SearchApiKey, - new SecuredApiKeyRestriction + new SecuredAPIKeyRestrictions { RestrictIndices = [DefaultIndex], }); diff --git a/playground/python/app/search.py b/playground/python/app/search.py index 3b93ed9598..1901e5e77c 100644 --- a/playground/python/app/search.py +++ b/playground/python/app/search.py @@ -2,7 +2,6 @@ from os import environ from algoliasearch.search.client import SearchClient -from algoliasearch.http.helpers import SecuredApiKeyRestrictions from algoliasearch.search import __version__ from dotenv import load_dotenv diff --git a/scripts/buildClients.ts b/scripts/buildClients.ts index ad2b193bd5..c6f41ee18b 100644 --- a/scripts/buildClients.ts +++ b/scripts/buildClients.ts @@ -1,5 +1,7 @@ /* eslint-disable no-case-declarations */ -import { run } from './common.js'; +import * as fsp from 'fs/promises'; + +import { run, toAbsolutePath } from './common.js'; import { getClientsConfigField, getLanguageFolder } from './config.js'; import { createSpinner } from './spinners.js'; import type { Generator, Language } from './types.js'; @@ -26,6 +28,10 @@ async function buildClient(language: Language, gens: Generator[]): Promise break; case 'java': case 'kotlin': + await fsp.rm(toAbsolutePath(`${cwd}/.gradle`), { + recursive: true, + force: true, + }); await run(`./gradle/gradlew --no-daemon -p ${cwd} assemble`, { language }); break; case 'python': diff --git a/scripts/buildSpecs.ts b/scripts/buildSpecs.ts index 4a716fd540..14ffef8dc7 100644 --- a/scripts/buildSpecs.ts +++ b/scripts/buildSpecs.ts @@ -56,10 +56,17 @@ async function transformBundle({ continue; } + if (specMethod['x-helper']) { + delete bundledDocSpec.paths[pathKey]; + break; + } + // Checks that specified tags are well defined at root level for (const tag of docMethod.tags) { if (tag === clientName) { - throw new Error(`Tag name "${tag}" must be different from client name ${clientName}`); + throw new Error( + `Tag name "${tag}" must be different from client name ${clientName} in operation ${specMethod.operationId}`, + ); } if (alias && tag === alias) { throw new Error(`Tag name "${tag} must be different from alias ${alias}`); @@ -121,11 +128,9 @@ async function lintCommon(useCache: boolean): Promise { async function buildLiteSpec({ spec, bundledPath, - outputFormat, }: { spec: string; bundledPath: string; - outputFormat: string; }): Promise { const parsed = yaml.load(await fsp.readFile(toAbsolutePath(bundledPath), 'utf8')) as Spec; @@ -143,11 +148,10 @@ async function buildLiteSpec({ {} as Spec['paths'], ); - const liteBundledPath = `specs/bundled/${spec}.${outputFormat}`; - await fsp.writeFile(toAbsolutePath(liteBundledPath), yaml.dump(parsed)); + await fsp.writeFile(bundledPath, yaml.dump(parsed)); await transformBundle({ - bundledPath: toAbsolutePath(liteBundledPath), + bundledPath, clientName: spec, // Lite does not need documentation because it's just a subset withDoc: false, @@ -205,12 +209,11 @@ async function buildSpec(spec: string, outputFormat: string, useCache: boolean): await buildLiteSpec({ spec, bundledPath: toAbsolutePath(bundledPath), - outputFormat, }); } spinner.text = `validating '${spec}' bundled spec`; - await run(`yarn openapi lint specs/bundled/${spec}.${outputFormat}`); + await run(`yarn openapi lint ${bundledPath}`); spinner.text = `linting '${spec}' bundled spec`; await run(`yarn specs:fix bundled/${spec}.${outputFormat}`); diff --git a/scripts/common.ts b/scripts/common.ts index 7748bdd848..34923908d7 100644 --- a/scripts/common.ts +++ b/scripts/common.ts @@ -15,7 +15,7 @@ import { getDockerImage } from './config'; import { generateOpenapitools } from './pre-gen'; import { getGitAuthor } from './release/common.js'; import { createSpinner } from './spinners.js'; -import type { Generator, Language, RunOptions } from './types.js'; +import type { Generator, GeneratorMode, Language, RunOptions } from './types.js'; export const MAIN_BRANCH = releaseConfig.mainBranch; export const OWNER = releaseConfig.owner; @@ -275,22 +275,16 @@ export function isVerbose(): boolean { return verbose; } -export async function callCTSGenerator(gen: Generator, mode: 'snippets' | 'tests'): Promise { - const spinner = createSpinner( - `generating ${mode === 'tests' ? 'CTS' : 'code snippets'} for ${gen.key}`, - ); - +export async function callGenerator(gen: Generator): Promise { await run( - `yarn openapi-generator-cli --custom-generator=generators/build/libs/algolia-java-openapi-generator-1.0.0.jar generate \ - -g algolia-cts -i specs/bundled/${gen.client}.yml --additional-properties="language=${gen.language},client=${gen.client},mode=${mode}"`, + `yarn openapi-generator-cli --custom-generator=generators/build/libs/algolia-java-openapi-generator-1.0.0.jar generate --generator-key ${gen.key}`, { language: 'java' }, ); - - spinner.succeed(); } export async function setupAndGen( generators: Generator[], + mode: GeneratorMode, fn: (gen: Generator) => Promise, ): Promise { if (!CI) { @@ -298,10 +292,12 @@ export async function setupAndGen( await buildSpecs(clients, 'yml', true); } - await generateOpenapitools(generators); + await generateOpenapitools(generators, mode); await buildCustomGenerators(); for (const gen of generators) { + const spinner = createSpinner(`generating ${mode} for ${gen.key}`); await fn(gen); + spinner.succeed(); } } diff --git a/scripts/cts/generate.ts b/scripts/cts/generate.ts index ebf733cf86..c812afe83d 100644 --- a/scripts/cts/generate.ts +++ b/scripts/cts/generate.ts @@ -1,12 +1,12 @@ -import { callCTSGenerator, run, setupAndGen } from '../common.js'; +import { callGenerator, run, setupAndGen } from '../common.js'; import { getTestOutputFolder } from '../config.js'; import { formatter } from '../formatter.js'; import type { Generator } from '../types.js'; export async function ctsGenerateMany(generators: Generator[]): Promise { - await setupAndGen(generators, async (gen) => { + await setupAndGen(generators, 'tests', async (gen) => { if (getTestOutputFolder(gen.language)) { - await callCTSGenerator(gen, 'tests'); + await callGenerator(gen); } }); diff --git a/scripts/generate.ts b/scripts/generate.ts index 28b5cd3353..e15b8ec915 100644 --- a/scripts/generate.ts +++ b/scripts/generate.ts @@ -1,8 +1,7 @@ -import { run, setupAndGen } from './common.js'; +import { callGenerator, setupAndGen } from './common.js'; import { getLanguageFolder } from './config.js'; import { formatter } from './formatter.js'; import { removeExistingCodegen } from './pre-gen/index.js'; -import { createSpinner } from './spinners.js'; import type { Generator } from './types.js'; async function preGen(gen: Generator): Promise { @@ -10,17 +9,9 @@ async function preGen(gen: Generator): Promise { } export async function generate(generators: Generator[]): Promise { - await setupAndGen(generators, async (gen) => { - const spinner = createSpinner(`pre-gen ${gen.key}`); + await setupAndGen(generators, 'client', async (gen) => { await preGen(gen); - - spinner.text = `generating ${gen.key}`; - await run( - `yarn openapi-generator-cli --custom-generator=generators/build/libs/algolia-java-openapi-generator-1.0.0.jar generate --generator-key ${gen.key}`, - { language: 'java' }, - ); - - spinner.succeed(); + await callGenerator(gen); }); for (const lang of [...new Set(generators.map((gen) => gen.language))]) { diff --git a/scripts/install.sh b/scripts/install.sh index 7fd3d02b4e..6624270ee5 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -58,7 +58,7 @@ _apic_cts_complete() { _apic_build_specs_complete() { if [[ COMP_CWORD -eq 3 ]]; then - COMPREPLY=($(compgen -W "$(_list_clients)" -- "$cur")) + COMPREPLY=($(compgen -W "algoliasearch $(_list_clients)" -- "$cur")) fi } diff --git a/scripts/playground.ts b/scripts/playground.ts index ac74d14bc1..0a1a43af91 100644 --- a/scripts/playground.ts +++ b/scripts/playground.ts @@ -61,9 +61,6 @@ export async function playground({ language, }); break; - case 'scala': - // run scala playground - break; default: } } diff --git a/scripts/pre-gen/generateOpenapitools.ts b/scripts/pre-gen/generateOpenapitools.ts index 53643a3844..9f3301c59d 100644 --- a/scripts/pre-gen/generateOpenapitools.ts +++ b/scripts/pre-gen/generateOpenapitools.ts @@ -2,7 +2,7 @@ import { writeFile } from 'fs/promises'; import { toAbsolutePath } from '../common.js'; import { getClientsConfigField } from '../config.js'; -import type { Generator } from '../types.js'; +import type { Generator, GeneratorMode } from '../types.js'; /** * Create an on the fly openapitools.json file with default options for all generators. @@ -10,9 +10,9 @@ import type { Generator } from '../types.js'; * Defaults options are used to * - Set config path. */ -export async function generateOpenapitools(gens: Generator[]): Promise { +export async function generateOpenapitools(gens: Generator[], mode: GeneratorMode): Promise { const generators = {}; - for (const { key, client, language, additionalProperties, ...rest } of gens) { + for (const { key, client, language, ...rest } of gens) { const templateDir = language === 'javascript' ? `#{cwd}/templates/${language}/clients` @@ -25,10 +25,12 @@ export async function generateOpenapitools(gens: Generator[]): Promise { gitRepoId: getClientsConfigField(language, 'gitRepoId'), glob: `specs/bundled/${client}.yml`, templateDir, - generatorName: `algolia-${language}`, - output: `#{cwd}/${rest.output}`, + generatorName: `algolia-${mode === 'client' ? language : 'cts'}`, + output: `#{cwd}/${mode === 'client' ? rest.output : ''}`, additionalProperties: { + language, client, + mode, }, }; } diff --git a/scripts/snippets/generate.ts b/scripts/snippets/generate.ts index 9bf5c76a22..72a9b49f8c 100644 --- a/scripts/snippets/generate.ts +++ b/scripts/snippets/generate.ts @@ -1,12 +1,12 @@ -import { callCTSGenerator, exists, run, setupAndGen, toAbsolutePath } from '../common.js'; +import { callGenerator, exists, run, setupAndGen, toAbsolutePath } from '../common.js'; import { getTestOutputFolder } from '../config.js'; import { formatter } from '../formatter.js'; import type { Generator } from '../types.js'; export async function snippetsGenerateMany(generators: Generator[]): Promise { - await setupAndGen(generators, async (gen) => { + await setupAndGen(generators, 'snippets', async (gen) => { if (getTestOutputFolder(gen.language)) { - await callCTSGenerator(gen, 'snippets'); + await callGenerator(gen); } }); diff --git a/scripts/spinners.ts b/scripts/spinners.ts index 00db1500b6..a1ba35f64c 100644 --- a/scripts/spinners.ts +++ b/scripts/spinners.ts @@ -66,6 +66,8 @@ class SpinnerWrapper { } } -export function createSpinner(text: string): SpinnerStatic | SpinnerWrapper { +export type Spinner = SpinnerStatic | SpinnerWrapper; + +export function createSpinner(text: string): Spinner { return CI || isVerbose() ? new SpinnerStatic(text) : new SpinnerWrapper(text, text); } diff --git a/scripts/types.ts b/scripts/types.ts index 5e5720a0f8..01af65c03c 100644 --- a/scripts/types.ts +++ b/scripts/types.ts @@ -24,9 +24,12 @@ export type Generator = Record & { language: Language; client: string; key: string; + output: string; additionalProperties: AdditionalProperties; }; +export type GeneratorMode = 'client' | 'snippets' | 'tests'; + export type RunOptions = { errorMessage?: string; cwd?: string; diff --git a/specs/search/helpers/generateSecuredApiKey.yml b/specs/search/helpers/generateSecuredApiKey.yml new file mode 100644 index 0000000000..f0a7dd170e --- /dev/null +++ b/specs/search/helpers/generateSecuredApiKey.yml @@ -0,0 +1,71 @@ +method: + get: + x-helper: true + tags: + - Api Keys + operationId: generateSecuredApiKey + summary: Generate a secured API key without any calls to Algolia's servers. + description: + Generate a secured API key without any calls to Algolia's servers. + + When you need to restrict the scope of an API key, generate a secured API key on your server, without any calls to Algolia. + You can't generate secured API keys from your Admin API key or from other secured API keys. + When you generate a secured API key, you can define several restrictions, such as how long the key is valid for and which indexes it can access. The more restrictions you set, the longer the key will be. If the key is longer than 500 characters, you may have problems using it on some networks. + If you want to limit the number of requests that can be made with a secured API key, you must also rate-limit the key that you use to generate it. You can create a rate-limited key in the Algolia dashboard or use the Add API key or Update API key methods of an API client. + parameters: + - in: query + name: apiKey + description: The search-only API key that the secured API key will inherit its restrictions from. + required: true + schema: + type: string + - in: query + name: restrictions + description: The options to add to the secured API key. + required: true + schema: + $ref: '#/securedAPIKeyRestrictions' + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + '400': + $ref: '../../common/responses/IndexNotFound.yml' + +securedAPIKeyRestrictions: + type: object + additionalProperties: false + properties: + searchParams: + $ref: '../../common/schemas/SearchParams.yml#/searchParamsObject' + filters: + type: string + description: > + Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. + + For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + validUntil: + type: number + format: duration + description: Unix timestamp used to set the expiration date of the API key. + restrictIndices: + type: array + items: + type: string + description: Index names that can be queried. + restrictSources: + type: string + description: > + IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. + + You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + userToken: + type: string + description: > + Unique user IP address. + + This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. + Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. diff --git a/specs/search/helpers/waitForApiKey.yml b/specs/search/helpers/waitForApiKey.yml new file mode 100644 index 0000000000..34b20f2d09 --- /dev/null +++ b/specs/search/helpers/waitForApiKey.yml @@ -0,0 +1,40 @@ +method: + get: + x-helper: true + tags: + - Api Keys + operationId: waitForApiKey + summary: Wait for an API key to be added, deleted, or updated. + description: description + parameters: + - in: query + name: operation + description: The `operation` that was done on a `key`. + required: true + schema: + $ref: '#/apiKeyOperation' + - in: query + name: key + description: The `key` that has been added, deleted or updated. + required: true + schema: + type: string + - in: query + name: apiKey + description: Used to compare fields of the getApiKey response on an `update` operation, to check if the `key` has been updated. + required: false + schema: + $ref: '../paths/keys/common/schemas.yml#/apiKey' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../paths/keys/common/schemas.yml#/getApiKeyResponse' + '400': + $ref: '../../common/responses/IndexNotFound.yml' + +apiKeyOperation: + type: string + enum: [add, delete, update] diff --git a/specs/search/spec.yml b/specs/search/spec.yml index 6f0cb160b2..082a2ffb71 100644 --- a/specs/search/spec.yml +++ b/specs/search/spec.yml @@ -231,3 +231,12 @@ paths: $ref: 'paths/manage_indices/operationIndex.yml' /1/indexes: $ref: 'paths/manage_indices/listIndices.yml' + + # ############### + # ### Helpers ### + # ############### + /waitForApiKey: + $ref: 'helpers/waitForApiKey.yml#/method' + + /generateSecuredApiKey: + $ref: 'helpers/generateSecuredApiKey.yml#/method' diff --git a/templates/go/api.mustache b/templates/go/api.mustache index af94fa9a11..17a4c8502d 100644 --- a/templates/go/api.mustache +++ b/templates/go/api.mustache @@ -312,262 +312,5 @@ func (c *APIClient) {{nickname}}WithContext(ctx context.Context, {{#hasParams}}r {{/operations}} {{#isSearchClient}} -/* -WaitForTask waits for a task to be published. -Wraps WaitForTaskWithContext with context.Background(). -It returns the task response if the operation was successful. -It returns an error if the operation failed. - -The maxRetries parameter is the maximum number of retries. -The initialDelay parameter is the initial delay between each retry. -The maxDelay parameter is the maximum delay between each retry. - - @param indexName string - Index name. - @param taskID int64 - Task ID. - @param maxRetries *int - Maximum number of retries. - @param initialDelay *time.Duration - Initial delay between retries. - @param maxDelay *time.Duration - Maximum delay between retries. - @param opts ...Option - Optional parameters for the request. - @return *GetTaskResponse - Task response. - @return error - Error if any. -*/ -func (c *APIClient) WaitForTask( - indexName string, - taskID int64, - maxRetries *int, - initialDelay *time.Duration, - maxDelay *time.Duration, - opts ...Option, -) (*GetTaskResponse, error) { - return c.WaitForTaskWithContext( - context.Background(), - indexName, - taskID, - maxRetries, - initialDelay, - maxDelay, - opts..., - ) -} - -/* -WaitForTaskWithContext waits for a task to be published. -It returns the task response if the operation was successful. -It returns an error if the operation failed. - -The maxRetries parameter is the maximum number of retries. -The initialDelay parameter is the initial delay between each retry. -The maxDelay parameter is the maximum delay between each retry. - - @param ctx context.Context - The context that will be drilled down to the actual request. - @param indexName string - Index name. - @param taskID int64 - Task ID. - @param maxRetries *int - Maximum number of retries. - @param initialDelay *time.Duration - Initial delay between retries. - @param maxDelay *time.Duration - Maximum delay between retries. - @param opts ...Option - Optional parameters for the request. - @return *GetTaskResponse - Task response. - @return error - Error if any. -*/ -func (c *APIClient) WaitForTaskWithContext( - ctx context.Context, - indexName string, - taskID int64, - maxRetries *int, - initialDelay *time.Duration, - maxDelay *time.Duration, - opts ...Option, -) (*GetTaskResponse, error) { - return utils.RetryUntil( //nolint:wrapcheck - func() (*GetTaskResponse, error) { - return c.GetTaskWithContext(ctx, c.NewApiGetTaskRequest(indexName, taskID), opts...) - }, - func(response *GetTaskResponse, err error) bool { - if err != nil || response == nil { - return false - } - - return response.Status == TASKSTATUS_PUBLISHED - }, - maxRetries, - initialDelay, - maxDelay, - ) -} - -/* -WaitForApiKey waits for an API key to be created, deleted or updated. -Wraps WaitForApiKeyWithContext with context.Background(). -It returns the API key response if the operation was successful. -It returns an error if the operation failed. - -The operation can be one of the following: - - "add": wait for the API key to be created - - "delete": wait for the API key to be deleted - - "update": wait for the API key to be updated - -The maxRetries parameter is the maximum number of retries. -The initialDelay parameter is the initial delay between each retry. -The maxDelay parameter is the maximum delay between each retry. - -If the operation is "update", the apiKey parameter must be set. -If the operation is "delete" or "add", the apiKey parameter is not used. - - @param key string - API key. - @param apiKey *ApiKey - API key structure - required for update operation. - @param operation string - Operation type - add, delete or update. - @param maxRetries *int - Maximum number of retries. - @param initialDelay *time.Duration - Initial delay between retries. - @param maxDelay *time.Duration - Maximum delay between retries. - @param opts ...Option - Optional parameters for the request. - @return *GetApiKeyResponse - API key response. - @return error - Error if any. -*/ -func (c *APIClient) WaitForApiKey( - key string, - apiKey *ApiKey, - operation string, - maxRetries *int, - initialDelay *time.Duration, - maxDelay *time.Duration, - opts ...Option, -) (*GetApiKeyResponse, error) { - return c.WaitForApiKeyWithContext( - context.Background(), - key, - apiKey, - operation, - maxRetries, - initialDelay, - maxDelay, - opts..., - ) -} - -/* -WaitForApiKeyWithContext waits for an API key to be created, deleted or updated. -It returns the API key response if the operation was successful. -It returns an error if the operation failed. - -The operation can be one of the following: - - "add": wait for the API key to be created - - "delete": wait for the API key to be deleted - - "update": wait for the API key to be updated - -The maxRetries parameter is the maximum number of retries. -The initialDelay parameter is the initial delay between each retry. -The maxDelay parameter is the maximum delay between each retry. - -If the operation is "update", the apiKey parameter must be set. -If the operation is "delete" or "add", the apiKey parameter is not used. - - @param ctx context.Context - The context that will be drilled down to the actual request. - @param key string - API key. - @param apiKey *ApiKey - API key structure - required for update operation. - @param operation string - Operation type - add, delete or update. - @param maxRetries *int - Maximum number of retries. - @param initialDelay *time.Duration - Initial delay between retries. - @param maxDelay *time.Duration - Maximum delay between retries. - @param opts ...Option - Optional parameters for the request. - @return *GetApiKeyResponse - API key response. - @return error - Error if any. -*/ -func (c *APIClient) WaitForApiKeyWithContext( - ctx context.Context, - key string, - apiKey *ApiKey, - operation string, - maxRetries *int, - initialDelay *time.Duration, - maxDelay *time.Duration, - opts ...Option, -) (*GetApiKeyResponse, error) { - if operation != "add" && operation != "delete" && operation != "update" { - return nil, &errs.WaitKeyOperationError{} - } - - if operation == "update" { - if apiKey == nil { - return nil, &errs.WaitKeyUpdateError{} - } - - return utils.RetryUntil( //nolint:wrapcheck - func() (*GetApiKeyResponse, error) { - return c.GetApiKeyWithContext(ctx, c.NewApiGetApiKeyRequest(key), opts...) - }, - func(response *GetApiKeyResponse, err error) bool { - if err != nil || response == nil { - return false - } - - if apiKey.GetDescription() != response.GetDescription() { - return false - } - - if apiKey.GetQueryParameters() != response.GetQueryParameters() { - return false - } - - if apiKey.GetMaxHitsPerQuery() != response.GetMaxHitsPerQuery() { - return false - } - - if apiKey.GetMaxQueriesPerIPPerHour() != response.GetMaxQueriesPerIPPerHour() { - return false - } - - if apiKey.GetValidity() != response.GetValidity() { - return false - } - - slices.Sort(apiKey.Acl) - slices.Sort(response.Acl) - - if !slices.Equal(apiKey.Acl, response.Acl) { - return false - } - - slices.Sort(apiKey.Indexes) - slices.Sort(response.Indexes) - - if !slices.Equal(apiKey.Indexes, response.Indexes) { - return false - } - - slices.Sort(apiKey.Referers) - slices.Sort(response.Referers) - - return slices.Equal(apiKey.Referers, response.Referers) - }, - maxRetries, - initialDelay, - maxDelay, - ) - } - - return utils.RetryUntil( //nolint:wrapcheck - func() (*GetApiKeyResponse, error) { - return c.GetApiKeyWithContext(ctx, c.NewApiGetApiKeyRequest(key), opts...) - }, - func(response *GetApiKeyResponse, err error) bool { - switch operation { - case "add": - return err == nil && response != nil && response.CreatedAt > 0 - case "delete": - if _, ok := err.(*APIError); ok { - apiErr := err.(*APIError) - - return apiErr.Status == 404 - } - - return false - } - return false - }, - maxRetries, - initialDelay, - maxDelay, - ) -} - +{{> search_helpers}} {{/isSearchClient}} diff --git a/templates/go/search_helpers.mustache b/templates/go/search_helpers.mustache new file mode 100644 index 0000000000..e9e0347857 --- /dev/null +++ b/templates/go/search_helpers.mustache @@ -0,0 +1,295 @@ +/* +WaitForTask waits for a task to be published. +Wraps WaitForTaskWithContext with context.Background(). +It returns the task response if the operation was successful. +It returns an error if the operation failed. + +The maxRetries parameter is the maximum number of retries. +The initialDelay parameter is the initial delay between each retry. +The maxDelay parameter is the maximum delay between each retry. + + @param indexName string - Index name. + @param taskID int64 - Task ID. + @param maxRetries *int - Maximum number of retries. + @param initialDelay *time.Duration - Initial delay between retries. + @param maxDelay *time.Duration - Maximum delay between retries. + @param opts ...Option - Optional parameters for the request. + @return *GetTaskResponse - Task response. + @return error - Error if any. +*/ +func (c *APIClient) WaitForTask( + indexName string, + taskID int64, + maxRetries *int, + initialDelay *time.Duration, + maxDelay *time.Duration, + opts ...Option, +) (*GetTaskResponse, error) { + return c.WaitForTaskWithContext( + context.Background(), + indexName, + taskID, + maxRetries, + initialDelay, + maxDelay, + opts..., + ) +} + +/* +WaitForTaskWithContext waits for a task to be published. +It returns the task response if the operation was successful. +It returns an error if the operation failed. + +The maxRetries parameter is the maximum number of retries. +The initialDelay parameter is the initial delay between each retry. +The maxDelay parameter is the maximum delay between each retry. + + @param ctx context.Context - The context that will be drilled down to the actual request. + @param indexName string - Index name. + @param taskID int64 - Task ID. + @param maxRetries *int - Maximum number of retries. + @param initialDelay *time.Duration - Initial delay between retries. + @param maxDelay *time.Duration - Maximum delay between retries. + @param opts ...Option - Optional parameters for the request. + @return *GetTaskResponse - Task response. + @return error - Error if any. +*/ +func (c *APIClient) WaitForTaskWithContext( + ctx context.Context, + indexName string, + taskID int64, + maxRetries *int, + initialDelay *time.Duration, + maxDelay *time.Duration, + opts ...Option, +) (*GetTaskResponse, error) { + return utils.RetryUntil( //nolint:wrapcheck + func() (*GetTaskResponse, error) { + return c.GetTaskWithContext(ctx, c.NewApiGetTaskRequest(indexName, taskID), opts...) + }, + func(response *GetTaskResponse, err error) bool { + if err != nil || response == nil { + return false + } + + return response.Status == TASKSTATUS_PUBLISHED + }, + maxRetries, + initialDelay, + maxDelay, + ) +} + +/* +WaitForApiKey waits for an API key to be created, deleted or updated. +It returns the API key response if the operation was successful. +It returns an error if the operation failed. + +The operation can be one of the following: + - "add": wait for the API key to be created + - "delete": wait for the API key to be deleted + - "update": wait for the API key to be updated + +If the operation is "update", the apiKey parameter must be set. +If the operation is "delete" or "add", the apiKey parameter is not used. + + @param operation ApiKeyOperation - Operation type - add, delete or update. + @param key string - API key. + @param apiKey *ApiKey - API key structure - required for update operation. + @param opts ...Option - Optional parameters for the request. + @return *GetApiKeyResponse - API key response. + @return error - Error if any. +*/ +func (c *APIClient) WaitForApiKey( + operation ApiKeyOperation, + key string, + apiKey *ApiKey, + opts ...Option, +) (*GetApiKeyResponse, error) { + return c.WaitForApiKeyWithContext( + context.Background(), + operation, + key, + apiKey, + nil, + nil, + nil, + opts..., + ) +} + +/* +WaitForApiKey waits for an API key to be created, deleted or updated. +Wraps WaitForApiKeyWithContext with context.Background(). +It returns the API key response if the operation was successful. +It returns an error if the operation failed. + +The operation can be one of the following: + - "add": wait for the API key to be created + - "delete": wait for the API key to be deleted + - "update": wait for the API key to be updated + +The maxRetries parameter is the maximum number of retries. +The initialDelay parameter is the initial delay between each retry. +The maxDelay parameter is the maximum delay between each retry. + +If the operation is "update", the apiKey parameter must be set. +If the operation is "delete" or "add", the apiKey parameter is not used. + + @param operation ApiKeyOperation - Operation type - add, delete or update. + @param key string - API key. + @param apiKey *ApiKey - API key structure - required for update operation. + @param maxRetries *int - Maximum number of retries. + @param initialDelay *time.Duration - Initial delay between retries. + @param maxDelay *time.Duration - Maximum delay between retries. + @param opts ...Option - Optional parameters for the request. + @return *GetApiKeyResponse - API key response. + @return error - Error if any. +*/ +func (c *APIClient) WaitForApiKeyWitOptions( + operation ApiKeyOperation, + key string, + apiKey *ApiKey, + maxRetries *int, + initialDelay *time.Duration, + maxDelay *time.Duration, + opts ...Option, +) (*GetApiKeyResponse, error) { + return c.WaitForApiKeyWithContext( + context.Background(), + operation, + key, + apiKey, + maxRetries, + initialDelay, + maxDelay, + opts..., + ) +} + +/* +WaitForApiKeyWithContext waits for an API key to be created, deleted or updated. +It returns the API key response if the operation was successful. +It returns an error if the operation failed. + +The operation can be one of the following: + - "add": wait for the API key to be created + - "delete": wait for the API key to be deleted + - "update": wait for the API key to be updated + +The maxRetries parameter is the maximum number of retries. +The initialDelay parameter is the initial delay between each retry. +The maxDelay parameter is the maximum delay between each retry. + +If the operation is "update", the apiKey parameter must be set. +If the operation is "delete" or "add", the apiKey parameter is not used. + + @param ctx context.Context - The context that will be drilled down to the actual request. + @param operation ApiKeyOperation - Operation type - add, delete or update. + @param key string - API key. + @param apiKey *ApiKey - API key structure - required for update operation. + @param maxRetries *int - Maximum number of retries. + @param initialDelay *time.Duration - Initial delay between retries. + @param maxDelay *time.Duration - Maximum delay between retries. + @param opts ...Option - Optional parameters for the request. + @return *GetApiKeyResponse - API key response. + @return error - Error if any. +*/ +func (c *APIClient) WaitForApiKeyWithContext( + ctx context.Context, + operation ApiKeyOperation, + key string, + apiKey *ApiKey, + maxRetries *int, + initialDelay *time.Duration, + maxDelay *time.Duration, + opts ...Option, +) (*GetApiKeyResponse, error) { + if operation != APIKEYOPERATION_ADD && operation != APIKEYOPERATION_DELETE && operation != APIKEYOPERATION_UPDATE { + return nil, &errs.WaitKeyOperationError{} + } + + if operation == APIKEYOPERATION_UPDATE { + if apiKey == nil { + return nil, &errs.WaitKeyUpdateError{} + } + + return utils.RetryUntil( //nolint:wrapcheck + func() (*GetApiKeyResponse, error) { + return c.GetApiKeyWithContext(ctx, c.NewApiGetApiKeyRequest(key), opts...) + }, + func(response *GetApiKeyResponse, err error) bool { + if err != nil || response == nil { + return false + } + + if apiKey.GetDescription() != response.GetDescription() { + return false + } + + if apiKey.GetQueryParameters() != response.GetQueryParameters() { + return false + } + + if apiKey.GetMaxHitsPerQuery() != response.GetMaxHitsPerQuery() { + return false + } + + if apiKey.GetMaxQueriesPerIPPerHour() != response.GetMaxQueriesPerIPPerHour() { + return false + } + + if apiKey.GetValidity() != response.GetValidity() { + return false + } + + slices.Sort(apiKey.Acl) + slices.Sort(response.Acl) + + if !slices.Equal(apiKey.Acl, response.Acl) { + return false + } + + slices.Sort(apiKey.Indexes) + slices.Sort(response.Indexes) + + if !slices.Equal(apiKey.Indexes, response.Indexes) { + return false + } + + slices.Sort(apiKey.Referers) + slices.Sort(response.Referers) + + return slices.Equal(apiKey.Referers, response.Referers) + }, + maxRetries, + initialDelay, + maxDelay, + ) + } + + return utils.RetryUntil( //nolint:wrapcheck + func() (*GetApiKeyResponse, error) { + return c.GetApiKeyWithContext(ctx, c.NewApiGetApiKeyRequest(key), opts...) + }, + func(response *GetApiKeyResponse, err error) bool { + switch operation { + case APIKEYOPERATION_ADD: + return err == nil && response != nil && response.CreatedAt > 0 + case APIKEYOPERATION_DELETE: + if _, ok := err.(*APIError); ok { + apiErr := err.(*APIError) + + return apiErr.Status == 404 + } + + return false + } + return false + }, + maxRetries, + initialDelay, + maxDelay, + ) +} diff --git a/templates/go/tests/requests/requests.mustache b/templates/go/tests/requests/requests.mustache index 6533b1c2df..b5e3d4fdf9 100644 --- a/templates/go/tests/requests/requests.mustache +++ b/templates/go/tests/requests/requests.mustache @@ -56,6 +56,7 @@ func createE2E{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}Client(t {{#blocksRequests}} func Test{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}_{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}(t *testing.T) { client, echo := create{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}Client(t) + _ = echo {{#tests}} t.Run("{{{testName}}}", func(t *testing.T) { @@ -66,36 +67,38 @@ func Test{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}_{{#lambda.ti {{/requestOptions}}) require.NoError(t, err) - require.Equal(t, "{{{request.path}}}", echo.Path) - require.Equal(t, "{{{request.method}}}", echo.Method) + {{#request}} + require.Equal(t, "{{path}}", echo.Path) + require.Equal(t, "{{{method}}}", echo.Method) - {{#request.body}} + {{#body}} ja := jsonassert.New(t) - ja.Assertf(*echo.Body, `{{{request.body}}}`) - {{/request.body}} - {{^request.body}} + ja.Assertf(*echo.Body, `{{{.}}}`) + {{/body}} + {{^body}} {{#assertNullBody}} require.Nil(t, echo.Body) {{/assertNullBody}} {{^assertNullBody}} require.Empty(t, echo.Body); {{/assertNullBody}} - {{/request.body}} - {{#request.headers}} + {{/body}} + {{#headers}} headers := map[string]string{} require.NoError(t, json.Unmarshal([]byte(`{{{.}}}`), &headers)) for k, v := range headers { require.Equal(t, v, echo.Header.Get(k)) } - {{/request.headers}} - {{#request.queryParameters}} + {{/headers}} + {{#queryParameters}} queryParams := map[string]string{} require.NoError(t, json.Unmarshal([]byte(`{{{.}}}`), &queryParams)) require.Len(t, queryParams, len(echo.Query)) for k, v := range queryParams { require.Equal(t, v, echo.Query.Get(k)) } - {{/request.queryParameters}} + {{/queryParameters}} + {{/request}} {{#response}} clientE2E := createE2E{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}Client(t) res, err := clientE2E.{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}({{#hasOperationParams}}client.NewApi{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}Request( diff --git a/templates/java/api_helpers.mustache b/templates/java/api_helpers.mustache index 2ea4cdd094..b0692cdeb3 100644 --- a/templates/java/api_helpers.mustache +++ b/templates/java/api_helpers.mustache @@ -141,7 +141,6 @@ public GetApiKeyResponse waitForApiKey( public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, int maxRetries, IntUnaryOperator timeout, RequestOptions requestOptions) { return this.waitForApiKey(operation, key, null, maxRetries, timeout, requestOptions); } - /** * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`. * @@ -153,7 +152,6 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, in public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, ApiKey apiKey, RequestOptions requestOptions) { return this.waitForApiKey(operation, key, apiKey, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, requestOptions); } - /** * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`. * @@ -164,7 +162,6 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, Ap public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, RequestOptions requestOptions) { return this.waitForApiKey(operation, key, null, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, requestOptions); } - /** * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`. * @@ -177,7 +174,6 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, Re public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, ApiKey apiKey, int maxRetries, IntUnaryOperator timeout) { return this.waitForApiKey(operation, key, apiKey, maxRetries, timeout, null); } - /** * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`. * @@ -189,7 +185,6 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, Ap public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, int maxRetries, IntUnaryOperator timeout) { return this.waitForApiKey(operation, key, null, maxRetries, timeout, null); } - /** * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`. * @@ -200,7 +195,6 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, in public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, ApiKey apiKey) { return this.waitForApiKey(operation, key, apiKey, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, null); } - /** * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`. * @@ -480,4 +474,4 @@ public CompletableFuture> searchForFacetsAsyn .collect(Collectors.toList()) ); } -{{/isSearchClient}} \ No newline at end of file +{{/isSearchClient}} diff --git a/templates/java/tests/requests/requests.mustache b/templates/java/tests/requests/requests.mustache index c6829ca2ed..294003aa80 100644 --- a/templates/java/tests/requests/requests.mustache +++ b/templates/java/tests/requests/requests.mustache @@ -71,8 +71,8 @@ class {{client}}RequestsTests { {{/requestOptions.headers.parametersWithDataType}} {{/hasRequestOptions}}); }); - EchoResponse req = echo.getLastResponse(); {{#request}} + EchoResponse req = echo.getLastResponse(); assertEquals("{{{path}}}", req.path); assertEquals("{{{method}}}", req.method); {{#body}} diff --git a/templates/javascript/clients/client/model/clientMethodProps.mustache b/templates/javascript/clients/client/model/clientMethodProps.mustache index 0ff4eaba75..4d7961a5bd 100644 --- a/templates/javascript/clients/client/model/clientMethodProps.mustache +++ b/templates/javascript/clients/client/model/clientMethodProps.mustache @@ -85,14 +85,14 @@ export type WaitForApiKeyOptions = WaitForOptions & { /** * The operation that has been performed, used to compute the stop condition. */ - operation: 'add' | 'delete'; + operation: Extract; apiKey?: never; } | { /** * The operation that has been performed, used to compute the stop condition. */ - operation: 'update'; + operation: Extract; /** * The updated fields, used to compute the stop condition. */ @@ -109,7 +109,7 @@ export type GenerateSecuredApiKeyOptions = { /** * A set of properties defining the restrictions of the secured API key. */ - restrictions?: SecuredApiKeyRestrictions; + restrictions?: SecuredAPIKeyRestrictions; } export type GetSecuredApiKeyRemainingValidityOptions = { @@ -119,30 +119,6 @@ export type GetSecuredApiKeyRemainingValidityOptions = { securedApiKey: string; } -export type SecuredApiKeyRestrictions = { - /** - * A Unix timestamp used to define the expiration date of the API key. - */ - validUntil?: number; - - /** - * List of index names that can be queried. - */ - restrictIndices?: string[] | string; - - /** - * IPv4 network allowed to use the generated key. This is used for more protection against API key leaking and reuse. - */ - restrictSources?: string; - - /** - * Specify a user identifier. This is often used with rate limits. - */ - userToken?: string; - - searchParams?: SearchParamsObject; -}; - export type ChunkedBatchOptions = ReplaceAllObjectsOptions & { /** * The `batch` `action` to perform on the given array of `objects`, defaults to `addObject`. @@ -190,4 +166,4 @@ export type ReplaceAllObjectsResponse = { } {{/isSearchClient}} -{{/apiInfo.apis.0}} \ No newline at end of file +{{/apiInfo.apis.0}} diff --git a/templates/javascript/tests/requests/helpers.mustache b/templates/javascript/tests/requests/helpers.mustache index 2dcb8f61bc..59c7d673c9 100644 --- a/templates/javascript/tests/requests/helpers.mustache +++ b/templates/javascript/tests/requests/helpers.mustache @@ -12,7 +12,7 @@ describe('generateSecuredApiKey', () => { restrictions: { validUntil: 100, restrictIndices: ['bar'], - restrictSources: 'baz', + restrictSources: '192,168.1.0/24', userToken: 'foobarbaz', searchParams: { query: 'foo', @@ -20,7 +20,7 @@ describe('generateSecuredApiKey', () => { }, }); expect(resp).toEqual( - 'YzY5YmVhMGVhYWYyYjNhYzMxYmVmNDA2Y2NiNjZhZTk2MDFkYjMxZGEyOGRlMDg5ZDFmNzBlYTBjMjY3Zjg3ZnZhbGlkVW50aWw9MTAwJnJlc3RyaWN0SW5kaWNlcz0lNUIlMjJiYXIlMjIlNUQmcmVzdHJpY3RTb3VyY2VzPWJheiZ1c2VyVG9rZW49Zm9vYmFyYmF6JnNlYXJjaFBhcmFtcz0lN0IlMjJxdWVyeSUyMiUzQSUyMmZvbyUyMiU3RA==' + 'M2RlY2Y5ZjgzMDU1ZDRiYjkyOTdjYjYxYWNjNWNhNTQ5ZGI5Mjc3ZmVjNmNmNjM2ZjAwMTA4OGRjNDI5YjFhOXZhbGlkVW50aWw9MTAwJnJlc3RyaWN0SW5kaWNlcz0lNUIlMjJiYXIlMjIlNUQmcmVzdHJpY3RTb3VyY2VzPTE5MiUyQzE2OC4xLjAlMkYyNCZ1c2VyVG9rZW49Zm9vYmFyYmF6JnNlYXJjaFBhcmFtcz0lN0IlMjJxdWVyeSUyMiUzQSUyMmZvbyUyMiU3RA==' ); }); }); diff --git a/templates/python/api.mustache b/templates/python/api.mustache index be5b2355dc..7b884e94e5 100644 --- a/templates/python/api.mustache +++ b/templates/python/api.mustache @@ -5,6 +5,7 @@ from algoliasearch.search.models.batch_request import BatchRequest from algoliasearch.search.models.scope_type import ScopeType from algoliasearch.search.models.action import Action +from algoliasearch.search.models.secured_api_key_restrictions import SecuredAPIKeyRestrictions {{/isSearchClient}} {{#operations}}{{#operation}}{{#imports}} @@ -183,4 +184,4 @@ class {{classname}}: return (await self.{{operationId}}_with_http_info({{#allParams}}{{paramName}},{{/allParams}}request_options)).deserialize({{{returnType}}}) {{/operation}} -{{/operations}} \ No newline at end of file +{{/operations}} diff --git a/templates/python/imports.mustache b/templates/python/imports.mustache index c7059f1600..2b6b5a1d53 100644 --- a/templates/python/imports.mustache +++ b/templates/python/imports.mustache @@ -39,7 +39,7 @@ from typing import ( Union, ) -from algoliasearch.http.helpers import create_iterable, RetryTimeout, SecuredApiKeyRestrictions +from algoliasearch.http.helpers import create_iterable, RetryTimeout from algoliasearch.http.serializer import bodySerializer, QueryParametersSerializer from algoliasearch.http.api_response import ApiResponse from algoliasearch.http.request_options import RequestOptions diff --git a/templates/python/search_helpers.mustache b/templates/python/search_helpers.mustache index 83c632a9dc..22402d7fc1 100644 --- a/templates/python/search_helpers.mustache +++ b/templates/python/search_helpers.mustache @@ -163,7 +163,7 @@ def generate_secured_api_key( self, parent_api_key: str, - restrictions: Optional[SecuredApiKeyRestrictions] = SecuredApiKeyRestrictions(), + restrictions: Optional[SecuredAPIKeyRestrictions] = SecuredAPIKeyRestrictions(), ) -> str: """ Helper: Generates a secured API key based on the given `parent_api_key` and given `restrictions`. @@ -171,7 +171,7 @@ query_parameters = dumps( QueryParametersSerializer( restrictions.to_dict() - if isinstance(restrictions, SecuredApiKeyRestrictions) + if isinstance(restrictions, SecuredAPIKeyRestrictions) else restrictions ).query_parameters ) @@ -292,4 +292,4 @@ "copy_operation_response": copy_operation_response, "batch_responses": batch_responses, "move_operation_response": move_operation_response, - } \ No newline at end of file + } diff --git a/templates/python/tests/requests/helpers.mustache b/templates/python/tests/requests/helpers.mustache index 4b39ddc12f..07e89f451c 100644 --- a/templates/python/tests/requests/helpers.mustache +++ b/templates/python/tests/requests/helpers.mustache @@ -3,7 +3,7 @@ def test_generate_secured_api_key_0(self): allow generating a secured api key without restrictions """ _resp = self._client.generate_secured_api_key(parent_api_key="foo") - assert _resp == "Y2M1MGVkOGY0Yjg2YzE4MDJjYTMwOGU2YTMzNjg1MTA4Y2UwNjkwZmNjODMzYzhjN2FhNGE3M2FhZTdiMjhiZnsic2VhcmNoX3BhcmFtcyI6ICJ7XCJxdWVyeVwiOiBcIlwiLCBcInNpbWlsYXJRdWVyeVwiOiBcIlwiLCBcImZpbHRlcnNcIjogXCJcIiwgXCJzdW1PckZpbHRlcnNTY29yZXNcIjogZmFsc2UsIFwiZmFjZXRpbmdBZnRlckRpc3RpbmN0XCI6IGZhbHNlLCBcInBhZ2VcIjogMCwgXCJhcm91bmRMYXRMbmdcIjogXCJcIiwgXCJhcm91bmRMYXRMbmdWaWFJUFwiOiBmYWxzZSwgXCJwZXJzb25hbGl6YXRpb25JbXBhY3RcIjogMTAwLCBcImdldFJhbmtpbmdJbmZvXCI6IGZhbHNlLCBcInN5bm9ueW1zXCI6IHRydWUsIFwiY2xpY2tBbmFseXRpY3NcIjogZmFsc2UsIFwiYW5hbHl0aWNzXCI6IHRydWUsIFwicGVyY2VudGlsZUNvbXB1dGF0aW9uXCI6IHRydWUsIFwiZW5hYmxlQUJUZXN0XCI6IHRydWUsIFwicmVsZXZhbmN5U3RyaWN0bmVzc1wiOiAxMDAsIFwiaGlnaGxpZ2h0UHJlVGFnXCI6IFwiPGVtPlwiLCBcImhpZ2hsaWdodFBvc3RUYWdcIjogXCI8L2VtPlwiLCBcInNuaXBwZXRFbGxpcHNpc1RleHRcIjogXCJcXHUyMDI2XCIsIFwicmVzdHJpY3RIaWdobGlnaHRBbmRTbmlwcGV0QXJyYXlzXCI6IGZhbHNlLCBcImhpdHNQZXJQYWdlXCI6IDIwLCBcIm1pbldvcmRTaXplZm9yMVR5cG9cIjogNCwgXCJtaW5Xb3JkU2l6ZWZvcjJUeXBvc1wiOiA4LCBcImFsbG93VHlwb3NPbk51bWVyaWNUb2tlbnNcIjogdHJ1ZSwgXCJrZWVwRGlhY3JpdGljc09uQ2hhcmFjdGVyc1wiOiBcIlwiLCBcImRlY29tcG91bmRRdWVyeVwiOiB0cnVlLCBcImVuYWJsZVJ1bGVzXCI6IHRydWUsIFwiZW5hYmxlUGVyc29uYWxpemF0aW9uXCI6IGZhbHNlLCBcImFkdmFuY2VkU3ludGF4XCI6IGZhbHNlLCBcInJlcGxhY2VTeW5vbnltc0luSGlnaGxpZ2h0XCI6IGZhbHNlLCBcIm1pblByb3hpbWl0eVwiOiAxLCBcIm1heEZhY2V0SGl0c1wiOiAxMCwgXCJtYXhWYWx1ZXNQZXJGYWNldFwiOiAxMDAsIFwic29ydEZhY2V0VmFsdWVzQnlcIjogXCJjb3VudFwiLCBcImF0dHJpYnV0ZUNyaXRlcmlhQ29tcHV0ZWRCeU1pblByb3hpbWl0eVwiOiBmYWxzZSwgXCJlbmFibGVSZVJhbmtpbmdcIjogdHJ1ZX0iLCAidmFsaWRfdW50aWwiOiAiMCJ9" + assert _resp == "Yzc2MzU2ZWZhMTlkMjE5ZDFkN2UwOGNjYjIwYjFkMjZkYjUzYjE0MzE1NmY0MDZjOTlkY2I4ZTA4NzZkNmM1NXt9" def test_generate_secured_api_key_1(self): """ @@ -22,20 +22,20 @@ def test_generate_secured_api_key_0(self): """ allow generating a secured api key with from the model """ - _resp = self._client.generate_secured_api_key(parent_api_key="bar", restrictions=SecuredApiKeyRestrictions( + _resp = self._client.generate_secured_api_key(parent_api_key="bar", restrictions=SecuredAPIKeyRestrictions( search_params={"query": "bar", "page": 3}, valid_until=42, restrict_indices=["baz"], restrict_sources="foo", user_token="bazbarfoo", )) - assert _resp == "NmY4NDZkZWM2OWI0YjRkYzdhNjgzOWY3NmQ3ZGRhNjNkYzBjZWRjNTYxY2NkOWEyOGQxNmEyOGNjMWIyYzJkYnsic2VhcmNoX3BhcmFtcyI6ICJ7XCJxdWVyeVwiOiBcImJhclwiLCBcInNpbWlsYXJRdWVyeVwiOiBcIlwiLCBcImZpbHRlcnNcIjogXCJcIiwgXCJzdW1PckZpbHRlcnNTY29yZXNcIjogZmFsc2UsIFwiZmFjZXRpbmdBZnRlckRpc3RpbmN0XCI6IGZhbHNlLCBcInBhZ2VcIjogMywgXCJhcm91bmRMYXRMbmdcIjogXCJcIiwgXCJhcm91bmRMYXRMbmdWaWFJUFwiOiBmYWxzZSwgXCJwZXJzb25hbGl6YXRpb25JbXBhY3RcIjogMTAwLCBcImdldFJhbmtpbmdJbmZvXCI6IGZhbHNlLCBcInN5bm9ueW1zXCI6IHRydWUsIFwiY2xpY2tBbmFseXRpY3NcIjogZmFsc2UsIFwiYW5hbHl0aWNzXCI6IHRydWUsIFwicGVyY2VudGlsZUNvbXB1dGF0aW9uXCI6IHRydWUsIFwiZW5hYmxlQUJUZXN0XCI6IHRydWUsIFwicmVsZXZhbmN5U3RyaWN0bmVzc1wiOiAxMDAsIFwiaGlnaGxpZ2h0UHJlVGFnXCI6IFwiPGVtPlwiLCBcImhpZ2hsaWdodFBvc3RUYWdcIjogXCI8L2VtPlwiLCBcInNuaXBwZXRFbGxpcHNpc1RleHRcIjogXCJcXHUyMDI2XCIsIFwicmVzdHJpY3RIaWdobGlnaHRBbmRTbmlwcGV0QXJyYXlzXCI6IGZhbHNlLCBcImhpdHNQZXJQYWdlXCI6IDIwLCBcIm1pbldvcmRTaXplZm9yMVR5cG9cIjogNCwgXCJtaW5Xb3JkU2l6ZWZvcjJUeXBvc1wiOiA4LCBcImFsbG93VHlwb3NPbk51bWVyaWNUb2tlbnNcIjogdHJ1ZSwgXCJrZWVwRGlhY3JpdGljc09uQ2hhcmFjdGVyc1wiOiBcIlwiLCBcImRlY29tcG91bmRRdWVyeVwiOiB0cnVlLCBcImVuYWJsZVJ1bGVzXCI6IHRydWUsIFwiZW5hYmxlUGVyc29uYWxpemF0aW9uXCI6IGZhbHNlLCBcImFkdmFuY2VkU3ludGF4XCI6IGZhbHNlLCBcInJlcGxhY2VTeW5vbnltc0luSGlnaGxpZ2h0XCI6IGZhbHNlLCBcIm1pblByb3hpbWl0eVwiOiAxLCBcIm1heEZhY2V0SGl0c1wiOiAxMCwgXCJtYXhWYWx1ZXNQZXJGYWNldFwiOiAxMDAsIFwic29ydEZhY2V0VmFsdWVzQnlcIjogXCJjb3VudFwiLCBcImF0dHJpYnV0ZUNyaXRlcmlhQ29tcHV0ZWRCeU1pblByb3hpbWl0eVwiOiBmYWxzZSwgXCJlbmFibGVSZVJhbmtpbmdcIjogdHJ1ZX0iLCAidmFsaWRfdW50aWwiOiAiNDIiLCAicmVzdHJpY3RfaW5kaWNlcyI6ICJiYXoiLCAicmVzdHJpY3Rfc291cmNlcyI6ICJmb28iLCAidXNlcl90b2tlbiI6ICJiYXpiYXJmb28ifQ==" + assert _resp == "Y2ZhNWM0Y2MxNjc1NTE2YjhiZjdlMGU5YWE1OGViOTk5MTdjMGU1YjRhNDU2NTczOWI5ZGE5Y2NjMTJmMDE0YXsic2VhcmNoUGFyYW1zIjogIntcInF1ZXJ5XCI6IFwiYmFyXCIsIFwic2ltaWxhclF1ZXJ5XCI6IFwiXCIsIFwiZmlsdGVyc1wiOiBcIlwiLCBcInN1bU9yRmlsdGVyc1Njb3Jlc1wiOiBmYWxzZSwgXCJmYWNldGluZ0FmdGVyRGlzdGluY3RcIjogZmFsc2UsIFwicGFnZVwiOiAzLCBcImFyb3VuZExhdExuZ1wiOiBcIlwiLCBcImFyb3VuZExhdExuZ1ZpYUlQXCI6IGZhbHNlLCBcInBlcnNvbmFsaXphdGlvbkltcGFjdFwiOiAxMDAsIFwiZ2V0UmFua2luZ0luZm9cIjogZmFsc2UsIFwic3lub255bXNcIjogdHJ1ZSwgXCJjbGlja0FuYWx5dGljc1wiOiBmYWxzZSwgXCJhbmFseXRpY3NcIjogdHJ1ZSwgXCJwZXJjZW50aWxlQ29tcHV0YXRpb25cIjogdHJ1ZSwgXCJlbmFibGVBQlRlc3RcIjogdHJ1ZSwgXCJyZWxldmFuY3lTdHJpY3RuZXNzXCI6IDEwMCwgXCJoaWdobGlnaHRQcmVUYWdcIjogXCI8ZW0+XCIsIFwiaGlnaGxpZ2h0UG9zdFRhZ1wiOiBcIjwvZW0+XCIsIFwic25pcHBldEVsbGlwc2lzVGV4dFwiOiBcIlxcdTIwMjZcIiwgXCJyZXN0cmljdEhpZ2hsaWdodEFuZFNuaXBwZXRBcnJheXNcIjogZmFsc2UsIFwiaGl0c1BlclBhZ2VcIjogMjAsIFwibWluV29yZFNpemVmb3IxVHlwb1wiOiA0LCBcIm1pbldvcmRTaXplZm9yMlR5cG9zXCI6IDgsIFwiYWxsb3dUeXBvc09uTnVtZXJpY1Rva2Vuc1wiOiB0cnVlLCBcImtlZXBEaWFjcml0aWNzT25DaGFyYWN0ZXJzXCI6IFwiXCIsIFwiZGVjb21wb3VuZFF1ZXJ5XCI6IHRydWUsIFwiZW5hYmxlUnVsZXNcIjogdHJ1ZSwgXCJlbmFibGVQZXJzb25hbGl6YXRpb25cIjogZmFsc2UsIFwiYWR2YW5jZWRTeW50YXhcIjogZmFsc2UsIFwicmVwbGFjZVN5bm9ueW1zSW5IaWdobGlnaHRcIjogZmFsc2UsIFwibWluUHJveGltaXR5XCI6IDEsIFwibWF4RmFjZXRIaXRzXCI6IDEwLCBcIm1heFZhbHVlc1BlckZhY2V0XCI6IDEwMCwgXCJzb3J0RmFjZXRWYWx1ZXNCeVwiOiBcImNvdW50XCIsIFwiYXR0cmlidXRlQ3JpdGVyaWFDb21wdXRlZEJ5TWluUHJveGltaXR5XCI6IGZhbHNlLCBcImVuYWJsZVJlUmFua2luZ1wiOiB0cnVlfSIsICJ2YWxpZFVudGlsIjogIjQyIiwgInJlc3RyaWN0SW5kaWNlcyI6ICJiYXoiLCAicmVzdHJpY3RTb3VyY2VzIjogImZvbyIsICJ1c2VyVG9rZW4iOiAiYmF6YmFyZm9vIn0=" def test_generate_secured_api_key_and_validity_0(self): """ is able to check the remaining validity of a key """ - _resp = self._client.generate_secured_api_key(parent_api_key="foo") + _resp = self._client.generate_secured_api_key(parent_api_key="foo", restrictions={"valid_until": 0}) _validity = self._client.get_secured_api_key_remaining_validity(_resp) assert abs(_validity) == int(round(time())) @@ -168,4 +168,4 @@ def test_generate_secured_api_key_0(self): "move_operation_response": UpdatedAtResponse( task_id=21, updated_at="foobar" ), - } \ No newline at end of file + } diff --git a/templates/python/tests/requests/requests.mustache b/templates/python/tests/requests/requests.mustache index afac751d1c..504fda0b3e 100644 --- a/templates/python/tests/requests/requests.mustache +++ b/templates/python/tests/requests/requests.mustache @@ -3,7 +3,7 @@ from os import environ from json import loads from unittest.mock import AsyncMock from algoliasearch.http.transporter import EchoTransporter -from algoliasearch.http.helpers import SecuredApiKeyRestrictions +from algoliasearch.search.models.secured_api_key_restrictions import SecuredAPIKeyRestrictions from algoliasearch.{{{import}}}.client import {{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}} from algoliasearch.{{{import}}}.config import {{#lambda.pascalcase}}{{clientPrefix}}Config{{/lambda.pascalcase}} from algoliasearch.search.models.batch_response import BatchResponse @@ -73,4 +73,4 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}{{#isE2E}}E2E{ {{#isSearchClient}} {{> tests/requests/helpers}} - {{/isSearchClient}} \ No newline at end of file + {{/isSearchClient}} diff --git a/tests/output/csharp/src/SecuredApiKeysTests.cs b/tests/output/csharp/src/SecuredApiKeysTests.cs index 297146a079..58025fddad 100644 --- a/tests/output/csharp/src/SecuredApiKeysTests.cs +++ b/tests/output/csharp/src/SecuredApiKeysTests.cs @@ -17,9 +17,9 @@ public void ShouldGenerateSecuredApiKey() var client = new SearchClient(new SearchConfig("test-app-id", "test-api-key")); var securedApiKey = client.GenerateSecuredApiKey( "parent-api-key", - new SecuredApiKeyRestriction + new SecuredAPIKeyRestrictions { - Query = new IndexSettingsAsSearchParams + SearchParams = new SearchParamsObject { Mode = Mode.NeuralSearch, HitsPerPage = 10, @@ -34,14 +34,14 @@ public void ShouldGenerateSecuredApiKey() AttributeCriteriaComputedByMinProximity = false }, RestrictIndices = ["index1", "index2"], - RestrictSources = ["source1", "source2"], + RestrictSources = "192.168.1.0/24", UserToken = "my-user-token", ValidUntil = 1 } ); const string expectedQueryParams = - "queryType=prefixNone&mode=neuralSearch&hitsPerPage=10&enableRules=true&optionalWords=one%2Ctwo&alternativesAsExact=ignorePlurals%2CsingleWordSynonym&attributeCriteriaComputedByMinProximity=false&validUntil=1&restrictIndices=index1%2Cindex2&restrictSources=source1%2Csource2&userToken=my-user-token"; + "queryType=prefixNone&mode=neuralSearch&hitsPerPage=10&enableRules=true&optionalWords=one%2Ctwo&alternativesAsExact=ignorePlurals%2CsingleWordSynonym&attributeCriteriaComputedByMinProximity=false&validUntil=1&restrictIndices=index1%2Cindex2&restrictSources=192.168.1.0%2F24&userToken=my-user-token"; var hash = HmacShaHelper.GetHash("parent-api-key", expectedQueryParams); var expectedKey = HmacShaHelper.Base64Encode($"{hash}{expectedQueryParams}"); @@ -52,7 +52,7 @@ public void ShouldGenerateSecuredApiKey() public void ShouldDetectExpiredKey() { // Test an expired key - var restriction = new SecuredApiKeyRestriction + var restriction = new SecuredAPIKeyRestrictions { ValidUntil = new DateTimeOffset(DateTime.UtcNow.AddMinutes(-10)).ToUnixTimeSeconds(), RestrictIndices = ["indexName"] @@ -73,7 +73,7 @@ public void ShouldDetectExpiredKey() public void ShouldDetectValidKey() { // Test a valid key - var restriction = new SecuredApiKeyRestriction + var restriction = new SecuredAPIKeyRestrictions { ValidUntil = new DateTimeOffset(DateTime.UtcNow.AddMinutes(10)).ToUnixTimeSeconds(), RestrictIndices = ["indexName"] @@ -94,7 +94,7 @@ public void ShouldDetectValidKey() public void TestRemainingValidityParameters() { // Test a valid key, but with no validUntil - var restriction = new SecuredApiKeyRestriction { RestrictIndices = ["indexName"] }; + var restriction = new SecuredAPIKeyRestrictions { RestrictIndices = ["indexName"] }; var client = new SearchClient( new SearchConfig("test-app-id", "test-api-key"), diff --git a/tests/output/swift/Tests/handwritten/SecuredAPIKeyHelpers.swift b/tests/output/swift/Tests/handwritten/SecuredAPIKeyHelpers.swift index b89acf569a..dd58d2bfc4 100644 --- a/tests/output/swift/Tests/handwritten/SecuredAPIKeyHelpers.swift +++ b/tests/output/swift/Tests/handwritten/SecuredAPIKeyHelpers.swift @@ -10,8 +10,8 @@ import XCTest @testable import Core @testable import Search -class SecuredAPIKeyHelpersTests: XCTestCase { - func testGenerateSecuredAPIKeySuccess() async throws { +class SecuredApiKeyHelpersTests: XCTestCase { + func testGenerateSecuredApiKeySuccess() async throws { let client = try SearchClient(appID: "my-app-id", apiKey: "my-api-key") let securedApiKey = try client.generateSecuredApiKey( @@ -36,9 +36,10 @@ class SecuredAPIKeyHelpersTests: XCTestCase { let securedApiKey = try client.generateSecuredApiKey( parentApiKey: "parent-api-key", - with: SecuredAPIKeyRestriction( + with: SecuredAPIKeyRestrictions( searchParams: SearchParamsObject(hitsPerPage: 2), - validUntil: now + .seconds(13) + validUntil: now + .seconds(13), + restrictIndices: ["index1", "index2"] ) ) From 1348364020db9f7af8767ecb7024d86392cd75bd Mon Sep 17 00:00:00 2001 From: algolia-bot Date: Tue, 27 Feb 2024 16:50:07 +0000 Subject: [PATCH 2/3] chore: generated code for commit ff405534ce25391f1a3db680fe646c3da82b966d. [skip ci] Co-authored-by: Pierre Millot --- .../Models/Search/ApiKeyOperation.cs | 37 ++ .../Search/SecuredAPIKeyRestrictions.cs | 150 +++++++ .../algoliasearch/lib/algoliasearch_lite.dart | 2 + .../algoliasearch/lib/src/deserialize.dart | 7 + .../lib/src/model/api_key_operation.dart | 25 ++ .../model/secured_api_key_restrictions.dart | 73 ++++ .../model/secured_api_key_restrictions.g.dart | 50 +++ .../lib/algolia_client_search.dart | 2 + .../client_search/lib/src/deserialize.dart | 7 + .../lib/src/model/api_key_operation.dart | 25 ++ .../model/secured_api_key_restrictions.dart | 73 ++++ .../model/secured_api_key_restrictions.g.dart | 50 +++ .../algolia/search/api_search.go | 58 ++- .../algolia/search/model_api_key_operation.go | 103 +++++ .../model_secured_api_key_restrictions.go | 350 ++++++++++++++++ .../algolia/model/search/ApiKeyOperation.java | 42 ++ .../search/SecuredAPIKeyRestrictions.java | 175 ++++++++ .../lite/model/apiKeyOperation.ts | 3 + .../algoliasearch/lite/model/index.ts | 2 + .../lite/model/securedAPIKeyRestrictions.ts | 32 ++ .../client-search/model/apiKeyOperation.ts | 3 + .../client-search/model/clientMethodProps.ts | 32 +- .../packages/client-search/model/index.ts | 2 + .../model/securedAPIKeyRestrictions.ts | 32 ++ .../client/model/search/ApiKeyOperation.kt | 19 + .../model/search/SecuredAPIKeyRestrictions.kt | 36 ++ .../lib/Model/Search/ApiKeyOperation.php | 36 ++ .../Search/SecuredAPIKeyRestrictions.php | 383 ++++++++++++++++++ .../algoliasearch/search/client.py | 13 +- .../search/models/api_key_operation.py | 28 ++ .../models/secured_api_key_restrictions.py | 99 +++++ .../models/search/api_key_operation.rb | 34 ++ .../search/secured_api_key_restrictions.rb | 248 ++++++++++++ .../algoliasearch/api/SearchClient.scala | 2 + .../search/ApiKeyOperation.scala | 48 +++ .../algoliasearch/search/JsonSupport.scala | 1 + .../search/SecuredAPIKeyRestrictions.scala | 45 ++ .../SecuredApiKeyRestrictionExtension.swift | 4 +- .../Search/Models/ApiKeyOperation.swift | 12 + .../Models/SecuredAPIKeyRestrictions.swift | 68 ++++ specs/bundled/algoliasearch.yml | 57 +++ specs/bundled/search.doc.yml | 57 +++ specs/bundled/search.yml | 141 +++++++ .../go/tests/requests/abtesting_test.go | 9 + .../go/tests/requests/analytics_test.go | 21 + .../go/tests/requests/ingestion_test.go | 37 ++ .../output/go/tests/requests/insights_test.go | 6 + .../go/tests/requests/monitoring_test.go | 13 + .../go/tests/requests/personalization_test.go | 8 + .../tests/requests/query-suggestions_test.go | 11 + .../go/tests/requests/recommend_test.go | 9 + tests/output/go/tests/requests/search_test.go | 61 +++ .../javascript/src/requests/search.test.ts | 4 +- .../python/tests/requests/search_test.py | 14 +- 54 files changed, 2806 insertions(+), 53 deletions(-) create mode 100644 clients/algoliasearch-client-csharp/algoliasearch/Models/Search/ApiKeyOperation.cs create mode 100644 clients/algoliasearch-client-csharp/algoliasearch/Models/Search/SecuredAPIKeyRestrictions.cs create mode 100644 clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/api_key_operation.dart create mode 100644 clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.dart create mode 100644 clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.g.dart create mode 100644 clients/algoliasearch-client-dart/packages/client_search/lib/src/model/api_key_operation.dart create mode 100644 clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.dart create mode 100644 clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.g.dart create mode 100644 clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go create mode 100644 clients/algoliasearch-client-go/algolia/search/model_secured_api_key_restrictions.go create mode 100644 clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/ApiKeyOperation.java create mode 100644 clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/SecuredAPIKeyRestrictions.java create mode 100644 clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/apiKeyOperation.ts create mode 100644 clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/securedAPIKeyRestrictions.ts create mode 100644 clients/algoliasearch-client-javascript/packages/client-search/model/apiKeyOperation.ts create mode 100644 clients/algoliasearch-client-javascript/packages/client-search/model/securedAPIKeyRestrictions.ts create mode 100644 clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/ApiKeyOperation.kt create mode 100644 clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/SecuredAPIKeyRestrictions.kt create mode 100644 clients/algoliasearch-client-php/lib/Model/Search/ApiKeyOperation.php create mode 100644 clients/algoliasearch-client-php/lib/Model/Search/SecuredAPIKeyRestrictions.php create mode 100644 clients/algoliasearch-client-python/algoliasearch/search/models/api_key_operation.py create mode 100644 clients/algoliasearch-client-python/algoliasearch/search/models/secured_api_key_restrictions.py create mode 100644 clients/algoliasearch-client-ruby/lib/algolia/models/search/api_key_operation.rb create mode 100644 clients/algoliasearch-client-ruby/lib/algolia/models/search/secured_api_key_restrictions.rb create mode 100644 clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/ApiKeyOperation.scala create mode 100644 clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/SecuredAPIKeyRestrictions.scala create mode 100644 clients/algoliasearch-client-swift/Sources/Search/Models/ApiKeyOperation.swift create mode 100644 clients/algoliasearch-client-swift/Sources/Search/Models/SecuredAPIKeyRestrictions.swift diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Models/Search/ApiKeyOperation.cs b/clients/algoliasearch-client-csharp/algoliasearch/Models/Search/ApiKeyOperation.cs new file mode 100644 index 0000000000..b483d23098 --- /dev/null +++ b/clients/algoliasearch-client-csharp/algoliasearch/Models/Search/ApiKeyOperation.cs @@ -0,0 +1,37 @@ +// +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// +using System; +using System.Text; +using System.Linq; +using System.Text.Json.Serialization; +using System.Collections.Generic; +using Algolia.Search.Serializer; +using System.Text.Json; + +namespace Algolia.Search.Models.Search; + +/// +/// Defines apiKeyOperation +/// +public enum ApiKeyOperation +{ + /// + /// Enum Add for value: add + /// + [JsonPropertyName("add")] + Add = 1, + + /// + /// Enum Delete for value: delete + /// + [JsonPropertyName("delete")] + Delete = 2, + + /// + /// Enum Update for value: update + /// + [JsonPropertyName("update")] + Update = 3 +} + diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Models/Search/SecuredAPIKeyRestrictions.cs b/clients/algoliasearch-client-csharp/algoliasearch/Models/Search/SecuredAPIKeyRestrictions.cs new file mode 100644 index 0000000000..b07ee144e0 --- /dev/null +++ b/clients/algoliasearch-client-csharp/algoliasearch/Models/Search/SecuredAPIKeyRestrictions.cs @@ -0,0 +1,150 @@ +// +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// +using System; +using System.Text; +using System.Linq; +using System.Text.Json.Serialization; +using System.Collections.Generic; +using Algolia.Search.Serializer; +using System.Text.Json; + +namespace Algolia.Search.Models.Search; + +/// +/// SecuredAPIKeyRestrictions +/// +public partial class SecuredAPIKeyRestrictions +{ + /// + /// Initializes a new instance of the SecuredAPIKeyRestrictions class. + /// + public SecuredAPIKeyRestrictions() + { + } + + /// + /// Gets or Sets SearchParams + /// + [JsonPropertyName("searchParams")] + public SearchParamsObject SearchParams { get; set; } + + /// + /// Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + /// + /// Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + [JsonPropertyName("filters")] + public string Filters { get; set; } + + /// + /// Unix timestamp used to set the expiration date of the API key. + /// + /// Unix timestamp used to set the expiration date of the API key. + [JsonPropertyName("validUntil")] + public decimal? ValidUntil { get; set; } + + /// + /// Index names that can be queried. + /// + /// Index names that can be queried. + [JsonPropertyName("restrictIndices")] + public List RestrictIndices { get; set; } + + /// + /// IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + /// + /// IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + [JsonPropertyName("restrictSources")] + public string RestrictSources { get; set; } + + /// + /// Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + /// + /// Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + [JsonPropertyName("userToken")] + public string UserToken { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class SecuredAPIKeyRestrictions {\n"); + sb.Append(" SearchParams: ").Append(SearchParams).Append("\n"); + sb.Append(" Filters: ").Append(Filters).Append("\n"); + sb.Append(" ValidUntil: ").Append(ValidUntil).Append("\n"); + sb.Append(" RestrictIndices: ").Append(RestrictIndices).Append("\n"); + sb.Append(" RestrictSources: ").Append(RestrictSources).Append("\n"); + sb.Append(" UserToken: ").Append(UserToken).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return JsonSerializer.Serialize(this, JsonConfig.Options); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object obj) + { + if (obj is not SecuredAPIKeyRestrictions input) + { + return false; + } + + return + (SearchParams == input.SearchParams || (SearchParams != null && SearchParams.Equals(input.SearchParams))) && + (Filters == input.Filters || (Filters != null && Filters.Equals(input.Filters))) && + (ValidUntil == input.ValidUntil || ValidUntil.Equals(input.ValidUntil)) && + (RestrictIndices == input.RestrictIndices || RestrictIndices != null && input.RestrictIndices != null && RestrictIndices.SequenceEqual(input.RestrictIndices)) && + (RestrictSources == input.RestrictSources || (RestrictSources != null && RestrictSources.Equals(input.RestrictSources))) && + (UserToken == input.UserToken || (UserToken != null && UserToken.Equals(input.UserToken))); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (SearchParams != null) + { + hashCode = (hashCode * 59) + SearchParams.GetHashCode(); + } + if (Filters != null) + { + hashCode = (hashCode * 59) + Filters.GetHashCode(); + } + hashCode = (hashCode * 59) + ValidUntil.GetHashCode(); + if (RestrictIndices != null) + { + hashCode = (hashCode * 59) + RestrictIndices.GetHashCode(); + } + if (RestrictSources != null) + { + hashCode = (hashCode * 59) + RestrictSources.GetHashCode(); + } + if (UserToken != null) + { + hashCode = (hashCode * 59) + UserToken.GetHashCode(); + } + return hashCode; + } + } + +} + diff --git a/clients/algoliasearch-client-dart/packages/algoliasearch/lib/algoliasearch_lite.dart b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/algoliasearch_lite.dart index 7d7de87d3b..39ece0ff5a 100644 --- a/clients/algoliasearch-client-dart/packages/algoliasearch/lib/algoliasearch_lite.dart +++ b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/algoliasearch_lite.dart @@ -12,6 +12,7 @@ export 'src/model/advanced_syntax_features.dart'; export 'src/model/alternatives_as_exact.dart'; export 'src/model/anchoring.dart'; export 'src/model/api_key.dart'; +export 'src/model/api_key_operation.dart'; export 'src/model/around_precision_from_value_inner.dart'; export 'src/model/around_radius_all.dart'; export 'src/model/automatic_facet_filter.dart'; @@ -87,6 +88,7 @@ export 'src/model/search_strategy.dart'; export 'src/model/search_synonyms_response.dart'; export 'src/model/search_type_default.dart'; export 'src/model/search_type_facet.dart'; +export 'src/model/secured_api_key_restrictions.dart'; export 'src/model/semantic_search.dart'; export 'src/model/snippet_result_option.dart'; export 'src/model/sort_remaining_by.dart'; diff --git a/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/deserialize.dart b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/deserialize.dart index 60830e1d22..495a6f6b9f 100644 --- a/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/deserialize.dart +++ b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/deserialize.dart @@ -5,6 +5,7 @@ import 'package:algoliasearch/src/model/advanced_syntax_features.dart'; import 'package:algoliasearch/src/model/alternatives_as_exact.dart'; import 'package:algoliasearch/src/model/anchoring.dart'; import 'package:algoliasearch/src/model/api_key.dart'; +import 'package:algoliasearch/src/model/api_key_operation.dart'; import 'package:algoliasearch/src/model/around_precision_from_value_inner.dart'; import 'package:algoliasearch/src/model/around_radius_all.dart'; import 'package:algoliasearch/src/model/automatic_facet_filter.dart'; @@ -80,6 +81,7 @@ import 'package:algoliasearch/src/model/search_strategy.dart'; import 'package:algoliasearch/src/model/search_synonyms_response.dart'; import 'package:algoliasearch/src/model/search_type_default.dart'; import 'package:algoliasearch/src/model/search_type_facet.dart'; +import 'package:algoliasearch/src/model/secured_api_key_restrictions.dart'; import 'package:algoliasearch/src/model/semantic_search.dart'; import 'package:algoliasearch/src/model/snippet_result_option.dart'; import 'package:algoliasearch/src/model/sort_remaining_by.dart'; @@ -128,6 +130,8 @@ ReturnType deserialize(dynamic value, String targetType, return Anchoring.fromJson(value) as ReturnType; case 'ApiKey': return ApiKey.fromJson(value as Map) as ReturnType; + case 'ApiKeyOperation': + return ApiKeyOperation.fromJson(value) as ReturnType; case 'AroundPrecisionFromValueInner': return AroundPrecisionFromValueInner.fromJson( value as Map) as ReturnType; @@ -319,6 +323,9 @@ ReturnType deserialize(dynamic value, String targetType, return SearchTypeDefault.fromJson(value) as ReturnType; case 'SearchTypeFacet': return SearchTypeFacet.fromJson(value) as ReturnType; + case 'SecuredAPIKeyRestrictions': + return SecuredAPIKeyRestrictions.fromJson(value as Map) + as ReturnType; case 'SemanticSearch': return SemanticSearch.fromJson(value as Map) as ReturnType; diff --git a/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/api_key_operation.dart b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/api_key_operation.dart new file mode 100644 index 0000000000..fa0e583b2b --- /dev/null +++ b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/api_key_operation.dart @@ -0,0 +1,25 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// ignore_for_file: unused_element +import 'package:json_annotation/json_annotation.dart'; + +@JsonEnum(valueField: 'raw') +enum ApiKeyOperation { + add(r'add'), + delete(r'delete'), + update(r'update'); + + const ApiKeyOperation(this.raw); + final dynamic raw; + + dynamic toJson() => raw; + + static ApiKeyOperation fromJson(dynamic json) { + for (final value in values) { + if (value.raw == json) return value; + } + throw ArgumentError.value(json, "raw", "No enum value with that value"); + } + + @override + String toString() => raw.toString(); +} diff --git a/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.dart b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.dart new file mode 100644 index 0000000000..debc3fef2b --- /dev/null +++ b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.dart @@ -0,0 +1,73 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// ignore_for_file: unused_element +import 'package:algoliasearch/src/model/search_params_object.dart'; + +import 'package:json_annotation/json_annotation.dart'; + +part 'secured_api_key_restrictions.g.dart'; + +@JsonSerializable() +final class SecuredAPIKeyRestrictions { + /// Returns a new [SecuredAPIKeyRestrictions] instance. + const SecuredAPIKeyRestrictions({ + this.searchParams, + this.filters, + this.validUntil, + this.restrictIndices, + this.restrictSources, + this.userToken, + }); + + @JsonKey(name: r'searchParams') + final SearchParamsObject? searchParams; + + /// Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + @JsonKey(name: r'filters') + final String? filters; + + /// Unix timestamp used to set the expiration date of the API key. + @JsonKey(name: r'validUntil') + final num? validUntil; + + /// Index names that can be queried. + @JsonKey(name: r'restrictIndices') + final List? restrictIndices; + + /// IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + @JsonKey(name: r'restrictSources') + final String? restrictSources; + + /// Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + @JsonKey(name: r'userToken') + final String? userToken; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is SecuredAPIKeyRestrictions && + other.searchParams == searchParams && + other.filters == filters && + other.validUntil == validUntil && + other.restrictIndices == restrictIndices && + other.restrictSources == restrictSources && + other.userToken == userToken; + + @override + int get hashCode => + searchParams.hashCode + + filters.hashCode + + validUntil.hashCode + + restrictIndices.hashCode + + restrictSources.hashCode + + userToken.hashCode; + + factory SecuredAPIKeyRestrictions.fromJson(Map json) => + _$SecuredAPIKeyRestrictionsFromJson(json); + + Map toJson() => _$SecuredAPIKeyRestrictionsToJson(this); + + @override + String toString() { + return toJson().toString(); + } +} diff --git a/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.g.dart b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.g.dart new file mode 100644 index 0000000000..2706ba068e --- /dev/null +++ b/clients/algoliasearch-client-dart/packages/algoliasearch/lib/src/model/secured_api_key_restrictions.g.dart @@ -0,0 +1,50 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'secured_api_key_restrictions.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SecuredAPIKeyRestrictions _$SecuredAPIKeyRestrictionsFromJson( + Map json) => + $checkedCreate( + 'SecuredAPIKeyRestrictions', + json, + ($checkedConvert) { + final val = SecuredAPIKeyRestrictions( + searchParams: $checkedConvert( + 'searchParams', + (v) => v == null + ? null + : SearchParamsObject.fromJson(v as Map)), + filters: $checkedConvert('filters', (v) => v as String?), + validUntil: $checkedConvert('validUntil', (v) => v as num?), + restrictIndices: $checkedConvert('restrictIndices', + (v) => (v as List?)?.map((e) => e as String).toList()), + restrictSources: + $checkedConvert('restrictSources', (v) => v as String?), + userToken: $checkedConvert('userToken', (v) => v as String?), + ); + return val; + }, + ); + +Map _$SecuredAPIKeyRestrictionsToJson( + SecuredAPIKeyRestrictions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('searchParams', instance.searchParams?.toJson()); + writeNotNull('filters', instance.filters); + writeNotNull('validUntil', instance.validUntil); + writeNotNull('restrictIndices', instance.restrictIndices); + writeNotNull('restrictSources', instance.restrictSources); + writeNotNull('userToken', instance.userToken); + return val; +} diff --git a/clients/algoliasearch-client-dart/packages/client_search/lib/algolia_client_search.dart b/clients/algoliasearch-client-dart/packages/client_search/lib/algolia_client_search.dart index 5166c86f6f..35422e2d9c 100644 --- a/clients/algoliasearch-client-dart/packages/client_search/lib/algolia_client_search.dart +++ b/clients/algoliasearch-client-dart/packages/client_search/lib/algolia_client_search.dart @@ -12,6 +12,7 @@ export 'src/model/advanced_syntax_features.dart'; export 'src/model/alternatives_as_exact.dart'; export 'src/model/anchoring.dart'; export 'src/model/api_key.dart'; +export 'src/model/api_key_operation.dart'; export 'src/model/around_precision_from_value_inner.dart'; export 'src/model/around_radius_all.dart'; export 'src/model/assign_user_id_params.dart'; @@ -127,6 +128,7 @@ export 'src/model/search_type_default.dart'; export 'src/model/search_type_facet.dart'; export 'src/model/search_user_ids_params.dart'; export 'src/model/search_user_ids_response.dart'; +export 'src/model/secured_api_key_restrictions.dart'; export 'src/model/semantic_search.dart'; export 'src/model/snippet_result_option.dart'; export 'src/model/sort_remaining_by.dart'; diff --git a/clients/algoliasearch-client-dart/packages/client_search/lib/src/deserialize.dart b/clients/algoliasearch-client-dart/packages/client_search/lib/src/deserialize.dart index 1928578f7c..bddcd5a7bd 100644 --- a/clients/algoliasearch-client-dart/packages/client_search/lib/src/deserialize.dart +++ b/clients/algoliasearch-client-dart/packages/client_search/lib/src/deserialize.dart @@ -5,6 +5,7 @@ import 'package:algolia_client_search/src/model/advanced_syntax_features.dart'; import 'package:algolia_client_search/src/model/alternatives_as_exact.dart'; import 'package:algolia_client_search/src/model/anchoring.dart'; import 'package:algolia_client_search/src/model/api_key.dart'; +import 'package:algolia_client_search/src/model/api_key_operation.dart'; import 'package:algolia_client_search/src/model/around_precision_from_value_inner.dart'; import 'package:algolia_client_search/src/model/around_radius_all.dart'; import 'package:algolia_client_search/src/model/assign_user_id_params.dart'; @@ -120,6 +121,7 @@ import 'package:algolia_client_search/src/model/search_type_default.dart'; import 'package:algolia_client_search/src/model/search_type_facet.dart'; import 'package:algolia_client_search/src/model/search_user_ids_params.dart'; import 'package:algolia_client_search/src/model/search_user_ids_response.dart'; +import 'package:algolia_client_search/src/model/secured_api_key_restrictions.dart'; import 'package:algolia_client_search/src/model/semantic_search.dart'; import 'package:algolia_client_search/src/model/snippet_result_option.dart'; import 'package:algolia_client_search/src/model/sort_remaining_by.dart'; @@ -173,6 +175,8 @@ ReturnType deserialize(dynamic value, String targetType, return Anchoring.fromJson(value) as ReturnType; case 'ApiKey': return ApiKey.fromJson(value as Map) as ReturnType; + case 'ApiKeyOperation': + return ApiKeyOperation.fromJson(value) as ReturnType; case 'AroundPrecisionFromValueInner': return AroundPrecisionFromValueInner.fromJson( value as Map) as ReturnType; @@ -480,6 +484,9 @@ ReturnType deserialize(dynamic value, String targetType, case 'SearchUserIdsResponse': return SearchUserIdsResponse.fromJson(value as Map) as ReturnType; + case 'SecuredAPIKeyRestrictions': + return SecuredAPIKeyRestrictions.fromJson(value as Map) + as ReturnType; case 'SemanticSearch': return SemanticSearch.fromJson(value as Map) as ReturnType; diff --git a/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/api_key_operation.dart b/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/api_key_operation.dart new file mode 100644 index 0000000000..fa0e583b2b --- /dev/null +++ b/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/api_key_operation.dart @@ -0,0 +1,25 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// ignore_for_file: unused_element +import 'package:json_annotation/json_annotation.dart'; + +@JsonEnum(valueField: 'raw') +enum ApiKeyOperation { + add(r'add'), + delete(r'delete'), + update(r'update'); + + const ApiKeyOperation(this.raw); + final dynamic raw; + + dynamic toJson() => raw; + + static ApiKeyOperation fromJson(dynamic json) { + for (final value in values) { + if (value.raw == json) return value; + } + throw ArgumentError.value(json, "raw", "No enum value with that value"); + } + + @override + String toString() => raw.toString(); +} diff --git a/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.dart b/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.dart new file mode 100644 index 0000000000..87ef4a4a9c --- /dev/null +++ b/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.dart @@ -0,0 +1,73 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// ignore_for_file: unused_element +import 'package:algolia_client_search/src/model/search_params_object.dart'; + +import 'package:json_annotation/json_annotation.dart'; + +part 'secured_api_key_restrictions.g.dart'; + +@JsonSerializable() +final class SecuredAPIKeyRestrictions { + /// Returns a new [SecuredAPIKeyRestrictions] instance. + const SecuredAPIKeyRestrictions({ + this.searchParams, + this.filters, + this.validUntil, + this.restrictIndices, + this.restrictSources, + this.userToken, + }); + + @JsonKey(name: r'searchParams') + final SearchParamsObject? searchParams; + + /// Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + @JsonKey(name: r'filters') + final String? filters; + + /// Unix timestamp used to set the expiration date of the API key. + @JsonKey(name: r'validUntil') + final num? validUntil; + + /// Index names that can be queried. + @JsonKey(name: r'restrictIndices') + final List? restrictIndices; + + /// IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + @JsonKey(name: r'restrictSources') + final String? restrictSources; + + /// Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + @JsonKey(name: r'userToken') + final String? userToken; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is SecuredAPIKeyRestrictions && + other.searchParams == searchParams && + other.filters == filters && + other.validUntil == validUntil && + other.restrictIndices == restrictIndices && + other.restrictSources == restrictSources && + other.userToken == userToken; + + @override + int get hashCode => + searchParams.hashCode + + filters.hashCode + + validUntil.hashCode + + restrictIndices.hashCode + + restrictSources.hashCode + + userToken.hashCode; + + factory SecuredAPIKeyRestrictions.fromJson(Map json) => + _$SecuredAPIKeyRestrictionsFromJson(json); + + Map toJson() => _$SecuredAPIKeyRestrictionsToJson(this); + + @override + String toString() { + return toJson().toString(); + } +} diff --git a/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.g.dart b/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.g.dart new file mode 100644 index 0000000000..2706ba068e --- /dev/null +++ b/clients/algoliasearch-client-dart/packages/client_search/lib/src/model/secured_api_key_restrictions.g.dart @@ -0,0 +1,50 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'secured_api_key_restrictions.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SecuredAPIKeyRestrictions _$SecuredAPIKeyRestrictionsFromJson( + Map json) => + $checkedCreate( + 'SecuredAPIKeyRestrictions', + json, + ($checkedConvert) { + final val = SecuredAPIKeyRestrictions( + searchParams: $checkedConvert( + 'searchParams', + (v) => v == null + ? null + : SearchParamsObject.fromJson(v as Map)), + filters: $checkedConvert('filters', (v) => v as String?), + validUntil: $checkedConvert('validUntil', (v) => v as num?), + restrictIndices: $checkedConvert('restrictIndices', + (v) => (v as List?)?.map((e) => e as String).toList()), + restrictSources: + $checkedConvert('restrictSources', (v) => v as String?), + userToken: $checkedConvert('userToken', (v) => v as String?), + ); + return val; + }, + ); + +Map _$SecuredAPIKeyRestrictionsToJson( + SecuredAPIKeyRestrictions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('searchParams', instance.searchParams?.toJson()); + writeNotNull('filters', instance.filters); + writeNotNull('validUntil', instance.validUntil); + writeNotNull('restrictIndices', instance.restrictIndices); + writeNotNull('restrictSources', instance.restrictSources); + writeNotNull('userToken', instance.userToken); + return val; +} diff --git a/clients/algoliasearch-client-go/algolia/search/api_search.go b/clients/algoliasearch-client-go/algolia/search/api_search.go index 75b7ed61a6..f61a634aa3 100644 --- a/clients/algoliasearch-client-go/algolia/search/api_search.go +++ b/clients/algoliasearch-client-go/algolia/search/api_search.go @@ -8591,6 +8591,44 @@ func (c *APIClient) WaitForTaskWithContext( ) } +/* +WaitForApiKey waits for an API key to be created, deleted or updated. +It returns the API key response if the operation was successful. +It returns an error if the operation failed. + +The operation can be one of the following: + - "add": wait for the API key to be created + - "delete": wait for the API key to be deleted + - "update": wait for the API key to be updated + +If the operation is "update", the apiKey parameter must be set. +If the operation is "delete" or "add", the apiKey parameter is not used. + + @param operation ApiKeyOperation - Operation type - add, delete or update. + @param key string - API key. + @param apiKey *ApiKey - API key structure - required for update operation. + @param opts ...Option - Optional parameters for the request. + @return *GetApiKeyResponse - API key response. + @return error - Error if any. +*/ +func (c *APIClient) WaitForApiKey( + operation ApiKeyOperation, + key string, + apiKey *ApiKey, + opts ...Option, +) (*GetApiKeyResponse, error) { + return c.WaitForApiKeyWithContext( + context.Background(), + operation, + key, + apiKey, + nil, + nil, + nil, + opts..., + ) +} + /* WaitForApiKey waits for an API key to be created, deleted or updated. Wraps WaitForApiKeyWithContext with context.Background(). @@ -8609,9 +8647,9 @@ The maxDelay parameter is the maximum delay between each retry. If the operation is "update", the apiKey parameter must be set. If the operation is "delete" or "add", the apiKey parameter is not used. + @param operation ApiKeyOperation - Operation type - add, delete or update. @param key string - API key. @param apiKey *ApiKey - API key structure - required for update operation. - @param operation string - Operation type - add, delete or update. @param maxRetries *int - Maximum number of retries. @param initialDelay *time.Duration - Initial delay between retries. @param maxDelay *time.Duration - Maximum delay between retries. @@ -8619,10 +8657,10 @@ If the operation is "delete" or "add", the apiKey parameter is not used. @return *GetApiKeyResponse - API key response. @return error - Error if any. */ -func (c *APIClient) WaitForApiKey( +func (c *APIClient) WaitForApiKeyWitOptions( + operation ApiKeyOperation, key string, apiKey *ApiKey, - operation string, maxRetries *int, initialDelay *time.Duration, maxDelay *time.Duration, @@ -8630,9 +8668,9 @@ func (c *APIClient) WaitForApiKey( ) (*GetApiKeyResponse, error) { return c.WaitForApiKeyWithContext( context.Background(), + operation, key, apiKey, - operation, maxRetries, initialDelay, maxDelay, @@ -8658,9 +8696,9 @@ If the operation is "update", the apiKey parameter must be set. If the operation is "delete" or "add", the apiKey parameter is not used. @param ctx context.Context - The context that will be drilled down to the actual request. + @param operation ApiKeyOperation - Operation type - add, delete or update. @param key string - API key. @param apiKey *ApiKey - API key structure - required for update operation. - @param operation string - Operation type - add, delete or update. @param maxRetries *int - Maximum number of retries. @param initialDelay *time.Duration - Initial delay between retries. @param maxDelay *time.Duration - Maximum delay between retries. @@ -8670,19 +8708,19 @@ If the operation is "delete" or "add", the apiKey parameter is not used. */ func (c *APIClient) WaitForApiKeyWithContext( ctx context.Context, + operation ApiKeyOperation, key string, apiKey *ApiKey, - operation string, maxRetries *int, initialDelay *time.Duration, maxDelay *time.Duration, opts ...Option, ) (*GetApiKeyResponse, error) { - if operation != "add" && operation != "delete" && operation != "update" { + if operation != APIKEYOPERATION_ADD && operation != APIKEYOPERATION_DELETE && operation != APIKEYOPERATION_UPDATE { return nil, &errs.WaitKeyOperationError{} } - if operation == "update" { + if operation == APIKEYOPERATION_UPDATE { if apiKey == nil { return nil, &errs.WaitKeyUpdateError{} } @@ -8747,9 +8785,9 @@ func (c *APIClient) WaitForApiKeyWithContext( }, func(response *GetApiKeyResponse, err error) bool { switch operation { - case "add": + case APIKEYOPERATION_ADD: return err == nil && response != nil && response.CreatedAt > 0 - case "delete": + case APIKEYOPERATION_DELETE: if _, ok := err.(*APIError); ok { apiErr := err.(*APIError) diff --git a/clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go b/clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go new file mode 100644 index 0000000000..e57ed6320e --- /dev/null +++ b/clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go @@ -0,0 +1,103 @@ +// File generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. +package search + +import ( + "encoding/json" + "fmt" +) + +// ApiKeyOperation the model 'ApiKeyOperation'. +type ApiKeyOperation string + +// List of apiKeyOperation. +const ( + APIKEYOPERATION_ADD ApiKeyOperation = "add" + APIKEYOPERATION_DELETE ApiKeyOperation = "delete" + APIKEYOPERATION_UPDATE ApiKeyOperation = "update" +) + +// All allowed values of ApiKeyOperation enum. +var AllowedApiKeyOperationEnumValues = []ApiKeyOperation{ + "add", + "delete", + "update", +} + +func (v *ApiKeyOperation) UnmarshalJSON(src []byte) error { + var value string + err := json.Unmarshal(src, &value) + if err != nil { + return fmt.Errorf("failed to unmarshal value '%s' for enum 'ApiKeyOperation': %w", string(src), err) + } + enumTypeValue := ApiKeyOperation(value) + for _, existing := range AllowedApiKeyOperationEnumValues { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + return fmt.Errorf("%+v is not a valid ApiKeyOperation", value) +} + +// NewApiKeyOperationFromValue returns a pointer to a valid ApiKeyOperation +// for the value passed as argument, or an error if the value passed is not allowed by the enum. +func NewApiKeyOperationFromValue(v string) (*ApiKeyOperation, error) { + ev := ApiKeyOperation(v) + if ev.IsValid() { + return &ev, nil + } else { + return nil, fmt.Errorf("invalid value '%v' for ApiKeyOperation: valid values are %v", v, AllowedApiKeyOperationEnumValues) + } +} + +// IsValid return true if the value is valid for the enum, false otherwise. +func (v ApiKeyOperation) IsValid() bool { + for _, existing := range AllowedApiKeyOperationEnumValues { + if existing == v { + return true + } + } + return false +} + +// Ptr returns reference to apiKeyOperation value. +func (v ApiKeyOperation) Ptr() *ApiKeyOperation { + return &v +} + +type NullableApiKeyOperation struct { + value *ApiKeyOperation + isSet bool +} + +func (v NullableApiKeyOperation) Get() *ApiKeyOperation { + return v.value +} + +func (v *NullableApiKeyOperation) Set(val *ApiKeyOperation) { + v.value = val + v.isSet = true +} + +func (v NullableApiKeyOperation) IsSet() bool { + return v.isSet +} + +func (v *NullableApiKeyOperation) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableApiKeyOperation(val *ApiKeyOperation) *NullableApiKeyOperation { + return &NullableApiKeyOperation{value: val, isSet: true} +} + +func (v NullableApiKeyOperation) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) //nolint:wrapcheck +} + +func (v *NullableApiKeyOperation) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) //nolint:wrapcheck +} diff --git a/clients/algoliasearch-client-go/algolia/search/model_secured_api_key_restrictions.go b/clients/algoliasearch-client-go/algolia/search/model_secured_api_key_restrictions.go new file mode 100644 index 0000000000..8b4b88b2b3 --- /dev/null +++ b/clients/algoliasearch-client-go/algolia/search/model_secured_api_key_restrictions.go @@ -0,0 +1,350 @@ +// File generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. +package search + +import ( + "encoding/json" + "fmt" +) + +// SecuredAPIKeyRestrictions struct for SecuredAPIKeyRestrictions. +type SecuredAPIKeyRestrictions struct { + SearchParams *SearchParamsObject `json:"searchParams,omitempty"` + // Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + Filters *string `json:"filters,omitempty"` + // Unix timestamp used to set the expiration date of the API key. + ValidUntil *float32 `json:"validUntil,omitempty"` + // Index names that can be queried. + RestrictIndices []string `json:"restrictIndices,omitempty"` + // IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + RestrictSources *string `json:"restrictSources,omitempty"` + // Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + UserToken *string `json:"userToken,omitempty"` +} + +type SecuredAPIKeyRestrictionsOption func(f *SecuredAPIKeyRestrictions) + +func WithSecuredAPIKeyRestrictionsSearchParams(val SearchParamsObject) SecuredAPIKeyRestrictionsOption { + return func(f *SecuredAPIKeyRestrictions) { + f.SearchParams = &val + } +} + +func WithSecuredAPIKeyRestrictionsFilters(val string) SecuredAPIKeyRestrictionsOption { + return func(f *SecuredAPIKeyRestrictions) { + f.Filters = &val + } +} + +func WithSecuredAPIKeyRestrictionsValidUntil(val float32) SecuredAPIKeyRestrictionsOption { + return func(f *SecuredAPIKeyRestrictions) { + f.ValidUntil = &val + } +} + +func WithSecuredAPIKeyRestrictionsRestrictIndices(val []string) SecuredAPIKeyRestrictionsOption { + return func(f *SecuredAPIKeyRestrictions) { + f.RestrictIndices = val + } +} + +func WithSecuredAPIKeyRestrictionsRestrictSources(val string) SecuredAPIKeyRestrictionsOption { + return func(f *SecuredAPIKeyRestrictions) { + f.RestrictSources = &val + } +} + +func WithSecuredAPIKeyRestrictionsUserToken(val string) SecuredAPIKeyRestrictionsOption { + return func(f *SecuredAPIKeyRestrictions) { + f.UserToken = &val + } +} + +// NewSecuredAPIKeyRestrictions instantiates a new SecuredAPIKeyRestrictions object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed. +func NewSecuredAPIKeyRestrictions(opts ...SecuredAPIKeyRestrictionsOption) *SecuredAPIKeyRestrictions { + this := &SecuredAPIKeyRestrictions{} + for _, opt := range opts { + opt(this) + } + return this +} + +// NewEmptySecuredAPIKeyRestrictions return a pointer to an empty SecuredAPIKeyRestrictions object. +func NewEmptySecuredAPIKeyRestrictions() *SecuredAPIKeyRestrictions { + return &SecuredAPIKeyRestrictions{} +} + +// GetSearchParams returns the SearchParams field value if set, zero value otherwise. +func (o *SecuredAPIKeyRestrictions) GetSearchParams() SearchParamsObject { + if o == nil || o.SearchParams == nil { + var ret SearchParamsObject + return ret + } + return *o.SearchParams +} + +// GetSearchParamsOk returns a tuple with the SearchParams field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *SecuredAPIKeyRestrictions) GetSearchParamsOk() (*SearchParamsObject, bool) { + if o == nil || o.SearchParams == nil { + return nil, false + } + return o.SearchParams, true +} + +// HasSearchParams returns a boolean if a field has been set. +func (o *SecuredAPIKeyRestrictions) HasSearchParams() bool { + if o != nil && o.SearchParams != nil { + return true + } + + return false +} + +// SetSearchParams gets a reference to the given SearchParamsObject and assigns it to the SearchParams field. +func (o *SecuredAPIKeyRestrictions) SetSearchParams(v *SearchParamsObject) *SecuredAPIKeyRestrictions { + o.SearchParams = v + return o +} + +// GetFilters returns the Filters field value if set, zero value otherwise. +func (o *SecuredAPIKeyRestrictions) GetFilters() string { + if o == nil || o.Filters == nil { + var ret string + return ret + } + return *o.Filters +} + +// GetFiltersOk returns a tuple with the Filters field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *SecuredAPIKeyRestrictions) GetFiltersOk() (*string, bool) { + if o == nil || o.Filters == nil { + return nil, false + } + return o.Filters, true +} + +// HasFilters returns a boolean if a field has been set. +func (o *SecuredAPIKeyRestrictions) HasFilters() bool { + if o != nil && o.Filters != nil { + return true + } + + return false +} + +// SetFilters gets a reference to the given string and assigns it to the Filters field. +func (o *SecuredAPIKeyRestrictions) SetFilters(v string) *SecuredAPIKeyRestrictions { + o.Filters = &v + return o +} + +// GetValidUntil returns the ValidUntil field value if set, zero value otherwise. +func (o *SecuredAPIKeyRestrictions) GetValidUntil() float32 { + if o == nil || o.ValidUntil == nil { + var ret float32 + return ret + } + return *o.ValidUntil +} + +// GetValidUntilOk returns a tuple with the ValidUntil field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *SecuredAPIKeyRestrictions) GetValidUntilOk() (*float32, bool) { + if o == nil || o.ValidUntil == nil { + return nil, false + } + return o.ValidUntil, true +} + +// HasValidUntil returns a boolean if a field has been set. +func (o *SecuredAPIKeyRestrictions) HasValidUntil() bool { + if o != nil && o.ValidUntil != nil { + return true + } + + return false +} + +// SetValidUntil gets a reference to the given float32 and assigns it to the ValidUntil field. +func (o *SecuredAPIKeyRestrictions) SetValidUntil(v float32) *SecuredAPIKeyRestrictions { + o.ValidUntil = &v + return o +} + +// GetRestrictIndices returns the RestrictIndices field value if set, zero value otherwise. +func (o *SecuredAPIKeyRestrictions) GetRestrictIndices() []string { + if o == nil || o.RestrictIndices == nil { + var ret []string + return ret + } + return o.RestrictIndices +} + +// GetRestrictIndicesOk returns a tuple with the RestrictIndices field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *SecuredAPIKeyRestrictions) GetRestrictIndicesOk() ([]string, bool) { + if o == nil || o.RestrictIndices == nil { + return nil, false + } + return o.RestrictIndices, true +} + +// HasRestrictIndices returns a boolean if a field has been set. +func (o *SecuredAPIKeyRestrictions) HasRestrictIndices() bool { + if o != nil && o.RestrictIndices != nil { + return true + } + + return false +} + +// SetRestrictIndices gets a reference to the given []string and assigns it to the RestrictIndices field. +func (o *SecuredAPIKeyRestrictions) SetRestrictIndices(v []string) *SecuredAPIKeyRestrictions { + o.RestrictIndices = v + return o +} + +// GetRestrictSources returns the RestrictSources field value if set, zero value otherwise. +func (o *SecuredAPIKeyRestrictions) GetRestrictSources() string { + if o == nil || o.RestrictSources == nil { + var ret string + return ret + } + return *o.RestrictSources +} + +// GetRestrictSourcesOk returns a tuple with the RestrictSources field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *SecuredAPIKeyRestrictions) GetRestrictSourcesOk() (*string, bool) { + if o == nil || o.RestrictSources == nil { + return nil, false + } + return o.RestrictSources, true +} + +// HasRestrictSources returns a boolean if a field has been set. +func (o *SecuredAPIKeyRestrictions) HasRestrictSources() bool { + if o != nil && o.RestrictSources != nil { + return true + } + + return false +} + +// SetRestrictSources gets a reference to the given string and assigns it to the RestrictSources field. +func (o *SecuredAPIKeyRestrictions) SetRestrictSources(v string) *SecuredAPIKeyRestrictions { + o.RestrictSources = &v + return o +} + +// GetUserToken returns the UserToken field value if set, zero value otherwise. +func (o *SecuredAPIKeyRestrictions) GetUserToken() string { + if o == nil || o.UserToken == nil { + var ret string + return ret + } + return *o.UserToken +} + +// GetUserTokenOk returns a tuple with the UserToken field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *SecuredAPIKeyRestrictions) GetUserTokenOk() (*string, bool) { + if o == nil || o.UserToken == nil { + return nil, false + } + return o.UserToken, true +} + +// HasUserToken returns a boolean if a field has been set. +func (o *SecuredAPIKeyRestrictions) HasUserToken() bool { + if o != nil && o.UserToken != nil { + return true + } + + return false +} + +// SetUserToken gets a reference to the given string and assigns it to the UserToken field. +func (o *SecuredAPIKeyRestrictions) SetUserToken(v string) *SecuredAPIKeyRestrictions { + o.UserToken = &v + return o +} + +func (o SecuredAPIKeyRestrictions) MarshalJSON() ([]byte, error) { + toSerialize := map[string]any{} + if o.SearchParams != nil { + toSerialize["searchParams"] = o.SearchParams + } + if o.Filters != nil { + toSerialize["filters"] = o.Filters + } + if o.ValidUntil != nil { + toSerialize["validUntil"] = o.ValidUntil + } + if o.RestrictIndices != nil { + toSerialize["restrictIndices"] = o.RestrictIndices + } + if o.RestrictSources != nil { + toSerialize["restrictSources"] = o.RestrictSources + } + if o.UserToken != nil { + toSerialize["userToken"] = o.UserToken + } + serialized, err := json.Marshal(toSerialize) + if err != nil { + return nil, fmt.Errorf("failed to marshal SecuredAPIKeyRestrictions: %w", err) + } + + return serialized, nil +} + +func (o SecuredAPIKeyRestrictions) String() string { + out := "" + out += fmt.Sprintf(" searchParams=%v\n", o.SearchParams) + out += fmt.Sprintf(" filters=%v\n", o.Filters) + out += fmt.Sprintf(" validUntil=%v\n", o.ValidUntil) + out += fmt.Sprintf(" restrictIndices=%v\n", o.RestrictIndices) + out += fmt.Sprintf(" restrictSources=%v\n", o.RestrictSources) + out += fmt.Sprintf(" userToken=%v\n", o.UserToken) + return fmt.Sprintf("SecuredAPIKeyRestrictions {\n%s}", out) +} + +type NullableSecuredAPIKeyRestrictions struct { + value *SecuredAPIKeyRestrictions + isSet bool +} + +func (v NullableSecuredAPIKeyRestrictions) Get() *SecuredAPIKeyRestrictions { + return v.value +} + +func (v *NullableSecuredAPIKeyRestrictions) Set(val *SecuredAPIKeyRestrictions) { + v.value = val + v.isSet = true +} + +func (v NullableSecuredAPIKeyRestrictions) IsSet() bool { + return v.isSet +} + +func (v *NullableSecuredAPIKeyRestrictions) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableSecuredAPIKeyRestrictions(val *SecuredAPIKeyRestrictions) *NullableSecuredAPIKeyRestrictions { + return &NullableSecuredAPIKeyRestrictions{value: val, isSet: true} +} + +func (v NullableSecuredAPIKeyRestrictions) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) //nolint:wrapcheck +} + +func (v *NullableSecuredAPIKeyRestrictions) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) //nolint:wrapcheck +} diff --git a/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/ApiKeyOperation.java b/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/ApiKeyOperation.java new file mode 100644 index 0000000000..1939bda306 --- /dev/null +++ b/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/ApiKeyOperation.java @@ -0,0 +1,42 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost +// - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +package com.algolia.model.search; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.annotation.*; + +/** Gets or Sets apiKeyOperation */ +public enum ApiKeyOperation { + ADD("add"), + + DELETE("delete"), + + UPDATE("update"); + + private final String value; + + ApiKeyOperation(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static ApiKeyOperation fromValue(String value) { + for (ApiKeyOperation b : ApiKeyOperation.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} diff --git a/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/SecuredAPIKeyRestrictions.java b/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/SecuredAPIKeyRestrictions.java new file mode 100644 index 0000000000..1a5c5d6273 --- /dev/null +++ b/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/model/search/SecuredAPIKeyRestrictions.java @@ -0,0 +1,175 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost +// - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +package com.algolia.model.search; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.annotation.*; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** SecuredAPIKeyRestrictions */ +public class SecuredAPIKeyRestrictions { + + @JsonProperty("searchParams") + private SearchParamsObject searchParams; + + @JsonProperty("filters") + private String filters; + + @JsonProperty("validUntil") + private BigDecimal validUntil; + + @JsonProperty("restrictIndices") + private List restrictIndices; + + @JsonProperty("restrictSources") + private String restrictSources; + + @JsonProperty("userToken") + private String userToken; + + public SecuredAPIKeyRestrictions setSearchParams(SearchParamsObject searchParams) { + this.searchParams = searchParams; + return this; + } + + /** Get searchParams */ + @javax.annotation.Nullable + public SearchParamsObject getSearchParams() { + return searchParams; + } + + public SecuredAPIKeyRestrictions setFilters(String filters) { + this.filters = filters; + return this; + } + + /** + * Filters that apply to every search made with the secured API key. You can add extra filters at + * search time with the filters query parameter. For example, if you set the filter group:admin on + * your generated API key, and you add groups:press OR groups:visitors with the filters query + * parameter, your final search filter is equivalent to groups:admin AND (groups:press OR + * groups:visitors). + */ + @javax.annotation.Nullable + public String getFilters() { + return filters; + } + + public SecuredAPIKeyRestrictions setValidUntil(BigDecimal validUntil) { + this.validUntil = validUntil; + return this; + } + + /** Unix timestamp used to set the expiration date of the API key. */ + @javax.annotation.Nullable + public BigDecimal getValidUntil() { + return validUntil; + } + + public SecuredAPIKeyRestrictions setRestrictIndices(List restrictIndices) { + this.restrictIndices = restrictIndices; + return this; + } + + public SecuredAPIKeyRestrictions addRestrictIndices(String restrictIndicesItem) { + if (this.restrictIndices == null) { + this.restrictIndices = new ArrayList<>(); + } + this.restrictIndices.add(restrictIndicesItem); + return this; + } + + /** Index names that can be queried. */ + @javax.annotation.Nullable + public List getRestrictIndices() { + return restrictIndices; + } + + public SecuredAPIKeyRestrictions setRestrictSources(String restrictSources) { + this.restrictSources = restrictSources; + return this; + } + + /** + * IPv4 network allowed to use the generated key. Use this to protect against API key leaking and + * reuse. You can only provide a single source, but you can specify a range of IPs (for example, + * 192.168.1.0/24). + */ + @javax.annotation.Nullable + public String getRestrictSources() { + return restrictSources; + } + + public SecuredAPIKeyRestrictions setUserToken(String userToken) { + this.userToken = userToken; + return this; + } + + /** + * Unique user IP address. This can be useful when you want to impose a rate limit on specific + * users. By default, rate limits are set based on the IP address. This can become an issue when + * several users search from the same IP address. To avoid this, you can set a unique userToken + * for each user when generating their API key. This lets you restrict each user to a maximum + * number of API calls per hour, even if they share their IP with another user. Specifying the + * userToken in a secured API key is also a good security practice as it ensures users don't + * change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the + * authenticity of user identifiers. Setting the userToken at the API key level ensures that + * downstream services work as expected and prevents abuse. + */ + @javax.annotation.Nullable + public String getUserToken() { + return userToken; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SecuredAPIKeyRestrictions securedAPIKeyRestrictions = (SecuredAPIKeyRestrictions) o; + return ( + Objects.equals(this.searchParams, securedAPIKeyRestrictions.searchParams) && + Objects.equals(this.filters, securedAPIKeyRestrictions.filters) && + Objects.equals(this.validUntil, securedAPIKeyRestrictions.validUntil) && + Objects.equals(this.restrictIndices, securedAPIKeyRestrictions.restrictIndices) && + Objects.equals(this.restrictSources, securedAPIKeyRestrictions.restrictSources) && + Objects.equals(this.userToken, securedAPIKeyRestrictions.userToken) + ); + } + + @Override + public int hashCode() { + return Objects.hash(searchParams, filters, validUntil, restrictIndices, restrictSources, userToken); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class SecuredAPIKeyRestrictions {\n"); + sb.append(" searchParams: ").append(toIndentedString(searchParams)).append("\n"); + sb.append(" filters: ").append(toIndentedString(filters)).append("\n"); + sb.append(" validUntil: ").append(toIndentedString(validUntil)).append("\n"); + sb.append(" restrictIndices: ").append(toIndentedString(restrictIndices)).append("\n"); + sb.append(" restrictSources: ").append(toIndentedString(restrictSources)).append("\n"); + sb.append(" userToken: ").append(toIndentedString(userToken)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/apiKeyOperation.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/apiKeyOperation.ts new file mode 100644 index 0000000000..736b63c668 --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/apiKeyOperation.ts @@ -0,0 +1,3 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +export type ApiKeyOperation = 'add' | 'delete' | 'update'; diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/index.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/index.ts index 936c9a3f8f..3dcf44d602 100644 --- a/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/index.ts +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/index.ts @@ -7,6 +7,7 @@ export * from './advancedSyntaxFeatures'; export * from './alternativesAsExact'; export * from './anchoring'; export * from './apiKey'; +export * from './apiKeyOperation'; export * from './aroundPrecision'; export * from './aroundPrecisionFromValueInner'; export * from './aroundRadius'; @@ -101,6 +102,7 @@ export * from './searchStrategy'; export * from './searchSynonymsResponse'; export * from './searchTypeDefault'; export * from './searchTypeFacet'; +export * from './securedAPIKeyRestrictions'; export * from './semanticSearch'; export * from './snippetResult'; export * from './snippetResultOption'; diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/securedAPIKeyRestrictions.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/securedAPIKeyRestrictions.ts new file mode 100644 index 0000000000..bba07db289 --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/lite/model/securedAPIKeyRestrictions.ts @@ -0,0 +1,32 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +import type { SearchParamsObject } from './searchParamsObject'; + +export type SecuredAPIKeyRestrictions = { + searchParams?: SearchParamsObject; + + /** + * Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + */ + filters?: string; + + /** + * Unix timestamp used to set the expiration date of the API key. + */ + validUntil?: number; + + /** + * Index names that can be queried. + */ + restrictIndices?: string[]; + + /** + * IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + */ + restrictSources?: string; + + /** + * Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don\'t change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + */ + userToken?: string; +}; diff --git a/clients/algoliasearch-client-javascript/packages/client-search/model/apiKeyOperation.ts b/clients/algoliasearch-client-javascript/packages/client-search/model/apiKeyOperation.ts new file mode 100644 index 0000000000..736b63c668 --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/client-search/model/apiKeyOperation.ts @@ -0,0 +1,3 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +export type ApiKeyOperation = 'add' | 'delete' | 'update'; diff --git a/clients/algoliasearch-client-javascript/packages/client-search/model/clientMethodProps.ts b/clients/algoliasearch-client-javascript/packages/client-search/model/clientMethodProps.ts index 0ee0456dfd..086014edea 100644 --- a/clients/algoliasearch-client-javascript/packages/client-search/model/clientMethodProps.ts +++ b/clients/algoliasearch-client-javascript/packages/client-search/model/clientMethodProps.ts @@ -4,6 +4,7 @@ import type { CreateIterablePromise } from '@algolia/client-common'; import type { Action } from './action'; import type { ApiKey } from './apiKey'; +import type { ApiKeyOperation } from './apiKeyOperation'; import type { AssignUserIdParams } from './assignUserIdParams'; import type { AttributeToUpdate } from './attributeToUpdate'; import type { BatchAssignUserIdsParams } from './batchAssignUserIdsParams'; @@ -25,6 +26,7 @@ import type { SearchParams } from './searchParams'; import type { SearchParamsObject } from './searchParamsObject'; import type { SearchRulesParams } from './searchRulesParams'; import type { SearchSynonymsParams } from './searchSynonymsParams'; +import type { SecuredAPIKeyRestrictions } from './securedAPIKeyRestrictions'; import type { Source } from './source'; import type { SynonymHit } from './synonymHit'; import type { UpdatedAtResponse } from './updatedAtResponse'; @@ -759,14 +761,14 @@ export type WaitForApiKeyOptions = WaitForOptions & { /** * The operation that has been performed, used to compute the stop condition. */ - operation: 'add' | 'delete'; + operation: Extract; apiKey?: never; } | { /** * The operation that has been performed, used to compute the stop condition. */ - operation: 'update'; + operation: Extract; /** * The updated fields, used to compute the stop condition. */ @@ -783,7 +785,7 @@ export type GenerateSecuredApiKeyOptions = { /** * A set of properties defining the restrictions of the secured API key. */ - restrictions?: SecuredApiKeyRestrictions; + restrictions?: SecuredAPIKeyRestrictions; }; export type GetSecuredApiKeyRemainingValidityOptions = { @@ -793,30 +795,6 @@ export type GetSecuredApiKeyRemainingValidityOptions = { securedApiKey: string; }; -export type SecuredApiKeyRestrictions = { - /** - * A Unix timestamp used to define the expiration date of the API key. - */ - validUntil?: number; - - /** - * List of index names that can be queried. - */ - restrictIndices?: string[] | string; - - /** - * IPv4 network allowed to use the generated key. This is used for more protection against API key leaking and reuse. - */ - restrictSources?: string; - - /** - * Specify a user identifier. This is often used with rate limits. - */ - userToken?: string; - - searchParams?: SearchParamsObject; -}; - export type ChunkedBatchOptions = ReplaceAllObjectsOptions & { /** * The `batch` `action` to perform on the given array of `objects`, defaults to `addObject`. diff --git a/clients/algoliasearch-client-javascript/packages/client-search/model/index.ts b/clients/algoliasearch-client-javascript/packages/client-search/model/index.ts index 8db900701a..edfc461022 100644 --- a/clients/algoliasearch-client-javascript/packages/client-search/model/index.ts +++ b/clients/algoliasearch-client-javascript/packages/client-search/model/index.ts @@ -7,6 +7,7 @@ export * from './advancedSyntaxFeatures'; export * from './alternativesAsExact'; export * from './anchoring'; export * from './apiKey'; +export * from './apiKeyOperation'; export * from './aroundPrecision'; export * from './aroundPrecisionFromValueInner'; export * from './aroundRadius'; @@ -141,6 +142,7 @@ export * from './searchTypeDefault'; export * from './searchTypeFacet'; export * from './searchUserIdsParams'; export * from './searchUserIdsResponse'; +export * from './securedAPIKeyRestrictions'; export * from './semanticSearch'; export * from './snippetResult'; export * from './snippetResultOption'; diff --git a/clients/algoliasearch-client-javascript/packages/client-search/model/securedAPIKeyRestrictions.ts b/clients/algoliasearch-client-javascript/packages/client-search/model/securedAPIKeyRestrictions.ts new file mode 100644 index 0000000000..bba07db289 --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/client-search/model/securedAPIKeyRestrictions.ts @@ -0,0 +1,32 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +import type { SearchParamsObject } from './searchParamsObject'; + +export type SecuredAPIKeyRestrictions = { + searchParams?: SearchParamsObject; + + /** + * Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + */ + filters?: string; + + /** + * Unix timestamp used to set the expiration date of the API key. + */ + validUntil?: number; + + /** + * Index names that can be queried. + */ + restrictIndices?: string[]; + + /** + * IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + */ + restrictSources?: string; + + /** + * Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don\'t change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + */ + userToken?: string; +}; diff --git a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/ApiKeyOperation.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/ApiKeyOperation.kt new file mode 100644 index 0000000000..5adaf8de0b --- /dev/null +++ b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/ApiKeyOperation.kt @@ -0,0 +1,19 @@ +/** Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. */ +package com.algolia.client.model.search + +import kotlinx.serialization.* + +@Serializable +public enum class ApiKeyOperation(public val value: kotlin.String) { + + @SerialName(value = "add") + Add("add"), + + @SerialName(value = "delete") + Delete("delete"), + + @SerialName(value = "update") + Update("update"); + + override fun toString(): kotlin.String = value +} diff --git a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/SecuredAPIKeyRestrictions.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/SecuredAPIKeyRestrictions.kt new file mode 100644 index 0000000000..7064b80b63 --- /dev/null +++ b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/search/SecuredAPIKeyRestrictions.kt @@ -0,0 +1,36 @@ +/** Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. */ +package com.algolia.client.model.search + +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +/** + * SecuredAPIKeyRestrictions + * + * @param searchParams + * @param filters Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + * @param validUntil Unix timestamp used to set the expiration date of the API key. + * @param restrictIndices Index names that can be queried. + * @param restrictSources IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + * @param userToken Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + */ +@Serializable +public data class SecuredAPIKeyRestrictions( + + @SerialName(value = "searchParams") val searchParams: SearchParamsObject? = null, + + /** Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). */ + @SerialName(value = "filters") val filters: String? = null, + + /** Unix timestamp used to set the expiration date of the API key. */ + @SerialName(value = "validUntil") val validUntil: Double? = null, + + /** Index names that can be queried. */ + @SerialName(value = "restrictIndices") val restrictIndices: List? = null, + + /** IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). */ + @SerialName(value = "restrictSources") val restrictSources: String? = null, + + /** Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. */ + @SerialName(value = "userToken") val userToken: String? = null, +) diff --git a/clients/algoliasearch-client-php/lib/Model/Search/ApiKeyOperation.php b/clients/algoliasearch-client-php/lib/Model/Search/ApiKeyOperation.php new file mode 100644 index 0000000000..0c75e7d422 --- /dev/null +++ b/clients/algoliasearch-client-php/lib/Model/Search/ApiKeyOperation.php @@ -0,0 +1,36 @@ + '\Algolia\AlgoliaSearch\Model\Search\SearchParamsObject', + 'filters' => 'string', + 'validUntil' => 'float', + 'restrictIndices' => 'string[]', + 'restrictSources' => 'string', + 'userToken' => 'string', + ]; + + /** + * Array of property to format mappings. Used for (de)serialization. + * + * @var string[] + */ + protected static $modelFormats = [ + 'searchParams' => null, + 'filters' => null, + 'validUntil' => 'duration', + 'restrictIndices' => null, + 'restrictSources' => null, + 'userToken' => null, + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name. + * + * @var string[] + */ + protected static $attributeMap = [ + 'searchParams' => 'searchParams', + 'filters' => 'filters', + 'validUntil' => 'validUntil', + 'restrictIndices' => 'restrictIndices', + 'restrictSources' => 'restrictSources', + 'userToken' => 'userToken', + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses). + * + * @var string[] + */ + protected static $setters = [ + 'searchParams' => 'setSearchParams', + 'filters' => 'setFilters', + 'validUntil' => 'setValidUntil', + 'restrictIndices' => 'setRestrictIndices', + 'restrictSources' => 'setRestrictSources', + 'userToken' => 'setUserToken', + ]; + + /** + * Array of attributes to getter functions (for serialization of requests). + * + * @var string[] + */ + protected static $getters = [ + 'searchParams' => 'getSearchParams', + 'filters' => 'getFilters', + 'validUntil' => 'getValidUntil', + 'restrictIndices' => 'getRestrictIndices', + 'restrictSources' => 'getRestrictSources', + 'userToken' => 'getUserToken', + ]; + + /** + * Associative array for storing property values. + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor. + * + * @param mixed[] $data Associated array of property values + */ + public function __construct(array $data = null) + { + if (isset($data['searchParams'])) { + $this->container['searchParams'] = $data['searchParams']; + } + if (isset($data['filters'])) { + $this->container['filters'] = $data['filters']; + } + if (isset($data['validUntil'])) { + $this->container['validUntil'] = $data['validUntil']; + } + if (isset($data['restrictIndices'])) { + $this->container['restrictIndices'] = $data['restrictIndices']; + } + if (isset($data['restrictSources'])) { + $this->container['restrictSources'] = $data['restrictSources']; + } + if (isset($data['userToken'])) { + $this->container['userToken'] = $data['userToken']; + } + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name. + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of property to type mappings. Used for (de)serialization. + * + * @return array + */ + public static function modelTypes() + { + return self::$modelTypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization. + * + * @return array + */ + public static function modelFormats() + { + return self::$modelFormats; + } + + /** + * Array of attributes to setter functions (for deserialization of responses). + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests). + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + return []; + } + + /** + * Validate all the properties in the model + * return true if all passed. + * + * @return bool True if all properties are valid + */ + public function valid() + { + return 0 === count($this->listInvalidProperties()); + } + + /** + * Gets searchParams. + * + * @return null|\Algolia\AlgoliaSearch\Model\Search\SearchParamsObject + */ + public function getSearchParams() + { + return $this->container['searchParams'] ?? null; + } + + /** + * Sets searchParams. + * + * @param null|\Algolia\AlgoliaSearch\Model\Search\SearchParamsObject $searchParams searchParams + * + * @return self + */ + public function setSearchParams($searchParams) + { + $this->container['searchParams'] = $searchParams; + + return $this; + } + + /** + * Gets filters. + * + * @return null|string + */ + public function getFilters() + { + return $this->container['filters'] ?? null; + } + + /** + * Sets filters. + * + * @param null|string $filters Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + * + * @return self + */ + public function setFilters($filters) + { + $this->container['filters'] = $filters; + + return $this; + } + + /** + * Gets validUntil. + * + * @return null|float + */ + public function getValidUntil() + { + return $this->container['validUntil'] ?? null; + } + + /** + * Sets validUntil. + * + * @param null|float $validUntil unix timestamp used to set the expiration date of the API key + * + * @return self + */ + public function setValidUntil($validUntil) + { + $this->container['validUntil'] = $validUntil; + + return $this; + } + + /** + * Gets restrictIndices. + * + * @return null|string[] + */ + public function getRestrictIndices() + { + return $this->container['restrictIndices'] ?? null; + } + + /** + * Sets restrictIndices. + * + * @param null|string[] $restrictIndices index names that can be queried + * + * @return self + */ + public function setRestrictIndices($restrictIndices) + { + $this->container['restrictIndices'] = $restrictIndices; + + return $this; + } + + /** + * Gets restrictSources. + * + * @return null|string + */ + public function getRestrictSources() + { + return $this->container['restrictSources'] ?? null; + } + + /** + * Sets restrictSources. + * + * @param null|string $restrictSources IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + * + * @return self + */ + public function setRestrictSources($restrictSources) + { + $this->container['restrictSources'] = $restrictSources; + + return $this; + } + + /** + * Gets userToken. + * + * @return null|string + */ + public function getUserToken() + { + return $this->container['userToken'] ?? null; + } + + /** + * Sets userToken. + * + * @param null|string $userToken Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + * + * @return self + */ + public function setUserToken($userToken) + { + $this->container['userToken'] = $userToken; + + return $this; + } + + /** + * Returns true if offset exists. False otherwise. + * + * @param int $offset Offset + * + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param int $offset Offset + * + * @return null|mixed + */ + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param null|int $offset Offset + * @param mixed $value Value to be set + */ + public function offsetSet($offset, $value) + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param int $offset Offset + */ + public function offsetUnset($offset) + { + unset($this->container[$offset]); + } +} diff --git a/clients/algoliasearch-client-python/algoliasearch/search/client.py b/clients/algoliasearch-client-python/algoliasearch/search/client.py index 804374b28f..845b779db2 100644 --- a/clients/algoliasearch-client-python/algoliasearch/search/client.py +++ b/clients/algoliasearch-client-python/algoliasearch/search/client.py @@ -20,11 +20,7 @@ from algoliasearch.http.api_response import ApiResponse from algoliasearch.http.exceptions import RequestException, ValidUntilNotFoundException -from algoliasearch.http.helpers import ( - RetryTimeout, - SecuredApiKeyRestrictions, - create_iterable, -) +from algoliasearch.http.helpers import RetryTimeout, create_iterable from algoliasearch.http.request_options import RequestOptions from algoliasearch.http.serializer import QueryParametersSerializer, bodySerializer from algoliasearch.http.transporter import Transporter @@ -102,6 +98,9 @@ from algoliasearch.search.models.search_synonyms_response import SearchSynonymsResponse from algoliasearch.search.models.search_user_ids_params import SearchUserIdsParams from algoliasearch.search.models.search_user_ids_response import SearchUserIdsResponse +from algoliasearch.search.models.secured_api_key_restrictions import ( + SecuredAPIKeyRestrictions, +) from algoliasearch.search.models.source import Source from algoliasearch.search.models.synonym_hit import SynonymHit from algoliasearch.search.models.update_api_key_response import UpdateApiKeyResponse @@ -365,7 +364,7 @@ async def _func(_prev: SearchRulesResponse) -> SearchRulesResponse: def generate_secured_api_key( self, parent_api_key: str, - restrictions: Optional[SecuredApiKeyRestrictions] = SecuredApiKeyRestrictions(), + restrictions: Optional[SecuredAPIKeyRestrictions] = SecuredAPIKeyRestrictions(), ) -> str: """ Helper: Generates a secured API key based on the given `parent_api_key` and given `restrictions`. @@ -373,7 +372,7 @@ def generate_secured_api_key( query_parameters = dumps( QueryParametersSerializer( restrictions.to_dict() - if isinstance(restrictions, SecuredApiKeyRestrictions) + if isinstance(restrictions, SecuredAPIKeyRestrictions) else restrictions ).query_parameters ) diff --git a/clients/algoliasearch-client-python/algoliasearch/search/models/api_key_operation.py b/clients/algoliasearch-client-python/algoliasearch/search/models/api_key_operation.py new file mode 100644 index 0000000000..ed60a70b39 --- /dev/null +++ b/clients/algoliasearch-client-python/algoliasearch/search/models/api_key_operation.py @@ -0,0 +1,28 @@ +# coding: utf-8 + +""" +Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +""" +from __future__ import annotations + +from enum import Enum +from json import loads +from typing import Self + + +class ApiKeyOperation(str, Enum): + """ + ApiKeyOperation + """ + + """ + allowed enum values + """ + ADD = "add" + DELETE = "delete" + UPDATE = "update" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ApiKeyOperation from a JSON string""" + return cls(loads(json_str)) diff --git a/clients/algoliasearch-client-python/algoliasearch/search/models/secured_api_key_restrictions.py b/clients/algoliasearch-client-python/algoliasearch/search/models/secured_api_key_restrictions.py new file mode 100644 index 0000000000..e0b572a212 --- /dev/null +++ b/clients/algoliasearch-client-python/algoliasearch/search/models/secured_api_key_restrictions.py @@ -0,0 +1,99 @@ +# coding: utf-8 + +""" +Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. +""" +from __future__ import annotations + +from json import loads +from typing import Any, Dict, List, Optional, Self, Union + +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr + +from algoliasearch.search.models.search_params_object import SearchParamsObject + + +class SecuredAPIKeyRestrictions(BaseModel): + """ + SecuredAPIKeyRestrictions + """ + + search_params: Optional[SearchParamsObject] = Field( + default=None, alias="searchParams" + ) + filters: Optional[StrictStr] = Field( + default=None, + description="Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). ", + ) + valid_until: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Unix timestamp used to set the expiration date of the API key.", + alias="validUntil", + ) + restrict_indices: Optional[List[StrictStr]] = Field( + default=None, + description="Index names that can be queried.", + alias="restrictIndices", + ) + restrict_sources: Optional[StrictStr] = Field( + default=None, + description="IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). ", + alias="restrictSources", + ) + user_token: Optional[StrictStr] = Field( + default=None, + description="Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. ", + alias="userToken", + ) + + model_config = {"populate_by_name": True, "validate_assignment": True} + + def to_json(self) -> str: + return self.model_dump_json(by_alias=True, exclude_unset=True) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of SecuredAPIKeyRestrictions from a JSON string""" + return cls.from_dict(loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + if self.search_params: + _dict["searchParams"] = self.search_params.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of SecuredAPIKeyRestrictions from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "searchParams": SearchParamsObject.from_dict(obj.get("searchParams")) + if obj.get("searchParams") is not None + else None, + "filters": obj.get("filters"), + "validUntil": obj.get("validUntil"), + "restrictIndices": obj.get("restrictIndices"), + "restrictSources": obj.get("restrictSources"), + "userToken": obj.get("userToken"), + } + ) + return _obj diff --git a/clients/algoliasearch-client-ruby/lib/algolia/models/search/api_key_operation.rb b/clients/algoliasearch-client-ruby/lib/algolia/models/search/api_key_operation.rb new file mode 100644 index 0000000000..517def2abb --- /dev/null +++ b/clients/algoliasearch-client-ruby/lib/algolia/models/search/api_key_operation.rb @@ -0,0 +1,34 @@ +# Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +require 'date' +require 'time' + +module Algolia + module Search + class ApiKeyOperation + ADD = "add".freeze + DELETE = "delete".freeze + UPDATE = "update".freeze + + def self.all_vars + @all_vars ||= [ADD, DELETE, UPDATE].freeze + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def self.build_from_hash(value) + new.build_from_hash(value) + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def build_from_hash(value) + return value if ApiKeyOperation.all_vars.include?(value) + + raise "Invalid ENUM value #{value} for class #ApiKeyOperation" + end + end + end +end diff --git a/clients/algoliasearch-client-ruby/lib/algolia/models/search/secured_api_key_restrictions.rb b/clients/algoliasearch-client-ruby/lib/algolia/models/search/secured_api_key_restrictions.rb new file mode 100644 index 0000000000..48451feb37 --- /dev/null +++ b/clients/algoliasearch-client-ruby/lib/algolia/models/search/secured_api_key_restrictions.rb @@ -0,0 +1,248 @@ +# Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +require 'date' +require 'time' + +module Algolia + module Search + class SecuredAPIKeyRestrictions + attr_accessor :search_params + + # Filters that apply to every search made with the secured API key. You can add extra filters at search time with the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors). + attr_accessor :filters + + # Unix timestamp used to set the expiration date of the API key. + attr_accessor :valid_until + + # Index names that can be queried. + attr_accessor :restrict_indices + + # IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + attr_accessor :restrict_sources + + # Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, rate limits are set based on the IP address. This can become an issue when several users search from the same IP address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and prevents abuse. + attr_accessor :user_token + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :search_params => :searchParams, + :filters => :filters, + :valid_until => :validUntil, + :restrict_indices => :restrictIndices, + :restrict_sources => :restrictSources, + :user_token => :userToken + } + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + attribute_map.values + end + + # Attribute type mapping. + def self.types_mapping + { + :search_params => :SearchParamsObject, + :filters => :String, + :valid_until => :Float, + :restrict_indices => :'Array', + :restrict_sources => :String, + :user_token => :String + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + unless attributes.is_a?(Hash) + raise ArgumentError, "The input argument (attributes) must be a hash in `Algolia::SecuredAPIKeyRestrictions` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + attributes = attributes.each_with_object({}) do |(k, v), h| + unless self.class.attribute_map.key?(k.to_sym) + raise ArgumentError, + "`#{k}` is not a valid attribute in `Algolia::SecuredAPIKeyRestrictions`. Please check the name to make sure it's valid. List of attributes: " + self.class.attribute_map.keys.inspect + end + + h[k.to_sym] = v + end + + if attributes.key?(:search_params) + self.search_params = attributes[:search_params] + end + + if attributes.key?(:filters) + self.filters = attributes[:filters] + end + + if attributes.key?(:valid_until) + self.valid_until = attributes[:valid_until] + end + + if attributes.key?(:restrict_indices) + if (value = attributes[:restrict_indices]).is_a?(Array) + self.restrict_indices = value + end + end + + if attributes.key?(:restrict_sources) + self.restrict_sources = attributes[:restrict_sources] + end + + if attributes.key?(:user_token) + self.user_token = attributes[:user_token] + end + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(other) + return true if equal?(other) + + self.class == other.class && + search_params == other.search_params && + filters == other.filters && + valid_until == other.valid_until && + restrict_indices == other.restrict_indices && + restrict_sources == other.restrict_sources && + user_token == other.user_token + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(other) + self == other + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [search_params, filters, valid_until, restrict_indices, restrict_sources, user_token].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + types_mapping.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash[key.to_sym] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash[key.to_sym] = attributes[attribute_map[key]].map { |v| _deserialize(::Regexp.last_match(1), v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash[key.to_sym] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def self._deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + {}.tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = Algolia::Search.const_get(type) + klass.respond_to?(:openapi_any_of) || klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + def to_json(*_args) + to_hash.to_json + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + {}.tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end + end + end +end diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/api/SearchClient.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/api/SearchClient.scala index 37f73fe337..82c30eae47 100644 --- a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/api/SearchClient.scala +++ b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/api/SearchClient.scala @@ -5,6 +5,7 @@ package algoliasearch.api import algoliasearch.search.AddApiKeyResponse import algoliasearch.search.ApiKey +import algoliasearch.search.ApiKeyOperation._ import algoliasearch.search.AssignUserIdParams import algoliasearch.search.AttributeToUpdate import algoliasearch.search.BatchAssignUserIdsParams @@ -57,6 +58,7 @@ import algoliasearch.search.SearchSynonymsParams import algoliasearch.search.SearchSynonymsResponse import algoliasearch.search.SearchUserIdsParams import algoliasearch.search.SearchUserIdsResponse +import algoliasearch.search.SecuredAPIKeyRestrictions import algoliasearch.search.Source import algoliasearch.search.SynonymHit import algoliasearch.search.UpdateApiKeyResponse diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/ApiKeyOperation.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/ApiKeyOperation.scala new file mode 100644 index 0000000000..85dfa7982d --- /dev/null +++ b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/ApiKeyOperation.scala @@ -0,0 +1,48 @@ +/** Search API Use the Search REST API to manage your data (indices and records), implement search, and improve + * relevance (with Rules, synonyms, and language dictionaries). Although Algolia provides a REST API, you should use + * the official open source API [clients, libraries, and + * tools](https://www.algolia.com/doc/guides/getting-started/how-algolia-works/in-depth/ecosystem/) instead. There's no + * [SLA](https://www.algolia.com/policies/sla/) if you use the REST API directly. + * + * The version of the OpenAPI document: 1.0.0 + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech Do not edit the class manually. + */ +package algoliasearch.search + +import org.json4s._ + +sealed trait ApiKeyOperation + +/** ApiKeyOperation enumeration + */ +object ApiKeyOperation { + case object Add extends ApiKeyOperation { + override def toString = "add" + } + case object Delete extends ApiKeyOperation { + override def toString = "delete" + } + case object Update extends ApiKeyOperation { + override def toString = "update" + } + val values: Seq[ApiKeyOperation] = Seq(Add, Delete, Update) + + def withName(name: String): ApiKeyOperation = ApiKeyOperation.values + .find(_.toString == name) + .getOrElse(throw new MappingException(s"Unknown ApiKeyOperation value: $name")) +} + +class ApiKeyOperationSerializer + extends CustomSerializer[ApiKeyOperation](_ => + ( + { + case JString(value) => ApiKeyOperation.withName(value) + case JNull => null + }, + { case value: ApiKeyOperation => + JString(value.toString) + } + ) + ) diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/JsonSupport.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/JsonSupport.scala index 8f095b2006..f2af940c60 100644 --- a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/JsonSupport.scala +++ b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/JsonSupport.scala @@ -20,6 +20,7 @@ object JsonSupport { new AdvancedSyntaxFeaturesSerializer() :+ new AlternativesAsExactSerializer() :+ new AnchoringSerializer() :+ + new ApiKeyOperationSerializer() :+ new AroundRadiusAllSerializer() :+ new BuiltInOperationTypeSerializer() :+ new DictionaryActionSerializer() :+ diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/SecuredAPIKeyRestrictions.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/SecuredAPIKeyRestrictions.scala new file mode 100644 index 0000000000..5abec92259 --- /dev/null +++ b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/search/SecuredAPIKeyRestrictions.scala @@ -0,0 +1,45 @@ +/** Search API Use the Search REST API to manage your data (indices and records), implement search, and improve + * relevance (with Rules, synonyms, and language dictionaries). Although Algolia provides a REST API, you should use + * the official open source API [clients, libraries, and + * tools](https://www.algolia.com/doc/guides/getting-started/how-algolia-works/in-depth/ecosystem/) instead. There's no + * [SLA](https://www.algolia.com/policies/sla/) if you use the REST API directly. + * + * The version of the OpenAPI document: 1.0.0 + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech Do not edit the class manually. + */ +package algoliasearch.search + +/** SecuredAPIKeyRestrictions + * + * @param filters + * Filters that apply to every search made with the secured API key. You can add extra filters at search time with + * the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you add + * groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to + * groups:admin AND (groups:press OR groups:visitors). + * @param validUntil + * Unix timestamp used to set the expiration date of the API key. + * @param restrictIndices + * Index names that can be queried. + * @param restrictSources + * IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can only + * provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + * @param userToken + * Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, + * rate limits are set based on the IP address. This can become an issue when several users search from the same IP + * address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets you + * restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. + * Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change + * it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user + * identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and + * prevents abuse. + */ +case class SecuredAPIKeyRestrictions( + searchParams: Option[SearchParamsObject] = scala.None, + filters: Option[String] = scala.None, + validUntil: Option[Double] = scala.None, + restrictIndices: Option[Seq[String]] = scala.None, + restrictSources: Option[String] = scala.None, + userToken: Option[String] = scala.None +) diff --git a/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift b/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift index dd8135fdbd..845bbbe6b2 100644 --- a/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift +++ b/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift @@ -1,8 +1,8 @@ -import Foundation import Core +import Foundation public extension SecuredAPIKeyRestrictions { - func toURLEncodedString() throws -> String { + func toURLEncodedString() throws -> String { var queryDictionary: [String: Any] = [:] if let searchParams { diff --git a/clients/algoliasearch-client-swift/Sources/Search/Models/ApiKeyOperation.swift b/clients/algoliasearch-client-swift/Sources/Search/Models/ApiKeyOperation.swift new file mode 100644 index 0000000000..3a6c40d248 --- /dev/null +++ b/clients/algoliasearch-client-swift/Sources/Search/Models/ApiKeyOperation.swift @@ -0,0 +1,12 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on +// https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +import AnyCodable +import Core +import Foundation + +public enum ApiKeyOperation: String, Codable, CaseIterable { + case add + case delete + case update +} diff --git a/clients/algoliasearch-client-swift/Sources/Search/Models/SecuredAPIKeyRestrictions.swift b/clients/algoliasearch-client-swift/Sources/Search/Models/SecuredAPIKeyRestrictions.swift new file mode 100644 index 0000000000..49aa441294 --- /dev/null +++ b/clients/algoliasearch-client-swift/Sources/Search/Models/SecuredAPIKeyRestrictions.swift @@ -0,0 +1,68 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on +// https://github.com/algolia/api-clients-automation. DO NOT EDIT. + +import AnyCodable +import Core +import Foundation + +public struct SecuredAPIKeyRestrictions: Codable, JSONEncodable, Hashable { + public var searchParams: SearchParamsObject? + /// Filters that apply to every search made with the secured API key. You can add extra filters at search time with + /// the filters query parameter. For example, if you set the filter group:admin on your generated API key, and you + /// add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to + /// groups:admin AND (groups:press OR groups:visitors). + public var filters: String? + /// Unix timestamp used to set the expiration date of the API key. + public var validUntil: Double? + /// Index names that can be queried. + public var restrictIndices: [String]? + /// IPv4 network allowed to use the generated key. Use this to protect against API key leaking and reuse. You can + /// only provide a single source, but you can specify a range of IPs (for example, 192.168.1.0/24). + public var restrictSources: String? + /// Unique user IP address. This can be useful when you want to impose a rate limit on specific users. By default, + /// rate limits are set based on the IP address. This can become an issue when several users search from the same IP + /// address. To avoid this, you can set a unique userToken for each user when generating their API key. This lets + /// you restrict each user to a maximum number of API calls per hour, even if they share their IP with another user. + /// Specifying the userToken in a secured API key is also a good security practice as it ensures users don't change + /// it. Many features like Analytics, Personalization, and Dynamic Re-ranking rely on the authenticity of user + /// identifiers. Setting the userToken at the API key level ensures that downstream services work as expected and + /// prevents abuse. + public var userToken: String? + + public init( + searchParams: SearchParamsObject? = nil, + filters: String? = nil, + validUntil: Double? = nil, + restrictIndices: [String]? = nil, + restrictSources: String? = nil, + userToken: String? = nil + ) { + self.searchParams = searchParams + self.filters = filters + self.validUntil = validUntil + self.restrictIndices = restrictIndices + self.restrictSources = restrictSources + self.userToken = userToken + } + + public enum CodingKeys: String, CodingKey, CaseIterable { + case searchParams + case filters + case validUntil + case restrictIndices + case restrictSources + case userToken + } + + // Encodable protocol methods + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(self.searchParams, forKey: .searchParams) + try container.encodeIfPresent(self.filters, forKey: .filters) + try container.encodeIfPresent(self.validUntil, forKey: .validUntil) + try container.encodeIfPresent(self.restrictIndices, forKey: .restrictIndices) + try container.encodeIfPresent(self.restrictSources, forKey: .restrictSources) + try container.encodeIfPresent(self.userToken, forKey: .userToken) + } +} diff --git a/specs/bundled/algoliasearch.yml b/specs/bundled/algoliasearch.yml index 202f11d177..6f3f1b434d 100644 --- a/specs/bundled/algoliasearch.yml +++ b/specs/bundled/algoliasearch.yml @@ -3046,6 +3046,63 @@ components: example: 100 required: - items + apiKeyOperation: + type: string + enum: + - add + - delete + - update + securedAPIKeyRestrictions: + type: object + additionalProperties: false + properties: + searchParams: + $ref: '#/components/schemas/searchParamsObject' + filters: + type: string + description: > + Filters that apply to every search made with the secured API key. + You can add extra filters at search time with the filters query + parameter. + + For example, if you set the filter group:admin on your generated API + key, and you add groups:press OR groups:visitors with the filters + query parameter, your final search filter is equivalent to + groups:admin AND (groups:press OR groups:visitors). + validUntil: + type: number + format: duration + description: Unix timestamp used to set the expiration date of the API key. + restrictIndices: + type: array + items: + type: string + description: Index names that can be queried. + restrictSources: + type: string + description: > + IPv4 network allowed to use the generated key. Use this to protect + against API key leaking and reuse. + + You can only provide a single source, but you can specify a range of + IPs (for example, 192.168.1.0/24). + userToken: + type: string + description: > + Unique user IP address. + + This can be useful when you want to impose a rate limit on specific + users. By default, rate limits are set based on the IP address. This + can become an issue when several users search from the same IP + address. To avoid this, you can set a unique userToken for each user + when generating their API key. This lets you restrict each user to a + maximum number of API calls per hour, even if they share their IP + with another user. Specifying the userToken in a secured API key is + also a good security practice as it ensures users don't change it. + Many features like Analytics, Personalization, and Dynamic + Re-ranking rely on the authenticity of user identifiers. Setting the + userToken at the API key level ensures that downstream services work + as expected and prevents abuse. responses: BadRequest: description: Bad request or request arguments. diff --git a/specs/bundled/search.doc.yml b/specs/bundled/search.doc.yml index d7a198a9cd..ac276250a7 100644 --- a/specs/bundled/search.doc.yml +++ b/specs/bundled/search.doc.yml @@ -3046,6 +3046,63 @@ components: example: 100 required: - items + apiKeyOperation: + type: string + enum: + - add + - delete + - update + securedAPIKeyRestrictions: + type: object + additionalProperties: false + properties: + searchParams: + $ref: '#/components/schemas/searchParamsObject' + filters: + type: string + description: > + Filters that apply to every search made with the secured API key. + You can add extra filters at search time with the filters query + parameter. + + For example, if you set the filter group:admin on your generated API + key, and you add groups:press OR groups:visitors with the filters + query parameter, your final search filter is equivalent to + groups:admin AND (groups:press OR groups:visitors). + validUntil: + type: number + format: duration + description: Unix timestamp used to set the expiration date of the API key. + restrictIndices: + type: array + items: + type: string + description: Index names that can be queried. + restrictSources: + type: string + description: > + IPv4 network allowed to use the generated key. Use this to protect + against API key leaking and reuse. + + You can only provide a single source, but you can specify a range of + IPs (for example, 192.168.1.0/24). + userToken: + type: string + description: > + Unique user IP address. + + This can be useful when you want to impose a rate limit on specific + users. By default, rate limits are set based on the IP address. This + can become an issue when several users search from the same IP + address. To avoid this, you can set a unique userToken for each user + when generating their API key. This lets you restrict each user to a + maximum number of API calls per hour, even if they share their IP + with another user. Specifying the userToken in a secured API key is + also a good security practice as it ensures users don't change it. + Many features like Analytics, Personalization, and Dynamic + Re-ranking rely on the authenticity of user identifiers. Setting the + userToken at the API key level ensures that downstream services work + as expected and prevents abuse. responses: BadRequest: description: Bad request or request arguments. diff --git a/specs/bundled/search.yml b/specs/bundled/search.yml index e8532e249d..6849f9811a 100644 --- a/specs/bundled/search.yml +++ b/specs/bundled/search.yml @@ -3046,6 +3046,63 @@ components: example: 100 required: - items + apiKeyOperation: + type: string + enum: + - add + - delete + - update + securedAPIKeyRestrictions: + type: object + additionalProperties: false + properties: + searchParams: + $ref: '#/components/schemas/searchParamsObject' + filters: + type: string + description: > + Filters that apply to every search made with the secured API key. + You can add extra filters at search time with the filters query + parameter. + + For example, if you set the filter group:admin on your generated API + key, and you add groups:press OR groups:visitors with the filters + query parameter, your final search filter is equivalent to + groups:admin AND (groups:press OR groups:visitors). + validUntil: + type: number + format: duration + description: Unix timestamp used to set the expiration date of the API key. + restrictIndices: + type: array + items: + type: string + description: Index names that can be queried. + restrictSources: + type: string + description: > + IPv4 network allowed to use the generated key. Use this to protect + against API key leaking and reuse. + + You can only provide a single source, but you can specify a range of + IPs (for example, 192.168.1.0/24). + userToken: + type: string + description: > + Unique user IP address. + + This can be useful when you want to impose a rate limit on specific + users. By default, rate limits are set based on the IP address. This + can become an issue when several users search from the same IP + address. To avoid this, you can set a unique userToken for each user + when generating their API key. This lets you restrict each user to a + maximum number of API calls per hour, even if they share their IP + with another user. Specifying the userToken in a secured API key is + also a good security practice as it ensures users don't change it. + Many features like Analytics, Personalization, and Dynamic + Re-ranking rely on the authenticity of user identifiers. Setting the + userToken at the API key level ensures that downstream services work + as expected and prevents abuse. responses: BadRequest: description: Bad request or request arguments. @@ -5897,3 +5954,87 @@ paths: $ref: '#/components/responses/MethodNotAllowed' '404': $ref: '#/components/responses/IndexNotFound' + /waitForApiKey: + get: + x-helper: true + tags: + - search + operationId: waitForApiKey + summary: Wait for an API key to be added, deleted, or updated. + description: description + parameters: + - in: query + name: operation + description: The `operation` that was done on a `key`. + required: true + schema: + $ref: '#/components/schemas/apiKeyOperation' + - in: query + name: key + description: The `key` that has been added, deleted or updated. + required: true + schema: + type: string + - in: query + name: apiKey + description: >- + Used to compare fields of the getApiKey response on an `update` + operation, to check if the `key` has been updated. + required: false + schema: + $ref: '#/components/schemas/apiKey' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/getApiKeyResponse' + '400': + $ref: '#/components/responses/IndexNotFound' + /generateSecuredApiKey: + get: + x-helper: true + tags: + - search + operationId: generateSecuredApiKey + summary: Generate a secured API key without any calls to Algolia's servers. + description: >- + Generate a secured API key without any calls to Algolia's servers. + + When you need to restrict the scope of an API key, generate a secured + API key on your server, without any calls to Algolia. You can't generate + secured API keys from your Admin API key or from other secured API keys. + When you generate a secured API key, you can define several + restrictions, such as how long the key is valid for and which indexes it + can access. The more restrictions you set, the longer the key will be. + If the key is longer than 500 characters, you may have problems using it + on some networks. If you want to limit the number of requests that can + be made with a secured API key, you must also rate-limit the key that + you use to generate it. You can create a rate-limited key in the Algolia + dashboard or use the Add API key or Update API key methods of an API + client. + parameters: + - in: query + name: apiKey + description: >- + The search-only API key that the secured API key will inherit its + restrictions from. + required: true + schema: + type: string + - in: query + name: restrictions + description: The options to add to the secured API key. + required: true + schema: + $ref: '#/components/schemas/securedAPIKeyRestrictions' + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + '400': + $ref: '#/components/responses/IndexNotFound' diff --git a/tests/output/go/tests/requests/abtesting_test.go b/tests/output/go/tests/requests/abtesting_test.go index 3582e4e246..9030d3c345 100644 --- a/tests/output/go/tests/requests/abtesting_test.go +++ b/tests/output/go/tests/requests/abtesting_test.go @@ -52,6 +52,7 @@ func createE2EAbtestingClient(t *testing.T) *abtesting.APIClient { func TestAbtesting_AddABTests(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("addABTests with minimal parameters", func(t *testing.T) { _, err := client.AddABTests(client.NewApiAddABTestsRequest( @@ -73,6 +74,7 @@ func TestAbtesting_AddABTests(t *testing.T) { func TestAbtesting_CustomDelete(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -106,6 +108,7 @@ func TestAbtesting_CustomDelete(t *testing.T) { func TestAbtesting_CustomGet(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -164,6 +167,7 @@ func TestAbtesting_CustomGet(t *testing.T) { func TestAbtesting_CustomPost(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -392,6 +396,7 @@ func TestAbtesting_CustomPost(t *testing.T) { func TestAbtesting_CustomPut(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -427,6 +432,7 @@ func TestAbtesting_CustomPut(t *testing.T) { func TestAbtesting_DeleteABTest(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("deleteABTest", func(t *testing.T) { _, err := client.DeleteABTest(client.NewApiDeleteABTestRequest( @@ -443,6 +449,7 @@ func TestAbtesting_DeleteABTest(t *testing.T) { func TestAbtesting_GetABTest(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("getABTest", func(t *testing.T) { _, err := client.GetABTest(client.NewApiGetABTestRequest( @@ -459,6 +466,7 @@ func TestAbtesting_GetABTest(t *testing.T) { func TestAbtesting_ListABTests(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("listABTests with minimal parameters", func(t *testing.T) { _, err := client.ListABTests(client.NewApiListABTestsRequest()) @@ -511,6 +519,7 @@ func TestAbtesting_ListABTests(t *testing.T) { func TestAbtesting_StopABTest(t *testing.T) { client, echo := createAbtestingClient(t) + _ = echo t.Run("stopABTest", func(t *testing.T) { _, err := client.StopABTest(client.NewApiStopABTestRequest( diff --git a/tests/output/go/tests/requests/analytics_test.go b/tests/output/go/tests/requests/analytics_test.go index 38cfc81e20..4efba98fd5 100644 --- a/tests/output/go/tests/requests/analytics_test.go +++ b/tests/output/go/tests/requests/analytics_test.go @@ -52,6 +52,7 @@ func createE2EAnalyticsClient(t *testing.T) *analytics.APIClient { func TestAnalytics_CustomDelete(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -85,6 +86,7 @@ func TestAnalytics_CustomDelete(t *testing.T) { func TestAnalytics_CustomGet(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -143,6 +145,7 @@ func TestAnalytics_CustomGet(t *testing.T) { func TestAnalytics_CustomPost(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -371,6 +374,7 @@ func TestAnalytics_CustomPost(t *testing.T) { func TestAnalytics_CustomPut(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -406,6 +410,7 @@ func TestAnalytics_CustomPut(t *testing.T) { func TestAnalytics_GetAverageClickPosition(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getAverageClickPosition with minimal parameters", func(t *testing.T) { _, err := client.GetAverageClickPosition(client.NewApiGetAverageClickPositionRequest( @@ -445,6 +450,7 @@ func TestAnalytics_GetAverageClickPosition(t *testing.T) { func TestAnalytics_GetClickPositions(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getClickPositions with minimal parameters", func(t *testing.T) { _, err := client.GetClickPositions(client.NewApiGetClickPositionsRequest( @@ -484,6 +490,7 @@ func TestAnalytics_GetClickPositions(t *testing.T) { func TestAnalytics_GetClickThroughRate(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getClickThroughRate with minimal parameters", func(t *testing.T) { _, err := client.GetClickThroughRate(client.NewApiGetClickThroughRateRequest( @@ -523,6 +530,7 @@ func TestAnalytics_GetClickThroughRate(t *testing.T) { func TestAnalytics_GetConversationRate(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getConversationRate with minimal parameters", func(t *testing.T) { _, err := client.GetConversationRate(client.NewApiGetConversationRateRequest( @@ -562,6 +570,7 @@ func TestAnalytics_GetConversationRate(t *testing.T) { func TestAnalytics_GetNoClickRate(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getNoClickRate with minimal parameters", func(t *testing.T) { _, err := client.GetNoClickRate(client.NewApiGetNoClickRateRequest( @@ -601,6 +610,7 @@ func TestAnalytics_GetNoClickRate(t *testing.T) { func TestAnalytics_GetNoResultsRate(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getNoResultsRate with minimal parameters", func(t *testing.T) { _, err := client.GetNoResultsRate(client.NewApiGetNoResultsRateRequest( @@ -640,6 +650,7 @@ func TestAnalytics_GetNoResultsRate(t *testing.T) { func TestAnalytics_GetSearchesCount(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getSearchesCount with minimal parameters", func(t *testing.T) { _, err := client.GetSearchesCount(client.NewApiGetSearchesCountRequest( @@ -679,6 +690,7 @@ func TestAnalytics_GetSearchesCount(t *testing.T) { func TestAnalytics_GetSearchesNoClicks(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getSearchesNoClicks with minimal parameters", func(t *testing.T) { _, err := client.GetSearchesNoClicks(client.NewApiGetSearchesNoClicksRequest( @@ -718,6 +730,7 @@ func TestAnalytics_GetSearchesNoClicks(t *testing.T) { func TestAnalytics_GetSearchesNoResults(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getSearchesNoResults with minimal parameters", func(t *testing.T) { _, err := client.GetSearchesNoResults(client.NewApiGetSearchesNoResultsRequest( @@ -757,6 +770,7 @@ func TestAnalytics_GetSearchesNoResults(t *testing.T) { func TestAnalytics_GetStatus(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getStatus with minimal parameters", func(t *testing.T) { _, err := client.GetStatus(client.NewApiGetStatusRequest( @@ -779,6 +793,7 @@ func TestAnalytics_GetStatus(t *testing.T) { func TestAnalytics_GetTopCountries(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getTopCountries with minimal parameters", func(t *testing.T) { _, err := client.GetTopCountries(client.NewApiGetTopCountriesRequest( @@ -818,6 +833,7 @@ func TestAnalytics_GetTopCountries(t *testing.T) { func TestAnalytics_GetTopFilterAttributes(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getTopFilterAttributes with minimal parameters", func(t *testing.T) { _, err := client.GetTopFilterAttributes(client.NewApiGetTopFilterAttributesRequest( @@ -857,6 +873,7 @@ func TestAnalytics_GetTopFilterAttributes(t *testing.T) { func TestAnalytics_GetTopFilterForAttribute(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getTopFilterForAttribute with minimal parameters", func(t *testing.T) { _, err := client.GetTopFilterForAttribute(client.NewApiGetTopFilterForAttributeRequest( @@ -930,6 +947,7 @@ func TestAnalytics_GetTopFilterForAttribute(t *testing.T) { func TestAnalytics_GetTopFiltersNoResults(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getTopFiltersNoResults with minimal parameters", func(t *testing.T) { _, err := client.GetTopFiltersNoResults(client.NewApiGetTopFiltersNoResultsRequest( @@ -969,6 +987,7 @@ func TestAnalytics_GetTopFiltersNoResults(t *testing.T) { func TestAnalytics_GetTopHits(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getTopHits with minimal parameters", func(t *testing.T) { _, err := client.GetTopHits(client.NewApiGetTopHitsRequest( @@ -1008,6 +1027,7 @@ func TestAnalytics_GetTopHits(t *testing.T) { func TestAnalytics_GetTopSearches(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getTopSearches with minimal parameters", func(t *testing.T) { _, err := client.GetTopSearches(client.NewApiGetTopSearchesRequest( @@ -1089,6 +1109,7 @@ func TestAnalytics_GetTopSearches(t *testing.T) { func TestAnalytics_GetUsersCount(t *testing.T) { client, echo := createAnalyticsClient(t) + _ = echo t.Run("get getUsersCount with minimal parameters", func(t *testing.T) { _, err := client.GetUsersCount(client.NewApiGetUsersCountRequest( diff --git a/tests/output/go/tests/requests/ingestion_test.go b/tests/output/go/tests/requests/ingestion_test.go index 89bbf89607..dba6aad42a 100644 --- a/tests/output/go/tests/requests/ingestion_test.go +++ b/tests/output/go/tests/requests/ingestion_test.go @@ -52,6 +52,7 @@ func createE2EIngestionClient(t *testing.T) *ingestion.APIClient { func TestIngestion_CreateAuthentication(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("createAuthenticationOAuth", func(t *testing.T) { _, err := client.CreateAuthentication(client.NewApiCreateAuthenticationRequest( @@ -85,6 +86,7 @@ func TestIngestion_CreateAuthentication(t *testing.T) { func TestIngestion_CreateDestination(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("createDestination", func(t *testing.T) { _, err := client.CreateDestination(client.NewApiCreateDestinationRequest( @@ -104,6 +106,7 @@ func TestIngestion_CreateDestination(t *testing.T) { func TestIngestion_CreateSource(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("createSource", func(t *testing.T) { _, err := client.CreateSource(client.NewApiCreateSourceRequest( @@ -125,6 +128,7 @@ func TestIngestion_CreateSource(t *testing.T) { func TestIngestion_CreateTask(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("createTaskOnDemand", func(t *testing.T) { _, err := client.CreateTask(client.NewApiCreateTaskRequest( @@ -172,6 +176,7 @@ func TestIngestion_CreateTask(t *testing.T) { func TestIngestion_CustomDelete(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -205,6 +210,7 @@ func TestIngestion_CustomDelete(t *testing.T) { func TestIngestion_CustomGet(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -263,6 +269,7 @@ func TestIngestion_CustomGet(t *testing.T) { func TestIngestion_CustomPost(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -491,6 +498,7 @@ func TestIngestion_CustomPost(t *testing.T) { func TestIngestion_CustomPut(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -526,6 +534,7 @@ func TestIngestion_CustomPut(t *testing.T) { func TestIngestion_DeleteAuthentication(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("deleteAuthentication", func(t *testing.T) { _, err := client.DeleteAuthentication(client.NewApiDeleteAuthenticationRequest( @@ -542,6 +551,7 @@ func TestIngestion_DeleteAuthentication(t *testing.T) { func TestIngestion_DeleteDestination(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("deleteDestination", func(t *testing.T) { _, err := client.DeleteDestination(client.NewApiDeleteDestinationRequest( @@ -558,6 +568,7 @@ func TestIngestion_DeleteDestination(t *testing.T) { func TestIngestion_DeleteSource(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("deleteSource", func(t *testing.T) { _, err := client.DeleteSource(client.NewApiDeleteSourceRequest( @@ -574,6 +585,7 @@ func TestIngestion_DeleteSource(t *testing.T) { func TestIngestion_DeleteTask(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("deleteTask", func(t *testing.T) { _, err := client.DeleteTask(client.NewApiDeleteTaskRequest( @@ -590,6 +602,7 @@ func TestIngestion_DeleteTask(t *testing.T) { func TestIngestion_DisableTask(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("disableTask", func(t *testing.T) { _, err := client.DisableTask(client.NewApiDisableTaskRequest( @@ -606,6 +619,7 @@ func TestIngestion_DisableTask(t *testing.T) { func TestIngestion_EnableTask(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("enable task e2e", func(t *testing.T) { _, err := client.EnableTask(client.NewApiEnableTaskRequest( @@ -647,6 +661,7 @@ func TestIngestion_EnableTask(t *testing.T) { func TestIngestion_GetAuthentication(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getAuthentication", func(t *testing.T) { _, err := client.GetAuthentication(client.NewApiGetAuthenticationRequest( @@ -663,6 +678,7 @@ func TestIngestion_GetAuthentication(t *testing.T) { func TestIngestion_GetAuthentications(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getAuthentications", func(t *testing.T) { _, err := client.GetAuthentications(client.NewApiGetAuthenticationsRequest()) @@ -719,6 +735,7 @@ func TestIngestion_GetAuthentications(t *testing.T) { func TestIngestion_GetDestination(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getDestination", func(t *testing.T) { _, err := client.GetDestination(client.NewApiGetDestinationRequest( @@ -735,6 +752,7 @@ func TestIngestion_GetDestination(t *testing.T) { func TestIngestion_GetDestinations(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getDestinations", func(t *testing.T) { _, err := client.GetDestinations(client.NewApiGetDestinationsRequest()) @@ -749,6 +767,7 @@ func TestIngestion_GetDestinations(t *testing.T) { func TestIngestion_GetDockerSourceStreams(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getDockerSourceStreams", func(t *testing.T) { _, err := client.GetDockerSourceStreams(client.NewApiGetDockerSourceStreamsRequest( @@ -765,6 +784,7 @@ func TestIngestion_GetDockerSourceStreams(t *testing.T) { func TestIngestion_GetEvent(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getEvent", func(t *testing.T) { _, err := client.GetEvent(client.NewApiGetEventRequest( @@ -781,6 +801,7 @@ func TestIngestion_GetEvent(t *testing.T) { func TestIngestion_GetEvents(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getEvents", func(t *testing.T) { _, err := client.GetEvents(client.NewApiGetEventsRequest( @@ -797,6 +818,7 @@ func TestIngestion_GetEvents(t *testing.T) { func TestIngestion_GetRun(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getRun", func(t *testing.T) { _, err := client.GetRun(client.NewApiGetRunRequest( @@ -813,6 +835,7 @@ func TestIngestion_GetRun(t *testing.T) { func TestIngestion_GetRuns(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getRuns", func(t *testing.T) { _, err := client.GetRuns(client.NewApiGetRunsRequest()) @@ -827,6 +850,7 @@ func TestIngestion_GetRuns(t *testing.T) { func TestIngestion_GetSource(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getSource", func(t *testing.T) { _, err := client.GetSource(client.NewApiGetSourceRequest( @@ -868,6 +892,7 @@ func TestIngestion_GetSource(t *testing.T) { func TestIngestion_GetSources(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getSources", func(t *testing.T) { _, err := client.GetSources(client.NewApiGetSourcesRequest()) @@ -882,6 +907,7 @@ func TestIngestion_GetSources(t *testing.T) { func TestIngestion_GetTask(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getTask", func(t *testing.T) { _, err := client.GetTask(client.NewApiGetTaskRequest( @@ -898,6 +924,7 @@ func TestIngestion_GetTask(t *testing.T) { func TestIngestion_GetTasks(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("getTasks", func(t *testing.T) { _, err := client.GetTasks(client.NewApiGetTasksRequest()) @@ -912,6 +939,7 @@ func TestIngestion_GetTasks(t *testing.T) { func TestIngestion_RunTask(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("runTask", func(t *testing.T) { _, err := client.RunTask(client.NewApiRunTaskRequest( @@ -928,6 +956,7 @@ func TestIngestion_RunTask(t *testing.T) { func TestIngestion_SearchAuthentications(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("searchAuthentications", func(t *testing.T) { _, err := client.SearchAuthentications(client.NewApiSearchAuthenticationsRequest( @@ -947,6 +976,7 @@ func TestIngestion_SearchAuthentications(t *testing.T) { func TestIngestion_SearchDestinations(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("searchDestinations", func(t *testing.T) { _, err := client.SearchDestinations(client.NewApiSearchDestinationsRequest( @@ -966,6 +996,7 @@ func TestIngestion_SearchDestinations(t *testing.T) { func TestIngestion_SearchSources(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("searchSources", func(t *testing.T) { _, err := client.SearchSources(client.NewApiSearchSourcesRequest( @@ -985,6 +1016,7 @@ func TestIngestion_SearchSources(t *testing.T) { func TestIngestion_SearchTasks(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("searchTasks", func(t *testing.T) { _, err := client.SearchTasks(client.NewApiSearchTasksRequest( @@ -1031,6 +1063,7 @@ func TestIngestion_SearchTasks(t *testing.T) { func TestIngestion_TriggerDockerSourceDiscover(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("triggerDockerSourceDiscover", func(t *testing.T) { _, err := client.TriggerDockerSourceDiscover(client.NewApiTriggerDockerSourceDiscoverRequest( @@ -1047,6 +1080,7 @@ func TestIngestion_TriggerDockerSourceDiscover(t *testing.T) { func TestIngestion_UpdateAuthentication(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("updateAuthentication", func(t *testing.T) { _, err := client.UpdateAuthentication(client.NewApiUpdateAuthenticationRequest( @@ -1065,6 +1099,7 @@ func TestIngestion_UpdateAuthentication(t *testing.T) { func TestIngestion_UpdateDestination(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("updateDestination", func(t *testing.T) { _, err := client.UpdateDestination(client.NewApiUpdateDestinationRequest( @@ -1083,6 +1118,7 @@ func TestIngestion_UpdateDestination(t *testing.T) { func TestIngestion_UpdateSource(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("updateSource", func(t *testing.T) { _, err := client.UpdateSource(client.NewApiUpdateSourceRequest( @@ -1101,6 +1137,7 @@ func TestIngestion_UpdateSource(t *testing.T) { func TestIngestion_UpdateTask(t *testing.T) { client, echo := createIngestionClient(t) + _ = echo t.Run("updateTask", func(t *testing.T) { _, err := client.UpdateTask(client.NewApiUpdateTaskRequest( diff --git a/tests/output/go/tests/requests/insights_test.go b/tests/output/go/tests/requests/insights_test.go index 53f0987c2f..73e202211a 100644 --- a/tests/output/go/tests/requests/insights_test.go +++ b/tests/output/go/tests/requests/insights_test.go @@ -52,6 +52,7 @@ func createE2EInsightsClient(t *testing.T) *insights.APIClient { func TestInsights_CustomDelete(t *testing.T) { client, echo := createInsightsClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -85,6 +86,7 @@ func TestInsights_CustomDelete(t *testing.T) { func TestInsights_CustomGet(t *testing.T) { client, echo := createInsightsClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -143,6 +145,7 @@ func TestInsights_CustomGet(t *testing.T) { func TestInsights_CustomPost(t *testing.T) { client, echo := createInsightsClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -371,6 +374,7 @@ func TestInsights_CustomPost(t *testing.T) { func TestInsights_CustomPut(t *testing.T) { client, echo := createInsightsClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -406,6 +410,7 @@ func TestInsights_CustomPut(t *testing.T) { func TestInsights_DeleteUserToken(t *testing.T) { client, echo := createInsightsClient(t) + _ = echo t.Run("deleteUserToken0", func(t *testing.T) { err := client.DeleteUserToken(client.NewApiDeleteUserTokenRequest( @@ -422,6 +427,7 @@ func TestInsights_DeleteUserToken(t *testing.T) { func TestInsights_PushEvents(t *testing.T) { client, echo := createInsightsClient(t) + _ = echo t.Run("pushEvents0", func(t *testing.T) { _, err := client.PushEvents(client.NewApiPushEventsRequest( diff --git a/tests/output/go/tests/requests/monitoring_test.go b/tests/output/go/tests/requests/monitoring_test.go index 23e6e7c690..f9bfed7281 100644 --- a/tests/output/go/tests/requests/monitoring_test.go +++ b/tests/output/go/tests/requests/monitoring_test.go @@ -32,6 +32,7 @@ func createMonitoringClient(t *testing.T) (*monitoring.APIClient, *tests.EchoReq func TestMonitoring_CustomDelete(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -65,6 +66,7 @@ func TestMonitoring_CustomDelete(t *testing.T) { func TestMonitoring_CustomGet(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -123,6 +125,7 @@ func TestMonitoring_CustomGet(t *testing.T) { func TestMonitoring_CustomPost(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -351,6 +354,7 @@ func TestMonitoring_CustomPost(t *testing.T) { func TestMonitoring_CustomPut(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -386,6 +390,7 @@ func TestMonitoring_CustomPut(t *testing.T) { func TestMonitoring_GetClusterIncidents(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getClusterIncidents", func(t *testing.T) { _, err := client.GetClusterIncidents(client.NewApiGetClusterIncidentsRequest( @@ -402,6 +407,7 @@ func TestMonitoring_GetClusterIncidents(t *testing.T) { func TestMonitoring_GetClusterStatus(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getClusterStatus", func(t *testing.T) { _, err := client.GetClusterStatus(client.NewApiGetClusterStatusRequest( @@ -418,6 +424,7 @@ func TestMonitoring_GetClusterStatus(t *testing.T) { func TestMonitoring_GetIncidents(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getIncidents", func(t *testing.T) { _, err := client.GetIncidents() @@ -432,6 +439,7 @@ func TestMonitoring_GetIncidents(t *testing.T) { func TestMonitoring_GetIndexingTime(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getIndexingTime", func(t *testing.T) { _, err := client.GetIndexingTime(client.NewApiGetIndexingTimeRequest( @@ -448,6 +456,7 @@ func TestMonitoring_GetIndexingTime(t *testing.T) { func TestMonitoring_GetInventory(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getInventory", func(t *testing.T) { _, err := client.GetInventory() @@ -462,6 +471,7 @@ func TestMonitoring_GetInventory(t *testing.T) { func TestMonitoring_GetLatency(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getLatency", func(t *testing.T) { _, err := client.GetLatency(client.NewApiGetLatencyRequest( @@ -478,6 +488,7 @@ func TestMonitoring_GetLatency(t *testing.T) { func TestMonitoring_GetMetrics(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getMetrics", func(t *testing.T) { _, err := client.GetMetrics(client.NewApiGetMetricsRequest( @@ -494,6 +505,7 @@ func TestMonitoring_GetMetrics(t *testing.T) { func TestMonitoring_GetReachability(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getReachability", func(t *testing.T) { _, err := client.GetReachability(client.NewApiGetReachabilityRequest( @@ -510,6 +522,7 @@ func TestMonitoring_GetReachability(t *testing.T) { func TestMonitoring_GetStatus(t *testing.T) { client, echo := createMonitoringClient(t) + _ = echo t.Run("getStatus", func(t *testing.T) { _, err := client.GetStatus() diff --git a/tests/output/go/tests/requests/personalization_test.go b/tests/output/go/tests/requests/personalization_test.go index dada86d6b5..c5a125d2e4 100644 --- a/tests/output/go/tests/requests/personalization_test.go +++ b/tests/output/go/tests/requests/personalization_test.go @@ -33,6 +33,7 @@ func createPersonalizationClient(t *testing.T) (*personalization.APIClient, *tes func TestPersonalization_CustomDelete(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -66,6 +67,7 @@ func TestPersonalization_CustomDelete(t *testing.T) { func TestPersonalization_CustomGet(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -124,6 +126,7 @@ func TestPersonalization_CustomGet(t *testing.T) { func TestPersonalization_CustomPost(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -352,6 +355,7 @@ func TestPersonalization_CustomPost(t *testing.T) { func TestPersonalization_CustomPut(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -387,6 +391,7 @@ func TestPersonalization_CustomPut(t *testing.T) { func TestPersonalization_DeleteUserProfile(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("delete deleteUserProfile", func(t *testing.T) { _, err := client.DeleteUserProfile(client.NewApiDeleteUserProfileRequest( @@ -403,6 +408,7 @@ func TestPersonalization_DeleteUserProfile(t *testing.T) { func TestPersonalization_GetPersonalizationStrategy(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("get getPersonalizationStrategy", func(t *testing.T) { _, err := client.GetPersonalizationStrategy() @@ -417,6 +423,7 @@ func TestPersonalization_GetPersonalizationStrategy(t *testing.T) { func TestPersonalization_GetUserTokenProfile(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("get getUserTokenProfile", func(t *testing.T) { _, err := client.GetUserTokenProfile(client.NewApiGetUserTokenProfileRequest( @@ -433,6 +440,7 @@ func TestPersonalization_GetUserTokenProfile(t *testing.T) { func TestPersonalization_SetPersonalizationStrategy(t *testing.T) { client, echo := createPersonalizationClient(t) + _ = echo t.Run("set setPersonalizationStrategy", func(t *testing.T) { _, err := client.SetPersonalizationStrategy(client.NewApiSetPersonalizationStrategyRequest( diff --git a/tests/output/go/tests/requests/query-suggestions_test.go b/tests/output/go/tests/requests/query-suggestions_test.go index f8473210cf..43b278de9c 100644 --- a/tests/output/go/tests/requests/query-suggestions_test.go +++ b/tests/output/go/tests/requests/query-suggestions_test.go @@ -52,6 +52,7 @@ func createE2ESuggestionsClient(t *testing.T) *suggestions.APIClient { func TestSuggestions_CreateConfig(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("createConfig0", func(t *testing.T) { _, err := client.CreateConfig(client.NewApiCreateConfigRequest( @@ -77,6 +78,7 @@ func TestSuggestions_CreateConfig(t *testing.T) { func TestSuggestions_CustomDelete(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -110,6 +112,7 @@ func TestSuggestions_CustomDelete(t *testing.T) { func TestSuggestions_CustomGet(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -168,6 +171,7 @@ func TestSuggestions_CustomGet(t *testing.T) { func TestSuggestions_CustomPost(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -396,6 +400,7 @@ func TestSuggestions_CustomPost(t *testing.T) { func TestSuggestions_CustomPut(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -431,6 +436,7 @@ func TestSuggestions_CustomPut(t *testing.T) { func TestSuggestions_DeleteConfig(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("deleteConfig0", func(t *testing.T) { _, err := client.DeleteConfig(client.NewApiDeleteConfigRequest( @@ -447,6 +453,7 @@ func TestSuggestions_DeleteConfig(t *testing.T) { func TestSuggestions_GetAllConfigs(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("getAllConfigs0", func(t *testing.T) { _, err := client.GetAllConfigs() @@ -461,6 +468,7 @@ func TestSuggestions_GetAllConfigs(t *testing.T) { func TestSuggestions_GetConfig(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("Retrieve QS config e2e", func(t *testing.T) { _, err := client.GetConfig(client.NewApiGetConfigRequest( @@ -502,6 +510,7 @@ func TestSuggestions_GetConfig(t *testing.T) { func TestSuggestions_GetConfigStatus(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("getConfigStatus0", func(t *testing.T) { _, err := client.GetConfigStatus(client.NewApiGetConfigStatusRequest( @@ -518,6 +527,7 @@ func TestSuggestions_GetConfigStatus(t *testing.T) { func TestSuggestions_GetLogFile(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("getLogFile0", func(t *testing.T) { _, err := client.GetLogFile(client.NewApiGetLogFileRequest( @@ -534,6 +544,7 @@ func TestSuggestions_GetLogFile(t *testing.T) { func TestSuggestions_UpdateConfig(t *testing.T) { client, echo := createSuggestionsClient(t) + _ = echo t.Run("updateConfig0", func(t *testing.T) { _, err := client.UpdateConfig(client.NewApiUpdateConfigRequest( diff --git a/tests/output/go/tests/requests/recommend_test.go b/tests/output/go/tests/requests/recommend_test.go index 59c0f5e536..1d009ae1ba 100644 --- a/tests/output/go/tests/requests/recommend_test.go +++ b/tests/output/go/tests/requests/recommend_test.go @@ -32,6 +32,7 @@ func createRecommendClient(t *testing.T) (*recommend.APIClient, *tests.EchoReque func TestRecommend_CustomDelete(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -65,6 +66,7 @@ func TestRecommend_CustomDelete(t *testing.T) { func TestRecommend_CustomGet(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -123,6 +125,7 @@ func TestRecommend_CustomGet(t *testing.T) { func TestRecommend_CustomPost(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -351,6 +354,7 @@ func TestRecommend_CustomPost(t *testing.T) { func TestRecommend_CustomPut(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -386,6 +390,7 @@ func TestRecommend_CustomPut(t *testing.T) { func TestRecommend_DeleteRecommendRule(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("deleteRecommendRule0", func(t *testing.T) { _, err := client.DeleteRecommendRule(client.NewApiDeleteRecommendRuleRequest( @@ -402,6 +407,7 @@ func TestRecommend_DeleteRecommendRule(t *testing.T) { func TestRecommend_GetRecommendRule(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("getRecommendRule0", func(t *testing.T) { _, err := client.GetRecommendRule(client.NewApiGetRecommendRuleRequest( @@ -418,6 +424,7 @@ func TestRecommend_GetRecommendRule(t *testing.T) { func TestRecommend_GetRecommendStatus(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("getRecommendStatus0", func(t *testing.T) { _, err := client.GetRecommendStatus(client.NewApiGetRecommendStatusRequest( @@ -434,6 +441,7 @@ func TestRecommend_GetRecommendStatus(t *testing.T) { func TestRecommend_GetRecommendations(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("get recommendations for recommend model with minimal parameters", func(t *testing.T) { _, err := client.GetRecommendations(client.NewApiGetRecommendationsRequest( @@ -562,6 +570,7 @@ func TestRecommend_GetRecommendations(t *testing.T) { func TestRecommend_SearchRecommendRules(t *testing.T) { client, echo := createRecommendClient(t) + _ = echo t.Run("searchRecommendRules0", func(t *testing.T) { _, err := client.SearchRecommendRules(client.NewApiSearchRecommendRulesRequest( diff --git a/tests/output/go/tests/requests/search_test.go b/tests/output/go/tests/requests/search_test.go index 7241d561c0..e48cac2047 100644 --- a/tests/output/go/tests/requests/search_test.go +++ b/tests/output/go/tests/requests/search_test.go @@ -51,6 +51,7 @@ func createE2ESearchClient(t *testing.T) *search.APIClient { func TestSearch_AddApiKey(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("addApiKey0", func(t *testing.T) { _, err := client.AddApiKey(client.NewApiAddApiKeyRequest( @@ -70,6 +71,7 @@ func TestSearch_AddApiKey(t *testing.T) { func TestSearch_AddOrUpdateObject(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("addOrUpdateObject0", func(t *testing.T) { _, err := client.AddOrUpdateObject(client.NewApiAddOrUpdateObjectRequest( @@ -87,6 +89,7 @@ func TestSearch_AddOrUpdateObject(t *testing.T) { func TestSearch_AppendSource(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("appendSource0", func(t *testing.T) { _, err := client.AppendSource(client.NewApiAppendSourceRequest( @@ -105,6 +108,7 @@ func TestSearch_AppendSource(t *testing.T) { func TestSearch_AssignUserId(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("assignUserId0", func(t *testing.T) { _, err := client.AssignUserId(client.NewApiAssignUserIdRequest( @@ -146,6 +150,7 @@ func TestSearch_AssignUserId(t *testing.T) { func TestSearch_Batch(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("allows batch method with `addObject` action", func(t *testing.T) { _, err := client.Batch(client.NewApiBatchRequest( @@ -249,6 +254,7 @@ func TestSearch_Batch(t *testing.T) { func TestSearch_BatchAssignUserIds(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("batchAssignUserIds0", func(t *testing.T) { _, err := client.BatchAssignUserIds(client.NewApiBatchAssignUserIdsRequest( @@ -273,6 +279,7 @@ func TestSearch_BatchAssignUserIds(t *testing.T) { func TestSearch_BatchDictionaryEntries(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("get batchDictionaryEntries results with minimal parameters", func(t *testing.T) { _, err := client.BatchDictionaryEntries(client.NewApiBatchDictionaryEntriesRequest( @@ -329,6 +336,7 @@ func TestSearch_BatchDictionaryEntries(t *testing.T) { func TestSearch_Browse(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("browse with minimal parameters", func(t *testing.T) { _, err := client.Browse(client.NewApiBrowseRequest( @@ -398,6 +406,7 @@ func TestSearch_Browse(t *testing.T) { func TestSearch_ClearObjects(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("clearObjects0", func(t *testing.T) { _, err := client.ClearObjects(client.NewApiClearObjectsRequest( @@ -414,6 +423,7 @@ func TestSearch_ClearObjects(t *testing.T) { func TestSearch_ClearRules(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("clearRules0", func(t *testing.T) { _, err := client.ClearRules(client.NewApiClearRulesRequest( @@ -430,6 +440,7 @@ func TestSearch_ClearRules(t *testing.T) { func TestSearch_ClearSynonyms(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("clearSynonyms0", func(t *testing.T) { _, err := client.ClearSynonyms(client.NewApiClearSynonymsRequest( @@ -446,6 +457,7 @@ func TestSearch_ClearSynonyms(t *testing.T) { func TestSearch_CustomDelete(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("allow del method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomDelete(client.NewApiCustomDeleteRequest( @@ -479,6 +491,7 @@ func TestSearch_CustomDelete(t *testing.T) { func TestSearch_CustomGet(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("allow get method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomGet(client.NewApiCustomGetRequest( @@ -537,6 +550,7 @@ func TestSearch_CustomGet(t *testing.T) { func TestSearch_CustomPost(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("allow post method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPost(client.NewApiCustomPostRequest( @@ -765,6 +779,7 @@ func TestSearch_CustomPost(t *testing.T) { func TestSearch_CustomPut(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("allow put method for a custom path with minimal parameters", func(t *testing.T) { _, err := client.CustomPut(client.NewApiCustomPutRequest( @@ -800,6 +815,7 @@ func TestSearch_CustomPut(t *testing.T) { func TestSearch_DeleteApiKey(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("deleteApiKey0", func(t *testing.T) { _, err := client.DeleteApiKey(client.NewApiDeleteApiKeyRequest( @@ -816,6 +832,7 @@ func TestSearch_DeleteApiKey(t *testing.T) { func TestSearch_DeleteBy(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("deleteBy0", func(t *testing.T) { _, err := client.DeleteBy(client.NewApiDeleteByRequest( @@ -834,6 +851,7 @@ func TestSearch_DeleteBy(t *testing.T) { func TestSearch_DeleteIndex(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("deleteIndex0", func(t *testing.T) { _, err := client.DeleteIndex(client.NewApiDeleteIndexRequest( @@ -850,6 +868,7 @@ func TestSearch_DeleteIndex(t *testing.T) { func TestSearch_DeleteObject(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("deleteObject0", func(t *testing.T) { _, err := client.DeleteObject(client.NewApiDeleteObjectRequest( @@ -866,6 +885,7 @@ func TestSearch_DeleteObject(t *testing.T) { func TestSearch_DeleteRule(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("delete rule simple case", func(t *testing.T) { _, err := client.DeleteRule(client.NewApiDeleteRuleRequest( @@ -893,6 +913,7 @@ func TestSearch_DeleteRule(t *testing.T) { func TestSearch_DeleteSource(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("deleteSource0", func(t *testing.T) { _, err := client.DeleteSource(client.NewApiDeleteSourceRequest( @@ -909,6 +930,7 @@ func TestSearch_DeleteSource(t *testing.T) { func TestSearch_DeleteSynonym(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("deleteSynonym0", func(t *testing.T) { _, err := client.DeleteSynonym(client.NewApiDeleteSynonymRequest( @@ -925,6 +947,7 @@ func TestSearch_DeleteSynonym(t *testing.T) { func TestSearch_GetApiKey(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getApiKey0", func(t *testing.T) { _, err := client.GetApiKey(client.NewApiGetApiKeyRequest( @@ -941,6 +964,7 @@ func TestSearch_GetApiKey(t *testing.T) { func TestSearch_GetDictionaryLanguages(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("get getDictionaryLanguages", func(t *testing.T) { _, err := client.GetDictionaryLanguages() @@ -955,6 +979,7 @@ func TestSearch_GetDictionaryLanguages(t *testing.T) { func TestSearch_GetDictionarySettings(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("get getDictionarySettings results", func(t *testing.T) { _, err := client.GetDictionarySettings() @@ -969,6 +994,7 @@ func TestSearch_GetDictionarySettings(t *testing.T) { func TestSearch_GetLogs(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getLogs with minimal parameters", func(t *testing.T) { _, err := client.GetLogs(client.NewApiGetLogsRequest()) @@ -998,6 +1024,7 @@ func TestSearch_GetLogs(t *testing.T) { func TestSearch_GetObject(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getObject0", func(t *testing.T) { _, err := client.GetObject(client.NewApiGetObjectRequest( @@ -1021,6 +1048,7 @@ func TestSearch_GetObject(t *testing.T) { func TestSearch_GetObjects(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getObjects0", func(t *testing.T) { _, err := client.GetObjects(client.NewApiGetObjectsRequest( @@ -1041,6 +1069,7 @@ func TestSearch_GetObjects(t *testing.T) { func TestSearch_GetRule(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getRule0", func(t *testing.T) { _, err := client.GetRule(client.NewApiGetRuleRequest( @@ -1057,6 +1086,7 @@ func TestSearch_GetRule(t *testing.T) { func TestSearch_GetSettings(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getSettings0", func(t *testing.T) { _, err := client.GetSettings(client.NewApiGetSettingsRequest( @@ -1098,6 +1128,7 @@ func TestSearch_GetSettings(t *testing.T) { func TestSearch_GetSources(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getSources0", func(t *testing.T) { _, err := client.GetSources() @@ -1112,6 +1143,7 @@ func TestSearch_GetSources(t *testing.T) { func TestSearch_GetSynonym(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getSynonym0", func(t *testing.T) { _, err := client.GetSynonym(client.NewApiGetSynonymRequest( @@ -1128,6 +1160,7 @@ func TestSearch_GetSynonym(t *testing.T) { func TestSearch_GetTask(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getTask0", func(t *testing.T) { _, err := client.GetTask(client.NewApiGetTaskRequest( @@ -1144,6 +1177,7 @@ func TestSearch_GetTask(t *testing.T) { func TestSearch_GetTopUserIds(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getTopUserIds0", func(t *testing.T) { _, err := client.GetTopUserIds() @@ -1158,6 +1192,7 @@ func TestSearch_GetTopUserIds(t *testing.T) { func TestSearch_GetUserId(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("getUserId0", func(t *testing.T) { _, err := client.GetUserId(client.NewApiGetUserIdRequest( @@ -1174,6 +1209,7 @@ func TestSearch_GetUserId(t *testing.T) { func TestSearch_HasPendingMappings(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("hasPendingMappings with minimal parameters", func(t *testing.T) { _, err := client.HasPendingMappings(client.NewApiHasPendingMappingsRequest()) @@ -1203,6 +1239,7 @@ func TestSearch_HasPendingMappings(t *testing.T) { func TestSearch_ListApiKeys(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("listApiKeys0", func(t *testing.T) { _, err := client.ListApiKeys() @@ -1217,6 +1254,7 @@ func TestSearch_ListApiKeys(t *testing.T) { func TestSearch_ListClusters(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("listClusters0", func(t *testing.T) { _, err := client.ListClusters() @@ -1231,6 +1269,7 @@ func TestSearch_ListClusters(t *testing.T) { func TestSearch_ListIndices(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("listIndices with minimal parameters", func(t *testing.T) { _, err := client.ListIndices(client.NewApiListIndicesRequest()) @@ -1260,6 +1299,7 @@ func TestSearch_ListIndices(t *testing.T) { func TestSearch_ListUserIds(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("listUserIds with minimal parameters", func(t *testing.T) { _, err := client.ListUserIds(client.NewApiListUserIdsRequest()) @@ -1289,6 +1329,7 @@ func TestSearch_ListUserIds(t *testing.T) { func TestSearch_MultipleBatch(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("multipleBatch0", func(t *testing.T) { _, err := client.MultipleBatch(client.NewApiMultipleBatchRequest( @@ -1308,6 +1349,7 @@ func TestSearch_MultipleBatch(t *testing.T) { func TestSearch_OperationIndex(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("operationIndex0", func(t *testing.T) { _, err := client.OperationIndex(client.NewApiOperationIndexRequest( @@ -1327,6 +1369,7 @@ func TestSearch_OperationIndex(t *testing.T) { func TestSearch_PartialUpdateObject(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("partialUpdateObject0", func(t *testing.T) { _, err := client.PartialUpdateObject(client.NewApiPartialUpdateObjectRequest( @@ -1351,6 +1394,7 @@ func TestSearch_PartialUpdateObject(t *testing.T) { func TestSearch_RemoveUserId(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("removeUserId0", func(t *testing.T) { _, err := client.RemoveUserId(client.NewApiRemoveUserIdRequest( @@ -1367,6 +1411,7 @@ func TestSearch_RemoveUserId(t *testing.T) { func TestSearch_ReplaceSources(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("replaceSources0", func(t *testing.T) { _, err := client.ReplaceSources(client.NewApiReplaceSourcesRequest( @@ -1385,6 +1430,7 @@ func TestSearch_ReplaceSources(t *testing.T) { func TestSearch_RestoreApiKey(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("restoreApiKey0", func(t *testing.T) { _, err := client.RestoreApiKey(client.NewApiRestoreApiKeyRequest( @@ -1401,6 +1447,7 @@ func TestSearch_RestoreApiKey(t *testing.T) { func TestSearch_SaveObject(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("saveObject0", func(t *testing.T) { _, err := client.SaveObject(client.NewApiSaveObjectRequest( @@ -1418,6 +1465,7 @@ func TestSearch_SaveObject(t *testing.T) { func TestSearch_SaveRule(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("saveRule with minimal parameters", func(t *testing.T) { _, err := client.SaveRule(client.NewApiSaveRuleRequest( @@ -1468,6 +1516,7 @@ func TestSearch_SaveRule(t *testing.T) { func TestSearch_SaveRules(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("saveRules with minimal parameters", func(t *testing.T) { _, err := client.SaveRules(client.NewApiSaveRulesRequest( @@ -1519,6 +1568,7 @@ func TestSearch_SaveRules(t *testing.T) { func TestSearch_SaveSynonym(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("saveSynonym0", func(t *testing.T) { _, err := client.SaveSynonym(client.NewApiSaveSynonymRequest( @@ -1544,6 +1594,7 @@ func TestSearch_SaveSynonym(t *testing.T) { func TestSearch_SaveSynonyms(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("saveSynonyms0", func(t *testing.T) { _, err := client.SaveSynonyms(client.NewApiSaveSynonymsRequest( @@ -1570,6 +1621,7 @@ func TestSearch_SaveSynonyms(t *testing.T) { func TestSearch_Search(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("search for a single hits request with minimal parameters", func(t *testing.T) { _, err := client.Search(client.NewApiSearchRequest( @@ -1800,6 +1852,7 @@ func TestSearch_Search(t *testing.T) { func TestSearch_SearchDictionaryEntries(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("get searchDictionaryEntries results with minimal parameters", func(t *testing.T) { _, err := client.SearchDictionaryEntries(client.NewApiSearchDictionaryEntriesRequest( @@ -1831,6 +1884,7 @@ func TestSearch_SearchDictionaryEntries(t *testing.T) { func TestSearch_SearchForFacetValues(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("get searchForFacetValues results with minimal parameters", func(t *testing.T) { _, err := client.SearchForFacetValues(client.NewApiSearchForFacetValuesRequest( @@ -1861,6 +1915,7 @@ func TestSearch_SearchForFacetValues(t *testing.T) { func TestSearch_SearchRules(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("searchRules0", func(t *testing.T) { _, err := client.SearchRules(client.NewApiSearchRulesRequest( @@ -1879,6 +1934,7 @@ func TestSearch_SearchRules(t *testing.T) { func TestSearch_SearchSingleIndex(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("search with minimal parameters", func(t *testing.T) { _, err := client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest( @@ -1972,6 +2028,7 @@ func TestSearch_SearchSingleIndex(t *testing.T) { func TestSearch_SearchSynonyms(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("searchSynonyms with minimal parameters", func(t *testing.T) { _, err := client.SearchSynonyms(client.NewApiSearchSynonymsRequest( @@ -2002,6 +2059,7 @@ func TestSearch_SearchSynonyms(t *testing.T) { func TestSearch_SearchUserIds(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("searchUserIds0", func(t *testing.T) { _, err := client.SearchUserIds(client.NewApiSearchUserIdsRequest( @@ -2020,6 +2078,7 @@ func TestSearch_SearchUserIds(t *testing.T) { func TestSearch_SetDictionarySettings(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("get setDictionarySettings results with minimal parameters", func(t *testing.T) { _, err := client.SetDictionarySettings(client.NewApiSetDictionarySettingsRequest( @@ -2053,6 +2112,7 @@ func TestSearch_SetDictionarySettings(t *testing.T) { func TestSearch_SetSettings(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("setSettings with minimal parameters", func(t *testing.T) { _, err := client.SetSettings(client.NewApiSetSettingsRequest( @@ -2281,6 +2341,7 @@ func TestSearch_SetSettings(t *testing.T) { func TestSearch_UpdateApiKey(t *testing.T) { client, echo := createSearchClient(t) + _ = echo t.Run("updateApiKey0", func(t *testing.T) { _, err := client.UpdateApiKey(client.NewApiUpdateApiKeyRequest( diff --git a/tests/output/javascript/src/requests/search.test.ts b/tests/output/javascript/src/requests/search.test.ts index 01e27a7a35..997dc09318 100644 --- a/tests/output/javascript/src/requests/search.test.ts +++ b/tests/output/javascript/src/requests/search.test.ts @@ -2666,7 +2666,7 @@ describe('generateSecuredApiKey', () => { restrictions: { validUntil: 100, restrictIndices: ['bar'], - restrictSources: 'baz', + restrictSources: '192,168.1.0/24', userToken: 'foobarbaz', searchParams: { query: 'foo', @@ -2674,7 +2674,7 @@ describe('generateSecuredApiKey', () => { }, }); expect(resp).toEqual( - 'YzY5YmVhMGVhYWYyYjNhYzMxYmVmNDA2Y2NiNjZhZTk2MDFkYjMxZGEyOGRlMDg5ZDFmNzBlYTBjMjY3Zjg3ZnZhbGlkVW50aWw9MTAwJnJlc3RyaWN0SW5kaWNlcz0lNUIlMjJiYXIlMjIlNUQmcmVzdHJpY3RTb3VyY2VzPWJheiZ1c2VyVG9rZW49Zm9vYmFyYmF6JnNlYXJjaFBhcmFtcz0lN0IlMjJxdWVyeSUyMiUzQSUyMmZvbyUyMiU3RA==' + 'M2RlY2Y5ZjgzMDU1ZDRiYjkyOTdjYjYxYWNjNWNhNTQ5ZGI5Mjc3ZmVjNmNmNjM2ZjAwMTA4OGRjNDI5YjFhOXZhbGlkVW50aWw9MTAwJnJlc3RyaWN0SW5kaWNlcz0lNUIlMjJiYXIlMjIlNUQmcmVzdHJpY3RTb3VyY2VzPTE5MiUyQzE2OC4xLjAlMkYyNCZ1c2VyVG9rZW49Zm9vYmFyYmF6JnNlYXJjaFBhcmFtcz0lN0IlMjJxdWVyeSUyMiUzQSUyMmZvbyUyMiU3RA==' ); }); }); diff --git a/tests/output/python/tests/requests/search_test.py b/tests/output/python/tests/requests/search_test.py index cb3c087de1..c9a29d0371 100644 --- a/tests/output/python/tests/requests/search_test.py +++ b/tests/output/python/tests/requests/search_test.py @@ -3,12 +3,14 @@ from time import time from unittest.mock import AsyncMock -from algoliasearch.http.helpers import SecuredApiKeyRestrictions from algoliasearch.http.transporter import EchoTransporter from algoliasearch.search.client import SearchClient from algoliasearch.search.config import SearchConfig from algoliasearch.search.models.batch_response import BatchResponse from algoliasearch.search.models.get_task_response import GetTaskResponse +from algoliasearch.search.models.secured_api_key_restrictions import ( + SecuredAPIKeyRestrictions, +) from algoliasearch.search.models.updated_at_response import UpdatedAtResponse from dotenv import load_dotenv @@ -3008,7 +3010,7 @@ def test_generate_secured_api_key_0(self): _resp = self._client.generate_secured_api_key(parent_api_key="foo") assert ( _resp - == "Y2M1MGVkOGY0Yjg2YzE4MDJjYTMwOGU2YTMzNjg1MTA4Y2UwNjkwZmNjODMzYzhjN2FhNGE3M2FhZTdiMjhiZnsic2VhcmNoX3BhcmFtcyI6ICJ7XCJxdWVyeVwiOiBcIlwiLCBcInNpbWlsYXJRdWVyeVwiOiBcIlwiLCBcImZpbHRlcnNcIjogXCJcIiwgXCJzdW1PckZpbHRlcnNTY29yZXNcIjogZmFsc2UsIFwiZmFjZXRpbmdBZnRlckRpc3RpbmN0XCI6IGZhbHNlLCBcInBhZ2VcIjogMCwgXCJhcm91bmRMYXRMbmdcIjogXCJcIiwgXCJhcm91bmRMYXRMbmdWaWFJUFwiOiBmYWxzZSwgXCJwZXJzb25hbGl6YXRpb25JbXBhY3RcIjogMTAwLCBcImdldFJhbmtpbmdJbmZvXCI6IGZhbHNlLCBcInN5bm9ueW1zXCI6IHRydWUsIFwiY2xpY2tBbmFseXRpY3NcIjogZmFsc2UsIFwiYW5hbHl0aWNzXCI6IHRydWUsIFwicGVyY2VudGlsZUNvbXB1dGF0aW9uXCI6IHRydWUsIFwiZW5hYmxlQUJUZXN0XCI6IHRydWUsIFwicmVsZXZhbmN5U3RyaWN0bmVzc1wiOiAxMDAsIFwiaGlnaGxpZ2h0UHJlVGFnXCI6IFwiPGVtPlwiLCBcImhpZ2hsaWdodFBvc3RUYWdcIjogXCI8L2VtPlwiLCBcInNuaXBwZXRFbGxpcHNpc1RleHRcIjogXCJcXHUyMDI2XCIsIFwicmVzdHJpY3RIaWdobGlnaHRBbmRTbmlwcGV0QXJyYXlzXCI6IGZhbHNlLCBcImhpdHNQZXJQYWdlXCI6IDIwLCBcIm1pbldvcmRTaXplZm9yMVR5cG9cIjogNCwgXCJtaW5Xb3JkU2l6ZWZvcjJUeXBvc1wiOiA4LCBcImFsbG93VHlwb3NPbk51bWVyaWNUb2tlbnNcIjogdHJ1ZSwgXCJrZWVwRGlhY3JpdGljc09uQ2hhcmFjdGVyc1wiOiBcIlwiLCBcImRlY29tcG91bmRRdWVyeVwiOiB0cnVlLCBcImVuYWJsZVJ1bGVzXCI6IHRydWUsIFwiZW5hYmxlUGVyc29uYWxpemF0aW9uXCI6IGZhbHNlLCBcImFkdmFuY2VkU3ludGF4XCI6IGZhbHNlLCBcInJlcGxhY2VTeW5vbnltc0luSGlnaGxpZ2h0XCI6IGZhbHNlLCBcIm1pblByb3hpbWl0eVwiOiAxLCBcIm1heEZhY2V0SGl0c1wiOiAxMCwgXCJtYXhWYWx1ZXNQZXJGYWNldFwiOiAxMDAsIFwic29ydEZhY2V0VmFsdWVzQnlcIjogXCJjb3VudFwiLCBcImF0dHJpYnV0ZUNyaXRlcmlhQ29tcHV0ZWRCeU1pblByb3hpbWl0eVwiOiBmYWxzZSwgXCJlbmFibGVSZVJhbmtpbmdcIjogdHJ1ZX0iLCAidmFsaWRfdW50aWwiOiAiMCJ9" + == "Yzc2MzU2ZWZhMTlkMjE5ZDFkN2UwOGNjYjIwYjFkMjZkYjUzYjE0MzE1NmY0MDZjOTlkY2I4ZTA4NzZkNmM1NXt9" ) def test_generate_secured_api_key_1(self): @@ -3036,7 +3038,7 @@ def test_generate_secured_api_key_2(self): """ _resp = self._client.generate_secured_api_key( parent_api_key="bar", - restrictions=SecuredApiKeyRestrictions( + restrictions=SecuredAPIKeyRestrictions( search_params={"query": "bar", "page": 3}, valid_until=42, restrict_indices=["baz"], @@ -3046,14 +3048,16 @@ def test_generate_secured_api_key_2(self): ) assert ( _resp - == "NmY4NDZkZWM2OWI0YjRkYzdhNjgzOWY3NmQ3ZGRhNjNkYzBjZWRjNTYxY2NkOWEyOGQxNmEyOGNjMWIyYzJkYnsic2VhcmNoX3BhcmFtcyI6ICJ7XCJxdWVyeVwiOiBcImJhclwiLCBcInNpbWlsYXJRdWVyeVwiOiBcIlwiLCBcImZpbHRlcnNcIjogXCJcIiwgXCJzdW1PckZpbHRlcnNTY29yZXNcIjogZmFsc2UsIFwiZmFjZXRpbmdBZnRlckRpc3RpbmN0XCI6IGZhbHNlLCBcInBhZ2VcIjogMywgXCJhcm91bmRMYXRMbmdcIjogXCJcIiwgXCJhcm91bmRMYXRMbmdWaWFJUFwiOiBmYWxzZSwgXCJwZXJzb25hbGl6YXRpb25JbXBhY3RcIjogMTAwLCBcImdldFJhbmtpbmdJbmZvXCI6IGZhbHNlLCBcInN5bm9ueW1zXCI6IHRydWUsIFwiY2xpY2tBbmFseXRpY3NcIjogZmFsc2UsIFwiYW5hbHl0aWNzXCI6IHRydWUsIFwicGVyY2VudGlsZUNvbXB1dGF0aW9uXCI6IHRydWUsIFwiZW5hYmxlQUJUZXN0XCI6IHRydWUsIFwicmVsZXZhbmN5U3RyaWN0bmVzc1wiOiAxMDAsIFwiaGlnaGxpZ2h0UHJlVGFnXCI6IFwiPGVtPlwiLCBcImhpZ2hsaWdodFBvc3RUYWdcIjogXCI8L2VtPlwiLCBcInNuaXBwZXRFbGxpcHNpc1RleHRcIjogXCJcXHUyMDI2XCIsIFwicmVzdHJpY3RIaWdobGlnaHRBbmRTbmlwcGV0QXJyYXlzXCI6IGZhbHNlLCBcImhpdHNQZXJQYWdlXCI6IDIwLCBcIm1pbldvcmRTaXplZm9yMVR5cG9cIjogNCwgXCJtaW5Xb3JkU2l6ZWZvcjJUeXBvc1wiOiA4LCBcImFsbG93VHlwb3NPbk51bWVyaWNUb2tlbnNcIjogdHJ1ZSwgXCJrZWVwRGlhY3JpdGljc09uQ2hhcmFjdGVyc1wiOiBcIlwiLCBcImRlY29tcG91bmRRdWVyeVwiOiB0cnVlLCBcImVuYWJsZVJ1bGVzXCI6IHRydWUsIFwiZW5hYmxlUGVyc29uYWxpemF0aW9uXCI6IGZhbHNlLCBcImFkdmFuY2VkU3ludGF4XCI6IGZhbHNlLCBcInJlcGxhY2VTeW5vbnltc0luSGlnaGxpZ2h0XCI6IGZhbHNlLCBcIm1pblByb3hpbWl0eVwiOiAxLCBcIm1heEZhY2V0SGl0c1wiOiAxMCwgXCJtYXhWYWx1ZXNQZXJGYWNldFwiOiAxMDAsIFwic29ydEZhY2V0VmFsdWVzQnlcIjogXCJjb3VudFwiLCBcImF0dHJpYnV0ZUNyaXRlcmlhQ29tcHV0ZWRCeU1pblByb3hpbWl0eVwiOiBmYWxzZSwgXCJlbmFibGVSZVJhbmtpbmdcIjogdHJ1ZX0iLCAidmFsaWRfdW50aWwiOiAiNDIiLCAicmVzdHJpY3RfaW5kaWNlcyI6ICJiYXoiLCAicmVzdHJpY3Rfc291cmNlcyI6ICJmb28iLCAidXNlcl90b2tlbiI6ICJiYXpiYXJmb28ifQ==" + == "Y2ZhNWM0Y2MxNjc1NTE2YjhiZjdlMGU5YWE1OGViOTk5MTdjMGU1YjRhNDU2NTczOWI5ZGE5Y2NjMTJmMDE0YXsic2VhcmNoUGFyYW1zIjogIntcInF1ZXJ5XCI6IFwiYmFyXCIsIFwic2ltaWxhclF1ZXJ5XCI6IFwiXCIsIFwiZmlsdGVyc1wiOiBcIlwiLCBcInN1bU9yRmlsdGVyc1Njb3Jlc1wiOiBmYWxzZSwgXCJmYWNldGluZ0FmdGVyRGlzdGluY3RcIjogZmFsc2UsIFwicGFnZVwiOiAzLCBcImFyb3VuZExhdExuZ1wiOiBcIlwiLCBcImFyb3VuZExhdExuZ1ZpYUlQXCI6IGZhbHNlLCBcInBlcnNvbmFsaXphdGlvbkltcGFjdFwiOiAxMDAsIFwiZ2V0UmFua2luZ0luZm9cIjogZmFsc2UsIFwic3lub255bXNcIjogdHJ1ZSwgXCJjbGlja0FuYWx5dGljc1wiOiBmYWxzZSwgXCJhbmFseXRpY3NcIjogdHJ1ZSwgXCJwZXJjZW50aWxlQ29tcHV0YXRpb25cIjogdHJ1ZSwgXCJlbmFibGVBQlRlc3RcIjogdHJ1ZSwgXCJyZWxldmFuY3lTdHJpY3RuZXNzXCI6IDEwMCwgXCJoaWdobGlnaHRQcmVUYWdcIjogXCI8ZW0+XCIsIFwiaGlnaGxpZ2h0UG9zdFRhZ1wiOiBcIjwvZW0+XCIsIFwic25pcHBldEVsbGlwc2lzVGV4dFwiOiBcIlxcdTIwMjZcIiwgXCJyZXN0cmljdEhpZ2hsaWdodEFuZFNuaXBwZXRBcnJheXNcIjogZmFsc2UsIFwiaGl0c1BlclBhZ2VcIjogMjAsIFwibWluV29yZFNpemVmb3IxVHlwb1wiOiA0LCBcIm1pbldvcmRTaXplZm9yMlR5cG9zXCI6IDgsIFwiYWxsb3dUeXBvc09uTnVtZXJpY1Rva2Vuc1wiOiB0cnVlLCBcImtlZXBEaWFjcml0aWNzT25DaGFyYWN0ZXJzXCI6IFwiXCIsIFwiZGVjb21wb3VuZFF1ZXJ5XCI6IHRydWUsIFwiZW5hYmxlUnVsZXNcIjogdHJ1ZSwgXCJlbmFibGVQZXJzb25hbGl6YXRpb25cIjogZmFsc2UsIFwiYWR2YW5jZWRTeW50YXhcIjogZmFsc2UsIFwicmVwbGFjZVN5bm9ueW1zSW5IaWdobGlnaHRcIjogZmFsc2UsIFwibWluUHJveGltaXR5XCI6IDEsIFwibWF4RmFjZXRIaXRzXCI6IDEwLCBcIm1heFZhbHVlc1BlckZhY2V0XCI6IDEwMCwgXCJzb3J0RmFjZXRWYWx1ZXNCeVwiOiBcImNvdW50XCIsIFwiYXR0cmlidXRlQ3JpdGVyaWFDb21wdXRlZEJ5TWluUHJveGltaXR5XCI6IGZhbHNlLCBcImVuYWJsZVJlUmFua2luZ1wiOiB0cnVlfSIsICJ2YWxpZFVudGlsIjogIjQyIiwgInJlc3RyaWN0SW5kaWNlcyI6ICJiYXoiLCAicmVzdHJpY3RTb3VyY2VzIjogImZvbyIsICJ1c2VyVG9rZW4iOiAiYmF6YmFyZm9vIn0=" ) def test_generate_secured_api_key_and_validity_0(self): """ is able to check the remaining validity of a key """ - _resp = self._client.generate_secured_api_key(parent_api_key="foo") + _resp = self._client.generate_secured_api_key( + parent_api_key="foo", restrictions={"valid_until": 0} + ) _validity = self._client.get_secured_api_key_remaining_validity(_resp) assert abs(_validity) == int(round(time())) From 18e8248876676bf7b61747f60b17f7c9333e217d Mon Sep 17 00:00:00 2001 From: Morgan Leroi Date: Tue, 27 Feb 2024 18:01:36 +0100 Subject: [PATCH 3/3] feat(csharp): add chunked batch (#2795) --- .github/.cache_version | 2 +- .../algoliasearch/Utils/ClientExtensions.cs | 97 +++++++++++++++---- .../Utils/ReplaceAllObjectsResponse.cs | 25 +++++ .../csharp/src/ClientExtensionsTests.cs | 25 ++++- 4 files changed, 127 insertions(+), 22 deletions(-) create mode 100644 clients/algoliasearch-client-csharp/algoliasearch/Utils/ReplaceAllObjectsResponse.cs diff --git a/.github/.cache_version b/.github/.cache_version index f8f3c08725..140333f6da 100644 --- a/.github/.cache_version +++ b/.github/.cache_version @@ -1 +1 @@ -1.0.18 +1.0.19 diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs b/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs index b59ca3a2a1..b976f0060a 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Utils/ClientExtensions.cs @@ -425,12 +425,15 @@ private static async Task RetryUntil(Func> func, Func val /// /// /// The index in which to perform the request. - /// The list of records to replace. + /// The list of `objects` to store in the given Algolia `indexName`. + /// The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000. /// Add extra http header or query parameters to Algolia. /// Cancellation Token to cancel the request. - public static List ReplaceAllObjects(this SearchClient client, string indexName, - IEnumerable data, RequestOptions options = null, CancellationToken cancellationToken = default) where T : class - => AsyncHelper.RunSync(() => client.ReplaceAllObjectsAsync(indexName, data, options, cancellationToken)); + public static ReplaceAllObjectsResponse ReplaceAllObjects(this SearchClient client, string indexName, + IEnumerable objects, int batchSize = 1000, RequestOptions options = null, + CancellationToken cancellationToken = default) where T : class + => AsyncHelper.RunSync(() => + client.ReplaceAllObjectsAsync(indexName, objects, batchSize, options, cancellationToken)); /// /// Push a new set of objects and remove all previous ones. Settings, synonyms and query rules are untouched. @@ -439,15 +442,17 @@ public static List ReplaceAllObjects(this SearchClient client, string i /// /// /// The index in which to perform the request. - /// The list of records to replace. + /// The list of `objects` to store in the given Algolia `indexName`. + /// The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000. /// Add extra http header or query parameters to Algolia. /// Cancellation Token to cancel the request. - public static async Task> ReplaceAllObjectsAsync(this SearchClient client, string indexName, - IEnumerable data, RequestOptions options = null, CancellationToken cancellationToken = default) where T : class + public static async Task ReplaceAllObjectsAsync(this SearchClient client, + string indexName, IEnumerable objects, int batchSize = 1000, RequestOptions options = null, + CancellationToken cancellationToken = default) where T : class { - if (data == null) + if (objects == null) { - throw new ArgumentNullException(nameof(data)); + throw new ArgumentNullException(nameof(objects)); } var rnd = new Random(); @@ -456,26 +461,84 @@ public static async Task> ReplaceAllObjectsAsync(this SearchClient // Copy settings, synonyms and query rules into the temporary index var copyResponse = await client.OperationIndexAsync(indexName, new OperationIndexParams(OperationType.Copy, tmpIndexName) - { Scope = [ScopeType.Rules, ScopeType.Settings, ScopeType.Synonyms] }, options, cancellationToken) + { Scope = [ScopeType.Rules, ScopeType.Settings, ScopeType.Synonyms] }, options, cancellationToken) .ConfigureAwait(false); - await client.WaitForTaskAsync(indexName, copyResponse.TaskID, requestOptions: options, ct: cancellationToken).ConfigureAwait(false); + await client.WaitForTaskAsync(indexName, copyResponse.TaskID, requestOptions: options, ct: cancellationToken) + .ConfigureAwait(false); // Add objects to the temporary index - var batchResponse = await client.BatchAsync(tmpIndexName, - new BatchWriteParams(data.Select(x => new BatchRequest(Action.AddObject, x)).ToList()), + var batchResponse = await client.ChunkedBatchAsync(tmpIndexName, objects, Action.AddObject, batchSize, options, cancellationToken).ConfigureAwait(false); - await client.WaitForTaskAsync(tmpIndexName, batchResponse.TaskID, requestOptions: options, ct: cancellationToken).ConfigureAwait(false); - // Move the temporary index to the main one var moveResponse = await client.OperationIndexAsync(tmpIndexName, new OperationIndexParams(OperationType.Move, indexName), options, cancellationToken) .ConfigureAwait(false); - await client.WaitForTaskAsync(tmpIndexName, moveResponse.TaskID, requestOptions: options, ct: cancellationToken).ConfigureAwait(false); + await client.WaitForTaskAsync(tmpIndexName, moveResponse.TaskID, requestOptions: options, ct: cancellationToken) + .ConfigureAwait(false); + + return new ReplaceAllObjectsResponse + { + CopyOperationResponse = copyResponse, + MoveOperationResponse = moveResponse, + BatchResponses = batchResponse + }; + } + + /// + /// Helper: Chunks the given `objects` list in subset of 1000 elements max in order to make it fit in `batch` requests. (Synchronous version) + /// + /// + /// The index in which to perform the request. + /// The list of `objects` to store in the given Algolia `indexName`. + /// The `batch` `action` to perform on the given array of `objects`, defaults to `addObject`. + /// The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000. + /// Add extra http header or query parameters to Algolia. + /// Cancellation Token to cancel the request. + /// + public static List ChunkedBatch(this SearchClient client, string indexName, + IEnumerable objects, Action action = Action.AddObject, int batchSize = 1000, RequestOptions options = null, + CancellationToken cancellationToken = default) where T : class => + AsyncHelper.RunSync(() => + client.ChunkedBatchAsync(indexName, objects, action, batchSize, options, cancellationToken)); + + /// + /// Helper: Chunks the given `objects` list in subset of 1000 elements max in order to make it fit in `batch` requests. + /// + /// + /// The index in which to perform the request. + /// The list of `objects` to store in the given Algolia `indexName`. + /// The `batch` `action` to perform on the given array of `objects`, defaults to `addObject`. + /// The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000. + /// Add extra http header or query parameters to Algolia. + /// Cancellation Token to cancel the request. + /// + public static async Task> ChunkedBatchAsync(this SearchClient client, string indexName, + IEnumerable objects, Action action = Action.AddObject, int batchSize = 1000, RequestOptions options = null, + CancellationToken cancellationToken = default) where T : class + { + var batchCount = (int)Math.Ceiling((double)objects.Count() / batchSize); + var responses = new List(); - return [copyResponse.TaskID, batchResponse.TaskID, moveResponse.TaskID]; + for (var i = 0; i < batchCount; i++) + { + var chunk = objects.Skip(i * batchSize).Take(batchSize); + var batchResponse = await client.BatchAsync(indexName, + new BatchWriteParams(chunk.Select(x => new BatchRequest(action, x)).ToList()), + options, cancellationToken).ConfigureAwait(false); + + responses.Add(batchResponse); + } + + foreach (var batch in responses) + { + await client.WaitForTaskAsync(indexName, batch.TaskID, requestOptions: options, ct: cancellationToken) + .ConfigureAwait(false); + } + + return responses; } private static int NextDelay(int retryCount) diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Utils/ReplaceAllObjectsResponse.cs b/clients/algoliasearch-client-csharp/algoliasearch/Utils/ReplaceAllObjectsResponse.cs new file mode 100644 index 0000000000..3d47233e95 --- /dev/null +++ b/clients/algoliasearch-client-csharp/algoliasearch/Utils/ReplaceAllObjectsResponse.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Algolia.Search.Models.Search; + +namespace Algolia.Search.Utils; + +/// +/// All responses from the ReplaceAllObjects calls. +/// +public class ReplaceAllObjectsResponse +{ + /// + /// The response of the copy operation. + /// + public UpdatedAtResponse CopyOperationResponse { get; set; } + + /// + /// The response of the batch operations. + /// + public List BatchResponses { get; set; } + + /// + /// The response of the move operation. + /// + public UpdatedAtResponse MoveOperationResponse { get; set; } +} diff --git a/tests/output/csharp/src/ClientExtensionsTests.cs b/tests/output/csharp/src/ClientExtensionsTests.cs index 9910b9a094..b7447e3595 100644 --- a/tests/output/csharp/src/ClientExtensionsTests.cs +++ b/tests/output/csharp/src/ClientExtensionsTests.cs @@ -615,7 +615,7 @@ public async Task ShouldReplaceAllObjects() HttpStatusCode = 200, Body = new MemoryStream( Encoding.UTF8.GetBytes( - serializer.Serialize(new UpdatedAtResponse(3, "2021-01-01T00:00:00Z")) + serializer.Serialize(new UpdatedAtResponse(4, "2021-01-01T00:00:00Z")) ) ) } @@ -635,6 +635,10 @@ public async Task ShouldReplaceAllObjects() r.Uri.AbsolutePath, "1\\/indexes\\/my-test-index_tmp_[0-9]+\\/task\\/3" ) + || Regex.IsMatch( + r.Uri.AbsolutePath, + "1\\/indexes\\/my-test-index_tmp_[0-9]+\\/task\\/4" + ) ), It.IsAny(), It.IsAny(), @@ -657,7 +661,7 @@ public async Task ShouldReplaceAllObjects() ); httpMock - .Setup(c => + .SetupSequence(c => c.SendRequestAsync( It.Is(r => Regex.IsMatch(r.Uri.AbsolutePath, "1\\/indexes\\/my-test-index_tmp_[0-9]+\\/batch") @@ -677,12 +681,25 @@ public async Task ShouldReplaceAllObjects() ) } ) + ).Returns( + Task.FromResult( + new AlgoliaHttpResponse + { + HttpStatusCode = 200, + Body = new MemoryStream( + Encoding.UTF8.GetBytes(serializer.Serialize(new BatchResponse(3, []))) + ) + } + ) ); - var results = await client.ReplaceAllObjectsAsync("my-test-index", new List { }); + var results = + await client.ReplaceAllObjectsAsync("my-test-index", new List { new(), new(), new() }, batchSize: 2); httpMock.VerifyAll(); - Assert.Equivalent(results, new List { 1, 2, 3 }); + Assert.Equal(1, results.CopyOperationResponse.TaskID); + Assert.Equal([2,3], results.BatchResponses.Select(r => r.TaskID)); + Assert.Equal(4, results.MoveOperationResponse.TaskID); } }