Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.openapitools.codegen.DefaultCodegen Maven / Gradle / Ivy
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Copyright 2018 SmartBear Software
*
* 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
*
* http://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 org.openapitools.codegen;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.CaseFormat;
import com.samskivert.mustache.Mustache.Compiler;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.callbacks.Callback;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.*;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.servers.ServerVariable;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openapitools.codegen.CodegenDiscriminator.MappedModel;
import org.openapitools.codegen.config.GeneratorProperties;
import org.openapitools.codegen.examples.ExampleGenerator;
import org.openapitools.codegen.serializer.SerializerUtils;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.openapitools.codegen.utils.StringUtils.*;
public class DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class);
protected String inputSpec;
protected String outputFolder = "";
protected Set defaultIncludes = new HashSet();
protected Map typeMapping = new HashMap();
protected Map instantiationTypes = new HashMap();
protected Set reservedWords = new HashSet();
protected Set languageSpecificPrimitives = new HashSet();
protected Map importMapping = new HashMap();
protected String modelPackage = "", apiPackage = "", fileSuffix;
protected String modelNamePrefix = "", modelNameSuffix = "";
protected String testPackage = "";
protected Map apiTemplateFiles = new HashMap();
protected Map modelTemplateFiles = new HashMap();
protected Map apiTestTemplateFiles = new HashMap();
protected Map modelTestTemplateFiles = new HashMap();
protected Map apiDocTemplateFiles = new HashMap();
protected Map modelDocTemplateFiles = new HashMap();
protected Map reservedWordsMappings = new HashMap();
protected String templateDir;
protected String embeddedTemplateDir;
protected String commonTemplateDir = "_common";
protected Map additionalProperties = new HashMap();
protected Map vendorExtensions = new HashMap();
protected List supportingFiles = new ArrayList();
protected List cliOptions = new ArrayList();
protected boolean skipOverwrite;
protected boolean removeOperationIdPrefix;
protected boolean supportsMultipleInheritance;
protected boolean supportsInheritance;
protected boolean supportsMixins;
protected Map supportedLibraries = new LinkedHashMap();
protected String library;
protected Boolean sortParamsByRequiredFlag = true;
protected Boolean ensureUniqueParams = true;
protected Boolean allowUnicodeIdentifiers = false;
protected String gitUserId, gitRepoId, releaseNote;
protected String httpUserAgent;
protected Boolean hideGenerationTimestamp = true;
// How to encode special characters like $
// They are translated to words like "Dollar" and prefixed with '
// Then translated back during JSON encoding and decoding
protected Map specialCharReplacements = new HashMap();
// When a model is an alias for a simple type
protected Map typeAliases = null;
protected Boolean prependFormOrBodyParameters = false;
// The extension of the generated documentation files (defaults to markdown .md)
protected String docExtension;
protected String ignoreFilePathOverride;
// flag to indicate whether to use environment variable to post process file
protected boolean enablePostProcessFile = false;
// flag to indicate whether to only update files whose contents have changed
protected boolean enableMinimalUpdate = false;
// make openapi available to all methods
protected OpenAPI openAPI;
public List cliOptions() {
return cliOptions;
}
public void processOpts() {
if (additionalProperties.containsKey(CodegenConstants.TEMPLATE_DIR)) {
this.setTemplateDir((String) additionalProperties.get(CodegenConstants.TEMPLATE_DIR));
}
if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
this.setModelPackage((String) additionalProperties.get(CodegenConstants.MODEL_PACKAGE));
}
if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
this.setApiPackage((String) additionalProperties.get(CodegenConstants.API_PACKAGE));
}
if (additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) {
setHideGenerationTimestamp(convertPropertyToBooleanAndWriteBack(CodegenConstants.HIDE_GENERATION_TIMESTAMP));
} else {
additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, hideGenerationTimestamp);
}
if (additionalProperties.containsKey(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG)) {
this.setSortParamsByRequiredFlag(Boolean.valueOf(additionalProperties
.get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS)) {
this.setPrependFormOrBodyParameters(Boolean.valueOf(additionalProperties
.get(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.ENSURE_UNIQUE_PARAMS)) {
this.setEnsureUniqueParams(Boolean.valueOf(additionalProperties
.get(CodegenConstants.ENSURE_UNIQUE_PARAMS).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS)) {
this.setAllowUnicodeIdentifiers(Boolean.valueOf(additionalProperties
.get(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.MODEL_NAME_PREFIX)) {
this.setModelNamePrefix((String) additionalProperties.get(CodegenConstants.MODEL_NAME_PREFIX));
}
if (additionalProperties.containsKey(CodegenConstants.MODEL_NAME_SUFFIX)) {
this.setModelNameSuffix((String) additionalProperties.get(CodegenConstants.MODEL_NAME_SUFFIX));
}
if (additionalProperties.containsKey(CodegenConstants.REMOVE_OPERATION_ID_PREFIX)) {
this.setRemoveOperationIdPrefix(Boolean.valueOf(additionalProperties
.get(CodegenConstants.REMOVE_OPERATION_ID_PREFIX).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.DOCEXTENSION)) {
this.setDocExtension(String.valueOf(additionalProperties
.get(CodegenConstants.DOCEXTENSION).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.ENABLE_POST_PROCESS_FILE)) {
this.setEnablePostProcessFile(Boolean.valueOf(additionalProperties
.get(CodegenConstants.ENABLE_POST_PROCESS_FILE).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.GENERATE_ALIAS_AS_MODEL)) {
ModelUtils.setGenerateAliasAsModel(Boolean.valueOf(additionalProperties
.get(CodegenConstants.GENERATE_ALIAS_AS_MODEL).toString()));
}
}
// override with any special post-processing for all models
@SuppressWarnings({"static-method", "unchecked"})
public Map postProcessAllModels(Map objs) {
return objs;
}
/**
* Loop through all models to update different flags (e.g. isSelfReference), children models, etc
*
* @param objs Map of models
* @return maps of models with various updates
*/
public Map updateAllModels(Map objs) {
// Index all CodegenModels by model name.
Map allModels = new HashMap();
for (Entry entry : objs.entrySet()) {
String modelName = toModelName(entry.getKey());
Map inner = (Map) entry.getValue();
List> models = (List>) inner.get("models");
for (Map mo : models) {
CodegenModel cm = (CodegenModel) mo.get("model");
allModels.put(modelName, cm);
}
}
// Fix up all parent and interface CodegenModel references.
for (CodegenModel cm : allModels.values()) {
if (cm.getParent() != null) {
cm.setParentModel(allModels.get(cm.getParent()));
}
if (cm.getInterfaces() != null && !cm.getInterfaces().isEmpty()) {
cm.setInterfaceModels(new ArrayList(cm.getInterfaces().size()));
for (String intf : cm.getInterfaces()) {
CodegenModel intfModel = allModels.get(intf);
if (intfModel != null) {
cm.getInterfaceModels().add(intfModel);
}
}
}
}
// Let parent know about all its children
for (String name : allModels.keySet()) {
CodegenModel cm = allModels.get(name);
CodegenModel parent = allModels.get(cm.getParent());
// if a discriminator exists on the parent, don't add this child to the inheritance hierarchy
// TODO Determine what to do if the parent discriminator name == the grandparent discriminator name
while (parent != null) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList());
}
parent.getChildren().add(cm);
parent.hasChildren = true;
if (parent.getDiscriminator() == null) {
parent = allModels.get(parent.getParent());
} else {
parent = null;
}
}
}
// loop through properties of each model to detect self-reference
for (Map.Entry entry : objs.entrySet()) {
Map inner = (Map) entry.getValue();
List> models = (List>) inner.get("models");
for (Map mo : models) {
CodegenModel cm = (CodegenModel) mo.get("model");
for (CodegenProperty cp : cm.allVars) {
// detect self import
if (cp.dataType.equalsIgnoreCase(cm.classname) ||
(cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(cm.classname))) {
cm.imports.remove(cm.classname); // remove self import
cp.isSelfReference = true;
}
}
}
}
return objs;
}
// override with any special post-processing
@SuppressWarnings("static-method")
public Map postProcessModels(Map objs) {
return objs;
}
/**
* post process enum defined in model's properties
*
* @param objs Map of models
* @return maps of models with better enum support
*/
public Map postProcessModelsEnum(Map objs) {
List models = (List) objs.get("models");
for (Object _mo : models) {
Map mo = (Map) _mo;
CodegenModel cm = (CodegenModel) mo.get("model");
// for enum model
if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) {
Map allowableValues = cm.allowableValues;
List values = (List) allowableValues.get("values");
List> enumVars = new ArrayList>();
String commonPrefix = findCommonPrefixOfVars(values);
int truncateIdx = commonPrefix.length();
for (Object value : values) {
Map enumVar = new HashMap();
String enumName;
if (truncateIdx == 0) {
enumName = value.toString();
} else {
enumName = value.toString().substring(truncateIdx);
if ("".equals(enumName)) {
enumName = value.toString();
}
}
enumVar.put("name", toEnumVarName(enumName, cm.dataType));
enumVar.put("value", toEnumValue(value.toString(), cm.dataType));
enumVar.put("isString", isDataTypeString(cm.dataType));
enumVars.add(enumVar);
}
// if "x-enum-varnames" or "x-enum-descriptions" defined, update varnames
updateEnumVarsWithExtensions(enumVars, cm.getVendorExtensions());
cm.allowableValues.put("enumVars", enumVars);
}
// update codegen property enum with proper naming convention
// and handling of numbers, special characters
for (CodegenProperty var : cm.vars) {
updateCodegenPropertyEnum(var);
}
for (CodegenProperty var : cm.allVars) {
updateCodegenPropertyEnum(var);
}
for (CodegenProperty var : cm.requiredVars) {
updateCodegenPropertyEnum(var);
}
for (CodegenProperty var : cm.optionalVars) {
updateCodegenPropertyEnum(var);
}
for (CodegenProperty var : cm.parentVars) {
updateCodegenPropertyEnum(var);
}
for (CodegenProperty var : cm.readOnlyVars) {
updateCodegenPropertyEnum(var);
}
for (CodegenProperty var : cm.readWriteVars) {
updateCodegenPropertyEnum(var);
}
}
return objs;
}
/**
* Returns the common prefix of variables for enum naming if
* two or more variables are present
*
* @param vars List of variable names
* @return the common prefix for naming
*/
public String findCommonPrefixOfVars(List vars) {
if (vars.size() > 1) {
try {
String[] listStr = vars.toArray(new String[vars.size()]);
String prefix = StringUtils.getCommonPrefix(listStr);
// exclude trailing characters that should be part of a valid variable
// e.g. ["status-on", "status-off"] => "status-" (not "status-o")
return prefix.replaceAll("[a-zA-Z0-9]+\\z", "");
} catch (ArrayStoreException e) {
// do nothing, just return default value
}
}
return "";
}
/**
* Return the enum default value in the language specified format
*
* @param value enum variable name
* @param datatype data type
* @return the default value for the enum
*/
public String toEnumDefaultValue(String value, String datatype) {
return datatype + "." + value;
}
/**
* Return the enum value in the language specified format
* e.g. status becomes "status"
*
* @param value enum variable name
* @param datatype data type
* @return the sanitized value for enum
*/
public String toEnumValue(String value, String datatype) {
if ("number".equalsIgnoreCase(datatype)) {
return value;
} else {
return "\"" + escapeText(value) + "\"";
}
}
/**
* Return the sanitized variable name for enum
*
* @param value enum variable name
* @param datatype data type
* @return the sanitized variable name for enum
*/
public String toEnumVarName(String value, String datatype) {
if (value.length() == 0) {
return "EMPTY";
}
String var = value.replaceAll("\\W+", "_").toUpperCase(Locale.ROOT);
if (var.matches("\\d.*")) {
return "_" + var;
} else {
return var;
}
}
@Override
public void setOpenAPI(OpenAPI openAPI) {
this.openAPI = openAPI;
}
// override with any special post-processing
@SuppressWarnings("static-method")
public Map postProcessOperationsWithModels(Map objs, List allModels) {
return objs;
}
// override with any special post-processing
@SuppressWarnings("static-method")
public Map postProcessSupportingFileData(Map objs) {
return objs;
}
// override to post-process any model properties
@SuppressWarnings("unused")
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
}
// override to post-process any parameters
@SuppressWarnings("unused")
public void postProcessParameter(CodegenParameter parameter) {
}
//override with any special handling of the entire swagger spec
@SuppressWarnings("unused")
public void preprocessOpenAPI(OpenAPI openAPI) {
}
// override with any special handling of the entire swagger spec
@SuppressWarnings("unused")
public void processOpenAPI(OpenAPI openAPI) {
}
// override with any special handling of the JMustache compiler
@SuppressWarnings("unused")
public Compiler processCompiler(Compiler compiler) {
return compiler;
}
// override with any special text escaping logic
@SuppressWarnings("static-method")
public String escapeText(String input) {
if (input == null) {
return input;
}
// remove \t, \n, \r
// replace \ with \\
// replace " with \"
// outter unescape to retain the original multi-byte characters
// finally escalate characters avoiding code injection
return escapeUnsafeCharacters(
StringEscapeUtils.unescapeJava(
StringEscapeUtils.escapeJava(input)
.replace("\\/", "/"))
.replaceAll("[\\t\\n\\r]", " ")
.replace("\\", "\\\\")
.replace("\"", "\\\""));
}
/**
* Escape characters while allowing new lines
*
* @param input String to be escaped
* @return escaped string
*/
public String escapeTextWhileAllowingNewLines(String input) {
if (input == null) {
return input;
}
// remove \t
// replace \ with \\
// replace " with \"
// outter unescape to retain the original multi-byte characters
// finally escalate characters avoiding code injection
return escapeUnsafeCharacters(
StringEscapeUtils.unescapeJava(
StringEscapeUtils.escapeJava(input)
.replace("\\/", "/"))
.replaceAll("[\\t]", " ")
.replace("\\", "\\\\")
.replace("\"", "\\\""));
}
// override with any special encoding and escaping logic
@SuppressWarnings("static-method")
public String encodePath(String input) {
return escapeText(input);
}
/**
* override with any special text escaping logic to handle unsafe
* characters so as to avoid code injection
*
* @param input String to be cleaned up
* @return string with unsafe characters removed or escaped
*/
public String escapeUnsafeCharacters(String input) {
LOGGER.warn("escapeUnsafeCharacters should be overridden in the code generator with proper logic to escape " +
"unsafe characters");
// doing nothing by default and code generator should implement
// the logic to prevent code injection
// later we'll make this method abstract to make sure
// code generator implements this method
return input;
}
/**
* Escape single and/or double quote to avoid code injection
*
* @param input String to be cleaned up
* @return string with quotation mark removed or escaped
*/
public String escapeQuotationMark(String input) {
LOGGER.warn("escapeQuotationMark should be overridden in the code generator with proper logic to escape " +
"single/double quote");
return input.replace("\"", "\\\"");
}
public Set defaultIncludes() {
return defaultIncludes;
}
public Map typeMapping() {
return typeMapping;
}
public Map instantiationTypes() {
return instantiationTypes;
}
public Set reservedWords() {
return reservedWords;
}
public Set languageSpecificPrimitives() {
return languageSpecificPrimitives;
}
public Map importMapping() {
return importMapping;
}
public String testPackage() {
return testPackage;
}
public String modelPackage() {
return modelPackage;
}
public String apiPackage() {
return apiPackage;
}
public String fileSuffix() {
return fileSuffix;
}
public String templateDir() {
return templateDir;
}
public String embeddedTemplateDir() {
if (embeddedTemplateDir != null) {
return embeddedTemplateDir;
} else {
return templateDir;
}
}
public String getCommonTemplateDir() {
return this.commonTemplateDir;
}
public void setCommonTemplateDir(String commonTemplateDir) {
this.commonTemplateDir = commonTemplateDir;
}
public Map apiDocTemplateFiles() {
return apiDocTemplateFiles;
}
public Map modelDocTemplateFiles() {
return modelDocTemplateFiles;
}
public Map reservedWordsMappings() {
return reservedWordsMappings;
}
public Map apiTestTemplateFiles() {
return apiTestTemplateFiles;
}
public Map modelTestTemplateFiles() {
return modelTestTemplateFiles;
}
public Map apiTemplateFiles() {
return apiTemplateFiles;
}
public Map modelTemplateFiles() {
return modelTemplateFiles;
}
public String apiFileFolder() {
return outputFolder + "/" + apiPackage().replace('.', '/');
}
public String modelFileFolder() {
return outputFolder + "/" + modelPackage().replace('.', '/');
}
public String apiTestFileFolder() {
return outputFolder + "/" + testPackage().replace('.', '/');
}
public String modelTestFileFolder() {
return outputFolder + "/" + testPackage().replace('.', '/');
}
public String apiDocFileFolder() {
return outputFolder;
}
public String modelDocFileFolder() {
return outputFolder;
}
public Map additionalProperties() {
return additionalProperties;
}
public Map vendorExtensions() {
return vendorExtensions;
}
public List supportingFiles() {
return supportingFiles;
}
public String outputFolder() {
return outputFolder;
}
public void setOutputDir(String dir) {
this.outputFolder = dir;
}
public String getOutputDir() {
return outputFolder();
}
public String getInputSpec() {
return inputSpec;
}
public void setInputSpec(String inputSpec) {
this.inputSpec = inputSpec;
}
public void setTemplateDir(String templateDir) {
this.templateDir = templateDir;
}
public void setModelPackage(String modelPackage) {
this.modelPackage = modelPackage;
}
public void setModelNamePrefix(String modelNamePrefix) {
this.modelNamePrefix = modelNamePrefix;
}
public void setModelNameSuffix(String modelNameSuffix) {
this.modelNameSuffix = modelNameSuffix;
}
public void setApiPackage(String apiPackage) {
this.apiPackage = apiPackage;
}
public void setSortParamsByRequiredFlag(Boolean sortParamsByRequiredFlag) {
this.sortParamsByRequiredFlag = sortParamsByRequiredFlag;
}
public void setPrependFormOrBodyParameters(Boolean prependFormOrBodyParameters) {
this.prependFormOrBodyParameters = prependFormOrBodyParameters;
}
public void setEnsureUniqueParams(Boolean ensureUniqueParams) {
this.ensureUniqueParams = ensureUniqueParams;
}
public void setAllowUnicodeIdentifiers(Boolean allowUnicodeIdentifiers) {
this.allowUnicodeIdentifiers = allowUnicodeIdentifiers;
}
/**
* Return the regular expression/JSON schema pattern (http://json-schema.org/latest/json-schema-validation.html#anchor33)
*
* @param pattern the pattern (regular expression)
* @return properly-escaped pattern
*/
public String toRegularExpression(String pattern) {
return addRegularExpressionDelimiter(escapeText(pattern));
}
/**
* Return the file name of the Api Test
*
* @param name the file name of the Api
* @return the file name of the Api
*/
public String toApiFilename(String name) {
return toApiName(name);
}
/**
* Return the file name of the Api Documentation
*
* @param name the file name of the Api
* @return the file name of the Api
*/
public String toApiDocFilename(String name) {
return toApiName(name);
}
/**
* Return the file name of the Api Test
*
* @param name the file name of the Api
* @return the file name of the Api
*/
public String toApiTestFilename(String name) {
return toApiName(name) + "Test";
}
/**
* Return the variable name in the Api
*
* @param name the varible name of the Api
* @return the snake-cased variable name
*/
public String toApiVarName(String name) {
return snakeCase(name);
}
/**
* Return the capitalized file name of the model
*
* @param name the model name
* @return the file name of the model
*/
public String toModelFilename(String name) {
return camelize(name);
}
/**
* Return the capitalized file name of the model test
*
* @param name the model name
* @return the file name of the model
*/
public String toModelTestFilename(String name) {
return camelize(name) + "Test";
}
/**
* Return the capitalized file name of the model documentation
*
* @param name the model name
* @return the file name of the model
*/
public String toModelDocFilename(String name) {
return camelize(name);
}
/**
* Return the operation ID (method name)
*
* @param operationId operation ID
* @return the sanitized method name
*/
@SuppressWarnings("static-method")
public String toOperationId(String operationId) {
// throw exception if method name is empty
if (StringUtils.isEmpty(operationId)) {
throw new RuntimeException("Empty method name (operationId) not allowed");
}
return operationId;
}
/**
* Return the variable name by removing invalid characters and proper escaping if
* it's a reserved word.
*
* @param name the variable name
* @return the sanitized variable name
*/
public String toVarName(String name) {
if (reservedWords.contains(name)) {
return escapeReservedWord(name);
} else if (((CharSequence) name).chars().anyMatch(character -> specialCharReplacements.keySet().contains("" + ((char) character)))) {
return escape(name, specialCharReplacements, null, null);
} else {
return name;
}
}
/**
* Return the parameter name by removing invalid characters and proper escaping if
* it's a reserved word.
*
* @param name Codegen property object
* @return the sanitized parameter name
*/
public String toParamName(String name) {
name = removeNonNameElementToCamelCase(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
if (reservedWords.contains(name)) {
return escapeReservedWord(name);
} else if (((CharSequence) name).chars().anyMatch(character -> specialCharReplacements.keySet().contains("" + ((char) character)))) {
return escape(name, specialCharReplacements, null, null);
}
return name;
}
/**
* Return the parameter name of array of model
*
* @param name name of the array model
* @return the sanitized parameter name
*/
public String toArrayModelParamName(String name) {
return toParamName(name);
}
/**
* Return the Enum name (e.g. StatusEnum given 'status')
*
* @param property Codegen property
* @return the Enum name
*/
@SuppressWarnings("static-method")
public String toEnumName(CodegenProperty property) {
return StringUtils.capitalize(property.name) + "Enum";
}
/**
* Return the escaped name of the reserved word
*
* @param name the name to be escaped
* @return the escaped reserved word
*
* throws Runtime exception as reserved word is not allowed (default behavior)
*/
@SuppressWarnings("static-method")
public String escapeReservedWord(String name) {
throw new RuntimeException("reserved word " + name + " not allowed");
}
/**
* Return the fully-qualified "Model" name for import
*
* @param name the name of the "Model"
* @return the fully-qualified "Model" name for import
*/
public String toModelImport(String name) {
if ("".equals(modelPackage())) {
return name;
} else {
return modelPackage() + "." + name;
}
}
/**
* Return the fully-qualified "Api" name for import
*
* @param name the name of the "Api"
* @return the fully-qualified "Api" name for import
*/
public String toApiImport(String name) {
return apiPackage() + "." + name;
}
/**
* Default constructor.
* This method will map between OAS type and language-specified type, as well as mapping
* between OAS type and the corresponding import statement for the language. This will
* also add some language specified CLI options, if any.
* returns string presentation of the example path (it's a constructor)
*/
public DefaultCodegen() {
defaultIncludes = new HashSet(
Arrays.asList("double",
"int",
"long",
"short",
"char",
"float",
"String",
"boolean",
"Boolean",
"Double",
"Void",
"Integer",
"Long",
"Float")
);
typeMapping = new HashMap();
typeMapping.put("array", "List");
typeMapping.put("map", "Map");
typeMapping.put("List", "List");
typeMapping.put("boolean", "Boolean");
typeMapping.put("string", "String");
typeMapping.put("int", "Integer");
typeMapping.put("float", "Float");
typeMapping.put("number", "BigDecimal");
typeMapping.put("DateTime", "Date");
typeMapping.put("long", "Long");
typeMapping.put("short", "Short");
typeMapping.put("char", "String");
typeMapping.put("double", "Double");
typeMapping.put("object", "Object");
typeMapping.put("integer", "Integer");
typeMapping.put("ByteArray", "byte[]");
typeMapping.put("binary", "File");
typeMapping.put("file", "File");
typeMapping.put("UUID", "UUID");
//typeMapping.put("BigDecimal", "BigDecimal"); //TODO need the mapping?
instantiationTypes = new HashMap();
reservedWords = new HashSet();
importMapping = new HashMap();
importMapping.put("BigDecimal", "java.math.BigDecimal");
importMapping.put("UUID", "java.util.UUID");
importMapping.put("File", "java.io.File");
importMapping.put("Date", "java.util.Date");
importMapping.put("Timestamp", "java.sql.Timestamp");
importMapping.put("Map", "java.util.Map");
importMapping.put("HashMap", "java.util.HashMap");
importMapping.put("Array", "java.util.List");
importMapping.put("ArrayList", "java.util.ArrayList");
importMapping.put("List", "java.util.*");
importMapping.put("Set", "java.util.*");
importMapping.put("DateTime", "org.joda.time.*");
importMapping.put("LocalDateTime", "org.joda.time.*");
importMapping.put("LocalDate", "org.joda.time.*");
importMapping.put("LocalTime", "org.joda.time.*");
// we've used the .openapi-generator-ignore approach as
// suppportingFiles can be cleared by code generator that extends
// the default codegen, leaving the commented code below for
// future reference
//supportingFiles.add(new GlobalSupportingFile("LICENSE", "LICENSE"));
cliOptions.add(CliOption.newBoolean(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG,
CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC).defaultValue(Boolean.TRUE.toString()));
cliOptions.add(CliOption.newBoolean(CodegenConstants.ENSURE_UNIQUE_PARAMS, CodegenConstants
.ENSURE_UNIQUE_PARAMS_DESC).defaultValue(Boolean.TRUE.toString()));
// name formatting options
cliOptions.add(CliOption.newBoolean(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, CodegenConstants
.ALLOW_UNICODE_IDENTIFIERS_DESC).defaultValue(Boolean.FALSE.toString()));
// option to change the order of form/body parameter
cliOptions.add(CliOption.newBoolean(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS,
CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS_DESC).defaultValue(Boolean.FALSE.toString()));
// initialize special character mapping
initalizeSpecialCharacterMapping();
}
/**
* Initialize special character mapping
*/
protected void initalizeSpecialCharacterMapping() {
// Initialize special characters
specialCharReplacements.put("$", "Dollar");
specialCharReplacements.put("^", "Caret");
specialCharReplacements.put("|", "Pipe");
specialCharReplacements.put("=", "Equal");
specialCharReplacements.put("*", "Star");
specialCharReplacements.put("-", "Minus");
specialCharReplacements.put("&", "Ampersand");
specialCharReplacements.put("%", "Percent");
specialCharReplacements.put("#", "Hash");
specialCharReplacements.put("@", "At");
specialCharReplacements.put("!", "Exclamation");
specialCharReplacements.put("+", "Plus");
specialCharReplacements.put(":", "Colon");
specialCharReplacements.put(">", "Greater_Than");
specialCharReplacements.put("<", "Less_Than");
specialCharReplacements.put(".", "Period");
specialCharReplacements.put("_", "Underscore");
specialCharReplacements.put("?", "Question_Mark");
specialCharReplacements.put(",", "Comma");
specialCharReplacements.put("'", "Quote");
specialCharReplacements.put("\"", "Double_Quote");
specialCharReplacements.put("/", "Slash");
specialCharReplacements.put("\\", "Back_Slash");
specialCharReplacements.put("(", "Left_Parenthesis");
specialCharReplacements.put(")", "Right_Parenthesis");
specialCharReplacements.put("{", "Left_Curly_Bracket");
specialCharReplacements.put("}", "Right_Curly_Bracket");
specialCharReplacements.put("[", "Left_Square_Bracket");
specialCharReplacements.put("]", "Right_Square_Bracket");
specialCharReplacements.put("~", "Tilde");
specialCharReplacements.put("`", "Backtick");
specialCharReplacements.put("<=", "Less_Than_Or_Equal_To");
specialCharReplacements.put(">=", "Greater_Than_Or_Equal_To");
specialCharReplacements.put("!=", "Not_Equal");
}
/**
* Return the symbol name of a symbol
*
* @param input Symbol (e.g. $)
* @return Symbol name (e.g. Dollar)
*/
protected String getSymbolName(String input) {
return specialCharReplacements.get(input);
}
/**
* Return the example path
*
* @param path the path of the operation
* @param operation OAS operation object
* @return string presentation of the example path
*/
@SuppressWarnings("static-method")
public String generateExamplePath(String path, Operation operation) {
StringBuilder sb = new StringBuilder();
sb.append(path);
if (operation.getParameters() != null) {
int count = 0;
for (Parameter param : operation.getParameters()) {
if (param instanceof QueryParameter) {
StringBuilder paramPart = new StringBuilder();
QueryParameter qp = (QueryParameter) param;
if (count == 0) {
paramPart.append("?");
} else {
paramPart.append(",");
}
count += 1;
if (!param.getRequired()) {
paramPart.append("[");
}
paramPart.append(param.getName()).append("=");
paramPart.append("{");
// TODO support for multi, tsv?
if (qp.getStyle() != null) {
paramPart.append(param.getName()).append("1");
if (Parameter.StyleEnum.FORM.equals(qp.getStyle())) {
if (qp.getExplode() != null && qp.getExplode()) {
paramPart.append(",");
} else {
paramPart.append("&").append(param.getName()).append("=");
paramPart.append(param.getName()).append("2");
}
} else if (Parameter.StyleEnum.PIPEDELIMITED.equals(qp.getStyle())) {
paramPart.append("|");
} else if (Parameter.StyleEnum.SPACEDELIMITED.equals(qp.getStyle())) {
paramPart.append("%20");
} else {
LOGGER.warn("query parameter '" + param.getName() + "style not support: " + qp.getStyle());
}
} else {
paramPart.append(param.getName());
}
paramPart.append("}");
if (!param.getRequired()) {
paramPart.append("]");
}
sb.append(paramPart.toString());
}
}
}
return sb.toString();
}
/**
* Return the instantiation type of the property, especially for map and array
*
* @param schema property schema
* @return string presentation of the instantiation type of the property
*/
public String toInstantiationType(Schema schema) {
if (ModelUtils.isMapSchema(schema)) {
Schema additionalProperties = ModelUtils.getAdditionalProperties(schema);
String inner = getSchemaType(additionalProperties);
return instantiationTypes.get("map") + "";
} else if (ModelUtils.isArraySchema(schema)) {
ArraySchema arraySchema = (ArraySchema) schema;
String inner = getSchemaType(arraySchema.getItems());
return instantiationTypes.get("array") + "<" + inner + ">";
} else {
return null;
}
}
/**
* Return the example value of the parameter.
*
* @param codegenParameter Codegen parameter
*/
public void setParameterExampleValue(CodegenParameter codegenParameter) {
// set the example value
// if not specified in x-example, generate a default value
// TODO need to revise how to obtain the example value
if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) {
codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example"));
} else if (Boolean.TRUE.equals(codegenParameter.isBoolean)) {
codegenParameter.example = "true";
} else if (Boolean.TRUE.equals(codegenParameter.isLong)) {
codegenParameter.example = "789";
} else if (Boolean.TRUE.equals(codegenParameter.isInteger)) {
codegenParameter.example = "56";
} else if (Boolean.TRUE.equals(codegenParameter.isFloat)) {
codegenParameter.example = "3.4";
} else if (Boolean.TRUE.equals(codegenParameter.isDouble)) {
codegenParameter.example = "1.2";
} else if (Boolean.TRUE.equals(codegenParameter.isNumber)) {
codegenParameter.example = "8.14";
} else if (Boolean.TRUE.equals(codegenParameter.isBinary)) {
codegenParameter.example = "BINARY_DATA_HERE";
} else if (Boolean.TRUE.equals(codegenParameter.isByteArray)) {
codegenParameter.example = "BYTE_ARRAY_DATA_HERE";
} else if (Boolean.TRUE.equals(codegenParameter.isFile)) {
codegenParameter.example = "/path/to/file.txt";
} else if (Boolean.TRUE.equals(codegenParameter.isDate)) {
codegenParameter.example = "2013-10-20";
} else if (Boolean.TRUE.equals(codegenParameter.isDateTime)) {
codegenParameter.example = "2013-10-20T19:20:30+01:00";
} else if (Boolean.TRUE.equals(codegenParameter.isUuid)) {
codegenParameter.example = "38400000-8cf0-11bd-b23e-10b96e4ef00d";
} else if (Boolean.TRUE.equals(codegenParameter.isString)) {
codegenParameter.example = codegenParameter.paramName + "_example";
} else if (Boolean.TRUE.equals(codegenParameter.isFreeFormObject)) {
codegenParameter.example = "Object";
}
}
/**
* Return the example value of the parameter.
*
* @param codegenParameter Codegen parameter
* @param parameter Parameter
*/
public void setParameterExampleValue(CodegenParameter codegenParameter, Parameter parameter) {
if (parameter.getExample() != null) {
codegenParameter.example = parameter.getExample().toString();
return;
}
if (parameter.getExamples() != null && !parameter.getExamples().isEmpty()) {
Example example = parameter.getExamples().values().iterator().next();
if (example.getValue() != null) {
codegenParameter.example = example.getValue().toString();
return;
}
}
Schema schema = parameter.getSchema();
if (schema != null && schema.getExample() != null) {
codegenParameter.example = schema.getExample().toString();
return;
}
setParameterExampleValue(codegenParameter);
}
/**
* Return the example value of the parameter.
*
* @param codegenParameter Codegen parameter
* @param requestBody Request body
*/
public void setParameterExampleValue(CodegenParameter codegenParameter, RequestBody requestBody) {
Content content = requestBody.getContent();
if (content.size() > 1) {
// @see ModelUtils.getSchemaFromContent()
LOGGER.warn("Multiple MediaTypes found, using only the first one");
}
MediaType mediaType = content.values().iterator().next();
if (mediaType.getExample() != null) {
codegenParameter.example = mediaType.getExample().toString();
return;
}
if (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty()) {
Example example = mediaType.getExamples().values().iterator().next();
if (example.getValue() != null) {
codegenParameter.example = example.getValue().toString();
return;
}
}
setParameterExampleValue(codegenParameter);
}
/**
* Return the example value of the property
*
* @param schema Property schema
* @return string presentation of the example value of the property
*/
public String toExampleValue(Schema schema) {
if (schema.getExample() != null) {
return schema.getExample().toString();
}
return getPropertyDefaultValue(schema);
}
/**
* Return the default value of the property
*
* @param schema Property schema
* @return string presentation of the default value of the property
*/
@SuppressWarnings("static-method")
public String toDefaultValue(Schema schema) {
if (schema.getDefault() != null) {
return schema.getDefault().toString();
}
return getPropertyDefaultValue(schema);
}
/**
* Return property value depending on property type.
* @param schema property type
* @return property value
*/
@SuppressWarnings("squid:S3923")
private String getPropertyDefaultValue(Schema schema) {
/**
* Although all branches return null, this is left intentionally as examples for new contributors
*/
if (ModelUtils.isBooleanSchema(schema)) {
return "null";
} else if (ModelUtils.isDateSchema(schema)) {
return "null";
} else if (ModelUtils.isDateTimeSchema(schema)) {
return "null";
} else if (ModelUtils.isNumberSchema(schema)) {
return "null";
} else if (ModelUtils.isIntegerSchema(schema)) {
return "null";
} else if (ModelUtils.isStringSchema(schema)) {
return "null";
} else if (ModelUtils.isObjectSchema(schema)) {
return "null";
} else {
return "null";
}
}
/**
* Return the property initialized from a data object
* Useful for initialization with a plain object in Javascript
*
* @param name Name of the property object
* @param schema Property schema
* @return string presentation of the default value of the property
*/
@SuppressWarnings("static-method")
public String toDefaultValueWithParam(String name, Schema schema) {
return " = data." + name + ";";
}
/**
* returns the OpenAPI type for the property. Use getAlias to handle $ref of primitive type
*
* @param schema property schema
* @return string presentation of the type
**/
@SuppressWarnings("static-method")
public String getSchemaType(Schema schema) {
if (schema instanceof ComposedSchema) { // composed schema
ComposedSchema cs = (ComposedSchema) schema;
List schemas = ModelUtils.getInterfaces(cs);
List names = new ArrayList<>();
for (Schema s : schemas) {
names.add(getSingleSchemaType(s));
}
if (cs.getAllOf() != null) {
return toAllOfName(names, cs);
} else if (cs.getAnyOf() != null) { // anyOf
return toAnyOfName(names, cs);
} else if (cs.getOneOf() != null) { // oneOf
return toOneOfName(names, cs);
}
}
return getSingleSchemaType(schema);
}
/**
* Return the name of the allOf schema
*
* @param names List of names
* @param composedSchema composed schema
* @return name of the allOf schema
*/
@SuppressWarnings("static-method")
public String toAllOfName(List names, ComposedSchema composedSchema) {
if (names.size() == 0) {
LOGGER.error("allOf has no member defined: {}. Default to ERROR_ALLOF_SCHEMA", composedSchema);
return "ERROR_ALLOF_SCHEMA";
} else if (names.size() == 1) {
return names.get(0);
} else {
LOGGER.warn("allOf with multiple schemas defined. Using only the first one: {}. To fully utilize allOf, please use $ref instead of inline schema definition", names.get(0));
return names.get(0);
}
}
/**
* Return the name of the anyOf schema
*
* @param names List of names
* @param composedSchema composed schema
* @return name of the anyOf schema
*/
@SuppressWarnings("static-method")
public String toAnyOfName(List names, ComposedSchema composedSchema) {
return "anyOf<" + String.join(",", names) + ">";
}
/**
* Return the name of the oneOf schema
*
* @param names List of names
* @param composedSchema composed schema
* @return name of the oneOf schema
*/
@SuppressWarnings("static-method")
public String toOneOfName(List names, ComposedSchema composedSchema) {
return "oneOf<" + String.join(",", names) + ">";
}
private String getSingleSchemaType(Schema schema) {
Schema unaliasSchema = ModelUtils.unaliasSchema(this.openAPI, schema);
if (StringUtils.isNotBlank(unaliasSchema.get$ref())) { // reference to another definition/schema
// get the schema/model name from $ref
String schemaName = ModelUtils.getSimpleRef(unaliasSchema.get$ref());
if (StringUtils.isNotEmpty(schemaName)) {
return getAlias(schemaName);
} else {
LOGGER.warn("Error obtaining the datatype from ref:" + unaliasSchema.get$ref() + ". Default to 'object'");
return "object";
}
} else { // primitive type or model
return getAlias(getPrimitiveType(unaliasSchema));
}
}
/**
* Return the OAI type (e.g. integer, long, etc) corresponding to a schema.
* $ref is not taken into account by this method.
*
* @param schema
* @return type
*/
private static String getPrimitiveType(Schema schema) {
if (schema == null) {
throw new RuntimeException("schema cannot be null in getPrimitiveType");
} else if (ModelUtils.isStringSchema(schema) && "number".equals(schema.getFormat())) {
// special handle of type: string, format: number
return "BigDecimal";
} else if (ModelUtils.isByteArraySchema(schema)) {
return "ByteArray";
} else if (ModelUtils.isFileSchema(schema)) {
return "file";
} else if (ModelUtils.isBinarySchema(schema)) {
return SchemaTypeUtil.BINARY_FORMAT;
} else if (ModelUtils.isBooleanSchema(schema)) {
return SchemaTypeUtil.BOOLEAN_TYPE;
} else if (ModelUtils.isDateSchema(schema)) {
return SchemaTypeUtil.DATE_FORMAT;
} else if (ModelUtils.isDateTimeSchema(schema)) {
return "DateTime";
} else if (ModelUtils.isNumberSchema(schema)) {
if (schema.getFormat() == null) { // no format defined
return "number";
} else if (ModelUtils.isFloatSchema(schema)) {
return SchemaTypeUtil.FLOAT_FORMAT;
} else if (ModelUtils.isDoubleSchema(schema)) {
return SchemaTypeUtil.DOUBLE_FORMAT;
} else {
LOGGER.warn("Unknown `format` detected for " + schema.getName() + ": " + schema.getFormat());
}
} else if (ModelUtils.isIntegerSchema(schema)) {
if (ModelUtils.isLongSchema(schema)) {
return "long";
} else {
return schema.getType(); // integer
}
} else if (ModelUtils.isMapSchema(schema)) {
return "map";
} else if (ModelUtils.isArraySchema(schema)) {
return "array";
} else if (ModelUtils.isUUIDSchema(schema)) {
return "UUID";
} else if (ModelUtils.isStringSchema(schema)) {
return "string";
} else if (ModelUtils.isFreeFormObject(schema)) {
return "object";
} else if (schema.getProperties() != null && !schema.getProperties().isEmpty()) { // having property implies it's a model
return "object";
} else if (StringUtils.isNotEmpty(schema.getType())) {
LOGGER.warn("Unknown type found in the schema: " + schema.getType());
return schema.getType();
}
return "object";
}
/**
* Return the snake-case of the string
*
* @param name string to be snake-cased
* @return snake-cased string
*/
@SuppressWarnings("static-method")
public String snakeCase(String name) {
return (name.length() > 0) ? (Character.toLowerCase(name.charAt(0)) + name.substring(1)) : "";
}
/**
* Output the type declaration of a given name
*
* @param name name
* @return a string presentation of the type
*/
@SuppressWarnings("static-method")
public String getTypeDeclaration(String name) {
return name;
}
/**
* Output the type declaration of the property
*
* @param schema property schema
* @return a string presentation of the property type
*/
public String getTypeDeclaration(Schema schema) {
if (schema == null) {
LOGGER.warn("Null schema found. Default type to `NULL_SCHMEA_ERR`");
return "NULL_SCHMEA_ERR";
}
String oasType = getSchemaType(schema);
if (typeMapping.containsKey(oasType)) {
return typeMapping.get(oasType);
}
return oasType;
}
/**
* Determine the type alias for the given type if it exists. This feature
* was originally developed for Java because the language does not have an aliasing
* mechanism of its own but later extends to handle other languages
*
* @param name The type name.
* @return The alias of the given type, if it exists. If there is no alias
* for this type, then returns the input type name.
*/
public String getAlias(String name) {
if (typeAliases != null && typeAliases.containsKey(name)) {
return typeAliases.get(name);
}
return name;
}
/**
* Output the Getter name for boolean property, e.g. getActive
*
* @param name the name of the property
* @return getter name based on naming convention
*/
public String toBooleanGetter(String name) {
return "get" + getterAndSetterCapitalize(name);
}
/**
* Output the Getter name, e.g. getSize
*
* @param name the name of the property
* @return getter name based on naming convention
*/
public String toGetter(String name) {
return "get" + getterAndSetterCapitalize(name);
}
/**
* Output the Setter name, e.g. setSize
*
* @param name the name of the property
* @return setter name based on naming convention
*/
public String toSetter(String name) {
return "set" + getterAndSetterCapitalize(name);
}
/**
* Output the API (class) name (capitalized) ending with "Api"
* Return DefaultApi if name is empty
*
* @param name the name of the Api
* @return capitalized Api name ending with "Api"
*/
public String toApiName(String name) {
if (name.length() == 0) {
return "DefaultApi";
}
return camelize(name) + "Api";
}
/**
* Output the proper model name (capitalized).
* In case the name belongs to the TypeSystem it won't be renamed.
*
* @param name the name of the model
* @return capitalized model name
*/
public String toModelName(final String name) {
return camelize(modelNamePrefix + "_" + name + "_" + modelNameSuffix);
}
/**
* Convert OAS Model object to Codegen Model object
*
* @param name the name of the model
* @param schema OAS Model object
* @return Codegen Model object
*/
public CodegenModel fromModel(String name, Schema schema) {
Map allDefinitions = ModelUtils.getSchemas(this.openAPI);
if (typeAliases == null) {
// Only do this once during first call
typeAliases = getAllAliases(allDefinitions);
}
// unalias schema
schema = ModelUtils.unaliasSchema(this.openAPI, schema);
if (schema == null) {
LOGGER.warn("Schema {} not found", name);
return null;
}
CodegenModel m = CodegenModelFactory.newInstance(CodegenModelType.MODEL);
if (reservedWords.contains(name)) {
m.name = escapeReservedWord(name);
} else {
m.name = name;
}
m.title = escapeText(schema.getTitle());
m.description = escapeText(schema.getDescription());
m.unescapedDescription = schema.getDescription();
m.classname = toModelName(name);
m.classVarName = toVarName(name);
m.classFilename = toModelFilename(name);
m.modelJson = Json.pretty(schema);
m.externalDocumentation = schema.getExternalDocs();
if (schema.getExtensions() != null && !schema.getExtensions().isEmpty()) {
m.getVendorExtensions().putAll(schema.getExtensions());
}
m.isAlias = typeAliases.containsKey(name);
m.discriminator = createDiscriminator(name, schema);
if (schema.getXml() != null) {
m.xmlPrefix = schema.getXml().getPrefix();
m.xmlNamespace = schema.getXml().getNamespace();
m.xmlName = schema.getXml().getName();
}
if (ModelUtils.isArraySchema(schema)) {
m.isArrayModel = true;
m.arrayModelType = fromProperty(name, schema).complexType;
addParentContainer(m, name, schema);
} else if (schema instanceof ComposedSchema) {
final ComposedSchema composed = (ComposedSchema) schema;
Map properties = new LinkedHashMap();
List required = new ArrayList();
Map allProperties = new LinkedHashMap();
List allRequired = new ArrayList();
// parent model
final String parentName = ModelUtils.getParentName(composed, allDefinitions);
final List allParents = ModelUtils.getAllParentsName(composed, allDefinitions);
final Schema parent = StringUtils.isBlank(parentName) || allDefinitions == null ? null : allDefinitions.get(parentName);
final boolean hasParent = StringUtils.isNotBlank(parentName);
// TODO revise the logic below to set dicriminator, xml attributes
if (supportsInheritance || supportsMixins) {
m.allVars = new ArrayList();
if (composed.getAllOf() != null) {
int modelImplCnt = 0; // only one inline object allowed in a ComposedModel
for (Schema innerSchema : composed.getAllOf()) { // TOOD need to work with anyOf, oneOf as well
if (m.discriminator == null) {
LOGGER.debug("discriminator is set to null (not correctly set earlier): {}", name);
m.discriminator = createDiscriminator(name, schema);
}
if (innerSchema.getXml() != null) {
m.xmlPrefix = innerSchema.getXml().getPrefix();
m.xmlNamespace = innerSchema.getXml().getNamespace();
m.xmlName = innerSchema.getXml().getName();
}
if (modelImplCnt++ > 1) {
LOGGER.warn("More than one inline schema specified in allOf:. Only the first one is recognized. All others are ignored.");
break; // only one schema with discriminator allowed in allOf
}
}
}
}
// interfaces (schemas defined in allOf, anyOf, oneOf)
List interfaces = ModelUtils.getInterfaces(composed);
if (!interfaces.isEmpty()) {
// m.interfaces is for backward compatibility
if (m.interfaces == null)
m.interfaces = new ArrayList();
for (Schema interfaceSchema : interfaces) {
if (StringUtils.isBlank(interfaceSchema.get$ref())) {
continue;
}
Schema refSchema = null;
String ref = ModelUtils.getSimpleRef(interfaceSchema.get$ref());
if (allDefinitions != null) {
refSchema = allDefinitions.get(ref);
}
final String modelName = toModelName(ref);
m.interfaces.add(modelName);
addImport(m, modelName);
if (allDefinitions != null && refSchema != null) {
if (allParents.contains(modelName) && supportsMultipleInheritance) {
// multiple inheritance
addProperties(allProperties, allRequired, refSchema);
} else if (parentName != null && parentName.equals(modelName) && supportsInheritance) {
// single inheritance
addProperties(allProperties, allRequired, refSchema);
} else {
// composition
addProperties(properties, required, refSchema);
}
}
if (composed.getAnyOf() != null) {
m.anyOf.add(modelName);
} else if (composed.getOneOf() != null) {
m.oneOf.add(modelName);
} else if (composed.getAllOf() != null) {
m.allOf.add(modelName);
} else {
LOGGER.error("Composed schema has incorrect anyOf, allOf, oneOf defined: {}", composed);
}
}
}
if (parent != null) {
m.parentSchema = parentName;
m.parent = toModelName(parentName);
if (supportsMultipleInheritance) {
m.allParents = new ArrayList();
for (String pname : allParents) {
String pModelName = toModelName(pname);
m.allParents.add(pModelName);
addImport(m, pModelName);
}
} else { // single inheritance
addImport(m, m.parent);
}
}
// child schema (properties owned by the schema itself)
for (Schema component : interfaces) {
if (component.get$ref() == null) {
if (component != null) {
// component is the child schema
addProperties(properties, required, component);
// includes child's properties (all, required) in allProperties, allRequired
addProperties(allProperties, allRequired, component);
}
break; // at most one child only
}
}
addVars(m, unaliasPropertySchema(properties), required, unaliasPropertySchema(allProperties), allRequired);
// end of code block for composed schema
} else {
m.dataType = getSchemaType(schema);
if (schema.getEnum() != null && !schema.getEnum().isEmpty()) {
m.isEnum = true;
// comment out below as allowableValues is not set in post processing model enum
m.allowableValues = new HashMap();
m.allowableValues.put("values", schema.getEnum());
}
if (ModelUtils.isMapSchema(schema)) {
addAdditionPropertiesToCodeGenModel(m, schema);
m.isMapModel = true;
}
if (ModelUtils.isIntegerSchema(schema)) { // integer type
if (!ModelUtils.isLongSchema(schema)) { // long type is not integer
m.isInteger = Boolean.TRUE;
}
}
if (ModelUtils.isStringSchema(schema)) {
m.isString = Boolean.TRUE;
}
// passing null to allProperties and allRequired as there's no parent
addVars(m, unaliasPropertySchema(schema.getProperties()), schema.getRequired(), null, null);
}
// remove duplicated properties
m.removeAllDuplicatedProperty();
// post process model properties
if (m.vars != null) {
for (CodegenProperty prop : m.vars) {
postProcessModelProperty(m, prop);
}
}
return m;
}
private CodegenDiscriminator createDiscriminator(String schemaName, Schema schema) {
if (schema.getDiscriminator() == null) {
return null;
}
CodegenDiscriminator discriminator = new CodegenDiscriminator();
discriminator.setPropertyName(toVarName(schema.getDiscriminator().getPropertyName()));
discriminator.setMapping(schema.getDiscriminator().getMapping());
if (schema.getDiscriminator().getMapping() != null && !schema.getDiscriminator().getMapping().isEmpty()) {
for (Entry e : schema.getDiscriminator().getMapping().entrySet()) {
String name = toModelName(ModelUtils.getSimpleRef(e.getValue())); // e.g e.getValue => #/components/schemas/Dog
discriminator.getMappedModels().add(new MappedModel(e.getKey(), name));
}
} else {
Map allDefinitions = ModelUtils.getSchemas(this.openAPI);
allDefinitions.forEach((childName, child) -> {
if (child instanceof ComposedSchema && ((ComposedSchema) child).getAllOf() != null) {
Set parentSchemas = ((ComposedSchema) child).getAllOf().stream()
.filter(s -> s.get$ref() != null)
.map(s -> ModelUtils.getSimpleRef(s.get$ref()))
.collect(Collectors.toSet());
if (parentSchemas.contains(schemaName)) {
discriminator.getMappedModels().add(new MappedModel(childName, toModelName(childName)));
}
}
});
}
return discriminator;
}
protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) {
addParentContainer(codegenModel, codegenModel.name, schema);
}
/**
* Add schema's properties to "properties" and "required" list
*
* @param properties all properties
* @param required required property only
* @param schema schema in which the properties will be added to the lists
*/
protected void addProperties(Map properties, List required, Schema schema) {
if (schema instanceof ComposedSchema) {
ComposedSchema composedSchema = (ComposedSchema) schema;
for (Schema component : composedSchema.getAllOf()) {
addProperties(properties, required, component);
}
if (composedSchema.getOneOf() != null) {
throw new RuntimeException("Please report the issue: Cannot process oneOf (Composed Scheme) in addProperties: " + schema);
}
if (composedSchema.getAnyOf() != null) {
throw new RuntimeException("Please report the issue: Cannot process anyOf (Composed Schema) in addProperties: " + schema);
}
return;
}
if (StringUtils.isNotBlank(schema.get$ref())) {
Schema interfaceSchema = ModelUtils.getReferencedSchema(this.openAPI, schema);
addProperties(properties, required, interfaceSchema);
return;
}
if (schema.getProperties() != null) {
properties.putAll(schema.getProperties());
}
if (schema.getRequired() != null) {
required.addAll(schema.getRequired());
}
}
/**
* Camelize the method name of the getter and setter
*
* @param name string to be camelized
* @return Camelized string
*/
public String getterAndSetterCapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
return camelize(toVarName(name));
}
/**
* Convert OAS Property object to Codegen Property object
*
* @param name name of the property
* @param p OAS property object
* @return Codegen Property object
*/
public CodegenProperty fromProperty(String name, Schema p) {
if (p == null) {
LOGGER.error("Unexpected missing property for name " + name);
return null;
}
LOGGER.debug("debugging fromProperty for " + name + " : " + p);
// unalias schema
p = ModelUtils.unaliasSchema(this.openAPI, p);
CodegenProperty property = CodegenModelFactory.newInstance(CodegenModelType.PROPERTY);
property.name = toVarName(name);
property.baseName = name;
if (p.getType() == null) {
property.openApiType = getSchemaType(p);
} else {
property.openApiType = p.getType();
}
property.nameInCamelCase = camelize(property.name, false);
property.nameInSnakeCase = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, property.nameInCamelCase);
property.description = escapeText(p.getDescription());
property.unescapedDescription = p.getDescription();
property.title = p.getTitle();
property.getter = toGetter(name);
property.setter = toSetter(name);
property.example = toExampleValue(p);
property.defaultValue = toDefaultValue(p);
property.defaultValueWithParam = toDefaultValueWithParam(name, p);
property.jsonSchema = Json.pretty(p);
if (p.getReadOnly() != null) {
property.isReadOnly = p.getReadOnly();
}
if (p.getWriteOnly() != null) {
property.isWriteOnly = p.getWriteOnly();
}
if (p.getNullable() != null) {
property.isNullable = p.getNullable();
}
if (p.getXml() != null) {
if (p.getXml().getAttribute() != null) {
property.isXmlAttribute = p.getXml().getAttribute();
}
property.xmlPrefix = p.getXml().getPrefix();
property.xmlName = p.getXml().getName();
property.xmlNamespace = p.getXml().getNamespace();
}
if (p.getExtensions() != null && !p.getExtensions().isEmpty()) {
property.getVendorExtensions().putAll(p.getExtensions());
}
String type = getSchemaType(p);
if (ModelUtils.isIntegerSchema(p)) { // integer type
property.isNumeric = Boolean.TRUE;
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) { // int64/long format
property.isLong = Boolean.TRUE;
} else { // int32 format
property.isInteger = Boolean.TRUE;
}
if (p.getMinimum() != null) {
property.minimum = String.valueOf(p.getMinimum().longValue());
}
if (p.getMaximum() != null) {
property.maximum = String.valueOf(p.getMaximum().longValue());
}
if (p.getExclusiveMinimum() != null) {
property.exclusiveMinimum = p.getExclusiveMinimum();
}
if (p.getExclusiveMaximum() != null) {
property.exclusiveMaximum = p.getExclusiveMaximum();
}
// check if any validation rule defined
// exclusive* are noop without corresponding min/max
if (property.minimum != null || property.maximum != null)
property.hasValidation = true;
} else if (ModelUtils.isBooleanSchema(p)) { // boolean type
property.isBoolean = true;
property.getter = toBooleanGetter(name);
} else if (ModelUtils.isDateSchema(p)) { // date format
property.isString = false; // for backward compatibility with 2.x
property.isDate = true;
} else if (ModelUtils.isDateTimeSchema(p)) { // date-time format
property.isString = false; // for backward compatibility with 2.x
property.isDateTime = true;
} else if (ModelUtils.isStringSchema(p)) {
if (ModelUtils.isByteArraySchema(p)) {
property.isByteArray = true;
} else if (ModelUtils.isBinarySchema(p)) {
property.isBinary = true;
property.isFile = true; // file = binary in OAS3
} else if (ModelUtils.isFileSchema(p)) {
property.isFile = true;
} else if (ModelUtils.isUUIDSchema(p)) {
// keep isString to true to make it backward compatible
property.isString = true;
property.isUuid = true;
} else if (ModelUtils.isEmailSchema(p)) {
property.isString = true;
property.isEmail = true;
} else {
property.isString = true;
}
property.maxLength = p.getMaxLength();
property.minLength = p.getMinLength();
property.pattern = toRegularExpression(p.getPattern());
// check if any validation rule defined
if (property.pattern != null || property.minLength != null || property.maxLength != null)
property.hasValidation = true;
} else if (ModelUtils.isNumberSchema(p)) {
property.isNumeric = Boolean.TRUE;
if (ModelUtils.isFloatSchema(p)) { // float
property.isFloat = Boolean.TRUE;
} else if (ModelUtils.isDoubleSchema(p)) { // double
property.isDouble = Boolean.TRUE;
} else { // type is number and without format
property.isNumber = Boolean.TRUE;
}
if (p.getMinimum() != null) {
property.minimum = String.valueOf(p.getMinimum());
}
if (p.getMaximum() != null) {
property.maximum = String.valueOf(p.getMaximum());
}
if (p.getExclusiveMinimum() != null) {
property.exclusiveMinimum = p.getExclusiveMinimum();
}
if (p.getExclusiveMaximum() != null) {
property.exclusiveMaximum = p.getExclusiveMaximum();
}
// check if any validation rule defined
// exclusive* are noop without corresponding min/max
if (property.minimum != null || property.maximum != null)
property.hasValidation = true;
} else if (ModelUtils.isFreeFormObject(p)) {
property.isFreeFormObject = true;
}
//Inline enum case:
if (p.getEnum() != null && !p.getEnum().isEmpty()) {
List _enum = p.getEnum();
property._enum = new ArrayList();
for (Object i : _enum) {
property._enum.add(String.valueOf(i));
}
property.isEnum = true;
Map allowableValues = new HashMap();
allowableValues.put("values", _enum);
if (allowableValues.size() > 0) {
property.allowableValues = allowableValues;
}
}
Schema referencedSchema = ModelUtils.getReferencedSchema(this.openAPI, p);
//Referenced enum case:
if (referencedSchema.getEnum() != null && !referencedSchema.getEnum().isEmpty()) {
List _enum = referencedSchema.getEnum();
Map allowableValues = new HashMap();
allowableValues.put("values", _enum);
if (allowableValues.size() > 0) {
property.allowableValues = allowableValues;
}
}
if (referencedSchema.getNullable() != null) {
property.isNullable = referencedSchema.getNullable();
}
property.dataType = getTypeDeclaration(p);
property.dataFormat = p.getFormat();
property.baseType = getSchemaType(p);
// this can cause issues for clients which don't support enums
if (property.isEnum) {
property.datatypeWithEnum = toEnumName(property);
property.enumName = toEnumName(property);
} else {
property.datatypeWithEnum = property.dataType;
}
if (ModelUtils.isArraySchema(p)) {
property.isContainer = true;
property.isListContainer = true;
property.containerType = "array";
property.baseType = getSchemaType(p);
if (p.getXml() != null) {
property.isXmlWrapped = p.getXml().getWrapped() == null ? false : p.getXml().getWrapped();
property.xmlPrefix = p.getXml().getPrefix();
property.xmlNamespace = p.getXml().getNamespace();
property.xmlName = p.getXml().getName();
}
// handle inner property
property.maxItems = p.getMaxItems();
property.minItems = p.getMinItems();
String itemName = null;
if (p.getExtensions() != null && p.getExtensions().get("x-item-name") != null) {
itemName = p.getExtensions().get("x-item-name").toString();
}
if (itemName == null) {
itemName = property.name;
}
Schema innerSchema = ModelUtils.unaliasSchema(this.openAPI, ((ArraySchema) p).getItems());
CodegenProperty cp = fromProperty(itemName, innerSchema);
updatePropertyForArray(property, cp);
} else if (ModelUtils.isMapSchema(p)) {
property.isContainer = true;
property.isMapContainer = true;
property.containerType = "map";
property.baseType = getSchemaType(p);
property.minItems = p.getMinProperties();
property.maxItems = p.getMaxProperties();
// handle inner property
Schema innerSchema = ModelUtils.unaliasSchema(this.openAPI, ModelUtils.getAdditionalProperties(p));
CodegenProperty cp = fromProperty("inner", innerSchema);
updatePropertyForMap(property, cp);
} else if (ModelUtils.isFreeFormObject(p)) {
property.isFreeFormObject = true;
property.baseType = getSchemaType(p);
} else { // model
// TODO revise the logic below
//if (StringUtils.isNotBlank(p.get$ref())) {
// property.baseType = getSimpleRef(p.get$ref());
//}
// --END of revision
setNonArrayMapProperty(property, type);
}
LOGGER.debug("debugging from property return: " + property);
return property;
}
/**
* Update property for array(list) container
*
* @param property Codegen property
* @param innerProperty Codegen inner property of map or list
*/
protected void updatePropertyForArray(CodegenProperty property, CodegenProperty innerProperty) {
if (innerProperty == null) {
LOGGER.warn("skipping invalid array property " + Json.pretty(property));
return;
}
property.dataFormat = innerProperty.dataFormat;
if (!languageSpecificPrimitives.contains(innerProperty.baseType)) {
property.complexType = innerProperty.baseType;
} else {
property.isPrimitiveType = true;
}
property.items = innerProperty;
property.mostInnerItems = getMostInnerItems(innerProperty);
// inner item is Enum
if (isPropertyInnerMostEnum(property)) {
// isEnum is set to true when the type is an enum
// or the inner type of an array/map is an enum
property.isEnum = true;
// update datatypeWithEnum and default value for array
// e.g. List => List
updateDataTypeWithEnumForArray(property);
// set allowable values to enum values (including array/map of enum)
property.allowableValues = getInnerEnumAllowableValues(property);
}
}
/**
* Update property for map container
*
* @param property Codegen property
* @param innerProperty Codegen inner property of map or list
*/
protected void updatePropertyForMap(CodegenProperty property, CodegenProperty innerProperty) {
if (innerProperty == null) {
LOGGER.warn("skipping invalid map property " + Json.pretty(property));
return;
}
if (!languageSpecificPrimitives.contains(innerProperty.baseType)) {
property.complexType = innerProperty.baseType;
} else {
property.isPrimitiveType = true;
}
property.items = innerProperty;
property.mostInnerItems = getMostInnerItems(innerProperty);
property.dataFormat = innerProperty.dataFormat;
// inner item is Enum
if (isPropertyInnerMostEnum(property)) {
// isEnum is set to true when the type is an enum
// or the inner type of an array/map is an enum
property.isEnum = true;
// update datatypeWithEnum and default value for map
// e.g. Dictionary => Dictionary
updateDataTypeWithEnumForMap(property);
// set allowable values to enum values (including array/map of enum)
property.allowableValues = getInnerEnumAllowableValues(property);
}
}
/**
* Update property for map container
*
* @param property Codegen property
* @return True if the inner most type is enum
*/
protected Boolean isPropertyInnerMostEnum(CodegenProperty property) {
CodegenProperty currentProperty = getMostInnerItems(property);
return currentProperty == null ? false : currentProperty.isEnum;
}
protected CodegenProperty getMostInnerItems(CodegenProperty property) {
CodegenProperty currentProperty = property;
while (currentProperty != null && (Boolean.TRUE.equals(currentProperty.isMapContainer)
|| Boolean.TRUE.equals(currentProperty.isListContainer))) {
currentProperty = currentProperty.items;
}
return currentProperty;
}
protected Map getInnerEnumAllowableValues(CodegenProperty property) {
CodegenProperty currentProperty = getMostInnerItems(property);
return currentProperty == null ? new HashMap() : currentProperty.allowableValues;
}
/**
* Update datatypeWithEnum for array container
*
* @param property Codegen property
*/
protected void updateDataTypeWithEnumForArray(CodegenProperty property) {
CodegenProperty baseItem = property.items;
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer)
|| Boolean.TRUE.equals(baseItem.isListContainer))) {
baseItem = baseItem.items;
}
if (baseItem != null) {
// set both datatype and datetypeWithEnum as only the inner type is enum
property.datatypeWithEnum = property.datatypeWithEnum.replace(baseItem.baseType, toEnumName(baseItem));
// naming the enum with respect to the language enum naming convention
// e.g. remove [], {} from array/map of enum
property.enumName = toEnumName(property);
// set default value for variable with inner enum
if (property.defaultValue != null) {
property.defaultValue = property.defaultValue.replace(baseItem.baseType, toEnumName(baseItem));
}
updateCodegenPropertyEnum(property);
}
}
/**
* Update datatypeWithEnum for map container
*
* @param property Codegen property
*/
protected void updateDataTypeWithEnumForMap(CodegenProperty property) {
CodegenProperty baseItem = property.items;
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer)
|| Boolean.TRUE.equals(baseItem.isListContainer))) {
baseItem = baseItem.items;
}
if (baseItem != null) {
// set both datatype and datetypeWithEnum as only the inner type is enum
property.datatypeWithEnum = property.datatypeWithEnum.replace(", " + baseItem.baseType, ", " + toEnumName(baseItem));
// naming the enum with respect to the language enum naming convention
// e.g. remove [], {} from array/map of enum
property.enumName = toEnumName(property);
// set default value for variable with inner enum
if (property.defaultValue != null) {
property.defaultValue = property.defaultValue.replace(", " + property.items.baseType, ", " + toEnumName(property.items));
}
updateCodegenPropertyEnum(property);
}
}
protected void setNonArrayMapProperty(CodegenProperty property, String type) {
property.isContainer = false;
if (languageSpecificPrimitives().contains(type)) {
property.isPrimitiveType = true;
} else {
property.complexType = property.baseType;
property.isModel = true;
}
}
/**
* Override with any special handling of response codes
*
* @param responses OAS Operation's responses
* @return default method response or null
if not found
*/
protected ApiResponse findMethodResponse(ApiResponses responses) {
String code = null;
for (String responseCode : responses.keySet()) {
if (responseCode.startsWith("2") || responseCode.equals("default")) {
if (code == null || code.compareTo(responseCode) > 0) {
code = responseCode;
}
}
}
if (code == null) {
return null;
}
return responses.get(code);
}
/**
* Convert OAS Operation object to Codegen Operation object
*
* @param httpMethod HTTP method
* @param operation OAS operation object
* @param path the path of the operation
* @param servers list of servers
* @return Codegen Operation object
*/
public CodegenOperation fromOperation(String path,
String httpMethod,
Operation operation,
List servers) {
LOGGER.debug("fromOperation => operation: " + operation);
if (operation == null)
throw new RuntimeException("operation cannot be null in fromOperation");
Map schemas = ModelUtils.getSchemas(this.openAPI);
CodegenOperation op = CodegenModelFactory.newInstance(CodegenModelType.OPERATION);
Set imports = new HashSet();
if (operation.getExtensions() != null && !operation.getExtensions().isEmpty()) {
op.vendorExtensions.putAll(operation.getExtensions());
Object isCallbackRequest = op.vendorExtensions.remove("x-callback-request");
op.isCallbackRequest = Boolean.TRUE.equals(isCallbackRequest);
}
// servers setting
if (operation.getServers() != null && !operation.getServers().isEmpty()) {
// use operation-level servers first if defined
op.servers = fromServers(operation.getServers());
} else if (servers != null && !servers.isEmpty()) {
// use path-level servers
op.servers = fromServers(servers);
}
// store the original operationId for plug-in
op.operationIdOriginal = operation.getOperationId();
String operationId = getOrGenerateOperationId(operation, path, httpMethod);
// remove prefix in operationId
if (removeOperationIdPrefix) {
int offset = operationId.indexOf('_');
if (offset > -1) {
operationId = operationId.substring(offset + 1);
}
}
operationId = removeNonNameElementToCamelCase(operationId);
if (path.startsWith("/")) {
op.path = path;
} else {
op.path = "/" + path;
}
op.operationId = toOperationId(operationId);
op.summary = escapeText(operation.getSummary());
op.unescapedNotes = operation.getDescription();
op.notes = escapeText(operation.getDescription());
op.hasConsumes = false;
op.hasProduces = false;
if (operation.getDeprecated() != null) {
op.isDeprecated = operation.getDeprecated();
}
addConsumesInfo(operation, op);
if (operation.getResponses() != null && !operation.getResponses().isEmpty()) {
ApiResponse methodResponse = findMethodResponse(operation.getResponses());
for (String key : operation.getResponses().keySet()) {
ApiResponse response = operation.getResponses().get(key);
addProducesInfo(response, op);
CodegenResponse r = fromResponse(key, response);
r.hasMore = true;
if (r.baseType != null &&
!defaultIncludes.contains(r.baseType) &&
!languageSpecificPrimitives.contains(r.baseType)) {
imports.add(r.baseType);
}
r.isDefault = response == methodResponse;
op.responses.add(r);
if (Boolean.TRUE.equals(r.isBinary) && Boolean.TRUE.equals(r.isDefault)) {
op.isResponseBinary = Boolean.TRUE;
}
if (Boolean.TRUE.equals(r.isFile) && Boolean.TRUE.equals(r.isDefault)) {
op.isResponseFile = Boolean.TRUE;
}
}
op.responses.get(op.responses.size() - 1).hasMore = false;
if (methodResponse != null) {
Schema responseSchema = ModelUtils.unaliasSchema(this.openAPI, ModelUtils.getSchemaFromResponse(methodResponse));
if (responseSchema != null) {
CodegenProperty cm = fromProperty("response", responseSchema);
if (ModelUtils.isArraySchema(responseSchema)) {
ArraySchema as = (ArraySchema) responseSchema;
CodegenProperty innerProperty = fromProperty("response", as.getItems());
op.returnBaseType = innerProperty.baseType;
} else if (ModelUtils.isMapSchema(responseSchema)) {
CodegenProperty innerProperty = fromProperty("response", ModelUtils.getAdditionalProperties(responseSchema));
op.returnBaseType = innerProperty.baseType;
} else {
if (cm.complexType != null) {
op.returnBaseType = cm.complexType;
} else {
op.returnBaseType = cm.baseType;
}
}
// generate examples
String exampleStatusCode = "200";
for (String key : operation.getResponses().keySet()) {
if (operation.getResponses().get(key) == methodResponse && !key.equals("default")) {
exampleStatusCode = key;
}
}
op.examples = new ExampleGenerator(schemas, this.openAPI).generateFromResponseSchema(exampleStatusCode, responseSchema, getProducesInfo(this.openAPI, operation));
op.defaultResponse = toDefaultValue(responseSchema);
op.returnType = cm.dataType;
op.hasReference = schemas.containsKey(op.returnBaseType);
// lookup discriminator
Schema schema = schemas.get(op.returnBaseType);
if (schema != null) {
CodegenModel cmod = fromModel(op.returnBaseType, schema);
op.discriminator = cmod.discriminator;
}
if (cm.isContainer) {
op.returnContainer = cm.containerType;
if ("map".equals(cm.containerType)) {
op.isMapContainer = true;
} else if ("list".equalsIgnoreCase(cm.containerType)) {
op.isListContainer = true;
} else if ("array".equalsIgnoreCase(cm.containerType)) {
op.isListContainer = true;
}
} else {
op.returnSimpleType = true;
}
if (languageSpecificPrimitives().contains(op.returnBaseType) || op.returnBaseType == null) {
op.returnTypeIsPrimitive = true;
}
}
addHeaders(methodResponse, op.responseHeaders);
}
}
if (operation.getCallbacks() != null && !operation.getCallbacks().isEmpty()) {
operation.getCallbacks().forEach((name, callback) -> {
CodegenCallback c = fromCallback(name, callback, servers);
c.hasMore = true;
op.callbacks.add(c);
});
op.callbacks.get(op.callbacks.size() - 1).hasMore = false;
}
List parameters = operation.getParameters();
List allParams = new ArrayList();
List bodyParams = new ArrayList();
List pathParams = new ArrayList();
List queryParams = new ArrayList();
List headerParams = new ArrayList();
List cookieParams = new ArrayList();
List formParams = new ArrayList();
List requiredParams = new ArrayList();
List optionalParams = new ArrayList();
CodegenParameter bodyParam = null;
RequestBody requestBody = operation.getRequestBody();
if (requestBody != null) {
if ("application/x-www-form-urlencoded".equalsIgnoreCase(getContentType(requestBody)) ||
"multipart/form-data".equalsIgnoreCase(getContentType(requestBody))) {
// process form parameters
formParams = fromRequestBodyToFormParameters(requestBody, imports);
for (CodegenParameter cp : formParams) {
postProcessParameter(cp);
}
// add form parameters to the beginning of all parameter list
if (prependFormOrBodyParameters) {
for (CodegenParameter cp : formParams) {
allParams.add(cp.copy());
}
}
} else {
// process body parameter
requestBody = ModelUtils.getReferencedRequestBody(this.openAPI, requestBody);
String bodyParameterName = "";
if (op.vendorExtensions != null && op.vendorExtensions.containsKey("x-codegen-request-body-name")) {
bodyParameterName = (String) op.vendorExtensions.get("x-codegen-request-body-name");
}
bodyParam = fromRequestBody(requestBody, imports, bodyParameterName);
bodyParam.description = escapeText(requestBody.getDescription());
postProcessParameter(bodyParam);
bodyParams.add(bodyParam);
if (prependFormOrBodyParameters) {
allParams.add(bodyParam);
}
// add example
if (schemas != null) {
op.requestBodyExamples = new ExampleGenerator(schemas, this.openAPI).generate(null, new ArrayList(getConsumesInfo(this.openAPI, operation)), bodyParam.baseType);
}
}
}
if (parameters != null) {
for (Parameter param : parameters) {
param = ModelUtils.getReferencedParameter(this.openAPI, param);
CodegenParameter p = fromParameter(param, imports);
// ensure unique params
if (ensureUniqueParams) {
if (!isParameterNameUnique(p, allParams)) {
p.paramName = generateNextName(p.paramName);
}
}
allParams.add(p);
if (param instanceof QueryParameter || "query".equalsIgnoreCase(param.getIn())) {
queryParams.add(p.copy());
} else if (param instanceof PathParameter || "path".equalsIgnoreCase(param.getIn())) {
pathParams.add(p.copy());
} else if (param instanceof HeaderParameter || "header".equalsIgnoreCase(param.getIn())) {
headerParams.add(p.copy());
} else if (param instanceof CookieParameter || "cookie".equalsIgnoreCase(param.getIn())) {
cookieParams.add(p.copy());
} else {
LOGGER.warn("Unknown parameter type " + p.baseType + " for " + p.baseName);
}
}
}
// add form/body parameter (if any) to the end of all parameter list
if (!prependFormOrBodyParameters) {
for (CodegenParameter cp : formParams) {
allParams.add(cp.copy());
}
for (CodegenParameter cp : bodyParams) {
allParams.add(cp.copy());
}
}
// create optional, required parameters
for (CodegenParameter cp : allParams) {
if (cp.required) { //required parameters
requiredParams.add(cp.copy());
} else { // optional parameters
optionalParams.add(cp.copy());
op.hasOptionalParams = true;
}
}
// add imports to operation import tag
for (String i : imports) {
if (needToImport(i)) {
op.imports.add(i);
}
}
op.bodyParam = bodyParam;
op.httpMethod = httpMethod.toUpperCase(Locale.ROOT);
// move "required" parameters in front of "optional" parameters
if (sortParamsByRequiredFlag) {
Collections.sort(allParams, new Comparator() {
@Override
public int compare(CodegenParameter one, CodegenParameter another) {
if (one.required == another.required) return 0;
else if (one.required) return -1;
else return 1;
}
});
}
op.allParams = addHasMore(allParams);
op.bodyParams = addHasMore(bodyParams);
op.pathParams = addHasMore(pathParams);
op.queryParams = addHasMore(queryParams);
op.headerParams = addHasMore(headerParams);
op.cookieParams = addHasMore(cookieParams);
op.formParams = addHasMore(formParams);
op.requiredParams = addHasMore(requiredParams);
op.optionalParams = addHasMore(optionalParams);
op.externalDocs = operation.getExternalDocs();
// legacy support
op.nickname = op.operationId;
if (op.allParams.size() > 0) {
op.hasParams = true;
}
op.hasRequiredParams = op.requiredParams.size() > 0;
// set Restful Flag
op.isRestfulShow = op.isRestfulShow();
op.isRestfulIndex = op.isRestfulIndex();
op.isRestfulCreate = op.isRestfulCreate();
op.isRestfulUpdate = op.isRestfulUpdate();
op.isRestfulDestroy = op.isRestfulDestroy();
op.isRestful = op.isRestful();
return op;
}
public boolean isParameterNameUnique(CodegenParameter p, List parameters) {
for (CodegenParameter parameter : parameters) {
if (System.identityHashCode(p) == System.identityHashCode(parameter)) {
continue; // skip itself
}
if (p.paramName.equals(parameter.paramName)) {
return false;
}
}
return true;
}
/**
* Convert OAS Response object to Codegen Response object
*
* @param responseCode HTTP response code
* @param response OAS Response object
* @return Codegen Response object
*/
public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
CodegenResponse r = CodegenModelFactory.newInstance(CodegenModelType.RESPONSE);
if ("default".equals(responseCode)) {
r.code = "0";
} else {
r.code = responseCode;
}
Schema responseSchema;
if (this.openAPI != null && this.openAPI.getComponents() != null) {
responseSchema = ModelUtils.unaliasSchema(this.openAPI, ModelUtils.getSchemaFromResponse(response));
} else { // no model/alias defined
responseSchema = ModelUtils.getSchemaFromResponse(response);
}
r.schema = responseSchema;
r.message = escapeText(response.getDescription());
// TODO need to revise and test examples in responses
// ApiResponse does not support examples at the moment
//r.examples = toExamples(response.getExamples());
r.jsonSchema = Json.pretty(response);
if (response.getExtensions() != null && !response.getExtensions().isEmpty()) {
r.vendorExtensions.putAll(response.getExtensions());
}
addHeaders(response, r.headers);
r.hasHeaders = !r.headers.isEmpty();
if (r.schema != null) {
Map allSchemas = null;
CodegenProperty cp = fromProperty("response", responseSchema);
if (ModelUtils.isArraySchema(responseSchema)) {
ArraySchema as = (ArraySchema) responseSchema;
CodegenProperty innerProperty = fromProperty("response", as.getItems());
CodegenProperty innerCp = innerProperty;
while (innerCp != null) {
r.baseType = innerCp.baseType;
innerCp = innerCp.items;
}
} else {
if (cp.complexType != null) {
r.baseType = cp.complexType;
r.isModel = true;
} else {
r.baseType = cp.baseType;
}
}
r.dataType = cp.dataType;
if (Boolean.TRUE.equals(cp.isString) && Boolean.TRUE.equals(cp.isEmail)) {
r.isEmail = true;
} else if (Boolean.TRUE.equals(cp.isString) && Boolean.TRUE.equals(cp.isUuid)) {
r.isUuid = true;
} else if (Boolean.TRUE.equals(cp.isByteArray)) {
r.isByteArray = true;
} else if (Boolean.TRUE.equals(cp.isString)) {
r.isString = true;
} else if (Boolean.TRUE.equals(cp.isBoolean)) {
r.isBoolean = true;
} else if (Boolean.TRUE.equals(cp.isLong)) {
r.isLong = true;
r.isNumeric = true;
} else if (Boolean.TRUE.equals(cp.isInteger)) {
r.isInteger = true;
r.isNumeric = true;
} else if (Boolean.TRUE.equals(cp.isNumber)) {
r.isNumber = true;
r.isNumeric = true;
} else if (Boolean.TRUE.equals(cp.isDouble)) {
r.isDouble = true;
r.isNumeric = true;
} else if (Boolean.TRUE.equals(cp.isFloat)) {
r.isFloat = true;
r.isNumeric = true;
} else if (Boolean.TRUE.equals(cp.isBinary)) {
r.isFile = true; // file = binary in OAS3
r.isBinary = true;
} else if (Boolean.TRUE.equals(cp.isFile)) {
r.isFile = true;
} else if (Boolean.TRUE.equals(cp.isDate)) {
r.isDate = true;
} else if (Boolean.TRUE.equals(cp.isDateTime)) {
r.isDateTime = true;
} else if (Boolean.TRUE.equals(cp.isFreeFormObject)) {
r.isFreeFormObject = true;
} else {
LOGGER.debug("Property type is not primitive: " + cp.dataType);
}
if (cp.isContainer) {
r.simpleType = false;
r.containerType = cp.containerType;
r.isMapContainer = "map".equals(cp.containerType);
r.isListContainer = "list".equalsIgnoreCase(cp.containerType) || "array".equalsIgnoreCase(cp.containerType);
} else {
r.simpleType = true;
}
r.primitiveType = (r.baseType == null || languageSpecificPrimitives().contains(r.baseType));
}
if (r.baseType == null) {
r.isMapContainer = false;
r.isListContainer = false;
r.primitiveType = true;
r.simpleType = true;
}
return r;
}
/**
* Convert OAS Callback object to Codegen Callback object
*
* @param name callback name
* @param callback OAS Callback object
* @param servers list of servers
* @return Codegen Response object
*/
public CodegenCallback fromCallback(String name, Callback callback, List servers) {
CodegenCallback c = new CodegenCallback();
c.name = name;
if (callback.getExtensions() != null && !callback.getExtensions().isEmpty()) {
c.vendorExtensions.putAll(callback.getExtensions());
}
callback.forEach((expression, pi) -> {
CodegenCallback.Url u = new CodegenCallback.Url();
u.expression = expression;
u.hasMore = true;
if (pi.getExtensions() != null && !pi.getExtensions().isEmpty()) {
u.vendorExtensions.putAll(pi.getExtensions());
}
Stream.of(
Pair.of("get", pi.getGet()),
Pair.of("head", pi.getHead()),
Pair.of("put", pi.getPut()),
Pair.of("post", pi.getPost()),
Pair.of("delete", pi.getDelete()),
Pair.of("patch", pi.getPatch()),
Pair.of("options", pi.getOptions()))
.filter(p -> p.getValue() != null)
.forEach(p -> {
String method = p.getKey();
Operation op = p.getValue();
boolean genId = op.getOperationId() == null;
if (genId) {
op.setOperationId(getOrGenerateOperationId(op, c.name + "_" + expression.replaceAll("\\{\\$.*}", ""), method));
}
if (op.getExtensions() == null) {
op.setExtensions(new HashMap<>());
}
// This extension will be removed later by `fromOperation()` as it is only needed here to
// distinguish between normal operations and callback requests
op.getExtensions().put("x-callback-request", true);
CodegenOperation co = fromOperation(expression, method, op, servers);
if (genId) {
co.operationIdOriginal = null;
// legacy (see `fromOperation()`)
co.nickname = co.operationId;
}
u.requests.add(co);
});
if (!u.requests.isEmpty()) {
u.requests.get(u.requests.size() - 1).hasMore = false;
}
c.urls.add(u);
});
if (!c.urls.isEmpty()) {
c.urls.get(c.urls.size() - 1).hasMore = false;
}
return c;
}
/**
* Convert OAS Parameter object to Codegen Parameter object
*
* @param parameter OAS parameter object
* @param imports set of imports for library/package/module
* @return Codegen Parameter object
*/
public CodegenParameter fromParameter(Parameter parameter, Set imports) {
CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER);
codegenParameter.baseName = parameter.getName();
codegenParameter.description = escapeText(parameter.getDescription());
codegenParameter.unescapedDescription = parameter.getDescription();
if (parameter.getRequired() != null) {
codegenParameter.required = parameter.getRequired();
}
codegenParameter.jsonSchema = Json.pretty(parameter);
if (GeneratorProperties.getProperty("debugParser") != null) {
LOGGER.info("working on Parameter " + parameter.getName());
LOGGER.info("JSON schema: " + codegenParameter.jsonSchema);
}
if (parameter.getExtensions() != null && !parameter.getExtensions().isEmpty()) {
codegenParameter.vendorExtensions.putAll(parameter.getExtensions());
}
if (parameter.getSchema() != null) {
Schema parameterSchema = ModelUtils.unaliasSchema(this.openAPI, parameter.getSchema());
if (parameterSchema == null) {
LOGGER.warn("warning! Schema not found for parameter \"" + parameter.getName() + "\", using String");
parameterSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to missing type definition.");
}
if (Boolean.TRUE.equals(parameterSchema.getNullable())) { // use nullable defined in the spec
codegenParameter.isNullable = true;
}
// set default value
codegenParameter.defaultValue = toDefaultValue(parameterSchema);
// TDOO revise collectionFormat
String collectionFormat = null;
if (ModelUtils.isArraySchema(parameterSchema)) { // for array parameter
final ArraySchema arraySchema = (ArraySchema) parameterSchema;
Schema inner = arraySchema.getItems();
if (inner == null) {
LOGGER.warn("warning! No inner type supplied for array parameter \"" + parameter.getName() + "\", using String");
inner = new StringSchema().description("//TODO automatically added by openapi-generator due to missing iner type definition in the spec");
arraySchema.setItems(inner);
}
collectionFormat = getCollectionFormat(parameter);
// default to csv:
collectionFormat = StringUtils.isEmpty(collectionFormat) ? "csv" : collectionFormat;
CodegenProperty codegenProperty = fromProperty("inner", inner);
codegenParameter.items = codegenProperty;
codegenParameter.mostInnerItems = codegenProperty.mostInnerItems;
codegenParameter.baseType = codegenProperty.dataType;
codegenParameter.isContainer = true;
codegenParameter.isListContainer = true;
// recursively add import
while (codegenProperty != null) {
imports.add(codegenProperty.baseType);
codegenProperty = codegenProperty.items;
}
} else if (ModelUtils.isMapSchema(parameterSchema)) { // for map parameter
CodegenProperty codegenProperty = fromProperty("inner", ModelUtils.getAdditionalProperties(parameterSchema));
codegenParameter.items = codegenProperty;
codegenParameter.mostInnerItems = codegenProperty.mostInnerItems;
codegenParameter.baseType = codegenProperty.dataType;
codegenParameter.isContainer = true;
codegenParameter.isMapContainer = true;
// recursively add import
while (codegenProperty != null) {
imports.add(codegenProperty.baseType);
codegenProperty = codegenProperty.items;
}
}
/* TODO revise the logic below
} else {
Map args = new HashMap();
String format = qp.getFormat();
args.put(PropertyId.ENUM, qp.getEnum());
property = PropertyBuilder.build(type, format, args);
}
*/
CodegenProperty codegenProperty = fromProperty(parameter.getName(), parameterSchema);
// TODO revise below which seems not working
//if (parameterSchema.getRequired() != null && !parameterSchema.getRequired().isEmpty() && parameterSchema.getRequired().contains(codegenProperty.baseName)) {
codegenProperty.required = Boolean.TRUE.equals(parameter.getRequired()) ? true : false;
//}
//codegenProperty.required = true;
// set boolean flag (e.g. isString)
setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty);
String parameterDataType = this.getParameterDataType(parameter, parameterSchema);
if (parameterDataType != null) {
codegenParameter.dataType = parameterDataType;
} else {
codegenParameter.dataType = codegenProperty.dataType;
}
codegenParameter.dataFormat = codegenProperty.dataFormat;
codegenParameter.required = codegenProperty.required;
if (codegenProperty.isEnum) {
codegenParameter.datatypeWithEnum = codegenProperty.datatypeWithEnum;
codegenParameter.enumName = codegenProperty.enumName;
}
// enum
updateCodegenPropertyEnum(codegenProperty);
codegenParameter.isEnum = codegenProperty.isEnum;
codegenParameter._enum = codegenProperty._enum;
codegenParameter.allowableValues = codegenProperty.allowableValues;
if (codegenProperty.items != null && codegenProperty.items.isEnum) {
codegenParameter.datatypeWithEnum = codegenProperty.datatypeWithEnum;
codegenParameter.enumName = codegenProperty.enumName;
codegenParameter.items = codegenProperty.items;
codegenParameter.mostInnerItems = codegenProperty.mostInnerItems;
}
codegenParameter.collectionFormat = collectionFormat;
if ("multi".equals(collectionFormat)) {
codegenParameter.isCollectionFormatMulti = true;
}
codegenParameter.paramName = toParamName(parameter.getName());
// import
if (codegenProperty.complexType != null) {
imports.add(codegenProperty.complexType);
}
// validation
// handle maximum, minimum properly for int/long by removing the trailing ".0"
if (ModelUtils.isIntegerSchema(parameterSchema)) {
codegenParameter.maximum = parameterSchema.getMaximum() == null ? null : String.valueOf(parameterSchema.getMaximum().longValue());
codegenParameter.minimum = parameterSchema.getMinimum() == null ? null : String.valueOf(parameterSchema.getMinimum().longValue());
} else {
codegenParameter.maximum = parameterSchema.getMaximum() == null ? null : String.valueOf(parameterSchema.getMaximum());
codegenParameter.minimum = parameterSchema.getMinimum() == null ? null : String.valueOf(parameterSchema.getMinimum());
}
codegenParameter.exclusiveMaximum = parameterSchema.getExclusiveMaximum() == null ? false : parameterSchema.getExclusiveMaximum();
codegenParameter.exclusiveMinimum = parameterSchema.getExclusiveMinimum() == null ? false : parameterSchema.getExclusiveMinimum();
codegenParameter.maxLength = parameterSchema.getMaxLength();
codegenParameter.minLength = parameterSchema.getMinLength();
codegenParameter.pattern = toRegularExpression(parameterSchema.getPattern());
codegenParameter.maxItems = parameterSchema.getMaxItems();
codegenParameter.minItems = parameterSchema.getMinItems();
codegenParameter.uniqueItems = parameterSchema.getUniqueItems() == null ? false : parameterSchema.getUniqueItems();
codegenParameter.multipleOf = parameterSchema.getMultipleOf();
// exclusive* are noop without corresponding min/max
if (codegenParameter.maximum != null || codegenParameter.minimum != null ||
codegenParameter.maxLength != null || codegenParameter.minLength != null ||
codegenParameter.maxItems != null || codegenParameter.minItems != null ||
codegenParameter.pattern != null) {
codegenParameter.hasValidation = true;
}
} else {
LOGGER.error("ERROR! Not handling " + parameter + " as Body Parameter at the moment");
/* TODO need to revise the logic below to handle body parameter
if (!(parameter instanceof BodyParameter)) {
LOGGER.error("Cannot use Parameter " + parameter + " as Body Parameter");
}
BodyParameter bp = (BodyParameter) param;
Model model = bp.getSchema();
if (model instanceof ModelImpl) {
ModelImpl impl = (ModelImpl) model;
CodegenModel cm = fromModel(bp.getName(), impl);
if (!cm.emptyVars) {
codegen.dataType = getTypeDeclaration(cm.classname);
imports.add(p.dataType);
} else {
Property prop = PropertyBuilder.build(impl.getType(), impl.getFormat(), null);
prop.setRequired(bp.getRequired());
CodegenProperty cp = fromProperty("property", prop);
if (cp != null) {
p.baseType = cp.baseType;
p.dataType = cp.datatype;
p.isPrimitiveType = cp.isPrimitiveType;
p.isBinary = isDataTypeBinary(cp.datatype);
p.isFile = isDataTypeFile(cp.datatype);
if (cp.complexType != null) {
imports.add(cp.complexType);
}
}
// set boolean flag (e.g. isString)
setParameterBooleanFlagWithCodegenProperty(p, cp);
}
} else if (model instanceof ArrayModel) {
// to use the built-in model parsing, we unwrap the ArrayModel
// and get a single property from it
ArrayModel impl = (ArrayModel) model;
// get the single property
ArrayProperty ap = new ArrayProperty().items(impl.getItems());
ap.setRequired(param.getRequired());
CodegenProperty cp = fromProperty("inner", ap);
if (cp.complexType != null) {
imports.add(cp.complexType);
}
imports.add(cp.baseType);
// recursively add import
CodegenProperty innerCp = cp;
while(innerCp != null) {
if(innerCp.complexType != null) {
imports.add(innerCp.complexType);
}
innerCp = innerCp.items;
}
p.items = cp;
p.dataType = cp.datatype;
p.baseType = cp.complexType;
p.isPrimitiveType = cp.isPrimitiveType;
p.isContainer = true;
p.isListContainer = true;
// set boolean flag (e.g. isString)
setParameterBooleanFlagWithCodegenProperty(p, cp);
} else {
Model sub = bp.getSchema();
if (sub instanceof RefModel) {
String name = ((RefModel) sub).getSimpleRef();
name = getAlias(name);
if (typeMapping.containsKey(name)) {
name = typeMapping.get(name);
p.baseType = name;
} else {
name = toModelName(name);
p.baseType = name;
if (defaultIncludes.contains(name)) {
imports.add(name);
}
imports.add(name);
name = getTypeDeclaration(name);
}
p.dataType = name;
}
}
p.paramName = toParamName(bp.getName());
*/
}
if (parameter instanceof QueryParameter || "query".equalsIgnoreCase(parameter.getIn())) {
codegenParameter.isQueryParam = true;
} else if (parameter instanceof PathParameter || "path".equalsIgnoreCase(parameter.getIn())) {
codegenParameter.required = true;
codegenParameter.isPathParam = true;
} else if (parameter instanceof HeaderParameter || "header".equalsIgnoreCase(parameter.getIn())) {
codegenParameter.isHeaderParam = true;
} else if (parameter instanceof CookieParameter || "cookie".equalsIgnoreCase(parameter.getIn())) {
codegenParameter.isCookieParam = true;
} else {
LOGGER.warn("Unknown parameter type: " + parameter.getName());
}
// default to UNKNOWN_PARAMETER_NAME if paramName is null
if (codegenParameter.paramName == null) {
LOGGER.warn("Parameter name not defined properly. Default to UNKNOWN_PARAMETER_NAME");
codegenParameter.paramName = "UNKNOWN_PARAMETER_NAME";
}
// set the parameter example value
// should be overridden by lang codegen
setParameterExampleValue(codegenParameter, parameter);
postProcessParameter(codegenParameter);
LOGGER.debug("debugging codegenParameter return: " + codegenParameter);
return codegenParameter;
}
/**
* Returns the data type of a parameter.
* Returns null by default to use the CodegenProperty.datatype value
*
* @param parameter Parameter
* @param schema Schema
* @return data type
*/
protected String getParameterDataType(Parameter parameter, Schema schema) {
return null;
}
// TODO revise below as it should be replaced by ModelUtils.isByteArraySchema(parameterSchema)
public boolean isDataTypeBinary(String dataType) {
if (dataType != null) {
return dataType.toLowerCase(Locale.ROOT).startsWith("byte");
} else {
return false;
}
}
// TODO revise below as it should be replaced by ModelUtils.isFileSchema(parameterSchema)
public boolean isDataTypeFile(String dataType) {
if (dataType != null) {
return dataType.toLowerCase(Locale.ROOT).equals("file");
} else {
return false;
}
}
/**
* Convert map of OAS SecurityScheme objects to a list of Codegen Security objects
*
* @param securitySchemeMap a map of OAS SecuritySchemeDefinition object
* @return a list of Codegen Security objects
*/
@SuppressWarnings("static-method")
public List fromSecurity(Map securitySchemeMap) {
if (securitySchemeMap == null) {
return Collections.emptyList();
}
List codegenSecurities = new ArrayList(securitySchemeMap.size());
for (String key : securitySchemeMap.keySet()) {
final SecurityScheme securityScheme = securitySchemeMap.get(key);
CodegenSecurity cs = CodegenModelFactory.newInstance(CodegenModelType.SECURITY);
cs.name = key;
cs.type = securityScheme.getType().toString();
cs.isCode = cs.isPassword = cs.isApplication = cs.isImplicit = false;
cs.isBasicBasic = cs.isBasicBearer = false;
cs.scheme = securityScheme.getScheme();
if (SecurityScheme.Type.APIKEY.equals(securityScheme.getType())) {
cs.isBasic = cs.isOAuth = false;
cs.isApiKey = true;
cs.keyParamName = securityScheme.getName();
cs.isKeyInHeader = securityScheme.getIn() == SecurityScheme.In.HEADER;
cs.isKeyInQuery = securityScheme.getIn() == SecurityScheme.In.QUERY;
cs.isKeyInCookie = securityScheme.getIn() == SecurityScheme.In.COOKIE; //it assumes a validation step prior to generation. (cookie-auth supported from OpenAPI 3.0.0)
} else if (SecurityScheme.Type.HTTP.equals(securityScheme.getType())) {
cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isOAuth = false;
cs.isBasic = true;
if ("basic".equals(securityScheme.getScheme())) {
cs.isBasicBasic = true;
} else if ("bearer".equals(securityScheme.getScheme())) {
cs.isBasicBearer = true;
cs.bearerFormat = securityScheme.getBearerFormat();
}
} else if (SecurityScheme.Type.OAUTH2.equals(securityScheme.getType())) {
cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isBasic = false;
cs.isOAuth = true;
final OAuthFlows flows = securityScheme.getFlows();
if (securityScheme.getFlows() == null) {
throw new RuntimeException("missing oauth flow in " + cs.name);
}
if (flows.getPassword() != null) {
setOauth2Info(cs, flows.getPassword());
cs.isPassword = true;
cs.flow = "password";
} else if (flows.getImplicit() != null) {
setOauth2Info(cs, flows.getImplicit());
cs.isImplicit = true;
cs.flow = "implicit";
} else if (flows.getClientCredentials() != null) {
setOauth2Info(cs, flows.getClientCredentials());
cs.isApplication = true;
cs.flow = "application";
} else if (flows.getAuthorizationCode() != null) {
setOauth2Info(cs, flows.getAuthorizationCode());
cs.isCode = true;
cs.flow = "accessCode";
} else {
throw new RuntimeException("Could not identify any oauth2 flow in " + cs.name);
}
}
codegenSecurities.add(cs);
}
// sort auth methods to maintain the same order
Collections.sort(codegenSecurities, new Comparator() {
@Override
public int compare(CodegenSecurity one, CodegenSecurity another) {
return ObjectUtils.compare(one.name, another.name);
}
});
// set 'hasMore'
Iterator it = codegenSecurities.iterator();
while (it.hasNext()) {
final CodegenSecurity security = it.next();
security.hasMore = it.hasNext();
}
return codegenSecurities;
}
protected void setReservedWordsLowerCase(List words) {
reservedWords = new HashSet();
for (String word : words) {
reservedWords.add(word.toLowerCase(Locale.ROOT));
}
}
protected boolean isReservedWord(String word) {
return word != null && reservedWords.contains(word.toLowerCase(Locale.ROOT));
}
/**
* Get operationId from the operation object, and if it's blank, generate a new one from the given parameters.
*
* @param operation the operation object
* @param path the path of the operation
* @param httpMethod the HTTP method of the operation
* @return the (generated) operationId
*/
protected String getOrGenerateOperationId(Operation operation, String path, String httpMethod) {
String operationId = operation.getOperationId();
if (StringUtils.isBlank(operationId)) {
String tmpPath = path;
tmpPath = tmpPath.replaceAll("\\{", "");
tmpPath = tmpPath.replaceAll("\\}", "");
String[] parts = (tmpPath + "/" + httpMethod).split("/");
StringBuilder builder = new StringBuilder();
if ("/".equals(tmpPath)) {
// must be root tmpPath
builder.append("root");
}
for (String part : parts) {
if (part.length() > 0) {
if (builder.toString().length() == 0) {
part = Character.toLowerCase(part.charAt(0)) + part.substring(1);
} else {
part = camelize(part);
}
builder.append(part);
}
}
operationId = sanitizeName(builder.toString());
LOGGER.warn("Empty operationId found for path: " + httpMethod + " " + path + ". Renamed to auto-generated operationId: " + operationId);
}
return operationId;
}
/**
* Check the type to see if it needs import the library/module/package
*
* @param type name of the type
* @return true if the library/module/package of the corresponding type needs to be imported
*/
protected boolean needToImport(String type) {
return StringUtils.isNotBlank(type) && !defaultIncludes.contains(type)
&& !languageSpecificPrimitives.contains(type);
}
@SuppressWarnings("static-method")
protected List> toExamples(Map examples) {
if (examples == null) {
return null;
}
final List> output = new ArrayList>(examples.size());
for (Map.Entry entry : examples.entrySet()) {
final Map kv = new HashMap();
kv.put("contentType", entry.getKey());
kv.put("example", entry.getValue());
output.add(kv);
}
return output;
}
/**
* Add headers to codegen property
*
* @param response API response
* @param properties list of codegen property
*/
private void addHeaders(ApiResponse response, List properties) {
if (response.getHeaders() != null) {
for (Map.Entry headerEntry : response.getHeaders().entrySet()) {
String description = headerEntry.getValue().getDescription();
// follow the $ref
Header header = ModelUtils.getReferencedHeader(this.openAPI, headerEntry.getValue());
Schema schema;
if (header.getSchema() == null) {
LOGGER.warn("No schema defined for Header '" + headerEntry.getKey() + "', using a String schema");
schema = new StringSchema();
} else {
schema = header.getSchema();
}
CodegenProperty cp = fromProperty(headerEntry.getKey(), schema);
cp.setDescription(escapeText(description));
cp.setUnescapedDescription(description);
properties.add(cp);
}
}
}
private static List addHasMore(List objs) {
if (objs != null) {
for (int i = 0; i < objs.size(); i++) {
if (i > 0) {
objs.get(i).secondaryParam = true;
}
if (i < objs.size() - 1) {
objs.get(i).hasMore = true;
}
}
}
return objs;
}
/**
* Add operation to group
*
* @param tag name of the tag
* @param resourcePath path of the resource
* @param operation OAS Operation object
* @param co Codegen Operation object
* @param operations map of Codegen operations
*/
@SuppressWarnings("static-method")
public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation
co, Map> operations) {
List opList = operations.get(tag);
if (opList == null) {
opList = new ArrayList();
operations.put(tag, opList);
}
// check for operationId uniqueness
String uniqueName = co.operationId;
int counter = 0;
for (CodegenOperation op : opList) {
if (uniqueName.equals(op.operationId)) {
uniqueName = co.operationId + "_" + counter;
counter++;
}
}
if (!co.operationId.equals(uniqueName)) {
LOGGER.warn("generated unique operationId `" + uniqueName + "`");
}
co.operationId = uniqueName;
co.operationIdLowerCase = uniqueName.toLowerCase(Locale.ROOT);
co.operationIdCamelCase = camelize(uniqueName);
co.operationIdSnakeCase = underscore(uniqueName);
opList.add(co);
co.baseName = tag;
}
private void addParentContainer(CodegenModel model, String name, Schema schema) {
final CodegenProperty property = fromProperty(name, schema);
addImport(model, property.complexType);
model.parent = toInstantiationType(schema);
final String containerType = property.containerType;
final String instantiationType = instantiationTypes.get(containerType);
if (instantiationType != null) {
addImport(model, instantiationType);
}
final String mappedType = typeMapping.get(containerType);
if (mappedType != null) {
addImport(model, mappedType);
}
}
/**
* Generate the next name for the given name, i.e. append "2" to the base name if not ending with a number,
* otherwise increase the number by 1. For example:
* status => status2
* status2 => status3
* myName100 => myName101
*
* @param name The base name
* @return The next name for the base name
*/
private static String generateNextName(String name) {
Pattern pattern = Pattern.compile("\\d+\\z");
Matcher matcher = pattern.matcher(name);
if (matcher.find()) {
String numStr = matcher.group();
int num = Integer.parseInt(numStr) + 1;
return name.substring(0, name.length() - numStr.length()) + num;
} else {
return name + "2";
}
}
protected void addImport(CodegenModel m, String type) {
if (type != null && needToImport(type)) {
m.imports.add(type);
}
}
/**
* Loop through propertiies and unalias the reference if $ref (reference) is defined
*
* @param properties model properties (schemas)
* @return model properties with direct reference to schemas
*/
private Map unaliasPropertySchema(Map properties) {
if (properties != null) {
for (String key : properties.keySet()) {
properties.put(key, ModelUtils.unaliasSchema(this.openAPI, properties.get(key)));
}
}
return properties;
}
private void addVars(CodegenModel m, Map properties, List required,
Map allProperties, List allRequired) {
m.hasRequired = false;
if (properties != null && !properties.isEmpty()) {
m.hasVars = true;
m.hasEnums = false; // TODO need to fix as its false in both cases
Set mandatory = required == null ? Collections.emptySet()
: new TreeSet(required);
// update "vars" without parent's properties (all, required)
addVars(m, m.vars, properties, mandatory);
m.allMandatory = m.mandatory = mandatory;
} else {
m.emptyVars = true;
m.hasVars = false;
m.hasEnums = false; // TODO need to fix as its false in both cases
}
if (allProperties != null) {
Set allMandatory = allRequired == null ? Collections.emptySet()
: new TreeSet(allRequired);
// update "vars" with parent's properties (all, required)
addVars(m, m.allVars, allProperties, allMandatory);
m.allMandatory = allMandatory;
} else { // without parent, allVars and vars are the same
m.allVars = m.vars;
m.allMandatory = m.mandatory;
}
// loop through list to update property name with toVarName
Set renamedMandatory = new TreeSet();
Iterator mandatoryIterator = m.mandatory.iterator();
while (mandatoryIterator.hasNext()) {
renamedMandatory.add(toVarName(mandatoryIterator.next()));
}
m.mandatory = renamedMandatory;
Set renamedAllMandatory = new TreeSet();
Iterator allMandatoryIterator = m.allMandatory.iterator();
while (allMandatoryIterator.hasNext()) {
renamedAllMandatory.add(toVarName(allMandatoryIterator.next()));
}
m.allMandatory = renamedAllMandatory;
}
/**
* Add variables (properties) to codegen model (list of properties, various flags, etc)
*
* @param m Codegen model
* @param vars list of codegen properties (e.g. vars, allVars) to be updated with the new properties
* @param properties a map of properties (schema)
* @param mandatory a set of required properties' name
*/
private void addVars(CodegenModel m, List vars, Map properties, Set mandatory) {
for (Map.Entry entry : properties.entrySet()) {
final String key = entry.getKey();
final Schema prop = entry.getValue();
if (prop == null) {
LOGGER.warn("Please report the issue. There shouldn't be null property for " + key);
} else {
final CodegenProperty cp = fromProperty(key, prop);
cp.required = mandatory.contains(key);
m.hasRequired = m.hasRequired || cp.required;
m.hasOptional = m.hasOptional || !cp.required;
if (cp.isEnum) {
// FIXME: if supporting inheritance, when called a second time for allProperties it is possible for
// m.hasEnums to be set incorrectly if allProperties has enumerations but properties does not.
m.hasEnums = true;
}
// set model's hasOnlyReadOnly to false if the property is read-only
if (!Boolean.TRUE.equals(cp.isReadOnly)) {
m.hasOnlyReadOnly = false;
}
// TODO revise the logic to include map
if (cp.isContainer) {
addImport(m, typeMapping.get("array"));
}
addImport(m, cp.baseType);
CodegenProperty innerCp = cp;
while (innerCp != null) {
addImport(m, innerCp.complexType);
innerCp = innerCp.items;
}
vars.add(cp);
// if required, add to the list "requiredVars"
if (Boolean.TRUE.equals(cp.required)) {
m.requiredVars.add(cp);
} else { // else add to the list "optionalVars" for optional property
m.optionalVars.add(cp);
}
// if readonly, add to readOnlyVars (list of properties)
if (Boolean.TRUE.equals(cp.isReadOnly)) {
m.readOnlyVars.add(cp);
} else { // else add to readWriteVars (list of properties)
// duplicated properties will be removed by removeAllDuplicatedProperty later
m.readWriteVars.add(cp);
}
}
}
}
/**
* Determine all of the types in the model definitions (schemas) that are aliases of
* simple types.
*
* @param schemas The complete set of model definitions (schemas).
* @return A mapping from model name to type alias
*/
static Map getAllAliases(Map schemas) {
if (schemas == null || schemas.isEmpty()) {
return new HashMap<>();
}
Map aliases = new HashMap<>();
for (Map.Entry entry : schemas.entrySet()) {
String oasName = entry.getKey();
Schema schema = entry.getValue();
String schemaType = getPrimitiveType(schema);
if (schemaType != null && !schemaType.equals("object") && !schemaType.equals("array")
&& schema.getEnum() == null && !ModelUtils.isMapSchema(schema)) {
aliases.put(oasName, schemaType);
}
}
return aliases;
}
/**
* Remove characters not suitable for variable or method name from the input and camelize it
*
* @param name string to be camelize
* @return camelized string
*/
@SuppressWarnings("static-method")
public String removeNonNameElementToCamelCase(String name) {
return removeNonNameElementToCamelCase(name, "[-_:;#]");
}
/**
* Remove characters that is not good to be included in method name from the input and camelize it
*
* @param name string to be camelize
* @param nonNameElementPattern a regex pattern of the characters that is not good to be included in name
* @return camelized string
*/
protected String removeNonNameElementToCamelCase(final String name, final String nonNameElementPattern) {
String result = Arrays.stream(name.split(nonNameElementPattern))
.map(StringUtils::capitalize)
.collect(Collectors.joining(""));
if (result.length() > 0) {
result = result.substring(0, 1).toLowerCase(Locale.ROOT) + result.substring(1);
}
return result;
}
public String apiFilename(String templateName, String tag) {
String suffix = apiTemplateFiles().get(templateName);
return apiFileFolder() + File.separator + toApiFilename(tag) + suffix;
}
/**
* Return the full path and API documentation file
*
* @param templateName template name
* @param tag tag
* @return the API documentation file name with full path
*/
public String apiDocFilename(String templateName, String tag) {
String docExtension = getDocExtension();
String suffix = docExtension != null ? docExtension : apiDocTemplateFiles().get(templateName);
return apiDocFileFolder() + File.separator + toApiDocFilename(tag) + suffix;
}
/**
* Return the full path and API test file
*
* @param templateName template name
* @param tag tag
* @return the API test file name with full path
*/
public String apiTestFilename(String templateName, String tag) {
String suffix = apiTestTemplateFiles().get(templateName);
return apiTestFileFolder() + File.separator + toApiTestFilename(tag) + suffix;
}
public boolean shouldOverwrite(String filename) {
return !(skipOverwrite && new File(filename).exists());
}
public boolean isSkipOverwrite() {
return skipOverwrite;
}
public void setSkipOverwrite(boolean skipOverwrite) {
this.skipOverwrite = skipOverwrite;
}
public boolean isRemoveOperationIdPrefix() {
return removeOperationIdPrefix;
}
public void setRemoveOperationIdPrefix(boolean removeOperationIdPrefix) {
this.removeOperationIdPrefix = removeOperationIdPrefix;
}
public boolean isHideGenerationTimestamp() {
return hideGenerationTimestamp;
}
public void setHideGenerationTimestamp(boolean hideGenerationTimestamp) {
this.hideGenerationTimestamp = hideGenerationTimestamp;
}
/**
* All library templates supported.
* (key: library name, value: library description)
*
* @return the supported libraries
*/
public Map supportedLibraries() {
return supportedLibraries;
}
/**
* Set library template (sub-template).
*
* @param library Library template
*/
public void setLibrary(String library) {
if (library != null && !supportedLibraries.containsKey(library)) {
StringBuilder sb = new StringBuilder("Unknown library: " + library + "\nAvailable libraries:");
if (supportedLibraries.size() == 0) {
sb.append("\n ").append("NONE");
} else {
for (String lib : supportedLibraries.keySet()) {
sb.append("\n ").append(lib);
}
}
throw new RuntimeException(sb.toString());
}
this.library = library;
}
/**
* Library template (sub-template).
*
* @return Library template
*/
public String getLibrary() {
return library;
}
/**
* Set Git user ID.
*
* @param gitUserId Git user ID
*/
public void setGitUserId(String gitUserId) {
this.gitUserId = gitUserId;
}
/**
* Git user ID
*
* @return Git user ID
*/
public String getGitUserId() {
return gitUserId;
}
/**
* Set Git repo ID.
*
* @param gitRepoId Git repo ID
*/
public void setGitRepoId(String gitRepoId) {
this.gitRepoId = gitRepoId;
}
/**
* Git repo ID
*
* @return Git repo ID
*/
public String getGitRepoId() {
return gitRepoId;
}
/**
* Set release note.
*
* @param releaseNote Release note
*/
public void setReleaseNote(String releaseNote) {
this.releaseNote = releaseNote;
}
/**
* Release note
*
* @return Release note
*/
public String getReleaseNote() {
return releaseNote;
}
/**
* Documentation files extension
*
* @return Documentation files extension
*/
public String getDocExtension() {
return docExtension;
}
/**
* Set Documentation files extension
*
* @param userDocExtension documentation files extension
*/
public void setDocExtension(String userDocExtension) {
this.docExtension = userDocExtension;
}
/**
* Set HTTP user agent.
*
* @param httpUserAgent HTTP user agent
*/
public void setHttpUserAgent(String httpUserAgent) {
this.httpUserAgent = httpUserAgent;
}
/**
* HTTP user agent
*
* @return HTTP user agent
*/
public String getHttpUserAgent() {
return httpUserAgent;
}
@SuppressWarnings("static-method")
protected CliOption buildLibraryCliOption(Map supportedLibraries) {
StringBuilder sb = new StringBuilder("library template (sub-template) to use:");
for (String lib : supportedLibraries.keySet()) {
sb.append("\n").append(lib).append(" - ").append(supportedLibraries.get(lib));
}
return new CliOption("library", sb.toString());
}
/**
* Sanitize name (parameter, property, method, etc)
*
* @param name string to be sanitize
* @return sanitized string
*/
@SuppressWarnings("static-method")
public String sanitizeName(String name) {
return sanitizeName(name, "\\W");
}
/**
* Sanitize name (parameter, property, method, etc)
*
* @param name string to be sanitize
* @param removeCharRegEx a regex containing all char that will be removed
* @return sanitized string
*/
public String sanitizeName(String name, String removeCharRegEx) {
// NOTE: performance wise, we should have written with 2 replaceAll to replace desired
// character with _ or empty character. Below aims to spell out different cases we've
// encountered so far and hopefully make it easier for others to add more special
// cases in the future.
// better error handling when map/array type is invalid
if (name == null) {
LOGGER.error("String to be sanitized is null. Default to ERROR_UNKNOWN");
return "ERROR_UNKNOWN";
}
// if the name is just '$', map it to 'value' for the time being.
if ("$".equals(name)) {
return "value";
}
// input[] => input
name = name.replaceAll("\\[\\]", ""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
// input[a][b] => input_a_b
name = name.replaceAll("\\[", "_");
name = name.replaceAll("\\]", "");
// input(a)(b) => input_a_b
name = name.replaceAll("\\(", "_");
name = name.replaceAll("\\)", "");
// input.name => input_name
name = name.replaceAll("\\.", "_");
// input-name => input_name
name = name.replaceAll("-", "_");
// a|b => a_b
name = name.replace("|", "_");
// input name and age => input_name_and_age
name = name.replaceAll(" ", "_");
// /api/films/get => _api_films_get
// \api\films\get => _api_films_get
name = name.replaceAll("/", "_");
name = name.replaceAll("\\\\", "_");
// remove everything else other than word, number and _
// $php_variable => php_variable
if (allowUnicodeIdentifiers) { //could be converted to a single line with ?: operator
name = Pattern.compile(removeCharRegEx, Pattern.UNICODE_CHARACTER_CLASS).matcher(name).replaceAll("");
} else {
name = name.replaceAll(removeCharRegEx, "");
}
return name;
}
/**
* Sanitize tag
*
* @param tag Tag
* @return Sanitized tag
*/
public String sanitizeTag(String tag) {
tag = camelize(sanitizeName(tag));
// tag starts with numbers
if (tag.matches("^\\d.*")) {
tag = "Class" + tag;
}
return tag;
}
/**
* Only write if the file doesn't exist
*
* @param outputFolder Output folder
* @param supportingFile Supporting file
*/
public void writeOptional(String outputFolder, SupportingFile supportingFile) {
String folder = "";
if (outputFolder != null && !"".equals(outputFolder)) {
folder += outputFolder + File.separator;
}
folder += supportingFile.folder;
if (!"".equals(folder)) {
folder += File.separator + supportingFile.destinationFilename;
} else {
folder = supportingFile.destinationFilename;
}
if (!new File(folder).exists()) {
supportingFiles.add(supportingFile);
} else {
LOGGER.info("Skipped overwriting " + supportingFile.destinationFilename + " as the file already exists in " + folder);
}
}
/**
* Set CodegenParameter boolean flag using CodegenProperty.
*
* @param parameter Codegen Parameter
* @param property Codegen property
*/
public void setParameterBooleanFlagWithCodegenProperty(CodegenParameter parameter, CodegenProperty property) {
if (parameter == null) {
LOGGER.error("Codegen Parameter cannot be null.");
return;
}
if (property == null) {
LOGGER.error("Codegen Property cannot be null.");
return;
}
if (Boolean.TRUE.equals(property.isEmail) && Boolean.TRUE.equals(property.isString)) {
parameter.isEmail = true;
} else if (Boolean.TRUE.equals(property.isUuid) && Boolean.TRUE.equals(property.isString)) {
parameter.isUuid = true;
} else if (Boolean.TRUE.equals(property.isByteArray)) {
parameter.isByteArray = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isBinary)) {
parameter.isBinary = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isString)) {
parameter.isString = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isBoolean)) {
parameter.isBoolean = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isLong)) {
parameter.isLong = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isInteger)) {
parameter.isInteger = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isDouble)) {
parameter.isDouble = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isFloat)) {
parameter.isFloat = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isNumber)) {
parameter.isNumber = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isDate)) {
parameter.isDate = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isDateTime)) {
parameter.isDateTime = true;
parameter.isPrimitiveType = true;
} else if (Boolean.TRUE.equals(property.isFreeFormObject)) {
parameter.isFreeFormObject = true;
} else {
LOGGER.debug("Property type is not primitive: " + property.dataType);
}
if (Boolean.TRUE.equals(property.isFile)) {
parameter.isFile = true;
}
if (Boolean.TRUE.equals(property.isModel)) {
parameter.isModel = true;
}
}
/**
* Update codegen property's enum by adding "enumVars" (with name and value)
*
* @param var list of CodegenProperty
*/
public void updateCodegenPropertyEnum(CodegenProperty var) {
Map allowableValues = var.allowableValues;
// handle array
if (var.mostInnerItems != null) {
allowableValues = var.mostInnerItems.allowableValues;
}
if (allowableValues == null) {
return;
}
List values = (List) allowableValues.get("values");
if (values == null) {
return;
}
String varDataType = var.mostInnerItems != null ? var.mostInnerItems.dataType : var.dataType;
Optional referencedSchema = ModelUtils.getSchemas(openAPI).entrySet().stream()
.filter(entry -> Objects.equals(varDataType, toModelName(entry.getKey())))
.map(Map.Entry::getValue)
.findFirst();
String dataType = (referencedSchema.isPresent()) ? getTypeDeclaration(referencedSchema.get()) : varDataType;
// put "enumVars" map into `allowableValues", including `name` and `value`
List> enumVars = new ArrayList<>();
String commonPrefix = findCommonPrefixOfVars(values);
int truncateIdx = commonPrefix.length();
for (Object value : values) {
Map enumVar = new HashMap<>();
String enumName;
if (truncateIdx == 0) {
enumName = value.toString();
} else {
enumName = value.toString().substring(truncateIdx);
if ("".equals(enumName)) {
enumName = value.toString();
}
}
enumVar.put("name", toEnumVarName(enumName, dataType));
enumVar.put("value", toEnumValue(value.toString(), dataType));
enumVar.put("isString", isDataTypeString(dataType));
enumVars.add(enumVar);
}
// if "x-enum-varnames" or "x-enum-descriptions" defined, update varnames
Map extensions = var.mostInnerItems != null ? var.mostInnerItems.getVendorExtensions() : var.getVendorExtensions();
if (referencedSchema.isPresent()) {
extensions = referencedSchema.get().getExtensions();
}
updateEnumVarsWithExtensions(enumVars, extensions);
allowableValues.put("enumVars", enumVars);
// handle default value for enum, e.g. available => StatusEnum.AVAILABLE
if (var.defaultValue != null) {
String enumName = null;
final String enumDefaultValue;
if ("string".equalsIgnoreCase(dataType)) {
enumDefaultValue = toEnumValue(var.defaultValue, dataType);
} else {
enumDefaultValue = var.defaultValue;
}
for (Map enumVar : enumVars) {
if (enumDefaultValue.equals(enumVar.get("value"))) {
enumName = (String) enumVar.get("name");
break;
}
}
if (enumName != null) {
var.defaultValue = toEnumDefaultValue(enumName, var.datatypeWithEnum);
}
}
}
private void updateEnumVarsWithExtensions(List> enumVars, Map vendorExtensions) {
if (vendorExtensions != null) {
updateEnumVarsWithExtensions(enumVars, vendorExtensions, "x-enum-varnames", "name");
updateEnumVarsWithExtensions(enumVars, vendorExtensions, "x-enum-descriptions", "enumDescription");
}
}
private void updateEnumVarsWithExtensions(List> enumVars, Map vendorExtensions, String extensionKey, String key) {
if (vendorExtensions.containsKey(extensionKey)) {
List values = (List) vendorExtensions.get(extensionKey);
int size = Math.min(enumVars.size(), values.size());
for (int i = 0; i < size; i++) {
enumVars.get(i).put(key, values.get(i));
}
}
}
/**
* If the pattern misses the delimiter, add "/" to the beginning and end
* Otherwise, return the original pattern
*
* @param pattern the pattern (regular expression)
* @return the pattern with delimiter
*/
public String addRegularExpressionDelimiter(String pattern) {
if (StringUtils.isEmpty(pattern)) {
return pattern;
}
if (!pattern.matches("^/.*")) {
return "/" + pattern.replaceAll("/", "\\\\/") + "/";
}
return pattern;
}
/**
* reads propertyKey from additionalProperties, converts it to a boolean and
* writes it back to additionalProperties to be usable as a boolean in
* mustache files.
*
* @param propertyKey property key
* @return property value as boolean
*/
public boolean convertPropertyToBooleanAndWriteBack(String propertyKey) {
boolean booleanValue = false;
if (additionalProperties.containsKey(propertyKey)) {
booleanValue = convertPropertyToBoolean(propertyKey);
// write back as boolean
writePropertyBack(propertyKey, booleanValue);
}
return booleanValue;
}
/**
* Provides an override location, if any is specified, for the .openapi-generator-ignore.
*
* This is originally intended for the first generation only.
*
* @return a string of the full path to an override ignore file.
*/
public String getIgnoreFilePathOverride() {
return ignoreFilePathOverride;
}
/**
* Sets an override location for the '.openapi-generator-ignore' location for the first code generation.
*
* @param ignoreFileOverride The full path to an ignore file
*/
public void setIgnoreFilePathOverride(final String ignoreFileOverride) {
this.ignoreFilePathOverride = ignoreFileOverride;
}
public boolean convertPropertyToBoolean(String propertyKey) {
boolean booleanValue = false;
if (additionalProperties.containsKey(propertyKey)) {
booleanValue = Boolean.valueOf(additionalProperties.get(propertyKey).toString());
}
return booleanValue;
}
public void writePropertyBack(String propertyKey, boolean value) {
additionalProperties.put(propertyKey, value);
}
protected String getContentType(RequestBody requestBody) {
if (requestBody == null || requestBody.getContent() == null || requestBody.getContent().isEmpty()) {
return null;
}
return new ArrayList<>(requestBody.getContent().keySet()).get(0);
}
private void setOauth2Info(CodegenSecurity codegenSecurity, OAuthFlow flow) {
codegenSecurity.authorizationUrl = flow.getAuthorizationUrl();
codegenSecurity.tokenUrl = flow.getTokenUrl();
if (flow.getScopes() != null && !flow.getScopes().isEmpty()) {
List> scopes = new ArrayList>();
int count = 0, numScopes = flow.getScopes().size();
for (Map.Entry scopeEntry : flow.getScopes().entrySet()) {
Map scope = new HashMap();
scope.put("scope", scopeEntry.getKey());
scope.put("description", escapeText(scopeEntry.getValue()));
count += 1;
if (count < numScopes) {
scope.put("hasMore", "true");
} else {
scope.put("hasMore", null);
}
scopes.add(scope);
}
codegenSecurity.scopes = scopes;
}
}
private void addConsumesInfo(Operation operation, CodegenOperation codegenOperation) {
RequestBody requestBody = ModelUtils.getReferencedRequestBody(this.openAPI, operation.getRequestBody());
if (requestBody == null || requestBody.getContent() == null || requestBody.getContent().isEmpty()) {
return;
}
Set consumes = requestBody.getContent().keySet();
List> mediaTypeList = new ArrayList<>();
int count = 0;
for (String key : consumes) {
Map mediaType = new HashMap<>();
if ("*/*".equals(key)) {
// skip as it implies `consumes` in OAS2 is not defined
continue;
} else {
mediaType.put("mediaType", escapeText(escapeQuotationMark(key)));
}
count += 1;
if (count < consumes.size()) {
mediaType.put("hasMore", "true");
} else {
mediaType.put("hasMore", null);
}
mediaTypeList.add(mediaType);
}
if (!mediaTypeList.isEmpty()) {
codegenOperation.consumes = mediaTypeList;
codegenOperation.hasConsumes = true;
}
}
public static Set getConsumesInfo(OpenAPI openAPI, Operation operation) {
RequestBody requestBody = ModelUtils.getReferencedRequestBody(openAPI, operation.getRequestBody());
if (requestBody == null || requestBody.getContent() == null || requestBody.getContent().isEmpty()) {
return Collections.emptySet(); // return empty set
}
return requestBody.getContent().keySet();
}
public boolean hasFormParameter(OpenAPI openAPI, Operation operation) {
Set consumesInfo = getConsumesInfo(openAPI, operation);
if (consumesInfo == null || consumesInfo.isEmpty()) {
return false;
}
for (String consume : consumesInfo) {
if ("application/x-www-form-urlencoded".equalsIgnoreCase(consume) || "multipart/form-data".equalsIgnoreCase(consume)) {
return true;
}
}
return false;
}
public boolean hasBodyParameter(OpenAPI openAPI, Operation operation) {
RequestBody requestBody = ModelUtils.getReferencedRequestBody(openAPI, operation.getRequestBody());
if (requestBody == null) {
return false;
}
Schema schema = ModelUtils.getSchemaFromRequestBody(requestBody);
return ModelUtils.getReferencedSchema(openAPI, schema) != null;
}
private void addProducesInfo(ApiResponse inputResponse, CodegenOperation codegenOperation) {
ApiResponse response = ModelUtils.getReferencedApiResponse(this.openAPI, inputResponse);
if (response == null || response.getContent() == null || response.getContent().isEmpty()) {
return;
}
Set produces = response.getContent().keySet();
if (codegenOperation.produces == null) {
codegenOperation.produces = new ArrayList<>();
}
Set existingMediaTypes = new HashSet<>();
for (Map mediaType : codegenOperation.produces) {
existingMediaTypes.add(mediaType.get("mediaType"));
}
int count = 0;
for (String key : produces) {
// escape quotation to avoid code injection, "*/*" is a special case, do nothing
String encodedKey = "*/*".equals(key) ? key : escapeText(escapeQuotationMark(key));
//Only unique media types should be added to "produces"
if (!existingMediaTypes.contains(encodedKey)) {
Map