Skip to content

Commit

Permalink
Fix generating default values
Browse files Browse the repository at this point in the history
Fixed #1686
  • Loading branch information
altro3 committed Aug 9, 2024
1 parent 018fd5c commit e0179ae
Show file tree
Hide file tree
Showing 11 changed files with 635 additions and 99 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@
*/
package io.micronaut.openapi.generator;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import io.micronaut.openapi.generator.Formatting.ReplaceDotsWithUnderscoreLambda;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.servers.Server;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.atteo.evo.inflector.English;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConstants;
Expand Down Expand Up @@ -81,6 +85,7 @@
import static org.openapitools.codegen.CodegenConstants.MODEL_PACKAGE;
import static org.openapitools.codegen.CodegenConstants.PACKAGE_NAME;
import static org.openapitools.codegen.languages.KotlinClientCodegen.DATE_LIBRARY;
import static org.openapitools.codegen.utils.OnceLogger.once;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;

Expand Down Expand Up @@ -626,12 +631,9 @@ private void maybeSetSwagger() {
if (additionalProperties.containsKey(OPT_GENERATE_SWAGGER_ANNOTATIONS)) {
String value = String.valueOf(additionalProperties.get(OPT_GENERATE_SWAGGER_ANNOTATIONS));
switch (value) {
case OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2, OPT_GENERATE_SWAGGER_ANNOTATIONS_TRUE ->
generateSwaggerAnnotations = OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2;
case OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE ->
generateSwaggerAnnotations = OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE;
default ->
throw new RuntimeException("Value \"" + value + "\" for the " + OPT_GENERATE_SWAGGER_ANNOTATIONS + " parameter is unsupported or misspelled");
case OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2, OPT_GENERATE_SWAGGER_ANNOTATIONS_TRUE -> generateSwaggerAnnotations = OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2;
case OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE -> generateSwaggerAnnotations = OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE;
default -> throw new RuntimeException("Value \"" + value + "\" for the " + OPT_GENERATE_SWAGGER_ANNOTATIONS + " parameter is unsupported or misspelled");
}
}
}
Expand Down Expand Up @@ -828,10 +830,10 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
allowableValues = (List<Object>) m.allowableValues.get("values");
}
example = getExampleValue(m.defaultValue, null, m.classname, true,
allowableValues, null, null, m.requiredVars, false);
allowableValues, null, null, m.requiredVars, false);
} else {
example = getExampleValue(null, null, op.returnType, false, null,
op.returnBaseType, null, null, false);
op.returnBaseType, null, null, false);
}
op.vendorExtensions.put("example", example);
}
Expand Down Expand Up @@ -1026,6 +1028,37 @@ public CodegenParameter fromParameter(Parameter p, Set<String> imports) {
}
parameter.vendorExtensions.put("realName", realName);

Schema parameterSchema;
if (p.getSchema() != null) {
parameterSchema = unaliasSchema(p.getSchema());
} else if (p.getContent() != null) {
Content content = p.getContent();
if (content.size() > 1) {
once(log).warn("Multiple schemas found in content, returning only the first one");
}
Map.Entry<String, MediaType> entry = content.entrySet().iterator().next();
parameterSchema = entry.getValue().getSchema();
} else {
parameterSchema = null;
}
if (parameterSchema != null && parameterSchema.get$ref() != null) {
parameterSchema = openAPI.getComponents().getSchemas().get(parameterSchema.get$ref().substring("#/components/schemas/".length()));
}

String defaultValueInit;
var items = parameter.items;
if (items == null) {
defaultValueInit = calcDefaultValues(null, null, false, parameterSchema).getLeft();
} else {
defaultValueInit = calcDefaultValues(items.datatypeWithEnum, items.dataType, items.getIsEnumOrRef(), parameterSchema).getLeft();
}
if (parameterSchema != null && ModelUtils.isEnumSchema(parameterSchema)) {
defaultValueInit = parameter.dataType + "." + toEnumVarName(parameter.defaultValue, parameter.dataType);
}
if (defaultValueInit != null) {
parameter.vendorExtensions.put("defaultValueInit", defaultValueInit);
}

