forked from loafle/openapi-generator-original
Adds generator unaliasSchema method, uses it to refactor python-experimental (#7274)
* Adds generator specific unaliasSchema method * Adds unaliasSchema and hasValidation methods in python-experimental generator * Removes primitive models with no validations, docs, tests, and models * Uses unaliasSchema in getSchemaType and adds todos * Deletes handleMethodResponse and fromResponse * Simplifies fromRequestBody * Removes unneeded handleMethodResponse * Updates javadoc * Updates fromModel * Adds python-exp java test defaultSettingInPrimitiveModelWithValidations, removes model NumberWithValidationsAndDefault form v3 sample spec * Deletes getSimpleTypeDeclaration * Removes straggler file * Deletes hasValidation and modelWillBeMade * Uses super in fromFormProperty * Regenerates samples for python-experimental * Updates postProcessAllModels
This commit is contained in:
parent
b4edfe477a
commit
892836f772
@ -1971,6 +1971,10 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
return "oneOf<" + String.join(",", names) + ">";
|
return "oneOf<" + String.join(",", names) + ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Schema unaliasSchema(Schema schema, Map<String, String> usedImportMappings) {
|
||||||
|
return ModelUtils.unaliasSchema(this.openAPI, schema, usedImportMappings);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a string representation of the schema type, resolving aliasing and references if necessary.
|
* Return a string representation of the schema type, resolving aliasing and references if necessary.
|
||||||
*
|
*
|
||||||
@ -1978,7 +1982,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
* @return the string representation of the schema type.
|
* @return the string representation of the schema type.
|
||||||
*/
|
*/
|
||||||
protected String getSingleSchemaType(Schema schema) {
|
protected String getSingleSchemaType(Schema schema) {
|
||||||
Schema unaliasSchema = ModelUtils.unaliasSchema(this.openAPI, schema, importMapping);
|
Schema unaliasSchema = unaliasSchema(schema, importMapping);
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(unaliasSchema.get$ref())) { // reference to another definition/schema
|
if (StringUtils.isNotBlank(unaliasSchema.get$ref())) { // reference to another definition/schema
|
||||||
// get the schema/model name from $ref
|
// get the schema/model name from $ref
|
||||||
@ -2216,7 +2220,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// unalias schema
|
// unalias schema
|
||||||
schema = ModelUtils.unaliasSchema(this.openAPI, schema, importMapping);
|
schema = unaliasSchema(schema, importMapping);
|
||||||
if (schema == null) {
|
if (schema == null) {
|
||||||
LOGGER.warn("Schema {} not found", name);
|
LOGGER.warn("Schema {} not found", name);
|
||||||
return null;
|
return null;
|
||||||
@ -2330,7 +2334,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
m.interfaces = new ArrayList<String>();
|
m.interfaces = new ArrayList<String>();
|
||||||
|
|
||||||
for (Schema interfaceSchema : interfaces) {
|
for (Schema interfaceSchema : interfaces) {
|
||||||
interfaceSchema = ModelUtils.unaliasSchema(this.openAPI, interfaceSchema, importMapping);
|
interfaceSchema = unaliasSchema(interfaceSchema, importMapping);
|
||||||
|
|
||||||
if (StringUtils.isBlank(interfaceSchema.get$ref())) {
|
if (StringUtils.isBlank(interfaceSchema.get$ref())) {
|
||||||
// primitive type
|
// primitive type
|
||||||
@ -2991,7 +2995,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
LOGGER.debug("debugging fromProperty for " + name + " : " + p);
|
LOGGER.debug("debugging fromProperty for " + name + " : " + p);
|
||||||
|
|
||||||
// unalias schema
|
// unalias schema
|
||||||
p = ModelUtils.unaliasSchema(this.openAPI, p, importMapping);
|
p = unaliasSchema(p, importMapping);
|
||||||
|
|
||||||
CodegenProperty property = CodegenModelFactory.newInstance(CodegenModelType.PROPERTY);
|
CodegenProperty property = CodegenModelFactory.newInstance(CodegenModelType.PROPERTY);
|
||||||
|
|
||||||
@ -3168,10 +3172,9 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
} else if (ModelUtils.isArraySchema(p)) {
|
} else if (ModelUtils.isArraySchema(p)) {
|
||||||
// default to string if inner item is undefined
|
// default to string if inner item is undefined
|
||||||
ArraySchema arraySchema = (ArraySchema) p;
|
ArraySchema arraySchema = (ArraySchema) p;
|
||||||
Schema innerSchema = ModelUtils.unaliasSchema(this.openAPI, getSchemaItems(arraySchema), importMapping);
|
Schema innerSchema = unaliasSchema(getSchemaItems(arraySchema), importMapping);
|
||||||
} else if (ModelUtils.isMapSchema(p)) {
|
} else if (ModelUtils.isMapSchema(p)) {
|
||||||
Schema innerSchema = ModelUtils.unaliasSchema(this.openAPI, getAdditionalProperties(p),
|
Schema innerSchema = unaliasSchema(getAdditionalProperties(p), importMapping);
|
||||||
importMapping);
|
|
||||||
if (innerSchema == null) {
|
if (innerSchema == null) {
|
||||||
LOGGER.error("Undefined map inner type for `{}`. Default to String.", p.getName());
|
LOGGER.error("Undefined map inner type for `{}`. Default to String.", p.getName());
|
||||||
innerSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to undefined type");
|
innerSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to undefined type");
|
||||||
@ -3251,7 +3254,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
itemName = property.name;
|
itemName = property.name;
|
||||||
}
|
}
|
||||||
ArraySchema arraySchema = (ArraySchema) p;
|
ArraySchema arraySchema = (ArraySchema) p;
|
||||||
Schema innerSchema = ModelUtils.unaliasSchema(this.openAPI, getSchemaItems(arraySchema), importMapping);
|
Schema innerSchema = unaliasSchema(getSchemaItems(arraySchema), importMapping);
|
||||||
CodegenProperty cp = fromProperty(itemName, innerSchema);
|
CodegenProperty cp = fromProperty(itemName, innerSchema);
|
||||||
updatePropertyForArray(property, cp);
|
updatePropertyForArray(property, cp);
|
||||||
} else if (ModelUtils.isMapSchema(p)) {
|
} else if (ModelUtils.isMapSchema(p)) {
|
||||||
@ -3263,8 +3266,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
property.maxItems = p.getMaxProperties();
|
property.maxItems = p.getMaxProperties();
|
||||||
|
|
||||||
// handle inner property
|
// handle inner property
|
||||||
Schema innerSchema = ModelUtils.unaliasSchema(this.openAPI, getAdditionalProperties(p),
|
Schema innerSchema = unaliasSchema(getAdditionalProperties(p), importMapping);
|
||||||
importMapping);
|
|
||||||
if (innerSchema == null) {
|
if (innerSchema == null) {
|
||||||
LOGGER.error("Undefined map inner type for `{}`. Default to String.", p.getName());
|
LOGGER.error("Undefined map inner type for `{}`. Default to String.", p.getName());
|
||||||
innerSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to undefined type");
|
innerSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to undefined type");
|
||||||
@ -3504,7 +3506,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
CodegenOperation op,
|
CodegenOperation op,
|
||||||
ApiResponse methodResponse,
|
ApiResponse methodResponse,
|
||||||
Map<String, String> importMappings) {
|
Map<String, String> importMappings) {
|
||||||
Schema responseSchema = ModelUtils.unaliasSchema(this.openAPI, ModelUtils.getSchemaFromResponse(methodResponse), importMappings);
|
Schema responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(methodResponse), importMapping);
|
||||||
|
|
||||||
if (responseSchema != null) {
|
if (responseSchema != null) {
|
||||||
CodegenProperty cm = fromProperty("response", responseSchema);
|
CodegenProperty cm = fromProperty("response", responseSchema);
|
||||||
@ -3908,8 +3910,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
}
|
}
|
||||||
Schema responseSchema;
|
Schema responseSchema;
|
||||||
if (this.openAPI != null && this.openAPI.getComponents() != null) {
|
if (this.openAPI != null && this.openAPI.getComponents() != null) {
|
||||||
responseSchema = ModelUtils.unaliasSchema(this.openAPI, ModelUtils.getSchemaFromResponse(response),
|
responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(response), importMapping);
|
||||||
importMapping);
|
|
||||||
} else { // no model/alias defined
|
} else { // no model/alias defined
|
||||||
responseSchema = ModelUtils.getSchemaFromResponse(response);
|
responseSchema = ModelUtils.getSchemaFromResponse(response);
|
||||||
}
|
}
|
||||||
@ -4150,7 +4151,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parameterSchema != null) {
|
if (parameterSchema != null) {
|
||||||
parameterSchema = ModelUtils.unaliasSchema(this.openAPI, parameterSchema);
|
parameterSchema = unaliasSchema(parameterSchema, Collections.<String, String>emptyMap());
|
||||||
if (parameterSchema == null) {
|
if (parameterSchema == null) {
|
||||||
LOGGER.warn("warning! Schema not found for parameter \"" + parameter.getName() + "\", using String");
|
LOGGER.warn("warning! Schema not found for parameter \"" + parameter.getName() + "\", using String");
|
||||||
parameterSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to missing type definition.");
|
parameterSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to missing type definition.");
|
||||||
@ -4690,7 +4691,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
private Map<String, Schema> unaliasPropertySchema(Map<String, Schema> properties) {
|
private Map<String, Schema> unaliasPropertySchema(Map<String, Schema> properties) {
|
||||||
if (properties != null) {
|
if (properties != null) {
|
||||||
for (String key : properties.keySet()) {
|
for (String key : properties.keySet()) {
|
||||||
properties.put(key, ModelUtils.unaliasSchema(this.openAPI, properties.get(key), importMapping()));
|
properties.put(key, unaliasSchema(properties.get(key), importMapping()));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5815,7 +5816,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
return codegenParameter;
|
return codegenParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBodyModelSchema(CodegenParameter codegenParameter, String name, Schema schema, Set<String> imports, String bodyParameterName, boolean forceSimpleRef) {
|
protected void addBodyModelSchema(CodegenParameter codegenParameter, String name, Schema schema, Set<String> imports, String bodyParameterName, boolean forceSimpleRef) {
|
||||||
CodegenModel codegenModel = null;
|
CodegenModel codegenModel = null;
|
||||||
if (StringUtils.isNotBlank(name)) {
|
if (StringUtils.isNotBlank(name)) {
|
||||||
schema.setName(name);
|
schema.setName(name);
|
||||||
|
@ -17,19 +17,16 @@
|
|||||||
package org.openapitools.codegen.languages;
|
package org.openapitools.codegen.languages;
|
||||||
|
|
||||||
import io.swagger.v3.core.util.Json;
|
import io.swagger.v3.core.util.Json;
|
||||||
import io.swagger.v3.oas.models.Operation;
|
|
||||||
import io.swagger.v3.oas.models.media.*;
|
import io.swagger.v3.oas.models.media.*;
|
||||||
import io.swagger.v3.oas.models.media.ArraySchema;
|
import io.swagger.v3.oas.models.media.ArraySchema;
|
||||||
import io.swagger.v3.oas.models.media.MediaType;
|
import io.swagger.v3.oas.models.media.MediaType;
|
||||||
import io.swagger.v3.oas.models.media.Schema;
|
import io.swagger.v3.oas.models.media.Schema;
|
||||||
import io.swagger.v3.oas.models.parameters.Parameter;
|
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||||
import io.swagger.v3.oas.models.parameters.RequestBody;
|
import io.swagger.v3.oas.models.parameters.RequestBody;
|
||||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
|
||||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.openapitools.codegen.*;
|
import org.openapitools.codegen.*;
|
||||||
import org.openapitools.codegen.CodegenDiscriminator.MappedModel;
|
import org.openapitools.codegen.CodegenDiscriminator.MappedModel;
|
||||||
import org.openapitools.codegen.examples.ExampleGenerator;
|
|
||||||
import org.openapitools.codegen.meta.features.*;
|
import org.openapitools.codegen.meta.features.*;
|
||||||
import org.openapitools.codegen.utils.ModelUtils;
|
import org.openapitools.codegen.utils.ModelUtils;
|
||||||
import org.openapitools.codegen.utils.ProcessUtils;
|
import org.openapitools.codegen.utils.ProcessUtils;
|
||||||
@ -225,6 +222,83 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
return "python-experimental";
|
return "python-experimental";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Schema unaliasSchema(Schema schema, Map<String, String> usedImportMappings) {
|
||||||
|
Map<String, Schema> allSchemas = ModelUtils.getSchemas(openAPI);
|
||||||
|
if (allSchemas == null || allSchemas.isEmpty()) {
|
||||||
|
// skip the warning as the spec can have no model defined
|
||||||
|
//LOGGER.warn("allSchemas cannot be null/empty in unaliasSchema. Returned 'schema'");
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema != null && StringUtils.isNotEmpty(schema.get$ref())) {
|
||||||
|
String simpleRef = ModelUtils.getSimpleRef(schema.get$ref());
|
||||||
|
if (usedImportMappings.containsKey(simpleRef)) {
|
||||||
|
LOGGER.debug("Schema unaliasing of {} omitted because aliased class is to be mapped to {}", simpleRef, usedImportMappings.get(simpleRef));
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
Schema ref = allSchemas.get(simpleRef);
|
||||||
|
Boolean hasValidation = (
|
||||||
|
ref.getMaxItems() != null ||
|
||||||
|
ref.getMinLength() != null ||
|
||||||
|
ref.getMinItems() != null ||
|
||||||
|
ref.getMultipleOf() != null ||
|
||||||
|
ref.getPattern() != null ||
|
||||||
|
ref.getMaxLength() != null ||
|
||||||
|
ref.getMinimum() != null ||
|
||||||
|
ref.getMaximum() != null ||
|
||||||
|
ref.getExclusiveMaximum() != null ||
|
||||||
|
ref.getExclusiveMinimum() != null ||
|
||||||
|
ref.getUniqueItems() != null
|
||||||
|
);
|
||||||
|
if (ref == null) {
|
||||||
|
once(LOGGER).warn("{} is not defined", schema.get$ref());
|
||||||
|
return schema;
|
||||||
|
} else if (ref.getEnum() != null && !ref.getEnum().isEmpty()) {
|
||||||
|
// top-level enum class
|
||||||
|
return schema;
|
||||||
|
} else if (ModelUtils.isArraySchema(ref)) {
|
||||||
|
if (ModelUtils.isGenerateAliasAsModel(ref)) {
|
||||||
|
return schema; // generate a model extending array
|
||||||
|
} else {
|
||||||
|
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())),
|
||||||
|
usedImportMappings);
|
||||||
|
}
|
||||||
|
} else if (ModelUtils.isComposedSchema(ref)) {
|
||||||
|
return schema;
|
||||||
|
} else if (ModelUtils.isMapSchema(ref)) {
|
||||||
|
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) // has at least one property
|
||||||
|
return schema; // treat it as model
|
||||||
|
else {
|
||||||
|
if (ModelUtils.isGenerateAliasAsModel(ref)) {
|
||||||
|
return schema; // generate a model extending map
|
||||||
|
} else {
|
||||||
|
// treat it as a typical map
|
||||||
|
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())),
|
||||||
|
usedImportMappings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ModelUtils.isObjectSchema(ref)) { // model
|
||||||
|
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) { // has at least one property
|
||||||
|
return schema;
|
||||||
|
} else { // free form object (type: object)
|
||||||
|
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())),
|
||||||
|
usedImportMappings);
|
||||||
|
}
|
||||||
|
} else if (hasValidation) {
|
||||||
|
// non object non array non map schemas that have validations
|
||||||
|
// are returned so we can generate those schemas as models
|
||||||
|
// we do this to:
|
||||||
|
// - preserve the validations in that model class in python
|
||||||
|
// - use those validations when we use this schema in composed oneOf schemas
|
||||||
|
return schema;
|
||||||
|
} else {
|
||||||
|
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())), usedImportMappings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
public String pythonDate(Object dateValue) {
|
public String pythonDate(Object dateValue) {
|
||||||
String strValue = null;
|
String strValue = null;
|
||||||
if (dateValue instanceof OffsetDateTime) {
|
if (dateValue instanceof OffsetDateTime) {
|
||||||
@ -329,57 +403,58 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
return objs;
|
return objs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/***
|
||||||
* Override with special post-processing for all models.
|
* Override with special post-processing for all models.
|
||||||
|
* we have a custom version of this method to:
|
||||||
|
* - remove any primitive models that do not contain validations
|
||||||
|
* these models are unaliased as inline definitions wherever the spec has them as refs
|
||||||
|
* this means that the generated client does not use these models
|
||||||
|
* because they are not used we do not write them
|
||||||
|
* - fix the model imports, go from model name to the full import string with toModelImport + globalImportFixer
|
||||||
|
*
|
||||||
|
* @param objs a map going from the model name to a object hoding the model info
|
||||||
|
* @return the updated objs
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"static-method", "unchecked"})
|
@Override
|
||||||
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
||||||
super.postProcessAllModels(objs);
|
super.postProcessAllModels(objs);
|
||||||
|
|
||||||
// loop through all models and delete ones where type!=object and the model has no validations and enums
|
List<String> modelsToRemove = new ArrayList<>();
|
||||||
// we will remove them because they are not needed
|
Map<String, Schema> allDefinitions = ModelUtils.getSchemas(this.openAPI);
|
||||||
Map<String, Schema> modelSchemasToRemove = new HashMap<String, Schema>();
|
for (String schemaName: allDefinitions.keySet()) {
|
||||||
|
Schema refSchema = new Schema().$ref("#/components/schemas/"+schemaName);
|
||||||
for (Object objModel: objs.values()) {
|
Schema unaliasedSchema = unaliasSchema(refSchema, importMapping);
|
||||||
HashMap<String, Object> hmModel = (HashMap<String, Object>) objModel;
|
String modelName = toModelName(schemaName);
|
||||||
List<Map<String, Object>> models = (List<Map<String, Object>>) hmModel.get("models");
|
if (unaliasedSchema.get$ref() == null) {
|
||||||
for (Map<String, Object> model : models) {
|
modelsToRemove.add(modelName);
|
||||||
CodegenModel cm = (CodegenModel) model.get("model");
|
} else {
|
||||||
|
HashMap<String, Object> objModel = (HashMap<String, Object>) objs.get(modelName);
|
||||||
// remove model if it is a primitive with no validations
|
List<Map<String, Object>> models = (List<Map<String, Object>>) objModel.get("models");
|
||||||
if (cm.isEnum || cm.isAlias) {
|
for (Map<String, Object> model : models) {
|
||||||
Schema modelSchema = ModelUtils.getSchema(this.openAPI, cm.name);
|
CodegenModel cm = (CodegenModel) model.get("model");
|
||||||
CodegenProperty modelProperty = fromProperty("_value", modelSchema);
|
String[] importModelNames = cm.imports.toArray(new String[0]);
|
||||||
if (!modelProperty.isEnum && !modelProperty.hasValidation && !cm.isArrayModel) {
|
cm.imports.clear();
|
||||||
// remove these models because they are aliases and do not have any enums or validations
|
for (String importModelName : importModelNames) {
|
||||||
modelSchemasToRemove.put(cm.name, modelSchema);
|
cm.imports.add(toModelImport(importModelName));
|
||||||
continue;
|
String globalImportFixer = "globals()['" + importModelName + "'] = " + importModelName;
|
||||||
|
cm.imports.add(globalImportFixer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix model imports
|
|
||||||
if (cm.imports.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String[] modelNames = cm.imports.toArray(new String[0]);
|
|
||||||
cm.imports.clear();
|
|
||||||
for (String modelName : modelNames) {
|
|
||||||
cm.imports.add(toModelImport(modelName));
|
|
||||||
String globalImportFixer = "globals()['" + modelName + "'] = " + modelName;
|
|
||||||
cm.imports.add(globalImportFixer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove modelSchemasToRemove models from objs
|
for (String modelName : modelsToRemove) {
|
||||||
for (String modelName : modelSchemasToRemove.keySet()) {
|
|
||||||
objs.remove(modelName);
|
objs.remove(modelName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return objs;
|
return objs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert OAS Property object to Codegen Property object
|
* Convert OAS Property object to Codegen Property object
|
||||||
|
* We have a custom version of this method to always set allowableValues.enumVars on all enum variables
|
||||||
|
* Together with unaliasSchema this sets primitive types with validations as models
|
||||||
|
* This method is used by fromResponse
|
||||||
*
|
*
|
||||||
* @param name name of the property
|
* @param name name of the property
|
||||||
* @param p OAS property object
|
* @param p OAS property object
|
||||||
@ -387,12 +462,17 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CodegenProperty fromProperty(String name, Schema p) {
|
public CodegenProperty fromProperty(String name, Schema p) {
|
||||||
// we have a custom version of this function to always set allowableValues.enumVars on all enum variables
|
CodegenProperty cp = super.fromProperty(name, p);
|
||||||
CodegenProperty result = super.fromProperty(name, p);
|
if (cp.isEnum) {
|
||||||
if (result.isEnum) {
|
updateCodegenPropertyEnum(cp);
|
||||||
updateCodegenPropertyEnum(result);
|
|
||||||
}
|
}
|
||||||
return result;
|
if (cp.isPrimitiveType && p.get$ref() != null) {
|
||||||
|
cp.complexType = cp.dataType;
|
||||||
|
}
|
||||||
|
if (cp.isListContainer && cp.complexType == null && cp.mostInnerItems.complexType != null) {
|
||||||
|
cp.complexType = cp.mostInnerItems.complexType;
|
||||||
|
}
|
||||||
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -439,167 +519,69 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
// overwriting defaultValue omitted from here
|
// overwriting defaultValue omitted from here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* We have a custom version of this method to produce links to models when they are
|
||||||
|
* primitive type (not map, not array, not object) and include validations or are enums
|
||||||
|
*
|
||||||
|
* @param body requesst body
|
||||||
|
* @param imports import collection
|
||||||
|
* @param bodyParameterName body parameter name
|
||||||
|
* @return the resultant CodegenParameter
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, String bodyParameterName) {
|
public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, String bodyParameterName) {
|
||||||
CodegenParameter result = super.fromRequestBody(body, imports, bodyParameterName);
|
CodegenParameter cp = super.fromRequestBody(body, imports, bodyParameterName);
|
||||||
// if we generated a model with a non-object type because it has validations or enums,
|
Schema schema = ModelUtils.getSchemaFromRequestBody(body);
|
||||||
// make sure that the datatype of that body parameter refers to our model class
|
if (schema.get$ref() == null) {
|
||||||
Content content = body.getContent();
|
return cp;
|
||||||
Set<String> keySet = content.keySet();
|
|
||||||
Object[] keyArray = (Object[]) keySet.toArray();
|
|
||||||
MediaType mediaType = content.get(keyArray[0]);
|
|
||||||
Schema schema = mediaType.getSchema();
|
|
||||||
String ref = schema.get$ref();
|
|
||||||
if (ref == null) {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
String modelName = ModelUtils.getSimpleRef(ref);
|
Schema unaliasedSchema = unaliasSchema(schema, importMapping);
|
||||||
// the result lacks validation info so we need to make a CodegenProperty from the schema to check
|
CodegenProperty unaliasedProp = fromProperty("body", unaliasedSchema);
|
||||||
// if we have validation and enum info exists
|
Boolean dataTypeMismatch = !cp.dataType.equals(unaliasedProp.dataType);
|
||||||
Schema realSchema = ModelUtils.getSchema(this.openAPI, modelName);
|
Boolean baseTypeMismatch = !cp.baseType.equals(unaliasedProp.complexType) && unaliasedProp.complexType != null;
|
||||||
CodegenProperty modelProp = fromProperty("body", realSchema);
|
if (dataTypeMismatch || baseTypeMismatch) {
|
||||||
if (modelProp.isPrimitiveType && (modelProp.hasValidation || modelProp.isEnum)) {
|
cp.dataType = unaliasedProp.dataType;
|
||||||
String simpleDataType = result.dataType;
|
cp.baseType = unaliasedProp.complexType;
|
||||||
result.dataType = toModelName(modelName);
|
|
||||||
result.baseType = result.dataType;
|
|
||||||
}
|
}
|
||||||
return result;
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/***
|
||||||
* Convert OAS Response object to Codegen Response object
|
* Adds the body model schema to the body parameter
|
||||||
|
* We have a custom version of this method so we can flip forceSimpleRef
|
||||||
|
* to True based upon the results of unaliasSchema
|
||||||
|
* With this customization, we ensure that when schemas are passed to getSchemaType
|
||||||
|
* - if they have ref in them they are a model
|
||||||
|
* - if they do not have ref in them they are not a model
|
||||||
*
|
*
|
||||||
* @param responseCode HTTP response code
|
* @param codegenParameter the body parameter
|
||||||
* @param response OAS Response object
|
* @param name model schema ref key in components
|
||||||
* @return Codegen Response object
|
* @param schema the model schema (not refed)
|
||||||
|
* @param imports collection of imports
|
||||||
|
* @param bodyParameterName body parameter name
|
||||||
|
* @param forceSimpleRef if true use a model reference
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
|
protected void addBodyModelSchema(CodegenParameter codegenParameter, String name, Schema schema, Set<String> imports, String bodyParameterName, boolean forceSimpleRef) {
|
||||||
// if a response points at a model whose type != object and it has validations and/or enums, then we will
|
if (name != null) {
|
||||||
// generate the model, and response.baseType must be the name
|
Schema bodySchema = new Schema().$ref("#/components/schemas/" + name);
|
||||||
// of the model. Point responses at models if the model is python class type ModelSimple
|
Schema unaliased = unaliasSchema(bodySchema, importMapping);
|
||||||
// When we serialize/deserialize ModelSimple models, validations and enums will be checked.
|
if (unaliased.get$ref() != null) {
|
||||||
Schema responseSchema;
|
forceSimpleRef = true;
|
||||||
if (this.openAPI != null && this.openAPI.getComponents() != null) {
|
|
||||||
responseSchema = ModelUtils.unaliasSchema(this.openAPI, ModelUtils.getSchemaFromResponse(response), importMapping);
|
|
||||||
} else { // no model/alias defined
|
|
||||||
responseSchema = ModelUtils.getSchemaFromResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
String newBaseType = null;
|
|
||||||
if (responseSchema != null) {
|
|
||||||
CodegenProperty cp = fromProperty("response", responseSchema);
|
|
||||||
if (cp.complexType != null) {
|
|
||||||
String modelName = cp.complexType;
|
|
||||||
Schema modelSchema = ModelUtils.getSchema(this.openAPI, modelName);
|
|
||||||
if (modelSchema != null && !"object".equals(modelSchema.getType())) {
|
|
||||||
CodegenProperty modelProp = fromProperty("response", modelSchema);
|
|
||||||
if (modelProp.isEnum == true || modelProp.hasValidation == true) {
|
|
||||||
// this model has validations and/or enums so we will generate it
|
|
||||||
newBaseType = modelName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (cp.isEnum == true || cp.hasValidation == true) {
|
|
||||||
// this model has validations and/or enums so we will generate it
|
|
||||||
Schema sc = ModelUtils.getSchemaFromResponse(response);
|
|
||||||
newBaseType = toModelName(ModelUtils.getSimpleRef(sc.get$ref()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
super.addBodyModelSchema(codegenParameter, name, schema, imports, bodyParameterName, forceSimpleRef);
|
||||||
|
|
||||||
CodegenResponse result = super.fromResponse(responseCode, response);
|
|
||||||
if (newBaseType != null) {
|
|
||||||
result.dataType = newBaseType;
|
|
||||||
// baseType is used to set the link to the model .md documentation
|
|
||||||
result.baseType = newBaseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set op's returnBaseType, returnType, examples etc.
|
|
||||||
*
|
|
||||||
* @param operation endpoint Operation
|
|
||||||
* @param schemas a map of the schemas in the openapi spec
|
|
||||||
* @param op endpoint CodegenOperation
|
|
||||||
* @param methodResponse the default ApiResponse for the endpoint
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void handleMethodResponse(Operation operation,
|
|
||||||
Map<String, Schema> schemas,
|
|
||||||
CodegenOperation op,
|
|
||||||
ApiResponse methodResponse) {
|
|
||||||
handleMethodResponse(operation, schemas, op, methodResponse, Collections.<String, String>emptyMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set op's returnBaseType, returnType, examples etc.
|
|
||||||
*
|
|
||||||
* @param operation endpoint Operation
|
|
||||||
* @param schemas a map of the schemas in the openapi spec
|
|
||||||
* @param op endpoint CodegenOperation
|
|
||||||
* @param methodResponse the default ApiResponse for the endpoint
|
|
||||||
* @param importMappings mappings of external types to be omitted by unaliasing
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void handleMethodResponse(Operation operation,
|
|
||||||
Map<String, Schema> schemas,
|
|
||||||
CodegenOperation op,
|
|
||||||
ApiResponse methodResponse,
|
|
||||||
Map<String, String> importMappings) {
|
|
||||||
// we have a custom version of this method to handle endpoints that return models where
|
|
||||||
// type != object the model has validations and/or enums
|
|
||||||
// we do this by invoking our custom fromResponse method to create defaultResponse
|
|
||||||
// which we then use to set op.returnType and op.returnBaseType
|
|
||||||
CodegenResponse defaultResponse = fromResponse("defaultResponse", methodResponse);
|
|
||||||
Schema responseSchema = ModelUtils.unaliasSchema(this.openAPI, ModelUtils.getSchemaFromResponse(methodResponse), importMappings);
|
|
||||||
|
|
||||||
if (responseSchema != null) {
|
|
||||||
op.returnBaseType = defaultResponse.baseType;
|
|
||||||
|
|
||||||
// generate examples
|
|
||||||
String exampleStatusCode = "200";
|
|
||||||
for (String key : operation.getResponses().keySet()) {
|
|
||||||
if (operation.getResponses().get(key) == methodResponse && !key.equals("default")) {
|
|
||||||
exampleStatusCode = key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
op.examples = new ExampleGenerator(schemas, this.openAPI).generateFromResponseSchema(exampleStatusCode, responseSchema, getProducesInfo(this.openAPI, operation));
|
|
||||||
op.defaultResponse = toDefaultValue(responseSchema);
|
|
||||||
op.returnType = defaultResponse.dataType;
|
|
||||||
op.hasReference = schemas.containsKey(op.returnBaseType);
|
|
||||||
|
|
||||||
// lookup discriminator
|
|
||||||
Schema schema = schemas.get(op.returnBaseType);
|
|
||||||
if (schema != null) {
|
|
||||||
CodegenModel cmod = fromModel(op.returnBaseType, schema);
|
|
||||||
op.discriminator = cmod.discriminator;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultResponse.isListContainer) {
|
|
||||||
op.isListContainer = true;
|
|
||||||
} else if (defaultResponse.isMapContainer) {
|
|
||||||
op.isMapContainer = true;
|
|
||||||
} else {
|
|
||||||
op.returnSimpleType = true;
|
|
||||||
}
|
|
||||||
if (languageSpecificPrimitives().contains(op.returnBaseType) || op.returnBaseType == null) {
|
|
||||||
op.returnTypeIsPrimitive = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addHeaders(methodResponse, op.responseHeaders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the sanitized variable name for enum
|
* Return the sanitized variable name for enum
|
||||||
*
|
*
|
||||||
* @param value enum variable name
|
* @param value enum variable name
|
||||||
* @param datatype data type
|
* @param datatype data type
|
||||||
* @return the sanitized variable name for enum
|
* @return the sanitized variable name for enum
|
||||||
*/
|
*/
|
||||||
public String toEnumVarName(String value, String datatype) {
|
public String toEnumVarName(String value, String datatype) {
|
||||||
// our enum var names are keys in a python dict, so change spaces to underscores
|
// our enum var names are keys in a python dict, so change spaces to underscores
|
||||||
if (value.length() == 0) {
|
if (value.length() == 0) {
|
||||||
@ -710,175 +692,62 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert OAS Model object to Codegen Model object
|
* Convert OAS Model object to Codegen Model object
|
||||||
|
* We have a custom version of this method so we can:
|
||||||
|
* - set the correct regex values for requiredVars + optionalVars
|
||||||
|
* - set model.defaultValue and model.hasRequired per the three use cases defined in this method
|
||||||
*
|
*
|
||||||
* @param name the name of the model
|
* @param name the name of the model
|
||||||
* @param schema OAS Model object
|
* @param sc OAS Model object
|
||||||
* @return Codegen Model object
|
* @return Codegen Model object
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CodegenModel fromModel(String name, Schema schema) {
|
public CodegenModel fromModel(String name, Schema sc) {
|
||||||
// we have a custom version of this function so we can produce
|
CodegenModel cm = super.fromModel(name, sc);
|
||||||
// models for components whose type != object and which have validations and enums
|
if (cm.requiredVars.size() > 0 && (cm.oneOf.size() > 0 || cm.anyOf.size() > 0)) {
|
||||||
// this ensures that:
|
addNullDefaultToOneOfAnyOfReqProps(sc, cm);
|
||||||
// - 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) {
|
|
||||||
for (Map.Entry<String, Schema> entry : propertiesMap.entrySet()) {
|
|
||||||
String schemaPropertyName = entry.getKey();
|
|
||||||
String pythonPropertyName = toVarName(schemaPropertyName);
|
|
||||||
Schema propertySchema = entry.getValue();
|
|
||||||
String ref = propertySchema.get$ref();
|
|
||||||
if (ref == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Schema refSchema = ModelUtils.getReferencedSchema(this.openAPI, propertySchema);
|
|
||||||
String refType = refSchema.getType();
|
|
||||||
if (refType == null || refType.equals("object")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CodegenProperty modelProperty = fromProperty("_fake_name", refSchema);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
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.defaultValue == null) {
|
|
||||||
result.hasRequired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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>>();
|
ArrayList<List<CodegenProperty>> listOfLists = new ArrayList<List<CodegenProperty>>();
|
||||||
listOfLists.add(result.vars);
|
listOfLists.add(cm.requiredVars);
|
||||||
listOfLists.add(result.allVars);
|
listOfLists.add(cm.optionalVars);
|
||||||
listOfLists.add(result.requiredVars);
|
|
||||||
listOfLists.add(result.optionalVars);
|
|
||||||
listOfLists.add(result.readOnlyVars);
|
|
||||||
listOfLists.add(result.readWriteVars);
|
|
||||||
for (List<CodegenProperty> cpList : listOfLists) {
|
for (List<CodegenProperty> cpList : listOfLists) {
|
||||||
for (CodegenProperty cp : cpList) {
|
for (CodegenProperty cp : cpList) {
|
||||||
// set regex values, before it was only done on model.vars
|
// sets regex values
|
||||||
postProcessModelProperty(result, cp);
|
postProcessModelProperty(cm, cp);
|
||||||
// fix references to non-object models
|
|
||||||
if (!propertyToModelName.containsKey(cp.name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cp.isPrimitiveType = false;
|
|
||||||
String modelName = propertyToModelName.get(cp.name);
|
|
||||||
cp.complexType = modelName;
|
|
||||||
cp.dataType = modelName;
|
|
||||||
cp.isEnum = false;
|
|
||||||
cp.hasValidation = false;
|
|
||||||
result.imports.add(modelName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Boolean isNotPythonModelSimpleModel = (ModelUtils.isComposedSchema(sc) || ModelUtils.isObjectSchema(sc) || ModelUtils.isMapSchema(sc));
|
||||||
// if a class has a property of type self, remove the self import from imports
|
if (isNotPythonModelSimpleModel) {
|
||||||
if (result.imports.contains(result.classname)) {
|
return cm;
|
||||||
result.imports.remove(result.classname);
|
|
||||||
}
|
}
|
||||||
|
// Use cases for default values / enums of length one
|
||||||
if (result.requiredVars.size() > 0 && (result.oneOf.size() > 0 || result.anyOf.size() > 0)) {
|
// 1. no default exists
|
||||||
addNullDefaultToOneOfAnyOfReqProps(schema, result);
|
// schema does not contain default
|
||||||
|
// cm.defaultValue unset, cm.hasRequired = true
|
||||||
|
// 2. server has a default
|
||||||
|
// schema contains default
|
||||||
|
// cm.defaultValue set, cm.hasRequired = true
|
||||||
|
// different value here to differentiate between use case 3 below
|
||||||
|
// This defaultValue is used in the client docs only and is not sent to the server
|
||||||
|
// 3. only one value is allowed in an enum
|
||||||
|
// schema does not contain default
|
||||||
|
// cm.defaultValue set, cm.hasRequired = false
|
||||||
|
// because we know what value needs to be set so the user doesn't need to input it
|
||||||
|
// This defaultValue is used in the client and is sent to the server
|
||||||
|
String defaultValue = toDefaultValue(sc);
|
||||||
|
if (sc.getDefault() == null && defaultValue == null) {
|
||||||
|
cm.hasRequired = true;
|
||||||
|
} else if (sc.getDefault() != null) {
|
||||||
|
cm.defaultValue = defaultValue;
|
||||||
|
cm.hasRequired = true;
|
||||||
|
} else if (defaultValue != null && cm.defaultValue == null) {
|
||||||
|
cm.defaultValue = defaultValue;
|
||||||
|
cm.hasRequired = false;
|
||||||
}
|
}
|
||||||
|
return cm;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the OpenAPI type for the property. Use getAlias to handle $ref of primitive type
|
* Returns the python type for the property.
|
||||||
* 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
|
* @param schema property schema
|
||||||
* @return string presentation of the type
|
* @return string presentation of the type
|
||||||
@ -886,102 +755,24 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
@SuppressWarnings("static-method")
|
@SuppressWarnings("static-method")
|
||||||
@Override
|
@Override
|
||||||
public String getSchemaType(Schema schema) {
|
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);
|
String openAPIType = getSingleSchemaType(schema);
|
||||||
if (typeMapping.containsKey(openAPIType)) {
|
if (typeMapping.containsKey(openAPIType)) {
|
||||||
String type = typeMapping.get(openAPIType);
|
String type = typeMapping.get(openAPIType);
|
||||||
if (languageSpecificPrimitives.contains(type)) {
|
return type;
|
||||||
return type;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return toModelName(openAPIType);
|
|
||||||
}
|
}
|
||||||
return openAPIType;
|
return toModelName(openAPIType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getModelName(Schema sc) {
|
public String getModelName(Schema sc) {
|
||||||
Boolean thisModelWillBeMade = modelWillBeMade(sc);
|
if (sc.get$ref() != null) {
|
||||||
Map<String, Schema> schemas = ModelUtils.getSchemas(openAPI);
|
Schema unaliasedSchema = unaliasSchema(sc, importMapping);
|
||||||
for (String thisSchemaName : schemas.keySet()) {
|
if (unaliasedSchema.get$ref() != null) {
|
||||||
Schema thisSchema = schemas.get(thisSchemaName);
|
return toModelName(ModelUtils.getSimpleRef(sc.get$ref()));
|
||||||
if (thisSchema == sc && thisModelWillBeMade) {
|
|
||||||
return toModelName(thisSchemaName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Output the type declaration of the property
|
|
||||||
*
|
|
||||||
* @param schema property schema
|
|
||||||
* @return a string presentation of the property type
|
|
||||||
*/
|
|
||||||
public String getSimpleTypeDeclaration(Schema schema) {
|
|
||||||
String oasType = getSchemaType(schema);
|
|
||||||
if (typeMapping.containsKey(oasType)) {
|
|
||||||
return typeMapping.get(oasType);
|
|
||||||
}
|
|
||||||
return oasType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean modelWillBeMade(Schema s) {
|
|
||||||
// only invoke this on $refed schemas
|
|
||||||
if (ModelUtils.isComposedSchema(s) || ModelUtils.isObjectSchema(s) || ModelUtils.isArraySchema(s) || ModelUtils.isMapSchema(s)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
List<Object> enums = s.getEnum();
|
|
||||||
if (enums != null && !enums.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Boolean hasValidation = (
|
|
||||||
s.getMaxItems() != null ||
|
|
||||||
s.getMinLength() != null ||
|
|
||||||
s.getMinItems() != null ||
|
|
||||||
s.getMultipleOf() != null ||
|
|
||||||
s.getPattern() != null ||
|
|
||||||
s.getMaxLength() != null ||
|
|
||||||
s.getMinimum() != null ||
|
|
||||||
s.getMaximum() != null ||
|
|
||||||
s.getExclusiveMaximum() != null ||
|
|
||||||
s.getExclusiveMinimum() != null ||
|
|
||||||
s.getUniqueItems() != null
|
|
||||||
);
|
|
||||||
if (hasValidation) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a string representation of the Python types for the specified OAS schema.
|
* Return a string representation of the Python types for the specified OAS schema.
|
||||||
* Primitive types in the OAS specification are implemented in Python using the corresponding
|
* Primitive types in the OAS specification are implemented in Python using the corresponding
|
||||||
@ -1011,8 +802,8 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
if (StringUtils.isNotEmpty(p.get$ref())) {
|
if (StringUtils.isNotEmpty(p.get$ref())) {
|
||||||
// The input schema is a reference. If the resolved schema is
|
// The input schema is a reference. If the resolved schema is
|
||||||
// a composed schema, convert the name to a Python class.
|
// a composed schema, convert the name to a Python class.
|
||||||
Schema s = ModelUtils.getReferencedSchema(this.openAPI, p);
|
Schema unaliasedSchema = unaliasSchema(p, importMapping);
|
||||||
if (modelWillBeMade(s)) {
|
if (unaliasedSchema.get$ref() != null) {
|
||||||
String modelName = toModelName(ModelUtils.getSimpleRef(p.get$ref()));
|
String modelName = toModelName(ModelUtils.getSimpleRef(p.get$ref()));
|
||||||
if (referencedModelNames != null) {
|
if (referencedModelNames != null) {
|
||||||
referencedModelNames.add(modelName);
|
referencedModelNames.add(modelName);
|
||||||
@ -1053,7 +844,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
if (ModelUtils.isFileSchema(p)) {
|
if (ModelUtils.isFileSchema(p)) {
|
||||||
return prefix + "file_type" + fullSuffix;
|
return prefix + "file_type" + fullSuffix;
|
||||||
}
|
}
|
||||||
String baseType = getSimpleTypeDeclaration(p);
|
String baseType = getSchemaType(p);
|
||||||
return prefix + baseType + fullSuffix;
|
return prefix + baseType + fullSuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1133,7 +924,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
* @param in input string
|
* @param in input string
|
||||||
* @return quoted string
|
* @return quoted string
|
||||||
*/
|
*/
|
||||||
public String ensureQuotes(String in) {
|
private String ensureQuotes(String in) {
|
||||||
Pattern pattern = Pattern.compile("\r\n|\r|\n");
|
Pattern pattern = Pattern.compile("\r\n|\r|\n");
|
||||||
Matcher matcher = pattern.matcher(in);
|
Matcher matcher = pattern.matcher(in);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
@ -1222,20 +1013,15 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
example = objExample.toString();
|
example = objExample.toString();
|
||||||
}
|
}
|
||||||
if (null != schema.get$ref()) {
|
if (null != schema.get$ref()) {
|
||||||
// $ref case:
|
|
||||||
Map<String, Schema> allDefinitions = ModelUtils.getSchemas(this.openAPI);
|
Map<String, Schema> allDefinitions = ModelUtils.getSchemas(this.openAPI);
|
||||||
String ref = ModelUtils.getSimpleRef(schema.get$ref());
|
String ref = ModelUtils.getSimpleRef(schema.get$ref());
|
||||||
if (allDefinitions != null) {
|
Schema refSchema = allDefinitions.get(ref);
|
||||||
Schema refSchema = allDefinitions.get(ref);
|
if (null == refSchema) {
|
||||||
if (null == refSchema) {
|
LOGGER.warn("Unable to find referenced schema "+schema.get$ref()+"\n");
|
||||||
return fullPrefix + "None" + closeChars;
|
return fullPrefix + "None" + closeChars;
|
||||||
} else {
|
|
||||||
String refModelName = getModelName(refSchema);
|
|
||||||
return toExampleValueRecursive(refModelName, refSchema, objExample, indentationLevel, prefix, exampleLine);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGGER.warn("allDefinitions not defined in toExampleValue!\n");
|
|
||||||
}
|
}
|
||||||
|
String refModelName = getModelName(schema);
|
||||||
|
return toExampleValueRecursive(refModelName, refSchema, objExample, indentationLevel, prefix, exampleLine);
|
||||||
} else if (ModelUtils.isNullType(schema) || isAnyTypeSchema(schema)) {
|
} else if (ModelUtils.isNullType(schema) || isAnyTypeSchema(schema)) {
|
||||||
// The 'null' type is allowed in OAS 3.1 and above. It is not supported by OAS 3.0.x,
|
// 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.
|
// though this tooling supports it.
|
||||||
@ -1537,92 +1323,12 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set<String> imports) {
|
public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set<String> imports) {
|
||||||
CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER);
|
CodegenParameter cp = super.fromFormProperty(name, propertySchema, imports);
|
||||||
|
|
||||||
LOGGER.debug("Debugging fromFormProperty {}: {}", name, propertySchema);
|
|
||||||
CodegenProperty codegenProperty = fromProperty(name, propertySchema);
|
|
||||||
|
|
||||||
ModelUtils.syncValidationProperties(propertySchema, codegenProperty);
|
|
||||||
|
|
||||||
codegenParameter.isFormParam = Boolean.TRUE;
|
|
||||||
codegenParameter.baseName = codegenProperty.baseName;
|
|
||||||
codegenParameter.paramName = toParamName((codegenParameter.baseName));
|
|
||||||
codegenParameter.baseType = codegenProperty.baseType;
|
|
||||||
codegenParameter.dataType = codegenProperty.dataType;
|
|
||||||
codegenParameter.dataFormat = codegenProperty.dataFormat;
|
|
||||||
codegenParameter.description = escapeText(codegenProperty.description);
|
|
||||||
codegenParameter.unescapedDescription = codegenProperty.getDescription();
|
|
||||||
codegenParameter.jsonSchema = Json.pretty(propertySchema);
|
|
||||||
codegenParameter.defaultValue = codegenProperty.getDefaultValue();
|
|
||||||
|
|
||||||
if (codegenProperty.getVendorExtensions() != null && !codegenProperty.getVendorExtensions().isEmpty()) {
|
|
||||||
codegenParameter.vendorExtensions = codegenProperty.getVendorExtensions();
|
|
||||||
}
|
|
||||||
if (propertySchema.getRequired() != null && !propertySchema.getRequired().isEmpty() && propertySchema.getRequired().contains(codegenProperty.baseName)) {
|
|
||||||
codegenParameter.required = Boolean.TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-array/map
|
|
||||||
updateCodegenPropertyEnum(codegenProperty);
|
|
||||||
codegenParameter.isEnum = codegenProperty.isEnum;
|
|
||||||
codegenParameter._enum = codegenProperty._enum;
|
|
||||||
codegenParameter.allowableValues = codegenProperty.allowableValues;
|
|
||||||
|
|
||||||
if (codegenProperty.isEnum) {
|
|
||||||
codegenParameter.datatypeWithEnum = codegenProperty.datatypeWithEnum;
|
|
||||||
codegenParameter.enumName = codegenProperty.enumName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codegenProperty.items != null && codegenProperty.items.isEnum) {
|
|
||||||
codegenParameter.items = codegenProperty.items;
|
|
||||||
codegenParameter.mostInnerItems = codegenProperty.mostInnerItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
// import
|
|
||||||
if (codegenProperty.complexType != null) {
|
|
||||||
imports.add(codegenProperty.complexType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// validation
|
|
||||||
// handle maximum, minimum properly for int/long by removing the trailing ".0"
|
|
||||||
if (ModelUtils.isIntegerSchema(propertySchema)) {
|
|
||||||
codegenParameter.maximum = propertySchema.getMaximum() == null ? null : String.valueOf(propertySchema.getMaximum().longValue());
|
|
||||||
codegenParameter.minimum = propertySchema.getMinimum() == null ? null : String.valueOf(propertySchema.getMinimum().longValue());
|
|
||||||
} else {
|
|
||||||
codegenParameter.maximum = propertySchema.getMaximum() == null ? null : String.valueOf(propertySchema.getMaximum());
|
|
||||||
codegenParameter.minimum = propertySchema.getMinimum() == null ? null : String.valueOf(propertySchema.getMinimum());
|
|
||||||
}
|
|
||||||
|
|
||||||
codegenParameter.exclusiveMaximum = propertySchema.getExclusiveMaximum() == null ? false : propertySchema.getExclusiveMaximum();
|
|
||||||
codegenParameter.exclusiveMinimum = propertySchema.getExclusiveMinimum() == null ? false : propertySchema.getExclusiveMinimum();
|
|
||||||
codegenParameter.maxLength = propertySchema.getMaxLength();
|
|
||||||
codegenParameter.minLength = propertySchema.getMinLength();
|
|
||||||
codegenParameter.pattern = toRegularExpression(propertySchema.getPattern());
|
|
||||||
codegenParameter.maxItems = propertySchema.getMaxItems();
|
|
||||||
codegenParameter.minItems = propertySchema.getMinItems();
|
|
||||||
codegenParameter.uniqueItems = propertySchema.getUniqueItems() == null ? false : propertySchema.getUniqueItems();
|
|
||||||
codegenParameter.multipleOf = propertySchema.getMultipleOf();
|
|
||||||
|
|
||||||
// exclusive* are noop without corresponding min/max
|
|
||||||
if (codegenParameter.maximum != null || codegenParameter.minimum != null ||
|
|
||||||
codegenParameter.maxLength != null || codegenParameter.minLength != null ||
|
|
||||||
codegenParameter.maxItems != null || codegenParameter.minItems != null ||
|
|
||||||
codegenParameter.pattern != null || codegenParameter.multipleOf != null) {
|
|
||||||
codegenParameter.hasValidation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty);
|
|
||||||
Parameter p = new Parameter();
|
Parameter p = new Parameter();
|
||||||
p.setSchema(propertySchema);
|
p.setSchema(propertySchema);
|
||||||
p.setName(codegenParameter.paramName);
|
p.setName(cp.paramName);
|
||||||
setParameterExampleValue(codegenParameter, p);
|
setParameterExampleValue(cp, p);
|
||||||
// setParameterExampleValue(codegenParameter);
|
return cp;
|
||||||
// set nullable
|
|
||||||
setParameterNullable(codegenParameter, codegenProperty);
|
|
||||||
|
|
||||||
//TODO collectionFormat for form parameter not yet supported
|
|
||||||
//codegenParameter.collectionFormat = getCollectionFormat(propertySchema);
|
|
||||||
return codegenParameter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +21,8 @@ import io.swagger.v3.oas.models.OpenAPI;
|
|||||||
import io.swagger.v3.oas.models.Operation;
|
import io.swagger.v3.oas.models.Operation;
|
||||||
import io.swagger.v3.oas.models.media.*;
|
import io.swagger.v3.oas.models.media.*;
|
||||||
import io.swagger.v3.parser.util.SchemaTypeUtil;
|
import io.swagger.v3.parser.util.SchemaTypeUtil;
|
||||||
import java.time.OffsetDateTime;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
import org.openapitools.codegen.*;
|
import org.openapitools.codegen.*;
|
||||||
import org.openapitools.codegen.languages.PythonClientExperimentalCodegen;
|
import org.openapitools.codegen.languages.PythonClientExperimentalCodegen;
|
||||||
import org.openapitools.codegen.utils.ModelUtils;
|
import org.openapitools.codegen.utils.ModelUtils;
|
||||||
@ -341,4 +342,37 @@ public class PythonClientExperimentalTest {
|
|||||||
Assert.assertEquals(importValue, "from models.special_model_name import SpecialModelName");
|
Assert.assertEquals(importValue, "from models.special_model_name import SpecialModelName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(description = "format imports of models containing special characters")
|
||||||
|
public void defaultSettingInPrimitiveModelWithValidations() {
|
||||||
|
final PythonClientExperimentalCodegen codegen = new PythonClientExperimentalCodegen();
|
||||||
|
|
||||||
|
OpenAPI openAPI = TestUtils.createOpenAPI();
|
||||||
|
final Schema noDefault = new ArraySchema()
|
||||||
|
.type("number")
|
||||||
|
.minimum(new BigDecimal("10"));
|
||||||
|
final Schema hasDefault = new Schema()
|
||||||
|
.type("number")
|
||||||
|
.minimum(new BigDecimal("10"));
|
||||||
|
hasDefault.setDefault("15.0");
|
||||||
|
final Schema noDefaultEumLengthOne = new Schema()
|
||||||
|
.type("number")
|
||||||
|
.minimum(new BigDecimal("10"));
|
||||||
|
noDefaultEumLengthOne.setEnum(Arrays.asList("15.0"));
|
||||||
|
openAPI.getComponents().addSchemas("noDefaultModel", noDefault);
|
||||||
|
openAPI.getComponents().addSchemas("hasDefaultModel", hasDefault);
|
||||||
|
openAPI.getComponents().addSchemas("noDefaultEumLengthOneModel", noDefaultEumLengthOne);
|
||||||
|
codegen.setOpenAPI(openAPI);
|
||||||
|
|
||||||
|
final CodegenModel noDefaultModel = codegen.fromModel("noDefaultModel", noDefault);
|
||||||
|
Assert.assertEquals(noDefaultModel.defaultValue, null);
|
||||||
|
Assert.assertEquals(noDefaultModel.hasRequired, true);
|
||||||
|
|
||||||
|
final CodegenModel hasDefaultModel = codegen.fromModel("hasDefaultModel", hasDefault);
|
||||||
|
Assert.assertEquals(hasDefaultModel.defaultValue, "15.0");
|
||||||
|
Assert.assertEquals(hasDefaultModel.hasRequired, true);
|
||||||
|
|
||||||
|
final CodegenModel noDefaultEumLengthOneModel = codegen.fromModel("noDefaultEumLengthOneModel", noDefaultEumLengthOne);
|
||||||
|
Assert.assertEquals(noDefaultEumLengthOneModel.defaultValue, "15.0");
|
||||||
|
Assert.assertEquals(noDefaultEumLengthOneModel.hasRequired, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user