Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix generating models with discriminator #1732

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -198,6 +200,7 @@ protected AbstractMicronautJavaCodegen() {
);

// Set additional properties
additionalProperties.put("useOneOfInterfaces", useOneOfInterfaces);
additionalProperties.put("openbrace", "{");
additionalProperties.put("closebrace", "}");

Expand Down Expand Up @@ -1240,12 +1243,33 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
}

@Override
public CodegenModel fromModel(String name, Schema model) {
CodegenModel codegenModel = super.fromModel(name, model);
codegenModel.imports.remove("ApiModel");
codegenModel.imports.remove("ApiModelProperty");
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");
allModels.put(name, model);
return model;
}

private boolean shouldBeImplicitHeader(CodegenParameter parameter) {
Expand Down Expand Up @@ -1669,24 +1693,47 @@ public boolean isGenerateHardNullable() {
private void processParentModel(CodegenModel model, List<CodegenProperty> requiredVarsWithoutDiscriminator,
List<CodegenProperty> requiredParentVarsWithoutDiscriminator,
List<CodegenProperty> 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);
}
}

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<CodegenProperty> requiredParentVarsWithoutDiscriminator) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -251,6 +254,7 @@ protected AbstractMicronautKotlinCodegen() {
);

// Set additional properties
additionalProperties.put("useOneOfInterfaces", useOneOfInterfaces);
additionalProperties.put("openbrace", "{");
additionalProperties.put("closebrace", "}");

Expand Down Expand Up @@ -308,7 +312,7 @@ protected AbstractMicronautKotlinCodegen() {

final CliOption serializationLibraryOpt = CliOption.newString(CodegenConstants.SERIALIZATION_LIBRARY, "Serialization library for model");
serializationLibraryOpt.defaultValue(SerializationLibraryKind.JACKSON.name());
Map<String, String> serializationLibraryOptions = new HashMap<>();
var serializationLibraryOptions = new HashMap<String, String>();
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);
Expand All @@ -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");
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -1478,13 +1502,13 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
var requiredVars = new ArrayList<CodegenProperty>();
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);
}
}
Expand All @@ -1494,9 +1518,7 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> 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);
Expand Down Expand Up @@ -1611,40 +1633,78 @@ private void processParentModel(CodegenModel model, List<CodegenProperty> requir
List<CodegenProperty> requiredParentVarsWithoutDiscriminator,
List<CodegenProperty> 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);
}
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<CodegenProperty> vars, List<CodegenProperty> requiredVarsWithoutDiscriminator, List<CodegenProperty> requiredParentVarsWithoutDiscriminator, boolean processParentModel) {

var parent = model.parentModel;
var hasParent = parent != null;
var parentRequiredVars = hasParent
? (List<CodegenProperty>) parent.vendorExtensions.get("requiredVarsWithoutDiscriminator")
: Collections.<CodegenProperty>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<CodegenProperty>) model.parentModel.vendorExtensions.get("requiredVarsWithoutDiscriminator"))) {
requiredVarsWithoutDiscriminator.add(v);
}
if (processParentModel && containsProp(v, (List<CodegenProperty>) 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;
Expand All @@ -1662,23 +1722,28 @@ private void requiredParentVarsWithoutDiscriminator(CodegenModel model, List<Cod
boolean isDiscriminator = isDiscriminator(v, model);
if (v.required && !isDiscriminator) {
v.vendorExtensions.put("isServerOrNotReadOnly", !v.isReadOnly || isServer());
if (!containsProp(v, requiredParentVarsWithoutDiscriminator)) {
requiredParentVarsWithoutDiscriminator.add(v);
if (notContainsProp(v, requiredParentVarsWithoutDiscriminator)) {
var childVar = v.clone();
if (!useOneOfInterfaces) {
childVar.isOverridden = true;
childVar.vendorExtensions.put("overridden", true);
}
requiredParentVarsWithoutDiscriminator.add(childVar);
}
}
}
}

private boolean containsProp(CodegenProperty prop, List<CodegenProperty> props) {
if (props == null) {
return false;
private boolean notContainsProp(CodegenProperty prop, List<CodegenProperty> 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) {
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading