Skip to content

Commit

Permalink
add support for union of strictfloat and strictint (#15124)
Browse files Browse the repository at this point in the history
  • Loading branch information
wing328 committed Apr 6, 2023
1 parent b409ceb commit 07227d4
Show file tree
Hide file tree
Showing 38 changed files with 1,530 additions and 34 deletions.
2 changes: 1 addition & 1 deletion bin/configs/python-nextgen-aiohttp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ templateDir: modules/openapi-generator/src/main/resources/python-nextgen
library: asyncio
additionalProperties:
packageName: petstore_api
floatStrictType: false
mapNumberTo: float
1 change: 1 addition & 0 deletions bin/configs/python-nextgen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ additionalProperties:
packageName: petstore_api
useOneOfDiscriminatorLookup: "true"
disallowAdditionalPropertiesIfNotPresent: false
mapNumberTo: StrictFloat
2 changes: 1 addition & 1 deletion docs/generators/python-nextgen.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|dateFormat|date format for query parameters| |%Y-%m-%d|
|datetimeFormat|datetime format for query parameters| |%Y-%m-%dT%H:%M:%S%z|
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|floatStrictType|Use strict type for float, i.e. StrictFloat or confloat(strict=true, ...)| |true|
|generateSourceCodeOnly|Specifies that only a library source code is to be generated.| |false|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|library|library template (sub-template) to use: asyncio, tornado (deprecated), urllib3| |urllib3|
|mapNumberTo|Map number to Union[StrictFloat, StrictInt], StrictStr or float.| |Union[StrictFloat, StrictInt]|
|packageName|python package name (convention: snake_case).| |openapi_client|
|packageUrl|python package URL.| |null|
|packageVersion|python package version.| |1.0.0|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ public class PythonNextgenClientCodegen extends AbstractPythonCodegen implements
public static final String PACKAGE_URL = "packageUrl";
public static final String DEFAULT_LIBRARY = "urllib3";
public static final String RECURSION_LIMIT = "recursionLimit";
public static final String FLOAT_STRICT_TYPE = "floatStrictType";
public static final String DATETIME_FORMAT = "datetimeFormat";
public static final String DATE_FORMAT = "dateFormat";
public static final String MAP_NUMBER_TO = "mapNumberTo";

protected String packageUrl;
protected String apiDocPath = "docs" + File.separator;
protected String modelDocPath = "docs" + File.separator;
protected boolean hasModelsToImport = Boolean.FALSE;
protected boolean useOneOfDiscriminatorLookup = false; // use oneOf discriminator's mapping for model lookup
protected boolean floatStrictType = true;
protected String datetimeFormat = "%Y-%m-%dT%H:%M:%S.%f%z";
protected String dateFormat = "%Y-%m-%d";
protected String mapNumberTo = "Union[StrictFloat, StrictInt]";

protected Map<Character, String> regexModifiers;

Expand Down Expand Up @@ -177,8 +177,8 @@ public PythonNextgenClientCodegen() {
cliOptions.add(new CliOption(CodegenConstants.SOURCECODEONLY_GENERATION, CodegenConstants.SOURCECODEONLY_GENERATION_DESC)
.defaultValue(Boolean.FALSE.toString()));
cliOptions.add(new CliOption(RECURSION_LIMIT, "Set the recursion limit. If not set, use the system default value."));
cliOptions.add(new CliOption(FLOAT_STRICT_TYPE, "Use strict type for float, i.e. StrictFloat or confloat(strict=true, ...)")
.defaultValue(Boolean.TRUE.toString()));
cliOptions.add(new CliOption(MAP_NUMBER_TO, "Map number to Union[StrictFloat, StrictInt], StrictStr or float.")
.defaultValue("Union[StrictFloat, StrictInt]"));
cliOptions.add(new CliOption(DATETIME_FORMAT, "datetime format for query parameters")
.defaultValue("%Y-%m-%dT%H:%M:%S%z"));
cliOptions.add(new CliOption(DATE_FORMAT, "date format for query parameters")
Expand Down Expand Up @@ -281,8 +281,8 @@ public void processOpts() {
additionalProperties.put(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, useOneOfDiscriminatorLookup);
}

if (additionalProperties.containsKey(FLOAT_STRICT_TYPE)) {
setFloatStrictType(convertPropertyToBooleanAndWriteBack(FLOAT_STRICT_TYPE));
if (additionalProperties.containsKey(MAP_NUMBER_TO)) {
setMapNumberTo(String.valueOf(additionalProperties.get(MAP_NUMBER_TO)));
}

if (additionalProperties.containsKey(DATETIME_FORMAT)) {
Expand Down Expand Up @@ -478,34 +478,59 @@ private String getPydanticType(CodegenParameter cp,
} else if (cp.isNumber || cp.isFloat || cp.isDouble) {
if (cp.hasValidation) {
List<String> fieldCustomization = new ArrayList<>();
List<String> intFieldCustomization = new ArrayList<>();

// e.g. confloat(ge=10, le=100, strict=True)
if (cp.getMaximum() != null) {
if (cp.getExclusiveMaximum()) {
fieldCustomization.add("gt=" + cp.getMaximum());
fieldCustomization.add("lt=" + cp.getMaximum());
intFieldCustomization.add("lt=" + Math.ceil(Double.valueOf(cp.getMaximum()))); // e.g. < 7.59 becomes < 8
} else {
fieldCustomization.add("ge=" + cp.getMaximum());
fieldCustomization.add("le=" + cp.getMaximum());
intFieldCustomization.add("le=" + Math.floor(Double.valueOf(cp.getMaximum()))); // e.g. <= 7.59 becomes <= 7
}
}
if (cp.getMinimum() != null) {
if (cp.getExclusiveMinimum()) {
fieldCustomization.add("lt=" + cp.getMinimum());
fieldCustomization.add("gt=" + cp.getMinimum());
intFieldCustomization.add("gt=" + Math.floor(Double.valueOf(cp.getMinimum()))); // e.g. > 7.59 becomes > 7
} else {
fieldCustomization.add("le=" + cp.getMinimum());
fieldCustomization.add("ge=" + cp.getMinimum());
intFieldCustomization.add("ge=" + Math.ceil(Double.valueOf(cp.getMinimum()))); // e.g. >= 7.59 becomes >= 8
}
}
if (cp.getMultipleOf() != null) {
fieldCustomization.add("multiple_of=" + cp.getMultipleOf());
}

if (floatStrictType) {
if ("Union[StrictFloat, StrictInt]".equals(mapNumberTo)) {
fieldCustomization.add("strict=True");
intFieldCustomization.add("strict=True");
pydanticImports.add("confloat");
pydanticImports.add("conint");
typingImports.add("Union");
return String.format(Locale.ROOT, "Union[%s(%s), %s(%s)]", "confloat",
StringUtils.join(fieldCustomization, ", "),
"conint",
StringUtils.join(intFieldCustomization, ", ")
);
} else if ("StrictFloat".equals(mapNumberTo)) {
fieldCustomization.add("strict=True");
pydanticImports.add("confloat");
return String.format(Locale.ROOT, "%s(%s)", "confloat",
StringUtils.join(fieldCustomization, ", "));
} else { // float
pydanticImports.add("confloat");
return String.format(Locale.ROOT, "%s(%s)", "confloat",
StringUtils.join(fieldCustomization, ", "));
}

pydanticImports.add("confloat");
return String.format(Locale.ROOT, "%s(%s)", "confloat",
StringUtils.join(fieldCustomization, ", "));
} else {
if (floatStrictType) {
if ("Union[StrictFloat, StrictInt]".equals(mapNumberTo)) {
typingImports.add("Union");
pydanticImports.add("StrictFloat");
pydanticImports.add("StrictInt");
return "Union[StrictFloat, StrictInt]";
} else if ("StrictFloat".equals(mapNumberTo)) {
pydanticImports.add("StrictFloat");
return "StrictFloat";
} else {
Expand Down Expand Up @@ -723,34 +748,59 @@ private String getPydanticType(CodegenProperty cp,
} else if (cp.isNumber || cp.isFloat || cp.isDouble) {
if (cp.hasValidation) {
List<String> fieldCustomization = new ArrayList<>();
List<String> intFieldCustomization = new ArrayList<>();

// e.g. confloat(ge=10, le=100, strict=True)
if (cp.getMaximum() != null) {
if (cp.getExclusiveMaximum()) {
fieldCustomization.add("lt=" + cp.getMaximum());
intFieldCustomization.add("lt=" + (int) Math.ceil(Double.valueOf(cp.getMaximum()))); // e.g. < 7.59 => < 8
} else {
fieldCustomization.add("le=" + cp.getMaximum());
intFieldCustomization.add("le=" + (int) Math.floor(Double.valueOf(cp.getMaximum()))); // e.g. <= 7.59 => <= 7
}
}
if (cp.getMinimum() != null) {
if (cp.getExclusiveMinimum()) {
fieldCustomization.add("gt=" + cp.getMinimum());
intFieldCustomization.add("gt=" + (int) Math.floor(Double.valueOf(cp.getMinimum()))); // e.g. > 7.59 => > 7
} else {
fieldCustomization.add("ge=" + cp.getMinimum());
intFieldCustomization.add("ge=" + (int) Math.ceil(Double.valueOf(cp.getMinimum()))); // e.g. >= 7.59 => >= 8
}
}
if (cp.getMultipleOf() != null) {
fieldCustomization.add("multiple_of=" + cp.getMultipleOf());
}

if (floatStrictType) {
if ("Union[StrictFloat, StrictInt]".equals(mapNumberTo)) {
fieldCustomization.add("strict=True");
intFieldCustomization.add("strict=True");
pydanticImports.add("confloat");
pydanticImports.add("conint");
typingImports.add("Union");
return String.format(Locale.ROOT, "Union[%s(%s), %s(%s)]", "confloat",
StringUtils.join(fieldCustomization, ", "),
"conint",
StringUtils.join(intFieldCustomization, ", ")
);
} else if ("StrictFloat".equals(mapNumberTo)) {
fieldCustomization.add("strict=True");
pydanticImports.add("confloat");
return String.format(Locale.ROOT, "%s(%s)", "confloat",
StringUtils.join(fieldCustomization, ", "));
} else { // float
pydanticImports.add("confloat");
return String.format(Locale.ROOT, "%s(%s)", "confloat",
StringUtils.join(fieldCustomization, ", "));
}

pydanticImports.add("confloat");
return String.format(Locale.ROOT, "%s(%s)", "confloat",
StringUtils.join(fieldCustomization, ", "));
} else {
if (floatStrictType) {
if ("Union[StrictFloat, StrictInt]".equals(mapNumberTo)) {
typingImports.add("Union");
pydanticImports.add("StrictFloat");
pydanticImports.add("StrictInt");
return "Union[StrictFloat, StrictInt]";
} else if ("StrictFloat".equals(mapNumberTo)) {
pydanticImports.add("StrictFloat");
return "StrictFloat";
} else {
Expand Down Expand Up @@ -1334,8 +1384,8 @@ public void postProcessPattern(String pattern, Map<String, Object> vendorExtensi
}
}

vendorExtensions.put("x-regex", regex.replace("\"","\\\""));
vendorExtensions.put("x-pattern", pattern.replace("\"","\\\""));
vendorExtensions.put("x-regex", regex.replace("\"", "\\\""));
vendorExtensions.put("x-pattern", pattern.replace("\"", "\\\""));
vendorExtensions.put("x-modifiers", modifiers);
}
}
Expand Down Expand Up @@ -1529,8 +1579,14 @@ public String escapeReservedWord(String name) {
return "var_" + name;
}

public void setFloatStrictType(boolean floatStrictType) {
this.floatStrictType = floatStrictType;
public void setMapNumberTo(String mapNumberTo) {
if ("Union[StrictFloat, StrictInt]".equals(mapNumberTo)
|| "StrictFloat".equals(mapNumberTo)
|| "float".equals(mapNumberTo)) {
this.mapNumberTo = mapNumberTo;
} else {
throw new IllegalArgumentException("mapNumberTo value must be Union[StrictFloat, StrictInt], StrictStr or float");
}
}

public void setDatetimeFormat(String datetimeFormat) {
Expand Down
13 changes: 13 additions & 0 deletions modules/openapi-generator/src/test/resources/3_0/echo_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,16 @@ components:
format: date-time
description: A date
- $ref: '#/components/schemas/Query'
NumberPropertiesOnly:
type: object
properties:
number:
type: number
float:
type: number
format: float
double:
type: number
format: double
minimum: 0.8
maximum: 50.2
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ docs/DataQueryAllOf.md
docs/DefaultValue.md
docs/FormApi.md
docs/HeaderApi.md
docs/NumberPropertiesOnly.md
docs/PathApi.md
docs/Pet.md
docs/Query.md
Expand Down Expand Up @@ -53,6 +54,7 @@ src/main/java/org/openapitools/client/model/Category.java
src/main/java/org/openapitools/client/model/DataQuery.java
src/main/java/org/openapitools/client/model/DataQueryAllOf.java
src/main/java/org/openapitools/client/model/DefaultValue.java
src/main/java/org/openapitools/client/model/NumberPropertiesOnly.java
src/main/java/org/openapitools/client/model/Pet.java
src/main/java/org/openapitools/client/model/Query.java
src/main/java/org/openapitools/client/model/StringEnumRef.java
Expand Down
1 change: 1 addition & 0 deletions samples/client/echo_api/java/apache-httpclient/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ Class | Method | HTTP request | Description
- [DataQuery](docs/DataQuery.md)
- [DataQueryAllOf](docs/DataQueryAllOf.md)
- [DefaultValue](docs/DefaultValue.md)
- [NumberPropertiesOnly](docs/NumberPropertiesOnly.md)
- [Pet](docs/Pet.md)
- [Query](docs/Query.md)
- [StringEnumRef](docs/StringEnumRef.md)
Expand Down
13 changes: 13 additions & 0 deletions samples/client/echo_api/java/apache-httpclient/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,19 @@ components:
allOf:
- $ref: '#/components/schemas/DataQuery_allOf'
- $ref: '#/components/schemas/Query'
NumberPropertiesOnly:
properties:
number:
type: number
float:
format: float
type: number
double:
format: double
maximum: 50.2
minimum: 0.8
type: number
type: object
test_form_integer_boolean_string_request:
properties:
integer_form:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@


# NumberPropertiesOnly


## Properties

| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|
|**number** | **BigDecimal** | | [optional] |
|**_float** | **Float** | | [optional] |
|**_double** | **Double** | | [optional] |



Loading

0 comments on commit 07227d4

Please sign in to comment.