Autoset constants (Required fields having single valid enum value) (#16761)

* Autoset constants (Required fields having single valid enum value) Python Implementation of #16547

* Fixing ``PythonClientCodegenTest.testHandleConstantParams`` for Windows platform.
This commit is contained in:
Prashant Pant 2023-10-16 14:47:59 +00:00 committed by GitHub
parent 8bb473316e
commit 2f214ee6c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 101 additions and 51 deletions

View File

@ -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";

View File

@ -304,6 +304,9 @@ public class DefaultCodegen implements CodegenConfig {
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 class DefaultCodegen implements CodegenConfig {
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()));
}
}
/***
@ -8282,4 +8289,47 @@ public class DefaultCodegen implements CodegenConfig {
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();
}
}

View File

@ -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 abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
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 abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
this.implicitHeadersRegex = implicitHeadersRegex;
}
public void setAutosetConstants(boolean autosetConstants) {
this.autosetConstants = autosetConstants;
}
public void setCamelCaseDollarSign(boolean camelCaseDollarSign) {
this.camelCaseDollarSign = camelCaseDollarSign;
}
@ -2293,46 +2283,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
}
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);
}

View File

@ -1281,6 +1281,9 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
}
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<>();

View File

@ -227,6 +227,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}}
@ -237,6 +244,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] = {}

View File

@ -2673,6 +2673,7 @@ public class JavaClientCodegenTest {
);
}
@Test
public void testHandleConstantParams() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
@ -2682,7 +2683,7 @@ public class JavaClientCodegenTest {
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);

View File

@ -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 org.openapitools.codegen.languages.features.CXFServerFeatures;
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.nio.file.Path;
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,28 @@ public class PythonClientCodegenTest {
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(Paths.get(output.getAbsolutePath(), "openapi_client", "api", "hello_example_api.py").toString());
assertNotNull(apiFile);
assertFileContains(apiFile.toPath(), "_header_params['X-CUSTOM_CONSTANT_HEADER'] = 'CONSTANT_VALUE'");
}
}