forked from loafle/openapi-generator-original
Fix the null pointer exception when generating examples for schemas in python-experimental (#12019)
* Improves example generator for python-experimental * Fixes quotes around date example * Improves object schema examplple gen * Samples regenerated * Adds back in AnyType + oneOf discriminator handling * Reverts version file * Returns early in example gen for array composed schemas * Adds toExampleValue method to python-exp * Improves pattern regex sample generation in python-experimental * Fixes comment typo
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
package org.openapitools.codegen.languages;
|
||||
|
||||
import com.github.curiousoddman.rgxgen.RgxGen;
|
||||
import com.github.curiousoddman.rgxgen.config.RgxGenOption;
|
||||
import com.github.curiousoddman.rgxgen.config.RgxGenProperties;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
@@ -611,7 +613,7 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
} else {
|
||||
strValue = dateValue.toString();
|
||||
}
|
||||
return "isoparse('" + strValue + "').date()";
|
||||
return strValue;
|
||||
}
|
||||
|
||||
public String pythonDateTime(Object dateTimeValue) {
|
||||
@@ -628,7 +630,7 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
} else {
|
||||
strValue = dateTimeValue.toString();
|
||||
}
|
||||
return "isoparse('" + strValue + "')";
|
||||
return strValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1355,6 +1357,13 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
return "\"" + in + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toExampleValue(Schema schema) {
|
||||
String modelName = getModelName(schema);
|
||||
Object objExample = getObjectExample(schema);
|
||||
return toExampleValueRecursive(modelName, schema, objExample, 1, "", 0, new ArrayList<>());
|
||||
}
|
||||
|
||||
public String toExampleValue(Schema schema, Object objExample) {
|
||||
String modelName = getModelName(schema);
|
||||
return toExampleValueRecursive(modelName, schema, objExample, 1, "", 0, new ArrayList<>());
|
||||
@@ -1447,12 +1456,66 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
}
|
||||
String refModelName = getModelName(schema);
|
||||
return toExampleValueRecursive(refModelName, refSchema, objExample, indentationLevel, prefix, exampleLine, includedSchemas);
|
||||
} else if (ModelUtils.isNullType(schema) || isAnyTypeSchema(schema)) {
|
||||
} else if (ModelUtils.isNullType(schema)) {
|
||||
// The 'null' type is allowed in OAS 3.1 and above. It is not supported by OAS 3.0.x,
|
||||
// though this tooling supports it.
|
||||
return fullPrefix + "None" + closeChars;
|
||||
} else if (ModelUtils.isAnyType(schema)) {
|
||||
/*
|
||||
This schema may be a composed schema
|
||||
TODO generate examples for some of these use cases in the future like
|
||||
only oneOf without a discriminator
|
||||
*/
|
||||
Boolean hasProperties = (schema.getProperties() != null && !schema.getProperties().isEmpty());
|
||||
CodegenDiscriminator disc = createDiscriminator(modelName, schema, openAPI);
|
||||
if (ModelUtils.isComposedSchema(schema)) {
|
||||
// complex composed object type schemas not yet handled and the code returns early
|
||||
if (hasProperties) {
|
||||
// what if this composed schema defined properties + allOf?
|
||||
// or items + properties, both a ist and a dict could be accepted as payloads
|
||||
return fullPrefix + "{}" + closeChars;
|
||||
}
|
||||
ComposedSchema cs = (ComposedSchema) schema;
|
||||
Integer allOfExists = 0;
|
||||
if (cs.getAllOf() != null && !cs.getAllOf().isEmpty()) {
|
||||
allOfExists = 1;
|
||||
}
|
||||
Integer anyOfExists = 0;
|
||||
if (cs.getAnyOf() != null && !cs.getAnyOf().isEmpty()) {
|
||||
anyOfExists = 1;
|
||||
}
|
||||
Integer oneOfExists = 0;
|
||||
if (cs.getOneOf() != null && !cs.getOneOf().isEmpty()) {
|
||||
oneOfExists = 1;
|
||||
}
|
||||
if (allOfExists + anyOfExists + oneOfExists > 1) {
|
||||
// what if it needs one oneOf schema, one anyOf schema, and two allOf schemas?
|
||||
return fullPrefix + "None" + closeChars;
|
||||
}
|
||||
// for now only oneOf with discriminator is supported
|
||||
if (oneOfExists == 1 && disc != null) {
|
||||
;
|
||||
} else {
|
||||
return fullPrefix + "None" + closeChars;
|
||||
}
|
||||
}
|
||||
if (disc != null) {
|
||||
// a discriminator means that the type must be object
|
||||
MappedModel mm = getDiscriminatorMappedModel(disc);
|
||||
if (mm == null) {
|
||||
return fullPrefix + "None" + closeChars;
|
||||
}
|
||||
String discPropNameValue = mm.getMappingName();
|
||||
String chosenModelName = mm.getModelName();
|
||||
Schema modelSchema = getModelNameToSchemaCache().get(chosenModelName);
|
||||
CodegenProperty cp = new CodegenProperty();
|
||||
cp.setName(disc.getPropertyName());
|
||||
cp.setExample(discPropNameValue);
|
||||
return exampleForObjectModel(modelSchema, fullPrefix, closeChars, cp, indentationLevel, exampleLine, closingIndentation, includedSchemas);
|
||||
}
|
||||
return fullPrefix + "None" + closeChars;
|
||||
} else if (ModelUtils.isBooleanSchema(schema)) {
|
||||
if (objExample == null) {
|
||||
if (example == null) {
|
||||
example = "True";
|
||||
} else {
|
||||
if ("false".equalsIgnoreCase(objExample.toString())) {
|
||||
@@ -1462,22 +1525,24 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
}
|
||||
}
|
||||
return fullPrefix + example + closeChars;
|
||||
} else if (ModelUtils.isDateSchema(schema)) {
|
||||
} else if (ModelUtils.isStringSchema(schema)) {
|
||||
if (example != null) {
|
||||
return fullPrefix + ensureQuotes(example) + closeChars;
|
||||
}
|
||||
if (ModelUtils.isDateSchema(schema)) {
|
||||
if (objExample == null) {
|
||||
example = pythonDate("1970-01-01");
|
||||
} else {
|
||||
example = pythonDate(objExample);
|
||||
}
|
||||
return fullPrefix + example + closeChars;
|
||||
} else if (ModelUtils.isDateTimeSchema(schema)) {
|
||||
if (objExample == null) {
|
||||
example = pythonDateTime("1970-01-01T00:00:00.00Z");
|
||||
} else {
|
||||
example = pythonDateTime(objExample);
|
||||
}
|
||||
return fullPrefix + example + closeChars;
|
||||
} else if (ModelUtils.isBinarySchema(schema)) {
|
||||
if (objExample == null) {
|
||||
if (example == null) {
|
||||
example = "/path/to/file";
|
||||
}
|
||||
example = "open('" + example + "', 'rb')";
|
||||
@@ -1486,26 +1551,47 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
if (objExample == null) {
|
||||
example = "'YQ=='";
|
||||
}
|
||||
return fullPrefix + example + closeChars;
|
||||
} else if (ModelUtils.isStringSchema(schema)) {
|
||||
if (objExample == null) {
|
||||
} else if ("Number".equalsIgnoreCase(schema.getFormat())) {
|
||||
// a BigDecimal:
|
||||
if ("Number".equalsIgnoreCase(schema.getFormat())) {
|
||||
example = "2";
|
||||
return fullPrefix + example + closeChars;
|
||||
} else if (StringUtils.isNotBlank(schema.getPattern())) {
|
||||
String pattern = schema.getPattern();
|
||||
RgxGen rgxGen = new RgxGen(pattern);
|
||||
/*
|
||||
RxGen does not support our ECMA dialect https://github.com/curious-odd-man/RgxGen/issues/56
|
||||
So strip off the leading / and trailing / and turn on ignore case if we have it
|
||||
*/
|
||||
Pattern valueExtractor = Pattern.compile("^/?(.+?)/?(.?)$");
|
||||
Matcher m = valueExtractor.matcher(pattern);
|
||||
RgxGen rgxGen = null;
|
||||
if (m.find()) {
|
||||
int groupCount = m.groupCount();
|
||||
if (groupCount == 1) {
|
||||
// only pattern found
|
||||
String isolatedPattern = m.group(1);
|
||||
rgxGen = new RgxGen(isolatedPattern);
|
||||
} else if (groupCount == 2) {
|
||||
// patterns and flag found
|
||||
String isolatedPattern = m.group(1);
|
||||
String flags = m.group(2);
|
||||
if (flags.contains("i")) {
|
||||
rgxGen = new RgxGen(isolatedPattern);
|
||||
RgxGenProperties properties = new RgxGenProperties();
|
||||
RgxGenOption.CASE_INSENSITIVE.setInProperties(properties, true);
|
||||
rgxGen.setProperties(properties);
|
||||
} else {
|
||||
rgxGen = new RgxGen(isolatedPattern);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rgxGen = new RgxGen(pattern);
|
||||
}
|
||||
|
||||
// this seed makes it so if we have [a-z] we pick a
|
||||
Random random = new Random(18);
|
||||
String sample = rgxGen.generate(random);
|
||||
// omit leading / and trailing /, omit trailing /i
|
||||
Pattern valueExtractor = Pattern.compile("^/?(.+?)/?.?$");
|
||||
Matcher m = valueExtractor.matcher(sample);
|
||||
if (m.find()) {
|
||||
example = m.group(m.groupCount());
|
||||
if (rgxGen != null) {
|
||||
example = rgxGen.generate(random);
|
||||
} else {
|
||||
example = "";
|
||||
throw new RuntimeException("rgxGen cannot be null. Please open an issue in the openapi-generator github repo.");
|
||||
}
|
||||
} else if (schema.getMinLength() != null) {
|
||||
example = "";
|
||||
@@ -1516,7 +1602,6 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
} else {
|
||||
example = "string_example";
|
||||
}
|
||||
}
|
||||
return fullPrefix + ensureQuotes(example) + closeChars;
|
||||
} else if (ModelUtils.isIntegerSchema(schema)) {
|
||||
if (objExample == null) {
|
||||
@@ -1539,7 +1624,11 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
} else if (ModelUtils.isArraySchema(schema)) {
|
||||
if (objExample instanceof Iterable) {
|
||||
// If the example is already a list, return it directly instead of wrongly wrap it in another list
|
||||
return fullPrefix + objExample.toString();
|
||||
return fullPrefix + objExample.toString() + closeChars;
|
||||
}
|
||||
if (ModelUtils.isComposedSchema(schema)) {
|
||||
// complex composed array type schemas not yet handled and the code returns early
|
||||
return fullPrefix + "[]" + closeChars;
|
||||
}
|
||||
ArraySchema arrayschema = (ArraySchema) schema;
|
||||
Schema itemSchema = arrayschema.getItems();
|
||||
@@ -1547,18 +1636,68 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
includedSchemas.add(schema);
|
||||
String itemExample = toExampleValueRecursive(itemModelName, itemSchema, objExample, indentationLevel + 1, "", exampleLine + 1, includedSchemas);
|
||||
if (StringUtils.isEmpty(itemExample) || cycleFound) {
|
||||
return fullPrefix + "[]";
|
||||
return fullPrefix + "[]" + closeChars;
|
||||
} else {
|
||||
return fullPrefix + "[" + "\n" + itemExample + "\n" + closingIndentation + "]" + closeChars;
|
||||
}
|
||||
} else if (ModelUtils.isMapSchema(schema)) {
|
||||
} else if (ModelUtils.isTypeObjectSchema(schema)) {
|
||||
if (modelName == null) {
|
||||
fullPrefix += "dict(";
|
||||
closeChars = ")";
|
||||
}
|
||||
if (cycleFound) {
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
Boolean hasProperties = (schema.getProperties() != null && !schema.getProperties().isEmpty());
|
||||
CodegenDiscriminator disc = createDiscriminator(modelName, schema, openAPI);
|
||||
if (ModelUtils.isComposedSchema(schema)) {
|
||||
// complex composed object type schemas not yet handled and the code returns early
|
||||
if (hasProperties) {
|
||||
// what if this composed schema defined properties + allOf?
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
ComposedSchema cs = (ComposedSchema) schema;
|
||||
Integer allOfExists = 0;
|
||||
if (cs.getAllOf() != null && !cs.getAllOf().isEmpty()) {
|
||||
allOfExists = 1;
|
||||
}
|
||||
Integer anyOfExists = 0;
|
||||
if (cs.getAnyOf() != null && !cs.getAnyOf().isEmpty()) {
|
||||
anyOfExists = 1;
|
||||
}
|
||||
Integer oneOfExists = 0;
|
||||
if (cs.getOneOf() != null && !cs.getOneOf().isEmpty()) {
|
||||
oneOfExists = 1;
|
||||
}
|
||||
if (allOfExists + anyOfExists + oneOfExists > 1) {
|
||||
// what if it needs one oneOf schema, one anyOf schema, and two allOf schemas?
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
// for now only oneOf with discriminator is supported
|
||||
if (oneOfExists == 1 && disc != null) {
|
||||
;
|
||||
} else {
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
}
|
||||
if (disc != null) {
|
||||
MappedModel mm = getDiscriminatorMappedModel(disc);
|
||||
if (mm == null) {
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
String discPropNameValue = mm.getMappingName();
|
||||
String chosenModelName = mm.getModelName();
|
||||
Schema modelSchema = getModelNameToSchemaCache().get(chosenModelName);
|
||||
CodegenProperty cp = new CodegenProperty();
|
||||
cp.setName(disc.getPropertyName());
|
||||
cp.setExample(discPropNameValue);
|
||||
return exampleForObjectModel(modelSchema, fullPrefix, closeChars, cp, indentationLevel, exampleLine, closingIndentation, includedSchemas);
|
||||
}
|
||||
Object addPropsObj = schema.getAdditionalProperties();
|
||||
if (hasProperties) {
|
||||
return exampleForObjectModel(schema, fullPrefix, closeChars, null, indentationLevel, exampleLine, closingIndentation, includedSchemas);
|
||||
} else if (addPropsObj instanceof Schema) {
|
||||
// TODO handle true case for additionalProperties
|
||||
if (addPropsObj instanceof Schema && !cycleFound) {
|
||||
Schema addPropsSchema = (Schema) addPropsObj;
|
||||
String key = "key";
|
||||
Object addPropsExample = getObjectExample(addPropsSchema);
|
||||
@@ -1576,51 +1715,6 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
|
||||
} else {
|
||||
example = fullPrefix + closeChars;
|
||||
}
|
||||
return example;
|
||||
} else if (ModelUtils.isObjectSchema(schema)) {
|
||||
if (modelName == null) {
|
||||
fullPrefix += "dict(";
|
||||
closeChars = ")";
|
||||
}
|
||||
if (cycleFound) {
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
CodegenDiscriminator disc = createDiscriminator(modelName, schema, openAPI);
|
||||
if (disc != null) {
|
||||
MappedModel mm = getDiscriminatorMappedModel(disc);
|
||||
if (mm != null) {
|
||||
String discPropNameValue = mm.getMappingName();
|
||||
String chosenModelName = mm.getModelName();
|
||||
// TODO handle this case in the future, this is when the discriminated
|
||||
// schema allOf includes this schema, like Cat allOf includes Pet
|
||||
// so this is the composed schema use case
|
||||
} else {
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
}
|
||||
return exampleForObjectModel(schema, fullPrefix, closeChars, null, indentationLevel, exampleLine, closingIndentation, includedSchemas);
|
||||
} else if (ModelUtils.isComposedSchema(schema)) {
|
||||
if (cycleFound) {
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
// TODO add examples for composed schema models without discriminators
|
||||
|
||||
CodegenDiscriminator disc = createDiscriminator(modelName, schema, openAPI);
|
||||
if (disc != null) {
|
||||
MappedModel mm = getDiscriminatorMappedModel(disc);
|
||||
if (mm != null) {
|
||||
String discPropNameValue = mm.getMappingName();
|
||||
String chosenModelName = mm.getModelName();
|
||||
Schema modelSchema = getModelNameToSchemaCache().get(chosenModelName);
|
||||
CodegenProperty cp = new CodegenProperty();
|
||||
cp.setName(disc.getPropertyName());
|
||||
cp.setExample(discPropNameValue);
|
||||
return exampleForObjectModel(modelSchema, fullPrefix, closeChars, cp, indentationLevel, exampleLine, closingIndentation, includedSchemas);
|
||||
} else {
|
||||
return fullPrefix + closeChars;
|
||||
}
|
||||
}
|
||||
return fullPrefix + closeChars;
|
||||
} else {
|
||||
LOGGER.warn("Type " + schema.getType() + " not handled properly in toExampleValue");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Type | Description | Notes
|
||||
------------- | ------------- | -------------
|
||||
**datetime** | | defaults to isoparse('2010-01-01T10:10:10.000111+01:00')
|
||||
**datetime** | | defaults to 2010-01-01T10:10:10.000111+01:00
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -766,7 +766,7 @@ with petstore_api.ApiClient(configuration) as api_client:
|
||||
api_instance = fake_api.FakeApi(api_client)
|
||||
|
||||
# example passing only optional values
|
||||
body = ComposedOneOfDifferentTypes()
|
||||
body = ComposedOneOfDifferentTypes(None)
|
||||
try:
|
||||
api_response = api_instance.composed_one_of_different_types(
|
||||
body=body,
|
||||
@@ -866,12 +866,12 @@ with petstore_api.ApiClient(configuration) as api_client:
|
||||
number=32.1,
|
||||
_float=3.14,
|
||||
double=67.8,
|
||||
string="a",
|
||||
pattern_without_delimiter="AUR,rZ#UM/?R,Fp^l6$ARjbhJk C",
|
||||
string="A",
|
||||
pattern_without_delimiter="Aj",
|
||||
byte='YQ==',
|
||||
binary=open('/path/to/file', 'rb'),
|
||||
date=isoparse('1970-01-01').date(),
|
||||
date_time=isoparse('2020-02-02T20:20:20.22222Z'),
|
||||
date="1970-01-01",
|
||||
date_time="2020-02-02T20:20:20.222220Z",
|
||||
password="password_example",
|
||||
callback="callback_example",
|
||||
)
|
||||
@@ -911,7 +911,7 @@ Name | Type | Description | Notes
|
||||
**byte** | **str** | None |
|
||||
**binary** | **file_type** | None | [optional]
|
||||
**date** | **date** | None | [optional]
|
||||
**dateTime** | **datetime** | None | [optional] if omitted the server will use the default value of isoparse('2010-02-01T10:20:10.11111+01:00')
|
||||
**dateTime** | **datetime** | None | [optional] if omitted the server will use the default value of 2010-02-01T10:20:10.11111+01:00
|
||||
**password** | **str** | None | [optional]
|
||||
**callback** | **str** | None | [optional]
|
||||
**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional]
|
||||
@@ -1434,12 +1434,12 @@ with petstore_api.ApiClient(configuration) as api_client:
|
||||
|
||||
# example passing only optional values
|
||||
query_params = {
|
||||
'compositionAtRoot': ,
|
||||
'compositionAtRoot': None,
|
||||
'compositionInProperty': dict(
|
||||
some_prop=,
|
||||
some_prop=None,
|
||||
),
|
||||
}
|
||||
body =
|
||||
body = None
|
||||
try:
|
||||
# testing composed schemas at inline locations
|
||||
api_response = api_instance.inline_composition(
|
||||
|
||||
@@ -314,7 +314,7 @@ with petstore_api.ApiClient(configuration) as api_client:
|
||||
id=1,
|
||||
pet_id=1,
|
||||
quantity=1,
|
||||
ship_date=isoparse('2020-02-02T20:20:20.000222Z'),
|
||||
ship_date="2020-02-02T20:20:20.000222Z",
|
||||
status="placed",
|
||||
complete=False,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user