Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1960,10 +1960,10 @@ public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
/**
* Return a string representation of the schema type, resolving aliasing and references if necessary.
*
* @param schema
* @param schema input
* @return the string representation of the schema type.
*/
private String getSingleSchemaType(Schema schema) {
protected String getSingleSchemaType(Schema schema) {
Schema unaliasSchema = ModelUtils.unaliasSchema(this.openAPI, schema, importMapping);

if (StringUtils.isNotBlank(unaliasSchema.get$ref())) { // reference to another definition/schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
CodegenProperty modelProperty = fromProperty("value", modelSchema);

// import complex type from additional properties
// TODO can I remove this? DOes it exist in defaultcodegen?
if (cm.additionalPropertiesType != null && modelProperty.items != null && modelProperty.items.complexType != null) {
cm.imports.add(modelProperty.items.complexType);
}
Expand Down Expand Up @@ -800,9 +801,11 @@ private void addNullDefaultToOneOfAnyOfReqProps(Schema schema, CodegenModel resu
public CodegenModel fromModel(String name, Schema schema) {
// we have a custom version of this function so we can produce
// models for components whose type != object and which have validations and enums
// this ensures that endpoint (operation) responses with validations and enums
// will generate models, and when those endpoint responses are received in python
// the response is cast as a model, and the model will validate the response using the enums and validations
// this ensures that:
// - endpoint (operation) responses with validations and type!=(object or array)
// - oneOf $ref components with validations and type!=(object or array)
// when endpoints receive payloads of these models
// that they will be converted into instances of these models
Map<String, String> propertyToModelName = new HashMap<String, String>();
Map<String, Schema> propertiesMap = schema.getProperties();
if (propertiesMap != null) {
Expand All @@ -820,45 +823,119 @@ public CodegenModel fromModel(String name, Schema schema) {
continue;
}
CodegenProperty modelProperty = fromProperty("_fake_name", refSchema);
if (modelProperty.isEnum == false && modelProperty.hasValidation == false) {
if (modelProperty.isEnum == true || modelProperty.hasValidation == false) {
continue;
}
String modelName = ModelUtils.getSimpleRef(ref);
propertyToModelName.put(pythonPropertyName, toModelName(modelName));
}
}
CodegenModel result = super.fromModel(name, schema);

// have oneOf point to the correct model
if (ModelUtils.isComposedSchema(schema)) {
ComposedSchema cs = (ComposedSchema) schema;
Map<String, Integer> importCounts = new HashMap<String, Integer>();
List<Schema> oneOfSchemas = cs.getOneOf();
if (oneOfSchemas != null) {
for (int i = 0; i < oneOfSchemas.size(); i++) {
Schema oneOfSchema = oneOfSchemas.get(i);
String languageType = getTypeDeclaration(oneOfSchema);
String ref = oneOfSchema.get$ref();
if (ref == null) {
Integer currVal = importCounts.getOrDefault(languageType, 0);
importCounts.put(languageType, currVal+1);
continue;
}
Schema refSchema = ModelUtils.getReferencedSchema(this.openAPI, oneOfSchema);
String refType = refSchema.getType();
if (refType == null || refType.equals("object")) {
Integer currVal = importCounts.getOrDefault(languageType, 0);
importCounts.put(languageType, currVal+1);
continue;
}

CodegenProperty modelProperty = fromProperty("_oneOfSchema", refSchema);
if (modelProperty.isEnum == true) {
Integer currVal = importCounts.getOrDefault(languageType, 0);
importCounts.put(languageType, currVal+1);
continue;
}

languageType = getTypeDeclaration(refSchema);
if (modelProperty.hasValidation == false) {
Integer currVal = importCounts.getOrDefault(languageType, 0);
importCounts.put(languageType, currVal+1);
continue;
}
Integer currVal = importCounts.getOrDefault(languageType, 0);
importCounts.put(languageType, currVal);
String modelName = toModelName(ModelUtils.getSimpleRef(ref));
result.imports.add(modelName);
result.oneOf.add(modelName);
currVal = importCounts.getOrDefault(modelName, 0);
importCounts.put(modelName, currVal+1);
}
}
for (Map.Entry<String, Integer> entry : importCounts.entrySet()) {
String importName = entry.getKey();
Integer importCount = entry.getValue();
if (importCount == 0) {
result.oneOf.remove(importName);
}
}
}

// use this to store the model name like Cat
// we can't use result.name because that is used to lookup models in the spec
// we can't use result.classname because that stores cat.Cat
// we can't use result.classVarName because that stores the variable for making example instances
result.unescapedDescription = simpleModelName(name);

// make non-object type models have one property so we can use it to store enums and validations
if (result.isAlias || result.isEnum || result.isArrayModel) {
Schema modelSchema = ModelUtils.getSchema(this.openAPI, result.name);
CodegenProperty modelProperty = fromProperty("value", modelSchema);
if (modelProperty.isEnum == true || modelProperty.hasValidation == true || result.isArrayModel) {
// these models are non-object models with enums and/or validations
// add a single property to the model so we can have a way to access validations
result.isAlias = true;
modelProperty.required = true;
List<CodegenProperty> theProperties = Arrays.asList(modelProperty);
result.setAllVars(theProperties);
result.setVars(theProperties);
result.setRequiredVars(theProperties);
// post process model properties
if (result.vars != null) {
for (CodegenProperty prop : result.vars) {
postProcessModelProperty(result, prop);
}
// this block handles models which have the python base class ModelSimple
// which are responsible for storing validations, enums, and an unnamed value
Schema modelSchema = ModelUtils.getSchema(this.openAPI, result.name);
CodegenProperty modelProperty = fromProperty("_value", modelSchema);

// import complex type from additional properties
if (result.additionalPropertiesType != null && modelProperty.items != null && modelProperty.items.complexType != null) {
result.imports.add(modelProperty.items.complexType);
}

Boolean isPythonModelSimpleModel = (result.isEnum || result.isArrayModel || result.isAlias && modelProperty.hasValidation);
if (isPythonModelSimpleModel) {
// In python, classes which inherit from our ModelSimple class store one value,
// like a str, int, list and extra data about that value like validations and enums

if (result.isEnum) {
// if there is only one allowed value then we know that it should be set, so value is optional
// -> hasRequired = false
// if there are more than one allowed value then value is positional and required so
// -> hasRequired = true
ArrayList values = (ArrayList) result.allowableValues.get("values");
if (values != null && values.size() > 1) {
result.hasRequired = true;
}

if (modelProperty.defaultValue != null && result.defaultValue == null) {
result.defaultValue = modelProperty.defaultValue;
}
} else {
if (result.isArrayModel && modelProperty.dataType != null && result.dataType == null) {
// needed for array models with complex types
result.dataType = modelProperty.dataType;
result.arrayModelType = getPythonClassName(result.arrayModelType);
}

if (result.defaultValue == null) {
result.hasRequired = true;
}
}
}

// set regex values, before it was only done on model.vars
// fix all property references to non-object models, make those properties non-primitive and
// fix all property references to ModelSimple models, make those properties non-primitive and
// set their dataType and complexType to the model name, so documentation will refer to the correct model
// set regex values, before it was only done on model.vars
// NOTE: this is done for models of type != object which are not enums and have validations
ArrayList<List<CodegenProperty>> listOfLists = new ArrayList<List<CodegenProperty>>();
listOfLists.add(result.vars);
listOfLists.add(result.allVars);
Expand All @@ -883,6 +960,7 @@ public CodegenModel fromModel(String name, Schema schema) {
result.imports.add(modelName);
}
}

// if a class has a property of type self, remove the self import from imports
if (result.imports.contains(result.classname)) {
result.imports.remove(result.classname);
Expand All @@ -895,6 +973,60 @@ public CodegenModel fromModel(String name, Schema schema) {
return result;
}

/**
* returns the OpenAPI type for the property. Use getAlias to handle $ref of primitive type
* We have a custom version of this function because for composed schemas we also want to return the model name
* In DefaultCodegen.java it returns a name built off of individual allOf/anyOf/oneOf which is not what
* python-experimental needs. Python-experimental needs the name of the composed schema
*
* @param schema property schema
* @return string presentation of the type
**/
@SuppressWarnings("static-method")
@Override
public String getSchemaType(Schema schema) {
if (schema instanceof ComposedSchema) { // composed schema
Schema unaliasSchema = ModelUtils.unaliasSchema(this.openAPI, schema, importMapping);
String ref = unaliasSchema.get$ref();
if (ref != null) {
String schemaName = ModelUtils.getSimpleRef(unaliasSchema.get$ref());
if (StringUtils.isNotEmpty(schemaName) && importMapping.containsKey(schemaName)) {
return schemaName;
}
return getAlias(schemaName);
} else {
// we may have be processing the component schema rather than a schema with a $ref
// to a component schema
// so loop through component schemas and use the found one's name if we match
Map<String, Schema> schemas = ModelUtils.getSchemas(openAPI);
for (String thisSchemaName : schemas.keySet()) {
Schema thisSchema = schemas.get(thisSchemaName);
if (!ModelUtils.isComposedSchema(thisSchema)) {
continue;
}
if (thisSchema == unaliasSchema) {
if (importMapping.containsKey(thisSchemaName)) {
return thisSchemaName;
}
return getAlias(thisSchemaName);
}
}
LOGGER.warn("Error obtaining the datatype from ref:" + unaliasSchema.get$ref() + ". Default to 'object'");
return "object";
}
}
String openAPIType = getSingleSchemaType(schema);
if (typeMapping.containsKey(openAPIType)) {
String type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type)) {
return type;
}
} else {
return toModelName(openAPIType);
}
return openAPIType;
}

/**
* Output the type declaration of the property
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,22 @@ from {{packageName}}.model_utils import ( # noqa: F401


{{^interfaces}}
{{#isAlias}}
{{> python-experimental/model_templates/model_simple }}
{{/isAlias}}
{{^isAlias}}
{{#isArrayModel}}
{{> python-experimental/model_templates/model_simple }}
{{/isArrayModel}}
{{#isEnum}}
{{> python-experimental/model_templates/model_simple }}
{{/isEnum}}
{{#isAlias}}
{{> python-experimental/model_templates/model_simple }}
{{/isAlias}}
{{^isArrayModel}}
{{^isEnum}}
{{^isAlias}}
{{> python-experimental/model_templates/model_normal }}
{{/isArrayModel}}
{{/isAlias}}
{{/isEnum}}
{{/isArrayModel}}
{{/interfaces}}
{{#interfaces}}
{{#-last}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
{{#isEnum}}
**value** | {{^arrayModelType}}**{{dataType}}**{{/arrayModelType}} | {{description}} | {{#defaultValue}}{{#hasRequired}} if omitted the server will use the default value of {{/hasRequired}}{{^hasRequired}}defaults to {{/hasRequired}}{{{.}}}{{/defaultValue}}{{#allowableValues}}{{#defaultValue}}, {{/defaultValue}} must be one of [{{#enumVars}}{{{value}}}, {{/enumVars}}]{{/allowableValues}}
{{/isEnum}}
{{#isAlias}}
**value** | {{^arrayModelType}}**{{dataType}}**{{/arrayModelType}} | {{description}} | {{#defaultValue}}{{#hasRequired}} if omitted the server will use the default value of {{/hasRequired}}{{^hasRequired}}defaults to {{/hasRequired}}{{{.}}}{{/defaultValue}}
{{/isAlias}}
{{#isArrayModel}}
**value** | {{^arrayModelType}}**{{dataType}}**{{/arrayModelType}}{{#arrayModelType}}[**{{dataType}}**]({{arrayModelType}}.md){{/arrayModelType}} | {{description}} | {{#defaultValue}}{{#hasRequired}} if omitted the server will use the default value of {{/hasRequired}}{{^hasRequired}}defaults to {{/hasRequired}}{{{.}}}{{/defaultValue}}
{{/isArrayModel}}
{{#requiredVars}}
{{^defaultValue}}
**{{name}}** | {{^complexType}}**{{dataType}}**{{/complexType}}{{#complexType}}[**{{dataType}}**]({{complexType}}.md){{/complexType}} | {{description}} | {{#isReadOnly}}[readonly] {{/isReadOnly}}
Expand Down
Loading