From d7e20755f1b1d2b075763eb87140457663112caa Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 17 Jun 2024 17:31:37 +0200 Subject: [PATCH] feat(cts): generate tests for helpers (#2798) --- .../Common/SecuredApiKeyRestrictionHelper.cs | 21 +++---- .../Utils/SearchClientExtensions.cs | 14 ++--- .../client-common/src/transporter/helpers.ts | 10 ++- .../algolia/client/extensions/SearchClient.kt | 8 +-- .../extensions/internal/SearchClient.kt | 6 +- .../com/algolia/client/TestSecureApiKey.kt | 8 +-- .../algoliasearch/http/serializer.py | 18 ++++-- .../Search/Extra/SearchClientExtension.swift | 8 +-- .../SecuredApiKeyRestrictionExtension.swift | 2 +- .../codegen/cts/tests/TestsClient.java | 24 ++++--- .../codegen/cts/tests/TestsRequest.java | 2 +- .../csharp/Playground/Playgrounds/Search.cs | 2 +- .../swift/playground/playground/main.swift | 2 +- scripts/cts/runCts.ts | 4 +- .../search/helpers/generateSecuredApiKey.yml | 7 ++- templates/csharp/tests/client/method.mustache | 2 +- templates/csharp/tests/client/suite.mustache | 5 ++ templates/dart/tests/client/method.mustache | 5 ++ templates/dart/tests/client/suite.mustache | 2 + templates/go/api.mustache | 6 +- templates/go/search_helpers.mustache | 6 +- templates/go/tests/client/method.mustache | 4 +- templates/go/tests/client/suite.mustache | 19 +++--- templates/go/tests/requests/helpers.mustache | 6 +- templates/java/api_helpers.mustache | 2 +- templates/java/tests/client/suite.mustache | 7 +++ .../clients/client/api/helpers.mustache | 2 +- .../clients/client/api/nodeHelpers.mustache | 24 ++++++- .../client/model/clientMethodProps.mustache | 2 +- .../javascript/tests/client/method.mustache | 2 +- .../javascript/tests/client/suite.mustache | 7 ++- .../tests/requests/helpers.mustache | 27 -------- templates/kotlin/tests/client/suite.mustache | 7 +++ templates/php/api.mustache | 21 ++++--- templates/php/tests/client/suite.mustache | 16 +++-- templates/python/api.mustache | 2 +- templates/python/imports.mustache | 1 + templates/python/model_generic.mustache | 11 +--- templates/python/search_helpers.mustache | 24 +++---- templates/python/tests/client/method.mustache | 2 +- templates/python/tests/client/suite.mustache | 7 ++- .../python/tests/requests/helpers.mustache | 34 +++++----- .../python/tests/requests/requests.mustache | 2 +- templates/ruby/search_helpers.mustache | 3 - templates/ruby/tests/client/suite.mustache | 5 ++ templates/scala/tests/client/suite.mustache | 7 +++ templates/swift/tests/client/method.mustache | 8 +-- templates/swift/tests/client/suite.mustache | 15 ++++- tests/CTS/client/search/helpers.json | 63 +++++++++++++++++++ .../output/csharp/src/SecuredApiKeysTests.cs | 43 +------------ ...lpers.swift => SecuredApiKeyHelpers.swift} | 4 +- 51 files changed, 322 insertions(+), 217 deletions(-) create mode 100644 tests/CTS/client/search/helpers.json rename tests/output/swift/Tests/handwritten/{SecuredAPIKeyHelpers.swift => SecuredApiKeyHelpers.swift} (96%) diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs b/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs index 272fb54710..8d0ef66d99 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Models/Common/SecuredApiKeyRestrictionHelper.cs @@ -3,13 +3,14 @@ using System.Text.Json.Serialization; using Algolia.Search.Http; using Algolia.Search.Utils; +using System.Collections.Generic; namespace Algolia.Search.Models.Search; /// /// Secured Api Key restrictions /// -public partial class SecuredAPIKeyRestrictions +public partial class SecuredApiKeyRestrictions { /// @@ -18,28 +19,26 @@ public partial class SecuredAPIKeyRestrictions /// public string ToQueryString() { - string restrictionQuery = null; + var restrictions = ToQueryMap(this, nameof(SearchParams)); if (SearchParams != null) { - restrictionQuery = ToQueryString(SearchParams); + // merge SearchParams into restrictions + restrictions = restrictions.Concat(ToQueryMap(SearchParams)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } - var restrictions = ToQueryString(this, nameof(SearchParams)); - var array = new[] { restrictionQuery, restrictions }; - - return string.Join("&", array.Where(s => !string.IsNullOrEmpty(s))); + return QueryStringHelper.ToQueryString(restrictions.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)); } /// - /// Transform a poco to a query string + /// Transform a poco to a map of query parameters /// /// /// /// /// - private static string ToQueryString(T value, params string[] ignoreList) + private static Dictionary ToQueryMap(T value, params string[] ignoreList) { - var properties = typeof(T).GetTypeInfo() + return typeof(T).GetTypeInfo() .DeclaredProperties.Where(p => p.GetValue(value, null) != null && !ignoreList.Contains(p.Name) && p.GetCustomAttribute() != null) @@ -48,8 +47,6 @@ private static string ToQueryString(T value, params string[] ignoreList) propsName = p.GetCustomAttribute().Name, value = QueryStringHelper.ParameterToString(p.GetValue(value, null)) }).ToDictionary(p => p.propsName, p => p.value); - - return properties.ToQueryString(); } } diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Utils/SearchClientExtensions.cs b/clients/algoliasearch-client-csharp/algoliasearch/Utils/SearchClientExtensions.cs index c1a2e86ce4..bc267f9e8a 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Utils/SearchClientExtensions.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Utils/SearchClientExtensions.cs @@ -264,7 +264,7 @@ public IEnumerable BrowseSynonyms(string indexName, SearchSynonymsPa /// Parent API Key /// Restriction to add the key /// - public string GenerateSecuredApiKey(string parentApiKey, SecuredAPIKeyRestrictions restriction) + public string GenerateSecuredApiKey(string parentApiKey, SecuredApiKeyRestrictions restriction) { var queryParams = restriction.ToQueryString(); var hash = HmacShaHelper.GetHash(parentApiKey, queryParams); @@ -273,20 +273,20 @@ public string GenerateSecuredApiKey(string parentApiKey, SecuredAPIKeyRestrictio /// - /// Get the remaining validity of a key generated by `GenerateSecuredApiKeys`. + /// Get the remaining validity of a key generated by `GenerateSecuredApiKey`. /// - /// The secured API Key + /// The secured API Key /// /// /// - public TimeSpan GetSecuredApiKeyRemainingValidity(string securedAPIKey) + public TimeSpan GetSecuredApiKeyRemainingValidity(string securedApiKey) { - if (string.IsNullOrWhiteSpace(securedAPIKey)) + if (string.IsNullOrWhiteSpace(securedApiKey)) { - throw new ArgumentNullException(nameof(securedAPIKey)); + throw new ArgumentNullException(nameof(securedApiKey)); } - var decodedKey = Encoding.UTF8.GetString(Convert.FromBase64String(securedAPIKey)); + var decodedKey = Encoding.UTF8.GetString(Convert.FromBase64String(securedApiKey)); var regex = new Regex(@"validUntil=\d+"); var matches = regex.Matches(decodedKey); diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts index c53f90ba1a..e63f8fd9da 100644 --- a/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts @@ -42,16 +42,14 @@ export function serializeUrl( } export function serializeQueryParameters(parameters: QueryParameters): string { - const isObjectOrArray = (value: any): boolean => - Object.prototype.toString.call(value) === '[object Object]' || - Object.prototype.toString.call(value) === '[object Array]'; - return Object.keys(parameters) + .filter((key) => parameters[key] !== undefined) + .sort() .map( (key) => `${key}=${encodeURIComponent( - isObjectOrArray(parameters[key]) - ? JSON.stringify(parameters[key]) + Object.prototype.toString.call(parameters[key]) === '[object Array]' + ? parameters[key].join(',') : parameters[key] ).replaceAll('+', '%20')}` ) 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 4890e16f54..2ffa489f26 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 @@ -344,13 +344,13 @@ public suspend fun SearchClient.replaceAllObjects( /** * Generate a virtual API Key without any call to the server. * - * @param parentAPIKey API key to generate from. + * @param parentApiKey API key to generate from. * @param restriction Restriction to add the key * @throws Exception if an error occurs during the encoding */ -public fun SearchClient.generateSecuredApiKey(parentAPIKey: String, restriction: SecuredAPIKeyRestrictions): String { +public fun SearchClient.generateSecuredApiKey(parentApiKey: String, restriction: SecuredApiKeyRestrictions): String { val restrictionString = buildRestrictionString(restriction) - val hash = encodeKeySHA256(parentAPIKey, restrictionString) + val hash = encodeKeySHA256(parentApiKey, restrictionString) return "$hash$restrictionString".encodeBase64() } @@ -359,7 +359,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 [SecuredAPIKeyRestrictions.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/internal/SearchClient.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/extensions/internal/SearchClient.kt index ef176120dc..f438addd24 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 @@ -2,16 +2,16 @@ package com.algolia.client.extensions.internal import com.algolia.client.api.SearchClient import com.algolia.client.model.search.SearchParamsObject -import com.algolia.client.model.search.SecuredAPIKeyRestrictions +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 [SecuredAPIKeyRestrictions]. + * Builds a restriction string based on provided [SecuredApiKeyRestrictions]. */ -internal fun SearchClient.buildRestrictionString(restriction: SecuredAPIKeyRestrictions): String { +internal fun SearchClient.buildRestrictionString(restriction: SecuredApiKeyRestrictions): String { return Parameters.build { restriction.searchParams?.let { searchParams -> val json = options.json.encodeToJsonElement(SearchParamsObject.serializer(), searchParams).jsonObject 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 e061cd633d..c3a6b23f5a 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.SecuredAPIKeyRestrictions +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 @@ -14,14 +14,14 @@ class TestSecureApiKey { @Test fun securedApiKey() { - val parentAPIKey = "SearchOnlyApiKeyKeptPrivate" - val restriction = SecuredAPIKeyRestrictions( + val parentApiKey = "SearchOnlyApiKeyKeptPrivate" + val restriction = SecuredApiKeyRestrictions( query = SearchParamsObject(filters = "_tags:user_42"), validUntil = Clock.System.now() + 2.days, ) val client = SearchClient("appId", "apiKey") - val securedApiKey = client.generateSecuredApiKey(parentAPIKey, restriction) + val securedApiKey = client.generateSecuredApiKey(parentApiKey, restriction) val validity = securedApiKeyRemainingValidity(securedApiKey) assertTrue { validity > 1.days } } diff --git a/clients/algoliasearch-client-python/algoliasearch/http/serializer.py b/clients/algoliasearch-client-python/algoliasearch/http/serializer.py index de31cd6b8c..9b57f254bc 100644 --- a/clients/algoliasearch-client-python/algoliasearch/http/serializer.py +++ b/clients/algoliasearch-client-python/algoliasearch/http/serializer.py @@ -1,5 +1,6 @@ from json import dumps from typing import Any, Dict +from urllib.parse import urlencode PRIMITIVE_TYPES = (float, bool, bytes, str, int) @@ -12,21 +13,30 @@ class QueryParametersSerializer: query_parameters: Dict[str, Any] = {} def parse(self, value) -> Any: - if isinstance(value, dict): - return dumps(value) - elif isinstance(value, list): + if isinstance(value, list): return ",".join([self.parse(item) for item in value]) + elif isinstance(value, dict): + return dumps(value) elif isinstance(value, bool): return "true" if value else "false" else: return str(value) + def encoded(self) -> str: + return urlencode( + dict(sorted(self.query_parameters.items(), key=lambda val: val[0])) + ).replace("+", "%20") + def __init__(self, query_parameters: Dict[str, Any]) -> None: self.query_parameters = {} if query_parameters is None: return for key, value in query_parameters.items(): - self.query_parameters[key] = self.parse(value) + if isinstance(value, dict): + for dkey, dvalue in value.items(): + self.query_parameters[dkey] = self.parse(dvalue) + else: + self.query_parameters[key] = self.parse(value) def bodySerializer(obj: Any) -> dict: diff --git a/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchClientExtension.swift b/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchClientExtension.swift index 1c432b632d..7e9004cf2a 100644 --- a/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchClientExtension.swift +++ b/clients/algoliasearch-client-swift/Sources/Search/Extra/SearchClientExtension.swift @@ -526,7 +526,7 @@ public extension SearchClient { /// - returns: String? func generateSecuredApiKey( parentApiKey: String, - with restriction: SecuredAPIKeyRestrictions = SecuredAPIKeyRestrictions() + with restriction: SecuredApiKeyRestrictions = SecuredApiKeyRestrictions() ) throws -> String? { let queryParams = try restriction.toURLEncodedString() let hash = queryParams.hmac256(withKey: parentApiKey) @@ -534,10 +534,10 @@ public extension SearchClient { } /// Get the remaining validity of a secured API key - /// - parameter securedAPIKey: The secured API key + /// - parameter securedApiKey: The secured API key /// - returns: TimeInterval? - func getSecuredApiKeyRemainingValidity(for securedAPIKey: String) -> TimeInterval? { - guard let rawDecodedAPIKey = String(data: Data(base64Encoded: securedAPIKey) ?? Data(), encoding: .utf8), + func getSecuredApiKeyRemainingValidity(for securedApiKey: String) -> TimeInterval? { + guard let rawDecodedAPIKey = String(data: Data(base64Encoded: securedApiKey) ?? Data(), encoding: .utf8), !rawDecodedAPIKey.isEmpty else { return nil } diff --git a/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift b/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift index 574fbbe77c..db687864d0 100644 --- a/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift +++ b/clients/algoliasearch-client-swift/Sources/Search/Extra/SecuredApiKeyRestrictionExtension.swift @@ -3,7 +3,7 @@ #endif import Foundation -public extension SecuredAPIKeyRestrictions { +public extension SecuredApiKeyRestrictions { func toURLEncodedString() throws -> String { var queryDictionary: [String: Any] = [:] diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsClient.java b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsClient.java index bafe7ab551..79fa71ec5e 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsClient.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsClient.java @@ -92,15 +92,22 @@ public void run(Map models, Map throw new CTSException("Cannot find operation for method: " + step.path, test.testName); } stepOut.put("stepTemplate", "tests/client/method.mustache"); - stepOut.put("isMethod", true); // TODO: remove once dart and kotlin are converted + stepOut.put("isMethod", true); // TODO: remove once kotlin is converted + stepOut.put("hasOperationParams", ope.hasParams); + + // set on testOut because we need to wrap everything for java. + testOut.put("isHelper", (boolean) ope.vendorExtensions.getOrDefault("x-helper", false)); + testOut.put("isAsync", (boolean) ope.vendorExtensions.getOrDefault("x-asynchronous-helper", true)); // default to true because most api calls are asynchronous } stepOut.put("object", step.object); stepOut.put("path", step.path); - Map requestOptions = new HashMap<>(); - paramsType.enhanceParameters(step.requestOptions, requestOptions); - stepOut.put("requestOptions", requestOptions); + if (step.requestOptions != null) { + Map requestOptions = new HashMap<>(); + paramsType.enhanceParameters(step.requestOptions, requestOptions); + stepOut.put("requestOptions", requestOptions); + } if (step.path != null && CUSTOM_METHODS.contains(step.path)) { stepOut.put("isCustom", true); @@ -148,10 +155,11 @@ public void run(Map models, Map stepOut.put("expectedError", step.expected.error.replace(step.path, Helpers.toPascalCase(step.path))); } } else if (step.expected.match != null) { - if (step.expected.match instanceof Map) { - Map match = new HashMap<>(); - paramsType.enhanceParameters((Map) step.expected.match, match); - stepOut.put("match", match); + Map matchMap = new HashMap<>(); + if (step.expected.match instanceof Map match) { + paramsType.enhanceParameters(match, matchMap); + stepOut.put("match", matchMap); + stepOut.put("matchIsObject", true); } else { stepOut.put("match", step.expected.match); } 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 2634688e70..12555213a5 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 @@ -69,7 +69,7 @@ public void run(Map models, Map boolean isHelper = (boolean) ope.vendorExtensions.getOrDefault("x-helper", false); if (!cts.containsKey(operationId)) { if (isHelper) { - continue; + continue; // some helpers don't have tests } throw new CTSException( diff --git a/playground/csharp/Playground/Playgrounds/Search.cs b/playground/csharp/Playground/Playgrounds/Search.cs index 01a022aac2..f641ad5188 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 SecuredAPIKeyRestrictions + new SecuredApiKeyRestrictions { RestrictIndices = [DefaultIndex], }); diff --git a/playground/swift/playground/playground/main.swift b/playground/swift/playground/playground/main.swift index 62c7eef430..c098a2fc10 100644 --- a/playground/swift/playground/playground/main.swift +++ b/playground/swift/playground/playground/main.swift @@ -18,7 +18,7 @@ guard let apiKey = Bundle.main.infoDictionary?["ALGOLIA_ADMIN_KEY"] as? String e } guard applicationID != "" && apiKey != "" else { - fatalError("AppID and APIKey must be filled in your Info.plist file") + fatalError("AppID and ApiKey must be filled in your Info.plist file") } struct Contact: Codable { diff --git a/scripts/cts/runCts.ts b/scripts/cts/runCts.ts index 427ff093eb..769facaea6 100644 --- a/scripts/cts/runCts.ts +++ b/scripts/cts/runCts.ts @@ -1,6 +1,6 @@ import * as fsp from 'fs/promises'; -import { run, runComposerInstall, toAbsolutePath } from '../common.js'; +import { isVerbose, run, runComposerInstall, toAbsolutePath } from '../common.js'; import { createSpinner } from '../spinners.js'; import type { Language } from '../types.js'; @@ -17,7 +17,7 @@ async function runCtsOne(language: string): Promise { await run('dart test', { cwd, language }); break; case 'go': - await run('go test -race -count 1 ./...', { + await run(`go test -race -count 1 ${isVerbose() ? '-v' : ''} ./...`, { cwd, language, }); diff --git a/specs/search/helpers/generateSecuredApiKey.yml b/specs/search/helpers/generateSecuredApiKey.yml index 594ac9c804..97afb11b9c 100644 --- a/specs/search/helpers/generateSecuredApiKey.yml +++ b/specs/search/helpers/generateSecuredApiKey.yml @@ -1,6 +1,7 @@ method: get: x-helper: true + x-asynchronous-helper: false tags: - Api Keys operationId: generateSecuredApiKey @@ -21,7 +22,7 @@ method: The generated API key can have the same restrictions as the parent API key, or be more restrictive. parameters: - in: query - name: apiKey + name: parentApiKey description: API key from which the secured API key will inherit its restrictions. required: true schema: @@ -31,7 +32,7 @@ method: description: Restrictions to add to the API key. required: true schema: - $ref: '#/securedAPIKeyRestrictions' + $ref: '#/securedApiKeyRestrictions' responses: '200': description: OK @@ -42,7 +43,7 @@ method: '400': $ref: '../../common/responses/IndexNotFound.yml' -securedAPIKeyRestrictions: +securedApiKeyRestrictions: type: object additionalProperties: false properties: diff --git a/templates/csharp/tests/client/method.mustache b/templates/csharp/tests/client/method.mustache index b58fe1c8d8..f255b76931 100644 --- a/templates/csharp/tests/client/method.mustache +++ b/templates/csharp/tests/client/method.mustache @@ -1,4 +1,4 @@ -{{^useEchoRequester}}var res = {{/useEchoRequester}}await client.{{#lambda.pascalcase}}{{#path}}.{{.}}{{/path}}{{/lambda.pascalcase}}Async{{#isGeneric}}{{/isGeneric}}({{#parametersWithDataType}}{{> tests/generateParams}}{{^-last}},{{/-last}}{{/parametersWithDataType}}{{#hasRequestOptions}}, new RequestOptions(){ +{{^useEchoRequester}}var res = {{/useEchoRequester}}{{#isAsync}}await {{/isAsync}}client.{{#lambda.pascalcase}}{{#path}}.{{.}}{{/path}}{{/lambda.pascalcase}}{{#isAsync}}Async{{/isAsync}}{{#isGeneric}}{{/isGeneric}}({{#parametersWithDataType}}{{> tests/generateParams}}{{^-last}},{{/-last}}{{/parametersWithDataType}}{{#hasRequestOptions}}, new RequestOptions(){ {{#requestOptions.queryParameters}} QueryParameters = new Dictionary(){ {{#parametersWithDataType}} {"{{{key}}}", {{> tests/requests/requestOptionsParams}} } {{^-last}},{{/-last}}{{/parametersWithDataType}} }, {{/requestOptions.queryParameters}} diff --git a/templates/csharp/tests/client/suite.mustache b/templates/csharp/tests/client/suite.mustache index 3c0c5e869a..63165340a7 100644 --- a/templates/csharp/tests/client/suite.mustache +++ b/templates/csharp/tests/client/suite.mustache @@ -56,7 +56,12 @@ public void Dispose() Assert.Equal("{{{match}}}", result.Host); {{/testHost}} {{#testResponse}} + {{#matchIsObject}} JsonAssert.EqualOverrideDefault("{{#lambda.escapeQuotes}}{{{match.parameters}}}{{/lambda.escapeQuotes}}", JsonSerializer.Serialize(res, JsonConfig.Options), new JsonDiffConfig(false)); + {{/matchIsObject}} + {{^matchIsObject}} + Assert.Equal("{{{match}}}", res); + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} diff --git a/templates/dart/tests/client/method.mustache b/templates/dart/tests/client/method.mustache index 193968f0c6..ee9661e629 100644 --- a/templates/dart/tests/client/method.mustache +++ b/templates/dart/tests/client/method.mustache @@ -6,7 +6,12 @@ try { ); {{#match}} {{#testResponse}} + {{#matchIsObject}} expectBody(res, """{{{match.parameters}}}"""); + {{/matchIsObject}} + {{^matchIsObject}} + expect(res, """{{match}}"""); + {{/matchIsObject}} {{/testResponse}} {{/match}} } on InterceptionException catch (_) { diff --git a/templates/dart/tests/client/suite.mustache b/templates/dart/tests/client/suite.mustache index 306b00aeda..9c407fc292 100644 --- a/templates/dart/tests/client/suite.mustache +++ b/templates/dart/tests/client/suite.mustache @@ -7,6 +7,7 @@ import 'package:test_api/hooks.dart'; void main() { {{#blocksClient}} {{#tests}} + {{^isHelper}} {{! Helper tests are not supported yet}} test('{{{testName}}}', () async { final requester = RequestInterceptor(); {{#autoCreateClient}} @@ -48,6 +49,7 @@ void main() { } ); + {{/isHelper}} {{/tests}} {{/blocksClient}} } \ No newline at end of file diff --git a/templates/go/api.mustache b/templates/go/api.mustache index 8e3aa69960..f676db937d 100644 --- a/templates/go/api.mustache +++ b/templates/go/api.mustache @@ -11,14 +11,14 @@ import ( "net/url" "strings" {{#isSearchClient}} - "slices" - "time" - "sort" "github.com/algolia/algoliasearch-client-go/v4/algolia/errs" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" + "slices" + "sort" + "time" {{/isSearchClient}} "github.com/algolia/algoliasearch-client-go/v4/algolia/utils" diff --git a/templates/go/search_helpers.mustache b/templates/go/search_helpers.mustache index a5655c9db2..3064e3c6b2 100644 --- a/templates/go/search_helpers.mustache +++ b/templates/go/search_helpers.mustache @@ -372,7 +372,7 @@ func (c *APIClient) WaitForApiKeyWithContext( ) } -func encodeRestrictions(restrictions *SecuredAPIKeyRestrictions) (string, error) { +func encodeRestrictions(restrictions *SecuredApiKeyRestrictions) (string, error) { if restrictions == nil { return "", nil } @@ -423,7 +423,7 @@ func encodeRestrictions(restrictions *SecuredAPIKeyRestrictions) (string, error) // GenerateSecuredApiKey generates a public API key intended to restrict access // to certain records. This new key is built upon the existing key named // `parentApiKey` and the following options. -func (c *APIClient) GenerateSecuredApiKey(parentApiKey string, restrictions *SecuredAPIKeyRestrictions) (string, error) { +func (c *APIClient) GenerateSecuredApiKey(parentApiKey string, restrictions *SecuredApiKeyRestrictions) (string, error) { h := hmac.New(sha256.New, []byte(parentApiKey)) message, err := encodeRestrictions(restrictions) @@ -562,4 +562,4 @@ func (c *APIClient) ReplaceAllObjects(indexName string, objects []map[string]any BatchResponses: batchResp, MoveOperationResponse: *moveResp, }, nil -} \ No newline at end of file +} diff --git a/templates/go/tests/client/method.mustache b/templates/go/tests/client/method.mustache index 2aaeec3d7b..028667c339 100644 --- a/templates/go/tests/client/method.mustache +++ b/templates/go/tests/client/method.mustache @@ -1,5 +1,5 @@ -{{^useEchoRequester}}res, err := {{/useEchoRequester}}{{#useEchoRequester}}_, err = {{/useEchoRequester}}client.{{#lambda.titlecase}}{{path}}{{/lambda.titlecase}}(client.NewApi{{#lambda.titlecase}}{{path}}{{/lambda.titlecase}}Request( +{{^useEchoRequester}}res, err := {{/useEchoRequester}}{{#useEchoRequester}}_, err = {{/useEchoRequester}}client.{{#lambda.titlecase}}{{path}}{{/lambda.titlecase}}({{^isHelper}}client.NewApi{{#lambda.titlecase}}{{path}}{{/lambda.titlecase}}Request({{/isHelper}} {{#parametersWithDataType}}{{#required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}} - ){{#parametersWithDataType}}{{^required}}.With{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{/required}}{{/parametersWithDataType}}{{#requestOptions}}, + {{^isHelper}}){{#parametersWithDataType}}{{^required}}.With{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{/required}}{{/parametersWithDataType}}{{/isHelper}}{{#isHelper}}{{#parametersWithDataType}}{{^required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}}{{/isHelper}}{{#requestOptions}}{{#hasOperationParams}},{{/hasOperationParams}} {{#queryParameters.parametersWithDataType}}{{clientPrefix}}.QueryParamOption("{{{key}}}", {{> tests/generateInnerParams}}),{{/queryParameters.parametersWithDataType}}{{#headers.parametersWithDataType}}{{clientPrefix}}.HeaderParamOption("{{{key}}}", {{> tests/generateInnerParams}}),{{/headers.parametersWithDataType}} {{/requestOptions}}) \ No newline at end of file diff --git a/templates/go/tests/client/suite.mustache b/templates/go/tests/client/suite.mustache index dc12e2d24e..877ddbd948 100644 --- a/templates/go/tests/client/suite.mustache +++ b/templates/go/tests/client/suite.mustache @@ -58,19 +58,24 @@ func Test{{#lambda.titlecase}}{{clientPrefix}}{{testType}}{{/lambda.titlecase}}{ require.NoError(t, err) {{#match}} {{#testUserAgent}} - require.Regexp(t, regexp.MustCompile(`{{{match}}}`), echo.Header.Get("User-Agent")) + require.Regexp(t, regexp.MustCompile(`{{{match}}}`), echo.Header.Get("User-Agent")) {{/testUserAgent}} {{#testTimeouts}} - require.Equal(t, int64({{{match.parametersWithDataTypeMap.connectTimeout.value}}}), echo.ConnectTimeout.Milliseconds()) - require.Equal(t, int64({{{match.parametersWithDataTypeMap.responseTimeout.value}}}), echo.Timeout.Milliseconds()) + require.Equal(t, int64({{{match.parametersWithDataTypeMap.connectTimeout.value}}}), echo.ConnectTimeout.Milliseconds()) + require.Equal(t, int64({{{match.parametersWithDataTypeMap.responseTimeout.value}}}), echo.Timeout.Milliseconds()) {{/testTimeouts}} {{#testHost}} - require.Equal(t, "{{{match}}}", echo.Host) + require.Equal(t, "{{{match}}}", echo.Host) {{/testHost}} {{#testResponse}} - rawBody, err := json.Marshal(res) - require.NoError(t, err) - require.JSONEq(t, `{{{match.parameters}}}`, string(rawBody)) + {{#matchIsObject}} + rawBody, err := json.Marshal(res) + require.NoError(t, err) + require.JSONEq(t, `{{{match.parameters}}}`, string(rawBody)) + {{/matchIsObject}} + {{^matchIsObject}} + require.Equal(t, `{{{match}}}`, res) + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} diff --git a/templates/go/tests/requests/helpers.mustache b/templates/go/tests/requests/helpers.mustache index 339d8b0fe9..aee73d0d12 100644 --- a/templates/go/tests/requests/helpers.mustache +++ b/templates/go/tests/requests/helpers.mustache @@ -10,7 +10,7 @@ func TestSearch_GenerateSecuredApiKey(t *testing.T) { }) t.Run("generates a key with restrictions", func(t *testing.T) { - key, err := client.GenerateSecuredApiKey("foo", search.NewSecuredAPIKeyRestrictions().SetValidUntil(100).SetRestrictIndices([]string{"bar"}).SetRestrictSources("192,168.1.0/24").SetUserToken("foobarbaz").SetSearchParams(search.NewSearchParamsObject().SetQuery("foo"))) + key, err := client.GenerateSecuredApiKey("foo", search.NewSecuredApiKeyRestrictions().SetValidUntil(100).SetRestrictIndices([]string{"bar"}).SetRestrictSources("192,168.1.0/24").SetUserToken("foobarbaz").SetSearchParams(search.NewSearchParamsObject().SetQuery("foo"))) require.NoError(t, err) require.Equal(t, "NGMxODk0MjViNjM3ODcxNjc4NWU4Y2I5NGIxNDAzMTg4MjU5Mjc4YTEwMzU4Mjk2YjBiMmVjOWViYTIyOTBiY3F1ZXJ5PWZvbyZyZXN0cmljdEluZGljZXM9YmFyJnJlc3RyaWN0U291cmNlcz0xOTIlMkMxNjguMS4wJTJGMjQmdXNlclRva2VuPWZvb2JhcmJheiZ2YWxpZFVudGlsPTEwMA==", key) @@ -22,7 +22,7 @@ func TestSearch_GetSecuredApiKeyRemainingVaildity(t *testing.T) { _ = echo t.Run("is able to check the remaining validity of a key", func(t *testing.T) { - key, err := client.GenerateSecuredApiKey("foo", search.NewSecuredAPIKeyRestrictions().SetValidUntil(42)) + key, err := client.GenerateSecuredApiKey("foo", search.NewSecuredApiKeyRestrictions().SetValidUntil(42)) require.NoError(t, err) require.Equal(t, "NDI5ZjRkMTRiNTBlZmExZWIyN2I3NzczOGUwMzE0NjYwMDU1M2M3NjYyY2IxODZhMDAxMWEwOWJmZjE5MzY0NnZhbGlkVW50aWw9NDI=", key) @@ -32,4 +32,4 @@ func TestSearch_GetSecuredApiKeyRemainingVaildity(t *testing.T) { require.Greater(t, validity, -time.Now().UnixNano()) }) -} \ No newline at end of file +} diff --git a/templates/java/api_helpers.mustache b/templates/java/api_helpers.mustache index fa2b13c9f8..26baa49d43 100644 --- a/templates/java/api_helpers.mustache +++ b/templates/java/api_helpers.mustache @@ -652,7 +652,7 @@ return new ReplaceAllObjectsResponse() * @throws AlgoliaApiException When the API sends an http error code * @throws AlgoliaRuntimeException When an error occurred during the serialization */ -public String generateSecuredApiKey(@Nonnull String parentApiKey, @Nonnull SecuredAPIKeyRestrictions restrictions) throws Exception { +public String generateSecuredApiKey(@Nonnull String parentApiKey, @Nonnull SecuredApiKeyRestrictions restrictions) throws Exception { Map restrictionsMap = new HashMap<>(); if (restrictions.getFilters() != null) restrictionsMap.put("filters", StringUtils.paramToString(restrictions.getFilters())); if (restrictions.getValidUntil() != 0) restrictionsMap.put("validUntil", StringUtils.paramToString(restrictions.getValidUntil())); diff --git a/templates/java/tests/client/suite.mustache b/templates/java/tests/client/suite.mustache index 4d22f5c7aa..cbf08158d8 100644 --- a/templates/java/tests/client/suite.mustache +++ b/templates/java/tests/client/suite.mustache @@ -53,6 +53,7 @@ class {{client}}ClientTests { {{client}} client = createClient(); {{/autoCreateClient}} + {{#isHelper}}assertDoesNotThrow(() -> { {{/isHelper}} {{#steps}} {{#isError}} { @@ -77,11 +78,17 @@ class {{client}}ClientTests { assertEquals("{{{match}}}", result.host); {{/testHost}} {{#testResponse}} + {{#matchIsObject}} assertDoesNotThrow(() -> JSONAssert.assertEquals("{{#lambda.escapeQuotes}}{{{match.parameters}}}{{/lambda.escapeQuotes}}", json.writeValueAsString(res), JSONCompareMode.STRICT)); + {{/matchIsObject}} + {{^matchIsObject}} + assertEquals("{{{match}}}", res); + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} {{/steps}} + {{#isHelper}} });{{/isHelper}} } {{/tests}} {{/blocksClient}} diff --git a/templates/javascript/clients/client/api/helpers.mustache b/templates/javascript/clients/client/api/helpers.mustache index 859a9fc435..111523236d 100644 --- a/templates/javascript/clients/client/api/helpers.mustache +++ b/templates/javascript/clients/client/api/helpers.mustache @@ -400,4 +400,4 @@ async replaceAllObjects( }); return { copyOperationResponse, batchResponses, moveOperationResponse }; -}, \ No newline at end of file +}, diff --git a/templates/javascript/clients/client/api/nodeHelpers.mustache b/templates/javascript/clients/client/api/nodeHelpers.mustache index ccfb33ed0e..f820b5051a 100644 --- a/templates/javascript/clients/client/api/nodeHelpers.mustache +++ b/templates/javascript/clients/client/api/nodeHelpers.mustache @@ -10,7 +10,29 @@ generateSecuredApiKey({ parentApiKey, restrictions = {}, }: GenerateSecuredApiKeyOptions): string { - const queryParameters = serializeQueryParameters(restrictions); + let mergedRestrictions = restrictions; + if (restrictions.searchParams) { + // merge searchParams with the root restrictions + mergedRestrictions = { + ...restrictions, + ...restrictions.searchParams, + }; + + delete mergedRestrictions.searchParams; + } + + mergedRestrictions = Object.keys(mergedRestrictions) + .sort() + .reduce( + (acc, key) => { + // eslint-disable-next-line no-param-reassign + acc[key] = (mergedRestrictions as any)[key]; + return acc; + }, + {} as Record + ); + + const queryParameters = serializeQueryParameters(mergedRestrictions); return Buffer.from( createHmac('sha256', parentApiKey) .update(queryParameters) diff --git a/templates/javascript/clients/client/model/clientMethodProps.mustache b/templates/javascript/clients/client/model/clientMethodProps.mustache index 00870b44a1..b57af4246a 100644 --- a/templates/javascript/clients/client/model/clientMethodProps.mustache +++ b/templates/javascript/clients/client/model/clientMethodProps.mustache @@ -111,7 +111,7 @@ export type GenerateSecuredApiKeyOptions = { /** * A set of properties defining the restrictions of the secured API key. */ - restrictions?: SecuredAPIKeyRestrictions; + restrictions?: SecuredApiKeyRestrictions; } export type GetSecuredApiKeyRemainingValidityOptions = { diff --git a/templates/javascript/tests/client/method.mustache b/templates/javascript/tests/client/method.mustache index bab8e61c1d..a1c8ce47da 100644 --- a/templates/javascript/tests/client/method.mustache +++ b/templates/javascript/tests/client/method.mustache @@ -1 +1 @@ -const result = await ({{object}}{{#path}}.{{.}}{{/path}}({{{parameters}}})){{#useEchoRequester}} as unknown as EchoResponse{{/useEchoRequester}}; \ No newline at end of file +const result = {{#isAsync}}await {{/isAsync}}({{object}}{{#path}}.{{.}}{{/path}}({{{parameters}}})){{#useEchoRequester}} as unknown as EchoResponse{{/useEchoRequester}}; \ No newline at end of file diff --git a/templates/javascript/tests/client/suite.mustache b/templates/javascript/tests/client/suite.mustache index aa09928f0b..229249ec59 100644 --- a/templates/javascript/tests/client/suite.mustache +++ b/templates/javascript/tests/client/suite.mustache @@ -42,7 +42,12 @@ describe('{{testType}}', () => { expect(result.host).toEqual("{{{match}}}"); {{/testHost}} {{#testResponse}} - expect(result).toEqual({{{match.parameters}}}); + {{#matchIsObject}} + expect(result).toEqual({{{match.parameters}}}); + {{/matchIsObject}} + {{^matchIsObject}} + expect(result).toEqual("{{{match}}}"); + {{/matchIsObject}} {{/testResponse}} {{/isError}} {{/steps}} diff --git a/templates/javascript/tests/requests/helpers.mustache b/templates/javascript/tests/requests/helpers.mustache index 902b8a1666..65ccdfb6fd 100644 --- a/templates/javascript/tests/requests/helpers.mustache +++ b/templates/javascript/tests/requests/helpers.mustache @@ -1,30 +1,3 @@ -describe('generateSecuredApiKey', () => { - test('generates a key without restrictions', () => { - const resp = client.generateSecuredApiKey({ parentApiKey: 'foo' }); - expect(resp).toEqual( - 'NjgzNzE2ZDlkN2Y4MmVlZDE3NGM2Y2FlYmUwODZlZTkzMzc2Yzc5ZDdjNjFkZDY3MGVhMDBmN2Y4ZDZlYjBhOA==' - ); - }); - - test('generates a key with restrictions', () => { - const resp = client.generateSecuredApiKey({ - parentApiKey: 'foo', - restrictions: { - validUntil: 100, - restrictIndices: ['bar'], - restrictSources: '192,168.1.0/24', - userToken: 'foobarbaz', - searchParams: { - query: 'foo', - }, - }, - }); - expect(resp).toEqual( - 'M2RlY2Y5ZjgzMDU1ZDRiYjkyOTdjYjYxYWNjNWNhNTQ5ZGI5Mjc3ZmVjNmNmNjM2ZjAwMTA4OGRjNDI5YjFhOXZhbGlkVW50aWw9MTAwJnJlc3RyaWN0SW5kaWNlcz0lNUIlMjJiYXIlMjIlNUQmcmVzdHJpY3RTb3VyY2VzPTE5MiUyQzE2OC4xLjAlMkYyNCZ1c2VyVG9rZW49Zm9vYmFyYmF6JnNlYXJjaFBhcmFtcz0lN0IlMjJxdWVyeSUyMiUzQSUyMmZvbyUyMiU3RA==' - ); - }); -}); - describe('getSecuredApiKeyRemainingValidity', () => { test('is able to check the remaining validity of a key', () => { const resp = client.generateSecuredApiKey({ diff --git a/templates/kotlin/tests/client/suite.mustache b/templates/kotlin/tests/client/suite.mustache index df4223f9b5..6525a00d9f 100644 --- a/templates/kotlin/tests/client/suite.mustache +++ b/templates/kotlin/tests/client/suite.mustache @@ -16,6 +16,7 @@ class {{clientPrefix}}Test { {{#blocksClient}} {{#tests}} + {{^isHelper}} {{! Helper tests are not supported yet}} @Test fun `{{#lambda.replaceBacktick}}{{{testName}}}{{/lambda.replaceBacktick}}`() = runTest { {{#autoCreateClient}} @@ -56,8 +57,13 @@ class {{clientPrefix}}Test { assertEquals("{{{match}}}", it.url.host); {{/testHost}} }{{/testResponse}}{{#testResponse}}response = { + {{#matchIsObject}} val response = Json.encodeToString(it) assertEquals("{{#lambda.escapeQuotes}}{{{match.parameters}}}{{/lambda.escapeQuotes}}", response) + {{/matchIsObject}} + {{^matchIsObject}} + assertEquals("{{{match}}}", it) + {{/matchIsObject}} }{{/testResponse}} {{/match}} ) @@ -65,6 +71,7 @@ class {{clientPrefix}}Test { {{/isError}} {{/steps}} } + {{/isHelper}} {{/tests}} {{/blocksClient}} } \ No newline at end of file diff --git a/templates/php/api.mustache b/templates/php/api.mustache index 0cf60f304d..8fdf674c6f 100644 --- a/templates/php/api.mustache +++ b/templates/php/api.mustache @@ -539,7 +539,7 @@ use {{invokerPackage}}\Support\Helpers; } /** - * Helper: Generate a secured API Key + * Helper: Generate a secured API Key. * * @param string $parentApiKey Parent API Key * @param array $restrictions API Key's restrictions @@ -548,7 +548,14 @@ use {{invokerPackage}}\Support\Helpers; */ public static function generateSecuredApiKey($parentApiKey, $restrictions) { - $urlEncodedRestrictions = Helpers::buildQuery($restrictions); + $formattedRestrictions = $restrictions; + if (isset($restrictions['searchParams'])) { + $formattedRestrictions = array_merge($restrictions, $restrictions['searchParams']); + unset($formattedRestrictions['searchParams']); + } + + ksort($formattedRestrictions); + $urlEncodedRestrictions = Helpers::buildQuery($formattedRestrictions); $content = hash_hmac('sha256', $urlEncodedRestrictions, $parentApiKey).$urlEncodedRestrictions; @@ -556,17 +563,17 @@ use {{invokerPackage}}\Support\Helpers; } /** - * Helper: Returns the time the given securedAPIKey remains valid in seconds. + * Helper: Returns the time the given securedApiKey remains valid in seconds. * - * @param string $securedAPIKey the key to check + * @param string $securedApiKey the key to check * * @return int remaining validity in seconds * * @throws ValidUntilNotFoundException */ - public static function getSecuredApiKeyRemainingValidity($securedAPIKey) + public static function getSecuredApiKeyRemainingValidity($securedApiKey) { - $decodedKey = base64_decode($securedAPIKey); + $decodedKey = base64_decode($securedApiKey); $regex = '/validUntil=(\d+)/'; preg_match($regex, $decodedKey, $matches); @@ -602,4 +609,4 @@ use {{invokerPackage}}\Support\Helpers; ); } } -{{/operations}} \ No newline at end of file +{{/operations}} diff --git a/templates/php/tests/client/suite.mustache b/templates/php/tests/client/suite.mustache index 4a02c98555..4912ab0d5f 100644 --- a/templates/php/tests/client/suite.mustache +++ b/templates/php/tests/client/suite.mustache @@ -97,10 +97,18 @@ class {{clientPrefix}}Test extends TestCase implements HttpClientInterface ); {{/testTimeouts}} {{#testResponse}} - $this->assertEquals( - '{{{match.parameters}}}', - json_encode($res) - ); + {{#matchIsObject}} + $this->assertEquals( + '{{{match.parameters}}}', + json_encode($res) + ); + {{/matchIsObject}} + {{^matchIsObject}} + $this->assertEquals( + '{{{match}}}', + $res + ); + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} diff --git a/templates/python/api.mustache b/templates/python/api.mustache index 7b884e94e5..e13be29615 100644 --- a/templates/python/api.mustache +++ b/templates/python/api.mustache @@ -5,7 +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 +from algoliasearch.search.models.secured_api_key_restrictions import SecuredApiKeyRestrictions {{/isSearchClient}} {{#operations}}{{#operation}}{{#imports}} diff --git a/templates/python/imports.mustache b/templates/python/imports.mustache index 2b6b5a1d53..3edb49afb8 100644 --- a/templates/python/imports.mustache +++ b/templates/python/imports.mustache @@ -15,6 +15,7 @@ from json import ( ) from pydantic import ( BaseModel, + ConfigDict, Field, StrictBool, StrictFloat, diff --git a/templates/python/model_generic.mustache b/templates/python/model_generic.mustache index 27bd24c7ed..352fd0c500 100644 --- a/templates/python/model_generic.mustache +++ b/templates/python/model_generic.mustache @@ -65,7 +65,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}} {{/isEnum}} {{/vars}} - model_config = { "populate_by_name": True, "validate_assignment": True } + model_config = ConfigDict(use_enum_values=True, populate_by_name=True, validate_assignment=True) {{#hasChildren}} @@ -196,15 +196,6 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}} _dict[_key] = _value {{/isAdditionalPropertiesTrue}} - {{#allVars}} - {{#isNullable}} - # set to None if {{{name}}} (nullable) is None - # and model_fields_set contains the field - if self.{{name}} is None and "{{{name}}}" in self.model_fields_set: - _dict['{{{baseName}}}'] = None - - {{/isNullable}} - {{/allVars}} return _dict @classmethod diff --git a/templates/python/search_helpers.mustache b/templates/python/search_helpers.mustache index 9c2765c27c..80a7ee0251 100644 --- a/templates/python/search_helpers.mustache +++ b/templates/python/search_helpers.mustache @@ -190,18 +190,20 @@ 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`. """ - query_parameters = dumps( - QueryParametersSerializer( - restrictions.to_dict() - if isinstance(restrictions, SecuredAPIKeyRestrictions) - else restrictions - ).query_parameters - ) + if not isinstance(restrictions, SecuredApiKeyRestrictions): + restrictions = SecuredApiKeyRestrictions.from_dict(restrictions) + + restrictions = restrictions.to_dict() + if "searchParams" in restrictions: + restrictions = {**restrictions, **restrictions["searchParams"]} + del restrictions["searchParams"] + + query_parameters = QueryParametersSerializer(dict(sorted(restrictions.items()))).encoded() secured_key = hmac.new( parent_api_key.encode(encoding="utf-8"), @@ -217,14 +219,14 @@ def get_secured_api_key_remaining_validity(self, secured_api_key: str) -> int: """ - Helper: Retrieves the remaining validity of the previous generated `secured_api_key`, the `ValidUntil` parameter must have been provided. + Helper: Retrieves the remaining validity of the previous generated `secured_api_key`, the `validUntil` parameter must have been provided. """ validity = search( - r'"valid_until": "(\d+)"', str(base64.b64decode(secured_api_key)) + r'validUntil=(\d+)', str(base64.b64decode(secured_api_key)) ) if validity is None: - raise ValidUntilNotFoundException("valid_until not found in api key.") + raise ValidUntilNotFoundException("validUntil not found in api key.") return int(validity.group(1)) - int(round(time())) diff --git a/templates/python/tests/client/method.mustache b/templates/python/tests/client/method.mustache index a8431c21ac..e7e6410fc8 100644 --- a/templates/python/tests/client/method.mustache +++ b/templates/python/tests/client/method.mustache @@ -1 +1 @@ -{{^isError}}_req = {{/isError}}await self._client.{{#lambda.snakecase}}{{{path}}}{{/lambda.snakecase}}{{#useEchoRequester}}_with_http_info{{/useEchoRequester}}({{#parametersWithDataType}}{{> tests/requests/generateParams}}{{/parametersWithDataType}}{{#hasRequestOptions}} request_options={ {{#requestOptions.headers.parameters}}"headers":loads("""{{{.}}}"""),{{/requestOptions.headers.parameters}}{{#requestOptions.queryParameters.parameters}}"query_parameters":loads("""{{{.}}}"""),{{/requestOptions.queryParameters.parameters}} }{{/hasRequestOptions}}) \ No newline at end of file +{{^isError}}_req = {{/isError}}{{#isAsync}}await {{/isAsync}}self._client.{{#lambda.snakecase}}{{{path}}}{{/lambda.snakecase}}{{#useEchoRequester}}_with_http_info{{/useEchoRequester}}({{#parametersWithDataType}}{{> tests/requests/generateParams}}{{/parametersWithDataType}}{{#hasRequestOptions}} request_options={ {{#requestOptions.headers.parameters}}"headers":loads("""{{{.}}}"""),{{/requestOptions.headers.parameters}}{{#requestOptions.queryParameters.parameters}}"query_parameters":loads("""{{{.}}}"""),{{/requestOptions.queryParameters.parameters}} }{{/hasRequestOptions}}) \ No newline at end of file diff --git a/templates/python/tests/client/suite.mustache b/templates/python/tests/client/suite.mustache index cc2888780c..f4ce449080 100644 --- a/templates/python/tests/client/suite.mustache +++ b/templates/python/tests/client/suite.mustache @@ -20,7 +20,7 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}: {{#tests}} async def test_{{#lambda.snakecase}}{{testType}}{{/lambda.snakecase}}_{{testIndex}}(self): """ - {{testName}} + {{{testName}}} """ {{#autoCreateClient}} self.create_client() @@ -49,7 +49,12 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}: assert _req.host == "{{{match}}}" {{/testHost}} {{#testResponse}} + {{#matchIsObject}} assert _req == """{{{match.parameters}}}""" + {{/matchIsObject}} + {{^matchIsObject}} + assert _req == """{{{match}}}""" + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} diff --git a/templates/python/tests/requests/helpers.mustache b/templates/python/tests/requests/helpers.mustache index de28ddd103..ef559aacfd 100644 --- a/templates/python/tests/requests/helpers.mustache +++ b/templates/python/tests/requests/helpers.mustache @@ -3,39 +3,39 @@ 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 == "Yzc2MzU2ZWZhMTlkMjE5ZDFkN2UwOGNjYjIwYjFkMjZkYjUzYjE0MzE1NmY0MDZjOTlkY2I4ZTA4NzZkNmM1NXt9" + assert _resp == "NjgzNzE2ZDlkN2Y4MmVlZDE3NGM2Y2FlYmUwODZlZTkzMzc2Yzc5ZDdjNjFkZDY3MGVhMDBmN2Y4ZDZlYjBhOA==" def test_generate_secured_api_key_1(self): """ allow generating a secured api key with a dict of restrictions """ _resp = self._client.generate_secured_api_key(parent_api_key="foo", restrictions={ - "search_params": {"query": "foo"}, - "valid_until": 100, - "restrict_indices": ["bar"], - "restrict_sources": "baz", - "user_token": "foobarbaz", + "searchParams": {"query": "foo"}, + "validUntil": 100, + "restrictIndices": ["bar"], + "restrictSources": "baz", + "userToken": "foobarbaz", }) - assert _resp == "OGM3YTUyNjI5MTExNjEwNWQ5ZTJhYzBlMWFmY2VjNTg3MmRlZTM4MjZmNzk2MjVmOTJkZGUyNjFhZTQzNDJlNXsic2VhcmNoX3BhcmFtcyI6ICJ7XCJxdWVyeVwiOiBcImZvb1wifSIsICJ2YWxpZF91bnRpbCI6ICIxMDAiLCAicmVzdHJpY3RfaW5kaWNlcyI6ICJiYXIiLCAicmVzdHJpY3Rfc291cmNlcyI6ICJiYXoiLCAidXNlcl90b2tlbiI6ICJmb29iYXJiYXoifQ==" + assert _resp == "NDNjMzVmODEzY2YyYzI1YTNkNzQ4YWZmZGM1YTdiMTE3MDBjNzc3YzU0ZWM3ODcyZjAwMGEwYTk1YTdhN2MzY3F1ZXJ5PWZvbyZyZXN0cmljdEluZGljZXM9YmFyJnJlc3RyaWN0U291cmNlcz1iYXomdXNlclRva2VuPWZvb2JhcmJheiZ2YWxpZFVudGlsPTEwMA==" def test_generate_secured_api_key_2(self): """ allow generating a secured api key with the model """ - _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", + _resp = self._client.generate_secured_api_key(parent_api_key="bar", restrictions=SecuredApiKeyRestrictions( + searchParams={"query": "bar", "page": 3}, + validUntil=42, + restrictIndices=["baz"], + restrictSources="foo", + userToken="bazbarfoo", )) - assert _resp == "NmIzMWY4NjlmOGJlZTQxZmI2MjRhZDkwZTY0NWRiY2E3Yzk0NWNhNDMyMjI5NWZiNDk2YzliZjM1YTA1M2Y3Y3sic2VhcmNoUGFyYW1zIjogIntcInF1ZXJ5XCI6IFwiYmFyXCIsIFwic2ltaWxhclF1ZXJ5XCI6IFwiXCIsIFwic3VtT3JGaWx0ZXJzU2NvcmVzXCI6IGZhbHNlLCBcImZhY2V0aW5nQWZ0ZXJEaXN0aW5jdFwiOiBmYWxzZSwgXCJwYWdlXCI6IDMsIFwiYXJvdW5kTGF0TG5nXCI6IFwiXCIsIFwiYXJvdW5kTGF0TG5nVmlhSVBcIjogZmFsc2UsIFwicGVyc29uYWxpemF0aW9uSW1wYWN0XCI6IDEwMCwgXCJnZXRSYW5raW5nSW5mb1wiOiBmYWxzZSwgXCJzeW5vbnltc1wiOiB0cnVlLCBcImNsaWNrQW5hbHl0aWNzXCI6IGZhbHNlLCBcImFuYWx5dGljc1wiOiB0cnVlLCBcInBlcmNlbnRpbGVDb21wdXRhdGlvblwiOiB0cnVlLCBcImVuYWJsZUFCVGVzdFwiOiB0cnVlLCBcInJlbGV2YW5jeVN0cmljdG5lc3NcIjogMTAwLCBcImhpZ2hsaWdodFByZVRhZ1wiOiBcIjxlbT5cIiwgXCJoaWdobGlnaHRQb3N0VGFnXCI6IFwiPC9lbT5cIiwgXCJzbmlwcGV0RWxsaXBzaXNUZXh0XCI6IFwiXFx1MjAyNlwiLCBcInJlc3RyaWN0SGlnaGxpZ2h0QW5kU25pcHBldEFycmF5c1wiOiBmYWxzZSwgXCJoaXRzUGVyUGFnZVwiOiAyMCwgXCJtaW5Xb3JkU2l6ZWZvcjFUeXBvXCI6IDQsIFwibWluV29yZFNpemVmb3IyVHlwb3NcIjogOCwgXCJhbGxvd1R5cG9zT25OdW1lcmljVG9rZW5zXCI6IHRydWUsIFwia2VlcERpYWNyaXRpY3NPbkNoYXJhY3RlcnNcIjogXCJcIiwgXCJkZWNvbXBvdW5kUXVlcnlcIjogdHJ1ZSwgXCJlbmFibGVSdWxlc1wiOiB0cnVlLCBcImVuYWJsZVBlcnNvbmFsaXphdGlvblwiOiBmYWxzZSwgXCJhZHZhbmNlZFN5bnRheFwiOiBmYWxzZSwgXCJyZXBsYWNlU3lub255bXNJbkhpZ2hsaWdodFwiOiBmYWxzZSwgXCJtaW5Qcm94aW1pdHlcIjogMSwgXCJtYXhGYWNldEhpdHNcIjogMTAsIFwibWF4VmFsdWVzUGVyRmFjZXRcIjogMTAwLCBcInNvcnRGYWNldFZhbHVlc0J5XCI6IFwiY291bnRcIiwgXCJhdHRyaWJ1dGVDcml0ZXJpYUNvbXB1dGVkQnlNaW5Qcm94aW1pdHlcIjogZmFsc2UsIFwiZW5hYmxlUmVSYW5raW5nXCI6IHRydWV9IiwgInZhbGlkVW50aWwiOiAiNDIiLCAicmVzdHJpY3RJbmRpY2VzIjogImJheiIsICJyZXN0cmljdFNvdXJjZXMiOiAiZm9vIiwgInVzZXJUb2tlbiI6ICJiYXpiYXJmb28ifQ==" + assert _resp == "OGYyNWMxZjI2YjkxOWNjY2U1ZjcxOWQ0OTQ0ZDY0ZDdlNGZlN2Y2NGI2YTkyZDFhZDMxYzNiNDRkZDMyOGNiMGFkdmFuY2VkU3ludGF4PWZhbHNlJmFsbG93VHlwb3NPbk51bWVyaWNUb2tlbnM9dHJ1ZSZhbmFseXRpY3M9dHJ1ZSZhcm91bmRMYXRMbmc9JmFyb3VuZExhdExuZ1ZpYUlQPWZhbHNlJmF0dHJpYnV0ZUNyaXRlcmlhQ29tcHV0ZWRCeU1pblByb3hpbWl0eT1mYWxzZSZjbGlja0FuYWx5dGljcz1mYWxzZSZkZWNvbXBvdW5kUXVlcnk9dHJ1ZSZlbmFibGVBQlRlc3Q9dHJ1ZSZlbmFibGVQZXJzb25hbGl6YXRpb249ZmFsc2UmZW5hYmxlUmVSYW5raW5nPXRydWUmZW5hYmxlUnVsZXM9dHJ1ZSZmYWNldGluZ0FmdGVyRGlzdGluY3Q9ZmFsc2UmZ2V0UmFua2luZ0luZm89ZmFsc2UmaGlnaGxpZ2h0UG9zdFRhZz0lM0MlMkZlbSUzRSZoaWdobGlnaHRQcmVUYWc9JTNDZW0lM0UmaGl0c1BlclBhZ2U9MjAma2VlcERpYWNyaXRpY3NPbkNoYXJhY3RlcnM9Jm1heEZhY2V0SGl0cz0xMCZtYXhWYWx1ZXNQZXJGYWNldD0xMDAmbWluUHJveGltaXR5PTEmbWluV29yZFNpemVmb3IxVHlwbz00Jm1pbldvcmRTaXplZm9yMlR5cG9zPTgmcGFnZT0zJnBlcmNlbnRpbGVDb21wdXRhdGlvbj10cnVlJnBlcnNvbmFsaXphdGlvbkltcGFjdD0xMDAmcXVlcnk9YmFyJnJlbGV2YW5jeVN0cmljdG5lc3M9MTAwJnJlcGxhY2VTeW5vbnltc0luSGlnaGxpZ2h0PWZhbHNlJnJlc3RyaWN0SGlnaGxpZ2h0QW5kU25pcHBldEFycmF5cz1mYWxzZSZyZXN0cmljdEluZGljZXM9YmF6JnJlc3RyaWN0U291cmNlcz1mb28mc2ltaWxhclF1ZXJ5PSZzbmlwcGV0RWxsaXBzaXNUZXh0PSVFMiU4MCVBNiZzb3J0RmFjZXRWYWx1ZXNCeT1jb3VudCZzdW1PckZpbHRlcnNTY29yZXM9ZmFsc2Umc3lub255bXM9dHJ1ZSZ1c2VyVG9rZW49YmF6YmFyZm9vJnZhbGlkVW50aWw9NDI=" 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", restrictions={"valid_until": 0}) + _resp = self._client.generate_secured_api_key(parent_api_key="foo", restrictions={"validUntil": 0}) _validity = self._client.get_secured_api_key_remaining_validity(_resp) assert abs(_validity) == int(round(time())) @@ -44,11 +44,11 @@ def test_generate_secured_api_key_0(self): throws when the validity field is not found """ try: - _resp = self._client.generate_secured_api_key("foo", {"valid_until": None}) + _resp = self._client.generate_secured_api_key("foo", {"validUntil": None}) self._client.get_secured_api_key_remaining_validity(_resp) assert False except Exception as e: - assert str(e) == "valid_until not found in api key." + assert str(e) == "validUntil not found in api key." def test_generate_secured_api_key_and_validity_2(self): """ diff --git a/templates/python/tests/requests/requests.mustache b/templates/python/tests/requests/requests.mustache index 71cb2a8e82..11429f02dd 100644 --- a/templates/python/tests/requests/requests.mustache +++ b/templates/python/tests/requests/requests.mustache @@ -4,7 +4,7 @@ from os import environ from json import loads from unittest.mock import AsyncMock from algoliasearch.http.transporter import EchoTransporter -from algoliasearch.search.models.secured_api_key_restrictions 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 diff --git a/templates/ruby/search_helpers.mustache b/templates/ruby/search_helpers.mustache index 1365ed44dd..7a06e4b5d4 100644 --- a/templates/ruby/search_helpers.mustache +++ b/templates/ruby/search_helpers.mustache @@ -179,9 +179,6 @@ def generate_secured_api_key(parent_api_key, restrictions = {}) end.join('&') hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), parent_api_key, url_encoded_restrictions) - - puts "hmac: #{hmac}" - puts "url_encoded_restrictions: #{url_encoded_restrictions}" Base64.encode64("#{hmac}#{url_encoded_restrictions}").gsub("\n", '') end diff --git a/templates/ruby/tests/client/suite.mustache b/templates/ruby/tests/client/suite.mustache index 64e0b04888..ed7dd3bea4 100644 --- a/templates/ruby/tests/client/suite.mustache +++ b/templates/ruby/tests/client/suite.mustache @@ -39,7 +39,12 @@ class TestClient{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}} < Test: assert_equal('{{{match}}}', req.host.url) {{/testHost}} {{#testResponse}} + {{#matchIsObject}} assert_equal({{{match.parameters}}}, req) + {{/matchIsObject}} + {{^matchIsObject}} + assert_equal('{{{match}}}', req) + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} diff --git a/templates/scala/tests/client/suite.mustache b/templates/scala/tests/client/suite.mustache index 14c6b5cbc8..da6dc021c7 100644 --- a/templates/scala/tests/client/suite.mustache +++ b/templates/scala/tests/client/suite.mustache @@ -39,6 +39,7 @@ class {{clientPrefix}}Test extends AnyFunSuite { {{#blocksClient}} {{#tests}} + {{^isHelper}} {{! Helper tests are not supported yet}} test("{{{testName}}}") { {{#autoCreateClient}} @@ -68,12 +69,18 @@ class {{clientPrefix}}Test extends AnyFunSuite { assert(echo.lastResponse.get.host == "{{{match}}}") {{/testHost}} {{#testResponse}} + {{#matchIsObject}} assert(write(res) == "{{#lambda.escapeQuotes}}{{{match.parameters}}}{{/lambda.escapeQuotes}}") + {{/matchIsObject}} + {{^matchIsObject}} + assert(res == "{{{match}}}") + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} {{/steps}} } + {{/isHelper}} {{/tests}} {{/blocksClient}} } \ No newline at end of file diff --git a/templates/swift/tests/client/method.mustache b/templates/swift/tests/client/method.mustache index a107ba1b5e..4ef729f083 100644 --- a/templates/swift/tests/client/method.mustache +++ b/templates/swift/tests/client/method.mustache @@ -1,14 +1,14 @@ -let response = try await client{{#path}}.{{.}}WithHTTPInfo{{/path}}( +let response = {{#isAsync}}try await {{/isAsync}}client{{#path}}.{{.}}{{#isAsync}}WithHTTPInfo{{/isAsync}}{{/path}}( {{#parametersWithDataType}}{{> tests/generateParams }}{{^-last}},{{/-last}} {{/parametersWithDataType}}{{#requestOptions.parametersWithDataType}}{{#-first}}, requestOptions: RequestOptions({{/-first}} {{> tests/generateParams }}{{^-last}},{{/-last}} ){{/requestOptions.parametersWithDataType}}) +{{^isHelper}} let responseBodyData = try XCTUnwrap(response.bodyData) {{#useEchoRequester}} let echoResponse = try CodableHelper.jsonDecoder.decode(EchoResponse.self, from: responseBodyData) {{/useEchoRequester}} {{^useEchoRequester}} let responseBodyJSON = try XCTUnwrap(responseBodyData.jsonString) -let comparableData = "{{#lambda.escapeQuotes}}{{{match.parameters}}}{{/lambda.escapeQuotes}}".data(using: .utf8) -let comparableJSON = try XCTUnwrap(comparableData?.jsonString) -{{/useEchoRequester}} \ No newline at end of file +{{/useEchoRequester}} +{{/isHelper}} \ No newline at end of file diff --git a/templates/swift/tests/client/suite.mustache b/templates/swift/tests/client/suite.mustache index 2fa412a465..eef3d2475e 100644 --- a/templates/swift/tests/client/suite.mustache +++ b/templates/swift/tests/client/suite.mustache @@ -10,8 +10,9 @@ final class {{client}}ClientTests: XCTestCase { let APPLICATION_ID = "my_application_id" let API_KEY = "my_api_key" -{{#blocksClient}} + {{#blocksClient}} {{#tests}} + {{^isHelper}} {{! Helper tests are not supported yet}} /** {{testName}} @@ -57,12 +58,22 @@ final class {{client}}ClientTests: XCTestCase { XCTAssertEqual("{{{match}}}", echoResponse.host); {{/testHost}} {{#testResponse}} - XCTAssertEqual(comparableJSON, responseBodyJSON); + {{#matchIsObject}} + let comparableData = "{{#lambda.escapeQuotes}}{{{match.parameters}}}{{/lambda.escapeQuotes}}".data(using: .utf8) + let comparableJSON = try XCTUnwrap(comparableData?.jsonString) + XCTAssertEqual(comparableJSON, responseBodyJSON); + {{/matchIsObject}} + {{^matchIsObject}} + let comparableData = "{{#lambda.escapeQuotes}}{{{match}}}{{/lambda.escapeQuotes}}".data(using: .utf8) + let comparableJSON = try XCTUnwrap(comparableData?.jsonString) + XCTAssertEqual(comparableJSON, responseBodyJSON); + {{/matchIsObject}} {{/testResponse}} {{/match}} {{/isError}} {{/steps}} } + {{/isHelper}} {{/tests}} {{/blocksClient}} } \ No newline at end of file diff --git a/tests/CTS/client/search/helpers.json b/tests/CTS/client/search/helpers.json new file mode 100644 index 0000000000..d8ead221f9 --- /dev/null +++ b/tests/CTS/client/search/helpers.json @@ -0,0 +1,63 @@ +[ + { + "testName": "generate secured api key basic", + "steps": [ + { + "type": "method", + "object": "$client", + "path": "generateSecuredApiKey", + "parameters": { + "parentApiKey": "2640659426d5107b6e47d75db9cbaef8", + "restrictions": { + "validUntil": 2524604400, + "restrictIndices": [ + "Movies" + ] + } + }, + "expected": { + "type": "response", + "match": "NjFhZmE0OGEyMTI3OThiODc0OTlkOGM0YjcxYzljY2M2NmU2NDE5ZWY0NDZjMWJhNjA2NzBkMjAwOTI2YWQyZnJlc3RyaWN0SW5kaWNlcz1Nb3ZpZXMmdmFsaWRVbnRpbD0yNTI0NjA0NDAw" + } + } + ] + }, + { + "testName": "generate secured api key with searchParams", + "steps": [ + { + "type": "method", + "object": "$client", + "path": "generateSecuredApiKey", + "parameters": { + "parentApiKey": "2640659426d5107b6e47d75db9cbaef8", + "restrictions": { + "validUntil": 2524604400, + "restrictIndices": [ + "Movies", + "cts_e2e_settings" + ], + "restrictSources": "192.168.1.0/24", + "filters": "category:Book OR category:Ebook AND _tags:published", + "userToken": "user123", + "searchParams": { + "query": "batman", + "typoTolerance": "strict", + "aroundRadius": "all", + "mode": "neuralSearch", + "hitsPerPage": 10, + "optionalWords": [ + "one", + "two" + ] + } + } + }, + "expected": { + "type": "response", + "match": "MzAxMDUwYjYyODMxODQ3ZWM1ZDYzNTkxZmNjNDg2OGZjMjAzYjQyOTZhMGQ1NDJhMDFiNGMzYTYzODRhNmMxZWFyb3VuZFJhZGl1cz1hbGwmZmlsdGVycz1jYXRlZ29yeSUzQUJvb2slMjBPUiUyMGNhdGVnb3J5JTNBRWJvb2slMjBBTkQlMjBfdGFncyUzQXB1Ymxpc2hlZCZoaXRzUGVyUGFnZT0xMCZtb2RlPW5ldXJhbFNlYXJjaCZvcHRpb25hbFdvcmRzPW9uZSUyQ3R3byZxdWVyeT1iYXRtYW4mcmVzdHJpY3RJbmRpY2VzPU1vdmllcyUyQ2N0c19lMmVfc2V0dGluZ3MmcmVzdHJpY3RTb3VyY2VzPTE5Mi4xNjguMS4wJTJGMjQmdHlwb1RvbGVyYW5jZT1zdHJpY3QmdXNlclRva2VuPXVzZXIxMjMmdmFsaWRVbnRpbD0yNTI0NjA0NDAw" + } + } + ] + } +] diff --git a/tests/output/csharp/src/SecuredApiKeysTests.cs b/tests/output/csharp/src/SecuredApiKeysTests.cs index 58025fddad..eede01dd4c 100644 --- a/tests/output/csharp/src/SecuredApiKeysTests.cs +++ b/tests/output/csharp/src/SecuredApiKeysTests.cs @@ -11,48 +11,11 @@ namespace Algolia.Search.Tests; public class SecuredApiKeysTests { - [Fact] - public void ShouldGenerateSecuredApiKey() - { - var client = new SearchClient(new SearchConfig("test-app-id", "test-api-key")); - var securedApiKey = client.GenerateSecuredApiKey( - "parent-api-key", - new SecuredAPIKeyRestrictions - { - SearchParams = new SearchParamsObject - { - Mode = Mode.NeuralSearch, - HitsPerPage = 10, - OptionalWords = ["one", "two"], - QueryType = QueryType.PrefixNone, - EnableRules = true, - AlternativesAsExact = - [ - AlternativesAsExact.IgnorePlurals, - AlternativesAsExact.SingleWordSynonym - ], - AttributeCriteriaComputedByMinProximity = false - }, - RestrictIndices = ["index1", "index2"], - 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=192.168.1.0%2F24&userToken=my-user-token"; - var hash = HmacShaHelper.GetHash("parent-api-key", expectedQueryParams); - var expectedKey = HmacShaHelper.Base64Encode($"{hash}{expectedQueryParams}"); - - Assert.Equal(expectedKey, securedApiKey); - } - [Fact] public void ShouldDetectExpiredKey() { // Test an expired key - var restriction = new SecuredAPIKeyRestrictions + var restriction = new SecuredApiKeyRestrictions { ValidUntil = new DateTimeOffset(DateTime.UtcNow.AddMinutes(-10)).ToUnixTimeSeconds(), RestrictIndices = ["indexName"] @@ -73,7 +36,7 @@ public void ShouldDetectExpiredKey() public void ShouldDetectValidKey() { // Test a valid key - var restriction = new SecuredAPIKeyRestrictions + var restriction = new SecuredApiKeyRestrictions { ValidUntil = new DateTimeOffset(DateTime.UtcNow.AddMinutes(10)).ToUnixTimeSeconds(), RestrictIndices = ["indexName"] @@ -94,7 +57,7 @@ public void ShouldDetectValidKey() public void TestRemainingValidityParameters() { // Test a valid key, but with no validUntil - var restriction = new SecuredAPIKeyRestrictions { 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 similarity index 96% rename from tests/output/swift/Tests/handwritten/SecuredAPIKeyHelpers.swift rename to tests/output/swift/Tests/handwritten/SecuredApiKeyHelpers.swift index 4aba47a215..4dba48c7b3 100644 --- a/tests/output/swift/Tests/handwritten/SecuredAPIKeyHelpers.swift +++ b/tests/output/swift/Tests/handwritten/SecuredApiKeyHelpers.swift @@ -1,5 +1,5 @@ // -// SecuredAPIKeyHelpers.swift +// SecuredApiKeyHelpers.swift // // // Created by Algolia on 23/02/2024. @@ -36,7 +36,7 @@ class SecuredApiKeyHelpersTests: XCTestCase { let securedApiKey = try client.generateSecuredApiKey( parentApiKey: "parent-api-key", - with: SecuredAPIKeyRestrictions( + with: SecuredApiKeyRestrictions( searchParams: SearchSearchParamsObject(hitsPerPage: 2), validUntil: Int64(now + .seconds(13)), restrictIndices: ["index1", "index2"]