forked from loafle/openapi-generator-original
add ts angular generator
This commit is contained in:
@@ -1436,7 +1436,7 @@ public class DefaultCodegen implements CodegenConfig {
|
||||
m.allowableValues.put("values", schema.getEnum());
|
||||
}
|
||||
if (schema.getAdditionalProperties() != null || schema instanceof MapSchema) {
|
||||
addParentContainer(m, m.name, schema);
|
||||
addAdditionPropertiesToCodeGenModel(m, schema);
|
||||
}
|
||||
addVars(m, schema.getProperties(), schema.getRequired());
|
||||
}
|
||||
@@ -1481,6 +1481,10 @@ public class DefaultCodegen implements CodegenConfig {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) {
|
||||
addParentContainer(codegenModel, codegenModel.name, schema);
|
||||
}
|
||||
|
||||
protected void addProperties(Map<String, Schema> properties, List<String> required, Schema schema, Map<String, Schema> allSchemas) {
|
||||
if (schema instanceof ComposedSchema) {
|
||||
ComposedSchema composedSchema = (ComposedSchema) schema;
|
||||
@@ -2467,9 +2471,15 @@ public class DefaultCodegen implements CodegenConfig {
|
||||
setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty);
|
||||
|
||||
|
||||
codegenParameter.required = codegenProperty.required;
|
||||
codegenParameter.dataType = codegenProperty.datatype;
|
||||
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;
|
||||
@@ -2674,6 +2684,17 @@ public class DefaultCodegen implements CodegenConfig {
|
||||
return codegenParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data type of a parameter.
|
||||
* Returns null by default to use the CodegenProperty.datatype value
|
||||
* @param parameter
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
protected String getParameterDataType(Parameter parameter, Schema schema) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isDataTypeBinary(String dataType) {
|
||||
if (dataType != null) {
|
||||
return dataType.toLowerCase().startsWith("byte");
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
package org.openapitools.codegen.languages;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import org.openapitools.codegen.CliOption;
|
||||
import org.openapitools.codegen.CodegenConfig;
|
||||
import org.openapitools.codegen.CodegenConstants;
|
||||
import org.openapitools.codegen.CodegenModel;
|
||||
import org.openapitools.codegen.CodegenProperty;
|
||||
import org.openapitools.codegen.CodegenType;
|
||||
import org.openapitools.codegen.DefaultCodegen;
|
||||
import io.swagger.v3.oas.models.media.*;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import io.swagger.v3.oas.models.PathItem.HttpMethod;
|
||||
import io.swagger.v3.oas.models.*;
|
||||
import io.swagger.v3.oas.models.parameters.*;
|
||||
import io.swagger.v3.oas.models.info.*;
|
||||
import io.swagger.v3.parser.util.SchemaTypeUtil;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
private static final String X_DISCRIMINATOR_TYPE = "x-discriminator-value";
|
||||
private static final String UNDEFINED_VALUE = "undefined";
|
||||
|
||||
protected String modelPropertyNaming = "camelCase";
|
||||
protected Boolean supportsES6 = true;
|
||||
protected HashSet<String> languageGenericTypes;
|
||||
|
||||
public AbstractTypeScriptClientCodegen() {
|
||||
super();
|
||||
|
||||
// clear import mapping (from default generator) as TS does not use it
|
||||
// at the moment
|
||||
importMapping.clear();
|
||||
|
||||
supportsInheritance = true;
|
||||
setReservedWordsLowerCase(Arrays.asList(
|
||||
// local variable names used in API methods (endpoints)
|
||||
"varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred",
|
||||
"requestOptions",
|
||||
// Typescript reserved words
|
||||
"abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield"));
|
||||
|
||||
languageSpecificPrimitives = new HashSet<>(Arrays.asList(
|
||||
"string",
|
||||
"String",
|
||||
"boolean",
|
||||
"Boolean",
|
||||
"Double",
|
||||
"Integer",
|
||||
"Long",
|
||||
"Float",
|
||||
"Object",
|
||||
"Array",
|
||||
"Date",
|
||||
"number",
|
||||
"any",
|
||||
"File",
|
||||
"Error",
|
||||
"Map"
|
||||
));
|
||||
|
||||
languageGenericTypes = new HashSet<String>(Arrays.asList(
|
||||
"Array"
|
||||
));
|
||||
|
||||
instantiationTypes.put("array", "Array");
|
||||
|
||||
typeMapping = new HashMap<String, String>();
|
||||
typeMapping.put("Array", "Array");
|
||||
typeMapping.put("array", "Array");
|
||||
typeMapping.put("List", "Array");
|
||||
typeMapping.put("boolean", "boolean");
|
||||
typeMapping.put("string", "string");
|
||||
typeMapping.put("int", "number");
|
||||
typeMapping.put("float", "number");
|
||||
typeMapping.put("number", "number");
|
||||
typeMapping.put("long", "number");
|
||||
typeMapping.put("short", "number");
|
||||
typeMapping.put("char", "string");
|
||||
typeMapping.put("double", "number");
|
||||
typeMapping.put("object", "any");
|
||||
typeMapping.put("integer", "number");
|
||||
typeMapping.put("Map", "any");
|
||||
typeMapping.put("date", "string");
|
||||
typeMapping.put("DateTime", "Date");
|
||||
//TODO binary should be mapped to byte array
|
||||
// mapped to String as a workaround
|
||||
typeMapping.put("binary", "string");
|
||||
typeMapping.put("ByteArray", "string");
|
||||
typeMapping.put("UUID", "string");
|
||||
typeMapping.put("File", "any");
|
||||
typeMapping.put("Error", "Error");
|
||||
|
||||
cliOptions.add(new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, CodegenConstants.MODEL_PROPERTY_NAMING_DESC).defaultValue("camelCase"));
|
||||
cliOptions.add(new CliOption(CodegenConstants.SUPPORTS_ES6, CodegenConstants.SUPPORTS_ES6_DESC).defaultValue("false"));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processOpts() {
|
||||
super.processOpts();
|
||||
|
||||
if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) {
|
||||
setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING));
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(CodegenConstants.SUPPORTS_ES6)) {
|
||||
setSupportsES6(Boolean.valueOf(additionalProperties.get(CodegenConstants.SUPPORTS_ES6).toString()));
|
||||
additionalProperties.put("supportsES6", getSupportsES6());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodegenType getTag() {
|
||||
return CodegenType.CLIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeReservedWord(String name) {
|
||||
if (this.reservedWordsMappings().containsKey(name)) {
|
||||
return this.reservedWordsMappings().get(name);
|
||||
}
|
||||
return "_" + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apiFileFolder() {
|
||||
return outputFolder + "/" + apiPackage().replace('.', File.separatorChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String modelFileFolder() {
|
||||
return outputFolder + "/" + modelPackage().replace('.', File.separatorChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toParamName(String name) {
|
||||
// should be the same as variable name
|
||||
return toVarName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toVarName(String name) {
|
||||
// sanitize name
|
||||
name = sanitizeName(name);
|
||||
|
||||
if ("_".equals(name)) {
|
||||
name = "_u";
|
||||
}
|
||||
|
||||
// if it's all uppper case, do nothing
|
||||
if (name.matches("^[A-Z_]*$")) {
|
||||
return name;
|
||||
}
|
||||
|
||||
name = getNameUsingModelPropertyNaming(name);
|
||||
|
||||
// for reserved word or word starting with number, append _
|
||||
if (isReservedWord(name) || name.matches("^\\d.*")) {
|
||||
name = escapeReservedWord(name);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelName(String name) {
|
||||
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
|
||||
|
||||
if (!StringUtils.isEmpty(modelNamePrefix)) {
|
||||
name = modelNamePrefix + "_" + name;
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(modelNameSuffix)) {
|
||||
name = name + "_" + modelNameSuffix;
|
||||
}
|
||||
|
||||
// model name cannot use reserved keyword, e.g. return
|
||||
if (isReservedWord(name)) {
|
||||
String modelName = camelize("model_" + name);
|
||||
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName);
|
||||
return modelName;
|
||||
}
|
||||
|
||||
// model name starts with number
|
||||
if (name.matches("^\\d.*")) {
|
||||
String modelName = camelize("model_" + name); // e.g. 200Response => Model200Response (after camelize)
|
||||
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
|
||||
return modelName;
|
||||
}
|
||||
|
||||
if (languageSpecificPrimitives.contains(name)) {
|
||||
String modelName = camelize("model_" + name);
|
||||
LOGGER.warn(name + " (model name matches existing language type) cannot be used as a model name. Renamed to " + modelName);
|
||||
return modelName;
|
||||
}
|
||||
|
||||
// camelize the model name
|
||||
// phone_number => PhoneNumber
|
||||
return camelize(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelFilename(String name) {
|
||||
// should be the same as the model name
|
||||
return toModelName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration(Schema p) {
|
||||
if (p instanceof ArraySchema) {
|
||||
ArraySchema ap = (ArraySchema) p;
|
||||
Schema inner = ap.getItems();
|
||||
return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">";
|
||||
} else if (p instanceof MapSchema) {
|
||||
MapSchema mp = (MapSchema) p;
|
||||
Schema inner = (Schema) mp.getAdditionalProperties();
|
||||
return "{ [key: string]: " + getTypeDeclaration(inner) + "; }";
|
||||
} else if (p instanceof FileSchema) {
|
||||
return "any";
|
||||
} else if (p instanceof StringSchema && SchemaTypeUtil.BINARY_FORMAT.equals(p.getFormat())) {
|
||||
return "any";
|
||||
}
|
||||
return super.getTypeDeclaration(p);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getParameterDataType(Parameter parameter, Schema p) {
|
||||
// handle enums of various data types
|
||||
Schema inner;
|
||||
if (p instanceof ArraySchema) {
|
||||
ArraySchema mp1 = (ArraySchema) p;
|
||||
inner = mp1.getItems();
|
||||
return this.getSchemaType(p) + "<" + this.getParameterDataType(parameter, inner) + ">";
|
||||
} else if (p instanceof MapSchema) {
|
||||
MapSchema mp = (MapSchema) p;
|
||||
inner = (Schema) mp.getAdditionalProperties();
|
||||
return "{ [key: string]: " + this.getParameterDataType(parameter, inner) + "; }";
|
||||
} else if (p instanceof StringSchema) {
|
||||
// Handle string enums
|
||||
StringSchema sp = (StringSchema) p;
|
||||
if (sp.getEnum() != null) {
|
||||
return enumValuesToEnumTypeUnion(sp.getEnum(), "string");
|
||||
}
|
||||
} else if (p instanceof IntegerSchema) {
|
||||
// Handle integer enums
|
||||
IntegerSchema sp = (IntegerSchema) p;
|
||||
if (sp.getEnum() != null) {
|
||||
return numericEnumValuesToEnumTypeUnion(new ArrayList<Number>(sp.getEnum()));
|
||||
}
|
||||
} else if (p instanceof NumberSchema) {
|
||||
// Handle double enums
|
||||
NumberSchema sp = (NumberSchema) p;
|
||||
if (sp.getEnum() != null) {
|
||||
return numericEnumValuesToEnumTypeUnion(new ArrayList<Number>(sp.getEnum()));
|
||||
}
|
||||
}
|
||||
/* TODO revise the logic below
|
||||
else if (p instanceof DateSchema) {
|
||||
// Handle date enums
|
||||
DateSchema sp = (DateSchema) p;
|
||||
if (sp.getEnum() != null) {
|
||||
return enumValuesToEnumTypeUnion(sp.getEnum(), "string");
|
||||
}
|
||||
} else if (p instanceof DateTimeSchema) {
|
||||
// Handle datetime enums
|
||||
DateTimeSchema sp = (DateTimeSchema) p;
|
||||
if (sp.getEnum() != null) {
|
||||
return enumValuesToEnumTypeUnion(sp.getEnum(), "string");
|
||||
}
|
||||
}*/
|
||||
return this.getTypeDeclaration(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of strings to a literal union for representing enum values as a type.
|
||||
* Example output: 'available' | 'pending' | 'sold'
|
||||
*
|
||||
* @param values list of allowed enum values
|
||||
* @param dataType either "string" or "number"
|
||||
* @return
|
||||
*/
|
||||
protected String enumValuesToEnumTypeUnion(List<String> values, String dataType) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean isFirst = true;
|
||||
for (String value : values) {
|
||||
if (!isFirst) {
|
||||
b.append(" | ");
|
||||
}
|
||||
b.append(toEnumValue(value.toString(), dataType));
|
||||
isFirst = false;
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of numbers to a literal union for representing enum values as a type.
|
||||
* Example output: 3 | 9 | 55
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
protected String numericEnumValuesToEnumTypeUnion(List<Number> values) {
|
||||
List<String> stringValues = new ArrayList<>();
|
||||
for (Number value : values) {
|
||||
stringValues.add(value.toString());
|
||||
}
|
||||
return enumValuesToEnumTypeUnion(stringValues, "number");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDefaultValue(Schema p) {
|
||||
if (p instanceof StringSchema) {
|
||||
StringSchema sp = (StringSchema) p;
|
||||
if (sp.getDefault() != null) {
|
||||
return "'" + sp.getDefault() + "'";
|
||||
}
|
||||
return UNDEFINED_VALUE;
|
||||
} else if (p instanceof BooleanSchema) {
|
||||
return UNDEFINED_VALUE;
|
||||
} else if (p instanceof DateSchema) {
|
||||
return UNDEFINED_VALUE;
|
||||
} else if (p instanceof DateTimeSchema) {
|
||||
return UNDEFINED_VALUE;
|
||||
} else if (p instanceof NumberSchema) {
|
||||
NumberSchema dp = (NumberSchema) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return dp.getDefault().toString();
|
||||
}
|
||||
return UNDEFINED_VALUE;
|
||||
} else if (p instanceof IntegerSchema) {
|
||||
IntegerSchema ip = (IntegerSchema) p;
|
||||
if (ip.getDefault() != null) {
|
||||
return ip.getDefault().toString();
|
||||
}
|
||||
return UNDEFINED_VALUE;
|
||||
} else {
|
||||
return UNDEFINED_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchemaType(Schema p) {
|
||||
String openAPIType = super.getSchemaType(p);
|
||||
String type = null;
|
||||
if (typeMapping.containsKey(openAPIType)) {
|
||||
type = typeMapping.get(openAPIType);
|
||||
if (languageSpecificPrimitives.contains(type))
|
||||
return type;
|
||||
} else
|
||||
type = openAPIType;
|
||||
return toModelName(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
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");
|
||||
}
|
||||
|
||||
// method name cannot use reserved keyword, e.g. return
|
||||
// append _ at the beginning, e.g. _return
|
||||
if (isReservedWord(operationId)) {
|
||||
return escapeReservedWord(camelize(sanitizeName(operationId), true));
|
||||
}
|
||||
|
||||
return camelize(sanitizeName(operationId), true);
|
||||
}
|
||||
|
||||
public void setModelPropertyNaming(String naming) {
|
||||
if ("original".equals(naming) || "camelCase".equals(naming) ||
|
||||
"PascalCase".equals(naming) || "snake_case".equals(naming)) {
|
||||
this.modelPropertyNaming = naming;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid model property naming '" +
|
||||
naming + "'. Must be 'original', 'camelCase', " +
|
||||
"'PascalCase' or 'snake_case'");
|
||||
}
|
||||
}
|
||||
|
||||
public String getModelPropertyNaming() {
|
||||
return this.modelPropertyNaming;
|
||||
}
|
||||
|
||||
public String getNameUsingModelPropertyNaming(String name) {
|
||||
switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) {
|
||||
case original:
|
||||
return name;
|
||||
case camelCase:
|
||||
return camelize(name, true);
|
||||
case PascalCase:
|
||||
return camelize(name);
|
||||
case snake_case:
|
||||
return underscore(name);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid model property naming '" +
|
||||
name + "'. Must be 'original', 'camelCase', " +
|
||||
"'PascalCase' or 'snake_case'");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEnumValue(String value, String datatype) {
|
||||
if ("number".equals(datatype)) {
|
||||
return value;
|
||||
} else {
|
||||
return "\'" + escapeText(value) + "\'";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEnumDefaultValue(String value, String datatype) {
|
||||
return datatype + "_" + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEnumVarName(String name, String datatype) {
|
||||
if (name.length() == 0) {
|
||||
return "Empty";
|
||||
}
|
||||
|
||||
// for symbol, e.g. $, #
|
||||
if (getSymbolName(name) != null) {
|
||||
return camelize(getSymbolName(name));
|
||||
}
|
||||
|
||||
// number
|
||||
if ("number".equals(datatype)) {
|
||||
String varName = "NUMBER_" + name;
|
||||
|
||||
varName = varName.replaceAll("-", "MINUS_");
|
||||
varName = varName.replaceAll("\\+", "PLUS_");
|
||||
varName = varName.replaceAll("\\.", "_DOT_");
|
||||
return varName;
|
||||
}
|
||||
|
||||
// string
|
||||
String enumName = sanitizeName(name);
|
||||
enumName = enumName.replaceFirst("^_", "");
|
||||
enumName = enumName.replaceFirst("_$", "");
|
||||
|
||||
// camelize the enum variable name
|
||||
// ref: https://basarat.gitbooks.io/typescript/content/docs/enums.html
|
||||
enumName = camelize(enumName);
|
||||
|
||||
if (enumName.matches("\\d.*")) { // starts with number
|
||||
return "_" + enumName;
|
||||
} else {
|
||||
return enumName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEnumName(CodegenProperty property) {
|
||||
String enumName = toModelName(property.name) + "Enum";
|
||||
|
||||
if (enumName.matches("\\d.*")) { // starts with number
|
||||
return "_" + enumName;
|
||||
} else {
|
||||
return enumName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||
// process enum in models
|
||||
List<Object> models = (List<Object>) postProcessModelsEnum(objs).get("models");
|
||||
for (Object _mo : models) {
|
||||
Map<String, Object> mo = (Map<String, Object>) _mo;
|
||||
CodegenModel cm = (CodegenModel) mo.get("model");
|
||||
cm.imports = new TreeSet(cm.imports);
|
||||
// name enum with model name, e.g. StatusEnum => Pet.StatusEnum
|
||||
for (CodegenProperty var : cm.vars) {
|
||||
if (Boolean.TRUE.equals(var.isEnum)) {
|
||||
var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + "." + var.enumName);
|
||||
}
|
||||
}
|
||||
if (cm.parent != null) {
|
||||
for (CodegenProperty var : cm.allVars) {
|
||||
if (Boolean.TRUE.equals(var.isEnum)) {
|
||||
var.datatypeWithEnum = var.datatypeWithEnum
|
||||
.replace(var.enumName, cm.classname + "." + var.enumName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return objs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
||||
Map<String, Object> result = super.postProcessAllModels(objs);
|
||||
|
||||
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||
Map<String, Object> inner = (Map<String, Object>) entry.getValue();
|
||||
List<Map<String, Object>> models = (List<Map<String, Object>>) inner.get("models");
|
||||
for (Map<String, Object> mo : models) {
|
||||
CodegenModel cm = (CodegenModel) mo.get("model");
|
||||
if (cm.discriminator != null && cm.children != null) {
|
||||
for (CodegenModel child : cm.children) {
|
||||
this.setDiscriminatorValue(child, cm.discriminator.getPropertyName(), this.getDiscriminatorValue(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSupportsES6(Boolean value) {
|
||||
supportsES6 = value;
|
||||
}
|
||||
|
||||
public Boolean getSupportsES6() {
|
||||
return supportsES6;
|
||||
}
|
||||
|
||||
private void setDiscriminatorValue(CodegenModel model, String baseName, String value) {
|
||||
for (CodegenProperty prop : model.allVars) {
|
||||
if (prop.baseName.equals(baseName)) {
|
||||
prop.discriminatorValue = value;
|
||||
}
|
||||
}
|
||||
if (model.children != null) {
|
||||
final boolean newDiscriminator = model.discriminator != null;
|
||||
for (CodegenModel child : model.children) {
|
||||
this.setDiscriminatorValue(child, baseName, newDiscriminator ? value : this.getDiscriminatorValue(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getDiscriminatorValue(CodegenModel model) {
|
||||
return model.vendorExtensions.containsKey(X_DISCRIMINATOR_TYPE) ?
|
||||
(String) model.vendorExtensions.get(X_DISCRIMINATOR_TYPE) : model.classname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeQuotationMark(String input) {
|
||||
// remove ', " to avoid code injection
|
||||
return input.replace("\"", "").replace("'", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeUnsafeCharacters(String input) {
|
||||
return input.replace("*/", "*_/").replace("/*", "/_*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
package org.openapitools.codegen.languages;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.parser.util.SchemaTypeUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openapitools.codegen.CliOption;
|
||||
import org.openapitools.codegen.CodegenModel;
|
||||
import org.openapitools.codegen.CodegenParameter;
|
||||
import org.openapitools.codegen.CodegenOperation;
|
||||
import org.openapitools.codegen.SupportingFile;
|
||||
import org.openapitools.codegen.utils.SemVer;
|
||||
|
||||
import io.swagger.v3.oas.models.media.*;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import io.swagger.v3.oas.models.PathItem.HttpMethod;
|
||||
import io.swagger.v3.oas.models.*;
|
||||
import io.swagger.v3.oas.models.parameters.*;
|
||||
import io.swagger.v3.oas.models.info.*;
|
||||
|
||||
public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCodegen {
|
||||
private static final SimpleDateFormat SNAPSHOT_SUFFIX_FORMAT = new SimpleDateFormat("yyyyMMddHHmm");
|
||||
private static final String X_DISCRIMINATOR_TYPE = "x-discriminator-value";
|
||||
|
||||
public static final String NPM_NAME = "npmName";
|
||||
public static final String NPM_VERSION = "npmVersion";
|
||||
public static final String NPM_REPOSITORY = "npmRepository";
|
||||
public static final String SNAPSHOT = "snapshot";
|
||||
public static final String WITH_INTERFACES = "withInterfaces";
|
||||
public static final String TAGGED_UNIONS = "taggedUnions";
|
||||
public static final String NG_VERSION = "ngVersion";
|
||||
|
||||
protected String npmName = null;
|
||||
protected String npmVersion = "1.0.0";
|
||||
protected String npmRepository = null;
|
||||
|
||||
private boolean taggedUnions = false;
|
||||
|
||||
public TypeScriptAngularClientCodegen() {
|
||||
super();
|
||||
this.outputFolder = "generated-code/typescript-angular";
|
||||
|
||||
embeddedTemplateDir = templateDir = "typescript-angular";
|
||||
modelTemplateFiles.put("model.mustache", ".ts");
|
||||
apiTemplateFiles.put("api.service.mustache", ".ts");
|
||||
languageSpecificPrimitives.add("Blob");
|
||||
typeMapping.put("file", "Blob");
|
||||
apiPackage = "api";
|
||||
modelPackage = "model";
|
||||
|
||||
this.cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package"));
|
||||
this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package"));
|
||||
this.cliOptions.add(new CliOption(NPM_REPOSITORY,
|
||||
"Use this property to set an url your private npmRepo in the package.json"));
|
||||
this.cliOptions.add(new CliOption(SNAPSHOT,
|
||||
"When setting this property to true the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm",
|
||||
SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
|
||||
this.cliOptions.add(new CliOption(WITH_INTERFACES,
|
||||
"Setting this property to true will generate interfaces next to the default class implementations.",
|
||||
SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
|
||||
this.cliOptions.add(new CliOption(TAGGED_UNIONS,
|
||||
"Use discriminators to create tagged unions instead of extending interfaces.",
|
||||
SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
|
||||
this.cliOptions.add(new CliOption(NG_VERSION, "The version of Angular. Default is '4.3'"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) {
|
||||
codegenModel.additionalPropertiesType = getTypeDeclaration((Schema) schema.getAdditionalProperties());
|
||||
addImport(codegenModel, codegenModel.additionalPropertiesType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "typescript-angular";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return "Generates a TypeScript Angular (2.x or 4.x) client library.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processOpts() {
|
||||
super.processOpts();
|
||||
supportingFiles.add(
|
||||
new SupportingFile("models.mustache", modelPackage().replace('.', File.separatorChar), "models.ts"));
|
||||
supportingFiles
|
||||
.add(new SupportingFile("apis.mustache", apiPackage().replace('.', File.separatorChar), "api.ts"));
|
||||
supportingFiles.add(new SupportingFile("index.mustache", getIndexDirectory(), "index.ts"));
|
||||
supportingFiles.add(new SupportingFile("api.module.mustache", getIndexDirectory(), "api.module.ts"));
|
||||
supportingFiles.add(new SupportingFile("configuration.mustache", getIndexDirectory(), "configuration.ts"));
|
||||
supportingFiles.add(new SupportingFile("variables.mustache", getIndexDirectory(), "variables.ts"));
|
||||
supportingFiles.add(new SupportingFile("encoder.mustache", getIndexDirectory(), "encoder.ts"));
|
||||
supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore"));
|
||||
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
|
||||
supportingFiles.add(new SupportingFile("README.mustache", getIndexDirectory(), "README.md"));
|
||||
|
||||
if (additionalProperties.containsKey(NPM_NAME)) {
|
||||
addNpmPackageGeneration();
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(WITH_INTERFACES)) {
|
||||
boolean withInterfaces = Boolean.parseBoolean(additionalProperties.get(WITH_INTERFACES).toString());
|
||||
if (withInterfaces) {
|
||||
apiTemplateFiles.put("apiInterface.mustache", "Interface.ts");
|
||||
}
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(TAGGED_UNIONS)) {
|
||||
taggedUnions = Boolean.parseBoolean(additionalProperties.get(TAGGED_UNIONS).toString());
|
||||
}
|
||||
|
||||
// determine NG version
|
||||
SemVer ngVersion;
|
||||
if (additionalProperties.containsKey(NG_VERSION)) {
|
||||
ngVersion = new SemVer(additionalProperties.get(NG_VERSION).toString());
|
||||
} else {
|
||||
ngVersion = new SemVer("4.3.0");
|
||||
LOGGER.info("generating code for Angular {} ...", ngVersion);
|
||||
LOGGER.info(" (you can select the angular version by setting the additionalProperty ngVersion)");
|
||||
}
|
||||
additionalProperties.put(NG_VERSION, ngVersion);
|
||||
additionalProperties.put("injectionToken", ngVersion.atLeast("4.0.0") ? "InjectionToken" : "OpaqueToken");
|
||||
additionalProperties.put("injectionTokenTyped", ngVersion.atLeast("4.0.0"));
|
||||
additionalProperties.put("useHttpClient", ngVersion.atLeast("4.3.0"));
|
||||
if (!ngVersion.atLeast("4.3.0")) {
|
||||
supportingFiles.add(new SupportingFile("rxjs-operators.mustache", getIndexDirectory(), "rxjs-operators.ts"));
|
||||
}
|
||||
}
|
||||
|
||||
private void addNpmPackageGeneration() {
|
||||
if (additionalProperties.containsKey(NPM_NAME)) {
|
||||
this.setNpmName(additionalProperties.get(NPM_NAME).toString());
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(NPM_VERSION)) {
|
||||
this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString());
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(SNAPSHOT)
|
||||
&& Boolean.valueOf(additionalProperties.get(SNAPSHOT).toString())) {
|
||||
this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.format(new Date()));
|
||||
}
|
||||
additionalProperties.put(NPM_VERSION, npmVersion);
|
||||
|
||||
if (additionalProperties.containsKey(NPM_REPOSITORY)) {
|
||||
this.setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString());
|
||||
}
|
||||
|
||||
//Files for building our lib
|
||||
supportingFiles.add(new SupportingFile("package.mustache", getIndexDirectory(), "package.json"));
|
||||
supportingFiles.add(new SupportingFile("typings.mustache", getIndexDirectory(), "typings.json"));
|
||||
supportingFiles.add(new SupportingFile("tsconfig.mustache", getIndexDirectory(), "tsconfig.json"));
|
||||
}
|
||||
|
||||
private String getIndexDirectory() {
|
||||
String indexPackage = modelPackage.substring(0, Math.max(0, modelPackage.lastIndexOf('.')));
|
||||
return indexPackage.replace('.', File.separatorChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataTypeFile(final String dataType) {
|
||||
return dataType != null && dataType.equals("Blob");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration(Schema p) {
|
||||
if (p instanceof FileSchema) {
|
||||
return "Blob";
|
||||
} else if (!StringUtils.isEmpty(p.get$ref())) {
|
||||
return "any";
|
||||
} else {
|
||||
return super.getTypeDeclaration(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getSchemaType(Schema p) {
|
||||
String openAPIType = super.getSchemaType(p);
|
||||
if (isLanguagePrimitive(openAPIType) || isLanguageGenericType(openAPIType)) {
|
||||
return openAPIType;
|
||||
}
|
||||
applyLocalTypeMapping(openAPIType);
|
||||
return openAPIType;
|
||||
}
|
||||
|
||||
private String applyLocalTypeMapping(String type) {
|
||||
if (typeMapping.containsKey(type)) {
|
||||
type = typeMapping.get(type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private boolean isLanguagePrimitive(String type) {
|
||||
return languageSpecificPrimitives.contains(type);
|
||||
}
|
||||
|
||||
private boolean isLanguageGenericType(String type) {
|
||||
for (String genericType : languageGenericTypes) {
|
||||
if (type.startsWith(genericType + "<")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessParameter(CodegenParameter parameter) {
|
||||
super.postProcessParameter(parameter);
|
||||
parameter.dataType = applyLocalTypeMapping(parameter.dataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessOperations(Map<String, Object> operations) {
|
||||
Map<String, Object> objs = (Map<String, Object>) operations.get("operations");
|
||||
|
||||
// Add filename information for api imports
|
||||
objs.put("apiFilename", getApiFilenameFromClassname(objs.get("classname").toString()));
|
||||
|
||||
List<CodegenOperation> ops = (List<CodegenOperation>) objs.get("operation");
|
||||
for (CodegenOperation op : ops) {
|
||||
if ((boolean) additionalProperties.get("useHttpClient")) {
|
||||
op.httpMethod = op.httpMethod.toLowerCase(Locale.ENGLISH);
|
||||
} else {
|
||||
// Convert httpMethod to Angular's RequestMethod enum
|
||||
// https://angular.io/docs/ts/latest/api/http/index/RequestMethod-enum.html
|
||||
switch (op.httpMethod) {
|
||||
case "GET":
|
||||
op.httpMethod = "RequestMethod.Get";
|
||||
break;
|
||||
case "POST":
|
||||
op.httpMethod = "RequestMethod.Post";
|
||||
break;
|
||||
case "PUT":
|
||||
op.httpMethod = "RequestMethod.Put";
|
||||
break;
|
||||
case "DELETE":
|
||||
op.httpMethod = "RequestMethod.Delete";
|
||||
break;
|
||||
case "OPTIONS":
|
||||
op.httpMethod = "RequestMethod.Options";
|
||||
break;
|
||||
case "HEAD":
|
||||
op.httpMethod = "RequestMethod.Head";
|
||||
break;
|
||||
case "PATCH":
|
||||
op.httpMethod = "RequestMethod.Patch";
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown HTTP Method " + op.httpMethod + " not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
// Prep a string buffer where we're going to set up our new version of the string.
|
||||
StringBuilder pathBuffer = new StringBuilder();
|
||||
StringBuilder parameterName = new StringBuilder();
|
||||
int insideCurly = 0;
|
||||
|
||||
// Iterate through existing string, one character at a time.
|
||||
for (int i = 0; i < op.path.length(); i++) {
|
||||
switch (op.path.charAt(i)) {
|
||||
case '{':
|
||||
// We entered curly braces, so track that.
|
||||
insideCurly++;
|
||||
|
||||
// Add the more complicated component instead of just the brace.
|
||||
pathBuffer.append("${encodeURIComponent(String(");
|
||||
break;
|
||||
case '}':
|
||||
// We exited curly braces, so track that.
|
||||
insideCurly--;
|
||||
|
||||
// Add the more complicated component instead of just the brace.
|
||||
pathBuffer.append(toVarName(parameterName.toString()));
|
||||
pathBuffer.append("))}");
|
||||
parameterName.setLength(0);
|
||||
break;
|
||||
default:
|
||||
if (insideCurly > 0) {
|
||||
parameterName.append(op.path.charAt(i));
|
||||
} else {
|
||||
pathBuffer.append(op.path.charAt(i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite path to TypeScript template string, after applying everything we just did.
|
||||
op.path = pathBuffer.toString();
|
||||
}
|
||||
|
||||
// Add additional filename information for model imports in the services
|
||||
List<Map<String, Object>> imports = (List<Map<String, Object>>) operations.get("imports");
|
||||
for (Map<String, Object> im : imports) {
|
||||
im.put("filename", im.get("import"));
|
||||
im.put("classname", getModelnameFromModelFilename(im.get("filename").toString()));
|
||||
}
|
||||
|
||||
return operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||
Map<String, Object> result = super.postProcessModels(objs);
|
||||
|
||||
return postProcessModelsEnum(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
||||
Map<String, Object> result = super.postProcessAllModels(objs);
|
||||
|
||||
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||
Map<String, Object> inner = (Map<String, Object>) entry.getValue();
|
||||
List<Map<String, Object>> models = (List<Map<String, Object>>) inner.get("models");
|
||||
for (Map<String, Object> mo : models) {
|
||||
CodegenModel cm = (CodegenModel) mo.get("model");
|
||||
if (taggedUnions) {
|
||||
mo.put(TAGGED_UNIONS, true);
|
||||
if (cm.discriminator != null && cm.children != null) {
|
||||
for (CodegenModel child : cm.children) {
|
||||
cm.imports.add(child.classname);
|
||||
}
|
||||
}
|
||||
if (cm.parent != null) {
|
||||
cm.imports.remove(cm.parent);
|
||||
}
|
||||
}
|
||||
// Add additional filename information for imports
|
||||
mo.put("tsImports", toTsImports(cm, cm.imports));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Map<String, String>> toTsImports(CodegenModel cm, Set<String> imports) {
|
||||
List<Map<String, String>> tsImports = new ArrayList<>();
|
||||
for (String im : imports) {
|
||||
if (!im.equals(cm.classname)) {
|
||||
HashMap<String, String> tsImport = new HashMap<>();
|
||||
tsImport.put("classname", im);
|
||||
tsImport.put("filename", toModelFilename(im));
|
||||
tsImports.add(tsImport);
|
||||
}
|
||||
}
|
||||
return tsImports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiName(String name) {
|
||||
if (name.length() == 0) {
|
||||
return "DefaultService";
|
||||
}
|
||||
return initialCaps(name) + "Service";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiFilename(String name) {
|
||||
if (name.length() == 0) {
|
||||
return "default.service";
|
||||
}
|
||||
return camelize(name, true) + ".service";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiImport(String name) {
|
||||
return apiPackage() + "/" + toApiFilename(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelFilename(String name) {
|
||||
return camelize(toModelName(name), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelImport(String name) {
|
||||
return modelPackage() + "/" + toModelFilename(name);
|
||||
}
|
||||
|
||||
public String getNpmName() {
|
||||
return npmName;
|
||||
}
|
||||
|
||||
public void setNpmName(String npmName) {
|
||||
this.npmName = npmName;
|
||||
}
|
||||
|
||||
public String getNpmVersion() {
|
||||
return npmVersion;
|
||||
}
|
||||
|
||||
public void setNpmVersion(String npmVersion) {
|
||||
this.npmVersion = npmVersion;
|
||||
}
|
||||
|
||||
public String getNpmRepository() {
|
||||
return npmRepository;
|
||||
}
|
||||
|
||||
public void setNpmRepository(String npmRepository) {
|
||||
this.npmRepository = npmRepository;
|
||||
}
|
||||
|
||||
private String getApiFilenameFromClassname(String classname) {
|
||||
String name = classname.substring(0, classname.length() - "Service".length());
|
||||
return toApiFilename(name);
|
||||
}
|
||||
|
||||
private String getModelnameFromModelFilename(String filename) {
|
||||
String name = filename.substring((modelPackage() + "/").length());
|
||||
return camelize(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,3 +28,4 @@ org.openapitools.codegen.languages.SlimFrameworkServerCodegen
|
||||
org.openapitools.codegen.languages.SilexServerCodegen
|
||||
org.openapitools.codegen.languages.SinatraServerCodegen
|
||||
org.openapitools.codegen.languages.TizenClientCodegen
|
||||
org.openapitools.codegen.languages.TypeScriptAngularClientCodegen
|
||||
|
||||
Reference in New Issue
Block a user