diff --git a/clients/algoliasearch-client-go/README.md b/clients/algoliasearch-client-go/README.md index 5f6078aad7..750b835aaa 100644 --- a/clients/algoliasearch-client-go/README.md +++ b/clients/algoliasearch-client-go/README.md @@ -47,30 +47,25 @@ import "github.com/algolia/algoliasearch-client-go/v4/algolia/search" client, err := search.NewClient("YOUR_APP_ID", "YOUR_API_KEY") // Add a new record to your Algolia index -response, err := client.SaveObject(client.NewApiSaveObjectRequest( - "", map[string]any{"objectID": "id", "test": "val"}, -)) +saveResponse, err := client.SaveObject("", map[string]any{"objectID": "id", "test": "val"}) if err != nil { // handle the eventual error panic(err) } // use the model directly -print(response) +print(saveResponse) // Poll the task status to know when it has been indexed -taskResponse, err := searchClient.WaitForTask("", response.TaskID, nil, nil, nil) +_, err = client.WaitForTask("", saveResponse.TaskID) if err != nil { panic(err) } // Fetch search results, with typo tolerance -response, err := client.Search(client.NewApiSearchRequest( - - search.NewEmptySearchMethodParams().SetRequests( - []search.SearchQuery{*search.SearchForHitsAsSearchQuery( - search.NewEmptySearchForHits().SetIndexName("").SetQuery("").SetHitsPerPage(50))}), -)) +response, err := client.Search([]search.SearchQuery{ + *search.SearchForHitsAsSearchQuery(search.NewSearchForHits("").SetQuery("").SetHitsPerPage(50)), +}, nil) if err != nil { // handle the eventual error panic(err) @@ -78,6 +73,8 @@ if err != nil { // use the model directly print(response) + +return 0 ``` For full documentation, visit the **[Algolia Go API Client](https://www.algolia.com/doc/libraries/go/)**. diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java index 821b9322f3..5adb90234f 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java @@ -1,8 +1,10 @@ package com.algolia.codegen; +import com.algolia.codegen.cts.lambda.ScreamingSnakeCaseLambda; import com.algolia.codegen.exceptions.*; -import com.algolia.codegen.lambda.ScreamingSnakeCaseLambda; import com.algolia.codegen.utils.*; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.samskivert.mustache.Mustache; @@ -11,6 +13,7 @@ import io.swagger.v3.oas.models.servers.Server; import java.io.File; import java.util.*; +import java.util.stream.Collectors; import org.openapitools.codegen.*; import org.openapitools.codegen.languages.GoClientCodegen; import org.openapitools.codegen.model.ModelMap; @@ -19,6 +22,9 @@ public class AlgoliaGoGenerator extends GoClientCodegen { + // This is used for the CTS generation + private static final AlgoliaGoGenerator INSTANCE = new AlgoliaGoGenerator(); + @Override public String getName() { return "algolia-go"; @@ -29,7 +35,6 @@ public void processOpts() { String client = (String) additionalProperties.get("client"); additionalProperties.put("packageName", client.equals("query-suggestions") ? "suggestions" : Helpers.camelize(client)); - additionalProperties.put("enumClassPrefix", true); additionalProperties.put("is" + Helpers.capitalize(Helpers.camelize(client)) + "Client", true); String outputFolder = "algolia" + File.separator + client; @@ -78,6 +83,10 @@ public void processOpenAPI(OpenAPI openAPI) { super.processOpenAPI(openAPI); Helpers.generateServers(super.fromServers(openAPI.getServers()), additionalProperties); Timeouts.enrichBundle(openAPI, additionalProperties); + additionalProperties.put( + "appDescription", + Arrays.stream(openAPI.getInfo().getDescription().split("\n")).map(line -> "// " + line).collect(Collectors.joining("\n")).trim() + ); } @Override @@ -140,9 +149,104 @@ public Map postProcessAllModels(Map objs) @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List models) { OperationsMap operations = super.postProcessOperationsWithModels(objs, models); + + // Flatten body params to remove the wrapping object + for (CodegenOperation ope : operations.getOperations().getOperation()) { + // clean up the description + String[] lines = ope.unescapedNotes.split("\n"); + ope.notes = (lines[0] + "\n" + Arrays.stream(lines).skip(1).map(line -> "// " + line).collect(Collectors.joining("\n"))).trim(); + + // enrich the params + for (CodegenParameter param : ope.optionalParams) { + param.nameInPascalCase = Helpers.capitalize(param.baseName); + } + + CodegenParameter bodyParam = ope.bodyParam; + if (bodyParam != null) { + flattenBody(ope); + } + + // If the optional param struct only has 1 param, we can remove the wrapper + if (ope.optionalParams.size() == 1) { + CodegenParameter param = ope.optionalParams.get(0); + + // move it to required, it's easier to handle im mustache + ope.hasOptionalParams = false; + ope.optionalParams.clear(); + + ope.hasRequiredParams = true; + ope.requiredParams.add(param); + } + } + ModelPruner.removeOrphans(this, operations, models); Helpers.removeHelpers(operations); GenericPropagator.propagateGenericsToOperations(operations, models); return operations; } + + private void flattenBody(CodegenOperation ope) { + CodegenParameter bodyParam = ope.bodyParam; + bodyParam.nameInPascalCase = Helpers.capitalize(bodyParam.baseName); + if (!bodyParam.isModel) { + return; + } + + if (!canFlattenBody(ope)) { + System.out.println( + "Operation " + ope.operationId + " has body param " + bodyParam.paramName + " in colision with a parameter, skipping flattening" + ); + return; + } + + bodyParam.vendorExtensions.put("x-flat-body", bodyParam.getVars().size() > 0); + + if (bodyParam.getVars().size() > 0) { + ope.allParams.removeIf(param -> param.isBodyParam); + ope.requiredParams.removeIf(param -> param.isBodyParam); + ope.optionalParams.removeIf(param -> param.isBodyParam); + } + + for (CodegenProperty prop : bodyParam.getVars()) { + prop.nameInLowerCase = toParamName(prop.baseName); + + CodegenParameter param = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .convertValue(prop, CodegenParameter.class); + param.nameInPascalCase = Helpers.capitalize(prop.baseName); + param.paramName = toParamName(prop.baseName); + + if (prop.required) { + ope.requiredParams.add(param); + ope.hasRequiredParams = true; + } else { + ope.optionalParams.add(param); + ope.hasOptionalParams = true; + } + ope.allParams.add(param); + } + } + + public static boolean canFlattenBody(CodegenOperation ope) { + if (ope.bodyParam == null || !ope.bodyParam.isModel) { + return false; + } + + if (ope.allParams.size() == 1) { + return true; + } + + for (CodegenProperty prop : ope.bodyParam.getVars()) { + for (CodegenParameter param : ope.allParams) { + if (param.paramName.equals(prop.baseName)) { + return false; + } + } + } + return true; + } + + public static String toEnum(String value) { + return INSTANCE.toEnumVarName(value, "String"); + } } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java index da658d695d..60a426a7fc 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java @@ -237,20 +237,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List addMustacheLambdas() { lambdas.put("escapeSlash", new EscapeSlashLambda()); lambdas.put("escapeJSON", new EscapeJSONLambda()); lambdas.put("replaceBacktick", new ReplaceBacktickLambda()); + lambdas.put("screamingSnakeCase", new ScreamingSnakeCaseLambda()); return lambdas; } diff --git a/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicSnippetLambda.java b/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicSnippetLambda.java index e5647d767a..2609da2254 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicSnippetLambda.java +++ b/generators/src/main/java/com/algolia/codegen/cts/lambda/DynamicSnippetLambda.java @@ -32,6 +32,7 @@ public class DynamicSnippetLambda implements Mustache.Lambda { private final Map operations; private final Map snippets; + private final String language; public DynamicSnippetLambda( DefaultCodegen generator, @@ -41,6 +42,7 @@ public DynamicSnippetLambda( String client ) { this.operations = operations; + this.language = language; this.paramsType = new ParametersWithDataType(models, language, client, true); JsonNode snippetsFile = Helpers.readJsonFile("tests/CTS/guides/" + client + ".json"); @@ -74,7 +76,7 @@ public void execute(Template.Fragment fragment, Writer writer) throws IOExceptio // set the method attributes Map context = (Map) fragment.context(); - snippet.addMethodCall(context, paramsType, operation); + snippet.addMethodCall(language, context, paramsType, operation); writer.write(adaptor.compileTemplate(executor, context, "tests/method.mustache")); } diff --git a/generators/src/main/java/com/algolia/codegen/lambda/ScreamingSnakeCaseLambda.java b/generators/src/main/java/com/algolia/codegen/cts/lambda/ScreamingSnakeCaseLambda.java similarity index 91% rename from generators/src/main/java/com/algolia/codegen/lambda/ScreamingSnakeCaseLambda.java rename to generators/src/main/java/com/algolia/codegen/cts/lambda/ScreamingSnakeCaseLambda.java index 29b6cd61b8..d09abaddb4 100644 --- a/generators/src/main/java/com/algolia/codegen/lambda/ScreamingSnakeCaseLambda.java +++ b/generators/src/main/java/com/algolia/codegen/cts/lambda/ScreamingSnakeCaseLambda.java @@ -1,4 +1,4 @@ -package com.algolia.codegen.lambda; +package com.algolia.codegen.cts.lambda; import com.algolia.codegen.utils.Helpers; import com.samskivert.mustache.Mustache; diff --git a/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java b/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java index 45090a0da7..4242e5089e 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java +++ b/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java @@ -1,7 +1,9 @@ package com.algolia.codegen.cts.manager; +import com.algolia.codegen.AlgoliaGoGenerator; import com.algolia.codegen.exceptions.GeneratorException; import com.algolia.codegen.utils.*; +import com.samskivert.mustache.Mustache.Lambda; import java.util.*; import org.openapitools.codegen.SupportingFile; @@ -40,4 +42,9 @@ public void addSnippetsSupportingFiles(List supportingFiles, Str supportingFiles.add(new SupportingFile("snippets/.golangci.mustache", output + "/go/.golangci.yml")); supportingFiles.add(new SupportingFile("snippets/go.mod.mustache", output + "/go/go.mod")); } + + @Override + public void addMustacheLambdas(Map lambdas) { + lambdas.put("toEnum", (fragment, writer) -> writer.write(AlgoliaGoGenerator.toEnum(fragment.execute()))); + } } diff --git a/generators/src/main/java/com/algolia/codegen/cts/manager/JavaCTSManager.java b/generators/src/main/java/com/algolia/codegen/cts/manager/JavaCTSManager.java index 42e5aece0b..886c873d0e 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/manager/JavaCTSManager.java +++ b/generators/src/main/java/com/algolia/codegen/cts/manager/JavaCTSManager.java @@ -52,6 +52,6 @@ public void addDataToBundle(Map bundle) throws GeneratorExceptio @Override public void addMustacheLambdas(Map lambdas) { - lambdas.put("javaEnum", (fragment, writer) -> writer.write(AlgoliaJavaGenerator.toEnum(fragment.execute()))); + lambdas.put("toEnum", (fragment, writer) -> writer.write(AlgoliaJavaGenerator.toEnum(fragment.execute()))); } } diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java b/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java index 0cd8c7ddaa..0ba4fe7673 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java @@ -1,5 +1,6 @@ package com.algolia.codegen.cts.tests; +import com.algolia.codegen.AlgoliaGoGenerator; import com.algolia.codegen.AlgoliaSwiftGenerator; import com.algolia.codegen.exceptions.*; import com.algolia.codegen.utils.*; @@ -60,7 +61,7 @@ public void enhanceParameters(Map parameters, Map parameters, Map paramWithType = traverseParams(param.getKey(), param.getValue(), specParam, "", 0, false); - parametersWithDataType.add(paramWithType); - parametersWithDataTypeMap.put((String) paramWithType.get("key"), paramWithType); + if ( + language.equals("go") && + specParam != null && + specParam.isBodyParam && + operation != null && + operation.bodyParam != null && + operation.bodyParam.isModel && + operation.bodyParam.getVars().size() > 0 && + AlgoliaGoGenerator.canFlattenBody(operation) + ) { + // flatten the body params by skipping one level + flattenBodyParams((Map) param.getValue(), operation, parametersWithDataType); + } else { + Map paramWithType = traverseParams(param.getKey(), param.getValue(), specParam, "", 0, false); + parametersWithDataType.add(paramWithType); + parametersWithDataTypeMap.put((String) paramWithType.get("key"), paramWithType); + } } } + } else if (language.equals("go") && parameters != null && operation.bodyParam.getVars().size() > 0) { + // also flatten when the body is the only parameter + flattenBodyParams(parameters, operation, parametersWithDataType); } else { Map paramWithType = traverseParams(paramName, parameters, spec, "", 0, false); parametersWithDataType.add(paramWithType); @@ -105,6 +123,22 @@ private String toJSONWithVar(Map parameters) throws JsonProcessi return Json.mapper().writeValueAsString(parameters).replaceAll("\"\\$var: (.*?)\"", "$1"); } + private void flattenBodyParams( + Map parameters, + CodegenOperation operation, + List> parametersWithDataType + ) throws CTSException { + for (String nestedParam : parameters.keySet()) { + for (CodegenProperty prop : operation.bodyParam.getVars()) { + if (prop.baseName.equals(nestedParam)) { + Map paramWithType = traverseParams(prop.baseName, parameters.get(nestedParam), prop, "", 0, false); + parametersWithDataType.add(paramWithType); + break; + } + } + } + } + private Map traverseParams( String paramName, Object param, @@ -414,15 +448,7 @@ private void handleModel( } if (language.equals("swift")) { - // Store ordered params from the spec - var orderedParams = spec.getVars().stream().map(v -> v.baseName).toList(); - - // Create a map to store the indices of each string in orderedParams - Map indexMap = IntStream.range(0, orderedParams.size()) - .boxed() - .collect(Collectors.toMap(orderedParams::get, i -> i)); - - values.sort(Comparator.comparing(value -> indexMap.getOrDefault((String) value.get("key"), Integer.MAX_VALUE))); + sortParameters(spec, values); } var hasAdditionalProperties = values @@ -750,4 +776,14 @@ private boolean couldMatchEnum(Object value, CodegenProperty model) { return ((List) values).contains(value); } + + private void sortParameters(IJsonSchemaValidationProperties spec, List> parameters) { + // Store ordered params from the spec + var orderedParams = spec.getVars().stream().map(v -> v.baseName).toList(); + + // Create a map to store the indices of each string in orderedParams + Map indexMap = IntStream.range(0, orderedParams.size()).boxed().collect(Collectors.toMap(orderedParams::get, i -> i)); + + parameters.sort(Comparator.comparing(param -> indexMap.getOrDefault((String) param.get("key"), Integer.MAX_VALUE))); + } } diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java b/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java index 42c4fc8dc6..e287d16952 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java @@ -38,7 +38,8 @@ public String toString() { return sb.toString(); } - public void addMethodCall(Map context, ParametersWithDataType paramsType, CodegenOperation ope) throws CTSException { + public void addMethodCall(String language, Map context, ParametersWithDataType paramsType, CodegenOperation ope) + throws CTSException { // for dynamic snippets, we need to reset the context because the order of generation is random context.put("method", method); context.put("returnType", null); @@ -52,11 +53,12 @@ public void addMethodCall(Map context, ParametersWithDataType pa } try { + boolean isHelper = (boolean) ope.vendorExtensions.getOrDefault("x-helper", false); context.put("isGeneric", (boolean) ope.vendorExtensions.getOrDefault("x-is-generic", false)); context.put("isCustomRequest", Helpers.CUSTOM_METHODS.contains(ope.operationIdOriginal)); context.put("isAsyncMethod", (boolean) ope.vendorExtensions.getOrDefault("x-asynchronous-helper", true)); context.put("hasParams", ope.hasParams); - context.put("isHelper", (boolean) ope.vendorExtensions.getOrDefault("x-helper", false)); + context.put("isHelper", isHelper); context.put("hasRequestOptions", requestOptions != null); if (requestOptions != null) { @@ -85,6 +87,8 @@ public void addMethodCall(Map context, ParametersWithDataType pa } } + TestsGenerator.setOptionalParameters(language, ope, context, parameters, isHelper); + paramsType.enhanceParameters(parameters, context, ope); } catch (CTSException e) { e.setTestName((String) context.get("testName")); diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java b/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java index 59422e098e..1041f2dbf2 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java @@ -118,7 +118,7 @@ public void run(Map models, Map test.put("testName", ops.size() > 1 ? name : "default"); test.put("description", name); test.put("testIndex", i == 0 ? "" : i); - snippet.addMethodCall(test, paramsType, ope); + snippet.addMethodCall(language, test, paramsType, ope); addRequestOptions(paramsType, snippet.requestOptions, test); tests.add(test); } 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 c18dd14cef..9619d75296 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 @@ -80,6 +80,12 @@ public void run(Map models, Map testOut.put("autoCreateClient", test.autoCreateClient); testOut.put("useEchoRequester", true); testOut.put("isBenchmark", withBenchmark); + + if (language.equals("go") && "`addApiKey` throws with invalid parameters".equals(test.testName)) { + // skip this test because the body is flattened in go + continue; + } + for (Step step : test.steps) { Map stepOut = new HashMap<>(); if (step.times > 1) stepOut.put("times", step.times); @@ -156,6 +162,7 @@ public void run(Map models, Map // default to true because most api calls are asynchronous testOut.put("isAsyncMethod", (boolean) ope.vendorExtensions.getOrDefault("x-asynchronous-helper", true)); + setOptionalParameters(language, ope, stepOut, step.parameters, isHelper); addRequestOptions(paramsType, step.requestOptions, stepOut); methodCount++; @@ -176,6 +183,7 @@ public void run(Map models, Map ((List>) stepOut.getOrDefault("parametersWithDataType", new ArrayList<>())).stream() .anyMatch(item -> (boolean) item.getOrDefault("isNullObject", false) || (boolean) item.getOrDefault("isNull", false)); if (isNotTestable) { + System.out.println("Skipping test " + test.testName + " for " + language + " because of nil object"); continue; } } diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsGenerator.java b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsGenerator.java index eee67de83c..da2fa94f59 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsGenerator.java @@ -1,5 +1,6 @@ package com.algolia.codegen.cts.tests; +import com.algolia.codegen.AlgoliaGoGenerator; import com.algolia.codegen.cts.manager.CTSManager; import com.algolia.codegen.exceptions.CTSException; import com.algolia.codegen.utils.*; @@ -11,9 +12,7 @@ import java.nio.file.Paths; import java.util.*; import org.apache.commons.lang3.ArrayUtils; -import org.openapitools.codegen.CodegenModel; -import org.openapitools.codegen.CodegenOperation; -import org.openapitools.codegen.SupportingFile; +import org.openapitools.codegen.*; public abstract class TestsGenerator { @@ -134,4 +133,69 @@ protected void addRequestOptions(ParametersWithDataType paramsType, RequestOptio output.put("requestOptions", requestOptions); } } + + public static void setOptionalParameters( + String language, + CodegenOperation ope, + Map test, + Map parameters, + boolean isHelper + ) { + if (!"go".equals(language)) return; + + boolean isBodyRequired = ope.bodyParam != null && ope.bodyParam.required; + boolean alreadyInlinedBody = ope.allParams.size() == 1 && ope.bodyParam != null && !ope.bodyParam.isArray; + // I can't figure out the correct condition for this one so it's harcoded for now + boolean isSFFV = + "searchForFacetValues".equals(ope.operationId) && !ope.tags.isEmpty() && "composition".equals(ope.tags.get(0).getName()); + + int bodyPropsOptional = 0; + boolean isBodyTooBig = false; + boolean actuallyHasOptional = false; + + if (AlgoliaGoGenerator.canFlattenBody(ope) && ope.bodyParam != null) { + List vars = ope.bodyParam.getVars(); + bodyPropsOptional = (int) vars.stream().filter(p -> !p.required).count(); + isBodyTooBig = vars.isEmpty(); + + Map paramBody = parameters; + if (!alreadyInlinedBody) { + Object paramObj = parameters.get(ope.bodyParam.paramName); + if (paramObj instanceof String) { + actuallyHasOptional = !isBodyRequired; + } else if (paramObj instanceof Map) { + paramBody = (Map) paramObj; + } + } + + for (CodegenProperty prop : vars) { + if (!prop.required && paramBody != null && paramBody.containsKey(prop.baseName)) { + actuallyHasOptional = true; + } + } + } + + for (CodegenParameter param : ope.allParams) { + if (!param.required && parameters.containsKey(param.baseName)) { + actuallyHasOptional = true; + break; + } + } + + int totalOptional = ope.optionalParams.size() + bodyPropsOptional; + + // hasOptionalWrapper if there is more that one optional param, after the body has been + // flattened. + boolean hasOptionalWrapper = totalOptional > 1 && actuallyHasOptional && !isSFFV; + boolean hasInlineOptional = ((totalOptional == 1 || isSFFV) && actuallyHasOptional) || isBodyTooBig; + boolean hasNilOptional = totalOptional > 0 && !actuallyHasOptional && !isHelper; + if (isBodyTooBig && !isBodyRequired) { + boolean isBodySet = alreadyInlinedBody ? !parameters.isEmpty() : parameters.containsKey(ope.bodyParam.paramName); + hasNilOptional = !isBodySet; + } + + test.put("hasOptionalWrapper", hasOptionalWrapper); + test.put("hasInlineOptional", hasInlineOptional); + test.put("hasNilOptional", hasNilOptional); + } } 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 e77e1596dd..8f2169abf1 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 @@ -161,6 +161,7 @@ public void run(Map models, Map test.put("hasParams", ope.hasParams); test.put("isHelper", isHelper); + setOptionalParameters(language, ope, test, req.parameters, isHelper); addRequestOptions(paramsType, req.requestOptions, test); // Determines whether the endpoint is expected to return a response payload deserialized diff --git a/playground/go/analytics.go b/playground/go/analytics.go index 4f18dc7327..d6c7860504 100644 --- a/playground/go/analytics.go +++ b/playground/go/analytics.go @@ -13,9 +13,7 @@ func testAnalytics(appID, apiKey string) int { panic(err) } - getTopFilterForAttributeResponse, err := analyticsClient.GetTopFilterForAttribute( - analyticsClient.NewApiGetTopFilterForAttributeRequest("myAttribute1,myAttribute2", indexName), - ) + getTopFilterForAttributeResponse, err := analyticsClient.GetTopFilterForAttribute("myAttribute1,myAttribute2", indexName, nil) if err != nil { fmt.Printf("request error with GetTopFilterForAttribute: %v\n", err) return 1 diff --git a/playground/go/ingestion.go b/playground/go/ingestion.go index b72371c045..d858e804f7 100644 --- a/playground/go/ingestion.go +++ b/playground/go/ingestion.go @@ -13,14 +13,14 @@ func testIngestion(appID, apiKey string) int { } // another example to generate payload for a request. - _, payload, err := ingestionClient.ListTasksWithHTTPInfo(ingestionClient.NewApiListTasksRequest()) + res, err := ingestionClient.ListTasks(nil) if err != nil { fmt.Printf("request error: %v\n", err) return 1 } - fmt.Println(string(payload)) + fmt.Println(res) return 0 } diff --git a/playground/go/insights.go b/playground/go/insights.go index 6dcebd25bb..0dca26438a 100644 --- a/playground/go/insights.go +++ b/playground/go/insights.go @@ -12,17 +12,15 @@ func testInsights(appID, apiKey string) int { panic(err) } - events := insights.NewInsightsEvents([]insights.EventsItems{ + events := []insights.EventsItems{ *insights.ClickedObjectIDsAsEventsItems(insights.NewClickedObjectIDs("myEvent", insights.CLICK_EVENT_CLICK, "test_index", []string{"myObjectID"}, "myToken", insights.WithClickedObjectIDsTimestamp(1234567890))), - }) - eventsResponse, err := insightsClient.PushEvents( - insightsClient.NewApiPushEventsRequest(events), - ) + } + eventsResponse, err := insightsClient.PushEvents(events) if err != nil { fmt.Printf("request error with PushEvents: %v\n", err) return 1 diff --git a/playground/go/personalization.go b/playground/go/personalization.go index 5fa84b4b73..bdffed3357 100644 --- a/playground/go/personalization.go +++ b/playground/go/personalization.go @@ -17,10 +17,7 @@ func testPersonalization(appID, apiKey string) int { defer cancel() // it will fail expectedly because of the very short timeout to showcase the context usage. - deleteUserProfileResponse, err := personalizationClient.DeleteUserProfile( - personalizationClient.NewApiDeleteUserProfileRequest("userToken"), - personalization.WithContext(ctx), - ) + deleteUserProfileResponse, err := personalizationClient.DeleteUserProfile("userToken", personalization.WithContext(ctx)) if err != nil { fmt.Printf("request error with DeleteUserProfile: %v\n", err) return 1 diff --git a/playground/go/query-suggestions.go b/playground/go/query-suggestions.go index 262ea7b036..b529c4c1e7 100644 --- a/playground/go/query-suggestions.go +++ b/playground/go/query-suggestions.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/algolia/algoliasearch-client-go/v4/algolia/query-suggestions" + suggestions "github.com/algolia/algoliasearch-client-go/v4/algolia/query-suggestions" ) func testQuerySuggestions(appID, apiKey string) int { @@ -12,7 +12,6 @@ func testQuerySuggestions(appID, apiKey string) int { panic(err) } - // if there is no params for the requests, we don't need to give empty request instance such as `suggestionsClient.NewApiGetAllConfigsRequest()`. querySuggestionsIndex, err := suggestionsClient.GetAllConfigs() if err != nil { fmt.Printf("request error with GetAllConfigs: %v\n", err) diff --git a/playground/go/recommend.go b/playground/go/recommend.go index 75d7a8c6bf..4085332ba1 100644 --- a/playground/go/recommend.go +++ b/playground/go/recommend.go @@ -13,27 +13,21 @@ func testRecommend(appID, apiKey string) int { } /* - recommend.NewGetRecommendationsParams([]recommend.RecommendationsRequest{ + params := []recommend.RecommendationsRequest{ recommend.RecommendationRequestAsRecommendationsRequest(recommend.NewRecommendationRequest(recommend.RECOMMENDATIONMODELS_BOUGHT_TOGETHER, "test_query", "test_index", 0)), - }) + } */ // alternative way to create the payloads, a similar approach can be used with any of the other clients - params := &recommend.GetRecommendationsParams{ - Requests: []recommend.RecommendationsRequest{ - { - BoughtTogetherQuery: &recommend.BoughtTogetherQuery{ - Model: recommend.FBT_MODEL_BOUGHT_TOGETHER, - ObjectID: "test_query", - IndexName: "test_index", - Threshold: 0, - }, - }, + params := []recommend.RecommendationsRequest{{ + BoughtTogetherQuery: &recommend.BoughtTogetherQuery{ + Model: recommend.FBT_MODEL_BOUGHT_TOGETHER, + ObjectID: "test_query", + IndexName: "test_index", + Threshold: 0, }, - } + }} - searchResponse, err := recommendClient.GetRecommendations( - recommendClient.NewApiGetRecommendationsRequest(params), - ) + searchResponse, err := recommendClient.GetRecommendations(params) if err != nil { fmt.Printf("request error with SearchSingleIndex: %v\n", err) return 1 diff --git a/playground/go/search.go b/playground/go/search.go index 4c0ce70ee8..3428a009a3 100644 --- a/playground/go/search.go +++ b/playground/go/search.go @@ -23,6 +23,16 @@ func testSearch(appID, apiKey string) int { panic(err) } + // old way + //searchClient.Search(searchClient.NewApiSearchRequest(search.NewSearchMethodParams([]search.SearchQuery{ + // *search.SearchForHitsAsSearchQuery(search.NewSearchForHits("indexName", search.WithSearchForHitsQuery("foo"))), + //}))) + + // new way + //searchClient.Search([]search.SearchQuery{ + // search.NewSearchForHits("indexName").WithQuery("foo"), + //}, nil) + /* response, err := searchClient.AddOrUpdateObject( searchClient.NewApiAddOrUpdateObjectRequest( diff --git a/specs/search/paths/search/browse.yml b/specs/search/paths/search/browse.yml index ddafd05d37..8c185f3692 100644 --- a/specs/search/paths/search/browse.yml +++ b/specs/search/paths/search/browse.yml @@ -13,23 +13,22 @@ post: browsing _just_ returns matching records. This can be useful if you want to export your indices. - - The Analytics API doesn't collect data when using `browse`. - - Records are ranked by attributes and custom ranking. - - There's no ranking for: typo-tolerance, number of matched words, proximity, geo distance. + - The Analytics API doesn't collect data when using `browse`. + - Records are ranked by attributes and custom ranking. + - There's no ranking for: typo-tolerance, number of matched words, proximity, geo distance. Browse requests automatically apply these settings: - - - `advancedSyntax`: `false` - - `attributesToHighlight`: `[]` - - `attributesToSnippet`: `[]` - - `distinct`: `false` - - `enablePersonalization`: `false` - - `enableRules`: `false` - - `facets`: `[]` - - `getRankingInfo`: `false` - - `ignorePlurals`: `false` - - `optionalFilters`: `[]` - - `typoTolerance`: `true` or `false` (`min` and `strict` evaluate to `true`) + - `advancedSyntax`: `false` + - `attributesToHighlight`: `[]` + - `attributesToSnippet`: `[]` + - `distinct`: `false` + - `enablePersonalization`: `false` + - `enableRules`: `false` + - `facets`: `[]` + - `getRankingInfo`: `false` + - `ignorePlurals`: `false` + - `optionalFilters`: `[]` + - `typoTolerance`: `true` or `false` (`min` and `strict` evaluate to `true`) If you send these parameters with your browse requests, they'll be ignored. parameters: diff --git a/specs/search/paths/search/search.yml b/specs/search/paths/search/search.yml index 688232c494..a817870da4 100644 --- a/specs/search/paths/search/search.yml +++ b/specs/search/paths/search/search.yml @@ -13,8 +13,8 @@ post: This can be useful in these cases: - - Different indices for different purposes, such as, one index for products, another one for marketing content. - - Multiple searches to the same index—for example, with different filters. + - Different indices for different purposes, such as, one index for products, another one for marketing content. + - Multiple searches to the same index—for example, with different filters. Use the helper `searchForHits` or `searchForFacets` to get the results in a more convenient format, if you already know the return type you want. requestBody: diff --git a/templates/go/api.mustache b/templates/go/api.mustache index df16992deb..8f9b341a17 100644 --- a/templates/go/api.mustache +++ b/templates/go/api.mustache @@ -1,11 +1,11 @@ -// {{{generationBanner}}} +{{{appDescription}}} package {{packageName}} +// {{{generationBanner}}} + {{#operations}} import ( "context" - "encoding/json" - "fmt" "net/http" "net/url" "strings" @@ -356,113 +356,128 @@ func replaceAllObjectsToChunkBactchOptions(opts []ReplaceAllObjectsOption) []Chu {{/isSearchClient}} {{#operation}} - {{#hasParams}} -func (r *{{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request) UnmarshalJSON(b []byte) error { - req := map[string]json.RawMessage{} - err := json.Unmarshal(b, &req) - if err != nil { - return fmt.Errorf("cannot unmarshal request: %w", err) - } - {{#allParams}} - if v, ok := req["{{#isQueryParam}}{{baseName}}{{/isQueryParam}}{{^isQueryParam}}{{paramName}}{{/isQueryParam}}"]; ok { - err = json.Unmarshal(v, &r.{{paramName}}) - if err != nil { - err = json.Unmarshal(b, &r.{{paramName}}) - if err != nil { - return fmt.Errorf("cannot unmarshal {{paramName}}: %w", err) - } - } - } {{#isBodyParam}}{{#required}}else { - err = json.Unmarshal(b, &r.{{paramName}}) - if err != nil { - return fmt.Errorf("cannot unmarshal body parameter {{paramName}}: %w", err) - } - }{{/required}}{{/isBodyParam}} - {{/allParams}} - - return nil -} - -// {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request represents the request with all the parameters for the API call. -type {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request struct { -{{#allParams}} - {{paramName}} {{^required}}{{^isPathParam}}{{^isFreeFormObject}}{{^isArray}}{{^isMap}}{{^isEnumRef}}*{{/isEnumRef}}{{/isMap}}{{/isArray}}{{/isFreeFormObject}}{{/isPathParam}}{{/required}}{{#required}}{{#isModel}}*{{/isModel}}{{/required}}{{{dataType}}} -{{/allParams}} +{{#hasOptionalParams}} +// {{operationId}}Options represents the optional params for the API call. +type {{operationId}}Options struct { + {{#optionalParams}} + {{nameInPascalCase}} {{^isFreeFormObject}}{{^isMap}}*{{/isMap}}{{/isFreeFormObject}}{{{dataType}}} + {{/optionalParams}} } {{#isDeprecated}} -// Deprecated +// Deprecated: {{operationId}}Options is deprecated {{/isDeprecated}} -//New{{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request creates an instance of the {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request to be used for the API call. -func (c *APIClient) NewApi{{{nickname}}}Request({{#requiredParams}} {{paramName}} {{^isPathParam}}{{^isFreeFormObject}}{{^isArray}}{{^isMap}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isMap}}{{/isArray}}{{/isFreeFormObject}}{{/isPathParam}}{{{dataType}}} {{^-last}},{{/-last}}{{/requiredParams}}) {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request { - return {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request{ - {{#requiredParams}} - {{paramName}}: {{paramName}}, - {{/requiredParams}} - } +// New{{operationId}}Options creates an instance of the {{operationId}}Options used to add optional parameters to {{operationId}}WithOptions. +func New{{{operationId}}}Options() *{{operationId}}Options { + return &{{operationId}}Options{} } -{{#allParams}} -{{^required}} -// With{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}} adds the {{paramName}} to the {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request and returns the request for chaining. +{{#optionalParams}} +// With{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}} {{#description}}{{{.}}}{{/description}}{{^description}}adds the {{paramName}} to the Api{{operationId}}Request and returns the request for chaining.{{/description}} {{#isDeprecated}} -// Deprecated +// Deprecated: With{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}} is deprecated {{/isDeprecated}} -func (r {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request) With{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}}({{paramName}} {{^isFreeFormObject}}{{^isArray}}{{^isMap}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isMap}}{{/isArray}}{{/isFreeFormObject}}{{{dataType}}}) {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request { - r.{{paramName}} = {{#isPrimitiveType}}{{^isMap}}&{{/isMap}}{{/isPrimitiveType}}{{paramName}} - return r +func (o *{{operationId}}Options) With{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}}({{paramName}} {{#isModel}}*{{/isModel}}{{{dataType}}}) *{{operationId}}Options { + o.{{nameInPascalCase}} = {{^isModel}}{{^isMap}}&{{/isMap}}{{/isModel}}{{paramName}} + return o } +{{/optionalParams}} -{{/required}} -{{/allParams}} -{{/hasParams}} -/* -{{operationId}} calls the API and returns the raw response from it. - {{#notes}} +{{/hasOptionalParams}} - {{{unescapedNotes}}} - {{/notes}} +// {{operationId}} {{{notes}}} {{#vendorExtensions}} - {{#x-acl.0}} - - Required API Key ACLs:{{/x-acl.0}} - {{#x-acl}} - - {{.}} - {{/x-acl}} + {{#x-acl.0}} +// +// Required API Key ACLs:{{/x-acl.0}} + {{#x-acl}} +// - {{.}} + {{/x-acl}} {{/vendorExtensions}} - - Request can be constructed by NewApi{{operationId}}Request with parameters below. - {{#allParams}} - @param {{paramName}} {{dataType}}{{#description}} - {{{.}}}{{/description}} - {{/allParams}} - @param opts ...RequestOption - Optional parameters for the API call - @return *http.Response - The raw response from the API - @return []byte - The raw response body from the API - @return error - An error if the API call fails + {{#hasParams}} +// Parameters: + {{/hasParams}} + {{#requiredParams}} +// - {{paramName}} {{#description}}- {{{.}}}{{/description}} + {{/requiredParams}} + {{#optionalParams}} +// - {{paramName}} {{#description}}- {{{.}}}{{/description}} (in optionalParams) + {{/optionalParams}} +// - opts - Optional parameters for the API call (e.g. WithContext, WithHeaderParam...) {{#isDeprecated}} +// +// Deprecated: {{operationId}} is deprecated + {{/isDeprecated}} +func (c *APIClient) {{nickname}}({{#requiredParams}}{{paramName}} {{#required}}{{#isModel}}*{{/isModel}}{{/required}}{{^required}}{{^isMap}}*{{/isMap}}{{/required}}{{{dataType}}}, {{/requiredParams}}{{#hasOptionalParams}}optionalParams *{{operationId}}Options, {{/hasOptionalParams}}opts ...RequestOption) ({{#returnType}}{{^isArray}}{{^returnTypeIsPrimitive}}*{{/returnTypeIsPrimitive}}{{/isArray}}{{{.}}}, {{/returnType}}error) { + {{#returnType}} + var returnValue {{^isArray}}{{^returnTypeIsPrimitive}}*{{/returnTypeIsPrimitive}}{{/isArray}}{{{.}}} + {{/returnType}} + + res, resBody, err := c.{{nickname}}WithHTTPInfo({{#requiredParams}}{{paramName}}, {{/requiredParams}}{{#hasOptionalParams}}optionalParams, {{/hasOptionalParams}}opts...) + if err != nil { + return {{#returnType}}returnValue, {{/returnType}}err + } + if res == nil { + return {{#returnType}}returnValue, {{/returnType}}reportError("res is nil") + } + + if res.StatusCode >= 300 { + return {{#returnType}}returnValue, {{/returnType}}c.decodeError(res, resBody) + } + + {{#returnType}} + err = c.decode(&returnValue, resBody) + if err != nil { + return {{#returnType}}returnValue, {{/returnType}}reportError("cannot decode result: %w", err) + } + {{/returnType}} + return {{#returnType}}returnValue, {{/returnType}}nil +} - Deprecated +// {{operationId}}WithHTTPInfo calls the API and returns the raw response from it. +// {{{notes}}} + {{#vendorExtensions}} + {{#x-acl.0}} +// +// Required API Key ACLs:{{/x-acl.0}} + {{#x-acl}} +// - {{.}} + {{/x-acl}} + {{/vendorExtensions}} + {{#hasParams}} +// +// Parameters: + {{/hasParams}} + {{#requiredParams}} +// - {{paramName}} {{#description}}- {{{.}}}{{/description}} + {{/requiredParams}} + {{#optionalParams}} +// - {{paramName}} {{#description}}- {{{.}}}{{/description}} (in optionalParams) + {{/optionalParams}} +// - opts - Optional parameters for the API call (e.g. WithContext, WithHeaderParam...) + {{#isDeprecated}} +// +// Deprecated: {{operationId}} is deprecated {{/isDeprecated}} - */ -func (c *APIClient) {{nickname}}WithHTTPInfo({{#hasParams}}r {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request,{{/hasParams}} opts ...RequestOption) (*http.Response, []byte, error) { +func (c *APIClient) {{nickname}}WithHTTPInfo({{#requiredParams}}{{paramName}} {{#required}}{{#isModel}}*{{/isModel}}{{/required}}{{^required}}{{^isMap}}*{{/isMap}}{{/required}}{{{dataType}}}, {{/requiredParams}}{{#hasOptionalParams}}optionalParams *{{operationId}}Options, {{/hasOptionalParams}}opts ...RequestOption) (*http.Response, []byte, error) { {{#vendorExtensions}} requestPath := "{{{path}}}"{{#pathParams}} - requestPath = strings.ReplaceAll(requestPath, {{=<% %>=}}"{<%baseName%>}"<%={{ }}=%>, {{#x-is-custom-request}}utils.ParameterToString(r.{{paramName}}){{/x-is-custom-request}}{{^x-is-custom-request}}url.PathEscape(utils.ParameterToString(r.{{paramName}})){{/x-is-custom-request}}){{/pathParams}} + requestPath = strings.ReplaceAll(requestPath, {{=<% %>=}}"{<%baseName%>}"<%={{ }}=%>, {{#x-is-custom-request}}utils.ParameterToString({{paramName}}){{/x-is-custom-request}}{{^x-is-custom-request}}url.PathEscape(utils.ParameterToString({{paramName}})){{/x-is-custom-request}}){{/pathParams}} {{/vendorExtensions}} {{#allParams}} {{#required}} {{#isString}} - if r.{{paramName}} == "" { + if {{paramName}} == "" { return nil, nil, reportError("Parameter `{{paramName}}` is required when calling `{{operationId}}`.") }{{/isString}}{{#isContainer}} - if len(r.{{paramName}}) == 0 { + if len({{paramName}}) == 0 { return nil, nil, reportError("Parameter `{{paramName}}` is required when calling `{{operationId}}`.") }{{/isContainer}}{{#isMap}} - if len(r.{{paramName}}) == 0 { + if len({{paramName}}) == 0 { return nil, nil, reportError("Parameter `{{paramName}}` is required when calling `{{operationId}}`.") }{{/isMap}}{{^isPrimitiveType}}{{^isContainer}}{{^isMap}}{{^isEnumRef}} - if r.{{paramName}} == nil { + if {{paramName}} == nil { return nil, nil, reportError("Parameter `{{paramName}}` is required when calling `{{operationId}}`.") }{{/isEnumRef}}{{/isMap}}{{/isContainer}}{{/isPrimitiveType}} {{/required}} @@ -483,8 +498,8 @@ func (c *APIClient) {{nickname}}WithHTTPInfo({{#hasParams}}r {{#structPrefix}}{{ {{#vendorExtensions.x-is-custom-request}} {{#queryParams}} - {{^required}}if !utils.IsNilOrEmpty(r.{{paramName}}) { {{/required}} - for k, v := range r.{{paramName}} { + {{^required}}if {{#hasOptionalParams}}optionalParams != nil && {{/hasOptionalParams}}!utils.IsNilOrEmpty({{> param_name}}) { {{/required}} + for k, v := range {{> param_name}} { conf.queryParams.Set(k, utils.QueryParameterToString(v)) } {{^required}} } {{/required}} @@ -493,22 +508,22 @@ func (c *APIClient) {{nickname}}WithHTTPInfo({{#hasParams}}r {{#structPrefix}}{{ {{^vendorExtensions.x-is-custom-request}} {{#queryParams}} {{#required}} - conf.queryParams.Set("{{baseName}}", utils.QueryParameterToString({{^isFreeFormObject}}{{^isArray}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isArray}}{{/isFreeFormObject}}r.{{paramName}})) + conf.queryParams.Set("{{baseName}}", utils.QueryParameterToString({{^isFreeFormObject}}{{^isArray}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isArray}}{{/isFreeFormObject}}{{paramName}})) {{/required}} {{^required}} - if !utils.IsNilOrEmpty(r.{{paramName}}) { - conf.queryParams.Set("{{baseName}}", utils.QueryParameterToString({{^isFreeFormObject}}{{^isArray}}{{^isEnumRef}}*{{/isEnumRef}}{{/isArray}}{{/isFreeFormObject}}r.{{paramName}})) + if {{#hasOptionalParams}}optionalParams != nil && {{/hasOptionalParams}}!utils.IsNilOrEmpty({{> param_name}}) { + conf.queryParams.Set("{{baseName}}", utils.QueryParameterToString({{^isFreeFormObject}}*{{/isFreeFormObject}}{{> param_name}})) } {{/required}} {{/queryParams}} {{/vendorExtensions.x-is-custom-request}} {{#headerParams}} {{#required}} - conf.headerParams["{{baseName}}"] = utils.ParameterToString({{^isFreeFormObject}}{{^isArray}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isArray}}{{/isFreeFormObject}}r.{{paramName}}) + conf.headerParams["{{baseName}}"] = utils.ParameterToString({{^isFreeFormObject}}{{^isArray}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isArray}}{{/isFreeFormObject}}{{paramName}}) {{/required}} {{^required}} - if !utils.IsNilOrEmpty(r.{{paramName}}) { - conf.headerParams["{{baseName}}"] = utils.ParameterToString({{^isFreeFormObject}}{{^isArray}}{{^isEnumRef}}*{{/isEnumRef}}{{/isArray}}{{/isFreeFormObject}}r.{{paramName}}) + if {{#hasOptionalParams}}optionalParams != nil && {{/hasOptionalParams}}!utils.IsNilOrEmpty({{> param_name}}) { + conf.headerParams["{{baseName}}"] = utils.ParameterToString({{^isFreeFormObject}}*{{/isFreeFormObject}}{{> param_name}}) } {{/required}} {{/headerParams}} @@ -518,17 +533,55 @@ func (c *APIClient) {{nickname}}WithHTTPInfo({{#hasParams}}r {{#structPrefix}}{{ opt.apply(&conf) } - var postBody any - {{#bodyParams}} - // body params{{^required}} - if utils.IsNilOrEmpty(r.{{paramName}}) { - postBody = "{}" - } else { {{/required}} - postBody = r.{{paramName}}{{^required}} - } {{/required}} + // body params + {{#vendorExtensions.x-flat-body}} + postBody := struct{ + {{#vars}} + {{name}} {{^required}}{{^isFreeFormObject}}*{{/isFreeFormObject}}{{/required}}{{#required}}{{#isModel}}*{{/isModel}}{{/required}}{{{dataType}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"` + {{/vars}} + }{ + {{#vars}} + {{#required}} + {{name}}: {{nameInLowerCase}}, + {{/required}} + {{^hasOptionalParams}} + {{^required}} + {{name}}: {{nameInLowerCase}}, + {{/required}} + {{/hasOptionalParams}} + {{/vars}} + } + {{#hasOptionalParams}} + if optionalParams != nil { + {{#vars}} + {{^required}} + postBody.{{name}} = optionalParams.{{nameInPascalCase}} + {{/required}} + {{/vars}} + } + {{/hasOptionalParams}} + {{/vendorExtensions.x-flat-body}} + {{^vendorExtensions.x-flat-body}} + {{#required}} + postBody := {{paramName}} + {{/required}} + {{^required}} + var postBody any = "{}" + {{#hasOptionalParams}} + if optionalParams != nil { + postBody = optionalParams.{{nameInPascalCase}} + } + {{/hasOptionalParams}} + {{^hasOptionalParams}} + if !utils.IsNilOrEmpty({{paramName}}) { + postBody = {{paramName}} + } + {{/hasOptionalParams}} + {{/required}} + {{/vendorExtensions.x-flat-body}} {{/bodyParams}} - req, err := c.prepareRequest(conf.context, requestPath, http.Method{{httpMethod}}, postBody, conf.headerParams, conf.queryParams) + req, err := c.prepareRequest(conf.context, requestPath, http.Method{{httpMethod}}, {{^bodyParams}}nil{{/bodyParams}}{{#bodyParams}}postBody{{/bodyParams}}, conf.headerParams, conf.queryParams) if err != nil { return nil, nil, err } @@ -536,36 +589,6 @@ func (c *APIClient) {{nickname}}WithHTTPInfo({{#hasParams}}r {{#structPrefix}}{{ return c.callAPI(req, {{#vendorExtensions}}{{#x-use-read-transporter}}true{{/x-use-read-transporter}}{{^x-use-read-transporter}}false{{/x-use-read-transporter}},{{/vendorExtensions}}conf.timeouts) } -/* -{{operationId}} casts the HTTP response body to a defined struct. -{{> operation_description}} -func (c *APIClient) {{nickname}}({{#hasParams}}r {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request,{{/hasParams}} opts ...RequestOption) ({{#returnType}}{{^isArray}}{{^returnTypeIsPrimitive}}*{{/returnTypeIsPrimitive}}{{/isArray}}{{{.}}}, {{/returnType}}error) { - {{#returnType}} - var returnValue {{^isArray}}{{^returnTypeIsPrimitive}}*{{/returnTypeIsPrimitive}}{{/isArray}}{{{.}}} - {{/returnType}} - - res, resBody, err := c.{{nickname}}WithHTTPInfo({{#hasParams}}r, {{/hasParams}}opts...) - if err != nil { - return {{#returnType}}returnValue, {{/returnType}}err - } - if res == nil { - return {{#returnType}}returnValue, {{/returnType}}reportError("res is nil") - } - - if res.StatusCode >= 300 { - return {{#returnType}}returnValue, {{/returnType}}c.decodeError(res, resBody) - } - - {{#returnType}} - err = c.decode(&returnValue, resBody) - if err != nil { - return {{#returnType}}returnValue, {{/returnType}}reportError("cannot decode result: %w", err) - } - - {{/returnType}} - return {{#returnType}}returnValue, {{/returnType}}nil -} - {{/operation}} {{/operations}} diff --git a/templates/go/client.mustache b/templates/go/client.mustache index 6a3552164f..5e28c339db 100644 --- a/templates/go/client.mustache +++ b/templates/go/client.mustache @@ -1,6 +1,7 @@ -// {{{generationBanner}}} package {{packageName}} +// {{{generationBanner}}} + import ( "bytes" "compress/gzip" diff --git a/templates/go/configuration.mustache b/templates/go/configuration.mustache index 755512ba70..d7bbae8f79 100644 --- a/templates/go/configuration.mustache +++ b/templates/go/configuration.mustache @@ -1,6 +1,7 @@ -// {{{generationBanner}}} package {{packageName}} +// {{{generationBanner}}} + import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" ) diff --git a/templates/go/model.mustache b/templates/go/model.mustache index 0b3d8e9c14..890a350a7f 100644 --- a/templates/go/model.mustache +++ b/templates/go/model.mustache @@ -1,6 +1,7 @@ -// {{{generationBanner}}} package {{packageName}} +// {{{generationBanner}}} + {{#models}} import ( "encoding/json" diff --git a/templates/go/model_enum.mustache b/templates/go/model_enum.mustache index 967ce85228..cb91512b4d 100644 --- a/templates/go/model_enum.mustache +++ b/templates/go/model_enum.mustache @@ -7,7 +7,7 @@ const ( {{#enumVars}} {{^-first}} {{/-first}} - {{#enumClassPrefix}}{{#lambda.screamingSnakeCase}}{{{classname}}}{{/lambda.screamingSnakeCase}}_{{/enumClassPrefix}}{{name}} {{{classname}}} = {{{value}}} + {{#lambda.screamingSnakeCase}}{{{classname}}}{{/lambda.screamingSnakeCase}}_{{name}} {{{classname}}} = {{{value}}} {{/enumVars}} {{/allowableValues}} ) diff --git a/templates/go/operation_description.mustache b/templates/go/operation_description.mustache deleted file mode 100644 index 14976120b3..0000000000 --- a/templates/go/operation_description.mustache +++ /dev/null @@ -1,23 +0,0 @@ -{{#notes}} - -{{{unescapedNotes}}} -{{/notes}} -{{#vendorExtensions}} -{{#x-acl.0}} - -Required API Key ACLs:{{/x-acl.0}} -{{#x-acl}} - - {{.}} -{{/x-acl}} -{{/vendorExtensions}} - -Request can be constructed by NewApi{{operationId}}Request with parameters below. - {{#allParams}} - @param {{paramName}} {{dataType}}{{#description}} - {{{.}}}{{/description}} - {{/allParams}} - {{#returnType}}@return {{{.}}}{{/returnType}} -{{#isDeprecated}} - -Deprecated -{{/isDeprecated}} -*/ \ No newline at end of file diff --git a/templates/go/param_name.mustache b/templates/go/param_name.mustache new file mode 100644 index 0000000000..67c301f739 --- /dev/null +++ b/templates/go/param_name.mustache @@ -0,0 +1 @@ +{{#hasOptionalParams}}optionalParams.{{nameInPascalCase}}{{/hasOptionalParams}}{{^hasOptionalParams}}{{paramName}}{{/hasOptionalParams}} \ No newline at end of file diff --git a/templates/go/search_helpers.mustache b/templates/go/search_helpers.mustache index 03fc060b26..929bc825b1 100644 --- a/templates/go/search_helpers.mustache +++ b/templates/go/search_helpers.mustache @@ -41,17 +41,10 @@ func CreateIterable[T any](execute func(*T, error) (*T, error), validate func(*T return executor(nil, nil) } -/* -SearchForHits calls the `search` method but with certainty that we will only request Algolia records (hits) and not facets. -Disclaimer: We don't assert that the parameters you pass to this method only contains `hits` requests to prevent impacting search performances, this helper is purely for typing purposes. - - @param r ApiSearchRequest - Body of the `search` operation. - @param opts ...RequestOption - Optional parameters for the request. - @return []SearchResponse - List of hits. - @return error - Error if any. - */ -func (c *APIClient) SearchForHits(r ApiSearchRequest, opts ...RequestOption) ([]SearchResponse, error) { - res, err := c.Search(r, opts...) +// SearchForHits calls the `search` method but with certainty that we will only request Algolia records (hits) and not facets. +// Disclaimer: We don't assert that the parameters you pass to this method only contains `hits` requests to prevent impacting search performances, this helper is purely for typing purposes. +func (c *APIClient) SearchForHits(requests []SearchQuery, strategy *SearchStrategy, opts ...RequestOption) ([]SearchResponse, error) { + res, err := c.Search(requests, strategy, opts...) if err != nil { return nil, err } @@ -67,17 +60,10 @@ func (c *APIClient) SearchForHits(r ApiSearchRequest, opts ...RequestOption) ([] return slices.Clip(hits), nil } -/* -SearchForFacets calls the `search` method but with certainty that we will only request Algolia facets and not records (hits). -Disclaimer: We don't assert that the parameters you pass to this method only contains `facets` requests to prevent impacting search performances, this helper is purely for typing purposes. - - @param r ApiSearchRequest - Body of the `search` operation. - @param opts ...RequestOption - Optional parameters for the request. - @return []SearchForFacetValuesResponse - List of facet hits. - @return error - Error if any. - */ -func (c *APIClient) SearchForFacets(r ApiSearchRequest, opts ...RequestOption) ([]SearchForFacetValuesResponse, error) { - res, err := c.Search(r, opts...) +// SearchForFacets calls the `search` method but with certainty that we will only request Algolia facets and not records (hits). +// Disclaimer: We don't assert that the parameters you pass to this method only contains `facets` requests to prevent impacting search performances, this helper is purely for typing purposes. +func (c *APIClient) SearchForFacets(requests []SearchQuery, strategy *SearchStrategy, opts ...RequestOption) ([]SearchForFacetValuesResponse, error) { + res, err := c.Search(requests, strategy, opts...) if err != nil { return nil, err } @@ -93,17 +79,9 @@ func (c *APIClient) SearchForFacets(r ApiSearchRequest, opts ...RequestOption) ( return slices.Clip(facetHits), nil } -/* -WaitForTask 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. - - @param indexName string - Index name. - @param taskID int64 - Task ID. - @param opts ...IterableOption - Optional parameters for the request. - @return *GetTaskResponse - Task response. - @return error - Error if any. -*/ +// WaitForTask 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. func (c *APIClient) WaitForTask( indexName string, taskID int64, @@ -116,7 +94,7 @@ func (c *APIClient) WaitForTask( return CreateIterable( //nolint:wrapcheck func(*GetTaskResponse, error) (*GetTaskResponse, error) { - return c.GetTask(c.NewApiGetTaskRequest(indexName, taskID), toRequestOptions(opts)...) + return c.GetTask(indexName, taskID, toRequestOptions(opts)...) }, func(response *GetTaskResponse, err error) (bool, error) { if err != nil || response == nil { @@ -129,16 +107,9 @@ func (c *APIClient) WaitForTask( ) } -/* -WaitForAppTask waits for an application-level task to be published. -It returns the task response if the operation was successful. -It returns an error if the operation failed. - - @param taskID int64 - Task ID. - @param opts ...IterableOption - Optional parameters for the request. - @return *GetTaskResponse - Task response. - @return error - Error if any. -*/ +// WaitForAppTask waits for an application-level task to be published. +// It returns the task response if the operation was successful. +// It returns an error if the operation failed. func (c *APIClient) WaitForAppTask( taskID int64, opts ...IterableOption, @@ -150,7 +121,7 @@ func (c *APIClient) WaitForAppTask( return CreateIterable( //nolint:wrapcheck func(*GetTaskResponse, error) (*GetTaskResponse, error) { - return c.GetAppTask(c.NewApiGetAppTaskRequest(taskID), toRequestOptions(opts)...) + return c.GetAppTask(taskID, toRequestOptions(opts)...) }, func(response *GetTaskResponse, err error) (bool, error) { if err != nil || response == nil { @@ -181,25 +152,17 @@ func slicesEqualUnordered[T cmp.Ordered](a []T, b []T) bool { return slices.Equal(aCopy, bCopy) } -/* -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 key string - API key. - @param operation ApiKeyOperation - Operation type - add, delete or update. - @param opts ...WaitForApiKeyOption - Optional parameters for the request, you must provide WithApiKey if the operation is "update". - @return *GetApiKeyResponse - API key response. - @return error - Error if any. -*/ +// 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. func (c *APIClient) WaitForApiKey( key string, operation ApiKeyOperation, @@ -284,23 +247,15 @@ func (c *APIClient) WaitForApiKey( return CreateIterable( //nolint:wrapcheck func(*GetApiKeyResponse, error) (*GetApiKeyResponse, error) { - return c.GetApiKey(c.NewApiGetApiKeyRequest(key), toRequestOptions(opts)...) + return c.GetApiKey(key, toRequestOptions(opts)...) }, validateFunc, waitForApiKeyToIterableOptions(opts)..., ) } -/* -BrowseObjects allows to aggregate all the hits returned by the API calls. -Use the `WithAggregator` option to collect all the responses. - - @param indexName string - Index name. - @param browseParams BrowseParamsObject - Browse parameters. - @param opts ...IterableOption - Optional parameters for the request. - @return *BrowseResponse - Browse response. - @return error - Error if any. -*/ +// BrowseObjects allows to aggregate all the hits returned by the API calls. +// Use the `WithAggregator` option to collect all the responses. func (c *APIClient) BrowseObjects( indexName string, browseParams BrowseParamsObject, @@ -317,7 +272,7 @@ func (c *APIClient) BrowseObjects( } return c.Browse( - c.NewApiBrowseRequest(indexName).WithBrowseParams(BrowseParamsObjectAsBrowseParams(&browseParams)), + indexName, BrowseParamsObjectAsBrowseParams(&browseParams), toRequestOptions(opts)..., ) }, @@ -330,42 +285,34 @@ func (c *APIClient) BrowseObjects( return err } -/* -BrowseRules allows to aggregate all the rules returned by the API calls. -Use the `WithAggregator` option to collect all the responses. - - @param indexName string - Index name. - @param searchRulesParams SearchRulesParams - Search rules parameters. - @param opts ...IterableOption - Optional parameters for the request. - @return *SearchRulesResponse - Search rules response. - @return error - Error if any. -*/ +// BrowseRules allows to aggregate all the rules returned by the API calls. +// Use the `WithAggregator` option to collect all the responses. func (c *APIClient) BrowseRules( indexName string, - searchRulesParams SearchRulesParams, + optionalParams *SearchRulesOptions, opts ...IterableOption, ) error { hitsPerPage := int32(1000) - if searchRulesParams.HitsPerPage != nil { - hitsPerPage = *searchRulesParams.HitsPerPage + if optionalParams == nil { + optionalParams = NewSearchRulesOptions() + } + if optionalParams.HitsPerPage != nil { + hitsPerPage = *optionalParams.HitsPerPage } _, err := CreateIterable( //nolint:wrapcheck func(previousResponse *SearchRulesResponse, previousErr error) (*SearchRulesResponse, error) { - searchRulesParams.HitsPerPage = &hitsPerPage + optionalParams.HitsPerPage = &hitsPerPage if previousResponse != nil { - searchRulesParams.Page = utils.ToPtr(previousResponse.Page + 1) + optionalParams.Page = utils.ToPtr(previousResponse.Page + 1) } - if searchRulesParams.Page == nil { - searchRulesParams.Page = utils.ToPtr(int32(0)) + if optionalParams.Page == nil { + optionalParams.Page = utils.ToPtr(int32(0)) } - return c.SearchRules( - c.NewApiSearchRulesRequest(indexName).WithSearchRulesParams(&searchRulesParams), - toRequestOptions(opts)..., - ) + return c.SearchRules(indexName, optionalParams, toRequestOptions(opts)...) }, func(response *SearchRulesResponse, err error) (bool, error) { return err != nil || (response != nil && len(response.Hits) < int(hitsPerPage)), err @@ -376,42 +323,34 @@ func (c *APIClient) BrowseRules( return err } -/* -BrowseSynonyms allows to aggregate all the synonyms returned by the API calls. -Use the `WithAggregator` option to collect all the responses. - - @param indexName string - Index name. - @param searchSynonymsParams SearchSynonymsParams - Search synonyms parameters. - @param opts ...IterableOption - Optional parameters for the request. - @return *SearchSynonymsResponse - Search synonyms response. - @return error - Error if any. -*/ +// BrowseSynonyms allows to aggregate all the synonyms returned by the API calls. +// Use the `WithAggregator` option to collect all the responses. func (c *APIClient) BrowseSynonyms( indexName string, - searchSynonymsParams SearchSynonymsParams, + optionalParams *SearchSynonymsOptions, opts ...IterableOption, ) error { hitsPerPage := int32(1000) - if searchSynonymsParams.HitsPerPage != nil { - hitsPerPage = *searchSynonymsParams.HitsPerPage + if optionalParams == nil { + optionalParams = NewSearchSynonymsOptions() + } + if optionalParams.HitsPerPage != nil { + hitsPerPage = *optionalParams.HitsPerPage } - if searchSynonymsParams.Page == nil { - searchSynonymsParams.Page = utils.ToPtr(int32(0)) + if optionalParams.Page == nil { + optionalParams.Page = utils.ToPtr(int32(0)) } _, err := CreateIterable( //nolint:wrapcheck func(previousResponse *SearchSynonymsResponse, previousErr error) (*SearchSynonymsResponse, error) { - searchSynonymsParams.HitsPerPage = &hitsPerPage + optionalParams.HitsPerPage = &hitsPerPage defer func() { - searchSynonymsParams.Page = utils.ToPtr(*searchSynonymsParams.Page + 1) + optionalParams.Page = utils.ToPtr(*optionalParams.Page + 1) }() - return c.SearchSynonyms( - c.NewApiSearchSynonymsRequest(indexName).WithSearchSynonymsParams(&searchSynonymsParams), - toRequestOptions(opts)..., - ) + return c.SearchSynonyms(indexName, optionalParams, toRequestOptions(opts)...) }, func(response *SearchSynonymsResponse, err error) (bool, error) { return err != nil || (response != nil && len(response.Hits) < int(hitsPerPage)), err @@ -470,15 +409,7 @@ func encodeRestrictions(restrictions *SecuredApiKeyRestrictions) (string, error) return strings.Join(queryString, "&"), nil } -/* -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. - - @param parentApiKey string - The parent API key. - @param restrictions *SecuredApiKeyRestrictions - The restrictions to apply to the new key. - @return string - The new secured API key. - @return error - Error if any. -*/ +// 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) { h := hmac.New(sha256.New, []byte(parentApiKey)) @@ -497,13 +428,7 @@ func (c *APIClient) GenerateSecuredApiKey(parentApiKey string, restrictions *Sec return key, nil } -/* -GetSecuredApiKeyRemainingValidity retrieves the remaining validity of the previously generated `securedApiKey`, the `ValidUntil` parameter must have been provided. - - @param securedApiKey string - The secured API key. - @return time.Duration - The remaining validity of the secured API key. - @return error - Error if any. -*/ +// GetSecuredApiKeyRemainingValidity retrieves the remaining validity of the previously generated `securedApiKey`, the `ValidUntil` parameter must have been provided. func (c *APIClient) GetSecuredApiKeyRemainingValidity(securedApiKey string) (time.Duration, error) { if len(securedApiKey) == 0 { return 0, fmt.Errorf("given secured API key is empty: %s", securedApiKey) @@ -528,30 +453,12 @@ func (c *APIClient) GetSecuredApiKeyRemainingValidity(securedApiKey string) (tim return time.Until(time.Unix(int64(ts), 0)), nil } - - -/* -Helper: Saves the given array of objects in the given index. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objects in it. - - @param indexName string - the index name to save objects into. - @param objects []map[string]any - List of objects to save. - @param opts ...ChunkedBatchOption - Optional parameters for the request. - @return []BatchResponse - List of batch responses. - @return error - Error if any. -*/ +// Helper: Saves the given array of objects in the given index. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objects in it. func (c *APIClient) SaveObjects(indexName string, objects []map[string]any, opts ...ChunkedBatchOption) ([]BatchResponse, error) { return c.ChunkedBatch(indexName, objects, ACTION_ADD_OBJECT, opts...) } -/* -Helper: Deletes every records for the given objectIDs. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objectIDs in it. - - @param indexName string - the index name to delete objects from. - @param objectIDs []string - List of objectIDs to delete. - @param opts ...ChunkedBatchOption - Optional parameters for the request. - @return []BatchResponse - List of batch responses. - @return error - Error if any. -*/ +// Helper: Deletes every records for the given objectIDs. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objectIDs in it. func (c *APIClient) DeleteObjects(indexName string, objectIDs []string, opts ...ChunkedBatchOption) ([]BatchResponse, error) { objects := make([]map[string]any, 0, len(objectIDs)) @@ -562,15 +469,7 @@ func (c *APIClient) DeleteObjects(indexName string, objectIDs []string, opts ... return c.ChunkedBatch(indexName, objects, ACTION_DELETE_OBJECT, opts...) } -/* -Helper: Replaces object content of all the given objects according to their respective `objectID` field. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objects in it. - - @param indexName string - the index name to save objects into. - @param objects []map[string]any - List of objects to save. - @param opts ...ChunkedBatchOption - Optional parameters for the request. - @return []BatchResponse - List of batch responses. - @return error - Error if any. -*/ +// Helper: Replaces object content of all the given objects according to their respective `objectID` field. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objects in it. func (c *APIClient) PartialUpdateObjects(indexName string, objects []map[string]any, opts ...PartialUpdateObjectsOption) ([]BatchResponse, error) { conf := config{ headerParams: map[string]string{}, @@ -592,16 +491,7 @@ func (c *APIClient) PartialUpdateObjects(indexName string, objects []map[string] return c.ChunkedBatch(indexName, objects, action, partialUpdateObjectsToChunkedBatchOptions(opts)...) } -/* -ChunkedBatch chunks the given `objects` list in subset of 1000 elements max in order to make it fit in `batch` requests. - - @param indexName string - the index name to save objects into. - @param objects []map[string]any - List of objects to save. - @param action Action - The action to perform on the objects. - @param opts ...ChunkedBatchOption - Optional parameters for the request. - @return []BatchResponse - List of batch responses. - @return error - Error if any. -*/ +// ChunkedBatch chunks the given `objects` list in subset of 1000 elements max in order to make it fit in `batch` requests. func (c *APIClient) ChunkedBatch(indexName string, objects []map[string]any, action Action, opts ...ChunkedBatchOption) ([]BatchResponse, error) { conf := config{ headerParams: map[string]string{}, @@ -620,7 +510,7 @@ func (c *APIClient) ChunkedBatch(indexName string, objects []map[string]any, act requests = append(requests, *NewBatchRequest(action, obj)) if len(requests) == conf.batchSize || i == len(objects)-1 { - resp, err := c.Batch(c.NewApiBatchRequest(indexName, NewBatchWriteParams(requests)), toRequestOptions(opts)...) + resp, err := c.Batch(indexName, requests, toRequestOptions(opts)...) if err != nil { return nil, err } @@ -642,16 +532,8 @@ func (c *APIClient) ChunkedBatch(indexName string, objects []map[string]any, act return responses, nil } -/* -ReplaceAllObjects replaces all objects (records) in the given `indexName` with the given `objects`. A temporary index is created during this process in order to backup your data. -See https://api-clients-automation.netlify.app/docs/add-new-api-client#5-helpers for implementation details. - - @param indexName string - the index name to replace objects into. - @param objects []map[string]any - List of objects to replace. - @param opts ...ReplaceAllObjectsOption - Optional parameters for the request. - @return *ReplaceAllObjectsResponse - The response of the replace all objects operation. - @return error - Error if any. -*/ +// ReplaceAllObjects replaces all objects (records) in the given `indexName` with the given `objects`. A temporary index is created during this process in order to backup your data. +// See https://api-clients-automation.netlify.app/docs/add-new-api-client#5-helpers for implementation details. func (c *APIClient) ReplaceAllObjects(indexName string, objects []map[string]any, opts ...ReplaceAllObjectsOption) (*ReplaceAllObjectsResponse, error) { tmpIndexName := fmt.Sprintf("%s_tmp_%d", indexName, time.Now().UnixNano()) @@ -666,49 +548,49 @@ func (c *APIClient) ReplaceAllObjects(indexName string, objects []map[string]any opts = append(opts, WithWaitForTasks(true)) - copyResp, err := c.OperationIndex(c.NewApiOperationIndexRequest(indexName, NewOperationIndexParams(OPERATION_TYPE_COPY, tmpIndexName, WithOperationIndexParamsScope(conf.scopes))), toRequestOptions(opts)...) + copyResp, err := c.OperationIndex(indexName, OPERATION_TYPE_COPY, tmpIndexName, &conf.scopes, toRequestOptions(opts)...) if err != nil { return nil, err } batchResp, err := c.ChunkedBatch(tmpIndexName, objects, ACTION_ADD_OBJECT, replaceAllObjectsToChunkBactchOptions(opts)...) if err != nil { - _, _ = c.DeleteIndex(c.NewApiDeleteIndexRequest(tmpIndexName)) + _, _ = c.DeleteIndex(tmpIndexName) return nil, err } _, err = c.WaitForTask(tmpIndexName, copyResp.TaskID, replaceAllObjectsToIterableOptions(opts)...) if err != nil { - _, _ = c.DeleteIndex(c.NewApiDeleteIndexRequest(tmpIndexName)) + _, _ = c.DeleteIndex(tmpIndexName) return nil, err } - copyResp, err = c.OperationIndex(c.NewApiOperationIndexRequest(indexName, NewOperationIndexParams(OPERATION_TYPE_COPY, tmpIndexName, WithOperationIndexParamsScope(conf.scopes))), toRequestOptions(opts)...) + copyResp, err = c.OperationIndex(indexName, OPERATION_TYPE_COPY, tmpIndexName, &conf.scopes, toRequestOptions(opts)...) if err != nil { - _, _ = c.DeleteIndex(c.NewApiDeleteIndexRequest(tmpIndexName)) + _, _ = c.DeleteIndex(tmpIndexName) return nil, err } _, err = c.WaitForTask(tmpIndexName, copyResp.TaskID, replaceAllObjectsToIterableOptions(opts)...) if err != nil { - _, _ = c.DeleteIndex(c.NewApiDeleteIndexRequest(tmpIndexName)) + _, _ = c.DeleteIndex(tmpIndexName) return nil, err } - moveResp, err := c.OperationIndex(c.NewApiOperationIndexRequest(tmpIndexName, NewOperationIndexParams(OPERATION_TYPE_MOVE, indexName)), toRequestOptions(opts)...) + moveResp, err := c.OperationIndex(tmpIndexName, OPERATION_TYPE_MOVE, indexName, nil, toRequestOptions(opts)...) if err != nil { - _, _ = c.DeleteIndex(c.NewApiDeleteIndexRequest(tmpIndexName)) + _, _ = c.DeleteIndex(tmpIndexName) return nil, err } _, err = c.WaitForTask(tmpIndexName, moveResp.TaskID, replaceAllObjectsToIterableOptions(opts)...) if err != nil { - _, _ = c.DeleteIndex(c.NewApiDeleteIndexRequest(tmpIndexName)) + _, _ = c.DeleteIndex(tmpIndexName) return nil, err } @@ -724,7 +606,7 @@ func (c *APIClient) ReplaceAllObjects(indexName string, objects []map[string]any // error. When encountering a network error, a non-nil error is returned along // with false. func (c *APIClient) IndexExists(indexName string) (bool, error) { - _, err := c.GetSettings(c.NewApiGetSettingsRequest(indexName)) + _, err := c.GetSettings(indexName) if err == nil { return true, nil } diff --git a/templates/go/snippets/method.mustache b/templates/go/snippets/method.mustache index 8f2b5c4204..d67855fb08 100644 --- a/templates/go/snippets/method.mustache +++ b/templates/go/snippets/method.mustache @@ -1,6 +1,7 @@ -// {{generationBanner}} package snippets +// {{generationBanner}} + // >IMPORT {{> snippets/import}} // IMPORT< diff --git a/templates/go/tests/client/benchmark.mustache b/templates/go/tests/client/benchmark.mustache index 9f4b51370c..8bb300a753 100644 --- a/templates/go/tests/client/benchmark.mustache +++ b/templates/go/tests/client/benchmark.mustache @@ -1,6 +1,7 @@ -// {{generationBanner}} package benchmark +// {{generationBanner}} + import ( "testing" "regexp" diff --git a/templates/go/tests/client/client.mustache b/templates/go/tests/client/client.mustache index cd5ced54cc..b360440f98 100644 --- a/templates/go/tests/client/client.mustache +++ b/templates/go/tests/client/client.mustache @@ -1,6 +1,7 @@ -// {{generationBanner}} package client +// {{generationBanner}} + import ( "encoding/json" "net/url" diff --git a/templates/go/tests/e2e/e2e.mustache b/templates/go/tests/e2e/e2e.mustache index 2382998148..700f1d8337 100644 --- a/templates/go/tests/e2e/e2e.mustache +++ b/templates/go/tests/e2e/e2e.mustache @@ -1,6 +1,7 @@ -// {{generationBanner}} package e2e +// {{generationBanner}} + import ( "os" "testing" diff --git a/templates/go/tests/generateInnerParams.mustache b/templates/go/tests/generateInnerParams.mustache index 7a4ff09f86..47bd315c52 100644 --- a/templates/go/tests/generateInnerParams.mustache +++ b/templates/go/tests/generateInnerParams.mustache @@ -1,3 +1,3 @@ -{{#isVerbatim}}{{{value}}}{{/isVerbatim}}{{#isHelper}}{{#goFunctionalParam}}{{clientPrefix}}.With{{#lambda.pascalcase}}{{key}}{{/lambda.pascalcase}}({{/goFunctionalParam}}{{/isHelper}}{{#isNull}}{{#inClientTest}}tests.ZeroValue[{{#isNullObject}}*{{clientPrefix}}.{{/isNullObject}}{{objectName}}](){{/inClientTest}}{{^inClientTest}}nil{{/inClientTest}}{{/isNull}}{{#isString}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}"{{/isString}}{{#isInteger}}{{{value}}}{{/isInteger}}{{#isLong}}{{{value}}}{{/isLong}}{{#isDouble}}{{{value}}}{{/isDouble}}{{#isBoolean}}{{{value}}}{{/isBoolean}}{{#isEnum}}{{clientPrefix}}.{{objectName}}("{{{value}}}"){{/isEnum}}{{#isArray}} +{{#isVerbatim}}{{{value}}}{{/isVerbatim}}{{#isHelper}}{{#goFunctionalParam}}{{clientPrefix}}.With{{#lambda.pascalcase}}{{key}}{{/lambda.pascalcase}}({{/goFunctionalParam}}{{/isHelper}}{{#isNull}}{{#inClientTest}}tests.ZeroValue[{{#isNullObject}}*{{clientPrefix}}.{{/isNullObject}}{{objectName}}](){{/inClientTest}}{{^inClientTest}}nil{{/inClientTest}}{{/isNull}}{{#isString}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}"{{/isString}}{{#isInteger}}{{{value}}}{{/isInteger}}{{#isLong}}{{{value}}}{{/isLong}}{{#isDouble}}{{{value}}}{{/isDouble}}{{#isBoolean}}{{{value}}}{{/isBoolean}}{{#isEnum}}{{clientPrefix}}.{{#lambda.screamingSnakeCase}}{{{objectName}}}{{/lambda.screamingSnakeCase}}_{{#lambda.toEnum}}{{value}}{{/lambda.toEnum}}{{/isEnum}}{{#isArray}} {{> tests/arrayType}}{{^value.isEmpty}}{ {{#value}}{{#isObject}}*{{/isObject}}{{#oneOfModel}}{{^isObject}}*{{/isObject}}{{/oneOfModel}}{{> tests/generateParams}},{{/value}} }{{/value.isEmpty}}{{/isArray}}{{#isObject}} {{clientPrefix}}.NewEmpty{{objectName}}(){{#value}}{{#isAdditionalProperty}}.SetAdditionalProperty("{{{key}}}", {{> tests/generateParams}}){{/isAdditionalProperty}}{{^isAdditionalProperty}}.Set{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{/isAdditionalProperty}}{{/value}}{{/isObject}}{{#isFreeFormObject}}{{#isAnyType}}map[string]any{ {{#value}}{{#entrySet}}"{{{key}}}": "{{{value}}}",{{/entrySet}}{{/value}} }{{/isAnyType}}{{^isAnyType}}{{> tests/mapType}}{ {{#value}}"{{{key}}}": {{#oneOfModel}}{{^isObject}}*{{/isObject}}{{/oneOfModel}}{{#isObject}}*{{/isObject}}{{> tests/generateParams}},{{/value}} }{{/isAnyType}}{{/isFreeFormObject}}{{#isHelper}}{{#goFunctionalParam}}){{/goFunctionalParam}}{{/isHelper}} \ No newline at end of file diff --git a/templates/go/tests/inlineOptional.mustache b/templates/go/tests/inlineOptional.mustache new file mode 100644 index 0000000000..97ac147fe1 --- /dev/null +++ b/templates/go/tests/inlineOptional.mustache @@ -0,0 +1 @@ +{{#hasInlineOptional}}{{#parametersWithDataType}}{{^required}}{{^isFreeFormObject}}{{^isObject}}{{^isVerbatim}}utils.ToPtr({{/isVerbatim}}{{/isObject}}{{/isFreeFormObject}}{{> tests/generateParams}}{{^isFreeFormObject}}{{^isObject}}{{^isVerbatim}}){{/isVerbatim}}{{/isObject}}{{/isFreeFormObject}},{{/required}}{{/parametersWithDataType}}{{/hasInlineOptional}} \ No newline at end of file diff --git a/templates/go/tests/method.mustache b/templates/go/tests/method.mustache index d2819805ae..8a4e9bda92 100644 --- a/templates/go/tests/method.mustache +++ b/templates/go/tests/method.mustache @@ -1,2 +1 @@ -client.{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}({{#hasParams}}{{^isHelper}}client.NewApi{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}Request({{/isHelper}} - {{#parametersWithDataType}}{{#required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}} {{^isHelper}}){{#parametersWithDataType}}{{^required}}.With{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{#-last}},{{/-last}}{{/required}}{{/parametersWithDataType}}{{/isHelper}}{{#isHelper}}{{#parametersWithDataType}}{{^required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}}{{/isHelper}}{{/hasParams}}{{#requestOptions}}{{#queryParameters.parametersWithDataType}}{{clientPrefix}}.WithQueryParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/queryParameters.parametersWithDataType}}{{#headers.parametersWithDataType}}{{clientPrefix}}.WithHeaderParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/headers.parametersWithDataType}} {{#timeouts.read}} ,{{clientPrefix}}.WithReadTimeout({{.}} * time.Millisecond), {{/timeouts.read}} {{#timeouts.write}} ,{{clientPrefix}}.WithWriteTimeout({{.}} * time.Millisecond), {{/timeouts.write}} {{#timeouts.connect}} ,{{clientPrefix}}.WithConnectTimeout({{.}} * time.Millisecond), {{/timeouts.connect}} {{/requestOptions}}) \ No newline at end of file +client.{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}({{> tests/requiredParams}}{{> tests/optionalWrapper}}{{> tests/nilWrapper}}{{> tests/inlineOptional}}{{> tests/requestOptions}}) \ No newline at end of file diff --git a/templates/go/tests/nilWrapper.mustache b/templates/go/tests/nilWrapper.mustache new file mode 100644 index 0000000000..d570014b6e --- /dev/null +++ b/templates/go/tests/nilWrapper.mustache @@ -0,0 +1 @@ +{{#hasNilOptional}}nil,{{/hasNilOptional}} \ No newline at end of file diff --git a/templates/go/tests/optionalWrapper.mustache b/templates/go/tests/optionalWrapper.mustache new file mode 100644 index 0000000000..ee1ba5c631 --- /dev/null +++ b/templates/go/tests/optionalWrapper.mustache @@ -0,0 +1 @@ +{{#hasOptionalWrapper}}{{^isHelper}}{{clientPrefix}}.New{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}Options(){{#parametersWithDataType}}{{^required}}.With{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{/required}}{{/parametersWithDataType}}{{/isHelper}}{{#isHelper}}{{#parametersWithDataType}}{{^required}}{{> tests/generateParams}}{{^-last}},{{/-last}} {{/required}}{{/parametersWithDataType}}{{/isHelper}}{{#requestOptions}},{{/requestOptions}}{{/hasOptionalWrapper}} \ No newline at end of file diff --git a/templates/go/tests/requestOptions.mustache b/templates/go/tests/requestOptions.mustache new file mode 100644 index 0000000000..ebba80c50f --- /dev/null +++ b/templates/go/tests/requestOptions.mustache @@ -0,0 +1 @@ +{{#requestOptions}}{{#queryParameters.parametersWithDataType}}{{clientPrefix}}.WithQueryParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/queryParameters.parametersWithDataType}}{{#headers.parametersWithDataType}}{{clientPrefix}}.WithHeaderParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/headers.parametersWithDataType}} {{#timeouts.read}} ,{{clientPrefix}}.WithReadTimeout({{.}} * time.Millisecond), {{/timeouts.read}} {{#timeouts.write}} {{clientPrefix}}.WithWriteTimeout({{.}} * time.Millisecond), {{/timeouts.write}} {{#timeouts.connect}} ,{{clientPrefix}}.WithConnectTimeout({{.}} * time.Millisecond), {{/timeouts.connect}} {{/requestOptions}} \ No newline at end of file diff --git a/templates/go/tests/requiredParams.mustache b/templates/go/tests/requiredParams.mustache new file mode 100644 index 0000000000..b93d591046 --- /dev/null +++ b/templates/go/tests/requiredParams.mustache @@ -0,0 +1 @@ +{{#parametersWithDataType}}{{#required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}} \ No newline at end of file diff --git a/templates/java/tests/generateInnerParams.mustache b/templates/java/tests/generateInnerParams.mustache index 81796c232b..a6deb95d19 100644 --- a/templates/java/tests/generateInnerParams.mustache +++ b/templates/java/tests/generateInnerParams.mustache @@ -20,7 +20,7 @@ {{{value}}} {{/isBoolean}} {{#isEnum}} - {{{objectName}}}.{{#lambda.javaEnum}}{{{value}}}{{/lambda.javaEnum}} + {{{objectName}}}.{{#lambda.toEnum}}{{{value}}}{{/lambda.toEnum}} {{/isEnum}} {{#isArray}} Arrays.asList({{#value}}{{> tests/generateParams}}{{^-last}},{{/-last}}{{/value}}) diff --git a/tests/CTS/requests/abtesting/addABTests.json b/tests/CTS/requests/abtesting/addABTests.json index 2c25924d4b..3a68a9ed12 100644 --- a/tests/CTS/requests/abtesting/addABTests.json +++ b/tests/CTS/requests/abtesting/addABTests.json @@ -2,7 +2,6 @@ { "testName": "addABTests with minimal parameters", "parameters": { - "endAt": "2022-12-31T00:00:00.000Z", "name": "myABTest", "variants": [ { @@ -13,13 +12,13 @@ "index": "AB_TEST_2", "trafficPercentage": 50 } - ] + ], + "endAt": "2022-12-31T00:00:00.000Z" }, "request": { "path": "/2/abtests", "method": "POST", "body": { - "endAt": "2022-12-31T00:00:00.000Z", "name": "myABTest", "variants": [ { @@ -30,7 +29,8 @@ "index": "AB_TEST_2", "trafficPercentage": 50 } - ] + ], + "endAt": "2022-12-31T00:00:00.000Z" } } } diff --git a/tests/CTS/requests/abtesting/scheduleABTest.json b/tests/CTS/requests/abtesting/scheduleABTest.json index 87a1c0bc21..f51988b34b 100644 --- a/tests/CTS/requests/abtesting/scheduleABTest.json +++ b/tests/CTS/requests/abtesting/scheduleABTest.json @@ -2,8 +2,6 @@ { "testName": "scheduleABTest with minimal parameters", "parameters": { - "endAt": "2022-12-31T00:00:00.000Z", - "scheduledAt": "2022-11-31T00:00:00.000Z", "name": "myABTest", "variants": [ { @@ -14,14 +12,14 @@ "index": "AB_TEST_2", "trafficPercentage": 50 } - ] + ], + "scheduledAt": "2022-11-31T00:00:00.000Z", + "endAt": "2022-12-31T00:00:00.000Z" }, "request": { "path": "/2/abtests/schedule", "method": "POST", "body": { - "endAt": "2022-12-31T00:00:00.000Z", - "scheduledAt": "2022-11-31T00:00:00.000Z", "name": "myABTest", "variants": [ { @@ -32,7 +30,9 @@ "index": "AB_TEST_2", "trafficPercentage": 50 } - ] + ], + "scheduledAt": "2022-11-31T00:00:00.000Z", + "endAt": "2022-12-31T00:00:00.000Z" } } }