Skip to content

Commit 156c4bf

Browse files
denyojvandort
andauthored
[typescript-rxjs] add support for raw response and progressSubscriber (#5465)
* feat(typescript-rxjs): add support for returning statusCode and progressSubscriber via function overloading * feat(typescript-rxjs): use ?? instead of || to support relative basePath of "", upgrade to typescript 3.7 * feat(typescript-rxjs): regenerate samples * refactor(typescript-rxjs): change explicit undefined checks to shorthand return * feat(typescript-rxjs): add missing progressSubscriber key when building RequestArgs * feat(typescript-rxjs): regenerate samples * style(typescript-rxjs): remove whitespace, add colons * feat(typescript-rxjs): regenerate samples * refactor(typescript-rxjs): destructure configuration in BaseApi * fix(typescript-rxjs): returning empty string for apiKey and accessToken * feat(typescript-rxjs): replace withStatusCode option with response = raw option, reuse rxjs AjaxRequest and AjaxResponse types * feat(typescript-rxjs): regenerate samples * Prints out the parameter name in throwIfNullOrUndefined * Fixed misspelling * feat(typescript-rxjs): add withProgressSubscriber additional-properties flag to cli, remove unused withInterfaces flag * refactor(typescript-rxjs): use backticks instead of String constructor in encodeURI * feat(typescript-rxjs): replace Object.keys() with Object.entries() in queryString helper * style(typescript-rxjs): improve indentation of new withProgressSubscriber checks within templates * feat(typescript-rxjs): use entire es2017 lib in tsconfig.json for building with target es6 * feat(typescript-rxjs): regenerate samples * feat(typescript-rxjs): adjust sample generation, regenerate samples * docs(typescript-rxjs): regenerate docs Co-authored-by: Justin Van Dort <justinvandort@gmail.com>
1 parent d9a6f4d commit 156c4bf

44 files changed

Lines changed: 965 additions & 707 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
generatorName: typescript-rxjs
2-
outputDir: samples/client/petstore/typescript-rxjs/builds/with-interfaces
2+
outputDir: samples/client/petstore/typescript-rxjs/builds/with-progress-subscriber
33
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
44
additionalProperties:
5-
withInterfaces: "true"
5+
withProgressSubscriber: "true"

docs/generators/typescript-rxjs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ sidebar_label: typescript-rxjs
2121
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
2222
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
2323
|supportsES6|Generate code that conforms to ES6.| |false|
24-
|withInterfaces|Setting this property to true will generate interfaces next to the default class implementations.| |false|
24+
|withProgressSubscriber|Setting this property to true will generate API controller methods with support for subscribing to request progress.| |false|
2525

2626
## IMPORT MAPPING
2727

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptRxjsClientCodegen.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class TypeScriptRxjsClientCodegen extends AbstractTypeScriptClientCodegen
3737
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTypeScriptClientCodegen.class);
3838

3939
public static final String NPM_REPOSITORY = "npmRepository";
40-
public static final String WITH_INTERFACES = "withInterfaces";
40+
public static final String WITH_PROGRESS_SUBSCRIBER = "withProgressSubscriber";
4141

