From 261a682fee47fb155d828b29f49b304ec2300dc1 Mon Sep 17 00:00:00 2001 From: Bernd Hacker Date: Fri, 11 Jan 2019 08:57:04 +0000 Subject: [PATCH] Merged in feature/rxjs (pull request #2) Feature/rxjs * feat: duplicate typescript-fetch, add docs * duplicate fetch codegen to use for rxjs * remove mapping helpers from modesl * introduce rxjs, adjust apis * make middlewares work * fix namespace issue with babel * fix enum generation * fix formatting of enum * remove other readme * feat: duplicate typescript-fetch, add docs * duplicate fetch codegen to use for rxjs * remove mapping helpers from modesl * introduce rxjs, adjust apis * make middlewares work * fix namespace issue with babel * fix enum generation * fix formatting of enum * remove other readme --- .../TypeScriptRxjsClientCodegen.java | 250 ++++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../resources/typescript-rxjs/README.mustache | 45 ++++ .../typescript-rxjs/apis.index.mustache | 7 + .../resources/typescript-rxjs/apis.mustache | 230 ++++++++++++++++ .../resources/typescript-rxjs/index.mustache | 3 + .../typescript-rxjs/licenseInfo.mustache | 11 + .../typescript-rxjs/modelEnum.mustache | 12 + .../typescript-rxjs/modelGeneric.mustache | 43 +++ .../typescript-rxjs/models.index.mustache | 5 + .../resources/typescript-rxjs/models.mustache | 12 + .../typescript-rxjs/package.mustache | 20 ++ .../typescript-rxjs/runtime.mustache | 200 ++++++++++++++ .../typescript-rxjs/tsconfig.mustache | 19 ++ 14 files changed, 858 insertions(+) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptRxjsClientCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/apis.index.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/apis.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/index.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/licenseInfo.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/modelEnum.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/modelGeneric.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/models.index.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/models.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/package.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/runtime.mustache create mode 100644 modules/openapi-generator/src/main/resources/typescript-rxjs/tsconfig.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptRxjsClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptRxjsClientCodegen.java new file mode 100644 index 000000000000..cb73af15f7f4 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptRxjsClientCodegen.java @@ -0,0 +1,250 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * Copyright 2018 SmartBear Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.languages; + +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.parser.util.SchemaTypeUtil; +import org.openapitools.codegen.CliOption; +import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenOperation; +import org.openapitools.codegen.CodegenParameter; +import org.openapitools.codegen.CodegenConstants; +import org.openapitools.codegen.SupportingFile; +import org.openapitools.codegen.CodegenConstants.MODEL_PROPERTY_NAMING_TYPE; +import org.openapitools.codegen.utils.ModelUtils; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.List; + +public class TypeScriptRxjsClientCodegen extends AbstractTypeScriptClientCodegen { + private static final SimpleDateFormat SNAPSHOT_SUFFIX_FORMAT = new SimpleDateFormat("yyyyMMddHHmm", Locale.ROOT); + + public static final String NPM_NAME = "npmName"; + public static final String NPM_VERSION = "npmVersion"; + public static final String NPM_REPOSITORY = "npmRepository"; + public static final String SNAPSHOT = "snapshot"; + public static final String WITH_INTERFACES = "withInterfaces"; + + protected String npmName = null; + protected String npmVersion = "1.0.0"; + protected String npmRepository = null; + + public TypeScriptRxjsClientCodegen() { + super(); + + // clear import mapping (from default generator) as TS does not use it + // at the moment + importMapping.clear(); + + outputFolder = "generated-code/typescript-rxjs"; + embeddedTemplateDir = templateDir = "typescript-rxjs"; + + this.apiPackage = "apis"; + this.apiTemplateFiles.put("apis.mustache", ".ts"); + this.modelPackage = "models"; + this.modelTemplateFiles.put("models.mustache", ".ts"); + this.addExtraReservedWords(); + + this.cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package")); + this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package")); + this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); + this.cliOptions.add(new CliOption(SNAPSHOT, "When setting this property to true the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString())); + this.cliOptions.add(new CliOption(WITH_INTERFACES, "Setting this property to true will generate interfaces next to the default class implementations.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString())); + } + + @Override + public String getName() { + return "typescript-rxjs"; + } + + @Override + public String getHelp() { + return "Generates a TypeScript client library using Rxjs API."; + } + + public String getNpmName() { + return npmName; + } + + public void setNpmName(String npmName) { + this.npmName = npmName; + } + + public String getNpmVersion() { + return npmVersion; + } + + public void setNpmVersion(String npmVersion) { + this.npmVersion = npmVersion; + } + + public String getNpmRepository() { + return npmRepository; + } + + public void setNpmRepository(String npmRepository) { + this.npmRepository = npmRepository; + } + + @Override + public void processOpts() { + super.processOpts(); + additionalProperties.put("isOriginalModelPropertyNaming", getModelPropertyNaming().equals("original")); + additionalProperties.put("modelPropertyNaming", getModelPropertyNaming()); + supportingFiles.add(new SupportingFile("index.mustache", "", "index.ts")); + supportingFiles.add(new SupportingFile("runtime.mustache", "", "runtime.ts")); + supportingFiles.add(new SupportingFile("apis.index.mustache", apiPackage().replace('.', File.separatorChar), "index.ts")); + supportingFiles.add(new SupportingFile("models.index.mustache", modelPackage().replace('.', File.separatorChar), "index.ts")); + supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json")); + if (additionalProperties.containsKey(NPM_NAME)) { + addNpmPackageGeneration(); + } + } + + @Override + public String getTypeDeclaration(Schema p) { + Schema inner; + if (ModelUtils.isArraySchema(p)) { + inner = ((ArraySchema) p).getItems(); + return this.getSchemaType(p) + "<" + this.getTypeDeclaration(inner) + ">"; + } else if (ModelUtils.isMapSchema(p)) { + inner = ModelUtils.getAdditionalProperties(p); + return "{ [key: string]: " + this.getTypeDeclaration(inner) + "; }"; + } else if (ModelUtils.isFileSchema(p)) { + return "Blob"; + } else if (ModelUtils.isBinarySchema(p)) { + return "Blob"; + } else if (ModelUtils.isDateSchema(p)) { + return "Date"; + } else if (ModelUtils.isDateTimeSchema(p)) { + return "Date"; + } + return super.getTypeDeclaration(p); + } + + @Override + protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) { + codegenModel.additionalPropertiesType = getTypeDeclaration(ModelUtils.getAdditionalProperties(schema)); + addImport(codegenModel, codegenModel.additionalPropertiesType); + } + + @Override + public Map postProcessModels(Map objs) { + // don't do enum modifications + return objs; + } + + @Override + public Map postProcessAllModels(Map objs) { + Map result = super.postProcessAllModels(objs); + for (Map.Entry entry : result.entrySet()) { + Map inner = (Map) entry.getValue(); + List> models = (List>) inner.get("models"); + for (Map model : models) { + CodegenModel codegenModel = (CodegenModel) model.get("model"); + model.put("hasImports", codegenModel.imports.size() > 0); + } + } + return result; + } + + private void addNpmPackageGeneration() { + if (additionalProperties.containsKey(NPM_NAME)) { + this.setNpmName(additionalProperties.get(NPM_NAME).toString()); + } + + if (additionalProperties.containsKey(NPM_VERSION)) { + this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString()); + } + + if (additionalProperties.containsKey(SNAPSHOT) && Boolean.valueOf(additionalProperties.get(SNAPSHOT).toString())) { + this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.format(new Date())); + } + additionalProperties.put(NPM_VERSION, npmVersion); + + if (additionalProperties.containsKey(NPM_REPOSITORY)) { + this.setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString()); + } + + //Files for building our lib + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("package.mustache", "", "package.json")); + } + + @Override + public Map postProcessOperationsWithModels(Map operations, List allModels) { + this.addOperationModelImportInfomation(operations); + this.updateOperationParameterEnumInformation(operations); + return operations; + } + + private void addOperationModelImportInfomation(Map operations) { + // This method will add extra infomation to the operations.imports array. + // The api template uses this infomation to import all the required + // models for a given operation. + List> imports = (List>) operations.get("imports"); + for (Map im : imports) { + im.put("className", im.get("import").toString().replace("models.", "")); + } + } + + private void updateOperationParameterEnumInformation(Map operations) { + // This method will add extra infomation as to whether or not we have enums and + // update their names with the operation.id prefixed. + Map _operations = (Map) operations.get("operations"); + List operationList = (List) _operations.get("operation"); + boolean hasEnum = false; + for (CodegenOperation op : operationList) { + for (CodegenParameter param : op.allParams) { + 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 addExtraReservedWords() { + this.reservedWords.add("BASE_PATH"); + this.reservedWords.add("BaseAPI"); + this.reservedWords.add("RequiredError"); + this.reservedWords.add("COLLECTION_FORMATS"); + this.reservedWords.add("ConfigurationParameters"); + 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("RequestArgs"); + this.reservedWords.add("RequestOpts"); + this.reservedWords.add("exists"); + this.reservedWords.add("RequestContext"); + this.reservedWords.add("ResponseContext"); + this.reservedWords.add("Middleware"); + this.reservedWords.add("AjaxResponse"); + } +} 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 910a76f04e2c..2bede3cb61eb 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 @@ -101,3 +101,4 @@ org.openapitools.codegen.languages.TypeScriptFetchClientCodegen org.openapitools.codegen.languages.TypeScriptInversifyClientCodegen org.openapitools.codegen.languages.TypeScriptJqueryClientCodegen org.openapitools.codegen.languages.TypeScriptNodeClientCodegen +org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/README.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/README.mustache new file mode 100644 index 000000000000..4cf502cb67a4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/README.mustache @@ -0,0 +1,45 @@ +## {{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 should be automatically resolved via `package.json`. ([Reference](http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.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-rxjs/apis.index.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/apis.index.mustache new file mode 100644 index 000000000000..6286332d7bcf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/apis.index.mustache @@ -0,0 +1,7 @@ +{{#apiInfo}} +{{#apis}} +{{#operations}} +export * from './{{ classFilename }}'; +{{/operations}} +{{/apis}} +{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/apis.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/apis.mustache new file mode 100644 index 000000000000..dc24a59b68ce --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/apis.mustache @@ -0,0 +1,230 @@ +// tslint:disable +{{>licenseInfo}} +import { Observable } from 'rxjs'; +import { BaseAPI, RequiredError, HttpHeaders, HttpQuery, COLLECTION_FORMATS } from '../runtime'; +{{#imports.0}} +import { + {{#imports}} + {{className}}, + {{/imports}} +} from '../models'; +{{/imports.0}} + +{{#operations}} +{{#operation}} +{{#allParams.0}} +export interface {{operationIdCamelCase}}Request { + {{#allParams}} + {{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}; + {{/allParams}} +} + +{{/allParams.0}} +{{/operation}} +{{/operations}} +{{#operations}} +/** + * {{#description}}{{{description}}}{{/description}}{{^description}}no description{{/description}} + */ +export class {{classname}} extends BaseAPI { + + {{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + */ + {{nickname}}Raw({{#allParams.0}}requestParameters: {{operationIdCamelCase}}Request{{/allParams.0}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{#allParams}} + {{#required}} + if (requestParameters.{{paramName}} === null || requestParameters.{{paramName}} === undefined) { + throw new RequiredError('{{paramName}}','Required parameter requestParameters.{{paramName}} was null or undefined when calling {{nickname}}.'); + } + + {{/required}} + {{/allParams}} + const queryParameters: HttpQuery = {}; + + {{#queryParams}} + {{#isListContainer}} + if (requestParameters.{{paramName}}) { + {{#isCollectionFormatMulti}} + queryParameters['{{baseName}}'] = requestParameters.{{paramName}}; + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + queryParameters['{{baseName}}'] = requestParameters.{{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]); + {{/isCollectionFormatMulti}} + } + + {{/isListContainer}} + {{^isListContainer}} + if (requestParameters.{{paramName}} !== undefined) { + {{#isDateTime}} + queryParameters['{{baseName}}'] = (requestParameters.{{paramName}} as any).toISOString(); + {{/isDateTime}} + {{^isDateTime}} + {{#isDate}} + queryParameters['{{baseName}}'] = (requestParameters.{{paramName}} as any).toISOString(); + {{/isDate}} + {{^isDate}} + queryParameters['{{baseName}}'] = requestParameters.{{paramName}}; + {{/isDate}} + {{/isDateTime}} + } + + {{/isListContainer}} + {{/queryParams}} + const headerParameters: HttpHeaders = {}; + + {{#bodyParam}} + {{^consumes}} + headerParameters['Content-Type'] = 'application/json'; + + {{/consumes}} + {{#consumes.0}} + headerParameters['Content-Type'] = '{{{mediaType}}}'; + + {{/consumes.0}} + {{/bodyParam}} + {{#headerParams}} + {{#isListContainer}} + if (requestParameters.{{paramName}}) { + headerParameters['{{baseName}}'] = requestParameters.{{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"])); + } + + {{/isListContainer}} + {{^isListContainer}} + if (requestParameters.{{paramName}} !== undefined && requestParameters.{{paramName}} !== null) { + headerParameters['{{baseName}}'] = String(requestParameters.{{paramName}}); + } + + {{/isListContainer}} + {{/headerParams}} + {{#authMethods}} + {{#isBasic}} + if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) { + headerParameters["Authorization"] = "Basic " + btoa(this.configuration.username + ":" + this.configuration.password); + } + + {{/isBasic}} + {{#isApiKey}} + {{#isKeyInHeader}} + if (this.configuration && this.configuration.apiKey) { + headerParameters["{{keyParamName}}"] = this.configuration.apiKey("{{keyParamName}}"); // {{name}} authentication + } + + {{/isKeyInHeader}} + {{#isKeyInQuery}} + if (this.configuration && this.configuration.apiKey) { + queryParameters["{{keyParamName}}"] = this.configuration.apiKey("{{keyParamName}}"); // {{name}} authentication + } + + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isOAuth}} + if (this.configuration && this.configuration.accessToken) { + // oauth required + if (typeof this.configuration.accessToken === 'function') { + headerParameters["Authorization"] = this.configuration.accessToken("{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}]); + } else { + headerParameters["Authorization"] = this.configuration.accessToken; + } + } + + {{/isOAuth}} + {{/authMethods}} + {{#hasFormParams}} + const formData = new FormData(); + {{/hasFormParams}} + {{#formParams}} + {{#isListContainer}} + if (requestParameters.{{paramName}}) { + {{#isCollectionFormatMulti}} + requestParameters.{{paramName}}.forEach((element) => { + formData.append('{{baseName}}', element as any); + }) + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + formData.append('{{baseName}}', requestParameters.{{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"])); + {{/isCollectionFormatMulti}} + } + + {{/isListContainer}} + {{^isListContainer}} + if (requestParameters.{{paramName}} !== undefined) { + formData.append('{{baseName}}', requestParameters.{{paramName}} as any); + } + + {{/isListContainer}} + {{/formParams}} + return this.request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>({ + path: `{{{path}}}`{{#pathParams}}.replace(`{${"{{baseName}}"}}`, encodeURIComponent(String(requestParameters.{{paramName}}))){{/pathParams}}, + method: '{{httpMethod}}', + headers: headerParameters, + query: queryParameters, + {{#hasBodyParam}} + {{#bodyParam}} + {{#isContainer}} + body: requestParameters.{{paramName}}, + {{/isContainer}} + {{^isContainer}} + {{^isPrimitiveType}} + body: requestParameters.{{paramName}}, + {{/isPrimitiveType}} + {{#isPrimitiveType}} + body: requestParameters.{{paramName}} as any, + {{/isPrimitiveType}} + {{/isContainer}} + {{/bodyParam}} + {{/hasBodyParam}} + {{#hasFormParams}} + body: formData, + {{/hasFormParams}} + }); + } + + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + */ + {{nickname}}({{#allParams.0}}requestParameters: {{operationIdCamelCase}}Request{{/allParams.0}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{#returnType}} + return this.{{nickname}}Raw({{#allParams.0}}requestParameters{{/allParams.0}}); + {{/returnType}} + {{^returnType}} + this.{{nickname}}Raw({{#allParams.0}}requestParameters{{/allParams.0}}); + {{/returnType}} + } + + {{/operation}} +} +{{/operations}} +{{#hasEnums}} + +{{#operations}} +{{#operation}} +{{#allParams}} +{{#isEnum}} +/** + * @export + * @enum {string} + */ +export enum {{operationIdCamelCase}}{{enumName}} { +{{#allowableValues}} + {{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} +{{/isEnum}} +{{/allParams}} +{{/operation}} +{{/operations}} +{{/hasEnums}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/index.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/index.mustache new file mode 100644 index 000000000000..848ecfa4d100 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/index.mustache @@ -0,0 +1,3 @@ +export * from './runtime'; +export * from './apis'; +export * from './models'; diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/licenseInfo.mustache new file mode 100644 index 000000000000..469fb03940fe --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/licenseInfo.mustache @@ -0,0 +1,11 @@ +/** + * {{{appName}}} + * {{{appDescription}}} + * + * {{#version}}OpenAPI spec version: {{{version}}}{{/version}} + * {{#infoEmail}}Contact: {{{infoEmail}}}{{/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-rxjs/modelEnum.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/modelEnum.mustache new file mode 100644 index 000000000000..dc04cb0bd63d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/modelEnum.mustache @@ -0,0 +1,12 @@ +/** + * {{{description}}} + * @export + * @enum {string} + */ +export enum {{classname}} { +{{#allowableValues}} +{{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} +{{/enumVars}} +{{/allowableValues}} +} diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/modelGeneric.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/modelGeneric.mustache new file mode 100644 index 000000000000..331b03ea1d30 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/modelGeneric.mustache @@ -0,0 +1,43 @@ +{{#hasImports}} +import { + {{#imports}} + {{{.}}}, + {{/imports}} +} from './'; + +{{/hasImports}} +/**{{#description}} + * {{{description}}}{{/description}} + * @export + * @interface {{classname}} + */ +export interface {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{ +{{#additionalPropertiesType}} + [key: string]: {{{additionalPropertiesType}}}{{#hasVars}} | any{{/hasVars}}; +{{/additionalPropertiesType}} +{{#vars}} + /** + * {{{description}}} + * @type {{=<% %>=}}{<%&datatype%>}<%={{ }}=%> + * @memberof {{classname}} + */ + {{#isReadOnly}}readonly {{/isReadOnly}}{{name}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}; +{{/vars}} +}{{#hasEnums}} + +{{#vars}} +{{#isEnum}} +/** + * @export + * @enum {string} + */ +export enum {{enumName}} { +{{#allowableValues}} + {{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} +{{/isEnum}} +{{/vars}} +{{/hasEnums}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/models.index.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/models.index.mustache new file mode 100644 index 000000000000..02a39c248c4d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/models.index.mustache @@ -0,0 +1,5 @@ +{{#models}} +{{#model}} +export * from './{{{ classFilename }}}'; +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/models.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/models.mustache new file mode 100644 index 000000000000..ff9993dc14ca --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/models.mustache @@ -0,0 +1,12 @@ +// tslint:disable +{{>licenseInfo}} +{{#models}} +{{#model}} +{{#isEnum}} +{{>modelEnum}} +{{/isEnum}} +{{^isEnum}} +{{>modelGeneric}} +{{/isEnum}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/package.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/package.mustache new file mode 100644 index 000000000000..1abb2697c722 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/package.mustache @@ -0,0 +1,20 @@ +{ + "name": "{{npmName}}", + "version": "{{npmVersion}}", + "description": "OpenAPI client for {{npmName}}", + "author": "OpenAPI-Generator", + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", + "scripts" : { + "build": "tsc --outDir dist/", + "prepare": "npm run build" + }, + "devDependencies": { + "typescript": "^2.4" + }{{#npmRepository}},{{/npmRepository}} +{{#npmRepository}} + "publishConfig":{ + "registry":"{{npmRepository}}" + } +{{/npmRepository}} +} diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/runtime.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/runtime.mustache new file mode 100644 index 000000000000..99e7717ef8ee --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/runtime.mustache @@ -0,0 +1,200 @@ +// tslint:disable +{{>licenseInfo}} +import { Observable, of } from 'rxjs'; +import { ajax, AjaxResponse } from 'rxjs/ajax'; +import { map, concatMap } from 'rxjs/operators'; +import { environment as env } from '../environments'; + +export const BASE_PATH = (env.api || '{{{basePath}}}').replace(/\/+$/, ''); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + private middleware: Middleware[]; + + constructor(protected configuration = new Configuration()) { + 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); + } + + protected request(context: RequestOpts): Observable { + return this.rxjsRequest(this.createRequestArgs(context)).pipe( + map((res) => { + if (res.status >= 200 && res.status < 300) { + return res.response as T; + } + throw res; + }) + ); + } + + private createRequestArgs(context: RequestOpts): RequestArgs { + 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 += '?' + querystring(context.query); + } + const body = context.body instanceof FormData ? context.body : JSON.stringify(context.body); + const options = { + method: context.method, + headers: context.headers, + body, + }; + return { url, options }; + } + + private rxjsRequest(params: RequestContext): Observable { + const preMiddlewares = this.middleware && this.middleware.filter((item) => item.pre); + const postMiddlewares = this.middleware && this.middleware.filter((item) => item.post); + + return of(params).pipe( + map((args) => { + if (preMiddlewares) { + preMiddlewares.forEach((mw) => (args = mw.pre({ ...args }))); + } + return args; + }), + concatMap((args) => + ajax({ url: args.url, ...args.options }).pipe( + map((response) => { + if (postMiddlewares) { + postMiddlewares.forEach((mw) => (response = mw.post({ ...params, response: 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; + } +} + +export class RequiredError extends Error { + name: 'RequiredError' = 'RequiredError'; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ',', + ssv: ' ', + tsv: '\t', + pipes: '|', +}; + +export interface ConfigurationParameters { + basePath?: string; // override base path + middleware?: Middleware[]; // middleware to apply before/after rxjs requests + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | ((name: string) => string); // parameter for apiKey security + accessToken?: string | ((name: string, scopes?: string[]) => string); // parameter for oauth2 security +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + get basePath(): string { + return this.configuration.basePath || BASE_PATH; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name: string, scopes?: string[]) => string) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : () => accessToken; + } + return undefined; + } +} + +export type Json = any; +export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'; +export type HttpHeaders = { [key: string]: string }; +export type HttpQuery = { [key: string]: string | number | null | boolean | Array }; +export type HttpBody = Json | FormData; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export interface RequestArgs { + url: string; + options: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HttpMethod; + headers: HttpHeaders; + query?: HttpQuery; + body?: HttpBody; +} + +export function querystring(params: HttpQuery) { + return Object.keys(params) + .map((key) => { + const value = params[key]; + if (value instanceof Array) { + const multiValue = value.join(`&${encodeURIComponent(key)}=`); + return `${encodeURIComponent(key)}=${multiValue}`; + } + return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`; + }) + .join('&'); +} + +export interface RequestContext extends RequestArgs {} +export interface ResponseContext extends RequestArgs { + response: AjaxResponse; +} + +export interface Middleware { + pre?(context: RequestContext): RequestArgs; + post?(context: ResponseContext): AjaxResponse; +} diff --git a/modules/openapi-generator/src/main/resources/typescript-rxjs/tsconfig.mustache b/modules/openapi-generator/src/main/resources/typescript-rxjs/tsconfig.mustache new file mode 100644 index 000000000000..33d10cd9f5e0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-rxjs/tsconfig.mustache @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "declaration": true, + "target": "{{#supportsES6}}es6{{/supportsES6}}{{^supportsES6}}es5{{/supportsES6}}", + "module": "commonjs", + "moduleResolution": "node", + "outDir": "dist", + "rootDir": "."{{^supportsES6}}, + "lib": [ + "es6", + "dom" + ] + {{/supportsES6}} + }, + "exclude": [ + "dist", + "node_modules" + ] +}