@devlearning/swagger-generator 0.0.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/src/index.ts ADDED
@@ -0,0 +1,383 @@
1
+ #!/usr/bin/env
2
+
3
+ import fetch from 'node-fetch';
4
+ import * as fs from 'fs';
5
+ import { Swagger, SwaggerComponent, SwaggerComponentProperty, SwaggerMethod, SwaggerSchema } from './models/swagger.js';
6
+ import { SwaggerDownloader } from './swagger-downloader.js';
7
+
8
+ var args = process.argv.slice(2);
9
+
10
+ if (args.length !== 2) {
11
+ console.log("Warning: Requires 2 arguments");
12
+ console.log("node index.js [swaggerJsonUrl] [outputDirectory]");
13
+ process.exit();
14
+ }
15
+
16
+ const swaggerJsonUrl = args[0];
17
+ const outputDirectory = args[1];
18
+
19
+ //const excludedModels = ['Type', 'MethodBase', 'Assembly', 'MethodInfo']
20
+ // const apiUrl = args[0]//"http://localhost:5208";
21
+ // const version = args[1]; //"1";
22
+ // const swaggerJsonUrl = `${apiUrl}/swagger/v${version}/swagger.json`;
23
+ const contentType = 'application/json';
24
+
25
+ export class Generator {
26
+ private _swagger: Swagger;
27
+
28
+ constructor(swagger: Swagger) {
29
+ this._swagger = swagger;
30
+ }
31
+
32
+ generateApi() {
33
+ console.debug(`Start autogeneration Apis`);
34
+
35
+ let apiMethods = ``;
36
+ for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.paths).length; index++) {
37
+ const apiName = Object.getOwnPropertyNames(this._swagger.paths)[index];
38
+ const swaggerMethod = this._swagger.paths[apiName];
39
+ const method = Object.getOwnPropertyNames(swaggerMethod)[0];
40
+ const swaggerMethodInfo = swaggerMethod[method];
41
+ console.debug(`\tAPI - ${apiName} - ${method}`);
42
+
43
+ let parametersString = this.retrieveParameters(swaggerMethodInfo);
44
+ let queryParameters = this.retrieveQueryParameters(swaggerMethodInfo);
45
+ let returnTypeString = this.retrieveReturnType(swaggerMethodInfo);
46
+ if (returnTypeString == null) returnTypeString = 'void';
47
+ let prepareRequestString = ``; //request = this._handleRequest(request);
48
+ let haveRequest = swaggerMethodInfo.requestBody != null;
49
+
50
+ if (haveRequest) {
51
+ prepareRequestString = `request = this._handleRequest(request);
52
+ `;
53
+ }
54
+
55
+ apiMethods +=
56
+ `
57
+ public ${apiName.replace('/api/v{version}/', '').replaceAll('/', '_')}(${parametersString}): Observable<${returnTypeString}>{
58
+ ${prepareRequestString}return this._http.${method}<${returnTypeString}>(\`\${environment.BASE_URL}${apiName.replace('{version}', '1')}${queryParameters}\`${haveRequest ? ', request' : ''}, httpOptions)
59
+ .pipe(
60
+ map(x => this._handleResponse(x)),
61
+ catchError((err, obs) => {
62
+ return this._handleError(err, <Observable<any>>obs);
63
+ })
64
+ );
65
+ }
66
+ `;
67
+ }
68
+
69
+ fs.writeFileSync(outputDirectory + "/api.autogenerated.ts",
70
+ `${apiPre}
71
+ ${apiMethods}
72
+ ${apiPost}`,
73
+ { flag: 'w' });
74
+ }
75
+
76
+ generateModel() {
77
+ let usedTypes: string[] = [];
78
+
79
+ for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.paths).length; index++) {
80
+ const apiName = Object.getOwnPropertyNames(this._swagger.paths)[index];
81
+ const swaggerMethod = this._swagger.paths[apiName];
82
+ const method = Object.getOwnPropertyNames(swaggerMethod)[0];
83
+ const swaggerMethodInfo = swaggerMethod[method];
84
+
85
+ // if(apiName == "/api/v{version}/Ticket/Create"){
86
+ // debugger
87
+ // }
88
+ let parametersRefType = swaggerMethodInfo.parameters.filter(x => x.in == 'query' && x.schema?.$ref != null).map(x => x.schema.$ref.replace('#/components/schemas/', ''))
89
+ usedTypes = usedTypes.concat(parametersRefType);
90
+
91
+ if (swaggerMethodInfo.responses[200].content[contentType].schema.$ref != null) {
92
+ usedTypes.push(swaggerMethodInfo.responses[200].content[contentType].schema.$ref.replace('#/components/schemas/', ''));
93
+ }
94
+
95
+ if (swaggerMethodInfo.requestBody?.content[contentType]?.schema?.$ref) {
96
+ usedTypes.push(swaggerMethodInfo.requestBody?.content[contentType]?.schema?.$ref.replace('#/components/schemas/', ''));
97
+ }
98
+ }
99
+
100
+ this.retrieveNestedObjects(usedTypes);
101
+
102
+ usedTypes = [...new Set(usedTypes.map(item => item))]; // [ 'A', 'B']
103
+
104
+ // usedTypes = usedTypes.filter((value, index, array) => {
105
+ // array.indexOf(value) === index;
106
+ // });
107
+ // usedTypes.forEach(element => {
108
+ // console.debug(element);
109
+ // });
110
+
111
+ console.debug(`Start autogeneration Models`);
112
+ let models = ``;
113
+ for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.components.schemas).length; index++) {
114
+ const modelName = Object.getOwnPropertyNames(this._swagger.components.schemas)[index];
115
+
116
+ if (modelName == 'ActivatedVoucherSnackListResponse') {
117
+ console.debug("ActivatedVoucherSnackListResponse");
118
+ }
119
+
120
+ if (usedTypes.indexOf(modelName) < 0) {
121
+ console.debug(`\tModel SKIP - ${modelName}`);
122
+ continue
123
+ };
124
+
125
+ const swaggerCopmponent = this._swagger.components.schemas[modelName];
126
+
127
+ console.debug(`\tModel - ${modelName}`);
128
+
129
+ let type = swaggerCopmponent.type == 'integer' ? 'enum' : 'class';
130
+ let content = this.retrieveObjectContent(modelName, swaggerCopmponent);
131
+
132
+ models +=
133
+ `
134
+ export ${type} ${modelName} {${content}
135
+ }
136
+ `;
137
+ }
138
+
139
+ fs.writeFileSync(outputDirectory + "/model.autogenerated.ts",
140
+ `${modelPre}
141
+ ${models}
142
+ ${modelPost}`,
143
+ { flag: 'w' });
144
+ }
145
+
146
+ retrieveParameters(swaggerMethodInfo: SwaggerMethod) {
147
+ if (swaggerMethodInfo.requestBody != null) {
148
+ return `request: ${swaggerMethodInfo.requestBody.content[contentType].schema.$ref.replace('#/components/schemas/', '')}`
149
+ }
150
+
151
+ let parameters = ``;
152
+ swaggerMethodInfo.parameters.filter(x => x.in == 'query').forEach(parameter => {
153
+ if (parameter.schema.$ref != null) {
154
+ parameters += `${parameter.name}: ${parameter.schema.$ref.replace('#/components/schemas/', '')}, `;
155
+ } else {
156
+ parameters += `${parameter.name}: ${this.getNativeType(parameter.schema)}, `;
157
+ }
158
+ });
159
+
160
+ if (parameters.length > 2)
161
+ parameters = parameters.substring(0, parameters.length - 2);
162
+
163
+ return parameters;
164
+ }
165
+
166
+ retrieveQueryParameters(swaggerMethodInfo: SwaggerMethod) {
167
+ if (swaggerMethodInfo.requestBody != null) return ``;
168
+
169
+ let filteredParameters = swaggerMethodInfo.parameters.filter(x => x.in == 'query');
170
+ if (filteredParameters.length == 0) return ``;
171
+
172
+ let parameters = `?`;
173
+ filteredParameters.forEach(parameter => {
174
+ if (parameter.schema.$ref != null) {
175
+ this.parametrizeObject(parameter.schema.$ref)
176
+ //parameters += `${parameter.name}: ${parameter.schema.$ref.replace('#/components/schemas/', '')}&`;
177
+ } else {
178
+ parameters += `${this.lowercaseFirstLetter(parameter.name)}=\${` + this.lowercaseFirstLetter(parameter.name) + `}&`;
179
+ }
180
+ });
181
+
182
+ if (parameters.length > 1)
183
+ parameters = parameters.substring(0, parameters.length - 1);
184
+
185
+ return parameters;
186
+ }
187
+
188
+ retrieveReturnType(swaggerMethodInfo: SwaggerMethod) {
189
+ if (swaggerMethodInfo.responses[200] == null)
190
+ return 'void';
191
+
192
+ if (swaggerMethodInfo.responses[200].content[contentType].schema.$ref != null)
193
+ return swaggerMethodInfo.responses[200]?.content[contentType].schema.$ref.replace('#/components/schemas/', '')
194
+
195
+ if (swaggerMethodInfo.responses[200]?.content[contentType].schema.type != null)
196
+ return this.getNativeType(swaggerMethodInfo.responses[200]?.content[contentType].schema);
197
+
198
+ console.error("unmanaged swaggerMethodInfo", swaggerMethodInfo);
199
+ throw new Error("unmanaged swaggerMethodInfo");
200
+ }
201
+
202
+ retrieveType(swaggerComponentProperty: SwaggerComponentProperty) {
203
+ if (swaggerComponentProperty.$ref != null)
204
+ return swaggerComponentProperty.$ref.replace('#/components/schemas/', '');
205
+
206
+ if (swaggerComponentProperty.type != null && swaggerComponentProperty.type == 'array')
207
+ if (swaggerComponentProperty.items.$ref != null)
208
+ return swaggerComponentProperty.items.$ref.replace('#/components/schemas/', '');
209
+ else
210
+ return this.getNativeType(swaggerComponentProperty);
211
+
212
+ if (swaggerComponentProperty.type != null)
213
+ return this.getNativeType(swaggerComponentProperty);
214
+
215
+ if (swaggerComponentProperty.type == null)
216
+ return '';
217
+
218
+ console.error("unmanaged swaggerMethodInfo", swaggerComponentProperty);
219
+ throw new Error("unmanaged swaggerMethodInfo");
220
+ }
221
+
222
+ parametrizeObject(objectName: string) {
223
+ let component = this._swagger.components.schemas[objectName.replace('#/components/schemas/', '')];
224
+ if (component == null || component.properties == null) return ``;
225
+
226
+ console.debug(component.properties);
227
+
228
+ return ``;
229
+ }
230
+
231
+ retrieveObjectContent(name: string, swaggerComponent: SwaggerComponent) {
232
+ if (swaggerComponent.type == 'object')
233
+ return this.retrieveObjectProperties(swaggerComponent);
234
+ else if (swaggerComponent.type == 'integer')
235
+ return this.retrieveEnumValues(name, swaggerComponent);
236
+ }
237
+
238
+ retrieveObjectProperties(swaggerCopmponent: SwaggerComponent) {
239
+ if (swaggerCopmponent.properties == null) return ``;
240
+
241
+ // console.debug(`\t\t${Object.getOwnPropertyNames(swaggerCopmponent.properties).length}`);
242
+
243
+ let properties = ``;
244
+ for (let index = 0; index < Object.getOwnPropertyNames(swaggerCopmponent.properties).length; index++) {
245
+ const propertyName = Object.getOwnPropertyNames(swaggerCopmponent.properties)[index];
246
+ properties +=
247
+ `
248
+ ${propertyName}: ${this.retrieveType(swaggerCopmponent.properties[propertyName])} | undefined;`
249
+ }
250
+
251
+ return properties;
252
+ }
253
+
254
+ retrieveEnumValues(name: string, swaggerCopmponent: SwaggerComponent) {
255
+ if (swaggerCopmponent.enum == null) return ``;
256
+
257
+ let properties = ``;
258
+ for (let index = 0; index < swaggerCopmponent.enum.length; index++) {
259
+ const name = swaggerCopmponent.enum[index].split('-')[0].trim();
260
+ const value = swaggerCopmponent.enum[index].split('-')[1].trim();
261
+ properties +=
262
+ `
263
+ ${name} = ${value},`;
264
+ }
265
+
266
+ return properties;
267
+ }
268
+
269
+ retrieveNestedObjects(usedTypes: string[]) {
270
+ for (let i = 0; i < usedTypes.length; i++) {
271
+ const swaggerCopmponent = this._swagger.components.schemas[usedTypes[i]];
272
+ // const name = usedTypes[i]
273
+ // const modelName = <string>Object.getOwnPropertyNames(this._swagger.components.schemas)[name];
274
+ this.retrieveNestedObjects2(swaggerCopmponent, usedTypes);
275
+ }
276
+ }
277
+
278
+ /*
279
+ "ActivatedVoucherSnackReadQueryResponse": {
280
+ "type": "object",
281
+ "properties": {
282
+ "list": {
283
+ "type": "array",
284
+ "items": {
285
+ "$ref": "#/components/schemas/ActivatedVoucherSnackListResponse"
286
+ },
287
+ "nullable": true
288
+ }
289
+ },
290
+ "additionalProperties": false
291
+ },
292
+ */
293
+
294
+ retrieveNestedObjects2(swaggerComponent: SwaggerComponent, usedTypes: string[]) {
295
+ if (!swaggerComponent.properties) return;
296
+
297
+ for (let j = 0; j < Object.getOwnPropertyNames(swaggerComponent.properties).length; j++) {
298
+ const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[j];
299
+ let nestedUsedType = '';
300
+ if (swaggerComponent.properties[propertyName].$ref != null) {
301
+ nestedUsedType = swaggerComponent.properties[propertyName].$ref.replace('#/components/schemas/', '');
302
+ } else if (swaggerComponent.properties[propertyName].type == 'array' && swaggerComponent.properties[propertyName].items.$ref != null) {
303
+ nestedUsedType = swaggerComponent.properties[propertyName].items.$ref.replace('#/components/schemas/', '');
304
+ }
305
+
306
+ if (nestedUsedType != '' && usedTypes.findIndex(x => x == nestedUsedType) == -1) {
307
+ usedTypes.push(nestedUsedType);
308
+ let nested = this._swagger.components.schemas[nestedUsedType];
309
+ this.retrieveNestedObjects2(nested, usedTypes);
310
+ }
311
+ }
312
+ }
313
+
314
+ getNativeType(schema: SwaggerSchema): string {
315
+ if (schema.type == 'integer') return 'number';
316
+ if (schema.type == 'string' && schema.format == null) return 'string';
317
+ if (schema.type == 'string' && schema.format == 'date-time') return 'moment.Moment';
318
+ if (schema.type == 'string' && schema.format == 'uuid') return 'string';
319
+ if (schema.type == 'number') return 'number';
320
+ if (schema.type == 'array') return '[]';
321
+ if (schema.type == 'boolean') return 'boolean';
322
+ if (schema.type == 'object') return 'any';
323
+
324
+ console.error("unmanaged schema type", schema);
325
+ throw new Error("unmanaged schema");
326
+ }
327
+
328
+ lowercaseFirstLetter(value: string) {
329
+ return value.charAt(0).toLowerCase() + value.slice(1);
330
+ }
331
+ }
332
+
333
+ const swaggerDownloader = new SwaggerDownloader();
334
+ const apiPre =
335
+ `import { HttpClient } from '@angular/common/http';
336
+ import { Injectable } from '@angular/core';
337
+ import { Observable, catchError, map } from 'rxjs';
338
+ import { httpOptions } from 'src/app/core/utils/http-options';
339
+ import { DatiUtenteTestataResponse } from '../models/dati-utente-testata-response';
340
+ import { LanguageCultureListQueryResponse } from '../models/language-culture';
341
+ import { API_AIRLINE_COMPANY, API_LANGUAGE_CULTURE, API_TEST, API_USER, API_VOUCHER_SNACK } from '../utils/constants';
342
+ import { ApiBase } from './../utils/api-base';
343
+ import { DialogMessageService } from './dialog-message.service';
344
+ import * as moment from 'moment-timezone';
345
+ import { VoucherSnackResponse } from '../models/api/voucher-snack-response.models';
346
+ import { VoucherSnackRequest } from '../models/api/voucher-snack-request.models';
347
+ import { ActivatedSnackVoucherResponse, SnackType } from 'src/app/ticket/models/richiesta-servizio-snack.models';
348
+ import { VoucherSnackRequestEmission } from '../models/api/voucher-snack-request-emission';
349
+ import { ActivateSnackVoucherResponse } from '../models/api/activate-snack-voucher-response.models';
350
+ import { VoucherActivationRequest } from '../models/api/voucher-activation-request.model';
351
+ import { Utente } from '../models/utente';
352
+ import { AirlineCompany } from 'src/app/sharedmodules/dialog-compagnie-aeree/compagnia-aerea.model';
353
+
354
+ @Injectable({
355
+ providedIn: 'root',
356
+ })
357
+ export class ApiService extends ApiBase {
358
+ constructor(
359
+ private _http: HttpClient,
360
+ _dialogMessage: DialogMessageService
361
+ ) {
362
+ super(_dialogMessage);
363
+ }
364
+ `;
365
+ const apiPost =
366
+ `}`;
367
+
368
+ const modelPre =
369
+ `import * as moment from 'moment';
370
+ `;
371
+
372
+ const modelPost =
373
+ `
374
+ `;
375
+
376
+ swaggerDownloader.download(new URL(swaggerJsonUrl))
377
+ .then(swaggerDoc => {
378
+ return new Generator(swaggerDoc);
379
+ })
380
+ .then(generator => { generator.generateApi(); generator.generateModel() });
381
+
382
+
383
+ // require('./index.js')({swaggerDownloader});
@@ -0,0 +1,63 @@
1
+ export interface Swagger {
2
+ openApi: string;
3
+ info: SwaggerInfo;
4
+ paths: { [key: string]: { [key: string]: SwaggerMethod; }; };
5
+ components: SwaggerComponents;
6
+ }
7
+
8
+ export interface SwaggerInfo {
9
+ title: string;
10
+ version: string;
11
+ }
12
+
13
+ export interface SwaggerMethod {
14
+ tags: string[];
15
+ parameters: SwaggerParameter[];
16
+ requestBody: SwaggerRequestBody;
17
+ responses: { [key: string]: SwaggerResponses; };
18
+ }
19
+
20
+ export interface SwaggerParameter {
21
+ name: string;
22
+ in: string;
23
+ required: boolean;
24
+ schema: SwaggerSchema;
25
+ }
26
+
27
+ export interface SwaggerRequestBody {
28
+ content: { [key: string]: SwaggerContent; };
29
+ }
30
+
31
+ export interface SwaggerResponses {
32
+ description: string;
33
+ content: { [key: string]: SwaggerContent; };
34
+ }
35
+
36
+ export interface SwaggerComponents {
37
+ schemas: { [key: string]: SwaggerComponent; };
38
+ }
39
+
40
+ export interface SwaggerComponent {
41
+ type: string;
42
+ properties: { [key: string]: SwaggerComponentProperty; };
43
+ additionalProperties: boolean;
44
+ enum: string[];
45
+ }
46
+
47
+ export interface SwaggerComponentProperty {
48
+ type: string;
49
+ $ref: string;
50
+ format: string;
51
+ items: SwaggerSchema;
52
+ }
53
+
54
+ export interface SwaggerContent {
55
+ schema: SwaggerSchema;
56
+ }
57
+
58
+ export interface SwaggerSchema {
59
+ type: string;
60
+ $ref: string;
61
+ format: string;
62
+ items: SwaggerSchema;
63
+ }
@@ -0,0 +1,11 @@
1
+ import { Swagger } from "./models/swagger.js";
2
+
3
+ const settings: RequestInit = { method: "Get" };
4
+
5
+ export class SwaggerDownloader {
6
+ async download(swaggerJsonUrl: URL) {
7
+ let response = await fetch(swaggerJsonUrl, settings);
8
+ let json = await response.json();
9
+ return <Swagger>json;
10
+ }
11
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "baseUrl": "./src",
6
+ "moduleResolution": "NodeNext",
7
+ "paths": {
8
+ "@src/*": [
9
+ "*"
10
+ ]
11
+ },
12
+ "outDir": "./dist",
13
+ "esModuleInterop": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "strict": true,
16
+ "skipLibCheck": true
17
+ },
18
+ "include": [
19
+ "src/**/*.ts"
20
+ ],
21
+ "exclude": [
22
+ "node_modules"
23
+ ],
24
+ "ts-node": {
25
+ "require": [
26
+ "tsconfig-paths/register"
27
+ ]
28
+ }
29
+ }
30
+
31
+ // {
32
+ // "compilerOptions": {
33
+ // "target": "es6",
34
+ // "module": "commonjs",
35
+ // "esModuleInterop": true,
36
+ // "outDir": "./dist",
37
+ // "rootDir": "./src",
38
+ // "resolveJsonModule": true,
39
+ // "sourceMap": true
40
+ // },
41
+ // "include": [
42
+ // "src//*.ts"
43
+ // ],
44
+ // "exclude": [
45
+ // "node_modules",
46
+ // "/*.spec.ts"
47
+ // ]
48
+ // }