From 98ec0ea46fa07f6d4ddf7b379658145a23370120 Mon Sep 17 00:00:00 2001 From: altro3 Date: Wed, 4 Sep 2024 22:59:39 +0700 Subject: [PATCH] Fix generating models with discriminator (#1732) --- .../AbstractMicronautJavaCodegen.java | 77 +++++++-- .../AbstractMicronautKotlinCodegen.java | 151 +++++++++++++----- .../common/model/model.mustache | 11 +- .../java-micronaut/common/model/pojo.mustache | 33 ++++ .../common/model/pojo.mustache | 16 +- .../JavaMicronautClientCodegenTest.java | 65 +++++++- .../KotlinMicronautClientCodegenTest.java | 79 +++++++++ .../test/resources/3_0/controller-enum2.yml | 2 +- .../src/test/resources/3_0/discirminator2.yml | 85 ++++++++++ 9 files changed, 456 insertions(+), 63 deletions(-) create mode 100644 openapi-generator/src/test/resources/3_0/discirminator2.yml diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java index 64449c23c5..8020214e1c 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java @@ -35,6 +35,8 @@ import org.openapitools.codegen.CodegenConstants; import org.openapitools.codegen.CodegenDiscriminator; import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenModelFactory; +import org.openapitools.codegen.CodegenModelType; import org.openapitools.codegen.CodegenOperation; import org.openapitools.codegen.CodegenParameter; import org.openapitools.codegen.CodegenProperty; @@ -198,6 +200,7 @@ protected AbstractMicronautJavaCodegen() { ); // Set additional properties + additionalProperties.put("useOneOfInterfaces", useOneOfInterfaces); additionalProperties.put("openbrace", "{"); additionalProperties.put("closebrace", "}"); @@ -1240,12 +1243,33 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List requiredVarsWithoutDiscriminator, List requiredParentVarsWithoutDiscriminator, List allVars) { - var parent = model.getParentModel(); + var parent = model.parentModel; var hasParent = parent != null; - allVars.addAll(model.vars); + for (var variable : model.vars) { + if (notContainsProp(variable, allVars)) { + allVars.add(variable); + } + } + var parentIsOneOfInterface = hasParent && Boolean.TRUE.equals(parent.getVendorExtensions().get("x-is-one-of-interface")); for (var v : model.requiredVars) { - if (!isDiscriminator(v, model) && notContainsProp(v, requiredVarsWithoutDiscriminator)) { + if (notContainsProp(v, requiredVarsWithoutDiscriminator) && (!isDiscriminator(v, model) || parentIsOneOfInterface)) { requiredVarsWithoutDiscriminator.add(v); } } @@ -1683,10 +1712,28 @@ private void processParentModel(CodegenModel model, List requir requiredParentVarsWithoutDiscriminator(model, requiredParentVarsWithoutDiscriminator); if (hasParent) { model.parentVars = parent.allVars; - } - if (hasParent) { processParentModel(parent, requiredVarsWithoutDiscriminator, requiredParentVarsWithoutDiscriminator, allVars); } + + if (parentIsOneOfInterface) { + for (var variable : parent.vars) { + if (notContainsProp(variable, model.vars)) { + if (parent.discriminator != null && parent.discriminator.getPropertyName().equals(variable.name)) { + variable.isDiscriminator = true; + variable.isOverridden = true; + } + model.vars.add(variable); + } + } + for (var variable : parent.requiredVars) { + if (notContainsProp(variable, model.requiredVars)) { + model.requiredVars.add(variable); + } + } + model.parentModel = null; + model.parent = null; + model.parentVars = null; + } } private void requiredParentVarsWithoutDiscriminator(CodegenModel model, List requiredParentVarsWithoutDiscriminator) { @@ -1811,7 +1858,7 @@ public String getExampleValue( example = null; } else { if (requiredPropertiesInConstructor) { - StringBuilder builder = new StringBuilder(); + var builder = new StringBuilder(); if (isProperty) { dataType = importMapping.getOrDefault(dataType, modelPackage + '.' + dataType); } @@ -1896,6 +1943,12 @@ public void setDateTimeLibrary(String name) { setDateLibrary(name); } + @Override + public void setUseOneOfInterfaces(Boolean useOneOfInterfaces) { + super.setUseOneOfInterfaces(useOneOfInterfaces); + additionalProperties.put("useOneOfInterfaces", useOneOfInterfaces); + } + @Override public boolean getUseInlineModelResolver() { return false; diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java index d55a46fb33..9de31e3dc2 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java @@ -36,6 +36,8 @@ import org.openapitools.codegen.CodegenConstants; import org.openapitools.codegen.CodegenDiscriminator; import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenModelFactory; +import org.openapitools.codegen.CodegenModelType; import org.openapitools.codegen.CodegenOperation; import org.openapitools.codegen.CodegenParameter; import org.openapitools.codegen.CodegenProperty; @@ -61,6 +63,7 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; @@ -251,6 +254,7 @@ protected AbstractMicronautKotlinCodegen() { ); // Set additional properties + additionalProperties.put("useOneOfInterfaces", useOneOfInterfaces); additionalProperties.put("openbrace", "{"); additionalProperties.put("closebrace", "}"); @@ -308,7 +312,7 @@ protected AbstractMicronautKotlinCodegen() { final CliOption serializationLibraryOpt = CliOption.newString(CodegenConstants.SERIALIZATION_LIBRARY, "Serialization library for model"); serializationLibraryOpt.defaultValue(SerializationLibraryKind.JACKSON.name()); - Map serializationLibraryOptions = new HashMap<>(); + var serializationLibraryOptions = new HashMap(); serializationLibraryOptions.put(SerializationLibraryKind.JACKSON.name(), "Jackson as serialization library"); serializationLibraryOptions.put(SerializationLibraryKind.MICRONAUT_SERDE_JACKSON.name(), "Use micronaut-serialization with Jackson annotations"); serializationLibraryOpt.setEnum(serializationLibraryOptions); @@ -328,8 +332,8 @@ protected AbstractMicronautKotlinCodegen() { "Body", "application" )); +// reservedWords.remove("value"); -// typeMapping = new HashMap<>(); typeMapping.put("string", "String"); typeMapping.put("boolean", "Boolean"); typeMapping.put("integer", "Int"); @@ -1019,16 +1023,36 @@ public String toEnumValue(String value, String datatype) { } @Override - public CodegenModel fromModel(String name, Schema model) { - CodegenModel codegenModel = super.fromModel(name, model); - codegenModel.imports.remove("ApiModel"); - codegenModel.imports.remove("ApiModelProperty"); - if (importMapping.containsKey(codegenModel.dataType) - && !codegenModel.imports.contains(codegenModel.dataType)) { - codegenModel.imports.add(codegenModel.dataType); - } - allModels.put(name, codegenModel); - return codegenModel; + public CodegenModel fromModel(String name, Schema schema) { + CodegenModel model = super.fromModel(name, schema); + if (!model.oneOf.isEmpty()) { + if (useOneOfInterfaces) { + model.vendorExtensions.put("x-is-one-of-interface", true); + } + if (ModelUtils.isTypeObjectSchema(schema)) { + CodegenModel m = CodegenModelFactory.newInstance(CodegenModelType.MODEL); + updateModelForObject(m, schema); + model.vars = m.vars; + model.allVars = m.allVars; + model.requiredVars = m.requiredVars; + model.readWriteVars = m.readWriteVars; + model.optionalVars = m.optionalVars; + model.readOnlyVars = m.readOnlyVars; + model.parentVars = m.parentVars; + model.nonNullableVars = m.nonNullableVars; + model.setRequiredVarsMap(m.getRequiredVarsMap()); + model.mandatory = m.mandatory; + model.allMandatory = m.allMandatory; + } + } + model.imports.remove("ApiModel"); + model.imports.remove("ApiModelProperty"); + if (importMapping.containsKey(model.dataType) + && !model.imports.contains(model.dataType)) { + model.imports.add(model.dataType); + } + allModels.put(name, model); + return model; } /** @@ -1478,13 +1502,13 @@ public Map postProcessAllModels(Map objs) var requiredVars = new ArrayList(); for (var v : model.vars) { v.vendorExtensions.put("hasChildren", model.hasChildren); - if (containsProp(v, requiredVarsWithoutDiscriminator) + if (!notContainsProp(v, requiredVarsWithoutDiscriminator) || (!model.hasChildren || (v.required && !v.isDiscriminator))) { - if (!containsProp(v, requiredVars)) { + if (notContainsProp(v, requiredVars)) { requiredVars.add(v); } } else { - if (!containsProp(v, optionalVars)) { + if (notContainsProp(v, optionalVars)) { optionalVars.add(v); } } @@ -1494,9 +1518,7 @@ public Map postProcessAllModels(Map objs) if (!requiredParentVarsWithoutDiscriminator.isEmpty()) { model.vendorExtensions.put("requiredParentVarsWithoutDiscriminator", requiredParentVarsWithoutDiscriminator); } - if (!requiredVarsWithoutDiscriminator.isEmpty()) { - model.vendorExtensions.put("requiredVarsWithoutDiscriminator", requiredVarsWithoutDiscriminator); - } + model.vendorExtensions.put("requiredVarsWithoutDiscriminator", requiredVarsWithoutDiscriminator); model.vendorExtensions.put("requiredVars", requiredVars); model.vendorExtensions.put("withRequiredOrOptionalVars", !requiredVarsWithoutDiscriminator.isEmpty() || !optionalVars.isEmpty()); model.vendorExtensions.put("optionalVars", optionalVars); @@ -1611,10 +1633,16 @@ private void processParentModel(CodegenModel model, List requir List requiredParentVarsWithoutDiscriminator, List allVars, boolean processParentModel) { - var parent = model.getParentModel(); + var parent = model.parentModel; var hasParent = parent != null; - allVars.addAll(model.vars); + for (var variable : model.vars) { + if (notContainsProp(variable, allVars)) { + allVars.add(variable); + } + } + + var parentIsOneOfInterface = hasParent && Boolean.TRUE.equals(parent.getVendorExtensions().get("x-is-one-of-interface")); if (!processParentModel) { processVar(model, model.vars, requiredVarsWithoutDiscriminator, requiredParentVarsWithoutDiscriminator, processParentModel); @@ -1622,29 +1650,61 @@ private void processParentModel(CodegenModel model, List requir processVar(model, model.requiredVars, requiredVarsWithoutDiscriminator, requiredParentVarsWithoutDiscriminator, processParentModel); requiredParentVarsWithoutDiscriminator(model, requiredParentVarsWithoutDiscriminator); - if (hasParent) { model.parentVars = parent.allVars; - } - if (hasParent) { processParentModel(parent, requiredVarsWithoutDiscriminator, requiredParentVarsWithoutDiscriminator, allVars, true); } + + if (parentIsOneOfInterface) { + for (var variable : parent.vars) { + if (notContainsProp(variable, model.vars)) { + if (parent.discriminator != null && parent.discriminator.getPropertyName().equals(variable.name)) { + variable.isDiscriminator = true; + variable.isOverridden = true; + } + model.vars.add(variable); + } + } + for (var variable : parent.requiredVars) { + if (notContainsProp(variable, model.requiredVars)) { + model.requiredVars.add(variable); + } + } + model.parentModel = null; + model.parent = null; + model.parentVars = null; + } } private void processVar(CodegenModel model, List vars, List requiredVarsWithoutDiscriminator, List requiredParentVarsWithoutDiscriminator, boolean processParentModel) { + + var parent = model.parentModel; + var hasParent = parent != null; + var parentRequiredVars = hasParent + ? (List) parent.vendorExtensions.get("requiredVarsWithoutDiscriminator") + : Collections.emptyList(); + var parentIsOneOfInterface = hasParent && Boolean.TRUE.equals(parent.getVendorExtensions().get("x-is-one-of-interface")); + for (var v : vars) { - if (!isDiscriminator(v, model) && !containsProp(v, requiredVarsWithoutDiscriminator)) { - if (v.isOverridden != null && !v.isOverridden && !v.vendorExtensions.containsKey("overridden") && !containsProp(v, vars)) { - v.isOverridden = true; - v.vendorExtensions.put("overridden", true); + + if (notContainsProp(v, requiredVarsWithoutDiscriminator) && (!isDiscriminator(v, model) || parentIsOneOfInterface)) { + var copyVar = v; + if ((hasParent && !useOneOfInterfaces && !notContainsProp(v, parent.allVars)) + || (v.isOverridden != null && !v.isOverridden && !v.vendorExtensions.containsKey("overridden") && notContainsProp(v, vars))) { + copyVar = v.clone(); + copyVar.isOverridden = true; + copyVar.vendorExtensions.put("overridden", true); } v.vendorExtensions.put("hasChildren", model.hasChildren); - if (model.parentModel == null || !containsProp(v, (List) model.parentModel.vendorExtensions.get("requiredVarsWithoutDiscriminator"))) { - requiredVarsWithoutDiscriminator.add(v); - } - if (processParentModel && containsProp(v, (List) model.vendorExtensions.get("requiredVarsWithoutDiscriminator"))) { - v.isOverridden = true; - v.vendorExtensions.put("overridden", true); + if (!hasParent || notContainsProp(v, parentRequiredVars) || parentIsOneOfInterface) { + if (processParentModel) { + copyVar = v.clone(); + copyVar.isOverridden = true; + } + if (copyVar.isOverridden != null && copyVar.isOverridden) { + copyVar.vendorExtensions.put("overridden", true); + } + requiredVarsWithoutDiscriminator.add(copyVar); } } v.isNullable = v.isNullable || (v.required && v.isReadOnly && isServer()) || !v.required; @@ -1662,23 +1722,28 @@ private void requiredParentVarsWithoutDiscriminator(CodegenModel model, List props) { - if (props == null) { - return false; + private boolean notContainsProp(CodegenProperty prop, List props) { + if (props == null || props.isEmpty()) { + return true; } for (var p : props) { if (prop.name.equals(p.name)) { - return true; + return false; } } - return false; + return true; } private boolean isDiscriminator(CodegenProperty prop, CodegenModel model) { @@ -2054,6 +2119,12 @@ private String firstTitleCase(final String input) { return input.substring(0, 1).toUpperCase(Locale.ROOT) + input.substring(1); } + @Override + public void setUseOneOfInterfaces(Boolean useOneOfInterfaces) { + super.setUseOneOfInterfaces(useOneOfInterfaces); + additionalProperties.put("useOneOfInterfaces", useOneOfInterfaces); + } + @Override public boolean getUseInlineModelResolver() { return false; diff --git a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/model.mustache b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/model.mustache index 45dd266e8e..5774441f32 100644 --- a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/model.mustache +++ b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/model.mustache @@ -21,10 +21,19 @@ import java.io.Serial; import java.io.Serializable; {{/serializableModel}} {{#lombok}} + {{#requiredPropertiesInConstructor}} + {{^vendorExtensions.withRequiredVars}} import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; import lombok.Data; + {{/vendorExtensions.withRequiredVars}} + {{/requiredPropertiesInConstructor}} + {{^requiredPropertiesInConstructor}} +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; + {{/requiredPropertiesInConstructor}} +import lombok.RequiredArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; diff --git a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache index 0a28e7933b..c0059aad38 100644 --- a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache +++ b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache @@ -13,11 +13,29 @@ {{#lombok}} {{#parent}} @EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) {{/parent}} @Accessors(chain = true) + {{#requiredPropertiesInConstructor}} + {{#vendorExtensions.withRequiredVars}} + {{^parent}} +@EqualsAndHashCode +@ToString + {{/parent}} +@Getter +@Setter + {{/vendorExtensions.withRequiredVars}} + {{^vendorExtensions.withRequiredVars}} +@NoArgsConstructor +@AllArgsConstructor +@Data + {{/vendorExtensions.withRequiredVars}} + {{/requiredPropertiesInConstructor}} + {{^requiredPropertiesInConstructor}} @NoArgsConstructor @AllArgsConstructor @Data + {{/requiredPropertiesInConstructor}} {{/lombok}} {{#micronaut_serde_jackson}} @Serdeable @@ -146,6 +164,11 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{#vendorExtensions.requiredParentVarsWithoutDiscriminator.0}}super({{/vendorExtensions.requiredParentVarsWithoutDiscriminator.0}}{{#vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{#vendorExtensions.isServerOrNotReadOnly}}{{^-first}}, {{/-first}}{{name}}{{/vendorExtensions.isServerOrNotReadOnly}}{{/vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{#vendorExtensions.requiredParentVarsWithoutDiscriminator.0}});{{/vendorExtensions.requiredParentVarsWithoutDiscriminator.0}} {{/parent}} {{#vendorExtensions.requiredVars}} + {{#isDiscriminator}} + {{#useOneOfInterfaces}} + this.{{name}} = {{name}}; + {{/useOneOfInterfaces}} + {{/isDiscriminator}} {{^isDiscriminator}} {{#isReadOnly}} {{#vendorExtensions.isServer}} @@ -174,6 +197,16 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{#vendorExtensions.x-extra-annotation}} {{{.}}} {{/vendorExtensions.x-extra-annotation}} + {{#isDiscriminator}} + {{#parent}} + @Override + {{/parent}} + {{^parent}} + {{#isOverridden}} + @Override + {{/isOverridden}} + {{/parent}} + {{/isDiscriminator}} public {{{datatypeWithEnum}}} {{getter}}() { {{#vendorExtensions.x-is-jackson-optional-nullable}} {{#isReadOnly}} diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/pojo.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/pojo.mustache index e0e13f4872..fa7037d3c3 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/pojo.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/pojo.mustache @@ -14,7 +14,7 @@ */ {{#description}} {{#generateSwagger2Annotations}} -@Schema({{#name}}name = "{{name}}", {{/name}}description = "{{{description}}}") +@Schema({{#vendorExtensions.realName}}name = "{{{vendorExtensions.realName}}}", {{/vendorExtensions.realName}}description = "{{{description}}}") {{/generateSwagger2Annotations}} {{/description}} {{#micronaut_serde_jackson}} @@ -53,10 +53,10 @@ {{#vendorExtensions.withInheritance}} {{#vendorExtensions.optionalVars}} {{>common/model/field_annotations}} - {{#vendorExtensions.hasChildren}}open {{/vendorExtensions.hasChildren}}var {{{name}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} = {{#vendorExtensions.defaultValueInit}}{{{.}}}{{/vendorExtensions.defaultValueInit}}{{^vendorExtensions.defaultValueInit}}null{{/vendorExtensions.defaultValueInit}}, + {{#vendorExtensions.overridden}}override {{/vendorExtensions.overridden}}{{^vendorExtensions.overridden}}{{#vendorExtensions.hasChildren}}open {{/vendorExtensions.hasChildren}}{{/vendorExtensions.overridden}}var {{{name}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} = {{#vendorExtensions.defaultValueInit}}{{{.}}}{{/vendorExtensions.defaultValueInit}}{{^vendorExtensions.defaultValueInit}}null{{/vendorExtensions.defaultValueInit}}, {{/vendorExtensions.optionalVars}} {{/vendorExtensions.withInheritance}} -{{/formatNoEmptyLines}}){{#parent}}: {{{parent}}}({{#vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.requiredParentVarsWithoutDiscriminator}}) {{/parent}}{{#vendorExtensions.x-implements}}{{#parent}}, {{/parent}}{{^parent}}: {{/parent}}{{^-first}}, {{/-first}}{{{.}}}{{/vendorExtensions.x-implements}} {{openbrace}} +{{/formatNoEmptyLines}}){{#parent}}: {{{parent}}}({{#vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{{name}}}{{^-last}}, {{/-last}}{{/vendorExtensions.requiredParentVarsWithoutDiscriminator}}) {{/parent}}{{#vendorExtensions.x-implements}}{{#parent}}, {{/parent}}{{^parent}}: {{/parent}}{{^-first}}, {{/-first}}{{{.}}}{{/vendorExtensions.x-implements}} {{openbrace}} {{#vendorExtensions.withInheritance}} @@ -69,21 +69,21 @@ other as {{classname}} {{#vars}} - if ({{name}} != other.{{name}}) return false + if ({{{name}}} != other.{{{name}}}) return false {{/vars}} return true } override fun hashCode(): Int { - var result = {{#parent}}super.hashCode(){{/parent}}{{^parent}}{{#vars.0}}{{name}}{{#isNullable}}?{{/isNullable}}.hashCode(){{#isNullable}} ?: 0{{/isNullable}}{{/vars.0}}{{/parent}} + var result = {{#parent}}super.hashCode(){{/parent}}{{^parent}}{{#vars.0}}{{{name}}}{{#isNullable}}?{{/isNullable}}.hashCode(){{#isNullable}} ?: 0{{/isNullable}}{{/vars.0}}{{/parent}} {{#vars}} {{#parent}} - result = 31 * result + {{#isNullable}}({{/isNullable}}{{name}}{{#isNullable}}?{{/isNullable}}.hashCode(){{#isNullable}} ?: 0){{/isNullable}} + result = 31 * result + {{#isNullable}}({{/isNullable}}{{{name}}}{{#isNullable}}?{{/isNullable}}.hashCode(){{#isNullable}} ?: 0){{/isNullable}} {{/parent}} {{^parent}} {{^-first}} - result = {{#-first}}{{#parent}}31 * result + {{/parent}}{{/-first}}{{^-first}}31 * result + {{/-first}}{{#isNullable}}({{/isNullable}}{{name}}{{#isNullable}}?{{/isNullable}}.hashCode(){{#isNullable}} ?: 0){{/isNullable}} + result = {{#-first}}{{#parent}}31 * result + {{/parent}}{{/-first}}{{^-first}}31 * result + {{/-first}}{{#isNullable}}({{/isNullable}}{{{name}}}{{#isNullable}}?{{/isNullable}}.hashCode(){{#isNullable}} ?: 0){{/isNullable}} {{/-first}} {{/parent}} {{/vars}} @@ -91,7 +91,7 @@ } override fun toString(): String { - return "{{classname}}({{#allVars}}{{name}}='${{name}}'{{^-last}}, {{/-last}}{{/allVars}})" + return "{{classname}}({{#allVars}}{{{vendorExtensions.realName}}}='${{{name}}}'{{^-last}}, {{/-last}}{{/allVars}})" } {{/vendorExtensions.withInheritance}} diff --git a/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java b/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java index e7470b9fe8..bb41f5a8f0 100644 --- a/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java +++ b/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java @@ -575,8 +575,8 @@ void testLombok() { assertFileContains(path + "model/V1ForecastIdGet400Response.java", "import lombok.AllArgsConstructor;", "import lombok.NoArgsConstructor;", - "import lombok.RequiredArgsConstructor;", "import lombok.Data;", + "import lombok.RequiredArgsConstructor;", "import lombok.EqualsAndHashCode;", "import lombok.Getter;", "import lombok.Setter;", @@ -623,4 +623,67 @@ void testInnerEnum() { assertFileContains(path + "model/CustomerCreateDTO.java", "import java.util.function.Function;"); } + + @Test + void testDiscriminatorWithoutUseOneOfInterfaces() { + + var codegen = new JavaMicronautClientCodegen(); + codegen.setUseOneOfInterfaces(false); + String outputPath = generateFiles(codegen, "src/test/resources/3_0/discirminator2.yml", CodegenConstants.MODELS); + String path = outputPath + "src/main/java/org/openapitools/"; + + assertFileContains(path + "model/JsonOp.java", + "private String path;", + "private String op;", + "public JsonOp(String path, String op) {" + ); + + assertFileNotContains(path + "model/JsonOp.java", + "private String value;", + "private String from;" + ); + + assertFileContains(path + "model/OpAdd.java", + "public class OpAdd extends JsonOp {", + "private String value;", + """ + public OpAdd(String path, String op) { + super(path, op); + } + """ + ); + } + + @Test + void testDiscriminatorWithUseOneOfInterfaces() { + + var codegen = new JavaMicronautClientCodegen(); + codegen.setUseOneOfInterfaces(true); + String outputPath = generateFiles(codegen, "src/test/resources/3_0/discirminator2.yml", CodegenConstants.MODELS); + String path = outputPath + "src/main/java/org/openapitools/"; + + assertFileContains(path + "model/JsonOp.java", + "public interface JsonOp {", + "String getOp();" + ); + + assertFileContains(path + "model/OpAdd.java", + "public class OpAdd implements JsonOp {", + "private String value;", + "private String path;", + "protected String op;", + """ + public OpAdd(String path, String op) { + this.path = path; + this.op = op; + } + """, + """ + @Override + public String getOp() { + return op; + } + """ + ); + } } diff --git a/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java b/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java index 232e738f4c..4fc59e1f99 100644 --- a/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java +++ b/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java @@ -651,4 +651,83 @@ void testImplicitHeadersRegex() { "@Header(name = \"Content-Type\", defaultValue = \"application/json\") @Nullable contentType: String? = \"application/json\"" ); } + + @Test + void testDiscriminatorWithoutUseOneOfInterfaces() { + + var codegen = new KotlinMicronautClientCodegen(); + codegen.setUseOneOfInterfaces(false); + String outputPath = generateFiles(codegen, "src/test/resources/3_0/discirminator2.yml", CodegenConstants.MODELS); + String path = outputPath + "src/main/kotlin/org/openapitools/"; + + assertFileContains(path + "model/JsonOp.kt", + """ + open class JsonOp( + @field:NotNull + @field:JsonProperty(JSON_PROPERTY_PATH) + open var path: String, + @field:NotNull + @field:JsonProperty(JSON_PROPERTY_OP) + open var op: String, + ) { + """ + ); + + assertFileNotContains(path + "model/JsonOp.kt", + "var `value`: String? = null", + "var from: String" + ); + + assertFileContains(path + "model/OpAdd.kt", + """ + data class OpAdd( + @field:Nullable + @field:JsonProperty(JSON_PROPERTY_VALUE) + @field:JsonInclude(JsonInclude.Include.USE_DEFAULTS) + var `value`: String? = null, + @field:NotNull + @field:JsonProperty(JSON_PROPERTY_PATH) + override var path: String, + @field:NotNull + @field:JsonProperty(JSON_PROPERTY_OP) + override var op: String, + ): JsonOp(path, op) { + """ + ); + } + + @Test + void testDiscriminatorWithUseOneOfInterfaces() { + + var codegen = new KotlinMicronautClientCodegen(); + codegen.setUseOneOfInterfaces(true); + String outputPath = generateFiles(codegen, "src/test/resources/3_0/discirminator2.yml", CodegenConstants.MODELS); + String path = outputPath + "src/main/kotlin/org/openapitools/"; + + assertFileContains(path + "model/JsonOp.kt", + """ + interface JsonOp { + + val op: String + } + """ + ); + + assertFileContains(path + "model/OpAdd.kt", + """ + data class OpAdd( + @field:Nullable + @field:JsonProperty(JSON_PROPERTY_VALUE) + @field:JsonInclude(JsonInclude.Include.USE_DEFAULTS) + var `value`: String? = null, + @field:NotNull + @field:JsonProperty(JSON_PROPERTY_PATH) + var path: String, + @field:NotNull + @field:JsonProperty(JSON_PROPERTY_OP) + override var op: String, + ): JsonOp { + """ + ); + } } diff --git a/openapi-generator/src/test/resources/3_0/controller-enum2.yml b/openapi-generator/src/test/resources/3_0/controller-enum2.yml index cda32da08d..c2e2d79bb7 100644 --- a/openapi-generator/src/test/resources/3_0/controller-enum2.yml +++ b/openapi-generator/src/test/resources/3_0/controller-enum2.yml @@ -1,6 +1,6 @@ swagger: '2.0' info: - description: Документация FINTECH REST API + description: docs FINTECH REST API version: '1' title: FINTECH contact: { } diff --git a/openapi-generator/src/test/resources/3_0/discirminator2.yml b/openapi-generator/src/test/resources/3_0/discirminator2.yml new file mode 100644 index 0000000000..b98b6ea149 --- /dev/null +++ b/openapi-generator/src/test/resources/3_0/discirminator2.yml @@ -0,0 +1,85 @@ +openapi: 3.0.1 +info: + title: test + description: desc + version: 0.0.1 +paths: + /: + get: + responses: + 200: + description: OK +components: + schemas: + JsonOp: + type: object + discriminator: + propertyName: op + mapping: + add: "#/components/schemas/OpAdd" + remove: "#/components/schemas/OpRemove" + replace: "#/components/schemas/OpReplace" + move: "#/components/schemas/OpMove" + copy: "#/components/schemas/OpCopy" + test: "#/components/schemas/OpTest" + required: + - path + - op + properties: + path: + type: string + format: relative-json-pointer + op: + type: string + oneOf: + - $ref: "#/components/schemas/OpAdd" + - $ref: "#/components/schemas/OpRemove" + - $ref: "#/components/schemas/OpReplace" + - $ref: "#/components/schemas/OpMove" + - $ref: "#/components/schemas/OpCopy" + - $ref: "#/components/schemas/OpTest" + + OpAdd: + allOf: + - $ref: "#/components/schemas/JsonOp" + - type: object + properties: + value: { type: string } + # required: [ value ] + + OpRemove: + allOf: + - $ref: "#/components/schemas/JsonOp" + - type: object + + OpReplace: + allOf: + - $ref: "#/components/schemas/JsonOp" + - type: object + properties: + value: { type: string } + # required: [ value ] + + OpMove: + allOf: + - $ref: "#/components/schemas/JsonOp" + - type: object + properties: + from: { type: string, format: relative-json-pointer } + # required: [ from ] + + OpCopy: + allOf: + - $ref: "#/components/schemas/JsonOp" + - type: object + properties: + from: { type: string, format: relative-json-pointer } + # required: [ from ] + + OpTest: + allOf: + - $ref: "#/components/schemas/JsonOp" + - type: object + properties: + value: { type: string } + # required: [ value ] \ No newline at end of file