@goast/typescript 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/assets/client/angular/angular-service.utils.ts +62 -8
- package/assets/client/angular/request-builder.ts +372 -372
- package/cjs/lib/ast/nodes/type-alias.js +2 -0
- package/cjs/lib/generators/models/model-generator.js +16 -3
- package/cjs/lib/generators/services/angular-services/angular-service-generator.js +21 -3
- package/cjs/lib/generators/services/angular-services/refs.js +1 -0
- package/cjs/lib/generators/services/fetch-clients/fetch-client-generator.js +18 -3
- package/esm/lib/ast/nodes/type-alias.js +2 -0
- package/esm/lib/generators/models/model-generator.js +16 -3
- package/esm/lib/generators/services/angular-services/angular-service-generator.js +21 -3
- package/esm/lib/generators/services/angular-services/refs.js +1 -0
- package/esm/lib/generators/services/fetch-clients/fetch-client-generator.js +18 -3
- package/package.json +1 -1
- package/types/lib/generators/services/angular-services/refs.d.ts +7 -0
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# typescript
|
|
2
|
-
|
|
3
|
-
This library was generated with [Nx](https://nx.dev).
|
|
4
|
-
|
|
5
|
-
## Building
|
|
6
|
-
|
|
7
|
-
Run `nx build typescript` to build the library.
|
|
8
|
-
|
|
9
|
-
## Running unit tests
|
|
10
|
-
|
|
11
|
-
Run `nx test typescript` to execute the unit tests via [Jest](https://jestjs.io).
|
|
1
|
+
# typescript
|
|
2
|
+
|
|
3
|
+
This library was generated with [Nx](https://nx.dev).
|
|
4
|
+
|
|
5
|
+
## Building
|
|
6
|
+
|
|
7
|
+
Run `nx build typescript` to build the library.
|
|
8
|
+
|
|
9
|
+
## Running unit tests
|
|
10
|
+
|
|
11
|
+
Run `nx test typescript` to execute the unit tests via [Jest](https://jestjs.io).
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { HttpErrorResponse, HttpEvent, HttpResponse } from '@angular/common/http';
|
|
2
|
-
import { Observable, filter,
|
|
2
|
+
import { Observable, Subscription, filter, take, map, catchError, EMPTY } from 'rxjs';
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export type AbortablePromise<T> = {
|
|
5
|
+
abort(): void;
|
|
6
|
+
then<TResult1 = T, TResult2 = never>(
|
|
7
|
+
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
8
|
+
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
|
|
9
|
+
): AbortablePromise<TResult1 | TResult2>;
|
|
10
|
+
catch<TResult = never>(
|
|
11
|
+
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
|
|
12
|
+
): AbortablePromise<T | TResult>;
|
|
13
|
+
finally(onfinally?: (() => void) | undefined | null): AbortablePromise<T>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function waitForResponse<T extends HttpResponse<any> | HttpErrorResponse>(
|
|
5
17
|
httpEvents: Observable<HttpEvent<any>>,
|
|
6
18
|
options: { errorResponseTypes: Record<number, 'text' | 'json'> },
|
|
7
|
-
):
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
): AbortablePromise<T> {
|
|
20
|
+
return firstValueFromAbortable<T>(
|
|
21
|
+
httpEvents.pipe(
|
|
22
|
+
filter((event) => event instanceof HttpResponse),
|
|
23
|
+
map((response) => response as T),
|
|
24
|
+
),
|
|
25
|
+
).catch((error) => {
|
|
12
26
|
if (error instanceof HttpErrorResponse) {
|
|
13
27
|
if (options.errorResponseTypes[error.status] === 'json' && typeof error.error === 'string') {
|
|
14
28
|
return new HttpErrorResponse({
|
|
@@ -22,5 +36,45 @@ export async function waitForResponse<T extends HttpResponse<any> | HttpErrorRes
|
|
|
22
36
|
return error as T;
|
|
23
37
|
}
|
|
24
38
|
throw error;
|
|
25
|
-
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function firstValueFromAbortable<T>(observable: Observable<T>): AbortablePromise<T> {
|
|
43
|
+
let subscription: Subscription | undefined = undefined;
|
|
44
|
+
let _reject: ((reason?: any) => void) | undefined = undefined;
|
|
45
|
+
const abortFn = () => {
|
|
46
|
+
if (!subscription) {
|
|
47
|
+
throw new Error('Not subscribed yet.');
|
|
48
|
+
}
|
|
49
|
+
subscription.unsubscribe();
|
|
50
|
+
_reject?.(new DOMException('The request has been aborted.', 'AbortError'));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return createAbortablePromise(
|
|
54
|
+
new Promise<T>((resolve, reject) => {
|
|
55
|
+
_reject = reject;
|
|
56
|
+
subscription = observable
|
|
57
|
+
.pipe(
|
|
58
|
+
take(1),
|
|
59
|
+
catchError((e: unknown) => {
|
|
60
|
+
reject(e);
|
|
61
|
+
return EMPTY;
|
|
62
|
+
}),
|
|
63
|
+
)
|
|
64
|
+
.subscribe((x: T) => resolve(x));
|
|
65
|
+
}),
|
|
66
|
+
abortFn,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createAbortablePromise<T>(promise: Promise<T>, abortFn: () => void) {
|
|
71
|
+
const thenFn = promise.then.bind(promise);
|
|
72
|
+
const catchFn = promise.catch.bind(promise);
|
|
73
|
+
const finallyFn = promise.finally?.bind(promise);
|
|
74
|
+
return Object.assign(promise, {
|
|
75
|
+
then: (...args: any[]) => createAbortablePromise(thenFn(...args), abortFn),
|
|
76
|
+
catch: (...args: any[]) => createAbortablePromise(catchFn(...args), abortFn),
|
|
77
|
+
finally: finallyFn ? (...args: any[]) => createAbortablePromise(finallyFn(...args), abortFn) : undefined,
|
|
78
|
+
abort: abortFn,
|
|
79
|
+
});
|
|
26
80
|
}
|
|
@@ -1,372 +1,372 @@
|
|
|
1
|
-
import { HttpRequest, HttpParameterCodec, HttpParams, HttpHeaders, HttpContext } from '@angular/common/http';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Custom parameter codec to correctly handle the plus sign in parameter
|
|
5
|
-
* values. See https://github.com/angular/angular/issues/18261
|
|
6
|
-
*/
|
|
7
|
-
class ParameterCodec implements HttpParameterCodec {
|
|
8
|
-
encodeKey(key: string): string {
|
|
9
|
-
return encodeURIComponent(key);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
encodeValue(value: string): string {
|
|
13
|
-
return encodeURIComponent(value);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
decodeKey(key: string): string {
|
|
17
|
-
return decodeURIComponent(key);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
decodeValue(value: string): string {
|
|
21
|
-
return decodeURIComponent(value);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
const ParameterCodecInstance = new ParameterCodec();
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Defines the options for appending a parameter
|
|
28
|
-
*/
|
|
29
|
-
interface ParameterOptions {
|
|
30
|
-
style?: string;
|
|
31
|
-
explode?: boolean;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Base class for a parameter
|
|
36
|
-
*/
|
|
37
|
-
abstract class Parameter {
|
|
38
|
-
constructor(
|
|
39
|
-
public name: string,
|
|
40
|
-
public value: any,
|
|
41
|
-
public options: ParameterOptions,
|
|
42
|
-
defaultStyle: string,
|
|
43
|
-
defaultExplode: boolean,
|
|
44
|
-
) {
|
|
45
|
-
this.options = options || {};
|
|
46
|
-
if (this.options.style === null || this.options.style === undefined) {
|
|
47
|
-
this.options.style = defaultStyle;
|
|
48
|
-
}
|
|
49
|
-
if (this.options.explode === null || this.options.explode === undefined) {
|
|
50
|
-
this.options.explode = defaultExplode;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
serializeValue(value: any, separator = ','): string {
|
|
55
|
-
if (value === null || value === undefined) {
|
|
56
|
-
return '';
|
|
57
|
-
} else if (value instanceof Array) {
|
|
58
|
-
return value
|
|
59
|
-
.map((v) => this.serializeValue(v).split(separator).join(encodeURIComponent(separator)))
|
|
60
|
-
.join(separator);
|
|
61
|
-
} else if (typeof value === 'object') {
|
|
62
|
-
const array: string[] = [];
|
|
63
|
-
for (const key of Object.keys(value)) {
|
|
64
|
-
let propVal = value[key];
|
|
65
|
-
if (propVal !== null && propVal !== undefined) {
|
|
66
|
-
propVal = this.serializeValue(propVal).split(separator).join(encodeURIComponent(separator));
|
|
67
|
-
if (this.options.explode) {
|
|
68
|
-
array.push(`${key}=${propVal}`);
|
|
69
|
-
} else {
|
|
70
|
-
array.push(key);
|
|
71
|
-
array.push(propVal);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return array.join(separator);
|
|
76
|
-
} else {
|
|
77
|
-
return String(value);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* A parameter in the operation path
|
|
84
|
-
*/
|
|
85
|
-
class PathParameter extends Parameter {
|
|
86
|
-
constructor(name: string, value: any, options: ParameterOptions) {
|
|
87
|
-
super(name, value, options, 'simple', false);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
append(path: string): string {
|
|
91
|
-
let value = this.value;
|
|
92
|
-
if (value === null || value === undefined) {
|
|
93
|
-
value = '';
|
|
94
|
-
}
|
|
95
|
-
let prefix = this.options.style === 'label' ? '.' : '';
|
|
96
|
-
const separator = this.options.explode ? (prefix === '' ? ',' : prefix) : ',';
|
|
97
|
-
let alreadySerialized = false;
|
|
98
|
-
if (this.options.style === 'matrix') {
|
|
99
|
-
// The parameter name is just used as prefix, except in some cases...
|
|
100
|
-
prefix = `;${this.name}=`;
|
|
101
|
-
if (this.options.explode && typeof value === 'object') {
|
|
102
|
-
prefix = ';';
|
|
103
|
-
if (value instanceof Array) {
|
|
104
|
-
// For arrays we have to repeat the name for each element
|
|
105
|
-
value = value.map((v) => `${this.name}=${this.serializeValue(v, ';')}`);
|
|
106
|
-
value = value.join(';');
|
|
107
|
-
alreadySerialized = true;
|
|
108
|
-
} else {
|
|
109
|
-
// For objects we have to put each the key / value pairs
|
|
110
|
-
value = this.serializeValue(value, ';');
|
|
111
|
-
alreadySerialized = true;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
value = prefix + (alreadySerialized ? value : this.serializeValue(value, separator));
|
|
116
|
-
// Replace both the plain variable and the corresponding variant taking in the prefix and explode into account
|
|
117
|
-
path = path.replace(`{${this.name}}`, value);
|
|
118
|
-
path = path.replace(`{${prefix}${this.name}${this.options.explode ? '*' : ''}}`, value);
|
|
119
|
-
return path;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
123
|
-
// @ts-ignore
|
|
124
|
-
serializeValue(value: any, separator = ','): string {
|
|
125
|
-
let result = typeof value === 'string' ? encodeURIComponent(value) : super.serializeValue(value, separator);
|
|
126
|
-
result = result.replace(/%3D/g, '=');
|
|
127
|
-
result = result.replace(/%3B/g, ';');
|
|
128
|
-
result = result.replace(/%2C/g, ',');
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* A parameter in the query
|
|
135
|
-
*/
|
|
136
|
-
class QueryParameter extends Parameter {
|
|
137
|
-
constructor(name: string, value: any, options: ParameterOptions) {
|
|
138
|
-
super(name, value, options, 'form', true);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
append(params: HttpParams): HttpParams {
|
|
142
|
-
if (this.value instanceof Array) {
|
|
143
|
-
// Array serialization
|
|
144
|
-
if (this.options.explode) {
|
|
145
|
-
for (const v of this.value) {
|
|
146
|
-
params = params.append(this.name, this.serializeValue(v));
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
const separator =
|
|
150
|
-
this.options.style === 'spaceDelimited' ? ' ' : this.options.style === 'pipeDelimited' ? '|' : ',';
|
|
151
|
-
return params.append(this.name, this.serializeValue(this.value, separator));
|
|
152
|
-
}
|
|
153
|
-
} else if (this.value !== null && typeof this.value === 'object') {
|
|
154
|
-
// Object serialization
|
|
155
|
-
if (this.options.style === 'deepObject') {
|
|
156
|
-
// Append a parameter for each key, in the form `name[key]`
|
|
157
|
-
for (const key of Object.keys(this.value)) {
|
|
158
|
-
const propVal = this.value[key];
|
|
159
|
-
if (propVal !== null && propVal !== undefined) {
|
|
160
|
-
params = params.append(`${this.name}[${key}]`, this.serializeValue(propVal));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
} else if (this.options.explode) {
|
|
164
|
-
// Append a parameter for each key without using the parameter name
|
|
165
|
-
for (const key of Object.keys(this.value)) {
|
|
166
|
-
const propVal = this.value[key];
|
|
167
|
-
if (propVal !== null && propVal !== undefined) {
|
|
168
|
-
params = params.append(key, this.serializeValue(propVal));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
} else {
|
|
172
|
-
// Append a single parameter whose values are a comma-separated list of key,value,key,value...
|
|
173
|
-
const array: any[] = [];
|
|
174
|
-
for (const key of Object.keys(this.value)) {
|
|
175
|
-
const propVal = this.value[key];
|
|
176
|
-
if (propVal !== null && propVal !== undefined) {
|
|
177
|
-
array.push(key);
|
|
178
|
-
array.push(propVal);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
params = params.append(this.name, this.serializeValue(array));
|
|
182
|
-
}
|
|
183
|
-
} else if (this.value !== null && this.value !== undefined) {
|
|
184
|
-
// Plain value
|
|
185
|
-
params = params.append(this.name, this.serializeValue(this.value));
|
|
186
|
-
}
|
|
187
|
-
return params;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* A parameter in the HTTP request header
|
|
193
|
-
*/
|
|
194
|
-
class HeaderParameter extends Parameter {
|
|
195
|
-
constructor(name: string, value: any, options: ParameterOptions) {
|
|
196
|
-
super(name, value, options, 'simple', false);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
append(headers: HttpHeaders): HttpHeaders {
|
|
200
|
-
if (this.value !== null && this.value !== undefined) {
|
|
201
|
-
if (this.value instanceof Array) {
|
|
202
|
-
for (const v of this.value) {
|
|
203
|
-
headers = headers.append(this.name, this.serializeValue(v));
|
|
204
|
-
}
|
|
205
|
-
} else {
|
|
206
|
-
headers = headers.append(this.name, this.serializeValue(this.value));
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return headers;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Helper to build http requests from parameters
|
|
215
|
-
*/
|
|
216
|
-
export class RequestBuilder {
|
|
217
|
-
private _path = new Map<string, PathParameter>();
|
|
218
|
-
private _query = new Map<string, QueryParameter>();
|
|
219
|
-
private _header = new Map<string, HeaderParameter>();
|
|
220
|
-
_bodyContent: any | null;
|
|
221
|
-
_bodyContentType?: string;
|
|
222
|
-
|
|
223
|
-
constructor(
|
|
224
|
-
public rootUrl: string,
|
|
225
|
-
public operationPath: string,
|
|
226
|
-
public method: string,
|
|
227
|
-
) {}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Sets a path parameter
|
|
231
|
-
*/
|
|
232
|
-
path(name: string, value: any, options?: ParameterOptions): void {
|
|
233
|
-
this._path.set(name, new PathParameter(name, value, options || {}));
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Sets a query parameter
|
|
238
|
-
*/
|
|
239
|
-
query(name: string, value: any, options?: ParameterOptions): void {
|
|
240
|
-
this._query.set(name, new QueryParameter(name, value, options || {}));
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Sets a header parameter
|
|
245
|
-
*/
|
|
246
|
-
header(name: string, value: any, options?: ParameterOptions): void {
|
|
247
|
-
this._header.set(name, new HeaderParameter(name, value, options || {}));
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Sets the body content, along with the content type
|
|
252
|
-
*/
|
|
253
|
-
body(value: any, contentType = 'application/json'): void {
|
|
254
|
-
if (value instanceof Blob) {
|
|
255
|
-
this._bodyContentType = value.type;
|
|
256
|
-
} else {
|
|
257
|
-
this._bodyContentType = contentType;
|
|
258
|
-
}
|
|
259
|
-
if (this._bodyContentType === 'application/x-www-form-urlencoded' && value !== null && typeof value === 'object') {
|
|
260
|
-
// Handle URL-encoded data
|
|
261
|
-
const pairs: Array<[string, string]> = [];
|
|
262
|
-
for (const key of Object.keys(value)) {
|
|
263
|
-
let val = value[key];
|
|
264
|
-
if (!(val instanceof Array)) {
|
|
265
|
-
val = [val];
|
|
266
|
-
}
|
|
267
|
-
for (const v of val) {
|
|
268
|
-
const formValue = this.formDataValue(v);
|
|
269
|
-
if (formValue !== null) {
|
|
270
|
-
pairs.push([key, formValue]);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
this._bodyContent = pairs.map((p) => `${encodeURIComponent(p[0])}=${encodeURIComponent(p[1])}`).join('&');
|
|
275
|
-
} else if (this._bodyContentType === 'multipart/form-data') {
|
|
276
|
-
// Handle multipart form data
|
|
277
|
-
const formData = new FormData();
|
|
278
|
-
if (value !== null && value !== undefined) {
|
|
279
|
-
for (const key of Object.keys(value)) {
|
|
280
|
-
const val = value[key];
|
|
281
|
-
if (val instanceof Array) {
|
|
282
|
-
for (const v of val) {
|
|
283
|
-
const toAppend = this.formDataValue(v);
|
|
284
|
-
if (toAppend !== null) {
|
|
285
|
-
formData.append(key, toAppend);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
} else {
|
|
289
|
-
const toAppend = this.formDataValue(val);
|
|
290
|
-
if (toAppend !== null) {
|
|
291
|
-
formData.set(key, toAppend);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
this._bodyContent = formData;
|
|
297
|
-
} else {
|
|
298
|
-
// The body is the plain content
|
|
299
|
-
this._bodyContent = value;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
private formDataValue(value: any): any {
|
|
304
|
-
if (value === null || value === undefined) {
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
if (value instanceof Blob) {
|
|
308
|
-
return value;
|
|
309
|
-
}
|
|
310
|
-
if (typeof value === 'object') {
|
|
311
|
-
return JSON.stringify(value);
|
|
312
|
-
}
|
|
313
|
-
return String(value);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Builds the request with the current set parameters
|
|
318
|
-
*/
|
|
319
|
-
build<T = any>(options?: {
|
|
320
|
-
/** Which content types to accept */
|
|
321
|
-
accept?: string;
|
|
322
|
-
|
|
323
|
-
/** The expected response type */
|
|
324
|
-
responseType?: 'json' | 'text' | 'blob' | 'arraybuffer';
|
|
325
|
-
|
|
326
|
-
/** Whether to report progress on uploads / downloads */
|
|
327
|
-
reportProgress?: boolean;
|
|
328
|
-
|
|
329
|
-
/** Allow passing HttpContext for HttpClient */
|
|
330
|
-
context?: HttpContext;
|
|
331
|
-
}): HttpRequest<T> {
|
|
332
|
-
options = options || {};
|
|
333
|
-
|
|
334
|
-
// Path parameters
|
|
335
|
-
let path = this.operationPath;
|
|
336
|
-
for (const pathParam of this._path.values()) {
|
|
337
|
-
path = pathParam.append(path);
|
|
338
|
-
}
|
|
339
|
-
const url = this.rootUrl + path;
|
|
340
|
-
|
|
341
|
-
// Query parameters
|
|
342
|
-
let httpParams = new HttpParams({
|
|
343
|
-
encoder: ParameterCodecInstance,
|
|
344
|
-
});
|
|
345
|
-
for (const queryParam of this._query.values()) {
|
|
346
|
-
httpParams = queryParam.append(httpParams);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Header parameters
|
|
350
|
-
let httpHeaders = new HttpHeaders();
|
|
351
|
-
if (options.accept) {
|
|
352
|
-
httpHeaders = httpHeaders.append('Accept', options.accept);
|
|
353
|
-
}
|
|
354
|
-
for (const headerParam of this._header.values()) {
|
|
355
|
-
httpHeaders = headerParam.append(httpHeaders);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Request content headers
|
|
359
|
-
if (this._bodyContentType && !(this._bodyContent instanceof FormData)) {
|
|
360
|
-
httpHeaders = httpHeaders.set('Content-Type', this._bodyContentType);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Perform the request
|
|
364
|
-
return new HttpRequest<T>(this.method.toUpperCase(), url, this._bodyContent, {
|
|
365
|
-
params: httpParams,
|
|
366
|
-
headers: httpHeaders,
|
|
367
|
-
responseType: options.responseType,
|
|
368
|
-
reportProgress: options.reportProgress,
|
|
369
|
-
context: options.context,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
1
|
+
import { HttpRequest, HttpParameterCodec, HttpParams, HttpHeaders, HttpContext } from '@angular/common/http';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Custom parameter codec to correctly handle the plus sign in parameter
|
|
5
|
+
* values. See https://github.com/angular/angular/issues/18261
|
|
6
|
+
*/
|
|
7
|
+
class ParameterCodec implements HttpParameterCodec {
|
|
8
|
+
encodeKey(key: string): string {
|
|
9
|
+
return encodeURIComponent(key);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
encodeValue(value: string): string {
|
|
13
|
+
return encodeURIComponent(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
decodeKey(key: string): string {
|
|
17
|
+
return decodeURIComponent(key);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
decodeValue(value: string): string {
|
|
21
|
+
return decodeURIComponent(value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const ParameterCodecInstance = new ParameterCodec();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Defines the options for appending a parameter
|
|
28
|
+
*/
|
|
29
|
+
interface ParameterOptions {
|
|
30
|
+
style?: string;
|
|
31
|
+
explode?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Base class for a parameter
|
|
36
|
+
*/
|
|
37
|
+
abstract class Parameter {
|
|
38
|
+
constructor(
|
|
39
|
+
public name: string,
|
|
40
|
+
public value: any,
|
|
41
|
+
public options: ParameterOptions,
|
|
42
|
+
defaultStyle: string,
|
|
43
|
+
defaultExplode: boolean,
|
|
44
|
+
) {
|
|
45
|
+
this.options = options || {};
|
|
46
|
+
if (this.options.style === null || this.options.style === undefined) {
|
|
47
|
+
this.options.style = defaultStyle;
|
|
48
|
+
}
|
|
49
|
+
if (this.options.explode === null || this.options.explode === undefined) {
|
|
50
|
+
this.options.explode = defaultExplode;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
serializeValue(value: any, separator = ','): string {
|
|
55
|
+
if (value === null || value === undefined) {
|
|
56
|
+
return '';
|
|
57
|
+
} else if (value instanceof Array) {
|
|
58
|
+
return value
|
|
59
|
+
.map((v) => this.serializeValue(v).split(separator).join(encodeURIComponent(separator)))
|
|
60
|
+
.join(separator);
|
|
61
|
+
} else if (typeof value === 'object') {
|
|
62
|
+
const array: string[] = [];
|
|
63
|
+
for (const key of Object.keys(value)) {
|
|
64
|
+
let propVal = value[key];
|
|
65
|
+
if (propVal !== null && propVal !== undefined) {
|
|
66
|
+
propVal = this.serializeValue(propVal).split(separator).join(encodeURIComponent(separator));
|
|
67
|
+
if (this.options.explode) {
|
|
68
|
+
array.push(`${key}=${propVal}`);
|
|
69
|
+
} else {
|
|
70
|
+
array.push(key);
|
|
71
|
+
array.push(propVal);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return array.join(separator);
|
|
76
|
+
} else {
|
|
77
|
+
return String(value);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A parameter in the operation path
|
|
84
|
+
*/
|
|
85
|
+
class PathParameter extends Parameter {
|
|
86
|
+
constructor(name: string, value: any, options: ParameterOptions) {
|
|
87
|
+
super(name, value, options, 'simple', false);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
append(path: string): string {
|
|
91
|
+
let value = this.value;
|
|
92
|
+
if (value === null || value === undefined) {
|
|
93
|
+
value = '';
|
|
94
|
+
}
|
|
95
|
+
let prefix = this.options.style === 'label' ? '.' : '';
|
|
96
|
+
const separator = this.options.explode ? (prefix === '' ? ',' : prefix) : ',';
|
|
97
|
+
let alreadySerialized = false;
|
|
98
|
+
if (this.options.style === 'matrix') {
|
|
99
|
+
// The parameter name is just used as prefix, except in some cases...
|
|
100
|
+
prefix = `;${this.name}=`;
|
|
101
|
+
if (this.options.explode && typeof value === 'object') {
|
|
102
|
+
prefix = ';';
|
|
103
|
+
if (value instanceof Array) {
|
|
104
|
+
// For arrays we have to repeat the name for each element
|
|
105
|
+
value = value.map((v) => `${this.name}=${this.serializeValue(v, ';')}`);
|
|
106
|
+
value = value.join(';');
|
|
107
|
+
alreadySerialized = true;
|
|
108
|
+
} else {
|
|
109
|
+
// For objects we have to put each the key / value pairs
|
|
110
|
+
value = this.serializeValue(value, ';');
|
|
111
|
+
alreadySerialized = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
value = prefix + (alreadySerialized ? value : this.serializeValue(value, separator));
|
|
116
|
+
// Replace both the plain variable and the corresponding variant taking in the prefix and explode into account
|
|
117
|
+
path = path.replace(`{${this.name}}`, value);
|
|
118
|
+
path = path.replace(`{${prefix}${this.name}${this.options.explode ? '*' : ''}}`, value);
|
|
119
|
+
return path;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
123
|
+
// @ts-ignore
|
|
124
|
+
serializeValue(value: any, separator = ','): string {
|
|
125
|
+
let result = typeof value === 'string' ? encodeURIComponent(value) : super.serializeValue(value, separator);
|
|
126
|
+
result = result.replace(/%3D/g, '=');
|
|
127
|
+
result = result.replace(/%3B/g, ';');
|
|
128
|
+
result = result.replace(/%2C/g, ',');
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A parameter in the query
|
|
135
|
+
*/
|
|
136
|
+
class QueryParameter extends Parameter {
|
|
137
|
+
constructor(name: string, value: any, options: ParameterOptions) {
|
|
138
|
+
super(name, value, options, 'form', true);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
append(params: HttpParams): HttpParams {
|
|
142
|
+
if (this.value instanceof Array) {
|
|
143
|
+
// Array serialization
|
|
144
|
+
if (this.options.explode) {
|
|
145
|
+
for (const v of this.value) {
|
|
146
|
+
params = params.append(this.name, this.serializeValue(v));
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
const separator =
|
|
150
|
+
this.options.style === 'spaceDelimited' ? ' ' : this.options.style === 'pipeDelimited' ? '|' : ',';
|
|
151
|
+
return params.append(this.name, this.serializeValue(this.value, separator));
|
|
152
|
+
}
|
|
153
|
+
} else if (this.value !== null && typeof this.value === 'object') {
|
|
154
|
+
// Object serialization
|
|
155
|
+
if (this.options.style === 'deepObject') {
|
|
156
|
+
// Append a parameter for each key, in the form `name[key]`
|
|
157
|
+
for (const key of Object.keys(this.value)) {
|
|
158
|
+
const propVal = this.value[key];
|
|
159
|
+
if (propVal !== null && propVal !== undefined) {
|
|
160
|
+
params = params.append(`${this.name}[${key}]`, this.serializeValue(propVal));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} else if (this.options.explode) {
|
|
164
|
+
// Append a parameter for each key without using the parameter name
|
|
165
|
+
for (const key of Object.keys(this.value)) {
|
|
166
|
+
const propVal = this.value[key];
|
|
167
|
+
if (propVal !== null && propVal !== undefined) {
|
|
168
|
+
params = params.append(key, this.serializeValue(propVal));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
// Append a single parameter whose values are a comma-separated list of key,value,key,value...
|
|
173
|
+
const array: any[] = [];
|
|
174
|
+
for (const key of Object.keys(this.value)) {
|
|
175
|
+
const propVal = this.value[key];
|
|
176
|
+
if (propVal !== null && propVal !== undefined) {
|
|
177
|
+
array.push(key);
|
|
178
|
+
array.push(propVal);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
params = params.append(this.name, this.serializeValue(array));
|
|
182
|
+
}
|
|
183
|
+
} else if (this.value !== null && this.value !== undefined) {
|
|
184
|
+
// Plain value
|
|
185
|
+
params = params.append(this.name, this.serializeValue(this.value));
|
|
186
|
+
}
|
|
187
|
+
return params;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* A parameter in the HTTP request header
|
|
193
|
+
*/
|
|
194
|
+
class HeaderParameter extends Parameter {
|
|
195
|
+
constructor(name: string, value: any, options: ParameterOptions) {
|
|
196
|
+
super(name, value, options, 'simple', false);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
append(headers: HttpHeaders): HttpHeaders {
|
|
200
|
+
if (this.value !== null && this.value !== undefined) {
|
|
201
|
+
if (this.value instanceof Array) {
|
|
202
|
+
for (const v of this.value) {
|
|
203
|
+
headers = headers.append(this.name, this.serializeValue(v));
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
headers = headers.append(this.name, this.serializeValue(this.value));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return headers;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Helper to build http requests from parameters
|
|
215
|
+
*/
|
|
216
|
+
export class RequestBuilder {
|
|
217
|
+
private _path = new Map<string, PathParameter>();
|
|
218
|
+
private _query = new Map<string, QueryParameter>();
|
|
219
|
+
private _header = new Map<string, HeaderParameter>();
|
|
220
|
+
_bodyContent: any | null;
|
|
221
|
+
_bodyContentType?: string;
|
|
222
|
+
|
|
223
|
+
constructor(
|
|
224
|
+
public rootUrl: string,
|
|
225
|
+
public operationPath: string,
|
|
226
|
+
public method: string,
|
|
227
|
+
) {}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Sets a path parameter
|
|
231
|
+
*/
|
|
232
|
+
path(name: string, value: any, options?: ParameterOptions): void {
|
|
233
|
+
this._path.set(name, new PathParameter(name, value, options || {}));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Sets a query parameter
|
|
238
|
+
*/
|
|
239
|
+
query(name: string, value: any, options?: ParameterOptions): void {
|
|
240
|
+
this._query.set(name, new QueryParameter(name, value, options || {}));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Sets a header parameter
|
|
245
|
+
*/
|
|
246
|
+
header(name: string, value: any, options?: ParameterOptions): void {
|
|
247
|
+
this._header.set(name, new HeaderParameter(name, value, options || {}));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Sets the body content, along with the content type
|
|
252
|
+
*/
|
|
253
|
+
body(value: any, contentType = 'application/json'): void {
|
|
254
|
+
if (value instanceof Blob) {
|
|
255
|
+
this._bodyContentType = value.type;
|
|
256
|
+
} else {
|
|
257
|
+
this._bodyContentType = contentType;
|
|
258
|
+
}
|
|
259
|
+
if (this._bodyContentType === 'application/x-www-form-urlencoded' && value !== null && typeof value === 'object') {
|
|
260
|
+
// Handle URL-encoded data
|
|
261
|
+
const pairs: Array<[string, string]> = [];
|
|
262
|
+
for (const key of Object.keys(value)) {
|
|
263
|
+
let val = value[key];
|
|
264
|
+
if (!(val instanceof Array)) {
|
|
265
|
+
val = [val];
|
|
266
|
+
}
|
|
267
|
+
for (const v of val) {
|
|
268
|
+
const formValue = this.formDataValue(v);
|
|
269
|
+
if (formValue !== null) {
|
|
270
|
+
pairs.push([key, formValue]);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
this._bodyContent = pairs.map((p) => `${encodeURIComponent(p[0])}=${encodeURIComponent(p[1])}`).join('&');
|
|
275
|
+
} else if (this._bodyContentType === 'multipart/form-data') {
|
|
276
|
+
// Handle multipart form data
|
|
277
|
+
const formData = new FormData();
|
|
278
|
+
if (value !== null && value !== undefined) {
|
|
279
|
+
for (const key of Object.keys(value)) {
|
|
280
|
+
const val = value[key];
|
|
281
|
+
if (val instanceof Array) {
|
|
282
|
+
for (const v of val) {
|
|
283
|
+
const toAppend = this.formDataValue(v);
|
|
284
|
+
if (toAppend !== null) {
|
|
285
|
+
formData.append(key, toAppend);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
const toAppend = this.formDataValue(val);
|
|
290
|
+
if (toAppend !== null) {
|
|
291
|
+
formData.set(key, toAppend);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
this._bodyContent = formData;
|
|
297
|
+
} else {
|
|
298
|
+
// The body is the plain content
|
|
299
|
+
this._bodyContent = value;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private formDataValue(value: any): any {
|
|
304
|
+
if (value === null || value === undefined) {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
if (value instanceof Blob) {
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
if (typeof value === 'object') {
|
|
311
|
+
return JSON.stringify(value);
|
|
312
|
+
}
|
|
313
|
+
return String(value);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Builds the request with the current set parameters
|
|
318
|
+
*/
|
|
319
|
+
build<T = any>(options?: {
|
|
320
|
+
/** Which content types to accept */
|
|
321
|
+
accept?: string;
|
|
322
|
+
|
|
323
|
+
/** The expected response type */
|
|
324
|
+
responseType?: 'json' | 'text' | 'blob' | 'arraybuffer';
|
|
325
|
+
|
|
326
|
+
/** Whether to report progress on uploads / downloads */
|
|
327
|
+
reportProgress?: boolean;
|
|
328
|
+
|
|
329
|
+
/** Allow passing HttpContext for HttpClient */
|
|
330
|
+
context?: HttpContext;
|
|
331
|
+
}): HttpRequest<T> {
|
|
332
|
+
options = options || {};
|
|
333
|
+
|
|
334
|
+
// Path parameters
|
|
335
|
+
let path = this.operationPath;
|
|
336
|
+
for (const pathParam of this._path.values()) {
|
|
337
|
+
path = pathParam.append(path);
|
|
338
|
+
}
|
|
339
|
+
const url = this.rootUrl + path;
|
|
340
|
+
|
|
341
|
+
// Query parameters
|
|
342
|
+
let httpParams = new HttpParams({
|
|
343
|
+
encoder: ParameterCodecInstance,
|
|
344
|
+
});
|
|
345
|
+
for (const queryParam of this._query.values()) {
|
|
346
|
+
httpParams = queryParam.append(httpParams);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Header parameters
|
|
350
|
+
let httpHeaders = new HttpHeaders();
|
|
351
|
+
if (options.accept) {
|
|
352
|
+
httpHeaders = httpHeaders.append('Accept', options.accept);
|
|
353
|
+
}
|
|
354
|
+
for (const headerParam of this._header.values()) {
|
|
355
|
+
httpHeaders = headerParam.append(httpHeaders);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Request content headers
|
|
359
|
+
if (this._bodyContentType && !(this._bodyContent instanceof FormData)) {
|
|
360
|
+
httpHeaders = httpHeaders.set('Content-Type', this._bodyContentType);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Perform the request
|
|
364
|
+
return new HttpRequest<T>(this.method.toUpperCase(), url, this._bodyContent, {
|
|
365
|
+
params: httpParams,
|
|
366
|
+
headers: httpHeaders,
|
|
367
|
+
responseType: options.responseType,
|
|
368
|
+
reportProgress: options.reportProgress,
|
|
369
|
+
context: options.context,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -16,6 +16,8 @@ class TsTypeAlias extends node_1.TsNode {
|
|
|
16
16
|
this.export = (_d = options.export) !== null && _d !== void 0 ? _d : false;
|
|
17
17
|
}
|
|
18
18
|
onWrite(builder) {
|
|
19
|
+
if (this.doc)
|
|
20
|
+
(0, write_ts_nodes_1.writeTsNode)(builder, this.doc);
|
|
19
21
|
if (this.export)
|
|
20
22
|
builder.append('export ');
|
|
21
23
|
builder.append('type ');
|
|
@@ -51,6 +51,10 @@ class DefaultTypeScriptModelGenerator extends file_generator_1.TypeScriptFileGen
|
|
|
51
51
|
getEnum(ctx) {
|
|
52
52
|
var _a, _b;
|
|
53
53
|
return ast_1.ts.enum(this.getDeclarationTypeName(ctx, ctx.schema), {
|
|
54
|
+
doc: ast_1.ts.doc({
|
|
55
|
+
description: ctx.schema.description,
|
|
56
|
+
tags: [ctx.schema.deprecated ? ast_1.ts.docTag('deprecated') : null],
|
|
57
|
+
}),
|
|
54
58
|
export: true,
|
|
55
59
|
members: ((_b = (_a = ctx.schema.enum) === null || _a === void 0 ? void 0 : _a.map((x) => String(x))) !== null && _b !== void 0 ? _b : []).map((x) => ast_1.ts.enumValue((0, core_1.toCasing)(x, ctx.config.enumValueNameCasing), { value: ast_1.ts.string(x) })),
|
|
56
60
|
});
|
|
@@ -58,7 +62,10 @@ class DefaultTypeScriptModelGenerator extends file_generator_1.TypeScriptFileGen
|
|
|
58
62
|
getInterface(ctx, schema) {
|
|
59
63
|
return ast_1.ts.interface(this.getDeclarationTypeName(ctx, ctx.schema), {
|
|
60
64
|
export: true,
|
|
61
|
-
doc: ast_1.ts.doc({
|
|
65
|
+
doc: ast_1.ts.doc({
|
|
66
|
+
description: schema.description,
|
|
67
|
+
tags: [ctx.schema.deprecated ? ast_1.ts.docTag('deprecated') : null],
|
|
68
|
+
}),
|
|
62
69
|
members: [...this.getProperties(ctx, schema), this.getIndexer(ctx, schema)],
|
|
63
70
|
});
|
|
64
71
|
}
|
|
@@ -67,7 +74,10 @@ class DefaultTypeScriptModelGenerator extends file_generator_1.TypeScriptFileGen
|
|
|
67
74
|
const hasDiscriminator = schema.discriminator && Object.keys(schema.discriminator.mapping).length > 0;
|
|
68
75
|
const result = ast_1.ts.typeAlias(this.getDeclarationTypeName(ctx, schema), (_a = (hasDiscriminator ? this.getDiscriminatorType(ctx, schema) : this.getType(ctx, schema, { skipSchemas: true }))) !== null && _a !== void 0 ? _a : this.getAnyType(ctx), {
|
|
69
76
|
export: true,
|
|
70
|
-
doc: ast_1.ts.doc({
|
|
77
|
+
doc: ast_1.ts.doc({
|
|
78
|
+
description: schema.description,
|
|
79
|
+
tags: [ctx.schema.deprecated ? ast_1.ts.docTag('deprecated') : null],
|
|
80
|
+
}),
|
|
71
81
|
});
|
|
72
82
|
if (hasDiscriminator) {
|
|
73
83
|
const propertyValue = this.getDiscriminatorTypeName(ctx, ctx.schema);
|
|
@@ -108,7 +118,10 @@ class DefaultTypeScriptModelGenerator extends file_generator_1.TypeScriptFileGen
|
|
|
108
118
|
getProperties(ctx, schema) {
|
|
109
119
|
return Array.from(schema.properties.values()).map((property) => {
|
|
110
120
|
return ast_1.ts.property(property.name, {
|
|
111
|
-
doc: ast_1.ts.doc({
|
|
121
|
+
doc: ast_1.ts.doc({
|
|
122
|
+
description: property.schema.description,
|
|
123
|
+
tags: [property.schema.deprecated ? ast_1.ts.docTag('deprecated') : null],
|
|
124
|
+
}),
|
|
112
125
|
readonly: ctx.config.immutableTypes,
|
|
113
126
|
optional: !schema.required.has(property.name),
|
|
114
127
|
type: ast_1.ts.unionType([this.getType(ctx, property.schema), property.schema.nullable ? 'null' : null]),
|
|
@@ -78,6 +78,10 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
|
|
|
78
78
|
]),
|
|
79
79
|
ast_1.ts.objectType({ members: [ast_1.ts.property('status', { type: 'TStatus' })] }),
|
|
80
80
|
]), {
|
|
81
|
+
doc: ast_1.ts.doc({
|
|
82
|
+
description: `Response model for operation ${this.getEndpointMethodName(ctx, endpoint)}`,
|
|
83
|
+
tags: [endpoint.deprecated ? ast_1.ts.docTag('deprecated') : null],
|
|
84
|
+
}),
|
|
81
85
|
export: true,
|
|
82
86
|
generics: [
|
|
83
87
|
ast_1.ts.genericParameter('TStatus', {
|
|
@@ -132,7 +136,10 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
|
|
|
132
136
|
for (const parameter of endpoint.parameters) {
|
|
133
137
|
const schema = parameter.schema;
|
|
134
138
|
type.members.push(ast_1.ts.property((0, core_1.toCasing)(parameter.name, ctx.config.propertyNameCasing), {
|
|
135
|
-
doc: ast_1.ts.doc({
|
|
139
|
+
doc: ast_1.ts.doc({
|
|
140
|
+
description: parameter.description,
|
|
141
|
+
tags: [parameter.deprecated ? ast_1.ts.docTag('deprecated') : null],
|
|
142
|
+
}),
|
|
136
143
|
type: schema ? (b) => b.appendModelUsage(ctx.input.models[schema.id]) : this.getAnyType(ctx),
|
|
137
144
|
optional: !parameter.required,
|
|
138
145
|
}));
|
|
@@ -152,7 +159,14 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
|
|
|
152
159
|
}
|
|
153
160
|
getClass(ctx) {
|
|
154
161
|
return ast_1.ts.class(this.getServiceClassName(ctx), {
|
|
155
|
-
doc: ast_1.ts.doc({
|
|
162
|
+
doc: ast_1.ts.doc({
|
|
163
|
+
description: ctx.service.description,
|
|
164
|
+
tags: [
|
|
165
|
+
ctx.service.endpoints.length === 0 || ctx.service.endpoints.some((x) => !x.deprecated)
|
|
166
|
+
? null
|
|
167
|
+
: ast_1.ts.docTag('deprecated'),
|
|
168
|
+
],
|
|
169
|
+
}),
|
|
156
170
|
decorators: [
|
|
157
171
|
ast_1.ts.decorator(ast_1.ts.refs.angular.injectable(), ctx.config.provideKind === 'root' ? [ast_1.ts.toNode({ providedIn: 'root' })] : []),
|
|
158
172
|
],
|
|
@@ -174,7 +188,7 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
|
|
|
174
188
|
const hasParams = this.hasEndpointParams(ctx, endpoint);
|
|
175
189
|
const paramsOptional = !endpoint.parameters.some((p) => p.required) && !((_a = endpoint.requestBody) === null || _a === void 0 ? void 0 : _a.required);
|
|
176
190
|
const responseModelType = ast_1.ts.reference(this.getResponseModelName(ctx, endpoint), this.getResponseModelFilePath(ctx));
|
|
177
|
-
const returnType =
|
|
191
|
+
const returnType = ctx.refs.abortablePromise([responseModelType]);
|
|
178
192
|
const accept = (_d = (_c = (_b = this.getEndpointSuccessResponse(ctx, endpoint)) === null || _b === void 0 ? void 0 : _b.contentOptions[0]) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : '*/*';
|
|
179
193
|
const responseType = this.contentTypeToResponseType(accept);
|
|
180
194
|
return ast_1.ts.method(this.getEndpointMethodName(ctx, endpoint), {
|
|
@@ -188,6 +202,10 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
|
|
|
188
202
|
: null,
|
|
189
203
|
ast_1.ts.parameter('context', { optional: true, type: ast_1.ts.refs.angular.httpContext() }),
|
|
190
204
|
],
|
|
205
|
+
doc: ast_1.ts.doc({
|
|
206
|
+
description: endpoint.description,
|
|
207
|
+
tags: [endpoint.deprecated ? ast_1.ts.docTag('deprecated') : null],
|
|
208
|
+
}),
|
|
191
209
|
returnType,
|
|
192
210
|
body: (0, core_1.appendValueGroup)([
|
|
193
211
|
(0, core_1.builderTemplate) `const rb = new ${ctx.refs.requestBuilder()}(this.rootUrl, ${this.getServiceClassName(ctx)}.${this.getEndpointPathPropertyName(ctx, endpoint)}, '${endpoint.method}');`,
|
|
@@ -10,6 +10,7 @@ function getReferenceFactories(options) {
|
|
|
10
10
|
const utilsDirPath = (0, path_1.resolve)(options.outputDir, options.utilsDirPath);
|
|
11
11
|
return {
|
|
12
12
|
// angular-service.utils.ts
|
|
13
|
+
abortablePromise: ast_1.ts.reference.genericFactory('AbortablePromise', (0, path_1.join)(utilsDirPath, 'angular-service.utils.ts')),
|
|
13
14
|
waitForResponse: ast_1.ts.reference.genericFactory('waitForResponse', (0, path_1.join)(utilsDirPath, 'angular-service.utils.ts')),
|
|
14
15
|
// request-builder.ts
|
|
15
16
|
requestBuilder: ast_1.ts.reference.factory('RequestBuilder', (0, path_1.join)(utilsDirPath, 'request-builder.ts')),
|
|
@@ -39,7 +39,14 @@ class DefaultTypeScriptFetchClientGenerator extends file_generator_1.TypeScriptF
|
|
|
39
39
|
}
|
|
40
40
|
getInterface(ctx) {
|
|
41
41
|
return ast_1.ts.interface(this.getInterfaceName(ctx), {
|
|
42
|
-
doc: ast_1.ts.doc({
|
|
42
|
+
doc: ast_1.ts.doc({
|
|
43
|
+
description: ctx.service.description,
|
|
44
|
+
tags: [
|
|
45
|
+
ctx.service.endpoints.length === 0 || ctx.service.endpoints.some((x) => !x.deprecated)
|
|
46
|
+
? null
|
|
47
|
+
: ast_1.ts.docTag('deprecated'),
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
43
50
|
export: true,
|
|
44
51
|
members: ctx.service.endpoints.map((endpoint) => this.getInterfaceEndpointMethod(ctx, endpoint)),
|
|
45
52
|
});
|
|
@@ -50,13 +57,14 @@ class DefaultTypeScriptFetchClientGenerator extends file_generator_1.TypeScriptF
|
|
|
50
57
|
const bodySchema = (_b = (_a = endpoint.requestBody) === null || _a === void 0 ? void 0 : _a.content[0]) === null || _b === void 0 ? void 0 : _b.schema;
|
|
51
58
|
const responseSchema = this.getResponseSchema(ctx, endpoint);
|
|
52
59
|
return ast_1.ts.method((0, core_1.toCasing)(endpoint.name, ctx.config.methodCasing), {
|
|
53
|
-
doc: ast_1.ts.doc({ description: endpoint.description }),
|
|
60
|
+
doc: ast_1.ts.doc({ description: endpoint.description, tags: [endpoint.deprecated ? ast_1.ts.docTag('deprecated') : null] }),
|
|
54
61
|
parameters: [
|
|
55
62
|
params.length > 0
|
|
56
63
|
? ast_1.ts.parameter('params', {
|
|
57
64
|
description: 'Parameters for the endpoint.',
|
|
58
65
|
type: ast_1.ts.objectType({
|
|
59
66
|
members: params.map((x) => ast_1.ts.property((0, core_1.toCasing)(x.name, ctx.config.propertyNameCasing), {
|
|
67
|
+
doc: ast_1.ts.doc({ tags: [x.deprecated ? ast_1.ts.docTag('deprecated') : null] }),
|
|
60
68
|
type: this.getSchemaType(ctx, x.schema),
|
|
61
69
|
optional: !x.required,
|
|
62
70
|
})),
|
|
@@ -92,7 +100,14 @@ class DefaultTypeScriptFetchClientGenerator extends file_generator_1.TypeScriptF
|
|
|
92
100
|
}
|
|
93
101
|
getClass(ctx) {
|
|
94
102
|
return ast_1.ts.class(this.getClassName(ctx), {
|
|
95
|
-
doc: ast_1.ts.doc({
|
|
103
|
+
doc: ast_1.ts.doc({
|
|
104
|
+
description: ctx.service.description,
|
|
105
|
+
tags: [
|
|
106
|
+
ctx.service.endpoints.length === 0 || ctx.service.endpoints.some((x) => !x.deprecated)
|
|
107
|
+
? null
|
|
108
|
+
: ast_1.ts.docTag('deprecated'),
|
|
109
|
+
],
|
|
110
|
+
}),
|
|
96
111
|
export: true,
|
|
97
112
|
implements: this.shouldGenerateInterface(ctx)
|
|
98
113
|
? [ast_1.ts.reference(this.getInterfaceName(ctx), this.getInterfaceFilePath(ctx))]
|
|
@@ -13,6 +13,8 @@ export class TsTypeAlias extends TsNode {
|
|
|
13
13
|
this.export = (_d = options.export) !== null && _d !== void 0 ? _d : false;
|
|
14
14
|
}
|
|
15
15
|
onWrite(builder) {
|
|
16
|
+
if (this.doc)
|
|
17
|
+
writeTsNode(builder, this.doc);
|
|
16
18
|
if (this.export)
|
|
17
19
|
builder.append('export ');
|
|
18
20
|
builder.append('type ');
|
|
@@ -48,6 +48,10 @@ export class DefaultTypeScriptModelGenerator extends TypeScriptFileGenerator {
|
|
|
48
48
|
getEnum(ctx) {
|
|
49
49
|
var _a, _b;
|
|
50
50
|
return ts.enum(this.getDeclarationTypeName(ctx, ctx.schema), {
|
|
51
|
+
doc: ts.doc({
|
|
52
|
+
description: ctx.schema.description,
|
|
53
|
+
tags: [ctx.schema.deprecated ? ts.docTag('deprecated') : null],
|
|
54
|
+
}),
|
|
51
55
|
export: true,
|
|
52
56
|
members: ((_b = (_a = ctx.schema.enum) === null || _a === void 0 ? void 0 : _a.map((x) => String(x))) !== null && _b !== void 0 ? _b : []).map((x) => ts.enumValue(toCasing(x, ctx.config.enumValueNameCasing), { value: ts.string(x) })),
|
|
53
57
|
});
|
|
@@ -55,7 +59,10 @@ export class DefaultTypeScriptModelGenerator extends TypeScriptFileGenerator {
|
|
|
55
59
|
getInterface(ctx, schema) {
|
|
56
60
|
return ts.interface(this.getDeclarationTypeName(ctx, ctx.schema), {
|
|
57
61
|
export: true,
|
|
58
|
-
doc: ts.doc({
|
|
62
|
+
doc: ts.doc({
|
|
63
|
+
description: schema.description,
|
|
64
|
+
tags: [ctx.schema.deprecated ? ts.docTag('deprecated') : null],
|
|
65
|
+
}),
|
|
59
66
|
members: [...this.getProperties(ctx, schema), this.getIndexer(ctx, schema)],
|
|
60
67
|
});
|
|
61
68
|
}
|
|
@@ -64,7 +71,10 @@ export class DefaultTypeScriptModelGenerator extends TypeScriptFileGenerator {
|
|
|
64
71
|
const hasDiscriminator = schema.discriminator && Object.keys(schema.discriminator.mapping).length > 0;
|
|
65
72
|
const result = ts.typeAlias(this.getDeclarationTypeName(ctx, schema), (_a = (hasDiscriminator ? this.getDiscriminatorType(ctx, schema) : this.getType(ctx, schema, { skipSchemas: true }))) !== null && _a !== void 0 ? _a : this.getAnyType(ctx), {
|
|
66
73
|
export: true,
|
|
67
|
-
doc: ts.doc({
|
|
74
|
+
doc: ts.doc({
|
|
75
|
+
description: schema.description,
|
|
76
|
+
tags: [ctx.schema.deprecated ? ts.docTag('deprecated') : null],
|
|
77
|
+
}),
|
|
68
78
|
});
|
|
69
79
|
if (hasDiscriminator) {
|
|
70
80
|
const propertyValue = this.getDiscriminatorTypeName(ctx, ctx.schema);
|
|
@@ -105,7 +115,10 @@ export class DefaultTypeScriptModelGenerator extends TypeScriptFileGenerator {
|
|
|
105
115
|
getProperties(ctx, schema) {
|
|
106
116
|
return Array.from(schema.properties.values()).map((property) => {
|
|
107
117
|
return ts.property(property.name, {
|
|
108
|
-
doc: ts.doc({
|
|
118
|
+
doc: ts.doc({
|
|
119
|
+
description: property.schema.description,
|
|
120
|
+
tags: [property.schema.deprecated ? ts.docTag('deprecated') : null],
|
|
121
|
+
}),
|
|
109
122
|
readonly: ctx.config.immutableTypes,
|
|
110
123
|
optional: !schema.required.has(property.name),
|
|
111
124
|
type: ts.unionType([this.getType(ctx, property.schema), property.schema.nullable ? 'null' : null]),
|
|
@@ -75,6 +75,10 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
|
|
|
75
75
|
]),
|
|
76
76
|
ts.objectType({ members: [ts.property('status', { type: 'TStatus' })] }),
|
|
77
77
|
]), {
|
|
78
|
+
doc: ts.doc({
|
|
79
|
+
description: `Response model for operation ${this.getEndpointMethodName(ctx, endpoint)}`,
|
|
80
|
+
tags: [endpoint.deprecated ? ts.docTag('deprecated') : null],
|
|
81
|
+
}),
|
|
78
82
|
export: true,
|
|
79
83
|
generics: [
|
|
80
84
|
ts.genericParameter('TStatus', {
|
|
@@ -129,7 +133,10 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
|
|
|
129
133
|
for (const parameter of endpoint.parameters) {
|
|
130
134
|
const schema = parameter.schema;
|
|
131
135
|
type.members.push(ts.property(toCasing(parameter.name, ctx.config.propertyNameCasing), {
|
|
132
|
-
doc: ts.doc({
|
|
136
|
+
doc: ts.doc({
|
|
137
|
+
description: parameter.description,
|
|
138
|
+
tags: [parameter.deprecated ? ts.docTag('deprecated') : null],
|
|
139
|
+
}),
|
|
133
140
|
type: schema ? (b) => b.appendModelUsage(ctx.input.models[schema.id]) : this.getAnyType(ctx),
|
|
134
141
|
optional: !parameter.required,
|
|
135
142
|
}));
|
|
@@ -149,7 +156,14 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
|
|
|
149
156
|
}
|
|
150
157
|
getClass(ctx) {
|
|
151
158
|
return ts.class(this.getServiceClassName(ctx), {
|
|
152
|
-
doc: ts.doc({
|
|
159
|
+
doc: ts.doc({
|
|
160
|
+
description: ctx.service.description,
|
|
161
|
+
tags: [
|
|
162
|
+
ctx.service.endpoints.length === 0 || ctx.service.endpoints.some((x) => !x.deprecated)
|
|
163
|
+
? null
|
|
164
|
+
: ts.docTag('deprecated'),
|
|
165
|
+
],
|
|
166
|
+
}),
|
|
153
167
|
decorators: [
|
|
154
168
|
ts.decorator(ts.refs.angular.injectable(), ctx.config.provideKind === 'root' ? [ts.toNode({ providedIn: 'root' })] : []),
|
|
155
169
|
],
|
|
@@ -171,7 +185,7 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
|
|
|
171
185
|
const hasParams = this.hasEndpointParams(ctx, endpoint);
|
|
172
186
|
const paramsOptional = !endpoint.parameters.some((p) => p.required) && !((_a = endpoint.requestBody) === null || _a === void 0 ? void 0 : _a.required);
|
|
173
187
|
const responseModelType = ts.reference(this.getResponseModelName(ctx, endpoint), this.getResponseModelFilePath(ctx));
|
|
174
|
-
const returnType =
|
|
188
|
+
const returnType = ctx.refs.abortablePromise([responseModelType]);
|
|
175
189
|
const accept = (_d = (_c = (_b = this.getEndpointSuccessResponse(ctx, endpoint)) === null || _b === void 0 ? void 0 : _b.contentOptions[0]) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : '*/*';
|
|
176
190
|
const responseType = this.contentTypeToResponseType(accept);
|
|
177
191
|
return ts.method(this.getEndpointMethodName(ctx, endpoint), {
|
|
@@ -185,6 +199,10 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
|
|
|
185
199
|
: null,
|
|
186
200
|
ts.parameter('context', { optional: true, type: ts.refs.angular.httpContext() }),
|
|
187
201
|
],
|
|
202
|
+
doc: ts.doc({
|
|
203
|
+
description: endpoint.description,
|
|
204
|
+
tags: [endpoint.deprecated ? ts.docTag('deprecated') : null],
|
|
205
|
+
}),
|
|
188
206
|
returnType,
|
|
189
207
|
body: appendValueGroup([
|
|
190
208
|
s `const rb = new ${ctx.refs.requestBuilder()}(this.rootUrl, ${this.getServiceClassName(ctx)}.${this.getEndpointPathPropertyName(ctx, endpoint)}, '${endpoint.method}');`,
|
|
@@ -7,6 +7,7 @@ export function getReferenceFactories(options) {
|
|
|
7
7
|
const utilsDirPath = resolve(options.outputDir, options.utilsDirPath);
|
|
8
8
|
return {
|
|
9
9
|
// angular-service.utils.ts
|
|
10
|
+
abortablePromise: ts.reference.genericFactory('AbortablePromise', join(utilsDirPath, 'angular-service.utils.ts')),
|
|
10
11
|
waitForResponse: ts.reference.genericFactory('waitForResponse', join(utilsDirPath, 'angular-service.utils.ts')),
|
|
11
12
|
// request-builder.ts
|
|
12
13
|
requestBuilder: ts.reference.factory('RequestBuilder', join(utilsDirPath, 'request-builder.ts')),
|
|
@@ -36,7 +36,14 @@ export class DefaultTypeScriptFetchClientGenerator extends TypeScriptFileGenerat
|
|
|
36
36
|
}
|
|
37
37
|
getInterface(ctx) {
|
|
38
38
|
return ts.interface(this.getInterfaceName(ctx), {
|
|
39
|
-
doc: ts.doc({
|
|
39
|
+
doc: ts.doc({
|
|
40
|
+
description: ctx.service.description,
|
|
41
|
+
tags: [
|
|
42
|
+
ctx.service.endpoints.length === 0 || ctx.service.endpoints.some((x) => !x.deprecated)
|
|
43
|
+
? null
|
|
44
|
+
: ts.docTag('deprecated'),
|
|
45
|
+
],
|
|
46
|
+
}),
|
|
40
47
|
export: true,
|
|
41
48
|
members: ctx.service.endpoints.map((endpoint) => this.getInterfaceEndpointMethod(ctx, endpoint)),
|
|
42
49
|
});
|
|
@@ -47,13 +54,14 @@ export class DefaultTypeScriptFetchClientGenerator extends TypeScriptFileGenerat
|
|
|
47
54
|
const bodySchema = (_b = (_a = endpoint.requestBody) === null || _a === void 0 ? void 0 : _a.content[0]) === null || _b === void 0 ? void 0 : _b.schema;
|
|
48
55
|
const responseSchema = this.getResponseSchema(ctx, endpoint);
|
|
49
56
|
return ts.method(toCasing(endpoint.name, ctx.config.methodCasing), {
|
|
50
|
-
doc: ts.doc({ description: endpoint.description }),
|
|
57
|
+
doc: ts.doc({ description: endpoint.description, tags: [endpoint.deprecated ? ts.docTag('deprecated') : null] }),
|
|
51
58
|
parameters: [
|
|
52
59
|
params.length > 0
|
|
53
60
|
? ts.parameter('params', {
|
|
54
61
|
description: 'Parameters for the endpoint.',
|
|
55
62
|
type: ts.objectType({
|
|
56
63
|
members: params.map((x) => ts.property(toCasing(x.name, ctx.config.propertyNameCasing), {
|
|
64
|
+
doc: ts.doc({ tags: [x.deprecated ? ts.docTag('deprecated') : null] }),
|
|
57
65
|
type: this.getSchemaType(ctx, x.schema),
|
|
58
66
|
optional: !x.required,
|
|
59
67
|
})),
|
|
@@ -89,7 +97,14 @@ export class DefaultTypeScriptFetchClientGenerator extends TypeScriptFileGenerat
|
|
|
89
97
|
}
|
|
90
98
|
getClass(ctx) {
|
|
91
99
|
return ts.class(this.getClassName(ctx), {
|
|
92
|
-
doc: ts.doc({
|
|
100
|
+
doc: ts.doc({
|
|
101
|
+
description: ctx.service.description,
|
|
102
|
+
tags: [
|
|
103
|
+
ctx.service.endpoints.length === 0 || ctx.service.endpoints.some((x) => !x.deprecated)
|
|
104
|
+
? null
|
|
105
|
+
: ts.docTag('deprecated'),
|
|
106
|
+
],
|
|
107
|
+
}),
|
|
93
108
|
export: true,
|
|
94
109
|
implements: this.shouldGenerateInterface(ctx)
|
|
95
110
|
? [ts.reference(this.getInterfaceName(ctx), this.getInterfaceFilePath(ctx))]
|
package/package.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { ts } from '../../../ast';
|
|
2
2
|
import type { TypeScriptAngularServicesGeneratorConfig } from './models';
|
|
3
3
|
export declare function getReferenceFactories(options: TypeScriptAngularServicesGeneratorConfig): {
|
|
4
|
+
abortablePromise: (<TBuilder extends import("@goast/core").SourceBuilder<never>>(generics: [ts.Type<TBuilder>]) => ts.Reference<TBuilder, never>) & {
|
|
5
|
+
refName: string;
|
|
6
|
+
moduleNameOrfilePath: import("@goast/core").Nullable<string>;
|
|
7
|
+
infer: <TBuilder_1 extends import("@goast/core").SourceBuilder<never>>() => ts.Reference<TBuilder_1, never>;
|
|
8
|
+
} & {
|
|
9
|
+
moduleNameOrfilePath: string;
|
|
10
|
+
};
|
|
4
11
|
waitForResponse: (<TBuilder extends import("@goast/core").SourceBuilder<never>>(generics: [ts.Type<TBuilder>]) => ts.Reference<TBuilder, never>) & {
|
|
5
12
|
refName: string;
|
|
6
13
|
moduleNameOrfilePath: import("@goast/core").Nullable<string>;
|