io.micronaut.openapi.generator.AbstractMicronautJavaCodegen Maven / Gradle / Ivy
/*
* Copyright 2017-2023 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.parser.util.SchemaTypeUtil;
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;
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;
import org.openapitools.codegen.CodegenResponse;
import org.openapitools.codegen.IJsonSchemaValidationProperties;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.VendorExtension;
import org.openapitools.codegen.config.GlobalSettings;
import org.openapitools.codegen.languages.AbstractJavaCodegen;
import org.openapitools.codegen.languages.features.BeanValidationFeatures;
import org.openapitools.codegen.languages.features.OptionalFeatures;
import org.openapitools.codegen.meta.features.DocumentationFeature;
import org.openapitools.codegen.meta.features.SecurityFeature;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.security.SecureRandom;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.FORMAT_INT16;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.FORMAT_INT8;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.FORMAT_SHORT;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_BYTE;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_CHAR;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_CHARACTER;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_DOUBLE;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_FLOAT;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_INT;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_LONG;
import static io.micronaut.openapi.generator.MnSchemaTypeUtil.TYPE_SHORT;
import static io.micronaut.openapi.generator.Utils.DEFAULT_BODY_PARAM_NAME;
import static io.micronaut.openapi.generator.Utils.DIVIDE_OPERATIONS_BY_CONTENT_TYPE;
import static io.micronaut.openapi.generator.Utils.EXT_ANNOTATIONS_CLASS;
import static io.micronaut.openapi.generator.Utils.EXT_ANNOTATIONS_FIELD;
import static io.micronaut.openapi.generator.Utils.EXT_ANNOTATIONS_OPERATION;
import static io.micronaut.openapi.generator.Utils.EXT_ANNOTATIONS_SETTER;
import static io.micronaut.openapi.generator.Utils.addStrValueToEnum;
import static io.micronaut.openapi.generator.Utils.isDateType;
import static io.micronaut.openapi.generator.Utils.normalizeExtraAnnotations;
import static io.micronaut.openapi.generator.Utils.processGenericAnnotations;
import static io.micronaut.openapi.generator.Utils.readListOfStringsProperty;
import static io.swagger.v3.parser.util.SchemaTypeUtil.BYTE_FORMAT;
import static io.swagger.v3.parser.util.SchemaTypeUtil.INTEGER_TYPE;
import static org.openapitools.codegen.CodegenConstants.API_PACKAGE;
import static org.openapitools.codegen.CodegenConstants.INVOKER_PACKAGE;
import static org.openapitools.codegen.CodegenConstants.MODEL_PACKAGE;
import static org.openapitools.codegen.utils.OnceLogger.once;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
/**
* Base generator for Micronaut.
*
* @param The generator options builder.
*/
@SuppressWarnings("checkstyle:DesignForExtension")
public abstract class AbstractMicronautJavaCodegen extends AbstractJavaCodegen implements BeanValidationFeatures, OptionalFeatures, MicronautCodeGenerator {
public static final String OPT_TITLE = "title";
public static final String OPT_TEST = "test";
public static final String OPT_TEST_JUNIT = "junit";
public static final String OPT_TEST_SPOCK = "spock";
public static final String OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR = "requiredPropertiesInConstructor";
public static final String OPT_USE_AUTH = "useAuth";
public static final String OPT_USE_LOMBOK = "lombok";
public static final String OPT_USE_PLURAL = "plural";
public static final String OPT_FLUX_FOR_ARRAYS = "fluxForArrays";
public static final String OPT_GENERATED_ANNOTATION = "generatedAnnotation";
public static final String OPT_VISITABLE = "visitable";
public static final String OPT_DATE_LIBRARY_ZONED_DATETIME = "ZONED_DATETIME";
public static final String OPT_DATE_LIBRARY_OFFSET_DATETIME = "OFFSET_DATETIME";
public static final String OPT_DATE_LIBRARY_LOCAL_DATETIME = "LOCAL_DATETIME";
public static final String OPT_DATE_FORMAT = "dateFormat";
public static final String OPT_DATE_TIME_FORMAT = "dateTimeFormat";
public static final String OPT_USE_ENUM_CASE_INSENSITIVE = "useEnumCaseInsensitive";
public static final String OPT_REACTIVE = "reactive";
public static final String OPT_GENERATE_HTTP_RESPONSE_ALWAYS = "generateHttpResponseAlways";
public static final String OPT_GENERATE_HTTP_RESPONSE_WHERE_REQUIRED = "generateHttpResponseWhereRequired";
public static final String OPT_APPLICATION_NAME = "applicationName";
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS = "generateSwaggerAnnotations";
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_1 = "swagger1";
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2 = "swagger2";
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_TRUE = "true";
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE = "false";
public static final String OPT_GENERATE_OPERATION_ONLY_FOR_FIRST_TAG = "generateOperationOnlyForFirstTag";
public static final String OPT_SKIP_SORTING_OPERATIONS = "skipSortingOperations";
public static final String CONTENT_TYPE_APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
public static final String CONTENT_TYPE_APPLICATION_JSON = "application/json";
public static final String CONTENT_TYPE_MULTIPART_FORM_DATA = "multipart/form-data";
public static final String CONTENT_TYPE_ANY = "*/*";
private static final String MONO_CLASS_NAME = "reactor.core.publisher.Mono";
private static final String FLUX_CLASS_NAME = "reactor.core.publisher.Flux";
protected SecureRandom random = new SecureRandom();
protected String title;
protected boolean useOptional;
protected boolean visitable;
protected boolean lombok;
protected boolean fluxForArrays;
protected boolean plural = true;
protected boolean generatedAnnotation = true;
protected String testTool;
protected boolean requiredPropertiesInConstructor = true;
protected boolean reactive;
protected boolean useEnumCaseInsensitive;
protected boolean generateHttpResponseAlways;
protected boolean generateHttpResponseWhereRequired = true;
protected String appName;
protected String dateFormat;
protected String dateTimeFormat;
protected String generateSwaggerAnnotations;
protected boolean generateOperationOnlyForFirstTag;
protected String serializationLibrary = SerializationLibraryKind.MICRONAUT_SERDE_JACKSON.name();
protected List parameterMappings = new ArrayList<>();
protected List responseBodyMappings = new ArrayList<>();
protected Map allModels = new HashMap<>();
private final Logger log = LoggerFactory.getLogger(getClass());
protected AbstractMicronautJavaCodegen() {
// CHECKSTYLE:OFF
// Set all the fields
useBeanValidation = true;
useJakartaEe = true;
useOptional = false;
visitable = false;
testTool = OPT_TEST_JUNIT;
outputFolder = this instanceof JavaMicronautClientCodegen ?
"generated-code/java-micronaut-client" : "generated-code/java-micronaut";
apiPackage = "org.openapitools.api";
modelPackage = "org.openapitools.model";
invokerPackage = "org.openapitools";
artifactId = this instanceof JavaMicronautClientCodegen ?
"openapi-micronaut-client" : "openapi-micronaut";
embeddedTemplateDir = templateDir = "templates/java-micronaut";
apiDocPath = "docs/apis";
modelDocPath = "docs/models";
dateLibrary = OPT_DATE_LIBRARY_ZONED_DATETIME;
reactive = true;
appName = artifactId;
generateSwaggerAnnotations = this instanceof JavaMicronautClientCodegen ? OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE : OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2;
generateOperationOnlyForFirstTag = this instanceof JavaMicronautServerCodegen;
openApiNullable = false;
useOneOfInterfaces = true;
inlineSchemaOption.put("RESOLVE_INLINE_ENUMS", "true");
// CHECKSTYLE:ON
languageSpecificPrimitives.addAll(Set.of(
"char",
"float",
"double",
"byte",
"short",
"int",
"long",
"Byte",
"Short",
"Character"
));
typeMapping.put("char", "Character");
typeMapping.put("byte", "Byte");
GlobalSettings.setProperty(DIVIDE_OPERATIONS_BY_CONTENT_TYPE, "true");
// Set implemented features for user information
modifyFeatureSet(features -> features
.includeDocumentationFeatures(
DocumentationFeature.Readme
)
.securityFeatures(EnumSet.of(
SecurityFeature.ApiKey,
SecurityFeature.BearerToken,
SecurityFeature.BasicAuth,
SecurityFeature.OAuth2_Implicit,
SecurityFeature.OAuth2_AuthorizationCode,
SecurityFeature.OAuth2_ClientCredentials,
SecurityFeature.OAuth2_Password,
SecurityFeature.OpenIDConnect,
SecurityFeature.SignatureAuth,
SecurityFeature.AWSV4Signature
))
);
// Set additional properties
additionalProperties.put("useOneOfInterfaces", useOneOfInterfaces);
additionalProperties.put("openbrace", "{");
additionalProperties.put("closebrace", "}");
// Set client options that will be presented to user
updateOption(INVOKER_PACKAGE, getInvokerPackage());
updateOption(CodegenConstants.ARTIFACT_ID, getArtifactId());
updateOption(CodegenConstants.API_PACKAGE, apiPackage);
updateOption(MODEL_PACKAGE, modelPackage);
cliOptions.add(new CliOption(OPT_TITLE, "Client service name").defaultValue(title));
cliOptions.add(new CliOption(OPT_APPLICATION_NAME, "Micronaut application name (Defaults to the " + CodegenConstants.ARTIFACT_ID + " value)").defaultValue(appName));
cliOptions.add(CliOption.newBoolean(OPT_USE_LOMBOK, "Whether or not to use lombok annotations in generated code", lombok));
cliOptions.add(CliOption.newBoolean(OPT_USE_PLURAL, "Whether or not to use plural for request body parameter name", plural));
cliOptions.add(CliOption.newBoolean(OPT_FLUX_FOR_ARRAYS, "Whether or not to use Flux> instead Mono> for arrays in generated code", fluxForArrays));
cliOptions.add(CliOption.newBoolean(OPT_GENERATED_ANNOTATION, "Generate code with \"@Generated\" annotation", generatedAnnotation));
cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations", useBeanValidation));
cliOptions.add(CliOption.newBoolean(USE_OPTIONAL, "Use Optional container for optional parameters", useOptional));
cliOptions.add(CliOption.newBoolean(OPT_VISITABLE, "Generate visitor for subtypes with a discriminator", visitable));
cliOptions.add(CliOption.newBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR, "Allow only to create models with all the required properties provided in constructor", requiredPropertiesInConstructor));
cliOptions.add(CliOption.newBoolean(OPT_REACTIVE, "Make the responses use Reactor Mono as wrapper", reactive));
cliOptions.add(CliOption.newBoolean(OPT_GENERATE_HTTP_RESPONSE_ALWAYS, "Always wrap the operations response in HttpResponse object", generateHttpResponseAlways));
cliOptions.add(CliOption.newBoolean(OPT_GENERATE_HTTP_RESPONSE_WHERE_REQUIRED, "Wrap the operations response in HttpResponse object where non-200 HTTP status codes or additional headers are defined", generateHttpResponseWhereRequired));
cliOptions.add(CliOption.newBoolean(OPT_GENERATE_OPERATION_ONLY_FOR_FIRST_TAG, "When false, the operation method will be duplicated in each of the tags if multiple tags are assigned to this operation. " +
"If true, each operation will be generated only once in the first assigned tag.", generateOperationOnlyForFirstTag));
cliOptions.add(CliOption.newBoolean(OPT_USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive));
var testToolOption = new CliOption(OPT_TEST, "Specify which test tool to generate files for").defaultValue(testTool);
var testToolOptionMap = new HashMap();
testToolOptionMap.put(OPT_TEST_JUNIT, "Use JUnit as test tool");
testToolOptionMap.put(OPT_TEST_SPOCK, "Use Spock as test tool");
testToolOption.setEnum(testToolOptionMap);
cliOptions.add(testToolOption);
var generateSwaggerAnnotationsOption = new CliOption(OPT_GENERATE_SWAGGER_ANNOTATIONS, "Specify if you want to generate swagger annotations and which version").defaultValue(generateSwaggerAnnotations);
var generateSwaggerAnnotationsOptionMap = new HashMap();
generateSwaggerAnnotationsOptionMap.put(OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2, "Use io.swagger.core.v3:swagger-annotations for annotating operations and schemas");
generateSwaggerAnnotationsOptionMap.put(OPT_GENERATE_SWAGGER_ANNOTATIONS_TRUE, "Equivalent to \"" + OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2 + "\"");
generateSwaggerAnnotationsOptionMap.put(OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE, "Do not generate swagger annotations");
generateSwaggerAnnotationsOption.setEnum(generateSwaggerAnnotationsOptionMap);
cliOptions.add(generateSwaggerAnnotationsOption);
cliOptions.add(new CliOption(OPT_DATE_FORMAT, "Specify the format pattern of date as a string"));
cliOptions.add(new CliOption(OPT_DATE_TIME_FORMAT, "Specify the format pattern of date-time as a string"));
// Modify the DATE_LIBRARY option to only have supported values
cliOptions.stream()
.filter(o -> o.getOpt().equals(DATE_LIBRARY))
.findFirst()
.ifPresent(opt -> {
var valuesEnum = new HashMap();
valuesEnum.put(OPT_DATE_LIBRARY_OFFSET_DATETIME, opt.getEnum().get(OPT_DATE_LIBRARY_OFFSET_DATETIME));
valuesEnum.put(OPT_DATE_LIBRARY_LOCAL_DATETIME, opt.getEnum().get(OPT_DATE_LIBRARY_LOCAL_DATETIME));
opt.setEnum(valuesEnum);
});
final CliOption serializationLibraryOpt = CliOption.newString(CodegenConstants.SERIALIZATION_LIBRARY, "Serialization library for model");
serializationLibraryOpt.defaultValue(SerializationLibraryKind.JACKSON.name());
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);
cliOptions.add(serializationLibraryOpt);
// Add reserved words
var micronautReservedWords = List.of(
// special words
"Object", "List", "File", "OffsetDateTime", "LocalDate", "LocalTime",
"Client", "Format", "QueryValue", "QueryParam", "PathVariable", "Header", "Cookie",
"Authorization", "Body", "application"
);
reservedWords.addAll(micronautReservedWords);
List.of(
"object",
"list",
"file",
"offsetdatetime",
"localdate",
"localtime"
).forEach(reservedWords::remove);
importMapping.put("DateTime", "java.time.Instant");
importMapping.put("LocalDateTime", "java.time.LocalDateTime");
importMapping.put("OffsetDateTime", "java.time.OffsetDateTime");
importMapping.put("ZonedDateTime", "java.time.ZonedDateTime");
importMapping.put("LocalDate", "java.time.LocalDate");
importMapping.put("LocalTime", "java.time.LocalTime");
importMapping.put("Function", "java.util.function.Function");
}
public void setGenerateHttpResponseAlways(boolean generateHttpResponseAlways) {
this.generateHttpResponseAlways = generateHttpResponseAlways;
}
public void setGenerateHttpResponseWhereRequired(boolean generateHttpResponseWhereRequired) {
this.generateHttpResponseWhereRequired = generateHttpResponseWhereRequired;
}
public void setReactive(boolean reactive) {
this.reactive = reactive;
}
public void setTestTool(String testTool) {
this.testTool = testTool;
}
@Override
public void setArtifactId(String artifactId) {
super.setArtifactId(artifactId);
updateOption(CodegenConstants.ARTIFACT_ID, artifactId);
}
@Override
public void setModelPackage(String modelPackage) {
super.setModelPackage(modelPackage);
updateOption(MODEL_PACKAGE, modelPackage);
}
@Override
public void setApiPackage(String apiPackage) {
super.setApiPackage(apiPackage);
updateOption(CodegenConstants.API_PACKAGE, apiPackage);
}
@Override
public void setApiNamePrefix(String apiNamePrefix) {
super.setApiNamePrefix(apiNamePrefix);
updateOption(CodegenConstants.API_NAME_PREFIX, apiNamePrefix);
}
@Override
public void setApiNameSuffix(String apiNameSuffix) {
super.setApiNameSuffix(apiNameSuffix);
updateOption(CodegenConstants.API_NAME_SUFFIX, apiNameSuffix);
}
@Override
public void setModelNamePrefix(String modelNamePrefix) {
super.setModelNamePrefix(modelNamePrefix);
updateOption(CodegenConstants.MODEL_NAME_PREFIX, modelNamePrefix);
}
@Override
public void setModelNameSuffix(String modelNameSuffix) {
super.setModelNameSuffix(modelNameSuffix);
updateOption(CodegenConstants.MODEL_NAME_SUFFIX, modelNameSuffix);
}
@Override
public void setInvokerPackage(String invokerPackage) {
super.setInvokerPackage(invokerPackage);
updateOption(INVOKER_PACKAGE, getInvokerPackage());
}
public void setLombok(boolean lombok) {
this.lombok = lombok;
}
public void setPlural(boolean plural) {
this.plural = plural;
}
public void setFluxForArrays(boolean fluxForArrays) {
this.fluxForArrays = fluxForArrays;
}
public void setGeneratedAnnotation(boolean generatedAnnotation) {
this.generatedAnnotation = generatedAnnotation;
}
@Override
public void processOpts() {
// need it to add ability to set List in `additionalModelTypeAnnotations` property
if (additionalProperties.containsKey(ADDITIONAL_MODEL_TYPE_ANNOTATIONS)) {
setAdditionalModelTypeAnnotations(readListOfStringsProperty(ADDITIONAL_MODEL_TYPE_ANNOTATIONS, additionalProperties));
additionalProperties.remove(ADDITIONAL_MODEL_TYPE_ANNOTATIONS);
}
if (additionalProperties.containsKey(ADDITIONAL_ONE_OF_TYPE_ANNOTATIONS)) {
setAdditionalOneOfTypeAnnotations(readListOfStringsProperty(ADDITIONAL_ONE_OF_TYPE_ANNOTATIONS, additionalProperties));
additionalProperties.remove(ADDITIONAL_ONE_OF_TYPE_ANNOTATIONS);
}
if (additionalProperties.containsKey(ADDITIONAL_ENUM_TYPE_ANNOTATIONS)) {
setAdditionalEnumTypeAnnotations(readListOfStringsProperty(ADDITIONAL_ENUM_TYPE_ANNOTATIONS, additionalProperties));
additionalProperties.remove(ADDITIONAL_ENUM_TYPE_ANNOTATIONS);
}
super.processOpts();
// Get properties
if (additionalProperties.containsKey(OPT_TITLE)) {
title = (String) additionalProperties.get(OPT_TITLE);
}
if (additionalProperties.containsKey(INVOKER_PACKAGE)) {
invokerPackage = (String) additionalProperties.get(INVOKER_PACKAGE);
} else {
additionalProperties.put(INVOKER_PACKAGE, invokerPackage);
}
if (additionalProperties.containsKey(API_PACKAGE)) {
apiPackage = (String) additionalProperties.get(API_PACKAGE);
} else {
additionalProperties.put(API_PACKAGE, apiPackage);
}
if (additionalProperties.containsKey(MODEL_PACKAGE)) {
modelPackage = (String) additionalProperties.get(MODEL_PACKAGE);
} else {
additionalProperties.put(MODEL_PACKAGE, modelPackage);
}
if (additionalProperties.containsKey(OPT_APPLICATION_NAME)) {
appName = (String) additionalProperties.get(OPT_APPLICATION_NAME);
} else {
additionalProperties.put(OPT_APPLICATION_NAME, artifactId);
}
// Get boolean properties
if (additionalProperties.containsKey(USE_BEANVALIDATION)) {
useBeanValidation = convertPropertyToBoolean(USE_BEANVALIDATION);
}
writePropertyBack(USE_BEANVALIDATION, useBeanValidation);
if (additionalProperties.containsKey(OPT_USE_LOMBOK)) {
lombok = convertPropertyToBoolean(OPT_USE_LOMBOK);
}
writePropertyBack(OPT_USE_LOMBOK, lombok);
if (additionalProperties.containsKey(OPT_USE_PLURAL)) {
plural = convertPropertyToBoolean(OPT_USE_PLURAL);
}
writePropertyBack(OPT_USE_PLURAL, plural);
if (additionalProperties.containsKey(OPT_FLUX_FOR_ARRAYS)) {
fluxForArrays = convertPropertyToBoolean(OPT_FLUX_FOR_ARRAYS);
}
writePropertyBack(OPT_FLUX_FOR_ARRAYS, fluxForArrays);
if (additionalProperties.containsKey(OPT_USE_ENUM_CASE_INSENSITIVE)) {
useEnumCaseInsensitive = convertPropertyToBoolean(OPT_USE_ENUM_CASE_INSENSITIVE);
}
writePropertyBack(OPT_USE_ENUM_CASE_INSENSITIVE, useEnumCaseInsensitive);
if (additionalProperties.containsKey(OPT_GENERATED_ANNOTATION)) {
generatedAnnotation = convertPropertyToBoolean(OPT_GENERATED_ANNOTATION);
}
writePropertyBack(OPT_GENERATED_ANNOTATION, generatedAnnotation);
if (additionalProperties.containsKey(USE_OPTIONAL)) {
useOptional = convertPropertyToBoolean(USE_OPTIONAL);
}
writePropertyBack(USE_OPTIONAL, useOptional);
if (additionalProperties.containsKey(OPT_VISITABLE)) {
visitable = convertPropertyToBoolean(OPT_VISITABLE);
}
writePropertyBack(OPT_VISITABLE, visitable);
if (additionalProperties.containsKey(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR)) {
requiredPropertiesInConstructor = convertPropertyToBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR);
}
writePropertyBack(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR, requiredPropertiesInConstructor);
if (additionalProperties.containsKey(OPT_REACTIVE)) {
reactive = convertPropertyToBoolean(OPT_REACTIVE);
}
writePropertyBack(OPT_REACTIVE, reactive);
if (additionalProperties.containsKey(OPT_DATE_FORMAT)) {
dateFormat = (String) additionalProperties.get(OPT_DATE_FORMAT);
}
writePropertyBack(OPT_DATE_FORMAT, dateFormat);
if (additionalProperties.containsKey(OPT_DATE_TIME_FORMAT)) {
dateTimeFormat = (String) additionalProperties.get(OPT_DATE_TIME_FORMAT);
}
writePropertyBack(OPT_DATE_TIME_FORMAT, dateFormat);
if (additionalProperties.containsKey(OPT_GENERATE_HTTP_RESPONSE_ALWAYS)) {
generateHttpResponseAlways = convertPropertyToBoolean(OPT_GENERATE_HTTP_RESPONSE_ALWAYS);
}
writePropertyBack(OPT_GENERATE_HTTP_RESPONSE_ALWAYS, generateHttpResponseAlways);
if (additionalProperties.containsKey(OPT_GENERATE_HTTP_RESPONSE_ALWAYS)) {
generateHttpResponseAlways = convertPropertyToBoolean(OPT_GENERATE_HTTP_RESPONSE_ALWAYS);
}
writePropertyBack(OPT_GENERATE_HTTP_RESPONSE_ALWAYS, generateHttpResponseAlways);
if (additionalProperties.containsKey(OPT_GENERATE_HTTP_RESPONSE_WHERE_REQUIRED)) {
generateHttpResponseWhereRequired = convertPropertyToBoolean(OPT_GENERATE_HTTP_RESPONSE_WHERE_REQUIRED);
}
writePropertyBack(OPT_GENERATE_HTTP_RESPONSE_WHERE_REQUIRED, generateHttpResponseWhereRequired);
if (additionalProperties.containsKey(OPT_GENERATE_OPERATION_ONLY_FOR_FIRST_TAG)) {
generateOperationOnlyForFirstTag = convertPropertyToBoolean(OPT_GENERATE_OPERATION_ONLY_FOR_FIRST_TAG);
}
writePropertyBack(OPT_GENERATE_OPERATION_ONLY_FOR_FIRST_TAG, generateOperationOnlyForFirstTag);
if (additionalProperties.containsKey(USE_JAKARTA_EE)) {
setUseJakartaEe(convertPropertyToBoolean(USE_JAKARTA_EE));
}
writePropertyBack(USE_JAKARTA_EE, useJakartaEe);
writePropertyBack(JAVAX_PACKAGE, useJakartaEe ? "jakarta" : "javax");
convertPropertyToBooleanAndWriteBack(OPT_SKIP_SORTING_OPERATIONS, this::setSkipSortingOperations);
maybeSetTestTool();
writePropertyBack(OPT_TEST, testTool);
if (testTool.equals(OPT_TEST_JUNIT)) {
additionalProperties.put("isTestJunit", true);
} else if (testTool.equals(OPT_TEST_SPOCK)) {
additionalProperties.put("isTestSpock", true);
}
maybeSetSwagger();
if (OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2.equals(generateSwaggerAnnotations)) {
additionalProperties.put("generateSwagger2Annotations", true);
}
if (additionalProperties.containsKey(CodegenConstants.SERIALIZATION_LIBRARY)) {
setSerializationLibrary((String) additionalProperties.get(CodegenConstants.SERIALIZATION_LIBRARY));
}
additionalProperties.put(serializationLibrary.toLowerCase(Locale.US), true);
if (SerializationLibraryKind.MICRONAUT_SERDE_JACKSON.name().equals(serializationLibrary)) {
additionalProperties.put(SerializationLibraryKind.JACKSON.name().toLowerCase(Locale.US), true);
}
// Add all the supporting files
String resourceFolder = projectFolder + "/resources";
supportingFiles.add(new SupportingFile("common/configuration/application.yml.mustache", resourceFolder, "application.yml").doNotOverwrite());
supportingFiles.add(new SupportingFile("common/configuration/logback.xml.mustache", resourceFolder, "logback.xml").doNotOverwrite());
// Use the default java time
switch (dateLibrary) {
case OPT_DATE_LIBRARY_OFFSET_DATETIME -> {
typeMapping.put("DateTime", "OffsetDateTime");
typeMapping.put("date", "LocalDate");
}
case OPT_DATE_LIBRARY_ZONED_DATETIME -> {
typeMapping.put("DateTime", "ZonedDateTime");
typeMapping.put("date", "LocalDate");
}
case OPT_DATE_LIBRARY_LOCAL_DATETIME -> {
typeMapping.put("DateTime", "LocalDateTime");
typeMapping.put("date", "LocalDate");
}
default -> {
}
}
// Add documentation files
modelDocTemplateFiles.clear();
modelDocTemplateFiles.put("common/doc/model_doc.mustache", ".md");
// Add model files
modelTemplateFiles.clear();
modelTemplateFiles.put("common/model/model.mustache", ".java");
// Add test files
modelTestTemplateFiles.clear();
if (testTool.equals(OPT_TEST_JUNIT)) {
modelTestTemplateFiles.put("common/test/model_test.mustache", ".java");
} else if (testTool.equals(OPT_TEST_SPOCK)) {
modelTestTemplateFiles.put("common/test/model_test.groovy.mustache", ".groovy");
}
// Set properties for documentation
final String invokerFolder = (sourceFolder + '/' + invokerPackage).replace(".", "/");
final String apiFolder = (sourceFolder + '/' + apiPackage()).replace('.', '/');
final String modelFolder = (sourceFolder + '/' + modelPackage()).replace('.', '/');
additionalProperties.put("invokerFolder", invokerFolder);
additionalProperties.put("resourceFolder", resourceFolder);
additionalProperties.put("apiFolder", apiFolder);
additionalProperties.put("modelFolder", modelFolder);
additionalProperties.put("formatNoEmptyLines", new Formatting.LineFormatter(0));
additionalProperties.put("formatOneEmptyLine", new Formatting.LineFormatter(1));
additionalProperties.put("formatSingleLine", new Formatting.SingleLineFormatter());
additionalProperties.put("indent", new Formatting.IndentFormatter(4));
}
public void addParameterMappings(List parameterMappings) {
this.parameterMappings.addAll(parameterMappings);
}
public void addResponseBodyMappings(List responseBodyMappings) {
this.responseBodyMappings.addAll(responseBodyMappings);
}
public void addSchemaMapping(Map schemaMapping) {
this.schemaMapping.putAll(schemaMapping);
}
public void addImportMapping(Map importMapping) {
this.importMapping.putAll(importMapping);
}
public void addNameMapping(Map nameMapping) {
this.nameMapping.putAll(nameMapping);
}
public void addTypeMapping(Map typeMapping) {
this.typeMapping.putAll(typeMapping);
}
public void addEnumNameMapping(Map enumNameMapping) {
this.enumNameMapping.putAll(enumNameMapping);
}
public void addModelNameMapping(Map modelNameMapping) {
this.modelNameMapping.putAll(modelNameMapping);
}
public void addInlineSchemaNameMapping(Map inlineSchemaNameMapping) {
this.inlineSchemaNameMapping.putAll(inlineSchemaNameMapping);
}
public void addInlineSchemaOption(Map inlineSchemaOption) {
this.inlineSchemaOption.putAll(inlineSchemaOption);
}
public void addOpenapiNormalizer(Map openapiNormalizer) {
this.openapiNormalizer.putAll(openapiNormalizer);
}
// CHECKSTYLE:OFF
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_1, 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");
}
}
}
private void maybeSetTestTool() {
if (additionalProperties.containsKey(OPT_TEST)) {
switch ((String) additionalProperties.get(OPT_TEST)) {
case OPT_TEST_JUNIT, OPT_TEST_SPOCK -> testTool = (String) additionalProperties.get(OPT_TEST);
default -> throw new RuntimeException("Test tool \"" + additionalProperties.get(OPT_TEST) + "\" is not supported or misspelled.");
}
}
}
// CHECKSTYLE:ON
public String testFileFolder() {
if (testTool.equals(OPT_TEST_SPOCK)) {
return (getOutputDir() + "/src/test/groovy/").replace('/', File.separatorChar);
}
return (getOutputDir() + "/src/test/java/").replace('/', File.separatorChar);
}
public abstract boolean isServer();
@Override
public String apiTestFileFolder() {
return testFileFolder() + apiPackage().replace(".", "/");
}
@Override
public String modelTestFileFolder() {
if (testTool.equals(OPT_TEST_SPOCK)) {
return getOutputDir() + "/src/test/groovy/" + modelPackage().replace('.', File.separatorChar);
}
return getOutputDir() + "/src/test/java/" + modelPackage().replace('.', File.separatorChar);
}
@Override
public String toApiTestFilename(String name) {
if (testTool.equals(OPT_TEST_SPOCK)) {
return toApiName(name) + "Spec";
}
return toApiName(name) + "Test";
}
@Override
public String toModelTestFilename(String name) {
if (testTool.equals(OPT_TEST_SPOCK)) {
return toModelName(name) + "Spec";
}
return toModelName(name) + "Test";
}
@Override
public CodegenParameter fromParameter(Parameter p, Set imports) {
var parameter = super.fromParameter(p, imports);
checkPrimitives(parameter, unaliasSchema(p.getSchema()));
// if name is escaped
var realName = parameter.paramName;
if (realName.startsWith("_") && !parameter.baseName.startsWith("_") && isReservedWord(parameter.baseName)) {
realName = realName.replaceFirst("_", "");
}
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 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, false,
false, false, false, false, false, parameterSchema).getLeft();
} else {
defaultValueInit = calcDefaultValues(items.datatypeWithEnum, items.dataType, items.getIsEnumOrRef(),
parameter.isArray, items.isString, items.isNumeric, items.isFloat, items.isMap, items.isNullable,
parameterSchema).getLeft();
}
if (parameterSchema != null && ModelUtils.isEnumSchema(parameterSchema)) {
defaultValueInit = parameter.dataType + ".fromValue(" + defaultValueInit + ")";
}
if (defaultValueInit != null) {
parameter.vendorExtensions.put("defaultValueInit", defaultValueInit);
}
addStrValueToEnum(parameter.items);
return parameter;
}
@Override
public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
var resp = super.fromResponse(responseCode, response);
checkPrimitives(resp, (Schema) resp.schema);
return resp;
}
@Override
public CodegenProperty fromProperty(String name, Schema schema, boolean required, boolean schemaIsFromAdditionalProperties) {
var property = super.fromProperty(name, schema, required, schemaIsFromAdditionalProperties);
checkPrimitives(property, unaliasSchema(schema));
// if name is escaped
var realName = property.name;
if (realName.startsWith("_") && !property.baseName.startsWith("_") && isReservedWord(property.baseName)) {
realName = realName.replaceFirst("_", "");
property.nameInCamelCase = camelize(realName);
property.nameInSnakeCase = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, property.nameInCamelCase);
// fix for getters and setters for escaped vars as reserved words and started with '_', like this: '_for'
if (!property.getter.startsWith("get_")) {
property.getter = "get" + getterAndSetterCapitalize(property.name);
}
if (!property.setter.startsWith("set_")) {
property.setter = "set" + getterAndSetterCapitalize(property.name);
}
}
property.vendorExtensions.put("realName", realName);
if (schema != null && schema.get$ref() != null) {
schema = ModelUtils.getSchemaFromRefToSchemaWithProperties(openAPI, schema.get$ref());
}
String defaultValueInit;
var items = property.items;
if (items == null) {
defaultValueInit = calcDefaultValues(null, null, false, false,
false, false, false, false, false, schema).getLeft();
} else {
defaultValueInit = calcDefaultValues(items.datatypeWithEnum, items.dataType, items.getIsEnumOrRef(),
property.isArray, items.isString, items.isNumeric, items.isFloat, items.isMap, items.isNullable,
schema).getLeft();
}
if (schema != null && ModelUtils.isEnumSchema(schema)) {
var enumVarName = toEnumVarName(property.defaultValue, property.dataType);
if (enumVarName != null) {
defaultValueInit = property.dataType + "." + enumVarName;
} else {
defaultValueInit = null;
}
}
if (defaultValueInit != null) {
property.vendorExtensions.put("defaultValueInit", defaultValueInit);
}
return property;
}
@Override
public String toEnumVarName(String value, String datatype) {
if (value == null) {
return null;
}
if (enumNameMapping.containsKey(value)) {
return enumNameMapping.get(value);
}
if (value.isEmpty()) {
return "EMPTY";
}
// for symbol, e.g. $, #
if (getSymbolName(value) != null) {
return getSymbolName(value).toUpperCase(Locale.ROOT);
}
if (" ".equals(value)) {
return "SPACE";
}
// number
if ("Int".equalsIgnoreCase(datatype)
|| "Byte".equalsIgnoreCase(datatype)
|| "Short".equalsIgnoreCase(datatype)
|| "Integer".equalsIgnoreCase(datatype)
|| "Long".equalsIgnoreCase(datatype)
|| "Float".equalsIgnoreCase(datatype)
|| "Double".equalsIgnoreCase(datatype)
|| "BigDecimal".equals(datatype)) {
String varName = "NUMBER_" + value;
varName = varName.replaceAll("-", "MINUS_");
varName = varName.replaceAll("\\+", "PLUS_");
varName = varName.replaceAll("\\.", "_DOT_");
return varName;
}
// string
String var = underscore(value.replaceAll("\\W+", "_")).toUpperCase(Locale.ROOT);
if (var.matches("\\d.*")) {
var = "_" + var;
}
return this.toVarName(var);
}
@Override
public String toDefaultValue(CodegenProperty cp, Schema schema) {
if (cp.items != null) {
return calcDefaultValues(cp.items.datatypeWithEnum, cp.items.dataType, cp.items.getIsEnumOrRef(),
cp.isArray, cp.items.isString, cp.items.isNumeric, cp.items.isFloat, cp.items.isMap, cp.items.isNullable,
schema).getRight();
} else {
return calcDefaultValues(null, null, false,
false, false, false, false, false, false, schema).getRight();
}
}
private Pair calcDefaultValues(String itemsDatatypeWithEnum, String itemsDataType, boolean itemsIsEnumOrRef,
boolean isArray, boolean itemsIsString, boolean itemsIsNumeric,
boolean itemsIsFloat, boolean itemsIsMap, boolean isNullable, Schema schema) {
String defaultValueInit = null;
String defaultValueStr = null;
schema = ModelUtils.getReferencedSchema(this.openAPI, schema);
if (ModelUtils.isArraySchema(schema)) {
if (schema.getDefault() == null) {
// nullable or containerDefaultToNull set to true
if (isNullable || containerDefaultToNull) {
return Pair.of(null, null);
}
return getDefaultCollectionType(schema);
}
return arrayDefaultValue(itemsDatatypeWithEnum, itemsDataType, itemsIsEnumOrRef,
isArray, itemsIsString, itemsIsNumeric, itemsIsFloat, itemsIsMap, schema);
} else if (ModelUtils.isMapSchema(schema) && !(ModelUtils.isComposedSchema(schema))) {
if (schema.getProperties() != null && !schema.getProperties().isEmpty()) {
// object is complex object with free-form additional properties
if (schema.getDefault() != null) {
defaultValueInit = super.toDefaultValue(schema);
defaultValueStr = super.toDefaultValue(schema);
}
}
// nullable or containerDefaultToNull set to true
if (isNullable || containerDefaultToNull) {
return Pair.of(null, null);
}
if (ModelUtils.getAdditionalProperties(schema) == null) {
return Pair.of(null, null);
}
defaultValueInit = String.format(Locale.ROOT, "new %s<>()", instantiationTypes().getOrDefault("map", "HashMap"));
defaultValueStr = null;
} else if (ModelUtils.isIntegerSchema(schema)) {
if (schema.getDefault() != null) {
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(schema.getFormat())) {
defaultValueInit = schema.getDefault().toString() + "L";
} else {
defaultValueInit = schema.getDefault().toString();
}
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isNumberSchema(schema)) {
if (schema.getDefault() != null) {
if (SchemaTypeUtil.FLOAT_FORMAT.equals(schema.getFormat())) {
defaultValueInit = schema.getDefault().toString() + "F";
} else if (SchemaTypeUtil.DOUBLE_FORMAT.equals(schema.getFormat())) {
defaultValueInit = schema.getDefault().toString() + "D";
} else {
defaultValueInit = "new BigDecimal(\"" + schema.getDefault().toString() + "\")";
}
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isBooleanSchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = schema.getDefault().toString();
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isURISchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = "URI.create(\"" + escapeText(String.valueOf(schema.getDefault())) + "\")";
defaultValueStr = schema.getDefault().toString();
}
} else if (ModelUtils.isStringSchema(schema)) {
if (schema.getDefault() != null) {
if (schema.getDefault() instanceof Date date) {
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
defaultValueInit = String.format(Locale.ROOT, "LocalDate.parse(\"%s\")", localDate.toString());
defaultValueStr = localDate.toString();
} else if (schema.getDefault() instanceof java.time.OffsetDateTime offsetDateTime) {
defaultValueInit = String.format(Locale.ROOT, "OffsetDateTime.parse(\"%s\", %s)",
offsetDateTime.atZoneSameInstant(ZoneId.systemDefault()),
"java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(java.time.ZoneId.systemDefault())");
defaultValueStr = offsetDateTime.toString();
} else if (schema.getDefault() instanceof UUID) {
defaultValueInit = "UUID.fromString(\"" + schema.getDefault() + "\")";
defaultValueStr = schema.getDefault().toString();
} else {
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);
}
} else if (ModelUtils.isComposedSchema(schema)) {
if (schema.getDefault() != null) {
defaultValueInit = super.toDefaultValue(schema);
defaultValueStr = super.toDefaultValue(schema);
}
}
return Pair.of(defaultValueInit, defaultValueStr);
}
// left - initStr, right - defaultStr
public Pair arrayDefaultValue(String itemsDatatypeWithEnum, String itemsDataType, boolean itemsIsEnumOrRef,
boolean isArray, boolean itemsIsString, boolean itemsIsNumeric,
boolean itemsIsFloat, boolean itemsIsMap, Schema schema) {
if (schema.getDefault() != null) { // has default value
if (isArray) {
List values = new ArrayList<>();
if (schema.getDefault() instanceof ArrayNode arrayNodeDefault) { // array of default values
if (arrayNodeDefault.isEmpty()) { // e.g. default: []
return getDefaultCollectionType(schema);
}
List finalValues = values;
arrayNodeDefault.elements().forEachRemaining((element) -> finalValues.add(element.asText()));
} else if (schema.getDefault() instanceof Collection> defCollection) {
List finalValues = values;
defCollection.forEach((element) -> finalValues.add(String.valueOf(element)));
} else { // single value
values = Collections.singletonList(String.valueOf(schema.getDefault()));
}
String defaultValue;
String defaultValueInit;
if (itemsIsEnumOrRef) { // inline or ref enum
var defaultValues = new ArrayList();
for (String value : values) {
var enumVarName = toEnumVarName(value, itemsDataType);
if (enumVarName == null) {
defaultValues.add(null);
} else {
defaultValues.add(itemsDatatypeWithEnum + "." + enumVarName);
}
}
defaultValue = StringUtils.join(defaultValues, ", ");
} else if (!values.isEmpty()) {
if (itemsIsString) { // array item is string
defaultValue = String.format(Locale.ROOT, "\"%s\"", StringUtils.join(values, "\", \""));
defaultValueInit = defaultValue;
} else if (itemsIsNumeric) {
defaultValue = String.join(", ", values);
defaultValueInit = values.stream()
.map(v -> {
if ("BigInteger".equals(itemsDataType)) {
return "new BigInteger(\"" + v + "\")";
} else if ("BigDecimal".equals(itemsDataType)) {
return "new BigDecimal(\"" + v + "\")";
} else if (itemsIsFloat) {
return v + "F";
} else {
return v;
}
})
.collect(Collectors.joining(", "));
} else { // array item is non-string, e.g. integer
defaultValue = StringUtils.join(values, ", ");
}
} else {
return getDefaultCollectionType(schema);
}
return getDefaultCollectionType(schema, defaultValue);
}
if (itemsIsMap) { // map
// TODO
return Pair.of(null, null);
} else {
throw new RuntimeException("Error. Codegen Property must be array/set/map: " + schema);
}
}
return Pair.of(null, null);
}
private Pair getDefaultCollectionType(Schema schema) {
return getDefaultCollectionType(schema, null);
}
private Pair getDefaultCollectionType(Schema schema, String defaultValues) {
String arrayFormat = "new %s<>(Arrays.asList(%s))";
if (defaultValues == null || defaultValues.isEmpty()) {
defaultValues = "";
arrayFormat = "new %s<>()";
}
if (ModelUtils.isSet(schema)) {
return Pair.of(String.format(Locale.ROOT, arrayFormat, instantiationTypes().getOrDefault("set", "LinkedHashSet"),
defaultValues), defaultValues);
}
return Pair.of(String.format(Locale.ROOT, arrayFormat, instantiationTypes().getOrDefault("array", "ArrayList"), defaultValues), defaultValues);
}
@Override
public void setUseBeanValidation(boolean useBeanValidation) {
this.useBeanValidation = useBeanValidation;
}
@Override
public void setUseOptional(boolean useOptional) {
this.useOptional = useOptional;
}
public void setVisitable(boolean visitable) {
this.visitable = visitable;
}
@Override
protected CodegenDiscriminator createDiscriminator(String schemaName, Schema schema) {
var discriminator = super.createDiscriminator(schemaName, schema);
if (discriminator == null) {
return null;
}
for (var entry : discriminator.getMapping().entrySet()) {
String name;
if (entry.getValue().indexOf('/') < 0) {
continue;
}
name = ModelUtils.getSimpleRef(entry.getValue());
var referencedSchema = ModelUtils.getSchema(openAPI, name);
if (referencedSchema == null) {
once(log).error("Failed to lookup the schema '{}' when processing the discriminator mapping of oneOf/anyOf. Please check to ensure it's defined properly.", name);
continue;
}
if (referencedSchema.getProperties() == null || referencedSchema.getProperties().isEmpty()) {
continue;
}
boolean isDiscriminatorPropTypeFound = false;
var props = (Map) referencedSchema.getProperties();
for (var propEntry : props.entrySet()) {
if (!propEntry.getKey().equals(discriminator.getPropertyName())) {
continue;
}
discriminator.setPropertyType(getTypeDeclaration(propEntry.getValue()));
isDiscriminatorPropTypeFound = true;
break;
}
if (isDiscriminatorPropTypeFound) {
break;
}
}
return discriminator;
}
@Override
public String toApiVarName(String name) {
String apiVarName = super.toApiVarName(name);
if (reservedWords.contains(apiVarName)) {
apiVarName = escapeReservedWord(apiVarName);
}
return apiVarName;
}
@Override
protected boolean isReservedWord(String word) {
return word != null && reservedWords.contains(word);
}
public boolean isVisitable() {
return visitable;
}
@Override
public String sanitizeTag(String tag) {
// Skip sanitization to get the original tag name in the addOperationToGroup() method.
// Inside that method tag is manually sanitized.
return tag;
}
@Override
public String toApiName(String name) {
return Utils.toApiName(name, apiNamePrefix, apiNameSuffix);
}
@Override
public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co,
Map> operations) {
if (generateOperationOnlyForFirstTag && !co.tags.get(0).getName().equals(tag)) {
// This is not the first assigned to this operation tag;
return;
}
super.addOperationToGroup(super.sanitizeTag(tag), resourcePath, operation, co, operations);
}
@Override
public void preprocessOpenAPI(OpenAPI openApi) {
if (openApi.getPaths() != null) {
for (var path : openApi.getPaths().values()) {
if (path.getParameters() == null || path.getParameters().isEmpty()) {
continue;
}
for (var op : path.readOperations()) {
if (op.getParameters() == null) {
op.setParameters(new ArrayList<>());
}
for (var param : path.getParameters()) {
var found = false;
for (var opParam : op.getParameters()) {
if (Objects.equals(opParam.getName(), param.getName())
&& Objects.equals(opParam.get$ref(), param.get$ref())) {
found = true;
break;
}
}
if (!found) {
op.getParameters().add(param);
}
}
}
}
}
var inlineModelResolver = new MicronautInlineModelResolver(openApi);
inlineModelResolver.setInlineSchemaNameMapping(inlineSchemaNameMapping);
inlineModelResolver.setInlineSchemaOptions(inlineSchemaOption);
inlineModelResolver.flatten();
var pathItems = openApi.getPaths();
// fix for ApiResponse with $ref
if (pathItems != null) {
for (var pathEntry : pathItems.entrySet()) {
for (var opEntry : pathEntry.getValue().readOperationsMap().entrySet()) {
// process all response bodies
if (opEntry.getValue().getResponses() != null) {
for (var responseEntry : opEntry.getValue().getResponses().entrySet()) {
var response = responseEntry.getValue();
var refResponse = ModelUtils.getReferencedApiResponse(openApi, response);
if (response.getContent() == null) {
response.setContent(refResponse.getContent());
}
if (response.getDescription() == null) {
response.setDescription(refResponse.getDescription());
}
if (response.getHeaders() == null || response.getHeaders().isEmpty()) {
response.setHeaders(refResponse.getHeaders());
}
if (response.getExtensions() == null || response.getExtensions().isEmpty()) {
response.setExtensions(refResponse.getExtensions());
}
if (response.getLinks() == null || response.getLinks().isEmpty()) {
response.setLinks(refResponse.getLinks());
}
}
}
}
}
}
super.preprocessOpenAPI(openApi);
}
@Override
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) {
objs = super.postProcessOperationsWithModels(objs, allModels);
Map models = allModels.stream()
.map(ModelMap::getModel)
.collect(Collectors.toMap(v -> v.classname, v -> v));
OperationMap operations = objs.getOperations();
List operationList = operations.getOperation();
for (CodegenOperation op : operationList) {
// Set whether body is supported in request
op.vendorExtensions.put("methodAllowsBody", op.httpMethod.equals("PUT")
|| op.httpMethod.equals("POST")
|| op.httpMethod.equals("PATCH")
|| op.httpMethod.equals("OPTIONS")
|| op.httpMethod.equals("DELETE")
);
if (StringUtils.isNotEmpty(op.notes)) {
op.notes = op.notes.strip();
}
if (StringUtils.isNotEmpty(op.summary)) {
op.summary = op.summary.strip();
}
normalizeExtraAnnotations(EXT_ANNOTATIONS_OPERATION, false, op.vendorExtensions);
// Set response example
if (op.returnType != null) {
String example;
String groovyExample;
if (models.containsKey(op.returnType)) {
CodegenModel m = models.get(op.returnType);
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy