@goast/typescript 0.5.3 → 0.6.0-beta.1
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/LICENSE +21 -21
- package/esm/src/file-builder.d.ts.map +1 -1
- package/esm/src/file-builder.js +2 -5
- package/esm/src/generators/models/model-generator.d.ts.map +1 -1
- package/esm/src/generators/models/model-generator.js +6 -2
- package/esm/src/generators/services/k6-clients/k6-client-generator.d.ts.map +1 -1
- package/esm/src/generators/services/k6-clients/k6-client-generator.js +2 -1
- package/package.json +3 -3
- package/script/src/file-builder.d.ts.map +1 -1
- package/script/src/file-builder.js +1 -5
- package/script/src/generators/models/model-generator.d.ts.map +1 -1
- package/script/src/generators/models/model-generator.js +5 -1
- package/script/src/generators/services/k6-clients/k6-client-generator.d.ts.map +1 -1
- package/script/src/generators/services/k6-clients/k6-client-generator.js +2 -1
- package/src/mod.ts +0 -7
- package/src/src/assets.ts +0 -9
- package/src/src/ast/_index.ts +0 -54
- package/src/src/ast/common.ts +0 -1
- package/src/src/ast/index.ts +0 -1
- package/src/src/ast/node.ts +0 -10
- package/src/src/ast/nodes/argument.ts +0 -40
- package/src/src/ast/nodes/array-type.ts +0 -49
- package/src/src/ast/nodes/arrow-function.ts +0 -91
- package/src/src/ast/nodes/call.ts +0 -55
- package/src/src/ast/nodes/class.ts +0 -133
- package/src/src/ast/nodes/constructor-parameter.ts +0 -70
- package/src/src/ast/nodes/constructor.ts +0 -77
- package/src/src/ast/nodes/decorator.ts +0 -76
- package/src/src/ast/nodes/doc-tag.ts +0 -291
- package/src/src/ast/nodes/doc.ts +0 -102
- package/src/src/ast/nodes/enum-value.ts +0 -72
- package/src/src/ast/nodes/enum.ts +0 -77
- package/src/src/ast/nodes/export.ts +0 -54
- package/src/src/ast/nodes/function-type.ts +0 -60
- package/src/src/ast/nodes/function.ts +0 -107
- package/src/src/ast/nodes/generic-parameter.ts +0 -71
- package/src/src/ast/nodes/indexer.ts +0 -60
- package/src/src/ast/nodes/interface.ts +0 -117
- package/src/src/ast/nodes/intersection-type.ts +0 -67
- package/src/src/ast/nodes/lookup-type.ts +0 -53
- package/src/src/ast/nodes/method.ts +0 -138
- package/src/src/ast/nodes/object-type.ts +0 -52
- package/src/src/ast/nodes/object.ts +0 -48
- package/src/src/ast/nodes/parameter.ts +0 -85
- package/src/src/ast/nodes/property-accessor.ts +0 -173
- package/src/src/ast/nodes/property.ts +0 -192
- package/src/src/ast/nodes/reference.ts +0 -194
- package/src/src/ast/nodes/string.ts +0 -49
- package/src/src/ast/nodes/tuple.ts +0 -53
- package/src/src/ast/nodes/type-alias.ts +0 -75
- package/src/src/ast/nodes/typeof.ts +0 -42
- package/src/src/ast/nodes/types.ts +0 -40
- package/src/src/ast/nodes/union-type.ts +0 -62
- package/src/src/ast/nodes/variable.ts +0 -73
- package/src/src/ast/references/angular.ts +0 -24
- package/src/src/ast/references/easy-network-stub.ts +0 -21
- package/src/src/ast/references/index.ts +0 -5
- package/src/src/ast/references/k6.ts +0 -8
- package/src/src/ast/references/rxjs.ts +0 -7
- package/src/src/ast/references/typescript.ts +0 -35
- package/src/src/ast/utils/get-type-script-builder-options.ts +0 -19
- package/src/src/ast/utils/to-ts-node.ts +0 -27
- package/src/src/ast/utils/write-ts-generic-parameters.ts +0 -25
- package/src/src/ast/utils/write-ts-members.ts +0 -28
- package/src/src/ast/utils/write-ts-nodes.ts +0 -35
- package/src/src/ast/utils/write-ts-parameters.ts +0 -25
- package/src/src/common-results.ts +0 -26
- package/src/src/config.ts +0 -89
- package/src/src/file-builder.ts +0 -136
- package/src/src/generators/file-generator.ts +0 -15
- package/src/src/generators/index.ts +0 -6
- package/src/src/generators/models/index.ts +0 -3
- package/src/src/generators/models/model-generator.ts +0 -469
- package/src/src/generators/models/models-generator.ts +0 -108
- package/src/src/generators/models/models.ts +0 -121
- package/src/src/generators/services/angular-services/angular-service-generator.ts +0 -406
- package/src/src/generators/services/angular-services/angular-services-generator.ts +0 -295
- package/src/src/generators/services/angular-services/index.ts +0 -3
- package/src/src/generators/services/angular-services/models.ts +0 -277
- package/src/src/generators/services/angular-services/refs.ts +0 -54
- package/src/src/generators/services/easy-network-stub/easy-network-stub-generator.ts +0 -242
- package/src/src/generators/services/easy-network-stub/easy-network-stubs-generator.ts +0 -211
- package/src/src/generators/services/easy-network-stub/index.ts +0 -3
- package/src/src/generators/services/easy-network-stub/models.ts +0 -124
- package/src/src/generators/services/easy-network-stub/refs.ts +0 -45
- package/src/src/generators/services/fetch-clients/fetch-client-generator.ts +0 -275
- package/src/src/generators/services/fetch-clients/fetch-clients-generator.ts +0 -163
- package/src/src/generators/services/fetch-clients/index.ts +0 -3
- package/src/src/generators/services/fetch-clients/models.ts +0 -130
- package/src/src/generators/services/fetch-clients/refs.ts +0 -21
- package/src/src/generators/services/k6-clients/index.ts +0 -3
- package/src/src/generators/services/k6-clients/k6-client-generator.ts +0 -465
- package/src/src/generators/services/k6-clients/k6-clients-generator.ts +0 -171
- package/src/src/generators/services/k6-clients/models.ts +0 -247
- package/src/src/generators/services/k6-clients/refs.ts +0 -16
- package/src/src/import-collection.ts +0 -236
- package/src/src/utils.ts +0 -72
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
type ApiEndpoint,
|
|
5
|
-
type ApiResponse,
|
|
6
|
-
type ApiSchema,
|
|
7
|
-
type AppendValueGroup,
|
|
8
|
-
appendValueGroup,
|
|
9
|
-
builderTemplate as s,
|
|
10
|
-
type MaybePromise,
|
|
11
|
-
type Nullable,
|
|
12
|
-
toCasing,
|
|
13
|
-
} from '@goast/core';
|
|
14
|
-
|
|
15
|
-
import { ts } from '../../../ast/index.js';
|
|
16
|
-
import type { TypeScriptExportOutput } from '../../../common-results.js';
|
|
17
|
-
import { TypeScriptFileBuilder } from '../../../file-builder.js';
|
|
18
|
-
import { TypeScriptFileGenerator } from '../../file-generator.js';
|
|
19
|
-
import type { TypeScriptAngularServiceGeneratorContext, TypeScriptAngularServiceGeneratorOutput } from './models.js';
|
|
20
|
-
|
|
21
|
-
type Context = TypeScriptAngularServiceGeneratorContext;
|
|
22
|
-
type Output = TypeScriptAngularServiceGeneratorOutput;
|
|
23
|
-
type Builder = TypeScriptFileBuilder;
|
|
24
|
-
|
|
25
|
-
export interface TypeScriptAngularServiceGenerator<TOutput extends Output = Output> {
|
|
26
|
-
generate(ctx: Context): MaybePromise<TOutput>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGenerator<Context, Output>
|
|
30
|
-
implements TypeScriptAngularServiceGenerator {
|
|
31
|
-
public generate(ctx: Context): MaybePromise<Output> {
|
|
32
|
-
const responseModels = this.generateResponseModels(ctx);
|
|
33
|
-
|
|
34
|
-
const filePath = this.getServiceFilePath(ctx);
|
|
35
|
-
const name = this.getServiceClassName(ctx);
|
|
36
|
-
|
|
37
|
-
TypeScriptFileBuilder.generate({
|
|
38
|
-
logName: `service ${name}`,
|
|
39
|
-
filePath,
|
|
40
|
-
options: ctx.config,
|
|
41
|
-
generator: (b) => b.append(this.getServiceFileContent(ctx)),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
filePath,
|
|
46
|
-
component: name,
|
|
47
|
-
responseModels,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// #region Response Model
|
|
52
|
-
protected generateResponseModels(ctx: Context): Record<string, TypeScriptExportOutput> {
|
|
53
|
-
const result: Record<string, TypeScriptExportOutput> = {};
|
|
54
|
-
const filePath = this.getResponseModelFilePath(ctx);
|
|
55
|
-
const builder = new TypeScriptFileBuilder(filePath, ctx.config);
|
|
56
|
-
|
|
57
|
-
for (const endpoint of ctx.service.endpoints) {
|
|
58
|
-
const name = this.getResponseModelName(ctx, endpoint);
|
|
59
|
-
builder.appendLine(this.getResponseModelFileContent(ctx, endpoint));
|
|
60
|
-
result[endpoint.id] = { component: name, filePath };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
builder.writeToFile();
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
protected getResponseModelFileContent(ctx: Context, endpoint: ApiEndpoint): AppendValueGroup<Builder> {
|
|
68
|
-
const result = appendValueGroup<Builder>([]);
|
|
69
|
-
const statusCodes = this.getEndpointStatusCodes(ctx, endpoint);
|
|
70
|
-
let statusCodeType: ts.Reference<Builder>;
|
|
71
|
-
|
|
72
|
-
const statusCodesTypeName = toCasing(`${endpoint.name}_StatusCodes`, ctx.config.typeNameCasing);
|
|
73
|
-
result.values.push(ts.typeAlias(statusCodesTypeName, ts.unionType(Object.keys(statusCodes))));
|
|
74
|
-
if (ctx.config.strictResponseTypes) {
|
|
75
|
-
statusCodeType = ts.reference(statusCodesTypeName);
|
|
76
|
-
} else {
|
|
77
|
-
statusCodeType = ctx.refs.httpStatusCode();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
result.values.push(
|
|
81
|
-
ts.typeAlias(
|
|
82
|
-
this.getResponseModelName(ctx, endpoint),
|
|
83
|
-
ts.intersectionType([
|
|
84
|
-
ts.unionType([
|
|
85
|
-
...Object.entries(statusCodes).map(([key, value]) => {
|
|
86
|
-
const isSuccess = key.startsWith('2');
|
|
87
|
-
return ts.intersectionType<Builder>([
|
|
88
|
-
isSuccess
|
|
89
|
-
? ts.refs.angular.httpResponse([value.type])
|
|
90
|
-
: ts.refs.omit([ts.refs.angular.httpErrorResponse(), ts.string('error')]),
|
|
91
|
-
ts.objectType({
|
|
92
|
-
members: [
|
|
93
|
-
isSuccess ? null : ts.property('error', { type: ts.unionType([value.type, ts.refs.null_()]) }),
|
|
94
|
-
ts.property('status', { type: key }),
|
|
95
|
-
ts.property('ok', { type: isSuccess ? 'true' : 'false' }),
|
|
96
|
-
],
|
|
97
|
-
}),
|
|
98
|
-
]);
|
|
99
|
-
}),
|
|
100
|
-
!ctx.config.strictResponseTypes
|
|
101
|
-
? ts.intersectionType([
|
|
102
|
-
ts.refs.angular.httpResponse([this.getAnyType(ctx)]),
|
|
103
|
-
ts.objectType({
|
|
104
|
-
members: [
|
|
105
|
-
ts.property('status', {
|
|
106
|
-
type: ts.refs.exclude([ctx.refs.httpStatusCode(), ts.reference(statusCodesTypeName)]),
|
|
107
|
-
}),
|
|
108
|
-
],
|
|
109
|
-
}),
|
|
110
|
-
])
|
|
111
|
-
: null,
|
|
112
|
-
]),
|
|
113
|
-
ts.objectType({ members: [ts.property('status', { type: 'TStatus' })] }),
|
|
114
|
-
]),
|
|
115
|
-
{
|
|
116
|
-
doc: ts.doc({
|
|
117
|
-
description: `Response model for operation ${this.getEndpointMethodName(ctx, endpoint)}`,
|
|
118
|
-
tags: [endpoint.deprecated ? ts.docTag('deprecated') : null],
|
|
119
|
-
}),
|
|
120
|
-
export: true,
|
|
121
|
-
generics: [
|
|
122
|
-
ts.genericParameter('TStatus', {
|
|
123
|
-
constraint: statusCodeType,
|
|
124
|
-
default: statusCodeType,
|
|
125
|
-
}),
|
|
126
|
-
],
|
|
127
|
-
},
|
|
128
|
-
),
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
return result;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
protected getEndpointStatusCodes(
|
|
135
|
-
ctx: Context,
|
|
136
|
-
endpoint: ApiEndpoint,
|
|
137
|
-
): Record<number, { parser: 'text' | 'json'; type: ts.Type<Builder> }> {
|
|
138
|
-
return Object.fromEntries([
|
|
139
|
-
...Object.entries(ctx.config.defaultStatusCodeResponseTypes).map(([key, value]) => [
|
|
140
|
-
key,
|
|
141
|
-
{
|
|
142
|
-
parser: value?.parser ?? 'text',
|
|
143
|
-
type: typeof value?.type === 'function'
|
|
144
|
-
? this.getSchemaType(ctx, value.type(ctx.data.schemas))
|
|
145
|
-
: value ?? this.getAnyType(ctx),
|
|
146
|
-
},
|
|
147
|
-
]),
|
|
148
|
-
...endpoint.responses
|
|
149
|
-
.filter((x) => x.statusCode)
|
|
150
|
-
.map((x) => [
|
|
151
|
-
x.statusCode,
|
|
152
|
-
{
|
|
153
|
-
parser: this.contentTypeToResponseType(x.contentOptions[0]?.type),
|
|
154
|
-
type: this.getSchemaType(ctx, x.contentOptions[0]?.schema, this.getAnyType(ctx)),
|
|
155
|
-
},
|
|
156
|
-
]),
|
|
157
|
-
]);
|
|
158
|
-
}
|
|
159
|
-
// #endregion
|
|
160
|
-
|
|
161
|
-
protected getServiceFileContent(ctx: Context): AppendValueGroup<Builder> {
|
|
162
|
-
const code = appendValueGroup<Builder>([], '\n');
|
|
163
|
-
for (const endpoint of ctx.service.endpoints) {
|
|
164
|
-
if (endpoint.parameters.length > 0 || endpoint.requestBody !== undefined) {
|
|
165
|
-
code.values.push(this.getEndpointParamsType(ctx, endpoint));
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
code.values.push(this.getClass(ctx));
|
|
169
|
-
return code;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
protected getEndpointParamsType(ctx: Context, endpoint: ApiEndpoint): ts.TypeAlias<Builder> {
|
|
173
|
-
const type = ts.objectType<Builder>();
|
|
174
|
-
|
|
175
|
-
for (const parameter of endpoint.parameters) {
|
|
176
|
-
const schema = parameter.schema;
|
|
177
|
-
type.members.push(
|
|
178
|
-
ts.property(toCasing(parameter.name, ctx.config.propertyNameCasing), {
|
|
179
|
-
doc: ts.doc({
|
|
180
|
-
description: parameter.description,
|
|
181
|
-
tags: [parameter.deprecated ? ts.docTag('deprecated') : null],
|
|
182
|
-
}),
|
|
183
|
-
type: schema ? (b) => b.appendModelUsage(ctx.input.typescript.models[schema.id]) : this.getAnyType(ctx),
|
|
184
|
-
optional: !parameter.required,
|
|
185
|
-
}),
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (endpoint.requestBody !== undefined) {
|
|
190
|
-
const body = endpoint.requestBody;
|
|
191
|
-
const schema = body.content[0].schema;
|
|
192
|
-
type.members.push(
|
|
193
|
-
ts.property('body', {
|
|
194
|
-
doc: ts.doc({ description: body.description }),
|
|
195
|
-
type: schema ? (b) => b.appendModelUsage(ctx.input.typescript.models[schema.id]) : this.getAnyType(ctx),
|
|
196
|
-
optional: !body.required,
|
|
197
|
-
}),
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return ts.typeAlias(this.getEndpointParamsTypeName(ctx, endpoint), type, {
|
|
202
|
-
doc: ts.doc({ description: `Parameters for operation ${this.getEndpointMethodName(ctx, endpoint)}` }),
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
protected getClass(ctx: Context): ts.Class<Builder> {
|
|
207
|
-
return ts.class<Builder>(this.getServiceClassName(ctx), {
|
|
208
|
-
doc: ts.doc({
|
|
209
|
-
description: ctx.service.description,
|
|
210
|
-
tags: [
|
|
211
|
-
ctx.service.endpoints.length === 0 || ctx.service.endpoints.some((x) => !x.deprecated)
|
|
212
|
-
? null
|
|
213
|
-
: ts.docTag('deprecated'),
|
|
214
|
-
],
|
|
215
|
-
}),
|
|
216
|
-
decorators: [
|
|
217
|
-
ts.decorator(
|
|
218
|
-
ts.refs.angular.injectable(),
|
|
219
|
-
ctx.config.provideKind === 'root' ? [ts.toNode({ providedIn: 'root' })] : [],
|
|
220
|
-
),
|
|
221
|
-
],
|
|
222
|
-
export: true,
|
|
223
|
-
extends: ctx.refs.apiBaseService(),
|
|
224
|
-
members: [
|
|
225
|
-
...ctx.service.endpoints.map((e) =>
|
|
226
|
-
ts.property(this.getEndpointPathPropertyName(ctx, e), {
|
|
227
|
-
accessModifier: 'private',
|
|
228
|
-
static: true,
|
|
229
|
-
readonly: true,
|
|
230
|
-
value: ts.string(e.path),
|
|
231
|
-
})
|
|
232
|
-
),
|
|
233
|
-
...ctx.service.endpoints.map((e) => this.getEndpointMethod(ctx, e)),
|
|
234
|
-
],
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
protected getEndpointMethod(ctx: Context, endpoint: ApiEndpoint): ts.Method<Builder> {
|
|
239
|
-
const hasParams = this.hasEndpointParams(ctx, endpoint);
|
|
240
|
-
const paramsOptional = !endpoint.parameters.some((p) => p.required) && !endpoint.requestBody?.required;
|
|
241
|
-
const responseModelType = ts.reference(
|
|
242
|
-
this.getResponseModelName(ctx, endpoint),
|
|
243
|
-
this.getResponseModelFilePath(ctx),
|
|
244
|
-
{
|
|
245
|
-
importType: 'type-import',
|
|
246
|
-
},
|
|
247
|
-
);
|
|
248
|
-
const returnType = ctx.refs.abortablePromise([responseModelType]);
|
|
249
|
-
const accept = this.getEndpointSuccessResponseType(ctx, endpoint);
|
|
250
|
-
const responseType = this.contentTypeToResponseType(accept);
|
|
251
|
-
return ts.method<Builder>(this.getEndpointMethodName(ctx, endpoint), {
|
|
252
|
-
accessModifier: 'public',
|
|
253
|
-
parameters: [
|
|
254
|
-
hasParams
|
|
255
|
-
? ts.parameter('params', {
|
|
256
|
-
optional: paramsOptional,
|
|
257
|
-
type: ts.reference(this.getEndpointParamsTypeName(ctx, endpoint)),
|
|
258
|
-
})
|
|
259
|
-
: null,
|
|
260
|
-
ts.parameter('context', { optional: true, type: ts.refs.angular.httpContext() }),
|
|
261
|
-
],
|
|
262
|
-
doc: ts.doc({
|
|
263
|
-
description: endpoint.description,
|
|
264
|
-
tags: [endpoint.deprecated ? ts.docTag('deprecated') : null],
|
|
265
|
-
}),
|
|
266
|
-
returnType,
|
|
267
|
-
body: appendValueGroup(
|
|
268
|
-
[
|
|
269
|
-
s`const rb = new ${ctx.refs.requestBuilder()}(this.rootUrl, ${
|
|
270
|
-
this.getServiceClassName(
|
|
271
|
-
ctx,
|
|
272
|
-
)
|
|
273
|
-
}.${this.getEndpointPathPropertyName(ctx, endpoint)}, '${endpoint.method}');`,
|
|
274
|
-
hasParams
|
|
275
|
-
? (b) =>
|
|
276
|
-
b.appendIf(paramsOptional, 'if (params) ').parenthesizeIf(
|
|
277
|
-
paramsOptional,
|
|
278
|
-
'{}',
|
|
279
|
-
appendValueGroup(
|
|
280
|
-
[
|
|
281
|
-
...endpoint.parameters
|
|
282
|
-
.filter((p) => p.target === 'path' || p.target === 'query' || p.target === 'header')
|
|
283
|
-
.map((p) => {
|
|
284
|
-
const options = ts.object({
|
|
285
|
-
members: [
|
|
286
|
-
p.style !== undefined ? ts.property('style', { value: ts.string(p.style) }) : null,
|
|
287
|
-
p.explode !== undefined ? ts.property('explode', { value: ts.toNode(p.explode) }) : null,
|
|
288
|
-
],
|
|
289
|
-
});
|
|
290
|
-
return s<Builder>`rb.${p.target}(${ts.string(p.name)}, params.${
|
|
291
|
-
toCasing(
|
|
292
|
-
p.name,
|
|
293
|
-
ctx.config.propertyNameCasing,
|
|
294
|
-
)
|
|
295
|
-
}, ${options});`;
|
|
296
|
-
}),
|
|
297
|
-
endpoint.requestBody
|
|
298
|
-
? s`rb.body(params.body, ${ts.string(this.getEndpointRequestContentType(ctx, endpoint))});`
|
|
299
|
-
: null,
|
|
300
|
-
],
|
|
301
|
-
'\n',
|
|
302
|
-
),
|
|
303
|
-
{ multiline: true },
|
|
304
|
-
)
|
|
305
|
-
: null,
|
|
306
|
-
'',
|
|
307
|
-
s`return ${ctx.refs.waitForResponse([responseModelType])}(${s.indent`
|
|
308
|
-
this.http.request(rb.build({${s.indent`
|
|
309
|
-
responseType: ${ts.string(responseType)},
|
|
310
|
-
accept: ${ts.string(accept)},
|
|
311
|
-
context,`}
|
|
312
|
-
})),
|
|
313
|
-
{${s.indent`
|
|
314
|
-
errorResponseTypes: ${
|
|
315
|
-
ts.object({
|
|
316
|
-
members: [
|
|
317
|
-
...Object.entries(this.getEndpointStatusCodes(ctx, endpoint))
|
|
318
|
-
.filter(([key]) => !key.startsWith('2'))
|
|
319
|
-
.map(([key, value]) => {
|
|
320
|
-
return ts.property(key, { value: ts.string(value.parser) });
|
|
321
|
-
}),
|
|
322
|
-
],
|
|
323
|
-
})
|
|
324
|
-
}`}
|
|
325
|
-
}`}
|
|
326
|
-
)`,
|
|
327
|
-
],
|
|
328
|
-
'\n',
|
|
329
|
-
),
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
protected getEndpointRequestContentType(ctx: Context, endpoint: ApiEndpoint): string {
|
|
334
|
-
return ctx.config.defaultRequestContentType ?? endpoint?.requestBody?.content[0].type ?? 'application/json';
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
protected getEndpointSuccessResponseType(ctx: Context, endpoint: ApiEndpoint): string {
|
|
338
|
-
const response = this.getEndpointSuccessResponse(ctx, endpoint);
|
|
339
|
-
if (!response) return '*/*';
|
|
340
|
-
|
|
341
|
-
if (!ctx.config.defaultSuccessResponseContentType) return response.contentOptions[0]?.type ?? '*/*';
|
|
342
|
-
|
|
343
|
-
return (
|
|
344
|
-
response.contentOptions.find((x) => x.type === ctx.config.defaultSuccessResponseContentType)?.type ??
|
|
345
|
-
response.contentOptions[0]?.type ??
|
|
346
|
-
'*/*'
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
protected getEndpointSuccessResponse(_ctx: Context, endpoint: ApiEndpoint): ApiResponse | undefined {
|
|
351
|
-
return (
|
|
352
|
-
endpoint.responses.find((x) => x.statusCode && x.statusCode >= 200 && x.statusCode < 300) ??
|
|
353
|
-
endpoint.responses.find((x) => x.statusCode === undefined)
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
protected getServiceClassName(ctx: Context): string {
|
|
358
|
-
return toCasing(ctx.service.name + '_Service', ctx.config.typeNameCasing);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
protected getResponseModelName(ctx: Context, endpoint: ApiEndpoint): string {
|
|
362
|
-
return toCasing(`${this.getEndpointMethodName(ctx, endpoint)}ApiResponse`, ctx.config.typeNameCasing);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
protected getEndpointParamsTypeName(ctx: Context, endpoint: ApiEndpoint): string {
|
|
366
|
-
return toCasing(this.getEndpointMethodName(ctx, endpoint) + '_Params', ctx.config.typeNameCasing);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
protected getEndpointPathPropertyName(ctx: Context, endpoint: ApiEndpoint): string {
|
|
370
|
-
return toCasing(this.getEndpointMethodName(ctx, endpoint) + '_Path', ctx.config.constantNameCasing);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
protected getEndpointMethodName(ctx: Context, endpoint: ApiEndpoint): string {
|
|
374
|
-
return toCasing(endpoint.name, ctx.config.functionNameCasing);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
protected getServiceFilePath(ctx: Context): string {
|
|
378
|
-
return resolve(
|
|
379
|
-
ctx.config.outputDir,
|
|
380
|
-
ctx.config.servicesDir,
|
|
381
|
-
`${toCasing(ctx.service.name, ctx.config.serviceFileNameCasing ?? ctx.config.fileNameCasing)}.ts`,
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
protected getResponseModelFilePath(ctx: Context): string {
|
|
386
|
-
return resolve(
|
|
387
|
-
ctx.config.outputDir,
|
|
388
|
-
ctx.config.responseModelsDir,
|
|
389
|
-
`${toCasing(ctx.service.name, ctx.config.responseModelFileNameCasing ?? ctx.config.fileNameCasing)}.ts`,
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
protected hasEndpointParams(_ctx: Context, endpoint: ApiEndpoint): boolean {
|
|
394
|
-
return endpoint.parameters.length > 0 || endpoint.requestBody !== undefined;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
protected getSchemaType(ctx: Context, schema: ApiSchema | undefined, fallback?: ts.Type<Builder>): ts.Type<Builder> {
|
|
398
|
-
const output = schema && ctx.input.typescript.models[schema.id];
|
|
399
|
-
if (!output) return fallback ?? this.getAnyType(ctx);
|
|
400
|
-
return (b) => b.appendModelUsage(output);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
private contentTypeToResponseType(contentType: Nullable<string>): 'text' | 'json' {
|
|
404
|
-
return contentType?.includes('json') ? 'json' : 'text';
|
|
405
|
-
}
|
|
406
|
-
}
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import fs from 'fs-extra';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
type ApiService,
|
|
8
|
-
type AppendValueGroup,
|
|
9
|
-
appendValueGroup,
|
|
10
|
-
Factory,
|
|
11
|
-
type MaybePromise,
|
|
12
|
-
type OpenApiGeneratorContext,
|
|
13
|
-
OpenApiServicesGenerationProviderBase,
|
|
14
|
-
toCasing,
|
|
15
|
-
} from '@goast/core';
|
|
16
|
-
|
|
17
|
-
import { copyAssetFile } from '../../../assets.js';
|
|
18
|
-
import { ts } from '../../../ast/index.js';
|
|
19
|
-
import { TypeScriptFileBuilder } from '../../../file-builder.js';
|
|
20
|
-
import { modifyString } from '../../../utils.js';
|
|
21
|
-
import {
|
|
22
|
-
DefaultTypeScriptAngularServiceGenerator,
|
|
23
|
-
type TypeScriptAngularServiceGenerator,
|
|
24
|
-
} from './angular-service-generator.js';
|
|
25
|
-
import {
|
|
26
|
-
defaultTypeScriptAngularServicesGeneratorConfig,
|
|
27
|
-
type TypeScriptAngularServiceGeneratorOutput,
|
|
28
|
-
type TypeScriptAngularServicesGeneratorConfig,
|
|
29
|
-
type TypeScriptAngularServicesGeneratorContext,
|
|
30
|
-
type TypeScriptAngularServicesGeneratorInput,
|
|
31
|
-
type TypeScriptAngularServicesGeneratorOutput,
|
|
32
|
-
} from './models.js';
|
|
33
|
-
import { getReferenceFactories } from './refs.js';
|
|
34
|
-
|
|
35
|
-
type Input = TypeScriptAngularServicesGeneratorInput;
|
|
36
|
-
type Output = TypeScriptAngularServicesGeneratorOutput;
|
|
37
|
-
type Config = TypeScriptAngularServicesGeneratorConfig;
|
|
38
|
-
type ServiceOutput = TypeScriptAngularServiceGeneratorOutput;
|
|
39
|
-
type Context = TypeScriptAngularServicesGeneratorContext;
|
|
40
|
-
|
|
41
|
-
export class TypeScriptAngularServicesGenerator extends OpenApiServicesGenerationProviderBase<
|
|
42
|
-
Input,
|
|
43
|
-
Output,
|
|
44
|
-
Config,
|
|
45
|
-
ServiceOutput,
|
|
46
|
-
Context
|
|
47
|
-
> {
|
|
48
|
-
private readonly _clientGeneratorFactory: Factory<TypeScriptAngularServiceGenerator, []>;
|
|
49
|
-
|
|
50
|
-
constructor(clientGeneratorFactory?: Factory<TypeScriptAngularServiceGenerator, []>) {
|
|
51
|
-
super();
|
|
52
|
-
this._clientGeneratorFactory = clientGeneratorFactory ??
|
|
53
|
-
Factory.fromValue(new DefaultTypeScriptAngularServiceGenerator());
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
protected override initResult(): Output {
|
|
57
|
-
return {
|
|
58
|
-
typescript: {
|
|
59
|
-
services: {},
|
|
60
|
-
indexFiles: { services: undefined, responseModels: undefined },
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
protected override buildContext(
|
|
66
|
-
context: OpenApiGeneratorContext<Input>,
|
|
67
|
-
config?: Partial<Config> | undefined,
|
|
68
|
-
): Context {
|
|
69
|
-
const providerContext = this.getProviderContext(context, config, defaultTypeScriptAngularServicesGeneratorConfig);
|
|
70
|
-
return Object.assign(providerContext, {
|
|
71
|
-
refs: getReferenceFactories(providerContext.config),
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
protected override async generateAdditionalFiles(ctx: TypeScriptAngularServicesGeneratorContext): Promise<void> {
|
|
76
|
-
await this.generateUtilsFiles(ctx);
|
|
77
|
-
ctx.output.typescript.indexFiles = this.generateIndexFiles(ctx);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
protected override generateService(ctx: Context, service: ApiService): MaybePromise<ServiceOutput> {
|
|
81
|
-
const clientGenerator = this._clientGeneratorFactory.create();
|
|
82
|
-
return clientGenerator.generate({
|
|
83
|
-
...ctx,
|
|
84
|
-
service,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
protected override addServiceResult(ctx: Context, service: ApiService, result: ServiceOutput): void {
|
|
89
|
-
ctx.output.typescript.services[service.id] = result;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// #region Utils
|
|
93
|
-
protected async generateUtilsFiles(ctx: Context): Promise<void> {
|
|
94
|
-
const targetDir = resolve(ctx.config.outputDir, ctx.config.utilsDir);
|
|
95
|
-
await fs.ensureDir(targetDir);
|
|
96
|
-
|
|
97
|
-
await copyAssetFile('client/angular/request-builder.ts', targetDir, 'Request Builder');
|
|
98
|
-
await copyAssetFile('client/angular/angular-service.utils.ts', targetDir, 'Angular Service Utils');
|
|
99
|
-
|
|
100
|
-
TypeScriptFileBuilder.generate({
|
|
101
|
-
logName: 'api configuration',
|
|
102
|
-
filePath: resolve(targetDir, 'api-configuration.ts'),
|
|
103
|
-
options: ctx.config,
|
|
104
|
-
generator: (b) => b.append(this.getApiConfigurationFileContent(ctx)),
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
TypeScriptFileBuilder.generate({
|
|
108
|
-
logName: 'api base service',
|
|
109
|
-
filePath: resolve(targetDir, 'api-base-service.ts'),
|
|
110
|
-
options: ctx.config,
|
|
111
|
-
generator: (b) => b.append(this.getApiBaseServiceFileContent(ctx)),
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
if (!ctx.config.strictResponseTypes) {
|
|
115
|
-
TypeScriptFileBuilder.generate({
|
|
116
|
-
logName: 'http status codes',
|
|
117
|
-
filePath: resolve(targetDir, 'http-status-code.ts'),
|
|
118
|
-
options: ctx.config,
|
|
119
|
-
generator: (b) => b.append(this.getHttpStatusCodeFileContent(ctx)),
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (ctx.config.provideKind === 'provide-fn') {
|
|
124
|
-
TypeScriptFileBuilder.generate({
|
|
125
|
-
logName: 'providers',
|
|
126
|
-
filePath: resolve(targetDir, 'provide.ts'),
|
|
127
|
-
options: ctx.config,
|
|
128
|
-
generator: (b) => b.append(this.getProvideFnFileContent(ctx)),
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
protected getApiConfigurationFileContent(ctx: Context): AppendValueGroup<TypeScriptFileBuilder> {
|
|
134
|
-
return appendValueGroup(
|
|
135
|
-
[
|
|
136
|
-
ts.class(toCasing(`${ctx.config.domainName ?? ''}ApiConfiguration`, ctx.config.typeNameCasing), {
|
|
137
|
-
doc: ts.doc({ description: `${ctx.config.domainName ?? 'Global'} API Configuration.` }),
|
|
138
|
-
decorators: [
|
|
139
|
-
ts.decorator(ts.refs.angular.injectable(), [
|
|
140
|
-
ctx.config.provideKind === 'root' ? ts.toNode({ providedIn: 'root' }) : null,
|
|
141
|
-
]),
|
|
142
|
-
],
|
|
143
|
-
export: true,
|
|
144
|
-
members: [
|
|
145
|
-
ts.property('rootUrl', {
|
|
146
|
-
accessModifier: 'public',
|
|
147
|
-
type: ts.refs.string(),
|
|
148
|
-
value: ts.string(this.getRootUrl(ctx)),
|
|
149
|
-
}),
|
|
150
|
-
],
|
|
151
|
-
}),
|
|
152
|
-
],
|
|
153
|
-
'\n',
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
protected getApiBaseServiceFileContent(ctx: Context): AppendValueGroup<TypeScriptFileBuilder> {
|
|
158
|
-
return appendValueGroup(
|
|
159
|
-
[
|
|
160
|
-
ts.class('ApiBaseService', {
|
|
161
|
-
doc: ts.doc({ description: 'Base class for API services.' }),
|
|
162
|
-
export: true,
|
|
163
|
-
abstract: true,
|
|
164
|
-
members: [
|
|
165
|
-
ts.property('config', {
|
|
166
|
-
accessModifier: 'protected',
|
|
167
|
-
value: (b) => b.append(ts.refs.angular.inject(), '(', ctx.refs.apiConfiguration(), ')'),
|
|
168
|
-
}),
|
|
169
|
-
ts.property('http', {
|
|
170
|
-
accessModifier: 'protected',
|
|
171
|
-
value: (b) => b.append(ts.refs.angular.inject(), '(', ts.refs.angular.httpClient(), ')'),
|
|
172
|
-
}),
|
|
173
|
-
ts.property('_rootUrl', { accessModifier: 'private', type: ts.refs.string(), value: ts.string('') }),
|
|
174
|
-
ts.property('rootUrl', {
|
|
175
|
-
doc: ts.doc({
|
|
176
|
-
description:
|
|
177
|
-
`Gets or sets the root URL for API operations provided by this service.\nFalls back to \`${ctx.refs.apiConfiguration.refName}.rootUrl\` if not set in this service.`,
|
|
178
|
-
}),
|
|
179
|
-
accessModifier: 'public',
|
|
180
|
-
type: ts.refs.string(),
|
|
181
|
-
get: ts.property.getter({ body: 'return this._rootUrl || this.config.rootUrl;' }),
|
|
182
|
-
set: ts.property.setter({ body: 'this._rootUrl = value;' }),
|
|
183
|
-
}),
|
|
184
|
-
],
|
|
185
|
-
}),
|
|
186
|
-
],
|
|
187
|
-
'\n',
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
protected getHttpStatusCodeFileContent(ctx: Context): AppendValueGroup<TypeScriptFileBuilder> {
|
|
192
|
-
return appendValueGroup(
|
|
193
|
-
[
|
|
194
|
-
ts.typeAlias('HttpStatusCode', ts.unionType(ctx.config.possibleStatusCodes), {
|
|
195
|
-
doc: ts.doc({ description: 'The possible status codes that any API endpoint can return.' }),
|
|
196
|
-
export: true,
|
|
197
|
-
}),
|
|
198
|
-
],
|
|
199
|
-
'\n',
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
protected getProvideFnFileContent(ctx: Context): AppendValueGroup<TypeScriptFileBuilder> {
|
|
204
|
-
return appendValueGroup(
|
|
205
|
-
[
|
|
206
|
-
ts.function(toCasing(`provide_${ctx.config.domainName ?? ''}_Api`, ctx.config.functionNameCasing), {
|
|
207
|
-
doc: ts.doc({
|
|
208
|
-
description: 'Provides all the API services' +
|
|
209
|
-
(ctx.config.domainName ? ` for ${ctx.config.domainName}` : '') + '.',
|
|
210
|
-
}),
|
|
211
|
-
export: true,
|
|
212
|
-
parameters: [ts.parameter('config', { type: ctx.refs.apiConfiguration(), optional: true })],
|
|
213
|
-
returnType: ts.refs.angular.provider(),
|
|
214
|
-
body: appendValueGroup([
|
|
215
|
-
'return ',
|
|
216
|
-
ts.tuple([
|
|
217
|
-
(b) =>
|
|
218
|
-
b.append(
|
|
219
|
-
'config ? { provide: ',
|
|
220
|
-
ctx.refs.apiConfiguration(),
|
|
221
|
-
', useValue: config } : ',
|
|
222
|
-
ctx.refs.apiConfiguration(),
|
|
223
|
-
),
|
|
224
|
-
...Object.values(ctx.output.typescript.services).map((x) => ts.reference(x.component, x.filePath)),
|
|
225
|
-
]),
|
|
226
|
-
';',
|
|
227
|
-
]),
|
|
228
|
-
}),
|
|
229
|
-
],
|
|
230
|
-
'\n',
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
protected getRootUrl(ctx: Context): string {
|
|
235
|
-
return modifyString<[]>(
|
|
236
|
-
(ctx.data.services[0].$src ?? ctx.data.services[0].endpoints[0]?.$src)?.document.servers?.[0]?.url ?? '/',
|
|
237
|
-
ctx.config.rootUrl,
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
// #endregion
|
|
241
|
-
|
|
242
|
-
// #region Index
|
|
243
|
-
protected generateIndexFiles(ctx: Context): Output['typescript']['indexFiles'] {
|
|
244
|
-
const servicesIndexFilePath = this.getServicesIndexFilePath(ctx);
|
|
245
|
-
const responseModelsIndexFilePath = this.getResponseModelsIndexFilePath(ctx);
|
|
246
|
-
|
|
247
|
-
TypeScriptFileBuilder.tryGenerate({
|
|
248
|
-
logName: 'services index file',
|
|
249
|
-
filePath: servicesIndexFilePath,
|
|
250
|
-
options: ctx.config,
|
|
251
|
-
generator: (b) => b.append(this.getServicesIndexFileContent(ctx)),
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
TypeScriptFileBuilder.tryGenerate({
|
|
255
|
-
logName: 'response models index file',
|
|
256
|
-
filePath: responseModelsIndexFilePath,
|
|
257
|
-
options: ctx.config,
|
|
258
|
-
generator: (b) => b.append(this.getResponseModelsIndexFileContent(ctx)),
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
return {
|
|
262
|
-
services: servicesIndexFilePath,
|
|
263
|
-
responseModels: responseModelsIndexFilePath,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
protected getServicesIndexFileContent(ctx: Context): AppendValueGroup<TypeScriptFileBuilder> {
|
|
268
|
-
return appendValueGroup([
|
|
269
|
-
ts.export(ctx.refs.apiConfiguration.refName, ctx.refs.apiConfiguration.moduleNameOrfilePath),
|
|
270
|
-
ctx.config.provideKind === 'provide-fn'
|
|
271
|
-
? ts.export(ctx.refs.provide.refName, ctx.refs.provide.moduleNameOrfilePath)
|
|
272
|
-
: null,
|
|
273
|
-
...Object.values(ctx.output.typescript.services).map((x) => ts.export(x.component, x.filePath)),
|
|
274
|
-
]);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
protected getResponseModelsIndexFileContent(ctx: Context): AppendValueGroup<TypeScriptFileBuilder> {
|
|
278
|
-
return appendValueGroup(
|
|
279
|
-
Object.values(ctx.output.typescript.services)
|
|
280
|
-
.flatMap((x) => Object.values(x.responseModels))
|
|
281
|
-
.map((x) => ts.export(x.component, x.filePath, { kind: 'type-export' })),
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
protected getServicesIndexFilePath(ctx: Context): string | null {
|
|
286
|
-
return ctx.config.servicesIndexFile ? resolve(ctx.config.outputDir, ctx.config.servicesIndexFile) : null;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
protected getResponseModelsIndexFilePath(ctx: Context): string | null {
|
|
290
|
-
return ctx.config.responseModelsIndexFile
|
|
291
|
-
? resolve(ctx.config.outputDir, ctx.config.responseModelsIndexFile)
|
|
292
|
-
: null;
|
|
293
|
-
}
|
|
294
|
-
// #endregion
|
|
295
|
-
}
|