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 + + + +## RESERVED WORDS + + + +## 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> toTsImports(CodegenModel cm, Set imports) { + List> tsImports = new ArrayList<>(); + for (String im : imports) { + if (!im.equals(cm.classname)) { + HashMap tsImport = new HashMap<>(); + // TVG: This is used as class name in the import statements of the model file + tsImport.put("classname", im); + tsImport.put("filename", toModelFilename(im)); + tsImports.add(tsImport); + } + } + return tsImports; + } + + private void autoSetDefaultValueForProperty(ExtendedCodegenProperty var) { + if (var.isArray || var.isModel) { + var.defaultValue = var.dataTypeAlternate + "()"; + } else if (var.isUniqueId) { + var.defaultValue = "\"-1\""; + } else if (var.isEnum) { + var.defaultValue = "'" + var._enum.get(0) + "'"; + updateCodegenPropertyEnum(var); + } else if (var.dataType.equalsIgnoreCase("string")) { + var.defaultValue = "\"\""; + } else if (var.dataType.equalsIgnoreCase("number")) { + var.defaultValue = "0"; + } else if (var.dataType.equalsIgnoreCase("boolean")) { + var.defaultValue = "false"; + } else { + if (var.allowableValues != null && var.allowableValues.get("enumVars") instanceof ArrayList && ((ArrayList) var.allowableValues.get("enumVars")).get(0) instanceof HashMap) { + var.defaultValue = var.dataTypeAlternate + "." + ((HashMap) ((ArrayList) var.allowableValues.get("enumVars")).get(0)).get("name"); + } + } + } + + private void addNpmPackageGeneration() { + + if (additionalProperties.containsKey(NPM_REPOSITORY)) { + this.setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString()); + } + + //Files for building our lib + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("package.mustache", "", "package.json")); + supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json")); + // in case ECMAScript 6 is supported add another tsconfig for an ESM (ECMAScript Module) + if (supportsES6) { + supportingFiles.add(new SupportingFile("tsconfig.esm.mustache", "", "tsconfig.esm.json")); + } + supportingFiles.add(new SupportingFile("npmignore.mustache", "", ".npmignore")); + supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); + } + + @Override + public List fromRequestBodyToFormParameters(RequestBody body, Set imports) { + List superParams = super.fromRequestBodyToFormParameters(body, imports); + List extendedParams = new ArrayList(); + for (CodegenParameter cp : superParams) { + extendedParams.add(new ExtendedCodegenParameter(cp)); + } + return extendedParams; + } + + @Override + public ExtendedCodegenParameter fromParameter(Parameter parameter, Set imports) { + CodegenParameter cp = super.fromParameter(parameter, imports); + return new ExtendedCodegenParameter(cp); + } + + @Override + public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set imports) { + CodegenParameter cp = super.fromFormProperty(name, propertySchema, imports); + return new ExtendedCodegenParameter(cp); + } + + @Override + public CodegenParameter fromRequestBody(RequestBody body, Set imports, String bodyParameterName) { + CodegenParameter cp = super.fromRequestBody(body, imports, bodyParameterName); + return new ExtendedCodegenParameter(cp); + } + + @Override + public ExtendedCodegenProperty fromProperty(String name, Schema p, boolean required) { + CodegenProperty cp = super.fromProperty(name, p, required); + return new ExtendedCodegenProperty(cp); + } + + @Override + public ExtendedCodegenModel fromModel(String name, Schema model) { + CodegenModel cm = super.fromModel(name, model); + return new ExtendedCodegenModel(cm); + } + + @Override + public ExtendedCodegenOperation fromOperation(String path, String httpMethod, Operation operation, List servers) { + CodegenOperation superOp = super.fromOperation(path, httpMethod, operation, servers); + ExtendedCodegenOperation op = new ExtendedCodegenOperation(superOp); + + if (this.getSagasAndRecords()) { + ApiResponse methodResponse = findMethodResponse(operation.getResponses()); + if (methodResponse != null) { + Map schemas = ModelUtils.getSchemas(this.openAPI); + Schema schema = null; + if (schemas != null) { + schema = schemas.get(op.returnBaseType); + } + + ExtendedCodegenModel cm = null; + if (schema != null) { + cm = fromModel(op.returnBaseType, schema); + + Object returnPassthrough = cm.vendorExtensions.get(X_OPERATION_RETURN_PASSTHROUGH); + if (returnPassthrough instanceof String) { + if (((String) returnPassthrough).isEmpty()) { + op.hasReturnPassthroughVoid = true; + op.returnPassthrough = null; + } else { + boolean foundMatch = false; + for (CodegenProperty var : cm.vars) { + if (var.name.equals(returnPassthrough)) { + foundMatch = true; + break; + } + } + if (foundMatch) { + op.returnPassthrough = (String) returnPassthrough; + } else { // no match, treat as if empty. + op.hasReturnPassthroughVoid = true; + op.returnPassthrough = null; + } + } + } else if (this.getDetectPassthroughModelsWithSuffixAndField() != null && op.returnBaseType.length() > this.getPassthroughSuffix().length() && op.returnBaseType.endsWith(this.getPassthroughSuffix())) { + boolean foundMatch = false; + for (CodegenProperty var : cm.vars) { + if (var.name.equals(this.getPassthroughField())) { + foundMatch = true; + break; + } + } + if (foundMatch) { + op.returnPassthrough = this.getPassthroughField(); + } else { // no match, treat as if empty. + op.hasReturnPassthroughVoid = true; + op.returnPassthrough = null; + } + } + } + + if (!op.hasReturnPassthroughVoid) { + Schema responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(openAPI, methodResponse)); + ExtendedCodegenProperty cp = null; + if (op.returnPassthrough instanceof String && cm != null) { + cp = (ExtendedCodegenProperty) this.processCodeGenModel(cm).vars.get(1); + } else if (responseSchema != null) { + cp = fromProperty("response", responseSchema, false); + this.processCodegenProperty(cp, "", null); + } + + op.returnBaseTypeAlternate = null; + if (cp != null) { + op.returnTypeAlternate = cp.dataTypeAlternate; + op.returnTypeIsModel = cp.isModel; + op.returnTypeIsArray = cp.isArray; + if (cp.isArray) { + if (cp.items.isModel) { + op.returnTypeSupportsEntities = true; + op.returnBaseTypeAlternate = cp.items.dataType + "Record"; + } else if (cp.items.allowableValues != null) { + op.returnBaseTypeAlternate = cp.items.dataType; + } + } else if (cp.isModel) { + op.returnTypeSupportsEntities = true; + op.returnBaseTypeAlternate = cp.dataTypeAlternate; + } else if (cp.allowableValues != null) { + op.returnBaseTypeAlternate = cp.dataTypeAlternate; + } + + } + } + } + } + + return op; + } + + @Override + public String escapeReservedWord(String name) { + if (this.getSagasAndRecords()) { + if (this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return "_" + name; + } else { + return super.escapeReservedWord(name); + } + } + + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap operations, List allModels) { + // Add supporting file only if we plan to generate files in /apis + if (!operations.isEmpty() && !addedApiIndex) { + addedApiIndex = true; + supportingFiles.add(new SupportingFile("apis.index.mustache", apiPackage().replace('.', File.separatorChar), "index.ts")); + if (this.getSagasAndRecords()) { + supportingFiles.add(new SupportingFile("sagaApiManager.mustache", apiPackage().replace('.', File.separatorChar), "SagaApiManager.ts")); + supportingFiles.add(new SupportingFile("allSagas.mustache", apiPackage().replace('.', File.separatorChar), "allSagas.ts")); + } + } + + // Add supporting file only if we plan to generate files in /models + if (!allModels.isEmpty() && !addedModelIndex) { + addedModelIndex = true; + supportingFiles.add(new SupportingFile("models.index.mustache", modelPackage().replace('.', File.separatorChar), "index.ts")); + } + + this.addOperationModelImportInformation(operations); + this.updateOperationParameterForEnum(operations); + if (this.getSagasAndRecords()) { + this.updateOperationParameterForSagaAndRecords(operations); + } + this.addOperationObjectResponseInformation(operations); + this.addOperationPrefixParameterInterfacesInformation(operations); + this.escapeOperationIds(operations); + return operations; + } + + @Override + public Map postProcessSupportingFileData(Map objs) { + Map parentObjs = super.postProcessSupportingFileData(objs); + + parentObjs.put("useSagaAndRecords", this.getSagasAndRecords()); + + return parentObjs; + } + + private ExtendedCodegenModel processCodeGenModel(ExtendedCodegenModel cm) { + if (this.getSagasAndRecords()) { + Object xEntityId = cm.vendorExtensions.get(X_ENTITY_ID); + if (xEntityId == null && this.getInferEntityFromUniqueIdWithName() != null) { + xEntityId = this.getInferEntityFromUniqueIdWithName(); + } + Object vendorKeepAsJSObject = cm.vendorExtensions.get(X_KEEP_AS_JS_OBJECT); + String[] propertiesToKeepAsJSObject = null; + if (vendorKeepAsJSObject instanceof String) { + propertiesToKeepAsJSObject = ((String) vendorKeepAsJSObject).split(","); + } + + for (CodegenProperty cpVar : cm.vars) { + ExtendedCodegenProperty var = (ExtendedCodegenProperty) cpVar; + if (propertiesToKeepAsJSObject != null && Arrays.asList(propertiesToKeepAsJSObject).contains(var.name)) { + var.keepAsJSObject = true; + } + boolean parentIsEntity = this.processCodegenProperty(var, cm.classname, xEntityId); + if (parentIsEntity) { + cm.isEntity = true; + } + } + + Object returnPassthrough = cm.vendorExtensions.get(X_OPERATION_RETURN_PASSTHROUGH); + if (returnPassthrough instanceof String) { + if (((String) returnPassthrough).isEmpty()) { + cm.hasReturnPassthroughVoid = true; + cm.returnPassthrough = null; + } else { + boolean foundMatch = false; + for (CodegenProperty var : cm.vars) { + if (var.name.equals(returnPassthrough)) { + foundMatch = true; + break; + } + } + if (foundMatch) { + cm.returnPassthrough = (String) returnPassthrough; + } else { // no match, treat as if empty. + cm.hasReturnPassthroughVoid = true; + cm.returnPassthrough = null; + } + } + } else if (this.getDetectPassthroughModelsWithSuffixAndField() != null && cm.name.length() > this.getPassthroughSuffix().length() && cm.name.endsWith(this.getPassthroughSuffix())) { + boolean foundMatch = false; + for (CodegenProperty var : cm.vars) { + if (var.name.equals(this.getPassthroughField())) { + foundMatch = true; + break; + } + } + if (foundMatch) { + cm.returnPassthrough = this.getPassthroughField(); + } else { // no match, treat as if empty. + cm.hasReturnPassthroughVoid = true; + cm.returnPassthrough = null; + } + } + } else { + for (CodegenProperty cpVar : cm.vars) { + ExtendedCodegenProperty var = (ExtendedCodegenProperty) cpVar; + this.processCodegenProperty(var, cm.classname, null); + } + } + + if (cm.parent != null) { + for (CodegenProperty cpVar : cm.allVars) { + ExtendedCodegenProperty var = (ExtendedCodegenProperty) cpVar; + + if (Boolean.TRUE.equals(var.isEnum)) { + var.datatypeWithEnum = var.datatypeWithEnum + .replace(var.enumName, cm.classname + var.enumName); + } + } + } + if (!cm.oneOf.isEmpty()) { + // For oneOfs only import $refs within the oneOf + TreeSet oneOfRefs = new TreeSet<>(); + for (String im : cm.imports) { + if (cm.oneOf.contains(im)) { + oneOfRefs.add(im); + } + } + cm.imports = oneOfRefs; + } + return cm; + } + + private boolean processCodegenProperty(ExtendedCodegenProperty var, String parentClassName, Object xEntityId) { + // name enum with model name, e.g. StatusEnum => PetStatusEnum + if (Boolean.TRUE.equals(var.isEnum)) { + // behaviour for enum names is specific for Typescript Fetch, not using namespaces + var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, parentClassName + var.enumName); + + // need to post-process defaultValue, was computed with previous var.datatypeWithEnum + if (var.defaultValue != null && !var.defaultValue.equals("undefined")) { + int dotPos = var.defaultValue.indexOf("."); + if (dotPos != -1) { + var.defaultValue = var.datatypeWithEnum + var.defaultValue.substring(dotPos); + } + } + } + + boolean parentIsEntity = false; + if (this.getSagasAndRecords()) { + if (var.vendorExtensions.get(X_IS_UNIQUE_ID) instanceof Boolean) { + var.isUniqueId = Boolean.TRUE.equals(var.vendorExtensions.get(X_IS_UNIQUE_ID)); + } else if (this.getInferUniqueIdFromNameSuffix() && (var.isArray && "number".equals(var.items.dataType)) || ("number".equals(var.dataType))) { + var.isUniqueId = this.isUniqueIdAccordingToNameSuffix(var.name); + } + if (var.isUniqueId && xEntityId != null && xEntityId.equals(var.name)) { + parentIsEntity = true; + } + + var.dataTypeAlternate = var.dataType; + if (var.isArray) { + var.isUniqueId = var.isUniqueId || var.itemsAreUniqueId(); + var.dataTypeAlternate = var.dataType.replace("Array<", "List<"); + String newItemsDataType = var.getItemsDataType(); + if (var.items.isModel) { + newItemsDataType = var.items.dataType + "Record"; + var.dataTypeAlternate = var.dataTypeAlternate.replace(var.items.dataType, newItemsDataType); + } else if (var.items.isEnum) { + newItemsDataType = var.items.datatypeWithEnum; + var.dataTypeAlternate = var.dataTypeAlternate.replace(var.items.dataType, newItemsDataType); + } else if (var.isUniqueId) { + newItemsDataType = "string"; + var.dataTypeAlternate = var.dataTypeAlternate.replace("number", newItemsDataType); + } + + if (var.itemsAreNullable()) { + var.dataTypeAlternate = var.dataTypeAlternate.replace(newItemsDataType, newItemsDataType + " | null"); + } + } else if (var.isEnum) { + var.dataTypeAlternate = var.datatypeWithEnum; + } else if (var.isModel) { + var.dataTypeAlternate = var.dataType + "Record"; + } else if (var.isUniqueId) { + var.dataTypeAlternate = "string"; + if (var.isNullable) { + var.dataTypeAlternate = var.dataTypeAlternate + " | null"; + } + } + if (var.defaultValue == null || var.defaultValue.equals("undefined")) { + this.autoSetDefaultValueForProperty(var); + } + } + return parentIsEntity; + } + + private boolean itemsAreNullable(ExtendedCodegenProperty var) { + return var.items.isNullable || (var.items.items != null && var.items.items.isNullable); + } + + private void escapeOperationIds(OperationsMap operations) { + for (CodegenOperation _op : operations.getOperations().getOperation()) { + ExtendedCodegenOperation op = (ExtendedCodegenOperation) _op; + String param = op.operationIdCamelCase + "Request"; + if (op.imports.contains(param)) { + // we import a model with the same name as the generated operation, escape it + op.operationIdCamelCase += "Operation"; + op.operationIdLowerCase += "operation"; + op.operationIdSnakeCase += "_operation"; + } + } + } + + private void addOperationModelImportInformation(OperationsMap operations) { + // This method will add extra information to the operations.imports array. + // The api template uses this information to import all the required + // models for a given operation. + List> imports = operations.getImports(); + List existingRecordClassNames = new ArrayList<>(); + List existingClassNames = new ArrayList<>(); + for (Map im : imports) { + String className = im.get("import").replace(modelPackage() + ".", ""); + existingClassNames.add(className); + existingRecordClassNames.add(className + "Record"); + im.put("className", className); + } + + if (this.getSagasAndRecords()) { + Set additionalPassthroughImports = new TreeSet<>(); + for (CodegenOperation _op : operations.getOperations().getOperation()) { + ExtendedCodegenOperation op = (ExtendedCodegenOperation) _op; + if (op.returnPassthrough != null && op.returnBaseTypeAlternate != null) { + if (op.returnTypeSupportsEntities && !existingRecordClassNames.contains(op.returnBaseTypeAlternate)) { + additionalPassthroughImports.add(op.returnBaseTypeAlternate); + } else if (!op.returnTypeSupportsEntities && !existingClassNames.contains(op.returnBaseTypeAlternate)) { + additionalPassthroughImports.add(op.returnBaseTypeAlternate); + } + } + } + operations.put("passthroughImports", additionalPassthroughImports); + operations.put("hasPassthroughImports", additionalPassthroughImports.size() > 0); + } + } + + private void updateOperationParameterForEnum(OperationsMap operations) { + // This method will add extra information as to whether or not we have enums and + // update their names with the operation.id prefixed. + // It will also set the uniqueId status if provided. + boolean hasEnum = false; + for (CodegenOperation _op : operations.getOperations().getOperation()) { + ExtendedCodegenOperation op = (ExtendedCodegenOperation) _op; + for (CodegenParameter cpParam : op.allParams) { + ExtendedCodegenParameter param = (ExtendedCodegenParameter) cpParam; + + if (Boolean.TRUE.equals(param.isEnum)) { + hasEnum = true; + param.datatypeWithEnum = param.datatypeWithEnum + .replace(param.enumName, op.operationIdCamelCase + param.enumName); + } + } + } + + operations.put("hasEnums", hasEnum); + } + + private void updateOperationParameterForSagaAndRecords(OperationsMap operations) { + // This method will add extra information as to whether or not we have enums and + // update their names with the operation.id prefixed. + // It will also set the uniqueId status if provided. + for (CodegenOperation _op : operations.getOperations().getOperation()) { + ExtendedCodegenOperation op = (ExtendedCodegenOperation) _op; + for (CodegenParameter cpParam : op.allParams) { + ExtendedCodegenParameter param = (ExtendedCodegenParameter) cpParam; + + if (param.vendorExtensions.get(X_IS_UNIQUE_ID) instanceof Boolean) { + param.isUniqueId = Boolean.TRUE.equals(param.vendorExtensions.get(X_IS_UNIQUE_ID)); + } else if (this.getInferUniqueIdFromNameSuffix() && (param.isArray && "number".equals(param.items.dataType)) || ("number".equals(param.dataType))) { + param.isUniqueId = this.isUniqueIdAccordingToNameSuffix(param.paramName); + } + + + param.dataTypeAlternate = param.dataType; + if (param.isArray) { + param.isUniqueId = param.isUniqueId || param.itemsAreUniqueId(); + param.dataTypeAlternate = param.dataType.replace("Array<", "List<"); + String newItemsDataType = param.getItemsDataType(); + if (param.items.isModel) { + newItemsDataType = param.items.dataType + "Record"; + param.dataTypeAlternate = param.dataTypeAlternate.replace(param.items.dataType, newItemsDataType); + } else if (param.items.isEnum) { + newItemsDataType = param.datatypeWithEnum.substring(param.datatypeWithEnum.lastIndexOf("<") + 1, param.datatypeWithEnum.indexOf(">")); + param.dataTypeAlternate = param.datatypeWithEnum.replace("Array<", "List<"); + } else if (param.isUniqueId) { + newItemsDataType = "string"; + param.dataTypeAlternate = param.dataTypeAlternate.replace("number", newItemsDataType); + } + + if (param.itemsAreNullable()) { + param.dataTypeAlternate = param.dataTypeAlternate.replace(newItemsDataType, newItemsDataType + " | null"); + } + } else if (param.isEnum) { + param.dataTypeAlternate = param.datatypeWithEnum; + } else if (param.isModel) { + param.dataTypeAlternate = param.dataType + "Record"; + } else if (param.isUniqueId) { + param.dataTypeAlternate = "string"; + if (param.isNullable) { + param.dataTypeAlternate = param.dataTypeAlternate + " | null"; + } + } + } + } + } + + private void addOperationObjectResponseInformation(OperationsMap operations) { + // This method will modify the information on the operations' return type. + // The api template uses this information to know when to return a text + // response for a given simple response operation. + for (CodegenOperation _op : operations.getOperations().getOperation()) { + ExtendedCodegenOperation op = (ExtendedCodegenOperation) _op; + if ("object".equals(op.returnType)) { + op.isMap = true; + op.returnSimpleType = false; + } + } + } + + private void addOperationPrefixParameterInterfacesInformation(Map operations) { + operations.put("prefixParameterInterfaces", getPrefixParameterInterfaces()); + } + + private void addExtraReservedWords() { + this.reservedWords.add("BASE_PATH"); + this.reservedWords.add("BaseAPI"); + this.reservedWords.add("RequiredError"); + this.reservedWords.add("COLLECTION_FORMATS"); + this.reservedWords.add("FetchAPI"); + this.reservedWords.add("ConfigurationParameters"); + this.reservedWords.add("Configuration"); + this.reservedWords.add("configuration"); + this.reservedWords.add("HTTPMethod"); + this.reservedWords.add("HTTPHeaders"); + this.reservedWords.add("HTTPQuery"); + this.reservedWords.add("HTTPBody"); + this.reservedWords.add("ModelPropertyNaming"); + this.reservedWords.add("FetchParams"); + this.reservedWords.add("RequestOpts"); + this.reservedWords.add("exists"); + this.reservedWords.add("RequestContext"); + this.reservedWords.add("ResponseContext"); + this.reservedWords.add("Middleware"); + this.reservedWords.add("ApiResponse"); + this.reservedWords.add("ResponseTransformer"); + this.reservedWords.add("JSONApiResponse"); + this.reservedWords.add("VoidApiResponse"); + this.reservedWords.add("BlobApiResponse"); + this.reservedWords.add("TextApiResponse"); + // "Index" would create a file "Index.ts" which on case insensitive filesystems + // would override our "index.js" file + this.reservedWords.add("Index"); + } + + private void addExtraReservedWordsForSagasAndRecords() { + // immutablejs Records have potentially many reserved words. Adding only strict minimum for now. + this.reservedWords.add("entries"); + } + + private boolean getUseSingleRequestParameter() { + return useSingleRequestParameter; + } + + private void setUseSingleRequestParameter(boolean useSingleRequestParameter) { + this.useSingleRequestParameter = useSingleRequestParameter; + } + + private boolean getPrefixParameterInterfaces() { + return prefixParameterInterfaces; + } + + private void setPrefixParameterInterfaces(boolean prefixParameterInterfaces) { + this.prefixParameterInterfaces = prefixParameterInterfaces; + } + + private static boolean itemsAreUniqueId(CodegenProperty items) { + if (items != null && items.items != null) { + return itemsAreUniqueId(items.items); + } + if (items != null && items.vendorExtensions.get(X_IS_UNIQUE_ID) instanceof Boolean) { + return Boolean.TRUE.equals(items.vendorExtensions.get(X_IS_UNIQUE_ID)); + } + return false; + } + + private static boolean itemsAreNullable(CodegenProperty items) { + if (items == null) { + return true; + } + if (items.items != null) { + return itemsAreNullable(items.items); + } + return items.isNullable; + } + + private static String getItemsDataType(CodegenProperty items) { + if (items == null) { + return null; + } + if (items.items != null) { + return getItemsDataType(items.items); + } + return items.dataType; + } + + class ExtendedCodegenParameter extends CodegenParameter { + public String dataTypeAlternate; + public boolean isUniqueId; // this parameter represents a unique id (x-isUniqueId: true) + public List readOnlyVars; // a list of read-only properties + public boolean hasReadOnly = false; // indicates the type has at least one read-only property + + public boolean itemsAreUniqueId() { + return TypeScriptPlaywrightClientCodegen.itemsAreUniqueId(this.items); + } + + public boolean itemsAreNullable() { + return TypeScriptPlaywrightClientCodegen.itemsAreNullable(this.items); + } + + public String getItemsDataType() { + return TypeScriptPlaywrightClientCodegen.getItemsDataType(this.items); + } + + public boolean isDateType() { + return isDate && "Date".equals(dataType); + } + + public boolean isDateTimeType() { + return isDateTime && "Date".equals(dataType); + } + + public ExtendedCodegenParameter(CodegenParameter cp) { + super(); + + this.isFormParam = cp.isFormParam; + this.isQueryParam = cp.isQueryParam; + this.isPathParam = cp.isPathParam; + this.isHeaderParam = cp.isHeaderParam; + this.isCookieParam = cp.isCookieParam; + this.isBodyParam = cp.isBodyParam; + this.isContainer = cp.isContainer; + this.isCollectionFormatMulti = cp.isCollectionFormatMulti; + this.isPrimitiveType = cp.isPrimitiveType; + this.isModel = cp.isModel; + this.isExplode = cp.isExplode; + this.baseName = cp.baseName; + this.paramName = cp.paramName; + this.dataType = cp.dataType; + this.datatypeWithEnum = cp.datatypeWithEnum; + this.dataFormat = cp.dataFormat; + this.contentType = cp.contentType; + this.collectionFormat = cp.collectionFormat; + this.description = cp.description; + this.unescapedDescription = cp.unescapedDescription; + this.baseType = cp.baseType; + this.defaultValue = cp.defaultValue; + this.enumName = cp.enumName; + this.style = cp.style; + this.nameInLowerCase = cp.nameInLowerCase; + this.example = cp.example; + this.jsonSchema = cp.jsonSchema; + this.isString = cp.isString; + this.isNumeric = cp.isNumeric; + this.isInteger = cp.isInteger; + this.isLong = cp.isLong; + this.isNumber = cp.isNumber; + this.isFloat = cp.isFloat; + this.isDouble = cp.isDouble; + this.isDecimal = cp.isDecimal; + this.isByteArray = cp.isByteArray; + this.isBinary = cp.isBinary; + this.isBoolean = cp.isBoolean; + this.isDate = cp.isDate; + this.isDateTime = cp.isDateTime; + this.isUuid = cp.isUuid; + this.isUri = cp.isUri; + this.isEmail = cp.isEmail; + this.isFreeFormObject = cp.isFreeFormObject; + this.isAnyType = cp.isAnyType; + this.isArray = cp.isArray; + this.isMap = cp.isMap; + this.isFile = cp.isFile; + this.isEnum = cp.isEnum; + this._enum = cp._enum; + this.allowableValues = cp.allowableValues; + this.items = cp.items; + this.additionalProperties = cp.additionalProperties; + this.vars = cp.vars; + this.requiredVars = cp.requiredVars; + this.mostInnerItems = cp.mostInnerItems; + this.vendorExtensions = cp.vendorExtensions; + this.hasValidation = cp.hasValidation; + this.isNullable = cp.isNullable; + this.required = cp.required; + this.maximum = cp.maximum; + this.exclusiveMaximum = cp.exclusiveMaximum; + this.minimum = cp.minimum; + this.exclusiveMinimum = cp.exclusiveMinimum; + this.maxLength = cp.maxLength; + this.minLength = cp.minLength; + this.pattern = cp.pattern; + this.maxItems = cp.maxItems; + this.minItems = cp.minItems; + this.uniqueItems = cp.uniqueItems; + this.multipleOf = cp.multipleOf; + this.setHasVars(cp.getHasVars()); + this.setHasRequired(cp.getHasRequired()); + this.setMaxProperties(cp.getMaxProperties()); + this.setMinProperties(cp.getMinProperties()); + setReadOnlyVars(); + } + + @Override + public ExtendedCodegenParameter copy() { + CodegenParameter superCopy = super.copy(); + ExtendedCodegenParameter output = new ExtendedCodegenParameter(superCopy); + output.dataTypeAlternate = this.dataTypeAlternate; + output.isUniqueId = this.isUniqueId; + return output; + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + + if (this.getClass() != o.getClass()) + return false; + + boolean result = super.equals(o); + ExtendedCodegenParameter that = (ExtendedCodegenParameter) o; + return result && + isUniqueId == that.isUniqueId && + Objects.equals(dataTypeAlternate, that.dataTypeAlternate); + } + + @Override + public int hashCode() { + int superHash = super.hashCode(); + return Objects.hash(superHash, dataTypeAlternate, isUniqueId); + } + + @Override + public String toString() { + String superString = super.toString(); + final StringBuilder sb = new StringBuilder(superString); + sb.append(", isUniqueId=").append(isUniqueId); + sb.append(", dataTypeAlternate='").append(dataTypeAlternate).append('\''); + return sb.toString(); + } + + private void setReadOnlyVars() { + readOnlyVars = vars.stream().filter(v -> v.isReadOnly).collect(Collectors.toList()); + hasReadOnly = !readOnlyVars.isEmpty(); + } + } + + class ExtendedCodegenProperty extends CodegenProperty { + public String dataTypeAlternate; + public boolean isEntity; //Is a model containing an "id" property marked as isUniqueId and which matches the 'x-entityId' value. + public boolean isUniqueId; // The property represents a unique id (x-isUniqueId: true) + public boolean keepAsJSObject; + public boolean isReservedRecordField; + + public boolean itemsAreUniqueId() { + return TypeScriptPlaywrightClientCodegen.itemsAreUniqueId(this.items); + } + + public boolean itemsAreNullable() { + return TypeScriptPlaywrightClientCodegen.itemsAreNullable(this.items); + } + + public String getItemsDataType() { + return TypeScriptPlaywrightClientCodegen.getItemsDataType(this.items); + } + + public boolean isDateType() { + return isDate && "Date".equals(dataType); + } + + public boolean isDateTimeType() { + return isDateTime && "Date".equals(dataType); + } + + public ExtendedCodegenProperty(CodegenProperty cp) { + super(); + + this.openApiType = cp.openApiType; + this.baseName = cp.baseName; + this.complexType = cp.complexType; + this.getter = cp.getter; + this.setter = cp.setter; + this.description = cp.description; + this.dataType = cp.dataType; + this.datatypeWithEnum = cp.datatypeWithEnum; + this.dataFormat = cp.dataFormat; + this.name = cp.name; + this.min = cp.min; + this.max = cp.max; + this.defaultValue = cp.defaultValue; + this.defaultValueWithParam = cp.defaultValueWithParam; + this.baseType = cp.baseType; + this.containerType = cp.containerType; + this.title = cp.title; + this.unescapedDescription = cp.unescapedDescription; + this.maxLength = cp.maxLength; + this.minLength = cp.minLength; + this.pattern = cp.pattern; + this.example = cp.example; + this.jsonSchema = cp.jsonSchema; + this.minimum = cp.minimum; + this.maximum = cp.maximum; + this.multipleOf = cp.multipleOf; + this.exclusiveMinimum = cp.exclusiveMinimum; + this.exclusiveMaximum = cp.exclusiveMaximum; + this.required = cp.required; + this.deprecated = cp.deprecated; + this.hasMoreNonReadOnly = cp.hasMoreNonReadOnly; + this.isPrimitiveType = cp.isPrimitiveType; + this.isModel = cp.isModel; + this.isContainer = cp.isContainer; + this.isString = cp.isString; + this.isNumeric = cp.isNumeric; + this.isInteger = cp.isInteger; + this.isLong = cp.isLong; + this.isNumber = cp.isNumber; + this.isFloat = cp.isFloat; + this.isDouble = cp.isDouble; + this.isDecimal = cp.isDecimal; + this.isByteArray = cp.isByteArray; + this.isBinary = cp.isBinary; + this.isFile = cp.isFile; + this.isBoolean = cp.isBoolean; + this.isDate = cp.isDate; // full-date notation as defined by RFC 3339, section 5.6, for example, 2017-07-21 + this.isDateTime = cp.isDateTime; // the date-time notation as defined by RFC 3339, section 5.6, for example, 2017-07-21T17:32:28Z + this.isUuid = cp.isUuid; + this.isUri = cp.isUri; + this.isEmail = cp.isEmail; + this.isFreeFormObject = cp.isFreeFormObject; + this.isAnyType = cp.isAnyType; + this.isArray = cp.isArray; + this.isMap = cp.isMap; + this.isEnum = cp.isEnum; + this.isReadOnly = cp.isReadOnly; + this.isWriteOnly = cp.isWriteOnly; + this.isNullable = cp.isNullable; + this.isSelfReference = cp.isSelfReference; + this.isCircularReference = cp.isCircularReference; + this.isDiscriminator = cp.isDiscriminator; + this._enum = cp._enum; + this.allowableValues = cp.allowableValues; + this.items = cp.items; + this.additionalProperties = cp.additionalProperties; + this.vars = cp.vars; + this.requiredVars = cp.requiredVars; + this.mostInnerItems = cp.mostInnerItems; + this.vendorExtensions = cp.vendorExtensions; + this.hasValidation = cp.hasValidation; + this.isInherited = cp.isInherited; + this.discriminatorValue = cp.discriminatorValue; + this.nameInLowerCase = cp.nameInLowerCase; + this.nameInPascalCase = cp.nameInPascalCase; + this.nameInSnakeCase = cp.nameInSnakeCase; + this.enumName = cp.enumName; + this.maxItems = cp.maxItems; + this.minItems = cp.minItems; + this.setMaxProperties(cp.getMaxProperties()); + this.setMinProperties(cp.getMinProperties()); + this.setUniqueItems(cp.getUniqueItems()); + this.isXmlAttribute = cp.isXmlAttribute; + this.xmlPrefix = cp.xmlPrefix; + this.xmlName = cp.xmlName; + this.xmlNamespace = cp.xmlNamespace; + this.isXmlWrapped = cp.isXmlWrapped; + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + + if (this.getClass() != o.getClass()) + return false; + + boolean result = super.equals(o); + ExtendedCodegenProperty that = (ExtendedCodegenProperty) o; + return result && + isEntity == that.isEntity && + isUniqueId == that.isUniqueId && + keepAsJSObject == that.keepAsJSObject && + isReservedRecordField == that.isReservedRecordField && + Objects.equals(dataTypeAlternate, that.dataTypeAlternate); + } + + @Override + public int hashCode() { + int superHash = super.hashCode(); + return Objects.hash(superHash, dataTypeAlternate, isEntity, isUniqueId, keepAsJSObject, isReservedRecordField); + } + + @Override + public String toString() { + String superString = super.toString(); + final StringBuilder sb = new StringBuilder(superString); + sb.append(", dataTypeAlternate='").append(dataTypeAlternate).append('\''); + sb.append(", isEntity=").append(isEntity); + sb.append(", isUniqueId=").append(isUniqueId); + sb.append(", keepAsJSObject=").append(keepAsJSObject); + sb.append(", isReservedRecordField=").append(isReservedRecordField); + return sb.toString(); + } + } + + class ExtendedCodegenOperation extends CodegenOperation { + boolean hasReturnPassthroughVoid, returnTypeSupportsEntities, returnTypeIsModel, returnTypeIsArray; + String returnTypeAlternate, returnBaseTypeAlternate, returnPassthrough; + + public ExtendedCodegenOperation(CodegenOperation o) { + super(); + + this.responseHeaders.addAll(o.responseHeaders); + this.hasAuthMethods = o.hasAuthMethods; + this.hasConsumes = o.hasConsumes; + this.hasProduces = o.hasProduces; + this.hasParams = o.hasParams; + this.hasOptionalParams = o.hasOptionalParams; + this.hasRequiredParams = o.hasRequiredParams; + this.returnTypeIsPrimitive = o.returnTypeIsPrimitive; + this.returnSimpleType = o.returnSimpleType; + this.subresourceOperation = o.subresourceOperation; + this.isMap = o.isMap; + this.isArray = o.isArray; + this.isMultipart = o.isMultipart; + this.isResponseBinary = o.isResponseBinary; + this.isResponseFile = o.isResponseFile; + this.isResponseOptional = o.isResponseOptional; + this.hasReference = o.hasReference; + this.isRestfulIndex = o.isRestfulIndex; + this.isRestfulShow = o.isRestfulShow; + this.isRestfulCreate = o.isRestfulCreate; + this.isRestfulUpdate = o.isRestfulUpdate; + this.isRestfulDestroy = o.isRestfulDestroy; + this.isRestful = o.isRestful; + this.isDeprecated = o.isDeprecated; + this.isCallbackRequest = o.isCallbackRequest; + this.uniqueItems = o.uniqueItems; + this.path = o.path; + this.operationId = o.operationId; + this.returnType = o.returnType; + this.returnFormat = o.returnFormat; + this.httpMethod = o.httpMethod; + this.returnBaseType = o.returnBaseType; + this.returnContainer = o.returnContainer; + this.summary = o.summary; + this.unescapedNotes = o.unescapedNotes; + this.notes = o.notes; + this.baseName = o.baseName; + this.defaultResponse = o.defaultResponse; + this.discriminator = o.discriminator; + this.consumes = o.consumes; + this.produces = o.produces; + this.prioritizedContentTypes = o.prioritizedContentTypes; + this.servers = o.servers; + this.bodyParam = o.bodyParam; + this.allParams = o.allParams; + this.bodyParams = o.bodyParams; + this.pathParams = o.pathParams; + this.queryParams = o.queryParams; + this.headerParams = o.headerParams; + this.formParams = o.formParams; + this.cookieParams = o.cookieParams; + this.requiredParams = o.requiredParams; + this.optionalParams = o.optionalParams; + this.authMethods = o.authMethods; + this.tags = o.tags; + this.responses = o.responses; + this.callbacks = o.callbacks; + this.imports = o.imports; + this.examples = o.examples; + this.requestBodyExamples = o.requestBodyExamples; + this.externalDocs = o.externalDocs; + this.vendorExtensions = o.vendorExtensions; + this.nickname = o.nickname; + this.operationIdOriginal = o.operationIdOriginal; + this.operationIdLowerCase = o.operationIdLowerCase; + this.operationIdCamelCase = o.operationIdCamelCase; + this.operationIdSnakeCase = o.operationIdSnakeCase; + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + + if (this.getClass() != o.getClass()) + return false; + + boolean result = super.equals(o); + ExtendedCodegenOperation that = (ExtendedCodegenOperation) o; + return result && + hasReturnPassthroughVoid == that.hasReturnPassthroughVoid && + returnTypeSupportsEntities == that.returnTypeSupportsEntities && + returnTypeIsArray == that.returnTypeIsArray && + returnTypeIsModel == that.returnTypeIsModel && + Objects.equals(returnTypeAlternate, that.returnTypeAlternate) && + Objects.equals(returnBaseTypeAlternate, that.returnBaseTypeAlternate) && + Objects.equals(returnPassthrough, that.returnPassthrough); + } + + @Override + public int hashCode() { + int superHash = super.hashCode(); + return Objects.hash(superHash, returnPassthrough, hasReturnPassthroughVoid, returnTypeSupportsEntities, returnTypeIsArray, returnTypeIsModel, returnTypeAlternate, returnBaseTypeAlternate); + } + + @Override + public String toString() { + String superString = super.toString(); + final StringBuilder sb = new StringBuilder(superString); + sb.append(", hasReturnPassthroughVoid=").append(hasReturnPassthroughVoid); + sb.append(", returnTypeSupportsEntities=").append(returnTypeSupportsEntities); + sb.append(", returnTypeIsArray=").append(returnTypeIsArray); + sb.append(", returnTypeIsModel=").append(returnTypeIsModel); + sb.append(", returnTypeAlternate='").append(returnTypeAlternate).append('\''); + sb.append(", returnBaseTypeAlternate='").append(returnBaseTypeAlternate).append('\''); + sb.append(", returnPassthrough='").append(returnPassthrough).append('\''); + return sb.toString(); + } + } + + class ExtendedCodegenModel extends CodegenModel { + @Getter + @Setter + public Set modelImports = new TreeSet(); + public boolean isEntity; // Is a model containing an "id" property marked as isUniqueId + public String returnPassthrough; + public boolean hasReturnPassthroughVoid; + + public boolean isDateType() { + return isDate && "Date".equals(dataType); + } + + public boolean isDateTimeType() { + return isDateTime && "Date".equals(dataType); + } + + public ExtendedCodegenModel(CodegenModel cm) { + super(); + + this.parent = cm.parent; + this.parentSchema = cm.parentSchema; + this.interfaces = cm.interfaces; + this.allParents = cm.allParents; + this.parentModel = cm.parentModel; + this.interfaceModels = cm.interfaceModels; + this.children = cm.children; + this.anyOf = cm.anyOf; + this.oneOf = cm.oneOf; + this.allOf = cm.allOf; + this.name = cm.name; + this.classname = cm.classname; + this.title = cm.title; + this.description = cm.description; + this.classVarName = cm.classVarName; + this.modelJson = cm.modelJson; + this.dataType = cm.dataType; + this.xmlPrefix = cm.xmlPrefix; + this.xmlNamespace = cm.xmlNamespace; + this.xmlName = cm.xmlName; + this.classFilename = cm.classFilename; + this.unescapedDescription = cm.unescapedDescription; + this.discriminator = cm.discriminator; + this.defaultValue = cm.defaultValue; + this.arrayModelType = cm.arrayModelType; + this.isAlias = cm.isAlias; + this.isString = cm.isString; + this.isInteger = cm.isInteger; + this.isLong = cm.isLong; + this.isNumber = cm.isNumber; + this.isNumeric = cm.isNumeric; + this.isFloat = cm.isFloat; + this.isDouble = cm.isDouble; + this.isDate = cm.isDate; + this.isDateTime = cm.isDateTime; + this.vars = cm.vars; + this.allVars = cm.allVars; + this.requiredVars = cm.requiredVars; + this.optionalVars = cm.optionalVars; + this.hasReadOnly = cm.hasReadOnly; + this.readOnlyVars = cm.readOnlyVars; + this.readWriteVars = cm.readWriteVars; + this.parentVars = cm.parentVars; + this.allowableValues = cm.allowableValues; + this.mandatory = cm.mandatory; + this.allMandatory = cm.allMandatory; + this.imports = cm.imports; + this.hasVars = cm.hasVars; + this.emptyVars = cm.emptyVars; + this.hasMoreModels = cm.hasMoreModels; + this.hasEnums = cm.hasEnums; + this.isEnum = cm.isEnum; + this.isNullable = cm.isNullable; + this.hasRequired = cm.hasRequired; + this.hasOptional = cm.hasOptional; + this.isArray = cm.isArray; + this.hasChildren = cm.hasChildren; + this.isMap = cm.isMap; + this.isDeprecated = cm.isDeprecated; + this.hasOnlyReadOnly = cm.hasOnlyReadOnly; + this.externalDocumentation = cm.externalDocumentation; + + this.vendorExtensions = cm.vendorExtensions; + this.additionalPropertiesType = cm.additionalPropertiesType; + this.isAdditionalPropertiesTrue = cm.isAdditionalPropertiesTrue; + this.setMaxProperties(cm.getMaxProperties()); + this.setMinProperties(cm.getMinProperties()); + this.setUniqueItems(cm.getUniqueItems()); + this.setMaxItems(cm.getMaxItems()); + this.setMinItems(cm.getMinItems()); + this.setMaxLength(cm.getMaxLength()); + this.setMinLength(cm.getMinLength()); + this.setExclusiveMinimum(cm.getExclusiveMinimum()); + this.setExclusiveMaximum(cm.getExclusiveMaximum()); + this.setMinimum(cm.getMinimum()); + this.setMaximum(cm.getMaximum()); + this.setPattern(cm.getPattern()); + this.setMultipleOf(cm.getMultipleOf()); + this.setItems(cm.getItems()); + this.setAdditionalProperties(cm.getAdditionalProperties()); + this.setIsModel(cm.getIsModel()); + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + + if (this.getClass() != o.getClass()) + return false; + + boolean result = super.equals(o); + ExtendedCodegenModel that = (ExtendedCodegenModel) o; + return result && + isEntity == that.isEntity && + hasReturnPassthroughVoid == that.hasReturnPassthroughVoid && + Objects.equals(returnPassthrough, that.returnPassthrough) && + Objects.equals(modelImports, that.modelImports); + + } + + @Override + public int hashCode() { + int superHash = super.hashCode(); + return Objects.hash(superHash, isEntity, returnPassthrough, hasReturnPassthroughVoid, getModelImports()); + } + + @Override + public String toString() { + String superString = super.toString(); + final StringBuilder sb = new StringBuilder(superString); + sb.append(", modelImports=").append(modelImports); + sb.append(", isEntity=").append(isEntity); + sb.append(", returnPassthrough='").append(returnPassthrough).append('\''); + sb.append(", hasReturnPassthroughVoid=").append(hasReturnPassthroughVoid); + return sb.toString(); + } + } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index ba2d139af98..43ee5053591 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -146,6 +146,7 @@ org.openapitools.codegen.languages.TypeScriptNestjsClientCodegen org.openapitools.codegen.languages.TypeScriptNodeClientCodegen org.openapitools.codegen.languages.TypeScriptReduxQueryClientCodegen org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen +org.openapitools.codegen.languages.TypeScriptPlaywrightClientCodegen org.openapitools.codegen.languages.WsdlSchemaCodegen org.openapitools.codegen.languages.XojoClientCodegen org.openapitools.codegen.languages.ZapierClientCodegen diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesRecord.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesRecord.mustache new file mode 100644 index 00000000000..d0b6c4cb269 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesRecord.mustache @@ -0,0 +1,26 @@ +import {Map, Record, RecordOf} from 'immutable'; + +import { +{{#models}} +{{#model}} +{{#isEntity}} + {{classname}}RecordEntity, +{{/isEntity}} +{{/model}} +{{/models}} +} from "./models/index{{importFileExtension}}" + +export const ApiEntitiesRecordProps = { + recType: "ApiEntitiesRecord" as "ApiEntitiesRecord", +{{#models}} +{{#model}} +{{#isEntity}} + {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}: ({{classname}}RecordEntity(), Map()), +{{/isEntity}} +{{/model}} +{{/models}} +}; + +export type ApiEntitiesRecordPropsType = typeof ApiEntitiesRecordProps; +export const ApiEntitiesRecord = Record(ApiEntitiesRecordProps, ApiEntitiesRecordProps.recType); +export type ApiEntitiesRecord = RecordOf; diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesReducer.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesReducer.mustache new file mode 100644 index 00000000000..781fc5129f0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesReducer.mustache @@ -0,0 +1,21 @@ +import {ApiEntitiesRecord} from "./ApiEntitiesRecord{{importFileExtension}}"; +import {ReducerBuilder} from "redux-ts-simple"; +import {normalizedEntities} from "./runtimeSagasAndRecords{{importFileExtension}}"; + +export const ApiEntitiesReducer = new ReducerBuilder(ApiEntitiesRecord()) + .on(normalizedEntities, (state, action): ApiEntitiesRecord => { + const {entities} = action.payload; + return state.withMutations(mutableState => { + for (const entityKey in entities) { + const entityMap = entities[entityKey]; + const currentEntityMap = mutableState.get(entityKey as any); + if (currentEntityMap) { + let mergedEntityMap = currentEntityMap.mergeDeep(entityMap); + if (!mergedEntityMap.equals(currentEntityMap)) { + mutableState.set(entityKey as any, mergedEntityMap); + } + } + } + }); + }) + .build(); diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesSelectors.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesSelectors.mustache new file mode 100644 index 00000000000..9eaffec0dfe --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/ApiEntitiesSelectors.mustache @@ -0,0 +1,5 @@ +export let getApiEntitiesState: (state: any) => any = (state: any) => state.app.apiEntities; + +export function setApiEntitiesStateGetter(getter: (state: any) => any) { // Use this to customize the location where you have placed your ApiEntitiesRecord in your project. + getApiEntitiesState = getter; +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/README.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/README.mustache new file mode 100644 index 00000000000..33dc3af27db --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/README.mustache @@ -0,0 +1,46 @@ +## {{npmName}}@{{npmVersion}} + +This generator creates TypeScript/JavaScript client that utilizes [Fetch API](https://fetch.spec.whatwg.org/). The generated Node module can be used in the following environments: + +Environment +* Node.js +* Webpack +* Browserify + +Language level +* ES5 - you must have a Promises/A+ library installed +* ES6 + +Module system +* CommonJS +* ES6 module system + +It can be used in both TypeScript and JavaScript. In TypeScript, the definition will be automatically resolved via `package.json`. ([Reference](https://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)) + +### Building + +To build and compile the typescript sources to javascript use: +``` +npm install +npm run build +``` + +### Publishing + +First build the package then run `npm publish` + +### Consuming + +navigate to the folder of your consuming project and run one of the following commands. + +_published:_ + +``` +npm install {{npmName}}@{{npmVersion}} --save +``` + +_unPublished (not recommended):_ + +``` +npm install PATH_TO_GENERATED_PACKAGE --save +``` diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/allSagas.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/allSagas.mustache new file mode 100644 index 00000000000..b756ab47c2c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/allSagas.mustache @@ -0,0 +1,19 @@ +import {all, fork} from "redux-saga/effects"; + +import { +{{#apiInfo}} +{{#apis}} + {{#lambda.camelcase}}{{classFilename}}{{/lambda.camelcase}}AllSagas, +{{/apis}} +{{/apiInfo}} +} from "./index{{importFileExtension}}"; + +export function *allApiSagas() { + yield all([ +{{#apiInfo}} +{{#apis}} + fork({{#lambda.camelcase}}{{classFilename}}{{/lambda.camelcase}}AllSagas), +{{/apis}} +{{/apiInfo}} + ]); +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/apis.index.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/apis.index.mustache new file mode 100644 index 00000000000..98afdba7a5b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/apis.index.mustache @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +{{#useSagaAndRecords}} +export * from './SagaApiManager{{importFileExtension}}' +export * from './allSagas{{importFileExtension}}' +{{/useSagaAndRecords}} +{{#apiInfo}} +{{#apis}} +{{#operations}} +export * from './{{ classFilename }}{{importFileExtension}}'; +{{#useSagaAndRecords}} +export * from './{{{ classFilename }}}Sagas{{importFileExtension}}'; +{{/useSagaAndRecords}} +{{/operations}} +{{/apis}} +{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/apis.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/apis.mustache new file mode 100644 index 00000000000..83b36941422 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/apis.mustache @@ -0,0 +1,446 @@ +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} + +import * as runtime from '../runtime{{importFileExtension}}'; +{{#imports.0}} +import type { + {{#imports}} + {{className}}, + {{/imports}} +} from '../models/index{{importFileExtension}}'; +{{^withoutRuntimeChecks}} +import { + {{#imports}} + {{className}}FromJSON, + {{className}}ToJSON, + {{/imports}} +} from '../models/index{{importFileExtension}}'; +{{/withoutRuntimeChecks}} +{{/imports.0}} + +{{#operations}} +{{#operation}} +{{#allParams.0}} +export interface {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request { +{{#allParams}} + {{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{#hasReadOnly}}Omit<{{{dataType}}}, {{#readOnlyVars}}'{{baseName}}'{{^-last}}|{{/-last}}{{/readOnlyVars}}>{{/hasReadOnly}}{{^hasReadOnly}}{{{dataType}}}{{/hasReadOnly}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}; +{{/allParams}} +} + +{{/allParams.0}} +{{/operation}} +{{/operations}} +{{#withInterfaces}} +{{#operations}} +/** + * {{classname}} - interface + * {{#lambda.indented_1}}{{{unescapedDescription}}}{{/lambda.indented_1}} + * @export + * @interface {{classname}}Interface + */ +export interface {{classname}}Interface { +{{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option. + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + * @throws {RequiredError} + * @memberof {{classname}}Interface + */ + {{nickname}}Raw({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + */ + {{^useSingleRequestParameter}} + {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{^returnType}}void{{/returnType}}>; + {{/useSingleRequestParameter}} + {{#useSingleRequestParameter}} + {{nickname}}({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{^returnType}}void{{/returnType}}>; + {{/useSingleRequestParameter}} + +{{/operation}} +} + +{{/operations}} +{{/withInterfaces}} +{{#operations}} +/** + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} + */ +{{#withInterfaces}} +export class {{classname}} extends runtime.BaseAPI implements {{classname}}Interface { +{{/withInterfaces}} +{{^withInterfaces}} +export class {{classname}} extends runtime.BaseAPI { +{{/withInterfaces}} + + {{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + */ + async {{nickname}}Raw({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + {{#allParams}} + {{#required}} + if (requestParameters['{{paramName}}'] == null) { + throw new runtime.RequiredError( + '{{paramName}}', + 'Required parameter "{{paramName}}" was null or undefined when calling {{nickname}}().' + ); + } + + {{/required}} + {{/allParams}} + const queryParameters: any = {}; + + {{#queryParams}} + {{#isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isCollectionFormatMulti}} + queryParameters['{{baseName}}'] = requestParameters['{{paramName}}']; + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + queryParameters['{{baseName}}'] = {{#uniqueItems}}Array.from({{/uniqueItems}}requestParameters['{{paramName}}']{{#uniqueItems}}){{/uniqueItems}}!.join(runtime.COLLECTION_FORMATS["{{collectionFormat}}"]); + {{/isCollectionFormatMulti}} + } + + {{/isArray}} + {{^isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isDateTimeType}} + queryParameters['{{baseName}}'] = (requestParameters['{{paramName}}'] as any).toISOString(); + {{/isDateTimeType}} + {{^isDateTimeType}} + {{#isDateType}} + queryParameters['{{baseName}}'] = (requestParameters['{{paramName}}'] as any).toISOString().substring(0,10); + {{/isDateType}} + {{^isDateType}} + queryParameters['{{baseName}}'] = requestParameters['{{paramName}}']; + {{/isDateType}} + {{/isDateTimeType}} + } + + {{/isArray}} + {{/queryParams}} + const headerParameters: runtime.HTTPHeaders = {}; + + {{#bodyParam}} + {{^consumes}} + headerParameters['Content-Type'] = 'application/json'; + + {{/consumes}} + {{#consumes.0}} + headerParameters['Content-Type'] = '{{{mediaType}}}'; + + {{/consumes.0}} + {{/bodyParam}} + {{#headerParams}} + {{#isArray}} + if (requestParameters['{{paramName}}'] != null) { + headerParameters['{{baseName}}'] = {{#uniqueItems}}Array.from({{/uniqueItems}}requestParameters['{{paramName}}']{{#uniqueItems}}){{/uniqueItems}}!.join(runtime.COLLECTION_FORMATS["{{collectionFormat}}"]); + } + + {{/isArray}} + {{^isArray}} + if (requestParameters['{{paramName}}'] != null) { + headerParameters['{{baseName}}'] = String(requestParameters['{{paramName}}']); + } + + {{/isArray}} + {{/headerParams}} + {{#authMethods}} + {{#isBasic}} + {{#isBasicBasic}} + if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) { + headerParameters["Authorization"] = "Basic " + btoa(this.configuration.username + ":" + this.configuration.password); + } + {{/isBasicBasic}} + {{#isBasicBearer}} + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}]); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + {{/isBasicBearer}} + {{/isBasic}} + {{#isApiKey}} + {{#isKeyInHeader}} + if (this.configuration && this.configuration.apiKey) { + headerParameters["{{keyParamName}}"] = await this.configuration.apiKey("{{keyParamName}}"); // {{name}} authentication + } + + {{/isKeyInHeader}} + {{#isKeyInQuery}} + if (this.configuration && this.configuration.apiKey) { + queryParameters["{{keyParamName}}"] = await this.configuration.apiKey("{{keyParamName}}"); // {{name}} authentication + } + + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isOAuth}} + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}]); + } + + {{/isOAuth}} + {{/authMethods}} + {{#hasFormParams}} + const consumes: runtime.Consume[] = [ + {{#consumes}} + { contentType: '{{{mediaType}}}' }, + {{/consumes}} + ]; + // @ts-ignore: canConsumeForm may be unused + const canConsumeForm = runtime.canConsumeForm(consumes); + + let formParams: { append(param: string, value: any): any }; + let useForm = false; + {{#formParams}} + {{#isFile}} + // use FormData to transmit files using content-type "multipart/form-data" + useForm = canConsumeForm; + {{/isFile}} + {{/formParams}} + if (useForm) { + formParams = new FormData(); + } else { + formParams = new URLSearchParams(); + } + + {{#formParams}} + {{#isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isCollectionFormatMulti}} + requestParameters['{{paramName}}'].forEach((element) => { + formParams.append('{{baseName}}{{#useSquareBracketsInArrayNames}}[]{{/useSquareBracketsInArrayNames}}', element as any); + }) + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + formParams.append('{{baseName}}{{#useSquareBracketsInArrayNames}}[]{{/useSquareBracketsInArrayNames}}', {{#uniqueItems}}Array.from({{/uniqueItems}}requestParameters['{{paramName}}']{{#uniqueItems}}){{/uniqueItems}}!.join(runtime.COLLECTION_FORMATS["{{collectionFormat}}"])); + {{/isCollectionFormatMulti}} + } + + {{/isArray}} + {{^isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isPrimitiveType}} + formParams.append('{{baseName}}', requestParameters['{{paramName}}'] as any); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{^withoutRuntimeChecks}} + formParams.append('{{baseName}}', new Blob([JSON.stringify({{{dataType}}}ToJSON(requestParameters['{{paramName}}']))], { type: "application/json", })); + {{/withoutRuntimeChecks}}{{#withoutRuntimeChecks}} + formParams.append('{{baseName}}', new Blob([JSON.stringify(requestParameters['{{paramName}}'])], { type: "application/json", })); + {{/withoutRuntimeChecks}} + {{/isPrimitiveType}} + } + + {{/isArray}} + {{/formParams}} + {{/hasFormParams}} + const response = await this.request({ + path: `{{{path}}}`{{#pathParams}}.replace(`{${"{{baseName}}"}}`, encodeURIComponent(String(requestParameters['{{paramName}}']))){{/pathParams}}, + method: '{{httpMethod}}', + headers: headerParameters, + query: queryParameters, + {{#hasBodyParam}} + {{#bodyParam}} + {{#isContainer}} + {{^withoutRuntimeChecks}} + body: requestParameters['{{paramName}}']{{#isArray}}{{#items}}{{^isPrimitiveType}}!.map({{datatype}}ToJSON){{/isPrimitiveType}}{{/items}}{{/isArray}}, + {{/withoutRuntimeChecks}} + {{#withoutRuntimeChecks}} + body: requestParameters['{{paramName}}'], + {{/withoutRuntimeChecks}} + {{/isContainer}} + {{^isContainer}} + {{^isPrimitiveType}} + {{^withoutRuntimeChecks}} + body: {{dataType}}ToJSON(requestParameters['{{paramName}}']), + {{/withoutRuntimeChecks}} + {{#withoutRuntimeChecks}} + body: requestParameters['{{paramName}}'], + {{/withoutRuntimeChecks}} + {{/isPrimitiveType}} + {{#isPrimitiveType}} + body: requestParameters['{{paramName}}'] as any, + {{/isPrimitiveType}} + {{/isContainer}} + {{/bodyParam}} + {{/hasBodyParam}} + {{#hasFormParams}} + body: formParams, + {{/hasFormParams}} + }, initOverrides); + + {{#returnType}} + {{#isResponseFile}} + return new runtime.BlobApiResponse(response); + {{/isResponseFile}} + {{^isResponseFile}} + {{#returnTypeIsPrimitive}} + {{#isMap}} + return new runtime.JSONApiResponse(response); + {{/isMap}} + {{#isArray}} + return new runtime.JSONApiResponse(response); + {{/isArray}} + {{#returnSimpleType}} + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse<{{returnType}}>(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + {{/returnSimpleType}} + {{/returnTypeIsPrimitive}} + {{^returnTypeIsPrimitive}} + {{#isArray}} + return new runtime.JSONApiResponse(response{{^withoutRuntimeChecks}}, (jsonValue) => {{#uniqueItems}}new Set({{/uniqueItems}}jsonValue.map({{returnBaseType}}FromJSON){{/withoutRuntimeChecks}}){{#uniqueItems}}){{/uniqueItems}}; + {{/isArray}} + {{^isArray}} + {{#isMap}} + return new runtime.JSONApiResponse(response{{^withoutRuntimeChecks}}, (jsonValue) => runtime.mapValues(jsonValue, {{returnBaseType}}FromJSON){{/withoutRuntimeChecks}}); + {{/isMap}} + {{^isMap}} + return new runtime.JSONApiResponse(response{{^withoutRuntimeChecks}}, (jsonValue) => {{returnBaseType}}FromJSON(jsonValue){{/withoutRuntimeChecks}}); + {{/isMap}} + {{/isArray}} + {{/returnTypeIsPrimitive}} + {{/isResponseFile}} + {{/returnType}} + {{^returnType}} + return new runtime.VoidApiResponse(response); + {{/returnType}} + } + + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + */ + {{^useSingleRequestParameter}} + async {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{#returnType}}{{#isResponseOptional}} | null | undefined {{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{#returnType}} + const response = await this.{{nickname}}Raw({{#allParams.0}}{ {{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}} }, {{/allParams.0}}initOverrides); + {{#isResponseOptional}} + switch (response.raw.status) { + {{#responses}} + {{#is2xx}} + case {{code}}: + return {{#dataType}}await response.value(){{/dataType}}{{^dataType}}null{{/dataType}}; + {{/is2xx}} + {{/responses}} + default: + return await response.value(); + } + {{/isResponseOptional}} + {{^isResponseOptional}} + return await response.value(); + {{/isResponseOptional}} + {{/returnType}} + {{^returnType}} + await this.{{nickname}}Raw({{#allParams.0}}{ {{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}} }, {{/allParams.0}}initOverrides); + {{/returnType}} + } + {{/useSingleRequestParameter}} + {{#useSingleRequestParameter}} + async {{nickname}}({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request{{^hasRequiredParams}} = {}{{/hasRequiredParams}}, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{#returnType}}{{#isResponseOptional}} | null | undefined {{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{#returnType}} + const response = await this.{{nickname}}Raw({{#allParams.0}}requestParameters, {{/allParams.0}}initOverrides); + {{#isResponseOptional}} + switch (response.raw.status) { + {{#responses}} + {{#is2xx}} + case {{code}}: + return {{#dataType}}await response.value(){{/dataType}}{{^dataType}}null{{/dataType}}; + {{/is2xx}} + {{/responses}} + default: + return await response.value(); + } + {{/isResponseOptional}} + {{^isResponseOptional}} + return await response.value(); + {{/isResponseOptional}} + {{/returnType}} + {{^returnType}} + await this.{{nickname}}Raw({{#allParams.0}}requestParameters, {{/allParams.0}}initOverrides); + {{/returnType}} + } + {{/useSingleRequestParameter}} + + {{/operation}} +} +{{/operations}} +{{#hasEnums}} + +{{#operations}} +{{#operation}} +{{#allParams}} +{{#isEnum}} +{{#stringEnums}} +/** + * @export + * @enum {string} + */ +export enum {{operationIdCamelCase}}{{enumName}} { +{{#allowableValues}} + {{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} +{{/stringEnums}} +{{^stringEnums}} +/** + * @export + */ +export const {{operationIdCamelCase}}{{enumName}} = { +{{#allowableValues}} + {{#enumVars}} + {{{name}}}: {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} as const; +export type {{operationIdCamelCase}}{{enumName}} = typeof {{operationIdCamelCase}}{{enumName}}[keyof typeof {{operationIdCamelCase}}{{enumName}}]; +{{/stringEnums}} +{{/isEnum}} +{{/allParams}} +{{/operation}} +{{/operations}} +{{/hasEnums}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/gitignore b/modules/openapi-generator/src/main/resources/typescript-playwright/gitignore new file mode 100644 index 00000000000..149b5765472 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/index.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/index.mustache new file mode 100644 index 00000000000..cd7c8db6e66 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/index.mustache @@ -0,0 +1,17 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime{{importFileExtension}}'; +{{#useSagaAndRecords}} +export * from './runtimeSagasAndRecords{{importFileExtension}}'; +export * from './ApiEntitiesRecord{{importFileExtension}}'; +export * from './ApiEntitiesReducer{{importFileExtension}}'; +export * from './ApiEntitiesSelectors{{importFileExtension}}'; +{{/useSagaAndRecords}} +{{#apiInfo}} +{{#apis.0}} +export * from './apis/index{{importFileExtension}}'; +{{/apis.0}} +{{/apiInfo}} +{{#models.0}} +export * from './models/index{{importFileExtension}}'; +{{/models.0}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/licenseInfo.mustache new file mode 100644 index 00000000000..be193d0c4fe --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/licenseInfo.mustache @@ -0,0 +1,11 @@ +/** + * {{{appName}}} + * {{{appDescription}}} + * + * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} + * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/modelEnum.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/modelEnum.mustache new file mode 100644 index 00000000000..20f21ef5c9f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/modelEnum.mustache @@ -0,0 +1,24 @@ +{{>modelEnumInterfaces}} + +export function instanceOf{{classname}}(value: any): boolean { + for (const key in {{classname}}) { + if (Object.prototype.hasOwnProperty.call({{classname}}, key)) { + if (({{classname}} as Record)[key] === value) { + return true; + } + } + } + return false; +} + +export function {{classname}}FromJSON(json: any): {{classname}} { + return {{classname}}FromJSONTyped(json, false); +} + +export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boolean): {{classname}} { + return json as {{classname}}; +} + +export function {{classname}}ToJSON(value?: {{classname}} | null): any { + return value as any; +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/modelEnumInterfaces.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/modelEnumInterfaces.mustache new file mode 100644 index 00000000000..5e5ea85ca30 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/modelEnumInterfaces.mustache @@ -0,0 +1,27 @@ +{{#stringEnums}} +/** + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} + * @export + * @enum {string} + */ +export enum {{classname}} { +{{#allowableValues}} +{{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} +{{/enumVars}} +{{/allowableValues}} +} +{{/stringEnums}}{{^stringEnums}} +/** + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} + * @export + */ +export const {{classname}} = { +{{#allowableValues}} +{{#enumVars}} + {{{name}}}: {{{value}}}{{^-last}},{{/-last}} +{{/enumVars}} +{{/allowableValues}} +} as const; +export type {{classname}} = typeof {{classname}}[keyof typeof {{classname}}]; +{{/stringEnums}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/modelGeneric.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/modelGeneric.mustache new file mode 100644 index 00000000000..0228aa5374e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/modelGeneric.mustache @@ -0,0 +1,160 @@ +import { mapValues } from '../runtime{{importFileExtension}}'; +{{#hasImports}} +{{#tsImports}} +import type { {{{classname}}} } from './{{filename}}{{importFileExtension}}'; +import { + {{classname}}FromJSON, + {{classname}}FromJSONTyped, + {{classname}}ToJSON, +} from './{{filename}}{{importFileExtension}}'; +{{/tsImports}} + +{{/hasImports}} +{{#discriminator}} +{{#discriminator.mappedModels}} +import { {{modelName}}FromJSONTyped } from './{{modelName}}{{importFileExtension}}'; +{{/discriminator.mappedModels}} +{{/discriminator}} +{{>modelGenericInterfaces}} + +/** + * Check if a given object implements the {{classname}} interface. + */ +export function instanceOf{{classname}}(value: object): value is {{classname}} { + {{#vars}} + {{#required}} + if (!('{{name}}' in value) || value['{{name}}'] === undefined) return false; + {{/required}} + {{/vars}} + return true; +} + +export function {{classname}}FromJSON(json: any): {{classname}} { + return {{classname}}FromJSONTyped(json, false); +} + +export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boolean): {{classname}} { + {{#hasVars}} + if (json == null) { + return json; + } +{{#discriminator}} + if (!ignoreDiscriminator) { +{{#discriminator.mappedModels}} + if (json['{{discriminator.propertyBaseName}}'] === '{{mappingName}}') { + return {{modelName}}FromJSONTyped(json, true); + } +{{/discriminator.mappedModels}} + } +{{/discriminator}} + return { + {{#parent}}...{{{.}}}FromJSONTyped(json, ignoreDiscriminator),{{/parent}} + {{#additionalPropertiesType}} + ...json, + {{/additionalPropertiesType}} + {{#vars}} + {{#isPrimitiveType}} + {{#isDateType}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}new Date(json['{{baseName}}'])), + {{/isDateType}} + {{#isDateTimeType}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}new Date(json['{{baseName}}'])), + {{/isDateTimeType}} + {{^isDateType}} + {{^isDateTimeType}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}json['{{baseName}}'], + {{/isDateTimeType}} + {{/isDateType}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isArray}} + {{#uniqueItems}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}new Set((json['{{baseName}}'] as Array).map({{#items}}{{datatype}}{{/items}}FromJSON))), + {{/uniqueItems}} + {{^uniqueItems}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}(json['{{baseName}}'] as Array).map({{#items}}{{datatype}}{{/items}}FromJSON)), + {{/uniqueItems}} + {{/isArray}} + {{#isMap}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}mapValues(json['{{baseName}}'], {{#items}}{{datatype}}{{/items}}FromJSON)), + {{/isMap}} + {{^isArray}} + {{^isMap}} + {{^isFreeFormObject}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}{{datatype}}FromJSON(json['{{baseName}}']), + {{/isFreeFormObject}} + {{#isFreeFormObject}} + '{{name}}': {{^required}}json['{{baseName}}'] == null ? undefined : {{/required}}json['{{baseName}}'], + {{/isFreeFormObject}} + {{/isMap}} + {{/isArray}} + {{/isPrimitiveType}} + {{/vars}} + }; + {{/hasVars}} + {{^hasVars}} + return json; + {{/hasVars}} +} + +export function {{classname}}ToJSON(value?: {{#hasReadOnly}}Omit<{{classname}}, {{#readOnlyVars}}'{{baseName}}'{{^-last}}|{{/-last}}{{/readOnlyVars}}>{{/hasReadOnly}}{{^hasReadOnly}}{{classname}}{{/hasReadOnly}} | null): any { + {{#hasVars}} + if (value == null) { + return value; + } + return { + {{#parent}}...{{{.}}}ToJSON(value),{{/parent}} + {{#additionalPropertiesType}} + ...value, + {{/additionalPropertiesType}} + {{#vars}} + {{^isReadOnly}} + {{#isPrimitiveType}} + {{#isDateType}} + '{{baseName}}': {{^required}}value['{{name}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}(value['{{name}}']{{#isNullable}} as any{{/isNullable}}).toISOString().substring(0,10)), + {{/isDateType}} + {{#isDateTimeType}} + '{{baseName}}': {{^required}}value['{{name}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}(value['{{name}}']{{#isNullable}} as any{{/isNullable}}).toISOString()), + {{/isDateTimeType}} + {{#isArray}} + '{{baseName}}': {{#uniqueItems}}{{^required}}value['{{name}}'] == null ? undefined : {{/required}}{{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}Array.from(value['{{name}}'] as Set){{/uniqueItems}}{{^uniqueItems}}value['{{name}}']{{/uniqueItems}}, + {{/isArray}} + {{^isDateType}} + {{^isDateTimeType}} + {{^isArray}} + '{{baseName}}': value['{{name}}'], + {{/isArray}} + {{/isDateTimeType}} + {{/isDateType}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isArray}} + {{#uniqueItems}} + '{{baseName}}': {{^required}}value['{{name}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}Array.from(value['{{name}}'] as Set).map({{#items}}{{datatype}}{{/items}}ToJSON)), + {{/uniqueItems}} + {{^uniqueItems}} + '{{baseName}}': {{^required}}value['{{name}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}(value['{{name}}'] as Array).map({{#items}}{{datatype}}{{/items}}ToJSON)), + {{/uniqueItems}} + {{/isArray}} + {{#isMap}} + '{{baseName}}': {{^required}}value['{{name}}'] == null ? undefined : {{/required}}({{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}mapValues(value['{{name}}'], {{#items}}{{datatype}}{{/items}}ToJSON)), + {{/isMap}} + {{^isArray}} + {{^isMap}} + {{^isFreeFormObject}} + '{{baseName}}': {{datatype}}ToJSON(value['{{name}}']), + {{/isFreeFormObject}} + {{#isFreeFormObject}} + '{{baseName}}': value['{{name}}'], + {{/isFreeFormObject}} + {{/isMap}} + {{/isArray}} + {{/isPrimitiveType}} + {{/isReadOnly}} + {{/vars}} + }; + {{/hasVars}} + {{^hasVars}} + return value; + {{/hasVars}} +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/modelGenericInterfaces.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/modelGenericInterfaces.mustache new file mode 100644 index 00000000000..68e5747598a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/modelGenericInterfaces.mustache @@ -0,0 +1,50 @@ +/** + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} + * @export + * @interface {{classname}} + */ +export interface {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{ +{{#additionalPropertiesType}} + [key: string]: {{{additionalPropertiesType}}}{{#hasVars}} | any{{/hasVars}}; +{{/additionalPropertiesType}} +{{#vars}} + /** + * {{#lambda.indented_star_4}}{{{unescapedDescription}}}{{/lambda.indented_star_4}} + * @type {{=<% %>=}}{<%&datatype%>}<%={{ }}=%> + * @memberof {{classname}} + {{#deprecated}} + * @deprecated + {{/deprecated}} + */ + {{#isReadOnly}}readonly {{/isReadOnly}}{{name}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}; +{{/vars}} +}{{#hasEnums}} + +{{#vars}} +{{#isEnum}} +{{#stringEnums}} +/** +* @export +* @enum {string} +*/ +export enum {{classname}}{{enumName}} { +{{#allowableValues}} + {{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} +{{/stringEnums}}{{^stringEnums}} +/** + * @export + */ +export const {{classname}}{{enumName}} = { +{{#allowableValues}} + {{#enumVars}} + {{{name}}}: {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} as const; +export type {{classname}}{{enumName}} = typeof {{classname}}{{enumName}}[keyof typeof {{classname}}{{enumName}}]; +{{/stringEnums}} +{{/isEnum}}{{/vars}}{{/hasEnums}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/modelOneOf.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/modelOneOf.mustache new file mode 100644 index 00000000000..0e65dec85ea --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/modelOneOf.mustache @@ -0,0 +1,66 @@ +{{#hasImports}} +{{#oneOf}} +import type { {{{.}}} } from './{{.}}{{importFileExtension}}'; +import { + instanceOf{{{.}}}, + {{{.}}}FromJSON, + {{{.}}}FromJSONTyped, + {{{.}}}ToJSON, +} from './{{.}}{{importFileExtension}}'; +{{/oneOf}} + +{{/hasImports}} +{{>modelOneOfInterfaces}} + +export function {{classname}}FromJSON(json: any): {{classname}} { + return {{classname}}FromJSONTyped(json, false); +} + +export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boolean): {{classname}} { + if (json == null) { + return json; + } +{{#discriminator}} + switch (json['{{discriminator.propertyBaseName}}']) { +{{#discriminator.mappedModels}} + case '{{mappingName}}': + return Object.assign({}, {{modelName}}FromJSONTyped(json, true), { {{discriminator.propertyName}}: '{{mappingName}}' } as const); +{{/discriminator.mappedModels}} + default: + throw new Error(`No variant of {{classname}} exists with '{{discriminator.propertyName}}=${json['{{discriminator.propertyName}}']}'`); + } +{{/discriminator}} +{{^discriminator}} + {{#oneOf}} + if (instanceOf{{{.}}}(json)) { + return {{{.}}}FromJSONTyped(json, true); + } + {{/oneOf}} +{{/discriminator}} +} + +export function {{classname}}ToJSON(value?: {{classname}} | null): any { + if (value == null) { + return value; + } +{{#discriminator}} + switch (value['{{discriminator.propertyName}}']) { +{{#discriminator.mappedModels}} + case '{{mappingName}}': + return {{modelName}}ToJSON(value); +{{/discriminator.mappedModels}} + default: + throw new Error(`No variant of {{classname}} exists with '{{discriminator.propertyName}}=${value['{{discriminator.propertyName}}']}'`); + } +{{/discriminator}} + +{{^discriminator}} + {{#oneOf}} + if (instanceOf{{{.}}}(value)) { + return {{{.}}}ToJSON(value as {{{.}}}); + } + {{/oneOf}} + + return {}; +{{/discriminator}} +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/modelOneOfInterfaces.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/modelOneOfInterfaces.mustache new file mode 100644 index 00000000000..ee4d1fccd8e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/modelOneOfInterfaces.mustache @@ -0,0 +1,6 @@ +/** + * @type {{classname}} + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} + * @export + */ +export type {{classname}} = {{#discriminator}}{{#mappedModels}}{ {{discriminator.propertyName}}: '{{mappingName}}' } & {{modelName}}{{^-last}} | {{/-last}}{{/mappedModels}}{{/discriminator}}{{^discriminator}}{{#oneOf}}{{{.}}}{{^-last}} | {{/-last}}{{/oneOf}}{{/discriminator}}; \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/models.index.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/models.index.mustache new file mode 100644 index 00000000000..b79d33935bf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/models.index.mustache @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +{{#models}} +{{#model}} +{{^withoutRuntimeChecks}} +export * from './{{{ classFilename }}}{{importFileExtension}}'; +{{#useSagaAndRecords}} +{{^isEnum}} +export * from './{{{ classFilename }}}Record{{importFileExtension}}'; +{{/isEnum}} +{{/useSagaAndRecords}} +{{/withoutRuntimeChecks}} +{{#withoutRuntimeChecks}} +{{#isEnum}} +{{>modelEnumInterfaces}} +{{/isEnum}} +{{^isEnum}} +{{#oneOf}} +{{#-first}} +{{>modelOneOfInterfaces}} +{{/-first}} +{{/oneOf}} +{{^oneOf}} +{{>modelGenericInterfaces}} +{{/oneOf}} +{{/isEnum}} +{{/withoutRuntimeChecks}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/models.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/models.mustache new file mode 100644 index 00000000000..20135a84425 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/models.mustache @@ -0,0 +1,20 @@ +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} +{{#models}} +{{#model}} +{{#isEnum}} +{{>modelEnum}} +{{/isEnum}} +{{^isEnum}} +{{#oneOf}} +{{#-first}} +{{>modelOneOf}} +{{/-first}} +{{/oneOf}} +{{^oneOf}} +{{>modelGeneric}} +{{/oneOf}} +{{/isEnum}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/npmignore.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/npmignore.mustache new file mode 100644 index 00000000000..42061c01a1c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/npmignore.mustache @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/package.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/package.mustache new file mode 100644 index 00000000000..ebb742fdb78 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/package.mustache @@ -0,0 +1,40 @@ +{ + "name": "{{npmName}}", + "version": "{{npmVersion}}", + "description": "OpenAPI client for {{npmName}}", + "author": "OpenAPI-Generator", + "repository": { + "type": "git", + "url": "https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}.git" + }, +{{#packageAsSourceOnlyLibrary}} + "main": "./index.ts", +{{/packageAsSourceOnlyLibrary}} +{{^packageAsSourceOnlyLibrary}} + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", +{{#supportsES6}} + "module": "./dist/esm/index.js", + "sideEffects": false, +{{/supportsES6}} + "scripts": { + "build": "tsc{{#supportsES6}} && tsc -p tsconfig.esm.json{{/supportsES6}}"{{^sagasAndRecords}}, + "prepare": "npm run build"{{/sagasAndRecords}} + }, +{{/packageAsSourceOnlyLibrary}} + "devDependencies": { +{{#sagasAndRecords}} + "immutable": "^4.0.0-rc.12", + "normalizr": "^3.6.1", + "redux-saga": "^1.1.3", + "redux-ts-simple": "^3.2.0", + "reselect": "^4.0.0", +{{/sagasAndRecords}} + "typescript": "^4.0 || ^5.0" + }{{#npmRepository}},{{/npmRepository}} +{{#npmRepository}} + "publishConfig": { + "registry": "{{npmRepository}}" + } +{{/npmRepository}} +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/recordGeneric.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/recordGeneric.mustache new file mode 100644 index 00000000000..819a29e44ee --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/recordGeneric.mustache @@ -0,0 +1,295 @@ +import {ApiRecordUtils, knownRecordFactories{{#returnPassthrough}}, appFromJS, NormalizedRecordEntities{{/returnPassthrough}}} from "../runtimeSagasAndRecords{{importFileExtension}}"; +import {getApiEntitiesState} from "../ApiEntitiesSelectors{{importFileExtension}}" +import {List, Record, RecordOf, Map} from 'immutable'; +import {Schema, schema, NormalizedSchema} from "normalizr"; +import {select, call} from "redux-saga/effects"; + +import { + {{classname}}, +{{#vars}} +{{#isEnum}} + {{classname}}{{enumName}}, +{{/isEnum}} +{{/vars}} +} from './{{classname}}{{importFileExtension}}'; + +{{#imports}} +import { + {{{.}}}, +} from './{{{.}}}{{importFileExtension}}'; +{{/imports}} + +{{#modelImports}} +import { + {{{.}}}Record, + {{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}RecordUtils +} from './{{{.}}}Record{{importFileExtension}}'; +{{/modelImports}} + +export const {{classname}}RecordProps = { + recType: "{{classname}}ApiRecord" as "{{classname}}ApiRecord", +{{#vars}} + {{#isArray}} + {{#items.isModel}} + {{#keepAsJSObject}} + {{name}}: {{#required}}new {{{dataType}}}(){{/required}}{{^required}}null as {{{dataType}}} | null{{/required}}, + {{/keepAsJSObject}} + {{^keepAsJSObject}} + {{name}}: ({{{items.dataType}}}Record(), {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null as {{{dataTypeAlternate}}} | null{{/required}}), + {{/keepAsJSObject}} + {{/items.isModel}} + {{^items.isModel}} + {{name}}: {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null as {{{dataTypeAlternate}}} | null{{/required}}, + {{/items.isModel}} + {{/isArray}} + {{#isModel}} + {{#keepAsJSObject}} + {{name}}: {{#required}}{} as any as {{{dataType}}}{{/required}}{{^required}}null as {{{dataType}}} | null{{/required}}, + {{/keepAsJSObject}} + {{^keepAsJSObject}} + {{name}}: {{#required}}{{{defaultValue}}}{{/required}}{{^required}}({{{defaultValue}}}, null as {{{dataTypeAlternate}}} | null){{/required}}, + {{/keepAsJSObject}} + {{/isModel}} + {{^isArray}} + {{^isModel}} + {{name}}: {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null as {{{dataTypeAlternate}}} | null{{/required}}, + {{/isModel}} + {{/isArray}} +{{/vars}} +}; + +export type {{classname}}RecordPropsType = typeof {{classname}}RecordProps; +export const {{classname}}Record = Record({{classname}}RecordProps, {{classname}}RecordProps.recType); +export type {{classname}}Record = RecordOf<{{classname}}RecordPropsType>; + +knownRecordFactories.set({{classname}}RecordProps.recType, {{classname}}Record); + +{{#isEntity}} +export const {{classname}}RecordEntityProps = { + ...{{classname}}RecordProps, + recType: "{{classname}}ApiRecordEntity" as "{{classname}}ApiRecordEntity", +{{#vars}} + {{#isEntity}} + {{^keepAsJSObject}} + {{name}}: {{#required}}"-1"{{/required}}{{^required}}null as string | null{{/required}}, + {{/keepAsJSObject}} + {{/isEntity}} + {{#isArray}} + {{#items.isEntity}} + {{^keepAsJSObject}} + {{name}}: {{#required}}List(){{/required}}{{^required}}null as List | null{{/required}}, + {{/keepAsJSObject}} + {{/items.isEntity}} + {{/isArray}} +{{/vars}} +}; + +export type {{classname}}RecordEntityPropsType = typeof {{classname}}RecordEntityProps; +export const {{classname}}RecordEntity = Record({{classname}}RecordEntityProps, {{classname}}RecordEntityProps.recType); +export type {{classname}}RecordEntity = RecordOf<{{classname}}RecordEntityPropsType>; + +knownRecordFactories.set({{classname}}RecordEntityProps.recType, {{classname}}RecordEntity); +{{/isEntity}} + +class {{classname}}RecordUtils extends ApiRecordUtils<{{classname}}, {{classname}}Record> { + public normalize(apiObject: {{classname}}, asEntity?: boolean): {{classname}} { + (apiObject as any).recType = {{#isEntity}}asEntity ? {{classname}}RecordEntityProps.recType : {{/isEntity}}{{classname}}RecordProps.recType; +{{#vars}} + {{#isUniqueId}} + {{#isArray}} + {{#items.isArray}} + {{^required}}if (apiObject['{{name}}']) { {{/required}}(apiObject as any)['{{name}}'] = apiObject['{{name}}'].map(item => item.map(item2 => item2?.toString()));{{^required}} } {{/required}} + {{/items.isArray}} + {{^items.isArray}} + {{^required}}if (apiObject['{{name}}']) { {{/required}}(apiObject as any)['{{name}}'] = apiObject['{{name}}'].map(item => item?.toString());{{^required}} } {{/required}} + {{/items.isArray}} + {{/isArray}} + {{^isArray}} + {{^required}}if (apiObject['{{name}}']) { {{/required}}(apiObject as any)['{{name}}'] = apiObject['{{name}}'].toString();{{^required}} } {{/required}} + {{/isArray}} + {{/isUniqueId}} + {{^keepAsJSObject}} + {{#isModel}} + {{^required}}if (apiObject['{{name}}']) { {{/required}}{{#lambda.camelcase}}{{{dataTypeAlternate}}}{{/lambda.camelcase}}Utils.normalize(apiObject['{{name}}']);{{^required}} } {{/required}} + {{/isModel}} + {{#isArray}} + {{#items.isModel}} + {{^required}}if (apiObject['{{name}}']) { {{/required}}{{#lambda.camelcase}}{{items.dataType}}{{/lambda.camelcase}}RecordUtils.normalizeArray(apiObject['{{name}}']);{{^required}} } {{/required}} + {{/items.isModel}} + {{/isArray}} + {{/keepAsJSObject}} +{{/vars}} + return apiObject; + } +{{#isEntity}} + + public getSchema(): Schema { + return new schema.Entity("{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}", { +{{#vars}} + {{#isEntity}} + {{^keepAsJSObject}} + {{name}}: {{#lambda.camelcase}}{{{dataTypeAlternate}}}{{/lambda.camelcase}}Utils.getSchema(), + {{/keepAsJSObject}} + {{/isEntity}} + {{#isArray}} + {{#items.isEntity}} + {{^keepAsJSObject}} + {{name}}: [{{#lambda.camelcase}}{{items.dataType}}{{/lambda.camelcase}}RecordUtils.getSchema()], + {{/keepAsJSObject}} + {{/items.isEntity}} + {{/isArray}} +{{/vars}} + }); + } + + public *toInlined(entityId?: string | null) { + if (!entityId) {return undefined; } + // @ts-ignore + const entity = yield select(apiEntity{{classname}}Selector, {id: entityId}); + if (!entity) {return undefined; } + + const { + recType, +{{#vars}} +{{#isEntity}} +{{^keepAsJSObject}} + {{name}}: {{name}}_original, +{{/keepAsJSObject}} +{{/isEntity}} +{{#isArray}} +{{#items.isEntity}} +{{^keepAsJSObject}} + {{name}}: {{name}}_original, +{{/keepAsJSObject}} +{{/items.isEntity}} +{{/isArray}} +{{/vars}} + ...unchangedProperties + } = entity; + + const entityProperties = { +{{#vars}} +{{#isEntity}} +{{^keepAsJSObject}} + // @ts-ignore + {{name}}: {{^required}}entity['{{name}}'] ? {{/required}}yield call({{#lambda.camelcase}}{{{dataTypeAlternate}}}{{/lambda.camelcase}}Utils.toInlined, entity['{{name}}']){{^required}} : null{{/required}}, +{{/keepAsJSObject}} +{{/isEntity}} +{{#isArray}} +{{#items.isEntity}} +{{^keepAsJSObject}} + // @ts-ignore + {{name}}: {{^required}}entity['{{name}}'] ? {{/required}}yield call({{#lambda.camelcase}}{{items.dataType}}{{/lambda.camelcase}}RecordUtils.toInlinedArray, entity['{{name}}']){{^required}} : null{{/required}}, +{{/keepAsJSObject}} +{{/items.isEntity}} +{{/isArray}} +{{/vars}} + } + + return {{classname}}Record({ + ...unchangedProperties, + ...entityProperties + }); + } + + public *toInlinedArray(entityIds: List | null) { + if (!entityIds) {return null; } + let entities = List<{{classname}}Record>(); + for (let entityIndex = 0; entityIndex < entityIds.count(); entityIndex++) { + // @ts-ignore + const entity = yield call(this.toInlined, entityIds.get(entityIndex)); + if (entity) { + entities.push(entity); + } + } + return entities; + } +{{/isEntity}} + + public toApi(record: {{classname}}Record): {{classname}} { + const apiObject = super.toApi(record); +{{#vars}} + {{#isUniqueId}} + {{#isArray}} + {{#items.isArray}} + {{^required}}if (record['{{name}}']) { {{/required}}apiObject['{{name}}'] = {{#isArray}}record['{{name}}'].map(item => item.toArray().map(item2 => (item2 ? parseFloat(item2) : null) as number)).toArray(){{/isArray}};{{^required}} } {{/required}} + {{/items.isArray}} + {{^items.isArray}} + {{^required}}if (record['{{name}}']) { {{/required}}apiObject['{{name}}'] = {{#isArray}}record['{{name}}'].map(item => (item ? parseFloat(item) : null) as number).toArray(){{/isArray}};{{^required}} } {{/required}} + {{/items.isArray}} + {{/isArray}} + {{^isArray}} + {{^required}}if (record['{{name}}']) { {{/required}}apiObject['{{name}}'] = {{^isArray}}parseFloat(record['{{name}}']){{/isArray}};{{^required}} } {{/required}} + {{/isArray}} + {{/isUniqueId}} + {{^keepAsJSObject}} + {{#isModel}} + {{^required}}if (record['{{name}}']) { {{/required}}apiObject['{{name}}'] = {{#lambda.camelcase}}{{{dataTypeAlternate}}}{{/lambda.camelcase}}Utils.toApi(record['{{name}}']);{{^required}} } {{/required}} + {{/isModel}} + {{#isArray}} + {{#items.isModel}} + {{^required}}if (record['{{name}}']) { {{/required}}apiObject['{{name}}'] = {{#lambda.camelcase}}{{items.dataType}}{{/lambda.camelcase}}RecordUtils.toApiArray(record['{{name}}']);{{^required}} } {{/required}} + {{/items.isModel}} + {{/isArray}} + {{/keepAsJSObject}} +{{/vars}} + return apiObject; + } +{{#returnPassthrough}} +{{#vars.1}} + + public fromApiPassthrough(apiObject: {{classname}}): {{{dataTypeAlternate}}} { + {{#isModel}} + if (!apiObject.{{{returnPassthrough}}}) {return {{{defaultValue}}}; } + const normalizedApiObject = {{#lambda.camelcase}}{{{dataTypeAlternate}}}{{/lambda.camelcase}}Utils.normalize(apiObject.{{{returnPassthrough}}}); + return appFromJS(normalizedApiObject); + {{/isModel}} + {{#isArray}} + {{#items.isModel}} + if (!apiObject.{{{returnPassthrough}}}) {return {{{defaultValue}}}; } + const normalizedApiObject = {{#lambda.camelcase}}{{items.dataType}}{{/lambda.camelcase}}RecordUtils.normalizeArray(apiObject.{{{returnPassthrough}}}); + return appFromJS(normalizedApiObject); + {{/items.isModel}} + {{^items.isModel}} + return appFromJS(apiObject.{{{returnPassthrough}}}); + {{/items.isModel}} + {{/isArray}} + {{^isModel}} + {{^isArray}} + return apiObject.{{{returnPassthrough}}}!; + {{/isArray}} + {{/isModel}} + } + + public fromApiPassthroughAsEntities(apiObject: {{classname}}): NormalizedRecordEntities { + {{#isEntity}} + if (!apiObject.{{{returnPassthrough}}}) {return {entities: {}, result: List()}; } + return ApiRecordUtils.toNormalizedRecordEntities({{#lambda.camelcase}}{{{dataTypeAlternate}}}{{/lambda.camelcase}}Utils.normalizeArrayAsEntities([apiObject.{{{returnPassthrough}}}]), true); + {{/isEntity}} + {{#isArray}} + {{#items.isEntity}} + if (!apiObject.{{{returnPassthrough}}}) {return {entities: {}, result: List()}; } + return ApiRecordUtils.toNormalizedRecordEntities({{#lambda.camelcase}}{{items.dataType}}{{/lambda.camelcase}}RecordUtils.normalizeArrayAsEntities(apiObject.{{{returnPassthrough}}}), true); + {{/items.isEntity}} + {{^items.isEntity}} + console.log("entities revival not supported on this response"); + return {entities: {}, result: List()}; + {{/items.isEntity}} + {{/isArray}} + {{^isEntity}} + {{^isArray}} + console.log("entities revival not supported on this response"); + return {entities: {}, result: List()}; + {{/isArray}} + {{/isEntity}} + } +{{/vars.1}} +{{/returnPassthrough}} +} + +export const {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}RecordUtils = new {{classname}}RecordUtils(); + +{{#isEntity}} +export const apiEntities{{classname}}Selector = (state: any) => getApiEntitiesState(state).{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} as Map; +export const apiEntity{{classname}}Selector = (state: any, {id}: {id?: string | null}) => id ? apiEntities{{classname}}Selector(state).get(id) : undefined; +{{/isEntity}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/records.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/records.mustache new file mode 100644 index 00000000000..5c8a0ad348a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/records.mustache @@ -0,0 +1,15 @@ +{{#models}} +{{#model}} +{{#isEnum}} +// This file is not needed and was generated only because of how codegen is built... Enums do not need to be converted to Records and can be used directly. +{{/isEnum}} +{{^isEnum}} +{{^oneOf}} +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} +{{>recordGeneric}} +{{/oneOf}} +{{/isEnum}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/runtime.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/runtime.mustache new file mode 100644 index 00000000000..991ac373495 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/runtime.mustache @@ -0,0 +1,417 @@ +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} + +export const BASE_PATH = "{{{basePath}}}".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +{{^withoutRuntimeChecks}} +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} +{{/withoutRuntimeChecks}} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/runtimeSagasAndRecords.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/runtimeSagasAndRecords.mustache new file mode 100644 index 00000000000..e182521fac3 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/runtimeSagasAndRecords.mustache @@ -0,0 +1,120 @@ +/* tslint:disable */ +/* eslint-disable */ + +import {fromJS as originalFromJS, isIndexed, List, Map as ImmMap, RecordOf} from 'immutable'; +import {normalize, NormalizedSchema, schema, Schema} from "normalizr"; +import {ActionDefinition, createAction} from "redux-ts-simple"; + +export const knownRecordFactories = new Map(); +export const knownIndexedSetByKey: (string | number)[] = []; + +export function appFromJS(any: any): any { + return originalFromJS(any, (key, value) => { + if (isIndexed(value)) { + return knownIndexedSetByKey.indexOf(key) !== -1 ? value.toSet() : value.toList(); + } // we're reviving an array -> it's a List + const MatchingType = knownRecordFactories.get(value.get('recType')) as { new(input?: any): any }; // check if we know a Record with this type + if (MatchingType) { + return new MatchingType(value); + } + return value.toMap(); // no matching Record type found -> it's a plain old Map + }); +} + +export type NormalizedRecordEntity = NormalizedSchema<{ [key: string]: Map> }, string>; +export type NormalizedRecordEntities = NormalizedSchema<{ [key: string]: Map> }, List>; + +export abstract class ApiRecordUtils> { + public abstract normalize(apiObject: TAPI, asEntity?: boolean): any; + + public getSchema(): Schema { + console.log("Entity mode not supported on this record."); + return new schema.Entity("entityNotSupported"); + } + + public normalizeArray(apiObjectArray: TAPI[], asEntity?: boolean): TAPI[] { + apiObjectArray.forEach(apiObject => this.normalize(apiObject, asEntity)); + return apiObjectArray; + } + + public normalizeAsEntities(apiObject: TAPI): NormalizedSchema { + const normalized = this.normalize(apiObject, true); + return normalize(normalized, this.getSchema()); + } + + public normalizeArrayAsEntities(apiObject: TAPI[]): NormalizedSchema { + const normalized = this.normalizeArray(apiObject, true); + return normalize(normalized, new schema.Array(this.getSchema())); + } + + public fromApi(apiObject: TAPI): TREC { + return appFromJS(this.normalize(apiObject)); + } + + public fromApiArray(apiObjectArray: TAPI[]): List { + this.normalizeArray(apiObjectArray); + return appFromJS(apiObjectArray); + } + + public fromApiAsEntities(apiObject: TAPI): NormalizedRecordEntity { + return ApiRecordUtils.toNormalizedRecordEntities(this.normalizeAsEntities(apiObject), false); + } + + public fromApiArrayAsEntities(apiObject: TAPI[]): NormalizedRecordEntities { + return ApiRecordUtils.toNormalizedRecordEntities(this.normalizeArrayAsEntities(apiObject), true); + } + + public toApi(record: TREC): TAPI { + const apiObject = record.toJS(); + delete apiObject.recType; + return apiObject; + } + + public toApiArray(records: List): TAPI[] { + return records.map(record => this.toApi(record)).toArray(); + } + + public static toNormalizedRecordEntities(normalizedAsEntities: any, forArray: boolean) { + const entities = normalizedAsEntities.entities; + for (const entityKey of Object.keys(entities)) { + entities[entityKey] = appFromJS(entities[entityKey]); + } + normalizedAsEntities.result = appFromJS(normalizedAsEntities.result || (forArray ? "" : [])); + return normalizedAsEntities; + } +} + +export const allApiActionFailures: SagaActionDefinition[] = []; + +export interface BaseEntitySupportPayloadApiAction { + toInlined?: boolean; + toEntities?: boolean; + markErrorsAsHandled?: boolean; +} + +export interface BasePayloadApiAction { + markErrorsAsHandled?: boolean; +} + +export interface SagaActionDefinition extends ActionDefinition { + toString: () => string; +} + +export function createSagaAction(type: string, options?: { doNotAutoRegisterFailure?: boolean, namespace?: string }): SagaActionDefinition { + const {doNotAutoRegisterFailure, namespace} = options || {} as any; + let actionDefinition = createAction(namespace ? `${namespace}-${type}` : type); + (actionDefinition as any).toString = () => actionDefinition.type; + if (type.endsWith("Failure") && !doNotAutoRegisterFailure) { + allApiActionFailures.push(actionDefinition); + } + return actionDefinition; +} + +export let apiCall: any>(context: Ctx, fn: Fn, ...args: Parameters) => Generator; + +export function setApiCall(apiCallFc: any>(context: Ctx, fn: Fn, ...args: Parameters) => Generator) { + console.log("init apiCall"); + apiCall = apiCallFc; +} + +export const normalizedEntities = createSagaAction("normalizedEntities"); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/sagaApiManager.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/sagaApiManager.mustache new file mode 100644 index 00000000000..d8979fd9eda --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/sagaApiManager.mustache @@ -0,0 +1,28 @@ +import { + Configuration, + ConfigurationParameters, +} from "../index{{importFileExtension}}"; + +import { +{{#apiInfo}} +{{#apis}} + {{classFilename}}, +{{/apis}} +{{/apiInfo}} +} from "./index{{importFileExtension}}"; + +export class Api { +{{#apiInfo}} +{{#apis}} + public static {{#lambda.camelcase}}{{classFilename}}{{/lambda.camelcase}}: {{classFilename}}; +{{/apis}} +{{/apiInfo}} + + public static init(apiBaseConfig: ConfigurationParameters) { +{{#apiInfo}} +{{#apis}} + Api.{{#lambda.camelcase}}{{classFilename}}{{/lambda.camelcase}} = new {{classFilename}}(new Configuration(apiBaseConfig)); +{{/apis}} +{{/apiInfo}} + } +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/sagas.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/sagas.mustache new file mode 100644 index 00000000000..304324cbd5a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/sagas.mustache @@ -0,0 +1,244 @@ +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} + +import {Api} from './index{{importFileExtension}}'; +import {List} from 'immutable'; +import {all, fork, put, takeLatest} from "redux-saga/effects"; +import {apiCall, createSagaAction as originalCreateSagaAction, BaseEntitySupportPayloadApiAction, BasePayloadApiAction, NormalizedRecordEntities, normalizedEntities} from "../runtimeSagasAndRecords{{importFileExtension}}"; +import {Action} from "redux-ts-simple"; + +{{#imports.0}} +import { + {{#imports}} + {{className}}, + {{className}}Record, + {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}RecordUtils, + {{/imports}} + {{#passthroughImports}} + {{.}}, + {{/passthroughImports}} +} from '../models/index{{importFileExtension}}'; +{{/imports.0}} +{{#hasEnums}} +{{#operations}} +{{#operation}} +{{#allParams}} +{{#isEnum}} + +import { + {{operationIdCamelCase}}{{enumName}}, +} from './{{classname}}{{importFileExtension}}'; +{{/isEnum}} +{{/allParams}} +{{/operation}} +{{/operations}} +{{/hasEnums}} + +const createSagaAction = (type: string) => originalCreateSagaAction(type, {namespace: "api_{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}"}); + +export const {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}SagaMap = new Map Generator>([ +{{#operations}} + {{#operation}} + ["{{nickname}}", {{nickname}}Saga], + {{/operation}} +{{/operations}} + ] +); + +export function *{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}AllSagas() { + yield all([...{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}SagaMap.values()].map(actionSaga => fork(actionSaga))); +} + +{{#operations}} +{{#operation}} +//region {{nickname}} + +{{#returnTypeSupportsEntities}} +export interface Payload{{#lambda.titlecase}}{{#lambda.camelcase}}{{nickname}}{{/lambda.camelcase}}{{/lambda.titlecase}} extends {{#allParams.0}}Payload{{#lambda.titlecase}}{{#lambda.camelcase}}{{nickname}}{{/lambda.camelcase}}{{/lambda.titlecase}}Request, {{/allParams.0}}BaseEntitySupportPayloadApiAction { +} +{{/returnTypeSupportsEntities}} +{{^returnTypeSupportsEntities}} +export interface Payload{{#lambda.titlecase}}{{#lambda.camelcase}}{{nickname}}{{/lambda.camelcase}}{{/lambda.titlecase}} extends {{#allParams.0}}Payload{{#lambda.titlecase}}{{#lambda.camelcase}}{{nickname}}{{/lambda.camelcase}}{{/lambda.titlecase}}Request, {{/allParams.0}}BasePayloadApiAction { +} +{{/returnTypeSupportsEntities}} + +{{#allParams.0}} +export interface Payload{{#lambda.titlecase}}{{#lambda.camelcase}}{{nickname}}{{/lambda.camelcase}}{{/lambda.titlecase}}Request { +{{#allParams}} + {{paramName}}{{^required}}?{{/required}}: {{{dataTypeAlternate}}}; +{{/allParams}} +} +{{/allParams.0}} + +export const {{nickname}}Request = createSagaAction<{{#allParams.0}}Payload{{#lambda.titlecase}}{{#lambda.camelcase}}{{nickname}}{{/lambda.camelcase}}{{/lambda.titlecase}}Request{{/allParams.0}}{{^allParams.0}}void{{/allParams.0}}>("{{nickname}}Request"); +{{#returnType}} +export const {{nickname}}Success = createSagaAction<{{#hasReturnPassthroughVoid}}void{{/hasReturnPassthroughVoid}}{{^hasReturnPassthroughVoid}}{{{returnTypeAlternate}}}{{/hasReturnPassthroughVoid}}>("{{nickname}}Success"); +{{#returnTypeSupportsEntities}} +export const {{nickname}}Success_Entities = createSagaAction("{{nickname}}Success_Entities"); +{{/returnTypeSupportsEntities}} +{{/returnType}} +{{^returnType}} +export const {{nickname}}Success = createSagaAction("{{nickname}}Success"); +{{/returnType}} +export const {{nickname}}Failure = createSagaAction<{error: any, requestPayload: Payload{{#lambda.titlecase}}{{#lambda.camelcase}}{{nickname}}{{/lambda.camelcase}}{{/lambda.titlecase}}}>("{{nickname}}Failure"); + +export const {{nickname}} = createSagaAction("{{nickname}}"); + +export function *{{nickname}}Saga() { + yield takeLatest({{nickname}}, {{nickname}}SagaImp); +} + +export function *{{nickname}}SagaImp(_action_: Action) { + const {markErrorsAsHandled, ..._payloadRest_} = _action_.payload; + try { +{{#returnTypeSupportsEntities}} + const {toEntities, toInlined = !toEntities, ...requestPayload} = _payloadRest_; +{{/returnTypeSupportsEntities}} +{{#allParams.0}} + const { +{{#allParams}} + {{paramName}}, +{{/allParams}} + } = _payloadRest_; +{{/allParams.0}} + + yield put({{nickname}}Request({{#allParams.0}}{{#returnTypeSupportsEntities}}requestPayload{{/returnTypeSupportsEntities}}{{^returnTypeSupportsEntities}}_action_.payload{{/returnTypeSupportsEntities}}{{/allParams.0}})); + + const response{{#returnType}}: Required<{{{returnType}}}>{{/returnType}} = yield apiCall(Api.{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}, Api.{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}['{{nickname}}'], +{{#allParams.0}} +{{#allParams}} +{{#isUniqueId}} +{{#isArray}} +{{#items.isArray}} + {{^required}}{{paramName}} ? {{/required}}{{paramName}}.map(p => p.toArray().map(p2 => (p2 ? parseFloat(p2) : null) as number)).toArray(){{^required}} : undefined{{/required}}, +{{/items.isArray}} +{{^items.isArray}} + {{^required}}{{paramName}} ? {{/required}}{{paramName}}.map(p => (p ? parseFloat(p) : null) as number ).toArray(){{^required}} : undefined{{/required}}, +{{/items.isArray}} +{{/isArray}} +{{^isArray}} + {{^required}}{{paramName}} ? {{/required}}parseFloat({{paramName}}){{^required}} : undefined{{/required}}, +{{/isArray}} +{{/isUniqueId}} +{{^isUniqueId}} +{{#isArray}} +{{#items.isModel}} + {{^required}}{{paramName}} ? {{/required}}{{#lambda.camelcase}}{{items.dataType}}{{/lambda.camelcase}}RecordUtils.toApiArray({{paramName}}){{^required}} : undefined{{/required}}, +{{/items.isModel}} +{{^items.isModel}} + {{^required}}{{paramName}} ? {{/required}}{{paramName}}.toJS(){{^required}} : undefined{{/required}}, +{{/items.isModel}} +{{/isArray}} +{{#isModel}} + {{^required}}{{paramName}} ? {{/required}}{{#lambda.camelcase}}{{{dataTypeAlternate}}}{{/lambda.camelcase}}Utils.toApi({{paramName}}){{^required}} : undefined{{/required}}, +{{/isModel}} +{{^isArray}} +{{^isModel}} + {{paramName}}, +{{/isModel}} +{{/isArray}} +{{/isUniqueId}} +{{/allParams}} +{{/allParams.0}} + ); + +{{#returnType}} +{{^hasReturnPassthroughVoid}} + let successReturnValue: any = undefined; +{{/hasReturnPassthroughVoid}} +{{/returnType}} +{{#returnTypeSupportsEntities}} + if (toEntities) { +{{#returnPassthrough}} + successReturnValue = {{#lambda.camelcase}}{{{returnType}}}{{/lambda.camelcase}}RecordUtils.fromApiPassthroughAsEntities(response); +{{/returnPassthrough}} +{{^hasReturnPassthroughVoid}} +{{^returnPassthrough}} +{{#returnTypeIsArray}} +{{#returnTypeAlternate}} + successReturnValue = {{#lambda.camelcase}}{{returnBaseTypeAlternate}}{{/lambda.camelcase}}Utils.fromApiArrayAsEntities(response); +{{/returnTypeAlternate}} +{{/returnTypeIsArray}} +{{#returnTypeIsModel}} + successReturnValue = {{#lambda.camelcase}}{{returnTypeAlternate}}{{/lambda.camelcase}}Utils.fromApiArrayAsEntities([response]); +{{/returnTypeIsModel}} +{{/returnPassthrough}} +{{/hasReturnPassthroughVoid}} + yield put(normalizedEntities(successReturnValue)); + yield put({{nickname}}Success_Entities(successReturnValue)); + } + if (toInlined) { +{{/returnTypeSupportsEntities}} +{{#returnType}} +{{#returnPassthrough}} + successReturnValue = {{#lambda.camelcase}}{{{returnType}}}{{/lambda.camelcase}}RecordUtils.fromApiPassthrough(response); + yield put({{nickname}}Success(successReturnValue)); +{{/returnPassthrough}} +{{#hasReturnPassthroughVoid}} + yield put({{nickname}}Success()); +{{/hasReturnPassthroughVoid}} +{{^hasReturnPassthroughVoid}} +{{^returnPassthrough}} +{{#returnTypeIsArray}} +{{#returnTypeAlternate}} + successReturnValue = {{#lambda.camelcase}}{{returnBaseTypeAlternate}}{{/lambda.camelcase}}Utils.fromApiArray(response); + yield put({{nickname}}Success(successReturnValue)); +{{/returnTypeAlternate}} +{{/returnTypeIsArray}} +{{#returnTypeIsModel}} + successReturnValue = {{#lambda.camelcase}}{{returnTypeAlternate}}{{/lambda.camelcase}}Utils.fromApi(response); + yield put({{nickname}}Success(successReturnValue)); +{{/returnTypeIsModel}} +{{^returnTypeIsArray}} +{{^returnTypeIsModel}} + yield put({{nickname}}Success(response)); +{{/returnTypeIsModel}} +{{/returnTypeIsArray}} +{{/returnPassthrough}} +{{/hasReturnPassthroughVoid}} +{{/returnType}} +{{^returnType}} + yield put({{nickname}}Success()); +{{/returnType}} +{{#returnTypeSupportsEntities}} + } +{{/returnTypeSupportsEntities}} + +{{#returnType}} +{{#returnPassthrough}} + return successReturnValue; +{{/returnPassthrough}} +{{#hasReturnPassthroughVoid}} + return undefined; +{{/hasReturnPassthroughVoid}} +{{^hasReturnPassthroughVoid}} +{{^returnPassthrough}} +{{#returnTypeIsArray}} +{{#returnTypeAlternate}} + return successReturnValue; +{{/returnTypeAlternate}} +{{/returnTypeIsArray}} +{{#returnTypeIsModel}} + return successReturnValue; +{{/returnTypeIsModel}} +{{^returnTypeIsArray}} +{{^returnTypeIsModel}} + return response; +{{/returnTypeIsModel}} +{{/returnTypeIsArray}} +{{/returnPassthrough}} +{{/hasReturnPassthroughVoid}} +{{/returnType}} +{{^returnType}} + return undefined; +{{/returnType}} + } catch (error) { + if (markErrorsAsHandled) {error.wasHandled = true; } + yield put({{nickname}}Failure({error, requestPayload: _action_.payload})); + return error; + } +} +//endregion +{{/operation}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/sourceLibraryIndex.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/sourceLibraryIndex.mustache new file mode 100644 index 00000000000..3a0629e06cc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/sourceLibraryIndex.mustache @@ -0,0 +1 @@ +export * from './src/index{{importFileExtension}}'; diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/tsconfig.esm.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/tsconfig.esm.mustache new file mode 100644 index 00000000000..2c0331cce04 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/tsconfig.esm.mustache @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "esnext", + "outDir": "dist/esm" + } +} diff --git a/modules/openapi-generator/src/main/resources/typescript-playwright/tsconfig.mustache b/modules/openapi-generator/src/main/resources/typescript-playwright/tsconfig.mustache new file mode 100644 index 00000000000..a8778b09659 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-playwright/tsconfig.mustache @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "declaration": true, + "target": "{{#supportsES6}}es6{{/supportsES6}}{{^supportsES6}}es5{{/supportsES6}}", +{{#sagasAndRecords}} + "strict": true, +{{/sagasAndRecords}} + "module": "commonjs", + "moduleResolution": "node", + "outDir": "dist", + {{^supportsES6}} + "lib": [ + "es6", + "dom" + ], + {{/supportsES6}} + "typeRoots": [ + "node_modules/@types" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/samples/client/petstore/typescript-wright/.openapi-generator-ignore b/samples/client/petstore/typescript-wright/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/client/petstore/typescript-wright/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/petstore/typescript-wright/.openapi-generator/FILES b/samples/client/petstore/typescript-wright/.openapi-generator/FILES new file mode 100644 index 00000000000..2a7406ef851 --- /dev/null +++ b/samples/client/petstore/typescript-wright/.openapi-generator/FILES @@ -0,0 +1,13 @@ +apis/PetApi.ts +apis/StoreApi.ts +apis/UserApi.ts +apis/index.ts +index.ts +models/Category.ts +models/ModelApiResponse.ts +models/Order.ts +models/Pet.ts +models/Tag.ts +models/User.ts +models/index.ts +runtime.ts diff --git a/samples/client/petstore/typescript-wright/.openapi-generator/VERSION b/samples/client/petstore/typescript-wright/.openapi-generator/VERSION new file mode 100644 index 00000000000..7e7b8b9bc73 --- /dev/null +++ b/samples/client/petstore/typescript-wright/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.7.0-SNAPSHOT diff --git a/samples/client/petstore/typescript-wright/apis/PetApi.ts b/samples/client/petstore/typescript-wright/apis/PetApi.ts new file mode 100644 index 00000000000..a791292c47a --- /dev/null +++ b/samples/client/petstore/typescript-wright/apis/PetApi.ts @@ -0,0 +1,465 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + ModelApiResponse, + Pet, +} from '../models/index'; +import { + ModelApiResponseFromJSON, + ModelApiResponseToJSON, + PetFromJSON, + PetToJSON, +} from '../models/index'; + +export interface AddPetRequest { + pet: Pet; +} + +export interface DeletePetRequest { + petId: number; + apiKey?: string; +} + +export interface FindPetsByStatusRequest { + status: Array; +} + +export interface FindPetsByTagsRequest { + tags: Array; +} + +export interface GetPetByIdRequest { + petId: number; +} + +export interface UpdatePetRequest { + pet: Pet; +} + +export interface UpdatePetWithFormRequest { + petId: number; + name?: string; + status?: string; +} + +export interface UploadFileRequest { + petId: number; + additionalMetadata?: string; + file?: Blob; +} + +/** + * + */ +export class PetApi extends runtime.BaseAPI { + + /** + * + * Add a new pet to the store + */ + async addPetRaw(requestParameters: AddPetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['pet'] == null) { + throw new runtime.RequiredError( + 'pet', + 'Required parameter "pet" was null or undefined when calling addPet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("petstore_auth", ["write:pets", "read:pets"]); + } + + const response = await this.request({ + path: `/pet`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: PetToJSON(requestParameters['pet']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PetFromJSON(jsonValue)); + } + + /** + * + * Add a new pet to the store + */ + async addPet(requestParameters: AddPetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.addPetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * + * Deletes a pet + */ + async deletePetRaw(requestParameters: DeletePetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['petId'] == null) { + throw new runtime.RequiredError( + 'petId', + 'Required parameter "petId" was null or undefined when calling deletePet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (requestParameters['apiKey'] != null) { + headerParameters['api_key'] = String(requestParameters['apiKey']); + } + + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("petstore_auth", ["write:pets", "read:pets"]); + } + + const response = await this.request({ + path: `/pet/{petId}`.replace(`{${"petId"}}`, encodeURIComponent(String(requestParameters['petId']))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * + * Deletes a pet + */ + async deletePet(requestParameters: DeletePetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.deletePetRaw(requestParameters, initOverrides); + } + + /** + * Multiple status values can be provided with comma separated strings + * Finds Pets by status + */ + async findPetsByStatusRaw(requestParameters: FindPetsByStatusRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + if (requestParameters['status'] == null) { + throw new runtime.RequiredError( + 'status', + 'Required parameter "status" was null or undefined when calling findPetsByStatus().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['status'] != null) { + queryParameters['status'] = requestParameters['status']!.join(runtime.COLLECTION_FORMATS["csv"]); + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("petstore_auth", ["read:pets"]); + } + + const response = await this.request({ + path: `/pet/findByStatus`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(PetFromJSON)); + } + + /** + * Multiple status values can be provided with comma separated strings + * Finds Pets by status + */ + async findPetsByStatus(requestParameters: FindPetsByStatusRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.findPetsByStatusRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + * Finds Pets by tags + * @deprecated + */ + async findPetsByTagsRaw(requestParameters: FindPetsByTagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + if (requestParameters['tags'] == null) { + throw new runtime.RequiredError( + 'tags', + 'Required parameter "tags" was null or undefined when calling findPetsByTags().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['tags'] != null) { + queryParameters['tags'] = requestParameters['tags']!.join(runtime.COLLECTION_FORMATS["csv"]); + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("petstore_auth", ["read:pets"]); + } + + const response = await this.request({ + path: `/pet/findByTags`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(PetFromJSON)); + } + + /** + * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + * Finds Pets by tags + * @deprecated + */ + async findPetsByTags(requestParameters: FindPetsByTagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.findPetsByTagsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Returns a single pet + * Find pet by ID + */ + async getPetByIdRaw(requestParameters: GetPetByIdRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['petId'] == null) { + throw new runtime.RequiredError( + 'petId', + 'Required parameter "petId" was null or undefined when calling getPetById().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/pet/{petId}`.replace(`{${"petId"}}`, encodeURIComponent(String(requestParameters['petId']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PetFromJSON(jsonValue)); + } + + /** + * Returns a single pet + * Find pet by ID + */ + async getPetById(requestParameters: GetPetByIdRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getPetByIdRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * + * Update an existing pet + */ + async updatePetRaw(requestParameters: UpdatePetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['pet'] == null) { + throw new runtime.RequiredError( + 'pet', + 'Required parameter "pet" was null or undefined when calling updatePet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("petstore_auth", ["write:pets", "read:pets"]); + } + + const response = await this.request({ + path: `/pet`, + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: PetToJSON(requestParameters['pet']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PetFromJSON(jsonValue)); + } + + /** + * + * Update an existing pet + */ + async updatePet(requestParameters: UpdatePetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updatePetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * + * Updates a pet in the store with form data + */ + async updatePetWithFormRaw(requestParameters: UpdatePetWithFormRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['petId'] == null) { + throw new runtime.RequiredError( + 'petId', + 'Required parameter "petId" was null or undefined when calling updatePetWithForm().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("petstore_auth", ["write:pets", "read:pets"]); + } + + const consumes: runtime.Consume[] = [ + { contentType: 'application/x-www-form-urlencoded' }, + ]; + // @ts-ignore: canConsumeForm may be unused + const canConsumeForm = runtime.canConsumeForm(consumes); + + let formParams: { append(param: string, value: any): any }; + let useForm = false; + if (useForm) { + formParams = new FormData(); + } else { + formParams = new URLSearchParams(); + } + + if (requestParameters['name'] != null) { + formParams.append('name', requestParameters['name'] as any); + } + + if (requestParameters['status'] != null) { + formParams.append('status', requestParameters['status'] as any); + } + + const response = await this.request({ + path: `/pet/{petId}`.replace(`{${"petId"}}`, encodeURIComponent(String(requestParameters['petId']))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: formParams, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * + * Updates a pet in the store with form data + */ + async updatePetWithForm(requestParameters: UpdatePetWithFormRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.updatePetWithFormRaw(requestParameters, initOverrides); + } + + /** + * + * uploads an image + */ + async uploadFileRaw(requestParameters: UploadFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['petId'] == null) { + throw new runtime.RequiredError( + 'petId', + 'Required parameter "petId" was null or undefined when calling uploadFile().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("petstore_auth", ["write:pets", "read:pets"]); + } + + const consumes: runtime.Consume[] = [ + { contentType: 'multipart/form-data' }, + ]; + // @ts-ignore: canConsumeForm may be unused + const canConsumeForm = runtime.canConsumeForm(consumes); + + let formParams: { append(param: string, value: any): any }; + let useForm = false; + // use FormData to transmit files using content-type "multipart/form-data" + useForm = canConsumeForm; + if (useForm) { + formParams = new FormData(); + } else { + formParams = new URLSearchParams(); + } + + if (requestParameters['additionalMetadata'] != null) { + formParams.append('additionalMetadata', requestParameters['additionalMetadata'] as any); + } + + if (requestParameters['file'] != null) { + formParams.append('file', requestParameters['file'] as any); + } + + const response = await this.request({ + path: `/pet/{petId}/uploadImage`.replace(`{${"petId"}}`, encodeURIComponent(String(requestParameters['petId']))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: formParams, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelApiResponseFromJSON(jsonValue)); + } + + /** + * + * uploads an image + */ + async uploadFile(requestParameters: UploadFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.uploadFileRaw(requestParameters, initOverrides); + return await response.value(); + } + +} + +/** + * @export + */ +export const FindPetsByStatusStatusEnum = { + Available: 'available', + Pending: 'pending', + Sold: 'sold' +} as const; +export type FindPetsByStatusStatusEnum = typeof FindPetsByStatusStatusEnum[keyof typeof FindPetsByStatusStatusEnum]; diff --git a/samples/client/petstore/typescript-wright/apis/StoreApi.ts b/samples/client/petstore/typescript-wright/apis/StoreApi.ts new file mode 100644 index 00000000000..2e8408d1d2c --- /dev/null +++ b/samples/client/petstore/typescript-wright/apis/StoreApi.ts @@ -0,0 +1,181 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + Order, +} from '../models/index'; +import { + OrderFromJSON, + OrderToJSON, +} from '../models/index'; + +export interface DeleteOrderRequest { + orderId: string; +} + +export interface GetOrderByIdRequest { + orderId: number; +} + +export interface PlaceOrderRequest { + order: Order; +} + +/** + * + */ +export class StoreApi extends runtime.BaseAPI { + + /** + * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + * Delete purchase order by ID + */ + async deleteOrderRaw(requestParameters: DeleteOrderRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['orderId'] == null) { + throw new runtime.RequiredError( + 'orderId', + 'Required parameter "orderId" was null or undefined when calling deleteOrder().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/store/order/{orderId}`.replace(`{${"orderId"}}`, encodeURIComponent(String(requestParameters['orderId']))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + * Delete purchase order by ID + */ + async deleteOrder(requestParameters: DeleteOrderRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.deleteOrderRaw(requestParameters, initOverrides); + } + + /** + * Returns a map of status codes to quantities + * Returns pet inventories by status + */ + async getInventoryRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/store/inventory`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * Returns a map of status codes to quantities + * Returns pet inventories by status + */ + async getInventory(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{ [key: string]: number; }> { + const response = await this.getInventoryRaw(initOverrides); + return await response.value(); + } + + /** + * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions + * Find purchase order by ID + */ + async getOrderByIdRaw(requestParameters: GetOrderByIdRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['orderId'] == null) { + throw new runtime.RequiredError( + 'orderId', + 'Required parameter "orderId" was null or undefined when calling getOrderById().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/store/order/{orderId}`.replace(`{${"orderId"}}`, encodeURIComponent(String(requestParameters['orderId']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => OrderFromJSON(jsonValue)); + } + + /** + * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions + * Find purchase order by ID + */ + async getOrderById(requestParameters: GetOrderByIdRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getOrderByIdRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * + * Place an order for a pet + */ + async placeOrderRaw(requestParameters: PlaceOrderRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['order'] == null) { + throw new runtime.RequiredError( + 'order', + 'Required parameter "order" was null or undefined when calling placeOrder().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/store/order`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: OrderToJSON(requestParameters['order']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => OrderFromJSON(jsonValue)); + } + + /** + * + * Place an order for a pet + */ + async placeOrder(requestParameters: PlaceOrderRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.placeOrderRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/samples/client/petstore/typescript-wright/apis/UserApi.ts b/samples/client/petstore/typescript-wright/apis/UserApi.ts new file mode 100644 index 00000000000..39e604591ff --- /dev/null +++ b/samples/client/petstore/typescript-wright/apis/UserApi.ts @@ -0,0 +1,389 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + User, +} from '../models/index'; +import { + UserFromJSON, + UserToJSON, +} from '../models/index'; + +export interface CreateUserRequest { + user: User; +} + +export interface CreateUsersWithArrayInputRequest { + user: Array; +} + +export interface CreateUsersWithListInputRequest { + user: Array; +} + +export interface DeleteUserRequest { + username: string; +} + +export interface GetUserByNameRequest { + username: string; +} + +export interface LoginUserRequest { + username: string; + password: string; +} + +export interface UpdateUserRequest { + username: string; + user: User; +} + +/** + * + */ +export class UserApi extends runtime.BaseAPI { + + /** + * This can only be done by the logged in user. + * Create user + */ + async createUserRaw(requestParameters: CreateUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['user'] == null) { + throw new runtime.RequiredError( + 'user', + 'Required parameter "user" was null or undefined when calling createUser().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/user`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: UserToJSON(requestParameters['user']), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * This can only be done by the logged in user. + * Create user + */ + async createUser(requestParameters: CreateUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.createUserRaw(requestParameters, initOverrides); + } + + /** + * + * Creates list of users with given input array + */ + async createUsersWithArrayInputRaw(requestParameters: CreateUsersWithArrayInputRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['user'] == null) { + throw new runtime.RequiredError( + 'user', + 'Required parameter "user" was null or undefined when calling createUsersWithArrayInput().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/user/createWithArray`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestParameters['user']!.map(UserToJSON), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * + * Creates list of users with given input array + */ + async createUsersWithArrayInput(requestParameters: CreateUsersWithArrayInputRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.createUsersWithArrayInputRaw(requestParameters, initOverrides); + } + + /** + * + * Creates list of users with given input array + */ + async createUsersWithListInputRaw(requestParameters: CreateUsersWithListInputRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['user'] == null) { + throw new runtime.RequiredError( + 'user', + 'Required parameter "user" was null or undefined when calling createUsersWithListInput().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/user/createWithList`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestParameters['user']!.map(UserToJSON), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * + * Creates list of users with given input array + */ + async createUsersWithListInput(requestParameters: CreateUsersWithListInputRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.createUsersWithListInputRaw(requestParameters, initOverrides); + } + + /** + * This can only be done by the logged in user. + * Delete user + */ + async deleteUserRaw(requestParameters: DeleteUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['username'] == null) { + throw new runtime.RequiredError( + 'username', + 'Required parameter "username" was null or undefined when calling deleteUser().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/user/{username}`.replace(`{${"username"}}`, encodeURIComponent(String(requestParameters['username']))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * This can only be done by the logged in user. + * Delete user + */ + async deleteUser(requestParameters: DeleteUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.deleteUserRaw(requestParameters, initOverrides); + } + + /** + * + * Get user by user name + */ + async getUserByNameRaw(requestParameters: GetUserByNameRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['username'] == null) { + throw new runtime.RequiredError( + 'username', + 'Required parameter "username" was null or undefined when calling getUserByName().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/user/{username}`.replace(`{${"username"}}`, encodeURIComponent(String(requestParameters['username']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => UserFromJSON(jsonValue)); + } + + /** + * + * Get user by user name + */ + async getUserByName(requestParameters: GetUserByNameRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getUserByNameRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * + * Logs user into the system + */ + async loginUserRaw(requestParameters: LoginUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['username'] == null) { + throw new runtime.RequiredError( + 'username', + 'Required parameter "username" was null or undefined when calling loginUser().' + ); + } + + if (requestParameters['password'] == null) { + throw new runtime.RequiredError( + 'password', + 'Required parameter "password" was null or undefined when calling loginUser().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['username'] != null) { + queryParameters['username'] = requestParameters['username']; + } + + if (requestParameters['password'] != null) { + queryParameters['password'] = requestParameters['password']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/user/login`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + } + + /** + * + * Logs user into the system + */ + async loginUser(requestParameters: LoginUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.loginUserRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * + * Logs out current logged in user session + */ + async logoutUserRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/user/logout`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * + * Logs out current logged in user session + */ + async logoutUser(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.logoutUserRaw(initOverrides); + } + + /** + * This can only be done by the logged in user. + * Updated user + */ + async updateUserRaw(requestParameters: UpdateUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['username'] == null) { + throw new runtime.RequiredError( + 'username', + 'Required parameter "username" was null or undefined when calling updateUser().' + ); + } + + if (requestParameters['user'] == null) { + throw new runtime.RequiredError( + 'user', + 'Required parameter "user" was null or undefined when calling updateUser().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["api_key"] = await this.configuration.apiKey("api_key"); // api_key authentication + } + + const response = await this.request({ + path: `/user/{username}`.replace(`{${"username"}}`, encodeURIComponent(String(requestParameters['username']))), + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: UserToJSON(requestParameters['user']), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * This can only be done by the logged in user. + * Updated user + */ + async updateUser(requestParameters: UpdateUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.updateUserRaw(requestParameters, initOverrides); + } + +} diff --git a/samples/client/petstore/typescript-wright/apis/index.ts b/samples/client/petstore/typescript-wright/apis/index.ts new file mode 100644 index 00000000000..3b870e5c4aa --- /dev/null +++ b/samples/client/petstore/typescript-wright/apis/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './PetApi'; +export * from './StoreApi'; +export * from './UserApi'; diff --git a/samples/client/petstore/typescript-wright/index.ts b/samples/client/petstore/typescript-wright/index.ts new file mode 100644 index 00000000000..bebe8bbbe20 --- /dev/null +++ b/samples/client/petstore/typescript-wright/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis/index'; +export * from './models/index'; diff --git a/samples/client/petstore/typescript-wright/models/Category.ts b/samples/client/petstore/typescript-wright/models/Category.ts new file mode 100644 index 00000000000..54f256e339f --- /dev/null +++ b/samples/client/petstore/typescript-wright/models/Category.ts @@ -0,0 +1,68 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * A category for a pet + * @export + * @interface Category + */ +export interface Category { + /** + * + * @type {number} + * @memberof Category + */ + id?: number; + /** + * + * @type {string} + * @memberof Category + */ + name?: string; +} + +/** + * Check if a given object implements the Category interface. + */ +export function instanceOfCategory(value: object): value is Category { + return true; +} + +export function CategoryFromJSON(json: any): Category { + return CategoryFromJSONTyped(json, false); +} + +export function CategoryFromJSONTyped(json: any, ignoreDiscriminator: boolean): Category { + if (json == null) { + return json; + } + return { + + 'id': json['id'] == null ? undefined : json['id'], + 'name': json['name'] == null ? undefined : json['name'], + }; +} + +export function CategoryToJSON(value?: Category | null): any { + if (value == null) { + return value; + } + return { + + 'id': value['id'], + 'name': value['name'], + }; +} + diff --git a/samples/client/petstore/typescript-wright/models/ModelApiResponse.ts b/samples/client/petstore/typescript-wright/models/ModelApiResponse.ts new file mode 100644 index 00000000000..bc250bd002c --- /dev/null +++ b/samples/client/petstore/typescript-wright/models/ModelApiResponse.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Describes the result of uploading an image resource + * @export + * @interface ModelApiResponse + */ +export interface ModelApiResponse { + /** + * + * @type {number} + * @memberof ModelApiResponse + */ + code?: number; + /** + * + * @type {string} + * @memberof ModelApiResponse + */ + type?: string; + /** + * + * @type {string} + * @memberof ModelApiResponse + */ + message?: string; +} + +/** + * Check if a given object implements the ModelApiResponse interface. + */ +export function instanceOfModelApiResponse(value: object): value is ModelApiResponse { + return true; +} + +export function ModelApiResponseFromJSON(json: any): ModelApiResponse { + return ModelApiResponseFromJSONTyped(json, false); +} + +export function ModelApiResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelApiResponse { + if (json == null) { + return json; + } + return { + + 'code': json['code'] == null ? undefined : json['code'], + 'type': json['type'] == null ? undefined : json['type'], + 'message': json['message'] == null ? undefined : json['message'], + }; +} + +export function ModelApiResponseToJSON(value?: ModelApiResponse | null): any { + if (value == null) { + return value; + } + return { + + 'code': value['code'], + 'type': value['type'], + 'message': value['message'], + }; +} + diff --git a/samples/client/petstore/typescript-wright/models/Order.ts b/samples/client/petstore/typescript-wright/models/Order.ts new file mode 100644 index 00000000000..f1a3a4bb0f0 --- /dev/null +++ b/samples/client/petstore/typescript-wright/models/Order.ts @@ -0,0 +1,112 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * An order for a pets from the pet store + * @export + * @interface Order + */ +export interface Order { + /** + * + * @type {number} + * @memberof Order + */ + id?: number; + /** + * + * @type {number} + * @memberof Order + */ + petId?: number; + /** + * + * @type {number} + * @memberof Order + */ + quantity?: number; + /** + * + * @type {Date} + * @memberof Order + */ + shipDate?: Date; + /** + * Order Status + * @type {string} + * @memberof Order + */ + status?: OrderStatusEnum; + /** + * + * @type {boolean} + * @memberof Order + */ + complete?: boolean; +} + + +/** + * @export + */ +export const OrderStatusEnum = { + Placed: 'placed', + Approved: 'approved', + Delivered: 'delivered' +} as const; +export type OrderStatusEnum = typeof OrderStatusEnum[keyof typeof OrderStatusEnum]; + + +/** + * Check if a given object implements the Order interface. + */ +export function instanceOfOrder(value: object): value is Order { + return true; +} + +export function OrderFromJSON(json: any): Order { + return OrderFromJSONTyped(json, false); +} + +export function OrderFromJSONTyped(json: any, ignoreDiscriminator: boolean): Order { + if (json == null) { + return json; + } + return { + + 'id': json['id'] == null ? undefined : json['id'], + 'petId': json['petId'] == null ? undefined : json['petId'], + 'quantity': json['quantity'] == null ? undefined : json['quantity'], + 'shipDate': json['shipDate'] == null ? undefined : (new Date(json['shipDate'])), + 'status': json['status'] == null ? undefined : json['status'], + 'complete': json['complete'] == null ? undefined : json['complete'], + }; +} + +export function OrderToJSON(value?: Order | null): any { + if (value == null) { + return value; + } + return { + + 'id': value['id'], + 'petId': value['petId'], + 'quantity': value['quantity'], + 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'status': value['status'], + 'complete': value['complete'], + }; +} + diff --git a/samples/client/petstore/typescript-wright/models/Pet.ts b/samples/client/petstore/typescript-wright/models/Pet.ts new file mode 100644 index 00000000000..a1b37b58f69 --- /dev/null +++ b/samples/client/petstore/typescript-wright/models/Pet.ts @@ -0,0 +1,128 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { Category } from './Category'; +import { + CategoryFromJSON, + CategoryFromJSONTyped, + CategoryToJSON, +} from './Category'; +import type { Tag } from './Tag'; +import { + TagFromJSON, + TagFromJSONTyped, + TagToJSON, +} from './Tag'; + +/** + * A pet for sale in the pet store + * @export + * @interface Pet + */ +export interface Pet { + /** + * + * @type {number} + * @memberof Pet + */ + id?: number; + /** + * + * @type {Category} + * @memberof Pet + */ + category?: Category; + /** + * + * @type {string} + * @memberof Pet + */ + name: string; + /** + * + * @type {Array} + * @memberof Pet + */ + photoUrls: Array; + /** + * + * @type {Array} + * @memberof Pet + */ + tags?: Array; + /** + * pet status in the store + * @type {string} + * @memberof Pet + * @deprecated + */ + status?: PetStatusEnum; +} + + +/** + * @export + */ +export const PetStatusEnum = { + Available: 'available', + Pending: 'pending', + Sold: 'sold' +} as const; +export type PetStatusEnum = typeof PetStatusEnum[keyof typeof PetStatusEnum]; + + +/** + * Check if a given object implements the Pet interface. + */ +export function instanceOfPet(value: object): value is Pet { + if (!('name' in value) || value['name'] === undefined) return false; + if (!('photoUrls' in value) || value['photoUrls'] === undefined) return false; + return true; +} + +export function PetFromJSON(json: any): Pet { + return PetFromJSONTyped(json, false); +} + +export function PetFromJSONTyped(json: any, ignoreDiscriminator: boolean): Pet { + if (json == null) { + return json; + } + return { + + 'id': json['id'] == null ? undefined : json['id'], + 'category': json['category'] == null ? undefined : CategoryFromJSON(json['category']), + 'name': json['name'], + 'photoUrls': json['photoUrls'], + 'tags': json['tags'] == null ? undefined : ((json['tags'] as Array).map(TagFromJSON)), + 'status': json['status'] == null ? undefined : json['status'], + }; +} + +export function PetToJSON(value?: Pet | null): any { + if (value == null) { + return value; + } + return { + + 'id': value['id'], + 'category': CategoryToJSON(value['category']), + 'name': value['name'], + 'photoUrls': value['photoUrls'], + 'tags': value['tags'] == null ? undefined : ((value['tags'] as Array).map(TagToJSON)), + 'status': value['status'], + }; +} + diff --git a/samples/client/petstore/typescript-wright/models/Tag.ts b/samples/client/petstore/typescript-wright/models/Tag.ts new file mode 100644 index 00000000000..fd877416ade --- /dev/null +++ b/samples/client/petstore/typescript-wright/models/Tag.ts @@ -0,0 +1,68 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * A tag for a pet + * @export + * @interface Tag + */ +export interface Tag { + /** + * + * @type {number} + * @memberof Tag + */ + id?: number; + /** + * + * @type {string} + * @memberof Tag + */ + name?: string; +} + +/** + * Check if a given object implements the Tag interface. + */ +export function instanceOfTag(value: object): value is Tag { + return true; +} + +export function TagFromJSON(json: any): Tag { + return TagFromJSONTyped(json, false); +} + +export function TagFromJSONTyped(json: any, ignoreDiscriminator: boolean): Tag { + if (json == null) { + return json; + } + return { + + 'id': json['id'] == null ? undefined : json['id'], + 'name': json['name'] == null ? undefined : json['name'], + }; +} + +export function TagToJSON(value?: Tag | null): any { + if (value == null) { + return value; + } + return { + + 'id': value['id'], + 'name': value['name'], + }; +} + diff --git a/samples/client/petstore/typescript-wright/models/User.ts b/samples/client/petstore/typescript-wright/models/User.ts new file mode 100644 index 00000000000..149be73b9ff --- /dev/null +++ b/samples/client/petstore/typescript-wright/models/User.ts @@ -0,0 +1,116 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * A User who is purchasing from the pet store + * @export + * @interface User + */ +export interface User { + /** + * + * @type {number} + * @memberof User + */ + id?: number; + /** + * + * @type {string} + * @memberof User + */ + username?: string; + /** + * + * @type {string} + * @memberof User + */ + firstName?: string; + /** + * + * @type {string} + * @memberof User + */ + lastName?: string; + /** + * + * @type {string} + * @memberof User + */ + email?: string; + /** + * + * @type {string} + * @memberof User + */ + password?: string; + /** + * + * @type {string} + * @memberof User + */ + phone?: string; + /** + * User Status + * @type {number} + * @memberof User + */ + userStatus?: number; +} + +/** + * Check if a given object implements the User interface. + */ +export function instanceOfUser(value: object): value is User { + return true; +} + +export function UserFromJSON(json: any): User { + return UserFromJSONTyped(json, false); +} + +export function UserFromJSONTyped(json: any, ignoreDiscriminator: boolean): User { + if (json == null) { + return json; + } + return { + + 'id': json['id'] == null ? undefined : json['id'], + 'username': json['username'] == null ? undefined : json['username'], + 'firstName': json['firstName'] == null ? undefined : json['firstName'], + 'lastName': json['lastName'] == null ? undefined : json['lastName'], + 'email': json['email'] == null ? undefined : json['email'], + 'password': json['password'] == null ? undefined : json['password'], + 'phone': json['phone'] == null ? undefined : json['phone'], + 'userStatus': json['userStatus'] == null ? undefined : json['userStatus'], + }; +} + +export function UserToJSON(value?: User | null): any { + if (value == null) { + return value; + } + return { + + 'id': value['id'], + 'username': value['username'], + 'firstName': value['firstName'], + 'lastName': value['lastName'], + 'email': value['email'], + 'password': value['password'], + 'phone': value['phone'], + 'userStatus': value['userStatus'], + }; +} + diff --git a/samples/client/petstore/typescript-wright/models/index.ts b/samples/client/petstore/typescript-wright/models/index.ts new file mode 100644 index 00000000000..9e4859f3b39 --- /dev/null +++ b/samples/client/petstore/typescript-wright/models/index.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './Category'; +export * from './ModelApiResponse'; +export * from './Order'; +export * from './Pet'; +export * from './Tag'; +export * from './User'; diff --git a/samples/client/petstore/typescript-wright/runtime.ts b/samples/client/petstore/typescript-wright/runtime.ts new file mode 100644 index 00000000000..2c4103a8908 --- /dev/null +++ b/samples/client/petstore/typescript-wright/runtime.ts @@ -0,0 +1,426 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "http://petstore.swagger.io/v2".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +}