diff --git a/bin/configs/typescript-rxjs-allOf-composition.yaml b/bin/configs/typescript-rxjs-allOf-composition.yaml new file mode 100644 index 00000000000..e9bca87aa19 --- /dev/null +++ b/bin/configs/typescript-rxjs-allOf-composition.yaml @@ -0,0 +1,4 @@ +generatorName: typescript-rxjs +outputDir: samples/client/others/typescript-rxjs/allOf-composition +inputSpec: modules/openapi-generator/src/test/resources/3_0/allOf_composition.yaml +templateDir: modules/openapi-generator/src/main/resources/typescript-rxjs 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 index e9091517b3b..aeaca5caf3c 100644 --- 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 @@ -18,6 +18,7 @@ package org.openapitools.codegen.languages; import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.parser.util.SchemaTypeUtil; import org.openapitools.codegen.*; import org.openapitools.codegen.meta.features.DocumentationFeature; @@ -420,4 +421,10 @@ public class TypeScriptRxjsClientCodegen extends AbstractTypeScriptClientCodegen this.hasOptionalQueryParams = false; // will be updated within addConditionalImportInformation } } + + @Override + protected void addImport(ComposedSchema composed, Schema childSchema, CodegenModel model, String modelName) { + // import everything (including child schema of a composed schema) + addImport(model, modelName); + } } diff --git a/samples/client/others/typescript-rxjs/allOf-composition/.gitignore b/samples/client/others/typescript-rxjs/allOf-composition/.gitignore new file mode 100644 index 00000000000..149b5765472 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/samples/client/others/typescript-rxjs/allOf-composition/.openapi-generator-ignore b/samples/client/others/typescript-rxjs/allOf-composition/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/.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/others/typescript-rxjs/allOf-composition/.openapi-generator/FILES b/samples/client/others/typescript-rxjs/allOf-composition/.openapi-generator/FILES new file mode 100644 index 00000000000..89276d499a4 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/.openapi-generator/FILES @@ -0,0 +1,15 @@ +.gitignore +apis/DefaultApi.ts +apis/index.ts +index.ts +models/Hero.ts +models/Human.ts +models/SuperBaby.ts +models/SuperBabyAllOf.ts +models/SuperBoy.ts +models/SuperBoyAllOf.ts +models/SuperMan.ts +models/index.ts +runtime.ts +servers.ts +tsconfig.json diff --git a/samples/client/others/typescript-rxjs/allOf-composition/.openapi-generator/VERSION b/samples/client/others/typescript-rxjs/allOf-composition/.openapi-generator/VERSION new file mode 100644 index 00000000000..ed829dbcdde --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.2.1-SNAPSHOT \ No newline at end of file diff --git a/samples/client/others/typescript-rxjs/allOf-composition/apis/DefaultApi.ts b/samples/client/others/typescript-rxjs/allOf-composition/apis/DefaultApi.ts new file mode 100644 index 00000000000..50216e7be70 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/apis/DefaultApi.ts @@ -0,0 +1,44 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 type { Observable } from 'rxjs'; +import type { AjaxResponse } from 'rxjs/ajax'; +import { BaseAPI, throwIfNullOrUndefined, encodeURI } from '../runtime'; +import type { OperationOpts } from '../runtime'; +import type { + SuperMan, +} from '../models'; + +export interface ListRequest { + personId: string; +} + +/** + * no description + */ +export class DefaultApi extends BaseAPI { + + /** + */ + list({ personId }: ListRequest): Observable + list({ personId }: ListRequest, opts?: OperationOpts): Observable> + list({ personId }: ListRequest, opts?: OperationOpts): Observable> { + throwIfNullOrUndefined(personId, 'personId', 'list'); + + return this.request({ + url: '/person/display/{personId}'.replace('{personId}', encodeURI(personId)), + method: 'GET', + }, opts?.responseOpts); + }; + +} diff --git a/samples/client/others/typescript-rxjs/allOf-composition/apis/index.ts b/samples/client/others/typescript-rxjs/allOf-composition/apis/index.ts new file mode 100644 index 00000000000..a1aa4698ff2 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/apis/index.ts @@ -0,0 +1 @@ +export * from './DefaultApi'; diff --git a/samples/client/others/typescript-rxjs/allOf-composition/index.ts b/samples/client/others/typescript-rxjs/allOf-composition/index.ts new file mode 100644 index 00000000000..b9e2f3ca3b7 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/index.ts @@ -0,0 +1,4 @@ +export * from './runtime'; +export * from './servers'; +export * from './apis'; +export * from './models'; diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/Hero.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/Hero.ts new file mode 100644 index 00000000000..98c39a6ecd6 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/Hero.ts @@ -0,0 +1,30 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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. + */ + +/** + * Hero + * @export + * @interface Hero + */ +export interface Hero { + /** + * @type {number} + * @memberof Hero + */ + reward?: number; + /** + * @type {string} + * @memberof Hero + */ + origin: string; +} diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/Human.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/Human.ts new file mode 100644 index 00000000000..2122f4c7a13 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/Human.ts @@ -0,0 +1,30 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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. + */ + +/** + * Human + * @export + * @interface Human + */ +export interface Human { + /** + * @type {number} + * @memberof Human + */ + id: number; + /** + * @type {string} + * @memberof Human + */ + name?: string; +} diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBaby.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBaby.ts new file mode 100644 index 00000000000..1647f9aa201 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBaby.ts @@ -0,0 +1,23 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 type { + Human, + SuperBabyAllOf, +} from './'; + +/** + * @type SuperBaby + * @export + */ +export type SuperBaby = Human & SuperBabyAllOf; diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBabyAllOf.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBabyAllOf.ts new file mode 100644 index 00000000000..459c28d0f4e --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBabyAllOf.ts @@ -0,0 +1,29 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 + * @interface SuperBabyAllOf + */ +export interface SuperBabyAllOf { + /** + * @type {string} + * @memberof SuperBabyAllOf + */ + gender?: string; + /** + * @type {number} + * @memberof SuperBabyAllOf + */ + age?: number; +} diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBoy.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBoy.ts new file mode 100644 index 00000000000..668faac8d17 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBoy.ts @@ -0,0 +1,23 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 type { + Human, + SuperBoyAllOf, +} from './'; + +/** + * @type SuperBoy + * @export + */ +export type SuperBoy = Human & SuperBoyAllOf; diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBoyAllOf.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBoyAllOf.ts new file mode 100644 index 00000000000..26003b90c7d --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperBoyAllOf.ts @@ -0,0 +1,29 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 + * @interface SuperBoyAllOf + */ +export interface SuperBoyAllOf { + /** + * @type {string} + * @memberof SuperBoyAllOf + */ + category?: string; + /** + * @type {number} + * @memberof SuperBoyAllOf + */ + level: number; +} diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/SuperMan.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperMan.ts new file mode 100644 index 00000000000..8548fb03519 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/SuperMan.ts @@ -0,0 +1,24 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 type { + Hero, + Human, + SuperBoyAllOf, +} from './'; + +/** + * @type SuperMan + * @export + */ +export type SuperMan = Hero & Human & SuperBoyAllOf; diff --git a/samples/client/others/typescript-rxjs/allOf-composition/models/index.ts b/samples/client/others/typescript-rxjs/allOf-composition/models/index.ts new file mode 100644 index 00000000000..d57bd6be7b9 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/models/index.ts @@ -0,0 +1,7 @@ +export * from './Hero'; +export * from './Human'; +export * from './SuperBaby'; +export * from './SuperBabyAllOf'; +export * from './SuperBoy'; +export * from './SuperBoyAllOf'; +export * from './SuperMan'; diff --git a/samples/client/others/typescript-rxjs/allOf-composition/runtime.ts b/samples/client/others/typescript-rxjs/allOf-composition/runtime.ts new file mode 100644 index 00000000000..64b0b2b4ad4 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/runtime.ts @@ -0,0 +1,193 @@ +// tslint:disable +/** + * Example + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 { of } from 'rxjs'; +import type { Observable } from 'rxjs'; +import { ajax } from 'rxjs/ajax'; +import type { AjaxConfig, AjaxResponse } from 'rxjs/ajax'; +import { map, concatMap } from 'rxjs/operators'; +import { servers } from './servers'; + +export const BASE_PATH = servers[0].getUrl(); + +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; + return apiKey ? (typeof apiKey === 'string' ? () => apiKey : apiKey) : undefined; + } + + get accessToken(): ((name: string, scopes?: string[]) => string) | undefined { + const { accessToken } = this.configuration; + return accessToken ? (typeof accessToken === 'string' ? () => accessToken : accessToken) : undefined; + } +} + +/** + * 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 = (middlewares: Middleware[]): this => { + const next = this.clone(); + next.middleware = next.middleware.concat(middlewares); + return next; + }; + + withPreMiddleware = (preMiddlewares: Array) => + this.withMiddleware(preMiddlewares.map((pre) => ({ pre }))); + + withPostMiddleware = (postMiddlewares: Array) => + this.withMiddleware(postMiddlewares.map((post) => ({ post }))); + + protected request(requestOpts: RequestOpts): Observable + protected request(requestOpts: RequestOpts, responseOpts?: ResponseOpts): Observable> + protected request(requestOpts: RequestOpts, responseOpts?: ResponseOpts): Observable> { + return this.rxjsRequest(this.createRequestArgs(requestOpts)).pipe( + map((res) => { + const { status, response } = res; + if (status >= 200 && status < 300) { + return responseOpts?.response === 'raw' ? res : response; + } + throw res; + }) + ); + } + + private createRequestArgs = ({ url: baseUrl, query, method, headers, body, responseType }: RequestOpts): AjaxConfig => { + // 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. + const url = `${this.configuration.basePath}${baseUrl}${query && Object.keys(query).length ? `?${queryString(query)}`: ''}`; + + return { + url, + method, + headers, + body: body instanceof FormData ? body : JSON.stringify(body), + responseType: responseType ?? 'json', + }; + } + + private rxjsRequest = (params: AjaxConfig): Observable> => + of(params).pipe( + map((request) => { + this.middleware.filter((item) => item.pre).forEach((mw) => (request = mw.pre!(request))); + return request; + }), + concatMap((args) => + ajax(args).pipe( + map((response) => { + this.middleware.filter((item) => item.post).forEach((mw) => (response = mw.post!(response))); + return response; + }) + ) + ) + ); + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone = (): this => + Object.assign(Object.create(Object.getPrototypeOf(this)), this); +} + +/** + * @deprecated + * export for not being a breaking change + */ +export class RequiredError extends Error { + override name: 'RequiredError' = 'RequiredError'; +} + +export const COLLECTION_FORMATS = { + csv: ',', + ssv: ' ', + tsv: '\t', + pipes: '|', +}; + +export type Json = any; +export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HttpHeaders = { [key: string]: string }; +export type HttpQuery = Partial<{ [key: string]: string | number | null | boolean | Array }>; // partial is needed for strict mode +export type HttpBody = Json | FormData; + +export interface RequestOpts extends AjaxConfig { + // TODO: replace custom 'query' prop with 'queryParams' + query?: HttpQuery; // additional prop + // the following props have improved types over AjaxRequest + method: HttpMethod; + headers?: HttpHeaders; + body?: HttpBody; +} + +export interface ResponseOpts { + response?: 'raw'; +} + +export interface OperationOpts { + responseOpts?: ResponseOpts; +} + +export const encodeURI = (value: any) => encodeURIComponent(`${value}`); + +const queryString = (params: HttpQuery): string => Object.entries(params) + .map(([key, value]) => value instanceof Array + ? value.map((val) => `${encodeURI(key)}=${encodeURI(val)}`).join('&') + : `${encodeURI(key)}=${encodeURI(value)}` + ) + .join('&'); + +export const throwIfNullOrUndefined = (value: any, paramName: string, nickname: string) => { + if (value == null) { + throw new Error(`Parameter "${paramName}" was null or undefined when calling "${nickname}".`); + } +}; + +export interface Middleware { + pre?(request: AjaxConfig): AjaxConfig; + post?(response: AjaxResponse): AjaxResponse; +} diff --git a/samples/client/others/typescript-rxjs/allOf-composition/servers.ts b/samples/client/others/typescript-rxjs/allOf-composition/servers.ts new file mode 100644 index 00000000000..3d22265d43f --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/servers.ts @@ -0,0 +1,43 @@ +/** + * + * Represents the configuration of a server including its + * url template and variable configuration based on the url. + * + */ +export class ServerConfiguration { + public constructor(private url: string, private variableConfiguration: T, private description: string) {} + + /** + * Sets the value of the variables of this server. + * + * @param variableConfiguration a partial variable configuration for the variables contained in the url + */ + public setVariables(variableConfiguration: Partial) { + Object.assign(this.variableConfiguration, variableConfiguration); + } + + public getConfiguration(): T { + return this.variableConfiguration; + } + + public getDescription(): string { + return this.description; + } + + /** + * Constructions the URL this server using the url with variables + * replaced with their respective values + */ + public getUrl(): string { + let replacedUrl = this.url; + for (const key in this.variableConfiguration) { + var re = new RegExp("{" + key + "}","g"); + replacedUrl = replacedUrl.replace(re, this.variableConfiguration[key]); + } + return replacedUrl; + } +} + +const server1 = new ServerConfiguration<{ }>("http://api.example.xyz/v1", { }, ""); + +export const servers = [server1]; diff --git a/samples/client/others/typescript-rxjs/allOf-composition/tsconfig.json b/samples/client/others/typescript-rxjs/allOf-composition/tsconfig.json new file mode 100644 index 00000000000..59a60838a05 --- /dev/null +++ b/samples/client/others/typescript-rxjs/allOf-composition/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "declaration": true, + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "outDir": "dist", + "rootDir": ".", + "lib": [ + "es6", + "dom", + "es2017" + ], + "typeRoots": [ + "node_modules/@types" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +}