diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index c38076d2e29d..a6f15cfef614 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -816,7 +816,7 @@ public void postProcessParameter(CodegenParameter parameter) { @Override @SuppressWarnings("unused") public void preprocessOpenAPI(OpenAPI openAPI) { - if (useOneOfInterfaces) { + if (useOneOfInterfaces && openAPI.getComponents() != null) { // we process the openapi schema here to find oneOf schemas and create interface models for them Map schemas = new HashMap(openAPI.getComponents().getSchemas()); if (schemas == null) { @@ -866,28 +866,21 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } schemas.putAll(propertySchemas); + // go through all gathered schemas and add them as interfaces to be created for (Map.Entry e : schemas.entrySet()) { - String n = toModelName(e.getKey()); - Schema s = e.getValue(); - String nOneOf = toModelName(n + "OneOf"); - if (ModelUtils.isComposedSchema(s)) { - if (e.getKey().contains("/")) { - // if this is property schema, we also need to generate the oneOf interface model - addOneOfNameExtension((ComposedSchema) s, nOneOf); - addOneOfInterfaceModel((ComposedSchema) s, nOneOf, openAPI); - } else { - // else this is a component schema, so we will just use that as the oneOf interface model - addOneOfNameExtension((ComposedSchema) s, n); - } - } else if (ModelUtils.isArraySchema(s)) { - Schema items = ((ArraySchema) s).getItems(); + String modelName = toModelName(e.getKey()); + Schema schema = e.getValue(); + String nOneOf = toModelName(modelName + "OneOf"); + if (ModelUtils.isComposedSchema(schema)) { + addOneOfForComposedSchema(e, modelName, (ComposedSchema) schema, nOneOf, openAPI); + } else if (ModelUtils.isArraySchema(schema)) { + Schema items = ((ArraySchema) schema).getItems(); if (ModelUtils.isComposedSchema(items)) { - addOneOfNameExtension((ComposedSchema) items, nOneOf); - addOneOfInterfaceModel((ComposedSchema) items, nOneOf, openAPI); + addOneOfForComposedSchemaArray(nOneOf, modelName, (ComposedSchema) items, openAPI); } - } else if (ModelUtils.isMapSchema(s)) { - Schema addProps = getAdditionalProperties(s); + } else if (ModelUtils.isMapSchema(schema)) { + Schema addProps = getAdditionalProperties(schema); if (addProps != null && ModelUtils.isComposedSchema(addProps)) { addOneOfNameExtension((ComposedSchema) addProps, nOneOf); addOneOfInterfaceModel((ComposedSchema) addProps, nOneOf, openAPI); @@ -897,6 +890,24 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } + protected void addOneOfForComposedSchemaArray(String nOneOf, String modelName, + ComposedSchema items, OpenAPI openAPI) { + addOneOfNameExtension(items, nOneOf); + addOneOfInterfaceModel(items, nOneOf, openAPI); + } + + protected void addOneOfForComposedSchema(Entry stringSchemaEntry, String modelName, ComposedSchema composedSchema, + String nOneOf, OpenAPI openAPI) { + if (stringSchemaEntry.getKey().contains("/")) { + // if this is property schema, we also need to generate the oneOf interface model + addOneOfNameExtension(composedSchema, nOneOf); + addOneOfInterfaceModel(composedSchema, nOneOf, openAPI); + } else { + // else this is a component schema, so we will just use that as the oneOf interface model + addOneOfNameExtension(composedSchema, modelName); + } + } + // override with any special handling of the entire OpenAPI spec document @Override @SuppressWarnings("unused") @@ -6192,8 +6203,8 @@ protected void addBodyModelSchema(CodegenParameter codegenParameter, String name "'application/x-www-form-urlencoded' or 'multipart/?'"); LOGGER.warn("schema: {}", schema); LOGGER.warn("codegenModel is null. Default to UNKNOWN_BASE_TYPE"); - codegenModelName = "UNKNOWN_BASE_TYPE"; codegenModelDescription = "UNKNOWN_DESCRIPTION"; + codegenModelName = getCodegenModelName(codegenProperty); } if (StringUtils.isEmpty(bodyParameterName)) { @@ -6399,6 +6410,14 @@ public CodegenParameter fromRequestBody(RequestBody body, Set imports, S return codegenParameter; } + protected void addAdditionalImports(Set imports, String complexType) { + imports.add(complexType); + } + + protected String getCodegenModelName(CodegenProperty codegenProperty) { + return "UNKNOWN_BASE_TYPE"; + } + private void addVarsRequiredVarsAdditionalProps(Schema schema, IJsonSchemaValidationProperties property){ setAddProps(schema, property); if (!"object".equals(schema.getType())) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index b0db9a4c124d..5685667b3165 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -21,6 +21,8 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.media.ComposedSchema; +import io.swagger.v3.oas.models.media.Schema; import org.apache.commons.lang3.tuple.Pair; import org.openapitools.codegen.*; import org.openapitools.codegen.languages.features.BeanValidationFeatures; @@ -38,6 +40,7 @@ import java.util.*; import java.util.regex.Matcher; import java.util.stream.Collectors; +import java.util.Map.Entry; import static org.apache.commons.lang3.StringUtils.isNotEmpty; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -234,6 +237,7 @@ public void processOpts() { super.processOpts(); + useOneOfInterfaces = true; // clear model and api doc template as this codegen // does not support auto-generated markdown doc at the moment //TODO: add doc templates @@ -577,6 +581,27 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } + @Override + protected void addOneOfForComposedSchema(Entry stringSchemaEntry, String modelName, ComposedSchema composedSchema, + String nOneOf, OpenAPI openAPI) { + addOneOfNameExtension(composedSchema, modelName); + addOneOfInterfaceModel(composedSchema, modelName, openAPI); + } + @Override + protected void addOneOfForComposedSchemaArray(String nOneOf, String modelName, + ComposedSchema composedSchema, OpenAPI openAPI) { + addOneOfNameExtension(composedSchema, modelName); + addOneOfInterfaceModel(composedSchema, modelName, openAPI); + } + + @Override + protected String getCodegenModelName(CodegenProperty codegenProperty) { + return codegenProperty.getComplexType(); + } + + @Override + protected void addAdditionalImports(Set imports, String complexType) { } + @Override public Map postProcessOperationsWithModels(Map objs, List allModels) { Map operations = (Map) objs.get("operations"); @@ -874,4 +899,18 @@ public void setPerformBeanValidation(boolean performBeanValidation) { public void setUseOptional(boolean useOptional) { this.useOptional = useOptional; } + + @Override + public void addImportsToOneOfInterface(List> imports) { + for (String i : Arrays.asList("JsonSubTypes", "JsonTypeInfo")) { + Map oneImport = new HashMap() {{ + put("import", importMapping.get(i)); + }}; + if (!imports.contains(oneImport)) { + imports.add(oneImport); + } + } + } + } + diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache index 78c3c9ae19d4..8b39ebd13c30 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache @@ -37,7 +37,7 @@ import org.springframework.hateoas.RepresentationModel; {{>enumOuterClass}} {{/isEnum}} {{^isEnum}} -{{>pojo}} +{{#vendorExtensions.x-is-one-of-interface}}{{>oneof_interface}}{{/vendorExtensions.x-is-one-of-interface}}{{^vendorExtensions.x-is-one-of-interface}}{{>pojo}}{{/vendorExtensions.x-is-one-of-interface}} {{/isEnum}} {{/model}} {{/models}} diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/oneof_interface.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/oneof_interface.mustache new file mode 100644 index 000000000000..4500871766c1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaSpring/oneof_interface.mustache @@ -0,0 +1,6 @@ +{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>typeInfoAnnotation}}{{>xmlAnnotation}} +public interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { + {{#discriminator}} + public {{propertyType}} {{propertyGetter}}(); + {{/discriminator}} +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache index df0b98bfaf7f..406f5288d9cb 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache @@ -3,7 +3,7 @@ */{{#description}} @ApiModel(description = "{{{.}}}"){{/description}} {{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}}{{>additionalModelTypeAnnotations}} -public class {{classname}} {{#parent}}extends {{{.}}}{{/parent}}{{^parent}}{{#hateoas}}extends RepresentationModel<{{classname}}> {{/hateoas}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { +public class {{classname}} {{#parent}}extends {{{.}}}{{/parent}}{{^parent}}{{#hateoas}}extends RepresentationModel<{{classname}}> {{/hateoas}}{{/parent}} {{#vendorExtensions.x-implements}}{{#-first}}implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}} { {{#serializableModel}} private static final long serialVersionUID = 1L;