4242
protected String npmRepository = null;
4343
protected Set<String> reservedParamNames = new HashSet<>();
@@ -60,7 +60,7 @@ public TypeScriptRxjsClientCodegen() {
6060
typeMapping.put("file", "Blob");
6161

6262
this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json"));
63-
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()));
63+
this.cliOptions.add(new CliOption(WITH_PROGRESS_SUBSCRIBER, "Setting this property to true will generate API controller methods with support for subscribing to request progress.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
6464

6565
// these are used in the api template for more efficient destructuring
6666
this.reservedParamNames.add("headers");

modules/openapi-generator/src/main/resources/typescript-rxjs/apis.mustache

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// tslint:disable
22
{{>licenseInfo}}
33
import { Observable } from 'rxjs';
4-
import { BaseAPI{{#hasHttpHeaders}}, HttpHeaders{{/hasHttpHeaders}}{{#hasQueryParams}}, HttpQuery{{/hasQueryParams}}{{#hasRequiredParams}}, throwIfNullOrUndefined{{/hasRequiredParams}}{{#hasPathParams}}, encodeURI{{/hasPathParams}}{{#hasListContainers}}, COLLECTION_FORMATS{{/hasListContainers}} } from '../runtime';
4+
import { BaseAPI{{#hasHttpHeaders}}, HttpHeaders{{/hasHttpHeaders}}{{#hasQueryParams}}, HttpQuery{{/hasQueryParams}}{{#hasRequiredParams}}, throwIfNullOrUndefined{{/hasRequiredParams}}{{#hasPathParams}}, encodeURI{{/hasPathParams}}{{#hasListContainers}}, COLLECTION_FORMATS{{/hasListContainers}}, OperationOpts, RawAjaxResponse } from '../runtime';
55
{{#imports.0}}
66
import {
77
{{#imports}}
@@ -37,7 +37,12 @@ export class {{classname}} extends BaseAPI {
3737
* {{&summary}}
3838
{{/summary}}
3939
*/
40-
{{nickname}} = ({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request{{/allParams.0}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> => {
40+
{{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request{{/allParams.0}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>
41+
{{#withProgressSubscriber}}
42+
{{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request, {{/allParams.0}}opts?: Pick<OperationOpts, 'progressSubscriber'>): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>
43+
{{/withProgressSubscriber}}
44+
{{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request, {{/allParams.0}}opts?: OperationOpts): Observable<{{#returnType}}RawAjaxResponse<{{{returnType}}}>{{/returnType}}{{^returnType}}void | RawAjaxResponse<void>{{/returnType}}>
45+
{{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request, {{/allParams.0}}opts?: OperationOpts): Observable<{{#returnType}}{{{returnType}}} | RawAjaxResponse<{{{returnType}}}>{{/returnType}}{{^returnType}}void | RawAjaxResponse<void>{{/returnType}}> {
4146
{{#hasParams}}
4247
{{#allParams}}
4348
{{#required}}
@@ -177,7 +182,7 @@ export class {{classname}} extends BaseAPI {
177182

178183
{{/hasFormParams}}
179184
return this.request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>({
180-
path: '{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>, encodeURI({{> paramNamePartial}})){{/pathParams}},
185+
url: '{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>, encodeURI({{> paramNamePartial}})){{/pathParams}},
181186
method: '{{httpMethod}}',
182187
{{#hasHttpHeaders}}
183188
headers,
@@ -204,9 +209,12 @@ export class {{classname}} extends BaseAPI {
204209
body: formData,
205210
{{/hasFormParams}}
206211
{{#isResponseFile}}
207-
responseType: 'blob'
212+
responseType: 'blob',
208213
{{/isResponseFile}}
209-
});
214+
{{#withProgressSubscriber}}
215+
progressSubscriber: opts?.progressSubscriber,
216+
{{/withProgressSubscriber}}
217+
}, opts?.responseOpts);
210218
};
211219

212220
{{/operation}}

modules/openapi-generator/src/main/resources/typescript-rxjs/package.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
"main": "./dist/index.js",
77
"typings": "./dist/index.d.ts",
88
"scripts" : {
9-
"build": "tsc --outDir dist/",
9+
"build": "node_modules/.bin/tsc --outDir dist/",
1010
"prepare": "npm run build"
1111
},
1212
"dependencies": {
1313
"rxjs": "^6.3.3"
1414
},
1515
"devDependencies": {
16-
"typescript": "^3.0.1"
16+
"typescript": "^3.7"
1717
}{{#npmRepository}},{{/npmRepository}}
1818
{{#npmRepository}}
1919
"publishConfig": {

modules/openapi-generator/src/main/resources/typescript-rxjs/runtime.mustache

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// tslint:disable
22
{{>licenseInfo}}
3-
import { Observable, of } from 'rxjs';
3+
import { Observable, of, Subscriber } from 'rxjs';
44
import { ajax, AjaxRequest, AjaxResponse } from 'rxjs/ajax';
55
import { map, concatMap } from 'rxjs/operators';
66

@@ -19,11 +19,11 @@ export class Configuration {
1919
constructor(private configuration: ConfigurationParameters = {}) {}
2020

2121
get basePath(): string {
22-
return this.configuration.basePath || BASE_PATH;
22+
return this.configuration.basePath ?? BASE_PATH;
2323
}
2424

2525
get middleware(): Middleware[] {
26-
return this.configuration.middleware || [];
26+
return this.configuration.middleware ?? [];
2727
}
2828

2929
get username(): string | undefined {
@@ -36,18 +36,12 @@ export class Configuration {
3636

3737
get apiKey(): ((name: string) => string) | undefined {
3838
const { apiKey } = this.configuration;
39-
if (!apiKey) {
40-
return undefined;
41-
}
42-
return typeof apiKey === 'string' ? () => apiKey : apiKey;
39+
return apiKey ? (typeof apiKey === 'string' ? () => apiKey : apiKey) : undefined;
4340
}
4441

4542
get accessToken(): ((name: string, scopes?: string[]) => string) | undefined {
4643
const { accessToken } = this.configuration;
47-
if (!accessToken) {
48-
return undefined;
49-
}
50-
return typeof accessToken === 'string' ? () => accessToken : accessToken;
44+
return accessToken ? (typeof accessToken === 'string' ? () => accessToken : accessToken) : undefined;
5145
}
5246
}
5347

@@ -73,31 +67,35 @@ export class BaseAPI {
7367
withPostMiddleware = (postMiddlewares: Array<Middleware['post']>) =>
7468
this.withMiddleware(postMiddlewares.map((post) => ({ post })));
7569

76-
protected request = <T>(requestOpts: RequestOpts): Observable<T> =>
77-
this.rxjsRequest(this.createRequestArgs(requestOpts)).pipe(
70+
protected request<T>(requestOpts: RequestOpts): Observable<T>
71+
protected request<T>(requestOpts: RequestOpts, responseOpts?: ResponseOpts): Observable<RawAjaxResponse<T>>
72+
protected request<T>(requestOpts: RequestOpts, responseOpts?: ResponseOpts): Observable<T | RawAjaxResponse<T>> {
73+
return this.rxjsRequest(this.createRequestArgs(requestOpts)).pipe(
7874
map((res) => {
79-
if (res.status >= 200 && res.status < 300) {
80-
return res.response as T;
75+
const { status, response } = res;
76+
if (status >= 200 && status < 300) {
77+
return responseOpts?.respone === 'raw' ? res : response;
8178
}
8279
throw res;
8380
})
8481
);
82+
}
8583

86-
private createRequestArgs = (requestOpts: RequestOpts): RequestArgs => {
87-
let url = this.configuration.basePath + requestOpts.path;
88-
if (requestOpts.query !== undefined && Object.keys(requestOpts.query).length !== 0) {
89-
// only add the queryString to the URL if there are query parameters.
90-
// this is done to avoid urls ending with a '?' character which buggy webservers
91-
// do not handle correctly sometimes.
92-
url += '?' + queryString(requestOpts.query);
93-
}
84+
private createRequestArgs = ({ url: baseUrl, query, method, headers, body, responseType{{#withProgressSubscriber}}, progressSubscriber{{/withProgressSubscriber}} }: RequestOpts): RequestArgs => {
85+
// only add the queryString to the URL if there are query parameters.
86+
// this is done to avoid urls ending with a '?' character which buggy webservers
87+
// do not handle correctly sometimes.
88+
const url = `${this.configuration.basePath}${baseUrl}${query && Object.keys(query).length ? `?${queryString(query)}`: ''}`;
9489

9590
return {
9691
url,
97-
method: requestOpts.method,
98-
headers: requestOpts.headers,
99-
body: requestOpts.body instanceof FormData ? requestOpts.body : JSON.stringify(requestOpts.body),
100-
responseType: requestOpts.responseType || 'json',
92+
method,
93+
headers,
94+
body: body instanceof FormData ? body : JSON.stringify(body),
95+
responseType: responseType ?? 'json',
96+
{{#withProgressSubscriber}}
97+
progressSubscriber,
98+
{{/withProgressSubscriber}}
10199
};
102100
}
103101

@@ -146,24 +144,41 @@ export type HttpHeaders = { [key: string]: string };
146144
export type HttpQuery = Partial<{ [key: string]: string | number | null | boolean | Array<string | number | null | boolean> }>; // partial is needed for strict mode
147145
export type HttpBody = Json | FormData;
148146

149-
export interface RequestOpts {
150-
path: string;
147+
export interface RequestOpts extends AjaxRequest {
148+
query?: HttpQuery; // additional prop
149+
// the following props have improved types over AjaxRequest
151150
method: HttpMethod;
152151
headers?: HttpHeaders;
153-
query?: HttpQuery;
154152
body?: HttpBody;
155153
responseType?: 'json' | 'blob' | 'arraybuffer' | 'text';
154+
{{#withProgressSubscriber}}
155+
progressSubscriber?: Subscriber<ProgressEvent>;
156+
{{/withProgressSubscriber}}
157+
}
158+
159+
export interface ResponseOpts {
160+
respone?: 'raw';
161+
}
162+
163+
export interface OperationOpts {
164+
{{#withProgressSubscriber}}
165+
progressSubscriber?: Subscriber<ProgressEvent>;
166+
{{/withProgressSubscriber}}
167+
responseOpts?: ResponseOpts;
168+
}
169+
170+
// AjaxResponse with typed response
171+
export interface RawAjaxResponse<T> extends AjaxResponse {
172+
response: T;
156173
}
157174

158-
export const encodeURI = (value: any) => encodeURIComponent(String(value));
175+
export const encodeURI = (value: any) => encodeURIComponent(`${value}`);
159176

160-
const queryString = (params: HttpQuery): string => Object.keys(params)
161-
.map((key) => {
162-
const value = params[key];
163-
return (value instanceof Array)
164-
? value.map((val) => `${encodeURI(key)}=${encodeURI(val)}`).join('&')
165-
: `${encodeURI(key)}=${encodeURI(value)}`;
166-
})
177+
const queryString = (params: HttpQuery): string => Object.entries(params)
178+
.map(([key, value]) => value instanceof Array
179+
? value.map((val) => `${encodeURI(key)}=${encodeURI(val)}`).join('&')
180+
: `${encodeURI(key)}=${encodeURI(value)}`
181+
)
167182
.join('&');
168183

169184
// alias fallback for not being a breaking change

modules/openapi-generator/src/main/resources/typescript-rxjs/tsconfig.mustache

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
"moduleResolution": "node",
77
"outDir": "dist",
88
"rootDir": ".",
9-
{{^supportsES6}}
109
"lib": [
10+
{{^supportsES6}}
1111
"es6",
12-
"dom"
12+
{{/supportsES6}}
13+
"dom",
14+
"es2017"
1315
],
14-
{{/supportsES6}}
1516
"typeRoots": [
1617
"node_modules/@types"
1718
]

0 commit comments

Comments
 (0)