diff --git a/bin/configs/typescript-playwright.yaml b/bin/configs/typescript-playwright.yaml
new file mode 100644
index 00000000000..a95c8cfa431
--- /dev/null
+++ b/bin/configs/typescript-playwright.yaml
@@ -0,0 +1,4 @@
+generatorName: typescript-playwright
+outputDir: samples/client/petstore/typescript-wright/
+inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
+templateDir: modules/openapi-generator/src/main/resources/typescript-playwright
diff --git a/docs/generators.md b/docs/generators.md
index 1fe95d2ad93..048b97fa666 100644
--- a/docs/generators.md
+++ b/docs/generators.md
@@ -74,6 +74,7 @@ The following generators are available:
* [typescript-jquery](generators/typescript-jquery.md)
* [typescript-nestjs (experimental)](generators/typescript-nestjs.md)
* [typescript-node](generators/typescript-node.md)
+* [typescript-playwright](generators/typescript-playwright.md)
* [typescript-redux-query](generators/typescript-redux-query.md)
* [typescript-rxjs](generators/typescript-rxjs.md)
* [xojo-client](generators/xojo-client.md)
diff --git a/docs/generators/typescript-playwright.md b/docs/generators/typescript-playwright.md
new file mode 100644
index 00000000000..9c2612f8a3e
--- /dev/null
+++ b/docs/generators/typescript-playwright.md
@@ -0,0 +1,303 @@
+---
+title: Documentation for the typescript-playwright Generator
+---
+
+## METADATA
+
+| Property | Value | Notes |
+| -------- | ----- | ----- |
+| generator name | typescript-playwright | pass this to the generate command after -g |
+| generator stability | STABLE | |
+| generator type | CLIENT | |
+| generator language | Typescript | |
+| generator default templating engine | mustache | |
+| helpTxt | Generates end-to-end tests using Playwright for TypeScript. | |
+
+## CONFIG OPTIONS
+These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
+
+| Option | Description | Values | Default |
+| ------ | ----------- | ------ | ------- |
+|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|
+|enumPropertyNamingReplaceSpecialChar|Set to true to replace '-' and '+' symbols with 'minus_' and 'plus_' in enum of type string| |false|
+|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|
+|fileNaming|Naming convention for the output files: 'PascalCase', 'camelCase', 'kebab-case'.| |PascalCase|
+|importFileExtension|File extension to use with relative imports. Set it to '.js' or '.mjs' when using [ESM](https://nodejs.org/api/esm.html).| ||
+|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|- **true**
- The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
- **false**
- The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true|
+|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |camelCase|
+|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|
+|prefixParameterInterfaces|Setting this property to true will generate parameter interface declarations prefixed with API class name to avoid name conflicts.| |false|
+|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
+|sagasAndRecords|Setting this property to true will generate additional files for use with redux-saga and immutablejs.| |false|
+|snapshot|When setting this property to true, the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm| |false|
+|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
+|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
+|stringEnums|Generate string enums instead of objects for enum values.| |false|
+|supportsES6|Generate code that conforms to ES6.| |false|
+|useSingleRequestParameter|Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter.| |true|
+|useSquareBracketsInArrayNames|Setting this property to true will add brackets to array attribute names, e.g. my_values[].| |false|
+|withInterfaces|Setting this property to true will generate interfaces next to the default class implementations.| |false|
+|withoutRuntimeChecks|Setting this property to true will remove any runtime checks on the request and response payloads. Payloads will be casted to their expected types.| |false|
+
+## IMPORT MAPPING
+
+| Type/Alias | Imports |
+| ---------- | ------- |
+
+
+## INSTANTIATION TYPES
+
+| Type/Alias | Instantiated By |
+| ---------- | --------------- |
+|array|Array|
+
+
+## LANGUAGE PRIMITIVES
+
+
+- Array
+- Boolean
+- Date
+- Double
+- Error
+- File
+- Float
+- Integer
+- Long
+- Map
+- Object
+- ReadonlyArray
+- Set
+- String
+- any
+- boolean
+- number
+- object
+- string
+
+
+## RESERVED WORDS
+
+
+- ApiResponse
+- BASE_PATH
+- BaseAPI
+- BlobApiResponse
+- COLLECTION_FORMATS
+- Configuration
+- ConfigurationParameters
+- FetchAPI
+- FetchParams
+- HTTPBody
+- HTTPHeaders
+- HTTPMethod
+- HTTPQuery
+- Index
+- JSONApiResponse
+- Middleware
+- ModelPropertyNaming
+- RequestContext
+- RequestOpts
+- RequiredError
+- ResponseContext
+- ResponseTransformer
+- TextApiResponse
+- VoidApiResponse
+- abstract
+- await
+- boolean
+- break
+- byte
+- case
+- catch
+- char
+- class
+- configuration
+- const
+- continue
+- debugger
+- default
+- delete
+- do
+- double
+- else
+- enum
+- exists
+- export
+- extends
+- false
+- final
+- finally
+- float
+- for
+- formParams
+- function
+- goto
+- headerParams
+- if
+- implements
+- import
+- in
+- instanceof
+- int
+- interface
+- let
+- long
+- native
+- new
+- null
+- package
+- private
+- protected
+- public
+- queryParameters
+- requestOptions
+- return
+- short
+- static
+- super
+- switch
+- synchronized
+- this
+- throw
+- transient
+- true
+- try
+- typeof
+- useFormData
+- var
+- varLocalDeferred
+- varLocalPath
+- void
+- volatile
+- while
+- with
+- yield
+
+
+## FEATURE SET
+
+
+### Client Modification Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|BasePath|✓|ToolingExtension
+|Authorizations|✗|ToolingExtension
+|UserAgent|✗|ToolingExtension
+|MockServer|✗|ToolingExtension
+
+### Data Type Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Custom|✗|OAS2,OAS3
+|Int32|✓|OAS2,OAS3
+|Int64|✓|OAS2,OAS3
+|Float|✓|OAS2,OAS3
+|Double|✓|OAS2,OAS3
+|Decimal|✓|ToolingExtension
+|String|✓|OAS2,OAS3
+|Byte|✓|OAS2,OAS3
+|Binary|✓|OAS2,OAS3
+|Boolean|✓|OAS2,OAS3
+|Date|✓|OAS2,OAS3
+|DateTime|✓|OAS2,OAS3
+|Password|✓|OAS2,OAS3
+|File|✓|OAS2
+|Uuid|✗|
+|Array|✓|OAS2,OAS3
+|Null|✗|OAS3
+|AnyType|✗|OAS2,OAS3
+|Object|✓|OAS2,OAS3
+|Maps|✓|ToolingExtension
+|CollectionFormat|✓|OAS2
+|CollectionFormatMulti|✓|OAS2
+|Enum|✓|OAS2,OAS3
+|ArrayOfEnum|✓|ToolingExtension
+|ArrayOfModel|✓|ToolingExtension
+|ArrayOfCollectionOfPrimitives|✓|ToolingExtension
+|ArrayOfCollectionOfModel|✓|ToolingExtension
+|ArrayOfCollectionOfEnum|✓|ToolingExtension
+|MapOfEnum|✓|ToolingExtension
+|MapOfModel|✓|ToolingExtension
+|MapOfCollectionOfPrimitives|✓|ToolingExtension
+|MapOfCollectionOfModel|✓|ToolingExtension
+|MapOfCollectionOfEnum|✓|ToolingExtension
+
+### Documentation Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Readme|✓|ToolingExtension
+|Model|✓|ToolingExtension
+|Api|✓|ToolingExtension
+
+### Global Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Host|✓|OAS2,OAS3
+|BasePath|✓|OAS2,OAS3
+|Info|✓|OAS2,OAS3
+|Schemes|✗|OAS2,OAS3
+|PartialSchemes|✓|OAS2,OAS3
+|Consumes|✓|OAS2
+|Produces|✓|OAS2
+|ExternalDocumentation|✓|OAS2,OAS3
+|Examples|✓|OAS2,OAS3
+|XMLStructureDefinitions|✗|OAS2,OAS3
+|MultiServer|✗|OAS3
+|ParameterizedServer|✗|OAS3
+|ParameterStyling|✗|OAS3
+|Callbacks|✗|OAS3
+|LinkObjects|✗|OAS3
+
+### Parameter Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Path|✓|OAS2,OAS3
+|Query|✓|OAS2,OAS3
+|Header|✓|OAS2,OAS3
+|Body|✓|OAS2
+|FormUnencoded|✓|OAS2
+|FormMultipart|✓|OAS2
+|Cookie|✓|OAS3
+
+### Schema Support Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Simple|✓|OAS2,OAS3
+|Composite|✓|OAS2,OAS3
+|Polymorphism|✓|OAS2,OAS3
+|Union|✗|OAS3
+|allOf|✗|OAS2,OAS3
+|anyOf|✗|OAS3
+|oneOf|✗|OAS3
+|not|✗|OAS3
+
+### Security Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|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
+|SignatureAuth|✗|OAS3
+|AWSV4Signature|✗|ToolingExtension
+
+### Wire Format Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|JSON|✓|OAS2,OAS3
+|XML|✓|OAS2,OAS3
+|PROTOBUF|✗|ToolingExtension
+|Custom|✗|OAS2,OAS3
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptPlaywrightClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptPlaywrightClientCodegen.java
new file mode 100644
index 00000000000..c4345cf0ebb
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptPlaywrightClientCodegen.java
@@ -0,0 +1,1614 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openapitools.codegen.languages;
+
+import com.google.common.collect.ImmutableMap;
+import com.samskivert.mustache.Mustache;
+import io.swagger.v3.oas.models.Operation;
+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 io.swagger.v3.oas.models.responses.ApiResponse;
+import io.swagger.v3.oas.models.servers.Server;
+import io.swagger.v3.parser.util.SchemaTypeUtil;
+import lombok.Getter;
+import lombok.Setter;
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.meta.features.DocumentationFeature;
+import org.openapitools.codegen.meta.features.SecurityFeature;
+import org.openapitools.codegen.model.ModelMap;
+import org.openapitools.codegen.model.ModelsMap;
+import org.openapitools.codegen.model.OperationsMap;
+import org.openapitools.codegen.templating.mustache.IndentedLambda;
+import org.openapitools.codegen.utils.ModelUtils;
+
+import java.io.File;
+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.*;
+
+public class TypeScriptPlaywrightClientCodegen extends AbstractTypeScriptClientCodegen {
+ public static final String NPM_REPOSITORY = "npmRepository";
+ public static final String WITH_INTERFACES = "withInterfaces";
+ public static final String USE_SINGLE_REQUEST_PARAMETER = "useSingleRequestParameter";
+ public static final String PREFIX_PARAMETER_INTERFACES = "prefixParameterInterfaces";
+ public static final String WITHOUT_RUNTIME_CHECKS = "withoutRuntimeChecks";
+ public static final String STRING_ENUMS = "stringEnums";
+ public static final String STRING_ENUMS_DESC = "Generate string enums instead of objects for enum values.";
+ public static final String IMPORT_FILE_EXTENSION_SWITCH = "importFileExtension";
+ public 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).";
+ public static final String FILE_NAMING = "fileNaming";
+ public static final String KEBAB_CASE = "kebab-case";
+ public static final String CAMEL_CASE = "camelCase";
+ public static final String PASCAL_CASE = "PascalCase";
+ public static final String USE_SQUARE_BRACKETS_IN_ARRAY_NAMES = "useSquareBracketsInArrayNames";
+
+ @Getter
+ @Setter
+ protected String npmRepository = null;
+ @Getter
+ @Setter
+ protected String importFileExtension = "";
+ private boolean useSingleRequestParameter = true;
+ private boolean prefixParameterInterfaces = false;
+ protected boolean addedApiIndex = false;
+ protected boolean addedModelIndex = false;
+ protected boolean withoutRuntimeChecks = false;
+ protected boolean stringEnums = false;
+ protected String fileNaming = PASCAL_CASE;
+
+ // "Saga and Record" mode.
+ public static final String SAGAS_AND_RECORDS = "sagasAndRecords";
+ public static final String DETECT_PASSTHROUGH_MODELS_WITH_SUFFIX_AND_FIELD = "detectPassthroughModelsWithSuffixAndField";
+ public static final String INFER_UNIQUE_ID_FROM_NAME_SUFFIX = "inferUniqueIdFromNameSuffix";
+ public static final String INFER_ENTITY_FROM_UNIQUE_ID_WITH_NAME = "inferEntityFromUniqueIdWithName";
+ public static final String PACKAGE_AS_SOURCE_ONLY_LIBRARY = "packageAsSourceOnlyLibrary";
+
+ private static final String X_IS_UNIQUE_ID = "x-isUniqueId";
+ private static final String X_ENTITY_ID = "x-entityId";
+ private static final String X_OPERATION_RETURN_PASSTHROUGH = "x-operationReturnPassthrough";
+ private static final String X_KEEP_AS_JS_OBJECT = "x-keepAsJSObject";
+
+ protected boolean sagasAndRecords = false;
+ @Getter
+ @Setter
+ protected String detectPassthroughModelsWithSuffixAndField = null; // Ex: "Response;data"
+ @Setter
+ protected boolean inferUniqueIdFromNameSuffix = false;
+ @Getter
+ @Setter
+ protected String inferEntityFromUniqueIdWithName = null;
+ @Setter
+ protected boolean packageAsSourceOnlyLibrary = false;
+
+
+ public TypeScriptPlaywrightClientCodegen() {
+ super();
+
+ modifyFeatureSet(features -> features
+ .includeDocumentationFeatures(DocumentationFeature.Readme)
+ .includeSecurityFeatures(SecurityFeature.BearerToken));
+
+ // clear import mapping (from default generator) as TS does not use it
+ // at the moment
+ importMapping.clear();
+
+ outputFolder = "generated-code/typescript-playwright";
+ embeddedTemplateDir = templateDir = "typescript-playwright";
+
+ this.apiTemplateFiles.put("apis.mustache", ".ts");
+
+ this.addExtraReservedWords();
+
+ supportModelPropertyNaming(CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.camelCase);
+ 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(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(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, CodegenConstants.USE_SINGLE_REQUEST_PARAMETER_DESC, SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.TRUE.toString()));
+ this.cliOptions.add(new CliOption(PREFIX_PARAMETER_INTERFACES, "Setting this property to true will generate parameter interface declarations prefixed with API class name to avoid name conflicts.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
+ this.cliOptions.add(new CliOption(WITHOUT_RUNTIME_CHECKS, "Setting this property to true will remove any runtime checks on the request and response payloads. Payloads will be casted to their expected types.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
+ this.cliOptions.add(new CliOption(SAGAS_AND_RECORDS, "Setting this property to true will generate additional files for use with redux-saga and immutablejs.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
+ this.cliOptions.add(new CliOption(STRING_ENUMS, STRING_ENUMS_DESC, SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
+ this.cliOptions.add(new CliOption(IMPORT_FILE_EXTENSION_SWITCH, IMPORT_FILE_EXTENSION_SWITCH_DESC).defaultValue(""));
+ this.cliOptions.add(new CliOption(FILE_NAMING, "Naming convention for the output files: 'PascalCase', 'camelCase', 'kebab-case'.").defaultValue(this.fileNaming));
+ this.cliOptions.add(new CliOption(USE_SQUARE_BRACKETS_IN_ARRAY_NAMES, "Setting this property to true will add brackets to array attribute names, e.g. my_values[].", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
+ }
+
+ @Override
+ public String toApiFilename(String name) {
+ return convertUsingFileNamingConvention(super.toApiFilename(name));
+ }
+
+ @Override
+ public String toModelFilename(String name) {
+ return convertUsingFileNamingConvention(super.toModelFilename(name));
+ }
+
+ /**
+ * Converts the original name according to the current fileNaming
strategy.
+ *
+ * @param originalName the original name to transform
+ * @return the transformed name
+ */
+ private String convertUsingFileNamingConvention(String originalName) {
+ String name = originalName;
+ if (KEBAB_CASE.equals(fileNaming)) {
+ name = dashize(underscore(name));
+ } else if (CAMEL_CASE.equals(fileNaming)) {
+ name = camelize(name, LOWERCASE_FIRST_LETTER);
+ }
+ return name;
+ }
+
+ @Override
+ public String getName() {
+ return "typescript-playwright";
+ }
+
+ @Override
+ public String getHelp() {
+ return "Generates end-to-end tests using Playwright for TypeScript.";
+ }
+
+ public Boolean getWithoutRuntimeChecks() {
+ return withoutRuntimeChecks;
+ }
+
+ public void setWithoutRuntimeChecks(Boolean withoutRuntimeChecks) {
+ this.withoutRuntimeChecks = withoutRuntimeChecks;
+ }
+
+ public Boolean getStringEnums() {
+ return this.stringEnums;
+ }
+
+ public void setStringEnums(Boolean stringEnums) {
+ this.stringEnums = stringEnums;
+ }
+
+ /**
+ * Set the file naming type.
+ *
+ * @param fileNaming the file naming to use
+ */
+ public void setFileNaming(String fileNaming) {
+ if (PASCAL_CASE.equals(fileNaming) || CAMEL_CASE.equals(fileNaming) || KEBAB_CASE.equals(fileNaming)) {
+ this.fileNaming = fileNaming;
+ } else {
+ throw new IllegalArgumentException("Invalid file naming '" +
+ fileNaming + "'. Must be 'PascalCase', 'camelCase' or 'kebab-case'");
+ }
+ }
+
+ public Boolean getSagasAndRecords() {
+ return sagasAndRecords;
+ }
+
+ public void setSagasAndRecords(Boolean sagasAndRecords) {
+ this.sagasAndRecords = sagasAndRecords;
+ }
+
+ public String getPassthroughSuffix() {
+ return detectPassthroughModelsWithSuffixAndField != null ? detectPassthroughModelsWithSuffixAndField.split("\\.")[0] : null;
+ }
+
+ public String getPassthroughField() {
+ return detectPassthroughModelsWithSuffixAndField != null ? detectPassthroughModelsWithSuffixAndField.split("\\.")[1] : null;
+ }
+
+ public boolean getInferUniqueIdFromNameSuffix() {
+ return inferUniqueIdFromNameSuffix;
+ }
+
+ public boolean getPackageAsSourceOnlyLibrary() {
+ return packageAsSourceOnlyLibrary;
+ }
+
+ public boolean isUniqueIdAccordingToNameSuffix(String name) {
+ if (name == null) {
+ return false;
+ }
+ return "id".equals(name) ||
+ "ids".equals(name) ||
+ (name.length() >= 3 && name.endsWith("Id")) ||
+ (name.length() >= 4 && name.endsWith("Ids"));
+ }
+
+ @Override
+ public void processOpts() {
+ super.processOpts();
+ additionalProperties.put("isOriginalModelPropertyNaming", getModelPropertyNaming() == CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.original);
+ additionalProperties.put("modelPropertyNaming", getModelPropertyNaming().name());
+
+ String sourceDir = "";
+ if (additionalProperties.containsKey(NPM_NAME)) {
+ sourceDir = "src" + File.separator;
+ }
+
+ this.apiPackage = sourceDir + "apis";
+ this.modelPackage = sourceDir + "models";
+
+ supportingFiles.add(new SupportingFile("index.mustache", sourceDir, "index.ts"));
+ supportingFiles.add(new SupportingFile("runtime.mustache", sourceDir, "runtime.ts"));
+
+ if (additionalProperties.containsKey(IMPORT_FILE_EXTENSION_SWITCH)) {
+ this.setImportFileExtension(additionalProperties.get(IMPORT_FILE_EXTENSION_SWITCH).toString());
+ }
+ writePropertyBack(IMPORT_FILE_EXTENSION_SWITCH, getImportFileExtension());
+
+ if (additionalProperties.containsKey(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER)) {
+ this.setUseSingleRequestParameter(convertPropertyToBoolean(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER));
+ }
+ writePropertyBack(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, getUseSingleRequestParameter());
+
+ if (additionalProperties.containsKey(PREFIX_PARAMETER_INTERFACES)) {
+ this.setPrefixParameterInterfaces(convertPropertyToBoolean(PREFIX_PARAMETER_INTERFACES));
+ }
+ writePropertyBack(PREFIX_PARAMETER_INTERFACES, getPrefixParameterInterfaces());
+
+ if (additionalProperties.containsKey(NPM_NAME)) {
+ addNpmPackageGeneration();
+ }
+
+ if (additionalProperties.containsKey(WITHOUT_RUNTIME_CHECKS)) {
+ this.setWithoutRuntimeChecks(convertPropertyToBoolean(WITHOUT_RUNTIME_CHECKS));
+ }
+
+ if (additionalProperties.containsKey(STRING_ENUMS)) {
+ this.setStringEnums(convertPropertyToBoolean(STRING_ENUMS));
+ }
+
+ if (additionalProperties.containsKey(FILE_NAMING)) {
+ this.setFileNaming(additionalProperties.get(FILE_NAMING).toString());
+ }
+
+ if (!withoutRuntimeChecks) {
+ this.modelTemplateFiles.put("models.mustache", ".ts");
+ typeMapping.put("date", "Date");
+ typeMapping.put("DateTime", "Date");
+ }
+
+ if (additionalProperties.containsKey(SAGAS_AND_RECORDS)) {
+ this.setSagasAndRecords(convertPropertyToBoolean(SAGAS_AND_RECORDS));
+ if (this.getSagasAndRecords()) {
+ apiTemplateFiles.put("sagas.mustache", "Sagas.ts");
+ modelTemplateFiles.put("records.mustache", "Record.ts");
+ supportingFiles.add(new SupportingFile("runtimeSagasAndRecords.mustache", sourceDir, "runtimeSagasAndRecords.ts"));
+ supportingFiles.add(new SupportingFile("ApiEntitiesRecord.mustache", sourceDir, "ApiEntitiesRecord.ts"));
+ supportingFiles.add(new SupportingFile("ApiEntitiesReducer.mustache", sourceDir, "ApiEntitiesReducer.ts"));
+ supportingFiles.add(new SupportingFile("ApiEntitiesSelectors.mustache", sourceDir, "ApiEntitiesSelectors.ts"));
+
+ if (additionalProperties.containsKey(DETECT_PASSTHROUGH_MODELS_WITH_SUFFIX_AND_FIELD)) {
+ this.setDetectPassthroughModelsWithSuffixAndField((String) additionalProperties.get(DETECT_PASSTHROUGH_MODELS_WITH_SUFFIX_AND_FIELD));
+ }
+ if (additionalProperties.containsKey(INFER_UNIQUE_ID_FROM_NAME_SUFFIX)) {
+ this.setInferUniqueIdFromNameSuffix(convertPropertyToBoolean(INFER_UNIQUE_ID_FROM_NAME_SUFFIX));
+ }
+ if (additionalProperties.containsKey(INFER_ENTITY_FROM_UNIQUE_ID_WITH_NAME)) {
+ this.setInferEntityFromUniqueIdWithName((String) additionalProperties.get(INFER_ENTITY_FROM_UNIQUE_ID_WITH_NAME));
+ }
+ if (additionalProperties.containsKey(PACKAGE_AS_SOURCE_ONLY_LIBRARY)) {
+ this.setPackageAsSourceOnlyLibrary(convertPropertyToBoolean(PACKAGE_AS_SOURCE_ONLY_LIBRARY));
+ }
+
+ this.addExtraReservedWordsForSagasAndRecords();
+
+ if (this.getPackageAsSourceOnlyLibrary()) {
+ supportingFiles.add(new SupportingFile("sourceLibraryIndex.mustache", "", "index.ts"));
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toEnumDefaultValue(String value, String datatype) {
+ if (this.getSagasAndRecords()) {
+ return datatype + "." + value;
+ }
+ return super.toEnumDefaultValue(value, datatype);
+ }
+
+ @Override
+ protected String getEnumDefaultValue(String defaultValue, String dataType) {
+ if (this.getSagasAndRecords()) {
+ return defaultValue;
+ }
+ return super.getEnumDefaultValue(defaultValue, dataType);
+ }
+
+
+ @Override
+ protected ImmutableMap.Builder addMustacheLambdas() {
+ ImmutableMap.Builder lambdas = super.addMustacheLambdas();
+ lambdas.put("indented_star_1", new IndentedLambda(1, " ", "* ", false, false));
+ lambdas.put("indented_star_4", new IndentedLambda(5, " ", "* ", false, false));
+ return lambdas;
+ }
+
+ @Override
+ public String getTypeDeclaration(Schema p) {
+ if (ModelUtils.isFileSchema(p)) {
+ return "Blob";
+ } else if (ModelUtils.isBinarySchema(p)) {
+ return "Blob";
+ }
+ return super.getTypeDeclaration(p);
+ }
+
+ @Override
+ protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) {
+ codegenModel.additionalPropertiesType = getTypeDeclaration(ModelUtils.getAdditionalProperties(schema));
+ addImport(codegenModel, codegenModel.additionalPropertiesType);
+ }
+
+ @Override
+ public ModelsMap postProcessModels(ModelsMap objs) {
+ List models = postProcessModelsEnum(objs).getModels();
+
+ // process enum and custom properties in models
+ for (ModelMap mo : models) {
+ ExtendedCodegenModel cm = (ExtendedCodegenModel) mo.getModel();
+ cm.imports = new TreeSet<>(cm.imports);
+ this.processCodeGenModel(cm);
+ }
+
+ // Add supporting file only if we plan to generate files in /models
+ if (!objs.isEmpty() && !addedModelIndex) {
+ addedModelIndex = true;
+ supportingFiles.add(new SupportingFile("models.index.mustache", modelPackage().replace('.', File.separatorChar), "index.ts"));
+ }
+
+ return objs;
+ }
+
+ @Override
+ public void postProcessParameter(CodegenParameter parameter) {
+ super.postProcessParameter(parameter);
+ if (parameter.isFormParam && parameter.isArray && "binary".equals(parameter.dataFormat)) {
+ parameter.isCollectionFormatMulti = true;
+ }
+ }
+
+ @Override
+ public Map postProcessAllModels(Map objs) {
+ List allModels = new ArrayList<>();
+ List entityModelClassnames = new ArrayList<>();
+
+ Map result = super.postProcessAllModels(objs);
+ for (ModelsMap entry : result.values()) {
+ for (ModelMap model : entry.getModels()) {
+ ExtendedCodegenModel codegenModel = (ExtendedCodegenModel) model.getModel();
+ model.put("hasImports", codegenModel.imports.size() > 0);
+ model.put("tsImports", toTsImports(codegenModel, parseImports(codegenModel)));
+ allModels.add(codegenModel);
+ if (codegenModel.isEntity) {
+ entityModelClassnames.add(codegenModel.classname);
+ }
+ }
+ }
+
+ for (ExtendedCodegenModel rootModel : allModels) {
+ for (String curImport : rootModel.imports) {
+ boolean isModelImport = false;
+ for (ExtendedCodegenModel model : allModels) {
+ if (model.classname.equals(curImport) && !model.isEnum) {
+ isModelImport = true;
+ break;
+ }
+ }
+ if (isModelImport) {
+ rootModel.modelImports.add(curImport);
+ }
+ }
+
+ for (CodegenProperty cpVar : rootModel.vars) {
+ ExtendedCodegenProperty var = (ExtendedCodegenProperty) cpVar;
+ if (var.isModel && entityModelClassnames.indexOf(var.dataType) != -1) {
+ var.isEntity = true;
+ } else if (var.isArray && var.items.isModel && entityModelClassnames.indexOf(var.items.dataType) != -1) {
+ ((ExtendedCodegenProperty) var.items).isEntity = true;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parse imports
+ */
+ private Set parseImports(CodegenModel cm) {
+ Set newImports = new HashSet<>();
+ if (cm.imports.size() > 0) {
+ for (String name : cm.imports) {
+ if (name.indexOf(" | ") >= 0) {
+ String[] parts = name.split(" \\| ");
+ Collections.addAll(newImports, parts);
+ } else {
+ newImports.add(name);
+ }
+ }
+ }
+ return newImports;
+ }
+
+ private List