addStrValueToEnum(parameter.items);

return parameter;
Expand All @@ -1044,6 +1077,24 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo
}
property.vendorExtensions.put("realName", realName);

if (p != null && p.get$ref() != null) {
p = ModelUtils.getSchemaFromRefToSchemaWithProperties(openAPI, p.get$ref());
}

String defaultValueInit;
var items = property.items;
if (items == null) {
defaultValueInit = calcDefaultValues(null, null, false, p).getLeft();
} else {
defaultValueInit = calcDefaultValues(items.datatypeWithEnum, items.dataType, items.getIsEnumOrRef(), p).getLeft();
}
if (p != null && ModelUtils.isEnumSchema(p)) {
defaultValueInit = property.dataType + "." + toEnumVarName(property.defaultValue, property.dataType);
}
if (defaultValueInit != null) {
property.vendorExtensions.put("defaultValueInit", defaultValueInit);
}

return property;
}

Expand Down Expand Up @@ -1091,6 +1142,9 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation

@Override
public String toEnumVarName(String value, String datatype) {
if (value == null) {
return null;
}
String modified;
if (value.isEmpty()) {
modified = "EMPTY";
Expand All @@ -1114,7 +1168,7 @@ public static boolean isNumeric(String str) {
* @param imports The operation imports.
*/
private void processParametersWithAdditionalMappings(List<CodegenParameter> params, Set<String> imports) {
Map<String, ParameterMapping> additionalMappings = new LinkedHashMap<>();
var additionalMappings = new LinkedHashMap<String, ParameterMapping>();
Iterator<CodegenParameter> iter = params.iterator();
while (iter.hasNext()) {
CodegenParameter param = iter.next();
Expand All @@ -1137,22 +1191,23 @@ private void processParametersWithAdditionalMappings(List<CodegenParameter> para
}

for (ParameterMapping mapping : additionalMappings.values()) {
if (mapping.mappedType() != null) {
CodegenParameter newParam = new CodegenParameter();
newParam.paramName = mapping.mappedName();
newParam.required = true;
newParam.isModel = mapping.isValidated();

String typeName = makeSureImported(mapping.mappedType(), imports);
newParam.dataType = typeName;

// Set the paramName if required
if (newParam.paramName == null) {
newParam.paramName = toParamName(typeName);
}
if (mapping.mappedType() == null) {
continue;
}
var newParam = new CodegenParameter();
newParam.paramName = mapping.mappedName();
newParam.required = true;
newParam.isModel = mapping.isValidated();

params.add(newParam);
String typeName = makeSureImported(mapping.mappedType(), imports);
newParam.dataType = typeName;

// Set the paramName if required
if (newParam.paramName == null) {
newParam.paramName = toParamName(typeName);
}

params.add(newParam);
}
}

Expand Down Expand Up @@ -1350,7 +1405,7 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
private void processOneOfModels(CodegenModel model, Collection<ModelsMap> models) {

if (!model.vendorExtensions.containsKey("x-is-one-of-interface")
|| !Boolean.parseBoolean(model.vendorExtensions.get("x-is-one-of-interface").toString())) {
|| !Boolean.parseBoolean(model.vendorExtensions.get("x-is-one-of-interface").toString())) {
return;
}

Expand Down Expand Up @@ -1646,6 +1701,121 @@ public String getExampleValue(
return example;
}

@Override
public String toDefaultValue(CodegenProperty cp, Schema schema) {
if (cp.items != null) {
return calcDefaultValues(cp.items.datatypeWithEnum, cp.items.dataType, cp.items.getIsEnumOrRef(), schema).getRight();
} else {
return calcDefaultValues(null, null, false, schema).getRight();
}
}

private Pair<String, String> calcDefaultValues(String itemsDatatypeWithEnum, String itemsDataType, boolean itemsIsEnumOrRef, Schema schema) {
String defaultValueInit = null;
String defaultValueStr = null;
schema = ModelUtils.getReferencedSchema(this.openAPI, schema);
if (ModelUtils.isBooleanSchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = schema.getDefault().toString();
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isDateSchema(schema)) {
// TODO
defaultValueInit = null;
defaultValueStr = null;
} else if (ModelUtils.isDateTimeSchema(schema)) {
// TODO
defaultValueInit = null;
defaultValueStr = null;
} else if (ModelUtils.isNumberSchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = fixNumberValue(schema.getDefault().toString(), schema);
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isIntegerSchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = fixNumberValue(schema.getDefault().toString(), schema);
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isURISchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = importMapping.get("URI") + ".create(\"" + schema.getDefault() + "\")";
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isArraySchema(schema) && itemsDatatypeWithEnum != null) {
var pair = toArrayDefaultValue(itemsDatatypeWithEnum, itemsDataType, itemsIsEnumOrRef, schema);
defaultValueInit = pair.getLeft();
defaultValueStr = pair.getRight();
} else if (ModelUtils.isStringSchema(schema)) {
if (schema.getDefault() != null) {
String def = schema.getDefault().toString();
if (schema.getEnum() == null) {
defaultValueInit = "\"" + escapeText(def) + "\"";
defaultValueStr = escapeText(def);
} else {
// convert to enum var name later in postProcessModels
defaultValueInit = "\"" + def + "\"";
defaultValueStr = def;
}
}
} else if (ModelUtils.isObjectSchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = super.toDefaultValue(schema);
defaultValueStr = super.toDefaultValue(schema);
}
}

return Pair.of(defaultValueInit, defaultValueStr);
}

private String fixNumberValue(String number, Schema p) {
if (ModelUtils.isFloatSchema(p)) {
return number + "F";
} else if (ModelUtils.isDoubleSchema(p)) {
if (number.contains(".")) {
return number;
}
return number + ".0";
} else if (ModelUtils.isLongSchema(p)) {
return number + "L";
}
return number;
}

// left - initStr, right - defaultStr
private Pair<String, String> toArrayDefaultValue(String itemsDatatypeWithEnum, String itemsDataType, boolean itemsIsEnumOrRef, Schema schema) {
if (schema.getDefault() != null) {
String arrInstantiationType = ModelUtils.isSet(schema) ? "set" : "arrayList";

if (!(schema.getDefault() instanceof ArrayNode def)) {
return Pair.of(null, null);
}
if (def.isEmpty()) {
return Pair.of(arrInstantiationType + "Of()", null);
}

var defaultContent = new StringBuilder();
Schema<?> itemsSchema = ModelUtils.getSchemaItems(schema);
def.elements().forEachRemaining((element) -> {
String defaultValue = element.asText();
if (defaultValue != null) {
if (itemsIsEnumOrRef) {
String className = itemsDatatypeWithEnum;
String enumVarName = toEnumVarName(defaultValue, itemsDataType);
defaultContent.append(className).append(".").append(enumVarName).append(",");
} else {
itemsSchema.setDefault(defaultValue);
defaultValue = calcDefaultValues(itemsDatatypeWithEnum, itemsDataType, itemsIsEnumOrRef, itemsSchema).getRight();
defaultContent.append(defaultValue).append(",");
}
}
});
defaultContent.deleteCharAt(defaultContent.length() - 1); // remove trailing comma
return Pair.of(arrInstantiationType + "Of(" + defaultContent + ")", defaultContent.toString());
}
return Pair.of(null, null);
}

@Override
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
{{#isContainer}}
private {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}{{#required}}{{^requiredPropertiesInConstructor}}{{#vendorExtensions.defaultValueIsNotNull}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/vendorExtensions.defaultValueIsNotNull}}{{/requiredPropertiesInConstructor}}{{/required}};
private {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}{{#required}}{{^requiredPropertiesInConstructor}}{{#vendorExtensions.defaultValueIsNotNull}}{{#vendorExtensions.defaultValueInit}} = {{{.}}}{{/vendorExtensions.defaultValueInit}}{{/vendorExtensions.defaultValueIsNotNull}}{{/requiredPropertiesInConstructor}}{{/required}};
{{/isContainer}}
{{^isContainer}}
{{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}private{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#vendorExtensions.defaultValueIsNotNull}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/vendorExtensions.defaultValueIsNotNull}};
{{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}private{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#vendorExtensions.defaultValueIsNotNull}}{{#vendorExtensions.defaultValueInit}} = {{{.}}}{{/vendorExtensions.defaultValueInit}}{{/vendorExtensions.defaultValueIsNotNull}};
{{/isContainer}}
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{/formatNoEmptyLines}}
Expand Down Expand Up @@ -284,7 +284,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE
{{^vendorExtensions.x-is-jackson-optional-nullable}}
{{^required}}
if ({{name}} == null) {
{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}};
{{name}} = {{#vendorExtensions.defaultValueInit}}{{{.}}}{{/vendorExtensions.defaultValueInit}}{{^vendorExtensions.defaultValueInit}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/vendorExtensions.defaultValueInit}};
}
{{/required}}
{{name}}.add({{name}}Item);
Expand Down Expand Up @@ -313,7 +313,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE
{{^vendorExtensions.x-is-jackson-optional-nullable}}
{{^required}}
if ({{name}} == null) {
{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}};
{{name}} = {{#vendorExtensions.defaultValueInit}} = {{{.}}}{{/vendorExtensions.defaultValueInit}}{{^vendorExtensions.defaultValueInit}}new HashMap<>(){{/vendorExtensions.defaultValueInit}};
}
{{/required}}
{{name}}.put(key, {{name}}Item);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#isCookieParam}}@CookieValue({{#defaultValue}}value = {{/defaultValue}}"{{baseName}}"{{#defaultValue}}, defaultValue = "{{defaultValue}}"{{/defaultValue}}) {{>common/params/validation}}{{>client/params/type}}{{{paramName}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isCookieParam}}
{{#isCookieParam}}@CookieValue({{#defaultValue}}value = {{/defaultValue}}"{{baseName}}"{{#defaultValue}}, defaultValue = "{{defaultValue}}"{{/defaultValue}}) {{>common/params/validation}}{{>client/params/type}}{{{paramName}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{#vendorExtensions.defaultValueInit}} = {{{vendorExtensions.defaultValueInit}}}{{/vendorExtensions.defaultValueInit}}{{/isCookieParam}}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#isHeaderParam}}@Header({{#defaultValue}}name = {{/defaultValue}}"{{baseName}}"{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) {{>common/params/validation}}{{>client/params/type}}{{{paramName}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isHeaderParam}}
{{#isHeaderParam}}@Header({{#defaultValue}}name = {{/defaultValue}}"{{baseName}}"{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) {{>common/params/validation}}{{>client/params/type}}{{{paramName}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{#vendorExtensions.defaultValueInit}} = {{{vendorExtensions.defaultValueInit}}}{{/vendorExtensions.defaultValueInit}}{{/isHeaderParam}}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#isPathParam}}@PathVariable({{#defaultValue}}name = {{/defaultValue}}"{{baseName}}"{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) {{>common/params/validation}}{{>client/params/type}}{{{paramName}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isPathParam}}
{{#isPathParam}}@PathVariable({{#defaultValue}}name = {{/defaultValue}}"{{baseName}}"{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) {{>common/params/validation}}{{>client/params/type}}{{{paramName}}}: {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{#vendorExtensions.defaultValueInit}} = {{{vendorExtensions.defaultValueInit}}}{{/vendorExtensions.defaultValueInit}}{{/isPathParam}}
Loading

0 comments on commit e0179ae

Please sign in to comment.