From 46d33547ea2d2ad12262fef25a15c6385cccea3e Mon Sep 17 00:00:00 2001 From: Prashant Pant Date: Sun, 8 Oct 2023 15:13:44 +0000 Subject: [PATCH] Autoset constants (Required fields having single valid enum value) Python Implementation of #16547 --- .../codegen/CodegenConstants.java | 2 + .../openapitools/codegen/DefaultCodegen.java | 50 ++++++++++++++++++ .../languages/AbstractJavaCodegen.java | 51 +------------------ .../languages/AbstractPythonCodegen.java | 3 ++ .../src/main/resources/python/api.mustache | 14 +++++ .../codegen/java/JavaClientCodegenTest.java | 3 +- .../python/PythonClientCodegenTest.java | 28 ++++++++++ 7 files changed, 100 insertions(+), 51 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java index edc2ef10c096..aefd17216ecb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java @@ -43,6 +43,8 @@ public class CodegenConstants { public static final String API_SUFFIX = "apiSuffix"; public static final String API_SUFFIX_DESC = "suffix for api classes"; + public static final String AUTOSET_CONSTANTS = "autosetConstants"; + public static final String MODEL_PACKAGE = "modelPackage"; public static final String MODEL_PACKAGE_DESC = "package for generated models"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index c38960787048..5ccfe3f453ba 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -304,6 +304,9 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected boolean addSuffixToDuplicateOperationNicknames = true; + // Whether to automatically hardcode params that are considered Constants by OpenAPI Spec + protected boolean autosetConstants = false; + public boolean getAddSuffixToDuplicateOperationNicknames() { return addSuffixToDuplicateOperationNicknames; } @@ -427,6 +430,10 @@ public void processOpts() { this.setEnumUnknownDefaultCase(Boolean.parseBoolean(additionalProperties .get(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE).toString())); } + if (additionalProperties.containsKey(CodegenConstants.AUTOSET_CONSTANTS)) { + this.setAutosetConstants( + Boolean.parseBoolean(additionalProperties.get(CodegenConstants.AUTOSET_CONSTANTS).toString())); + } } /*** @@ -8281,4 +8288,47 @@ protected Schema getSchemaFromBooleanOrSchema(Object schema) { throw new IllegalArgumentException("Invalid schema type; type must be Boolean or Schema"); } } + + public void setAutosetConstants(boolean autosetConstants) { + this.autosetConstants = autosetConstants; + } + + /** + * This method removes all constant Query, Header and Cookie Params from allParams and sets them as constantParams in the CodegenOperation. + * The definition of constant is single valued required enum params. + * The constantParams in the the generated code should be hardcoded to the constantValue if autosetConstants feature is enabled. + * + * @param operation - operation to be processed + */ + protected void handleConstantParams(CodegenOperation operation) { + if (!autosetConstants || operation.allParams.isEmpty()) { + return; + } + final ArrayList copy = new ArrayList<>(operation.allParams); + // Remove all params from Params, Non constant params will be added back later. + operation.allParams.clear(); + + // Finds all constant params, removes them from allParams and adds them to constant params. + // Also, adds back non constant params to allParams. + for (CodegenParameter p : copy) { + if (p.isEnum && p.required && p._enum != null && p._enum.size() == 1) { + // Add to constantParams for use in the code generation templates. + operation.constantParams.add(p); + if(p.isQueryParam) { + operation.queryParams.removeIf(param -> param.baseName.equals(p.baseName)); + } + if(p.isHeaderParam) { + operation.headerParams.removeIf(param -> param.baseName.equals(p.baseName)); + } + if(p.isCookieParam) { + operation.cookieParams.removeIf(param -> param.baseName.equals(p.baseName)); + } + LOGGER.info("Update operation [{}]. Remove parameter [{}] because it can only have a fixed value of [{}]", operation.operationId, p.baseName, p._enum.get(0)); + } else { + // Add back to allParams as the param is not a constant. + operation.allParams.add(p); + } + } + operation.hasParams = !operation.allParams.isEmpty(); + } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index b389a550b01a..e0f94fea1928 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -82,7 +82,6 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code public static final String TEST_OUTPUT = "testOutput"; public static final String IMPLICIT_HEADERS = "implicitHeaders"; public static final String IMPLICIT_HEADERS_REGEX = "implicitHeadersRegex"; - public static final String AUTOSET_CONSTANTS = "autosetConstants"; public static final String JAVAX_PACKAGE = "javaxPackage"; public static final String USE_JAKARTA_EE = "useJakartaEe"; public static final String CONTAINER_DEFAULT_TO_NULL = "containerDefaultToNull"; @@ -136,7 +135,6 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code protected AnnotationLibrary annotationLibrary; protected boolean implicitHeaders = false; protected String implicitHeadersRegex = null; - protected boolean autosetConstants = false; protected boolean camelCaseDollarSign = false; protected boolean useJakartaEe = false; protected boolean containerDefaultToNull = false; @@ -558,10 +556,6 @@ public void processOpts() { this.setImplicitHeadersRegex(additionalProperties.get(IMPLICIT_HEADERS_REGEX).toString()); } - if (additionalProperties.containsKey(AUTOSET_CONSTANTS)) { - this.setAutosetConstants(Boolean.parseBoolean(additionalProperties.get(AUTOSET_CONSTANTS).toString())); - } - if (additionalProperties.containsKey(CAMEL_CASE_DOLLAR_SIGN)) { this.setCamelCaseDollarSign(Boolean.parseBoolean(additionalProperties.get(CAMEL_CASE_DOLLAR_SIGN).toString())); } @@ -2052,10 +2046,6 @@ public void setImplicitHeadersRegex(String implicitHeadersRegex) { this.implicitHeadersRegex = implicitHeadersRegex; } - public void setAutosetConstants(boolean autosetConstants) { - this.autosetConstants = autosetConstants; - } - public void setCamelCaseDollarSign(boolean camelCaseDollarSign) { this.camelCaseDollarSign = camelCaseDollarSign; } @@ -2293,46 +2283,7 @@ protected void handleImplicitHeaders(CodegenOperation operation) { } operation.hasParams = !operation.allParams.isEmpty(); } - - /** - * This method removes all constant Query, Header and Cookie Params from allParams and sets them as constantParams in the CodegenOperation. - * The definition of constant is single valued required enum params. - * The constantParams in the the generated code should be hardcoded to the constantValue if autosetConstants feature is enabled. - * - * @param operation - operation to be processed - */ - protected void handleConstantParams(CodegenOperation operation) { - if (!autosetConstants || operation.allParams.isEmpty()) { - return; - } - final ArrayList copy = new ArrayList<>(operation.allParams); - // Remove all params from Params, Non constant params will be added back later. - operation.allParams.clear(); - - // Finds all constant params, removes them from allParams and adds them to constant params. - // Also, adds back non constant params to allParams. - for (CodegenParameter p : copy) { - if (p.isEnum && p.required && p._enum != null && p._enum.size() == 1) { - // Add to constantParams for use in the code generation templates. - operation.constantParams.add(p); - if(p.isQueryParam) { - operation.queryParams.removeIf(param -> param.baseName.equals(p.baseName)); - } - if(p.isHeaderParam) { - operation.headerParams.removeIf(param -> param.baseName.equals(p.baseName)); - } - if(p.isCookieParam) { - operation.cookieParams.removeIf(param -> param.baseName.equals(p.baseName)); - } - LOGGER.info("Update operation [{}]. Remove parameter [{}] because it can only have a fixed value of [{}]", operation.operationId, p.baseName, p._enum.get(0)); - } else { - // Add back to allParams as the param is not a constant. - operation.allParams.add(p); - } - } - operation.hasParams = !operation.allParams.isEmpty(); - } - + private boolean shouldBeImplicitHeader(CodegenParameter parameter) { return StringUtils.isNotBlank(implicitHeadersRegex) && parameter.baseName.matches(implicitHeadersRegex); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index ec63f2846203..dae3b400ae9a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -1281,6 +1281,9 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List> newImports = new ArrayList<>(); diff --git a/modules/openapi-generator/src/main/resources/python/api.mustache b/modules/openapi-generator/src/main/resources/python/api.mustache index d6dc91c99b97..287b519263ee 100644 --- a/modules/openapi-generator/src/main/resources/python/api.mustache +++ b/modules/openapi-generator/src/main/resources/python/api.mustache @@ -219,6 +219,13 @@ class {{classname}}: {{/isArray}} {{/queryParams}} + {{#constantParams}} + {{#isQueryParam}} + # Set client side default value of Query Param "{{baseName}}". + _query_params['{{baseName}}'] = {{#_enum}}'{{{.}}}'{{/_enum}} + + {{/isQueryParam}} + {{/constantParams}} # process the header parameters _header_params = dict(_params.get('_headers', {})) {{#headerParams}} @@ -229,6 +236,13 @@ class {{classname}}: {{/isArray}} {{/headerParams}} + {{#constantParams}} + {{#isHeaderParam}} + # Set client side default value of Header Param "{{baseName}}". + _header_params['{{baseName}}'] = {{#_enum}}'{{{.}}}'{{/_enum}} + + {{/isHeaderParam}} + {{/constantParams}} # process the form parameters _form_params: List[Tuple[str, str]] = [] _files: Dict[str, str] = {} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index 5b548acf56cd..aef203469363 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -2673,6 +2673,7 @@ public void testWebClientResponseTypeWithUseAbstractionForFiles_issue16589() thr ); } + @Test public void testHandleConstantParams() throws IOException { File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); output.deleteOnExit(); @@ -2682,7 +2683,7 @@ public void testHandleConstantParams() throws IOException { clientOptInput.openAPI(openAPI); JavaClientCodegen javaClientCodegen = new JavaClientCodegen(); javaClientCodegen.setOutputDir(output.getAbsolutePath()); - javaClientCodegen.additionalProperties().put(JavaClientCodegen.AUTOSET_CONSTANTS, "true"); + javaClientCodegen.additionalProperties().put(CodegenConstants.AUTOSET_CONSTANTS, "true"); javaClientCodegen.setAutosetConstants(true); clientOptInput.config(javaClientCodegen); defaultGenerator.opts(clientOptInput); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java index b0554c1ae856..d71ccda423fc 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java @@ -17,6 +17,7 @@ package org.openapitools.codegen.python; +import static org.junit.Assert.assertNotNull; import com.google.common.collect.Sets; import io.swagger.parser.OpenAPIParser; import io.swagger.v3.oas.models.OpenAPI; @@ -30,6 +31,7 @@ import static org.openapitools.codegen.TestUtils.assertFileContains; import static org.openapitools.codegen.TestUtils.assertFileExists; import org.openapitools.codegen.TestUtils; +import org.openapitools.codegen.java.assertions.JavaFileAssert; import org.testng.Assert; import org.testng.annotations.Test; import java.io.File; @@ -40,6 +42,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; public class PythonClientCodegenTest { @@ -501,4 +506,27 @@ public void modelTestDollarSign() { CodegenProperty property = vars.get(0); Assert.assertEquals(property.name, "dollar_value"); } + + @Test + public void testHandleConstantParams() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/java/autoset_constant.yaml"); + final DefaultGenerator defaultGenerator = new DefaultGenerator(); + final ClientOptInput clientOptInput = new ClientOptInput(); + clientOptInput.openAPI(openAPI); + PythonClientCodegen pythonClientCodegen = new PythonClientCodegen(); + pythonClientCodegen.setOutputDir(output.getAbsolutePath()); + pythonClientCodegen.additionalProperties().put(CodegenConstants.AUTOSET_CONSTANTS, "true"); + pythonClientCodegen.setAutosetConstants(true); + clientOptInput.config(pythonClientCodegen); + defaultGenerator.opts(clientOptInput); + + Map files = defaultGenerator.generate().stream() + .collect(Collectors.toMap(File::getPath, Function.identity())); + + File apiFile = files.get(output.getAbsolutePath() + "/openapi_client/api/hello_example_api.py"); + assertNotNull(apiFile); + assertFileContains(apiFile.toPath(), "_header_params['X-CUSTOM_CONSTANT_HEADER'] = 'CONSTANT_VALUE'"); + } }