Skip to content

Commit

Permalink
Autoset constants (Required fields having single valid enum value) Py…
Browse files Browse the repository at this point in the history
…thon Implementation of #16547
  • Loading branch information
prashant-pant committed Oct 8, 2023
1 parent f568001 commit 46d3354
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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()));
}
}

/***
Expand Down Expand Up @@ -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<CodegenParameter> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()));
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<CodegenParameter> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,9 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
}
operation.vendorExtensions.put("x-py-example-import", imports);
}

// Remove constant params from allParams list and add to constantParams
handleConstantParams(operation);
}

List<Map<String, String>> newImports = new ArrayList<>();
Expand Down
14 changes: 14 additions & 0 deletions modules/openapi-generator/src/main/resources/python/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand All @@ -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] = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2673,6 +2673,7 @@ public void testWebClientResponseTypeWithUseAbstractionForFiles_issue16589() thr
);
}

@Test
public void testHandleConstantParams() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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 {

Expand Down Expand Up @@ -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<String, File> 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'");
}
}

0 comments on commit 46d3354

Please sign in to comment.