diff --git a/modules/openapi-generator-cli/pom.xml b/modules/openapi-generator-cli/pom.xml index 38fa43403a35..d32fd1b85ee0 100644 --- a/modules/openapi-generator-cli/pom.xml +++ b/modules/openapi-generator-cli/pom.xml @@ -4,7 +4,7 @@ org.openapitools openapi-generator-project - 5.0.0-SNAPSHOT + 5.0.0-SYM ../.. diff --git a/modules/openapi-generator-core/pom.xml b/modules/openapi-generator-core/pom.xml index dc45dda9eea3..632e83e29031 100644 --- a/modules/openapi-generator-core/pom.xml +++ b/modules/openapi-generator-core/pom.xml @@ -6,7 +6,7 @@ openapi-generator-project org.openapitools - 5.0.0-SNAPSHOT + 5.0.0-SYM ../.. diff --git a/modules/openapi-generator-gradle-plugin/pom.xml b/modules/openapi-generator-gradle-plugin/pom.xml index 004badbb4261..8677ba1184f5 100644 --- a/modules/openapi-generator-gradle-plugin/pom.xml +++ b/modules/openapi-generator-gradle-plugin/pom.xml @@ -4,7 +4,7 @@ org.openapitools openapi-generator-project - 5.0.0-SNAPSHOT + 5.0.0-SYM ../.. diff --git a/modules/openapi-generator-maven-plugin/pom.xml b/modules/openapi-generator-maven-plugin/pom.xml index 2e41d33653cb..83a726149492 100644 --- a/modules/openapi-generator-maven-plugin/pom.xml +++ b/modules/openapi-generator-maven-plugin/pom.xml @@ -5,7 +5,7 @@ org.openapitools openapi-generator-project - 5.0.0-SNAPSHOT + 5.0.0-SYM ../.. diff --git a/modules/openapi-generator-online/pom.xml b/modules/openapi-generator-online/pom.xml index c158dd879f34..50b9fa50be28 100644 --- a/modules/openapi-generator-online/pom.xml +++ b/modules/openapi-generator-online/pom.xml @@ -4,7 +4,7 @@ org.openapitools openapi-generator-project - 5.0.0-SNAPSHOT + 5.0.0-SYM ../.. diff --git a/modules/openapi-generator/pom.xml b/modules/openapi-generator/pom.xml index 0262582d8845..c7bdede60805 100644 --- a/modules/openapi-generator/pom.xml +++ b/modules/openapi-generator/pom.xml @@ -4,7 +4,7 @@ org.openapitools openapi-generator-project - 5.0.0-SNAPSHOT + 5.0.0-SYM ../.. 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 fd949a0e9a9d..79a00524192c 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 @@ -804,26 +804,18 @@ public void preprocessOpenAPI(OpenAPI openAPI) { // 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); @@ -833,6 +825,23 @@ 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 @SuppressWarnings("unused") public void processOpenAPI(OpenAPI openAPI) { @@ -4610,13 +4619,13 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera * of the 'additionalProperties' keyword. Some language generator use class inheritance * to implement additional properties. For example, in Java the generated model class * has 'extends HashMap' to represent the additional properties. - * + * * TODO: it's not a good idea to use single class inheritance to implement * additionalProperties. That may work for non-composed schemas, but that does not * work for composed 'allOf' schemas. For example, in Java, if additionalProperties * is set to true (which it should be by default, per OAS spec), then the generated * code has extends HashMap. That wouldn't work for composed 'allOf' schemas. - * + * * @param model the codegen representation of the OAS schema. * @param name the name of the model. * @param schema the input OAS schema. @@ -5847,7 +5856,7 @@ private 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"; + codegenModelName = getCodegenModelName(codegenProperty); codegenModelDescription = "UNKNOWN_DESCRIPTION"; } @@ -6062,6 +6071,10 @@ private void addJsonSchemaForBodyRequestInCaseItsNotPresent(CodegenParameter cod codegenParameter.jsonSchema = Json.pretty(body); } + protected String getCodegenModelName(CodegenProperty codegenProperty) { + return "UNKNOWN_BASE_TYPE"; + } + protected void addOption(String key, String description, String defaultValue) { CliOption option = new CliOption(key, description); if (defaultValue != null) @@ -6471,7 +6484,7 @@ protected boolean isFreeFormObject(Schema schema) { /** * Returns the additionalProperties Schema for the specified input schema. - * + * * The additionalProperties keyword is used to control the handling of additional, undeclared * properties, that is, properties whose names are not listed in the properties keyword. * The additionalProperties keyword may be either a boolean or an object. @@ -6479,7 +6492,7 @@ protected boolean isFreeFormObject(Schema schema) { * By default when the additionalProperties keyword is not specified in the input schema, * any additional properties are allowed. This is equivalent to setting additionalProperties * to the boolean value True or setting additionalProperties: {} - * + * * @param schema the input schema that may or may not have the additionalProperties keyword. * @return the Schema of the additionalProperties. The null value is returned if no additional * properties are allowed. diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index 9430b4e7f4b8..f61fed93ba38 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -61,6 +61,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code public static final String BOOLEAN_GETTER_PREFIX = "booleanGetterPrefix"; public static final String ADDITIONAL_MODEL_TYPE_ANNOTATIONS = "additionalModelTypeAnnotations"; public static final String DISCRIMINATOR_CASE_SENSITIVE = "discriminatorCaseSensitive"; + public static final String USE_ONEOF_INTERFACES = "useOneOfInterfaces"; protected String dateLibrary = "threetenbp"; protected boolean supportAsync = false; @@ -197,6 +198,7 @@ public AbstractJavaCodegen() { cliOptions.add(CliOption.newBoolean(DISCRIMINATOR_CASE_SENSITIVE, "Whether the discriminator value lookup should be case-sensitive or not. This option only works for Java API client", discriminatorCaseSensitive)); cliOptions.add(CliOption.newBoolean(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC, this.isHideGenerationTimestamp())); cliOptions.add(CliOption.newBoolean(WITH_XML, "whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)")); + cliOptions.add(CliOption.newBoolean(USE_ONEOF_INTERFACES, "Generate interfaces for OneOf types")); CliOption dateLibrary = new CliOption(DATE_LIBRARY, "Option. Date library to use").defaultValue(this.getDateLibrary()); Map dateOptions = new HashMap<>(); @@ -540,6 +542,11 @@ public void processOpts() { } else if (dateLibrary.equals("legacy")) { additionalProperties.put("legacyDates", "true"); } + + if (additionalProperties.containsKey(USE_ONEOF_INTERFACES)) { + useOneOfInterfaces = Boolean.parseBoolean(additionalProperties.get(USE_ONEOF_INTERFACES).toString()); + addOneOfInterfaceImports = useOneOfInterfaces; + } } @Override @@ -1651,7 +1658,7 @@ public void setAdditionalModelTypeAnnotations(final List additionalModel protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) { if (!supportsAdditionalPropertiesWithComposedSchema) { // The additional (undeclared) propertiees are modeled in Java as a HashMap. - // + // // 1. supportsAdditionalPropertiesWithComposedSchema is set to false: // The generated model class extends from the HashMap. That does not work // with composed schemas that also use a discriminator because the model class @@ -1669,4 +1676,36 @@ protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Sc addImport(codegenModel, codegenModel.additionalPropertiesType); } } + + @Override + protected String getCodegenModelName(CodegenProperty codegenProperty) { + return codegenProperty.getComplexType(); + } + + @Override + protected void addOneOfForComposedSchema(Map.Entry stringSchemaEntry, String modelName, + ComposedSchema composedSchema, String nOneOf, OpenAPI openAPI) { + // if this is property schema, we also need to generate the oneOf interface model + addOneOfNameExtension(composedSchema, modelName); + addOneOfInterfaceModel(composedSchema, modelName, openAPI); + } + + @Override + protected void addOneOfForComposedSchemaArray(String nOneOf, String modelName, ComposedSchema items, OpenAPI openAPI) { + addOneOfNameExtension(items, modelName); + addOneOfInterfaceModel(items, modelName, openAPI); + } + + @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/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index 4daf182d0f93..a4ca6e41e294 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; diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache index fbe3a9f1445d..4f703e2d12d9 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/model.mustache @@ -31,11 +31,6 @@ import org.springframework.hateoas.RepresentationModel; {{#models}} {{#model}} -{{#isEnum}} -{{>enumOuterClass}} -{{/isEnum}} -{{^isEnum}} -{{>pojo}} -{{/isEnum}} +{{#isEnum}}{{>enumOuterClass}}{{/isEnum}}{{^isEnum}}{{#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..815256771ae0 --- /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}} +} diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache index 0601e70f6902..4013c288423c 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}}}"){{/description}} {{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}}{{>additionalModelTypeAnnotations}} -public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}}{{^parent}}{{#hateoas}}extends RepresentationModel<{{classname}}> {{/hateoas}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { +public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}{{#hateoas}} extends RepresentationModel<{{classname}}>{{/hateoas}}{{/parent}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{{^vendorExtensions.x-implements}}{{#serializableModel}} implements Serializable{{/serializableModel}}{{/vendorExtensions.x-implements}} { {{#serializableModel}} private static final long serialVersionUID = 1L; @@ -14,7 +14,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}}{{^parent}} {{>enumClass}} {{/isContainer}} {{#isContainer}} - {{#mostInnerItems}} + {{#mostInnerItems}} {{>enumClass}} {{/mostInnerItems}} {{/isContainer}} diff --git a/pom.xml b/pom.xml index dc7ebf7a3ba1..fd877f165bb4 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ pom openapi-generator-project - 5.0.0-SNAPSHOT + 5.0.0-SYM https://github.com/openapitools/openapi-generator