Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the null pointer exception when generating examples for schemas in python-experimental #12019

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -609,7 +611,7 @@ public String pythonDate(Object dateValue) {
} else {
strValue = dateValue.toString();
}
return "isoparse('" + strValue + "').date()";
return strValue;
}

public String pythonDateTime(Object dateTimeValue) {
Expand All @@ -626,7 +628,7 @@ public String pythonDateTime(Object dateTimeValue) {
} else {
strValue = dateTimeValue.toString();
}
return "isoparse('" + strValue + "')";
return strValue;
}

/**
Expand Down Expand Up @@ -1353,6 +1355,13 @@ private String ensureQuotes(String in) {
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<>());
Expand Down Expand Up @@ -1445,12 +1454,66 @@ private String toExampleValueRecursive(String modelName, Schema schema, Object o
}
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())) {
Expand All @@ -1460,60 +1523,82 @@ private String toExampleValueRecursive(String modelName, Schema schema, Object o
}
}
return fullPrefix + example + closeChars;
} else 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) {
example = "/path/to/file";
}
example = "open('" + example + "', 'rb')";
return fullPrefix + example + closeChars;
} else if (ModelUtils.isByteArraySchema(schema)) {
if (objExample == null) {
example = "'YQ=='";
}
return fullPrefix + example + closeChars;
} else if (ModelUtils.isStringSchema(schema)) {
if (objExample == null) {
if (example != null) {
return fullPrefix + ensureQuotes(example) + closeChars;
}
if (ModelUtils.isDateSchema(schema)) {
if (objExample == null) {
example = pythonDate("1970-01-01");
} else {
example = pythonDate(objExample);
}
} else if (ModelUtils.isDateTimeSchema(schema)) {
if (objExample == null) {
example = pythonDateTime("1970-01-01T00:00:00.00Z");
} else {
example = pythonDateTime(objExample);
}
} else if (ModelUtils.isBinarySchema(schema)) {
if (example == null) {
example = "/path/to/file";
}
example = "open('" + example + "', 'rb')";
return fullPrefix + example + closeChars;
} else if (ModelUtils.isByteArraySchema(schema)) {
if (objExample == null) {
example = "'YQ=='";
}
} 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);
// 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());
} else {
example = "";
example = "2";
} else if (StringUtils.isNotBlank(schema.getPattern())) {
String pattern = schema.getPattern();
/*
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 if (schema.getMinLength() != null) {
example = "";
int len = schema.getMinLength().intValue();
for (int i = 0; i < len; i++) example += "a";
} else if (ModelUtils.isUUIDSchema(schema)) {
example = "046b6c7f-0b8a-43b9-b35d-6489e6daee91";
} else {
example = "string_example";
rgxGen = new RgxGen(pattern);
}

// this seed makes it so if we have [a-z] we pick a
Random random = new Random(18);
if (rgxGen != null) {
example = rgxGen.generate(random);
} else {
throw new RuntimeException("rgxGen cannot be null. Please open an issue in the openapi-generator github repo.");
}
} else if (schema.getMinLength() != null) {
example = "";
int len = schema.getMinLength().intValue();
for (int i = 0; i < len; i++) example += "a";
} else if (ModelUtils.isUUIDSchema(schema)) {
example = "046b6c7f-0b8a-43b9-b35d-6489e6daee91";
} else {
example = "string_example";
}
return fullPrefix + ensureQuotes(example) + closeChars;
} else if (ModelUtils.isIntegerSchema(schema)) {
Expand All @@ -1537,26 +1622,80 @@ private String toExampleValueRecursive(String modelName, Schema schema, Object o
} 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();
String itemModelName = getModelName(itemSchema);
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();
// TODO handle true case for additionalProperties
if (addPropsObj instanceof Schema && !cycleFound) {
if (hasProperties) {
return exampleForObjectModel(schema, fullPrefix, closeChars, null, indentationLevel, exampleLine, closingIndentation, includedSchemas);
} else if (addPropsObj instanceof Schema) {
// TODO handle true case for additionalProperties
Schema addPropsSchema = (Schema) addPropsObj;
String key = "key";
Object addPropsExample = getObjectExample(addPropsSchema);
Expand All @@ -1574,51 +1713,6 @@ private String toExampleValueRecursive(String modelName, Schema schema, Object o
} 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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Loading