diff --git a/docs/generators/typescript.md b/docs/generators/typescript.md index 58e50f191f29..a5ff44cf2b35 100644 --- a/docs/generators/typescript.md +++ b/docs/generators/typescript.md @@ -21,6 +21,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl |allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false| |disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true| |ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| +|enumNameSuffix|Suffix that will be appended to all enum names.| |Enum| +|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |PascalCase| |enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|
**false**
No changes to the enum's are made, this is the default option.
**true**
With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.
|false| |fileContentDataType|Specifies the type to use for the content of a file - i.e. Blob (Browser, Deno) / Buffer (node)| |Buffer| |framework|Specify the framework which should be used in the client code.|
**fetch-api**
fetch-api
**jquery**
jquery
|fetch-api| @@ -30,6 +32,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl |npmName|The name under which you want to publish generated npm package. Required to generate a full package| |null| |npmRepository|Use this property to set an url your private npmRepo in the package.json| |null| |npmVersion|The version of your npm package. If not provided, using the version from the OpenAPI specification file.| |1.0.0| +|nullSafeAdditionalProps|Set to make additional properties types declare that their indexer may return undefined| |false| +|paramNaming|Naming convention for parameters: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |camelCase| |platform|Specifies the platform the code should run on. The default is 'node' for the 'request' framework and 'browser' otherwise.|
**browser**
browser
**node**
node
**deno**
deno
|browser| |prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false| |snapshot|When setting this property to true, the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm| |false| @@ -67,11 +71,13 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • Long
  • Map
  • Object
  • +
  • ReadonlyArray
  • Set
  • String
  • any
  • boolean
  • number
  • +
  • object
  • string
  • @@ -156,7 +162,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl ### Client Modification Feature | Name | Supported | Defined By | | ---- | --------- | ---------- | -|BasePath|✗|ToolingExtension +|BasePath|✓|ToolingExtension |Authorizations|✗|ToolingExtension |UserAgent|✗|ToolingExtension |MockServer|✗|ToolingExtension @@ -201,7 +207,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl ### Documentation Feature | Name | Supported | Defined By | | ---- | --------- | ---------- | -|Readme|✗|ToolingExtension +|Readme|✓|ToolingExtension |Model|✓|ToolingExtension |Api|✓|ToolingExtension @@ -221,7 +227,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |MultiServer|✗|OAS3 |ParameterizedServer|✗|OAS3 |ParameterStyling|✗|OAS3 -|Callbacks|✓|OAS3 +|Callbacks|✗|OAS3 |LinkObjects|✗|OAS3 ### Parameter Feature @@ -250,14 +256,14 @@ These options may be applied as additional-properties (cli) or configOptions (pl ### Security Feature | Name | Supported | Defined By | | ---- | --------- | ---------- | -|BasicAuth|✓|OAS2,OAS3 -|ApiKey|✓|OAS2,OAS3 +|BasicAuth|✗|OAS2,OAS3 +|ApiKey|✗|OAS2,OAS3 |OpenIDConnect|✗|OAS3 -|BearerToken|✓|OAS3 -|OAuth2_Implicit|✓|OAS2,OAS3 -|OAuth2_Password|✓|OAS2,OAS3 -|OAuth2_ClientCredentials|✓|OAS2,OAS3 -|OAuth2_AuthorizationCode|✓|OAS2,OAS3 +|BearerToken|✗|OAS3 +|OAuth2_Implicit|✗|OAS2,OAS3 +|OAuth2_Password|✗|OAS2,OAS3 +|OAuth2_ClientCredentials|✗|OAS2,OAS3 +|OAuth2_AuthorizationCode|✗|OAS2,OAS3 ### Wire Format Feature | Name | Supported | Defined By | diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index 779a71ccf60f..89ac30bb52d8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -25,10 +25,10 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; +import org.openapitools.codegen.*; import org.openapitools.codegen.CodegenConstants.ENUM_PROPERTY_NAMING_TYPE; import org.openapitools.codegen.CodegenConstants.MODEL_PROPERTY_NAMING_TYPE; import org.openapitools.codegen.CodegenConstants.PARAM_NAMING_TYPE; -import org.openapitools.codegen.*; import org.openapitools.codegen.meta.features.*; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; @@ -46,7 +46,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.openapitools.codegen.languages.AbstractTypeScriptClientCodegen.ParameterExpander.ParamStyle.*; +import static org.openapitools.codegen.languages.AbstractTypeScriptClientCodegen.ParameterExpander.ParamStyle.form; +import static org.openapitools.codegen.languages.AbstractTypeScriptClientCodegen.ParameterExpander.ParamStyle.simple; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; import static org.openapitools.codegen.utils.StringUtils.underscore; @@ -392,10 +393,7 @@ public void processOpts() { setParamNaming((String) additionalProperties.get(CodegenConstants.PARAM_NAMING)); } - if (additionalProperties.containsKey(CodegenConstants.SUPPORTS_ES6)) { - setSupportsES6(Boolean.valueOf(additionalProperties.get(CodegenConstants.SUPPORTS_ES6).toString())); - additionalProperties.put("supportsES6", getSupportsES6()); - } + setSupportsES6(convertPropertyToBooleanAndWriteBack(CodegenConstants.SUPPORTS_ES6)); if (additionalProperties.containsKey(NULL_SAFE_ADDITIONAL_PROPS)) { setNullSafeAdditionalProps(Boolean.valueOf(additionalProperties.get(NULL_SAFE_ADDITIONAL_PROPS).toString())); @@ -839,7 +837,7 @@ public String toEnumValue(String value, String datatype) { if ("number".equals(datatype) || "boolean".equals(datatype)) { return value; } else { - return "\'" + escapeText(value) + "\'"; + return "'" + escapeText(value) + "'"; } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index d346d9030635..62887e2cf617 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -17,17 +17,12 @@ package org.openapitools.codegen.languages; +import com.github.curiousoddman.rgxgen.RgxGen; import com.google.common.collect.Sets; - import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.media.*; -import io.swagger.v3.oas.models.media.MediaType; -import io.swagger.v3.oas.models.parameters.RequestBody; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.media.ArraySchema; -import io.swagger.v3.oas.models.media.ComposedSchema; -import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.*; import org.openapitools.codegen.CodegenDiscriminator.MappedModel; @@ -40,42 +35,30 @@ import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.github.curiousoddman.rgxgen.RgxGen; -import com.github.curiousoddman.rgxgen.config.RgxGenOption; -import com.github.curiousoddman.rgxgen.config.RgxGenProperties; import java.io.File; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.text.SimpleDateFormat; import java.util.*; -import java.util.stream.Collectors; - -import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; -import static org.openapitools.codegen.utils.StringUtils.camelize; -import static org.openapitools.codegen.utils.StringUtils.underscore; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.OnceLogger.once; -public class TypeScriptClientCodegen extends DefaultCodegen implements CodegenConfig { +public class TypeScriptClientCodegen extends AbstractTypeScriptClientCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(TypeScriptClientCodegen.class); - private static final String X_DISCRIMINATOR_TYPE = "x-discriminator-value"; - private static final String UNDEFINED_VALUE = "undefined"; - private static final String FRAMEWORK_SWITCH = "framework"; private static final String FRAMEWORK_SWITCH_DESC = "Specify the framework which should be used in the client code."; - private static final String[] FRAMEWORKS = { "fetch-api", "jquery" }; + private static final String[] FRAMEWORKS = {"fetch-api", "jquery"}; private static final String PLATFORM_SWITCH = "platform"; private static final String PLATFORM_SWITCH_DESC = "Specifies the platform the code should run on. The default is 'node' for the 'request' framework and 'browser' otherwise."; - private static final String[] PLATFORMS = { "browser", "node", "deno" }; + private static final String[] PLATFORMS = {"browser", "node", "deno"}; private static final String IMPORT_FILE_EXTENSION_SWITCH = "importFileExtension"; private static final String IMPORT_FILE_EXTENSION_SWITCH_DESC = "File extension to use with relative imports. Set it to '.js' or '.mjs' when using [ESM](https://nodejs.org/api/esm.html). Defaults to '.ts' when 'platform' is set to 'deno'."; - private static final String FILE_CONTENT_DATA_TYPE= "fileContentDataType"; + private static final String FILE_CONTENT_DATA_TYPE = "fileContentDataType"; private static final String FILE_CONTENT_DATA_TYPE_DESC = "Specifies the type to use for the content of a file - i.e. Blob (Browser, Deno) / Buffer (node)"; private static final String USE_RXJS_SWITCH = "useRxJS"; private static final String USE_RXJS_SWITCH_DESC = "Enable this to internally use rxjs observables. If disabled, a stub is used instead. This is required for the 'angular' framework."; @@ -85,27 +68,17 @@ public class TypeScriptClientCodegen extends DefaultCodegen implements CodegenCo private static final String USE_OBJECT_PARAMS_SWITCH = "useObjectParameters"; private static final String USE_OBJECT_PARAMS_DESC = "Use aggregate parameter objects as function arguments for api operations instead of passing each parameter as a separate function argument."; - private final Map frameworkToHttpLibMap; // NPM Options - private static final String SNAPSHOT = "snapshot"; - @SuppressWarnings("squid:S5164") - protected static final ThreadLocal SNAPSHOT_SUFFIX_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmm", Locale.ROOT)); private static final String NPM_REPOSITORY = "npmRepository"; - private static final String NPM_NAME = "npmName"; - private static final String NPM_VERSION = "npmVersion"; // NPM Option Values protected String npmRepository = null; protected String snapshot = null; - protected String npmName = null; - protected String npmVersion = "1.0.0"; - protected String modelPropertyNaming = "camelCase"; - protected HashSet languageGenericTypes; - private DateTimeFormatter iso8601Date = DateTimeFormatter.ISO_DATE; - private DateTimeFormatter iso8601DateTime = DateTimeFormatter.ISO_DATE_TIME; + private final DateTimeFormatter iso8601Date = DateTimeFormatter.ISO_DATE; + private final DateTimeFormatter iso8601DateTime = DateTimeFormatter.ISO_DATE_TIME; public TypeScriptClientCodegen() { super(); @@ -114,88 +87,23 @@ public TypeScriptClientCodegen() { this.frameworkToHttpLibMap.put("fetch-api", "isomorphic-fetch"); this.frameworkToHttpLibMap.put("jquery", "jquery"); - this.generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata).stability(Stability.EXPERIMENTAL).build(); outputFolder = "generated-code" + File.separator + "typescript"; embeddedTemplateDir = templateDir = "typescript"; - supportsInheritance = true; - // NOTE: TypeScript uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons. reservedWords.addAll(Arrays.asList( // local variable names used in API methods (endpoints) - "varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred", - "requestOptions", "from", + "from", // Typescript reserved words - "abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "constructor", "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", - "Set" - )); - - languageGenericTypes = new HashSet<>(Arrays.asList( - "Array" - )); - - instantiationTypes.put("array", "Array"); + "constructor")); - typeMapping = new HashMap<>(); - 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("map", "any"); - typeMapping.put("Set", "Set"); - typeMapping.put("set", "Set"); - typeMapping.put("date", "string"); typeMapping.put("DateTime", "Date"); - typeMapping.put("binary", "any"); - typeMapping.put("File", "any"); - typeMapping.put("file", "any"); - typeMapping.put("ByteArray", "string"); - typeMapping.put("UUID", "string"); - typeMapping.put("Error", "Error"); - typeMapping.put("AnyType", "any"); - - - cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package." + - " Required to generate a full package")); - cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package. If not provided, using the version from the OpenAPI specification file.").defaultValue(this.getNpmVersion())); - cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); - cliOptions.add(CliOption.newBoolean(SNAPSHOT, - "When setting this property to true, the version will be suffixed with -SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().toPattern(), - false)); - 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")); + cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); cliOptions.add(new CliOption(TypeScriptClientCodegen.FILE_CONTENT_DATA_TYPE, TypeScriptClientCodegen.FILE_CONTENT_DATA_TYPE_DESC).defaultValue("Buffer")); cliOptions.add(new CliOption(TypeScriptClientCodegen.USE_RXJS_SWITCH, TypeScriptClientCodegen.USE_RXJS_SWITCH_DESC).defaultValue("false")); cliOptions.add(new CliOption(TypeScriptClientCodegen.USE_OBJECT_PARAMS_SWITCH, TypeScriptClientCodegen.USE_OBJECT_PARAMS_DESC).defaultValue("false")); @@ -203,20 +111,23 @@ public TypeScriptClientCodegen() { cliOptions.add(new CliOption(TypeScriptClientCodegen.IMPORT_FILE_EXTENSION_SWITCH, TypeScriptClientCodegen.IMPORT_FILE_EXTENSION_SWITCH_DESC)); CliOption frameworkOption = new CliOption(TypeScriptClientCodegen.FRAMEWORK_SWITCH, TypeScriptClientCodegen.FRAMEWORK_SWITCH_DESC); - for (String option: TypeScriptClientCodegen.FRAMEWORKS) { + for (String option : TypeScriptClientCodegen.FRAMEWORKS) { frameworkOption.addEnum(option, option); } frameworkOption.defaultValue(FRAMEWORKS[0]); cliOptions.add(frameworkOption); CliOption platformOption = new CliOption(TypeScriptClientCodegen.PLATFORM_SWITCH, TypeScriptClientCodegen.PLATFORM_SWITCH_DESC); - for (String option: TypeScriptClientCodegen.PLATFORMS) { + for (String option : TypeScriptClientCodegen.PLATFORMS) { platformOption.addEnum(option, option); } platformOption.defaultValue(PLATFORMS[0]); cliOptions.add(platformOption); + // Set property naming to camelCase + supportModelPropertyNaming(CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.camelCase); + // Git supportingFiles.add(new SupportingFile(".gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); @@ -274,42 +185,12 @@ public void setNpmVersion(String npmVersion) { this.npmVersion = npmVersion; } - @Override - public CodegenType getTag() { - return CodegenType.CLIENT; - } - - @Override - public void preprocessOpenAPI(OpenAPI openAPI) { - - if (additionalProperties.containsKey(NPM_NAME)) { - - // If no npmVersion is provided in additional properties, version from API specification is used. - // If none of them is provided then fallbacks to default version - if (additionalProperties.containsKey(NPM_VERSION)) { - this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString()); - } else if (openAPI.getInfo() != null && openAPI.getInfo().getVersion() != null) { - this.setNpmVersion(openAPI.getInfo().getVersion()); - } - - if (additionalProperties.containsKey(SNAPSHOT) && Boolean.parseBoolean(additionalProperties.get(SNAPSHOT).toString())) { - if (npmVersion.toUpperCase(Locale.ROOT).matches("^.*-SNAPSHOT$")) { - this.setNpmVersion(npmVersion + "." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date())); - } else { - this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date())); - } - } - additionalProperties.put(NPM_VERSION, npmVersion); - - } - } - @Override public Map postProcessSupportingFileData(Map objs) { final Object propFramework = additionalProperties.get(FRAMEWORK_SWITCH); Map frameworks = new HashMap<>(); - for (String framework: FRAMEWORKS) { + for (String framework : FRAMEWORKS) { frameworks.put(framework, framework.equals(propFramework)); } objs.put("framework", propFramework); @@ -331,7 +212,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L OperationMap operationsMap = operations.getOperations(); List operationList = operationsMap.getOperation(); - for (CodegenOperation operation: operationList) { + for (CodegenOperation operation : operationList) { List responses = operation.responses; operation.returnType = this.getReturnType(responses); } @@ -340,12 +221,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L /** * Returns the correct return type based on all 2xx HTTP responses defined for an operation. + * * @param responses all CodegenResponses defined for one operation * @return TypeScript return type */ private String getReturnType(List responses) { Set returnTypes = new HashSet<>(); - for (CodegenResponse response: responses) { + for (CodegenResponse response : responses) { if (response.is2xx) { if (response.dataType != null) { returnTypes.add(response.dataType); @@ -362,22 +244,8 @@ private String getReturnType(List responses) { return String.join(" | ", returnTypes); } - @Override - public String escapeReservedWord(String name) { - if (this.reservedWordsMappings().containsKey(name)) { - return this.reservedWordsMappings().get(name); - } - return "_" + name; - } - @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); @@ -390,246 +258,30 @@ public String toVarName(String name) { 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; + return super.toParamName(name); } @Override - public String toModelName(final String name) { - String fullModelName = name; - fullModelName = addPrefix(fullModelName, modelNamePrefix); - fullModelName = addSuffix(fullModelName, modelNameSuffix); - return toTypescriptTypeName(fullModelName, "Model"); - } - - @Override - public String toModelImport(String name) { - // Use `/` instead of `File.Separator`. `File.Separator` is `\` in Windows, which is invalid Typescript. - return "../" + modelPackage() + "/" + toModelName(name); - } - - protected String addPrefix(String name, String prefix) { - if (!StringUtils.isEmpty(prefix)) { - name = prefix + "_" + name; - } - return name; - } - - protected String addSuffix(String name, String suffix) { - if (!StringUtils.isEmpty(suffix)) { - name = name + "_" + suffix; - } - - return name; - } - - protected String toTypescriptTypeName(final String name, String safePrefix) { - ArrayList exceptions = new ArrayList<>(Arrays.asList("\\|", " ")); - String sanName = sanitizeName(name, "(?![| ])\\W", exceptions); - - sanName = camelize(sanName); - - // model name cannot use reserved keyword, e.g. return - // this is unlikely to happen, because we have just camelized the name, while reserved words are usually all lowercase - if (isReservedWord(sanName)) { - String modelName = safePrefix + sanName; - LOGGER.warn("{} (reserved word) cannot be used as model name. Renamed to {}", sanName, modelName); - return modelName; - } - - // model name starts with number - if (sanName.matches("^\\d.*")) { - String modelName = safePrefix + sanName; // e.g. 200Response => Model200Response - LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", sanName, - modelName); - return modelName; - } - - if (languageSpecificPrimitives.contains(sanName)) { - String modelName = safePrefix + sanName; - LOGGER.warn("{} (model name matches existing language type) cannot be used as a model name. Renamed to {}", - sanName, modelName); - return modelName; - } - - return sanName; - } - - @Override - public String toModelFilename(String name) { - // should be the same as the model name - return toModelName(name); - } - - @Override - protected String getParameterDataType(Parameter parameter, Schema p) { - // handle enums of various data types - Schema inner; - if (ModelUtils.isArraySchema(p)) { - ArraySchema mp1 = (ArraySchema) p; - inner = mp1.getItems(); - return this.getSchemaType(p) + "<" + this.getParameterDataType(parameter, inner) + ">"; - } else if (ModelUtils.isMapSchema(p)) { - inner = (Schema) p.getAdditionalProperties(); - return "{ [key: string]: " + this.getParameterDataType(parameter, inner) + "; }"; - } else if (ModelUtils.isStringSchema(p)) { - // Handle string enums - if (p.getEnum() != null) { - return enumValuesToEnumTypeUnion(p.getEnum(), "string"); - } - } else if (ModelUtils.isIntegerSchema(p)) { - // Handle integer enums - if (p.getEnum() != null) { - return numericEnumValuesToEnumTypeUnion(new ArrayList(p.getEnum())); - } - } else if (ModelUtils.isNumberSchema(p)) { - // Handle double enums - if (p.getEnum() != null) { - return numericEnumValuesToEnumTypeUnion(new ArrayList(p.getEnum())); - } - } - 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 a literal union for representing enum values as a type - */ - protected String enumValuesToEnumTypeUnion(List values, String dataType) { - StringBuilder b = new StringBuilder(); - boolean isFirst = true; - for (String value : values) { - if (!isFirst) { - b.append(" | "); - } - b.append(toEnumValue(value, dataType)); - isFirst = false; - } - return b.toString(); - } + public String toVarName(String name) { + // sanitize name + name = sanitizeName(name); - /** - * Converts a list of numbers to a literal union for representing enum values as a type. - * Example output: 3 | 9 | 55 - * - * @param values a list of numbers - * @return a literal union for representing enum values as a type - */ - protected String numericEnumValuesToEnumTypeUnion(List values) { - List stringValues = new ArrayList<>(); - for (Number value : values) { - stringValues.add(value.toString()); + if ("_".equals(name)) { + name = "_u"; } - return enumValuesToEnumTypeUnion(stringValues, "number"); - } - @Override - public String toDefaultValue(Schema p) { - if (ModelUtils.isBooleanSchema(p)) { - return UNDEFINED_VALUE; - } else if (ModelUtils.isDateSchema(p)) { - return UNDEFINED_VALUE; - } else if (ModelUtils.isDateTimeSchema(p)) { - return UNDEFINED_VALUE; - } else if (ModelUtils.isNumberSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - return UNDEFINED_VALUE; - } else if (ModelUtils.isIntegerSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - return UNDEFINED_VALUE; - } else if (ModelUtils.isStringSchema(p)) { - if (p.getDefault() != null) { - return "'" + (String) p.getDefault() + "'"; - } - return UNDEFINED_VALUE; - } else { - return UNDEFINED_VALUE; + // if it's all upper case, do nothing + if (name.matches("^[A-Z_]*$")) { + return name; } + return super.toVarName(name); } @Override - protected boolean isReservedWord(String word) { - // NOTE: This differs from super's implementation in that TypeScript does _not_ want case insensitive matching. - return reservedWords.contains(word); - } - - @Override - public String getSchemaType(Schema p) { - String openAPIType = super.getSchemaType(p); - String type = null; - if (ModelUtils.isComposedSchema(p)) { - return openAPIType; - } else 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), LOWERCASE_FIRST_LETTER)); - } - - return camelize(sanitizeName(operationId), LOWERCASE_FIRST_LETTER); - } - - 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, LOWERCASE_FIRST_LETTER); - 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'"); - } - + public String toModelImport(String name) { + // Use `/` instead of `File.Separator`. `File.Separator` is `\` in Windows, which is invalid Typescript. + return "../" + modelPackage() + "/" + toModelName(name); } @Override @@ -637,60 +289,7 @@ 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; + return "'" + escapeText(value) + "'"; } } @@ -738,53 +337,6 @@ private List> toTsImports(CodegenModel cm, Set impor return tsImports; } - @Override - public Map postProcessAllModels(Map objs) { - Map result = super.postProcessAllModels(objs); - - for (ModelsMap entry : result.values()) { - for (ModelMap mo : entry.getModels()) { - CodegenModel cm = mo.getModel(); - if (cm.discriminator != null && cm.children != null) { - for (CodegenModel child : cm.children) { - this.setDiscriminatorValue(child, cm.discriminator.getPropertyName(), this.getDiscriminatorValue(child)); - } - } - } - } - return result; - } - - 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("/*", "/_*"); - } - @Override public String getName() { return "typescript"; @@ -795,17 +347,10 @@ public String getHelp() { return "Generates a TypeScript client library using Fetch API (beta)."; } - @Override public void processOpts() { super.processOpts(); - if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) { - setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); - } - - convertPropertyToBooleanAndWriteBack(CodegenConstants.SUPPORTS_ES6); - // change package names apiPackage = this.apiPackage + ".apis"; testPackage = this.testPackage + ".tests"; @@ -815,8 +360,8 @@ public void processOpts() { String httpLibName = this.getHttpLibForFramework(additionalProperties.get(FRAMEWORK_SWITCH).toString()); supportingFiles.add(new SupportingFile( - "http" + File.separator + httpLibName + ".mustache", - "http", httpLibName + ".ts" + "http" + File.separator + httpLibName + ".mustache", + "http", httpLibName + ".ts" )); Object propPlatform = additionalProperties.get(PLATFORM_SWITCH); @@ -826,7 +371,7 @@ public void processOpts() { } Map platforms = new HashMap<>(); - for (String platform: PLATFORMS) { + for (String platform : PLATFORMS) { platforms.put(platform, platform.equals(propPlatform)); } additionalProperties.put("platforms", platforms); @@ -861,14 +406,6 @@ public void processOpts() { } // NPM Settings - if (additionalProperties.containsKey(NPM_NAME)) { - setNpmName(additionalProperties.get(NPM_NAME).toString()); - } - - if (additionalProperties.containsKey(NPM_VERSION)) { - setNpmVersion(additionalProperties.get(NPM_VERSION).toString()); - } - if (additionalProperties.containsKey(NPM_REPOSITORY)) { setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString()); } @@ -1396,9 +933,7 @@ protected String setPropertyExampleValue(CodegenProperty p) { return example; } - /*** - * * Set the codegenParameter example value * We have a custom version of this function so we can invoke toExampleValue * @@ -1489,64 +1024,6 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set return cp; } - @Override - public String toAnyOfName(List names, ComposedSchema composedSchema) { - List types = getTypesFromSchemas(composedSchema.getAnyOf()); - - return String.join(" | ", types); - } - - @Override - public String toOneOfName(List names, ComposedSchema composedSchema) { - List types = getTypesFromSchemas(composedSchema.getOneOf()); - - return String.join(" | ", types); - } - - @Override - public String toAllOfName(List names, ComposedSchema composedSchema) { - List types = getTypesFromSchemas(composedSchema.getAllOf()); - - return String.join(" & ", types); - } - - /** - * Extracts the list of type names from a list of schemas. - * Excludes `AnyType` if there are other valid types extracted. - * - * @param schemas list of schemas - * @return list of types - */ - protected List getTypesFromSchemas(List schemas) { - List filteredSchemas = schemas.size() > 1 - ? schemas.stream().filter(schema -> !"AnyType".equals(super.getSchemaType(schema))).collect(Collectors.toList()) - : schemas; - - return filteredSchemas.stream().map(schema -> { - String schemaType = getSchemaType(schema); - if (ModelUtils.isArraySchema(schema)) { - ArraySchema ap = (ArraySchema) schema; - Schema inner = ap.getItems(); - schemaType = schemaType + "<" + getSchemaType(inner) + ">"; - } - return schemaType; - }).distinct().collect(Collectors.toList()); - } - - @Override - protected void addImport(CodegenModel m, String type) { - if (type == null) { - return; - } - - String[] parts = splitComposedType(type); - for (String s : parts) { - if (needToImport(s)) { - m.imports.add(s); - } - } - } - @Override protected void addImport(Set importsToBeAddedTo, String type) { if (type == null) { @@ -1567,9 +1044,6 @@ protected void addImport(Set importsToBeAddedTo, String type) { * @return list of types */ protected String[] splitComposedType(String type) { - return type.replace(" ","").split("[|&<>]"); + return type.replace(" ", "").split("[|&<>]"); } - - @Override - public GeneratorLanguage generatorLanguage() { return GeneratorLanguage.TYPESCRIPT; } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java index da55a175fbc9..8a229bf14ff3 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java @@ -89,7 +89,7 @@ public void containsESMTSConfigFileInCaseOfES6AndNPM() { codegen.additionalProperties().put("npmName", "@openapi/typescript-axios-petstore"); codegen.additionalProperties().put("snapshot", false); codegen.additionalProperties().put("npmVersion", "1.0.0-SNAPSHOT"); - codegen.setSupportsES6(true); + codegen.additionalProperties().put("supportsES6", true); codegen.processOpts(); @@ -104,7 +104,7 @@ public void doesNotContainESMTSConfigFileInCaseOfES5AndNPM() { codegen.additionalProperties().put("npmName", "@openapi/typescript-axios-petstore"); codegen.additionalProperties().put("snapshot", false); codegen.additionalProperties().put("npmVersion", "1.0.0-SNAPSHOT"); - codegen.setSupportsES6(false); + codegen.additionalProperties().put("supportsES6", false); codegen.processOpts(); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java index 34e67404179e..0f19827b4483 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java @@ -116,7 +116,7 @@ public void containsESMTSConfigFileInCaseOfES6AndNPM() { codegen.additionalProperties().put("npmName", "@openapi/typescript-fetch-petstore"); codegen.additionalProperties().put("snapshot", false); codegen.additionalProperties().put("npmVersion", "1.0.0-SNAPSHOT"); - codegen.setSupportsES6(true); + codegen.additionalProperties().put("supportsES6", true); codegen.processOpts(); @@ -131,7 +131,7 @@ public void doesNotContainESMTSConfigFileInCaseOfES5AndNPM() { codegen.additionalProperties().put("npmName", "@openapi/typescript-fetch-petstore"); codegen.additionalProperties().put("snapshot", false); codegen.additionalProperties().put("npmVersion", "1.0.0-SNAPSHOT"); - codegen.setSupportsES6(false); + codegen.additionalProperties().put("supportsES6", false); codegen.processOpts();