@devlearning/swagger-generator 1.1.21 → 1.1.23

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.
Files changed (56) hide show
  1. package/.vscode/launch.json +28 -28
  2. package/README-OLD.md +209 -209
  3. package/README.md +277 -277
  4. package/dist/api.constants.d.ts +1 -1
  5. package/dist/api.constants.js +22 -22
  6. package/dist/generators-writers/angular/api-angular-writer.js +38 -38
  7. package/dist/generators-writers/angular/constants.js +24 -24
  8. package/dist/generators-writers/angular/model-angular-writer.js +6 -6
  9. package/dist/generators-writers/dart/model-dart-writer.js +11 -4
  10. package/dist/generators-writers/dart/templates/api.mustache +143 -143
  11. package/dist/generators-writers/dart/templates/enum.mustache +14 -14
  12. package/dist/generators-writers/dart/templates/model.mustache +23 -23
  13. package/dist/generators-writers/nextjs/api-nextjs-writer.js +12 -12
  14. package/dist/generators-writers/nextjs/constants.js +4 -4
  15. package/dist/generators-writers/nextjs/model-nextjs-writer.js +6 -6
  16. package/dist/generators-writers/utils.d.ts +1 -0
  17. package/dist/generators-writers/utils.js +16 -4
  18. package/dist/templates/api.mustache +29 -0
  19. package/dist/templates/model.mustache +18 -0
  20. package/package.json +49 -49
  21. package/src/api.constants.ts +26 -26
  22. package/src/generator-old.ts +449 -449
  23. package/src/generator.ts +752 -752
  24. package/src/generators-writers/angular/api-angular-writer.ts +187 -187
  25. package/src/generators-writers/angular/constants.ts +36 -36
  26. package/src/generators-writers/angular/model-angular-writer.ts +65 -65
  27. package/src/generators-writers/angular/normalizator.ts +41 -41
  28. package/src/generators-writers/dart/api-dart-writer.ts +303 -303
  29. package/src/generators-writers/dart/model-dart-writer.ts +226 -219
  30. package/src/generators-writers/dart/models/import-definition-dart.ts +5 -5
  31. package/src/generators-writers/dart/normalizator.ts +72 -72
  32. package/src/generators-writers/dart/templates/api.mustache +143 -143
  33. package/src/generators-writers/dart/templates/enum.mustache +14 -14
  34. package/src/generators-writers/dart/templates/model.mustache +23 -23
  35. package/src/generators-writers/nextjs/api-nextjs-writer.ts +157 -157
  36. package/src/generators-writers/nextjs/constants.ts +5 -5
  37. package/src/generators-writers/nextjs/model-nextjs-writer.ts +61 -61
  38. package/src/generators-writers/utils.ts +111 -93
  39. package/src/index.ts +103 -103
  40. package/src/models/api-dto.ts +17 -17
  41. package/src/models/enum-value-dto.ts +3 -3
  42. package/src/models/model-dto.ts +9 -9
  43. package/src/models/parameter-dto.ts +7 -7
  44. package/src/models/property-dto.ts +4 -4
  45. package/src/models/swagger/swagger-component-property.ts +11 -11
  46. package/src/models/swagger/swagger-component.ts +17 -17
  47. package/src/models/swagger/swagger-content.ts +4 -4
  48. package/src/models/swagger/swagger-info.ts +3 -3
  49. package/src/models/swagger/swagger-method.ts +7 -7
  50. package/src/models/swagger/swagger-schema.ts +21 -21
  51. package/src/models/swagger/swagger.ts +38 -38
  52. package/src/models/type-dto.ts +7 -7
  53. package/src/swagger-downloader.ts +46 -46
  54. package/src/utils/logger.ts +73 -73
  55. package/src/utils/swagger-validator.ts +89 -89
  56. package/tsconfig.json +33 -33
package/src/generator.ts CHANGED
@@ -1,753 +1,753 @@
1
- import fs from 'fs';
2
- import { SwaggerComponent } from './models/swagger/swagger-component.js';
3
- import { Swagger } from './models/swagger/swagger.js';
4
- import { SwaggerMethod } from './models/swagger/swagger-method.js';
5
- import { SwaggerSchema } from './models/swagger/swagger-schema.js';
6
- import { ApiDto } from './models/api-dto.js';
7
- import { TypeDto } from './models/type-dto.js';
8
- import { ParameterDto } from './models/parameter-dto.js';
9
- import { ApiNextJsWriter } from './generators-writers/nextjs/api-nextjs-writer.js';
10
- import { ApiAngularWriter } from './generators-writers/angular/api-angular-writer.js';
11
- import { ModelDto } from './models/model-dto.js';
12
- import { SingleBar, Presets } from 'cli-progress';
13
- import { ModelNextJsWriter } from './generators-writers/nextjs/model-nextjs-writer.js';
14
- import { ModelAngularWriter } from './generators-writers/angular/model-angular-writer.js';
15
- import { PropertyDto } from './models/property-dto.js';
16
- import { EnumValueDto } from './models/enum-value-dto.js';
17
- import { SwaggerContent } from './models/swagger/swagger-content.js';
18
- import { CommandLineArgs, DateTimeLibrary, TargetGeneration } from './index.js';
19
- import { ApiDartWriter } from './generators-writers/dart/api-dart-writer.js';
20
- import { ModelDartWriter } from './generators-writers/dart/model-dart-writer.js';
21
- import { Utils } from './generators-writers/utils.js';
22
- import { logger } from './utils/logger.js';
23
-
24
- const contentTypeApplicationJson = 'application/json';
25
- const contentTypeMultipartFormData = 'multipart/form-data';
26
-
27
- export class Generator {
28
- private _swagger: Swagger;
29
- private _commandLineArgs: CommandLineArgs;
30
-
31
- private _apis: ApiDto[] = [];
32
- private _models: ModelDto[] = [];
33
-
34
- private _barApis = new SingleBar({
35
- format: '{bar} {percentage}% | {message} {value}/{total} Elapsed: {duration_formatted}', // Formato della barra
36
- barCompleteChar: '\u2588', // Carattere pieno per la barra
37
- barIncompleteChar: '\u2591', // Carattere vuoto per la parte non completata
38
- hideCursor: true, // Nascondi il cursore durante l'esecuzione
39
- barsize: 20,
40
- }, Presets.shades_classic);
41
-
42
- private _barModels = new SingleBar({
43
- format: '{bar} {percentage}% | {message} {value}/{total} Elapsed: {duration_formatted}', // Formato della barra
44
- barCompleteChar: '\u2588', // Carattere pieno per la barra
45
- barIncompleteChar: '\u2591', // Carattere vuoto per la parte non completata
46
- hideCursor: true, // Nascondi il cursore durante l'esecuzione
47
- barsize: 20,
48
- }, Presets.shades_classic);
49
-
50
- constructor(swagger: Swagger, commandLineArgs: CommandLineArgs) {
51
- this._swagger = swagger;
52
- this._commandLineArgs = commandLineArgs;
53
- }
54
-
55
- generate() {
56
- logger.info('Starting Swagger API generation...');
57
-
58
- Utils.clearDirectory(this._commandLineArgs.outputDirectory);
59
-
60
- this.computeApi();
61
- this.computeModel();
62
-
63
- this.generateModel();
64
- this.generateApi();
65
-
66
- logger.success('Swagger generation completed successfully!');
67
- }
68
-
69
- computeApi() {
70
- this._barApis.start(Object.getOwnPropertyNames(this._swagger.paths).length, 0);
71
- for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.paths).length; index++) {
72
- const apiName = Object.getOwnPropertyNames(this._swagger.paths)[index];
73
- this._barApis.update(index, { message: `Apis parsing... ${apiName}` });
74
- const apiSwaggerMethodKey = this._swagger.paths[apiName];
75
- const apiMethod = Object.getOwnPropertyNames(apiSwaggerMethodKey)[0];
76
- const apiSwaggerMethod = apiSwaggerMethodKey[apiMethod];
77
-
78
- let apiDto: ApiDto = {
79
- name: apiName,
80
- url: apiName.replace('{version}', '1'),
81
- method: apiMethod,
82
- parameters: this.computeParameters(apiName, apiSwaggerMethod),
83
- returnType: this.computeResponseType(apiSwaggerMethod),
84
- swaggerMethodKey: apiSwaggerMethodKey,
85
- swaggerMethod: apiSwaggerMethod,
86
- haveRequest: apiSwaggerMethod.requestBody != null,
87
- isMultiPart: apiSwaggerMethod.requestBody != null && apiSwaggerMethod.requestBody.content[contentTypeMultipartFormData] != null,
88
- tag: apiSwaggerMethod.tags && apiSwaggerMethod.tags.length > 0 ? apiSwaggerMethod.tags[0] : 'default',
89
- };
90
- this._apis.push(apiDto);
91
- }
92
- }
93
-
94
- computeModel() {
95
- this._barModels.start(Object.getOwnPropertyNames(this._swagger.paths).length, 0);
96
-
97
- let usedTypes: TypeDto[] = [];
98
- let usedMultiPart: string[] = [];
99
-
100
- for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.paths).length; index++) {
101
- const apiName = Object.getOwnPropertyNames(this._swagger.paths)[index];
102
- const apiSwaggerMethodKey = this._swagger.paths[apiName];
103
- const apiMethod = Object.getOwnPropertyNames(apiSwaggerMethodKey)[0];
104
- const apiSwaggerMethod = apiSwaggerMethodKey[apiMethod];
105
-
106
- const multipartSchema = apiSwaggerMethod.requestBody?.content?.[contentTypeMultipartFormData]?.schema;
107
- // Only create an ad-hoc request model when the multipart schema is inline.
108
- // If the schema is a $ref, it's already part of components/schemas and will be generated there.
109
- if (multipartSchema != null && multipartSchema.$ref == null) {
110
- usedMultiPart.push(apiName);
111
- }
112
- }
113
-
114
- for (let index = 0; index < this._apis.length; index++) {
115
- if (this._apis[index].returnType && this._apis[index].returnType!.isTypeReference) {
116
- usedTypes.push(this._apis[index].returnType!);
117
- }
118
-
119
- if (this._apis[index].parameters) {
120
- this._apis[index].parameters.forEach(parameter => {
121
- if (parameter.isTypeReference) {
122
- usedTypes.push(parameter);
123
- }
124
- });
125
- }
126
- }
127
-
128
- usedTypes = [...new Set(usedTypes.map(item => item))]; // distinct
129
-
130
- this.retrieveNestedObjects(usedTypes);
131
-
132
- let models = ``;
133
- if (this._swagger.components != null
134
- && this._swagger.components != undefined
135
- && this._swagger.components.schemas != null
136
- && this._swagger.components.schemas != undefined) {
137
-
138
- for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.components.schemas).length; index++) {
139
- const modelName = Object.getOwnPropertyNames(this._swagger.components.schemas)[index];
140
-
141
- const swaggerComponent = this._swagger.components.schemas[modelName];
142
-
143
- // Flutter target: support enums of type string/integer and inline property enums.
144
- if (this._commandLineArgs.target === TargetGeneration.Flutter) {
145
- const isEnumSchema = swaggerComponent.enum != null && (swaggerComponent.type === 'string' || swaggerComponent.type === 'integer');
146
- this._models.push({
147
- typeName: modelName,
148
- modelType: isEnumSchema ? 'enum' : 'class',
149
- name: modelName,
150
- properties: (swaggerComponent.type === 'object') ? this.retrieveComponentPropertiesFlutter(modelName, swaggerComponent) : [],
151
- enumValues: isEnumSchema ? this.retrieveEnumValuesFlexible(swaggerComponent.enum) : [],
152
- });
153
- continue;
154
- }
155
-
156
- this._models.push({
157
- typeName: modelName,
158
- modelType: swaggerComponent.type == 'integer' ? 'enum' : 'class',
159
- name: modelName,
160
- properties: (swaggerComponent.type == 'object') ? this.retrieveComponentProperties(swaggerComponent) : [],
161
- enumValues: (swaggerComponent.type == 'integer') ? this.retrieveEnumValues(modelName, swaggerComponent) : [],
162
- });
163
- }
164
-
165
- }
166
-
167
- usedMultiPart.forEach(apiName => {
168
- const swaggerMethod = this._swagger.paths[apiName];
169
- const method = Object.getOwnPropertyNames(swaggerMethod)[0];
170
- const swaggerMethodInfo = swaggerMethod[method];
171
- const schema = swaggerMethodInfo.requestBody.content[contentTypeMultipartFormData].schema;
172
-
173
- if (schema?.$ref != null) {
174
- return;
175
- }
176
-
177
- this._models.push({
178
- typeName: this.getApiNameNormalized(apiName),
179
- modelType: 'class',
180
- name: this.getApiNameNormalized(apiName),
181
- properties: this.retrieveSchemaProperties(schema),
182
- enumValues: [],
183
- });
184
- });
185
- }
186
-
187
- generateApi() {
188
- this._barApis.update(this._apis.length, { message: `Api generating...` });
189
-
190
- if (this._commandLineArgs.target == TargetGeneration.Angular) {
191
- const apiWriter = new ApiAngularWriter(this._commandLineArgs);
192
- apiWriter.write(this._apis);
193
- } else if (this._commandLineArgs.target == TargetGeneration.Next) {
194
- const apiWriter = new ApiNextJsWriter(this._commandLineArgs);
195
- apiWriter.write(this._apis);
196
- } else if (this._commandLineArgs.target == TargetGeneration.Flutter) {
197
- const apiWriter = new ApiDartWriter(this._commandLineArgs);
198
- apiWriter.write(this._apis, this._models);
199
- }
200
-
201
- this._barApis.update(this._apis.length, { message: `Api was generated successfully` });
202
-
203
- this._barApis.stop();
204
- }
205
-
206
-
207
- generateModel() {
208
- this._barModels.update(this._apis.length, { message: `Model generation...` });
209
-
210
- if (this._commandLineArgs.target == TargetGeneration.Angular) {
211
- const apiWriter = new ModelAngularWriter(this._commandLineArgs.outputDirectory);
212
- apiWriter.write(this._models);
213
- } else if (this._commandLineArgs.target == TargetGeneration.Next) {
214
- const apiWriter = new ModelNextJsWriter(this._commandLineArgs.outputDirectory);
215
- apiWriter.write(this._models);
216
- } else if (this._commandLineArgs.target == TargetGeneration.Flutter) {
217
- const apiWriter = new ModelDartWriter(this._commandLineArgs);
218
- apiWriter.write(this._models);
219
- }
220
-
221
- this._barModels.update(this._apis.length, { message: `Model was generated successfully` });
222
-
223
- this._barModels.stop();
224
- }
225
-
226
- computeParameters(apiName: string, swaggerMethod: SwaggerMethod | undefined) {
227
- if (!apiName) return [];
228
- if (!swaggerMethod || swaggerMethod == null) return [];
229
-
230
- let parameters: ParameterDto[] = [];
231
- try {
232
- // Path params are always explicit parameters.
233
- swaggerMethod.parameters?.filter(x => x.in == 'path').forEach(parameter => {
234
- const name = this.toFirstLetterLowercase(parameter.name);
235
- if (parameter.schema?.$ref != null) {
236
- const type = this.retrieveType(parameter.schema);
237
- parameters.push({
238
- name,
239
- typeName: type.typeName,
240
- nullable: false,
241
- swaggerParameter: parameter,
242
- isQuery: false,
243
- isEnum: this.isEnum(parameter.schema.$ref),
244
- isTypeReference: false,
245
- isNativeType: false,
246
- isArray: false,
247
- isVoid: false,
248
- });
249
- } else {
250
- parameters.push({
251
- name,
252
- typeName: this.getNativeType(parameter.schema),
253
- nullable: false,
254
- swaggerParameter: parameter,
255
- isQuery: false,
256
- isEnum: false,
257
- isTypeReference: false,
258
- isNativeType: true,
259
- isArray: false,
260
- isVoid: false,
261
- });
262
- }
263
- });
264
-
265
- if (swaggerMethod.requestBody != null && swaggerMethod.requestBody.content[contentTypeMultipartFormData] != null) {
266
- const schema = swaggerMethod.requestBody.content[contentTypeMultipartFormData].schema;
267
- const type = this.retrieveType(schema);
268
- parameters.unshift({
269
- name: 'request',
270
- typeName: type.typeName,
271
- nullable: false,
272
- isQuery: false,
273
- isEnum: false,
274
- isTypeReference: type.isTypeReference ?? true,
275
- isNativeType: type.isNativeType ?? false,
276
- isArray: type.isArray ?? false,
277
- isVoid: type.isVoid ?? false,
278
- });
279
- } else {
280
- if (swaggerMethod.requestBody != null) {
281
- const type = this.retrieveType(swaggerMethod.requestBody.content[contentTypeApplicationJson].schema);
282
- parameters.unshift({
283
- name: 'request',
284
- typeName: type.typeName, //swaggerMethod.requestBody.content[contentTypeApplicationJson].schema.$ref.replace('#/components/schemas/', ''),
285
- nullable: false,
286
- isQuery: false,
287
- isEnum: false,
288
- isTypeReference: true,
289
- isNativeType: false,
290
- isArray: false,
291
- isVoid: false,
292
- });
293
- } else {
294
- swaggerMethod.parameters?.filter(x => x.in == 'query').forEach(parameter => {
295
- if (parameter.schema.$ref != null) {
296
- const type = this.retrieveType(parameter.schema);
297
- parameters.push({
298
- name: this.toFirstLetterLowercase(parameter.name),
299
- typeName: type.typeName, //parameter.schema.$ref.replace('#/components/schemas/', ''),
300
- nullable: !parameter.required,
301
- swaggerParameter: parameter,
302
- isQuery: true,
303
- isEnum: this.isEnum(parameter.schema.$ref),
304
- isTypeReference: false,
305
- isNativeType: false,
306
- isArray: false,
307
- isVoid: false,
308
- });
309
- } else {
310
- parameters.push({
311
- name: this.toFirstLetterLowercase(parameter.name),
312
- typeName: this.getNativeType(parameter.schema),
313
- nullable: !parameter.required,
314
- swaggerParameter: parameter,
315
- isQuery: true,
316
- isEnum: false,
317
- isTypeReference: false,
318
- isNativeType: true,
319
- isArray: false,
320
- isVoid: false,
321
- });
322
- }
323
- });
324
- }
325
- }
326
- } catch (error) {
327
- console.error(`\t\tError in ${apiName} - Attenzione forse hai dimenticato IActionResult e non hai tipizzato il tipo restituito dal servizio`);
328
- }
329
- return parameters;
330
- }
331
-
332
- computeResponseType(swaggerMethod: SwaggerMethod) {
333
- if (swaggerMethod.responses[200] == null) {
334
- return <TypeDto>{
335
- typeName: 'void',
336
- nullable: false,
337
- isVoid: true,
338
- }
339
- }
340
-
341
- return this.computeSwaggerContentType(swaggerMethod.responses[200].content[contentTypeApplicationJson]);
342
- }
343
-
344
- computeRequestBodyType(swaggerMethod: SwaggerMethod) {
345
- if (swaggerMethod.requestBody == null) {
346
- return <TypeDto>{
347
- typeName: 'void',
348
- nullable: false,
349
- isVoid: true,
350
- }
351
- }
352
-
353
- return this.computeSwaggerContentType(swaggerMethod.requestBody.content[contentTypeApplicationJson]);
354
- }
355
-
356
- computeSwaggerContentType(swaggerContent: SwaggerContent) {
357
- try {
358
- if (swaggerContent.schema.$ref != null)
359
- return <TypeDto>{
360
- typeName: this.getObjectName(swaggerContent.schema.$ref),
361
- nullable: false,
362
- isNativeType: false,
363
- };
364
- } catch (error) {
365
- const errorMessage = "Make sure your API endpoint uses IActionResult with a typed return value";
366
- logger.error(errorMessage, error);
367
- throw new Error(errorMessage);
368
- }
369
-
370
- if (swaggerContent.schema.type != null) {
371
- let schema = swaggerContent.schema;
372
- if (schema.type == 'array') {
373
- if (schema.items?.$ref != null) {
374
- return <TypeDto>{
375
- typeName: `${this.getObjectName(schema.items.$ref)}`,
376
- nullable: false,
377
- isNativeType: false,
378
- isArray: true,
379
- };
380
- }
381
- } else {
382
- return <TypeDto>{
383
- typeName: this.getNativeType(swaggerContent.schema),
384
- nullable: false,
385
- isNativeType: true,
386
- };
387
- }
388
- // if (swaggerComponentProperty.items.$ref != null)
389
- // return `${this.getObjectName(swaggerComponentProperty.items.$ref)}[]`;
390
-
391
-
392
- } else {
393
- return <TypeDto>{
394
- typeName: this.getNativeType(swaggerContent.schema),
395
- nullable: false,
396
- isNativeType: true,
397
- };
398
- }
399
- }
400
-
401
- retrieveType(swaggerComponentProperty: SwaggerSchema): TypeDto {
402
- if (swaggerComponentProperty.$ref != null) {
403
- return {
404
- typeName: swaggerComponentProperty.$ref.replace('#/components/schemas/', ''),
405
- isTypeReference: true,
406
- isNativeType: false,
407
- nullable: swaggerComponentProperty.nullable ?? false,
408
- isArray: false,
409
- isVoid: false,
410
- };
411
- }
412
-
413
- if (swaggerComponentProperty.allOf != null) {
414
- const type = this.retrieveType(swaggerComponentProperty.allOf[0]); //TODO per ora prendo solo il primo, ma potrebbe essere un array di tipi
415
- return {
416
- ...type,
417
- nullable: swaggerComponentProperty.nullable ?? false,
418
- };
419
- }
420
-
421
- if (swaggerComponentProperty.type != null && swaggerComponentProperty.type == 'array') {
422
- if (swaggerComponentProperty.items?.$ref != null) {
423
- return {
424
- typeName: `${this.getObjectName(swaggerComponentProperty.items.$ref)}`,
425
- isTypeReference: true,
426
- isNativeType: false,
427
- nullable: swaggerComponentProperty.nullable ?? false,
428
- isArray: true,
429
- isVoid: false,
430
- };
431
- }
432
- else {
433
- // Array of native types (including binary/file)
434
- const itemType = swaggerComponentProperty.items
435
- ? this.getNativeType(swaggerComponentProperty.items)
436
- : this.getNativeType(swaggerComponentProperty);
437
- return {
438
- typeName: itemType,
439
- isTypeReference: false,
440
- isNativeType: true,
441
- nullable: swaggerComponentProperty.nullable ?? false,
442
- isArray: true,
443
- isVoid: false,
444
- };
445
- }
446
- }
447
-
448
- if (swaggerComponentProperty.type != null) {
449
- return {
450
- typeName: this.getNativeType(swaggerComponentProperty),
451
- isTypeReference: false,
452
- isNativeType: true,
453
- nullable: swaggerComponentProperty.nullable ?? false,
454
- isArray: false,
455
- isVoid: false,
456
- };
457
- }
458
- if (swaggerComponentProperty.type == null) {
459
- return {
460
- typeName: 'void',
461
- isTypeReference: false,
462
- isNativeType: false,
463
- nullable: swaggerComponentProperty.nullable ?? false,
464
- isArray: false,
465
- isVoid: true,
466
- };
467
- }
468
- console.error("unmanaged swaggerMethodInfo", swaggerComponentProperty);
469
- throw new Error("unmanaged swaggerMethodInfo");
470
- }
471
-
472
- parametrizeObject(objectName: string) {
473
- let component = this._swagger.components.schemas[this.getObjectName(objectName)];
474
-
475
- if (component == null || component.properties == null) return ``;
476
-
477
- console.debug(component.properties);
478
-
479
- return ``;
480
- }
481
-
482
- isEnum(objectName: string) {
483
- let component = this._swagger.components.schemas[this.getObjectName(objectName)];
484
- return component.enum != null;
485
- }
486
-
487
- isDate(schema: SwaggerSchema) {
488
- return schema.type == 'string' && schema.format == 'date-time';
489
- }
490
-
491
- getObjectName(ref: string) {
492
- return ref.replace('#/components/schemas/', '');
493
- }
494
-
495
- retrieveComponentProperties(swaggerComponent: SwaggerComponent) {
496
- if (swaggerComponent.properties == null) return [];
497
-
498
- let properties: PropertyDto[] = [];
499
- for (let index = 0; index < Object.getOwnPropertyNames(swaggerComponent.properties).length; index++) {
500
- const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[index];
501
- const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
502
- const type = this.retrieveType(swaggerComponent.properties[propertyName]);
503
- properties.push({
504
- ...type,
505
- name: propertyName,
506
- nullable: required || type.nullable,
507
- });
508
- }
509
-
510
- return properties;
511
- }
512
-
513
- private retrieveComponentPropertiesFlutter(parentModelName: string, swaggerComponent: SwaggerComponent) {
514
- if (swaggerComponent.properties == null) return [];
515
-
516
- let properties: PropertyDto[] = [];
517
- for (let index = 0; index < Object.getOwnPropertyNames(swaggerComponent.properties).length; index++) {
518
- const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[index];
519
- const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
520
- const schema = swaggerComponent.properties[propertyName];
521
-
522
- // Inline enum on property.
523
- const inlineEnum = schema?.enum;
524
- if (inlineEnum != null && (schema.type === 'string' || schema.type === 'integer')) {
525
- const enumTypeName = `${parentModelName}${Utils.toPascalCase(propertyName)}`;
526
- this.ensureEnumModel(enumTypeName, inlineEnum);
527
-
528
- properties.push({
529
- typeName: enumTypeName,
530
- isTypeReference: true,
531
- isNativeType: false,
532
- nullable: required || (schema.nullable ?? false),
533
- isArray: false,
534
- isVoid: false,
535
- name: propertyName,
536
- });
537
- continue;
538
- }
539
-
540
- // Array of enums.
541
- if (schema?.type === 'array' && schema.items?.enum != null && (schema.items.type === 'string' || schema.items.type === 'integer')) {
542
- const enumTypeName = `${parentModelName}${Utils.toPascalCase(propertyName)}Item`;
543
- this.ensureEnumModel(enumTypeName, schema.items.enum);
544
-
545
- properties.push({
546
- typeName: enumTypeName,
547
- isTypeReference: true,
548
- isNativeType: false,
549
- nullable: required || (schema.nullable ?? false),
550
- isArray: true,
551
- isVoid: false,
552
- name: propertyName,
553
- });
554
- continue;
555
- }
556
-
557
- const type = this.retrieveType(schema);
558
- properties.push({
559
- ...type,
560
- name: propertyName,
561
- nullable: required || type.nullable,
562
- });
563
- }
564
-
565
- return properties;
566
- }
567
-
568
- private ensureEnumModel(typeName: string, enumValues: Array<string | number>) {
569
- if (this._models.findIndex(m => m.typeName === typeName) !== -1) return;
570
-
571
- this._models.push({
572
- typeName,
573
- modelType: 'enum',
574
- name: typeName,
575
- properties: [],
576
- enumValues: this.retrieveEnumValuesFlexible(enumValues),
577
- });
578
- }
579
-
580
- private retrieveEnumValuesFlexible(enumValues: Array<string | number> | undefined | null): EnumValueDto[] {
581
- if (enumValues == null) return [];
582
-
583
- const values: EnumValueDto[] = [];
584
- for (let index = 0; index < enumValues.length; index++) {
585
- const raw = enumValues[index];
586
- if (raw == null) continue;
587
-
588
- // Legacy format: "NAME-1" (kept for backward compatibility)
589
- const asString = String(raw);
590
- if (asString.includes('-')) {
591
- const parts = asString.split('-');
592
- const namePart = parts[0]?.trim();
593
- const valuePart = parts.slice(1).join('-').trim();
594
- if (namePart && valuePart) {
595
- values.push({
596
- name: namePart,
597
- value: valuePart,
598
- });
599
- continue;
600
- }
601
- }
602
-
603
- // Standard OpenAPI string/number enum values.
604
- // Many APIs expose ALL_CAPS_UNDERSCORE values; normalize to lowerCamelCase.
605
- const normalizedForName = asString.toLowerCase();
606
- const enumCaseName = this.sanitizeEnumCaseName(
607
- Utils.toFirstLetterLowercase(Utils.toPascalCase(normalizedForName))
608
- );
609
- const literal = typeof raw === 'number' ? String(raw) : JSON.stringify(asString);
610
- values.push({
611
- name: enumCaseName,
612
- value: literal,
613
- });
614
- }
615
-
616
- return values;
617
- }
618
-
619
- private sanitizeEnumCaseName(name: string): string {
620
- if (!name) return 'value';
621
- // Dart identifiers cannot start with a digit.
622
- if (/^\d/.test(name)) return `v${name}`;
623
- return name;
624
- }
625
-
626
- retrieveSchemaProperties(schema: SwaggerSchema) {
627
- if (schema.properties == null) return [];
628
-
629
- let properties: PropertyDto[] = [];
630
-
631
- for (let index = 0; index < Object.getOwnPropertyNames(schema.properties).length; index++) {
632
- const propertyName = Object.getOwnPropertyNames(schema.properties)[index];
633
- const property = schema.properties[propertyName];
634
- const type = this.retrieveType(property);
635
- properties.push({
636
- ...type,
637
- name: propertyName,
638
- nullable: type.nullable,
639
- });
640
- }
641
-
642
- return properties;
643
- }
644
-
645
- retrieveEnumValues(name: string, swaggerCopmponent: SwaggerComponent) {
646
- if (swaggerCopmponent.enum == null) return [];
647
-
648
- let values: EnumValueDto[] = [];
649
- for (let index = 0; index < swaggerCopmponent.enum.length; index++) {
650
- try {
651
- const item = String(swaggerCopmponent.enum[index]);
652
- values.push({
653
- name: item.split('-')[0].trim(),
654
- value: item.split('-')[1].trim(),
655
- });
656
- } catch (error) {
657
- logger.error(`Error parsing enum value at index ${index}:`, swaggerCopmponent.enum[index]);
658
- }
659
-
660
- }
661
-
662
- return values;
663
- }
664
-
665
- retrieveNestedObjects(usedTypes: TypeDto[]) {
666
- for (let i = 0; i < usedTypes.length; i++) {
667
- const swaggerCopmponent = this._swagger.components.schemas[usedTypes[i].typeName];
668
- if (!swaggerCopmponent) continue;
669
- this.retrieveNestedObjectsRecursive(swaggerCopmponent, usedTypes);
670
- }
671
- }
672
-
673
- retrieveNestedObjectsRecursive(swaggerComponent: SwaggerComponent, usedTypes: TypeDto[]) {
674
- if (!swaggerComponent.properties) return;
675
-
676
- for (let j = 0; j < Object.getOwnPropertyNames(swaggerComponent.properties).length; j++) {
677
- const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[j];
678
-
679
- try {
680
- let nestedUsedType = '';
681
-
682
- if (!swaggerComponent.properties[propertyName]) continue;
683
-
684
- if (swaggerComponent.properties[propertyName].$ref) {
685
- nestedUsedType = swaggerComponent.properties[propertyName].$ref!.replace('#/components/schemas/', '');
686
- } else if (swaggerComponent.properties[propertyName].allOf && swaggerComponent.properties[propertyName].allOf![0] && swaggerComponent.properties[propertyName].allOf![0]!.$ref) {
687
- nestedUsedType = swaggerComponent.properties[propertyName].allOf![0]!.$ref!.replace('#/components/schemas/', ''); //TODO Assuming allOf contains a single $ref
688
- } else if (swaggerComponent.properties[propertyName].type == 'array' && swaggerComponent.properties[propertyName].items?.$ref) {
689
- nestedUsedType = swaggerComponent.properties[propertyName].items!.$ref!.replace('#/components/schemas/', '');
690
- }
691
-
692
- if (nestedUsedType != '' && usedTypes.findIndex(x => x.typeName == nestedUsedType) == -1) {
693
- let nested = this._swagger.components.schemas[nestedUsedType];
694
- const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
695
- usedTypes.push({
696
- typeName: nestedUsedType,
697
- isTypeReference: nested.type == 'object',
698
- isNativeType: nested.type != 'object',
699
- nullable: required,
700
- isArray: false,
701
- isVoid: false,
702
- //potrebbe essere un enum
703
- });
704
- this.retrieveNestedObjectsRecursive(nested, usedTypes);
705
- }
706
- } catch (error) {
707
- logger.error(`Error processing nested object for property ${propertyName}`, error);
708
- }
709
- }
710
- }
711
-
712
- getNativeType(schema: SwaggerSchema): string {
713
- let nativeType = 'n.d.';
714
-
715
- if (schema.$ref != null) {
716
- logger.error('Unexpected $ref in getNativeType:', schema.$ref);
717
- throw new Error(`Schema with $ref should not be processed as native type: ${schema.$ref}`);
718
- } else if (schema.type == 'array' && schema.items != null) {
719
- nativeType = this.getNativeType(schema.items);
720
- nativeType += '[]';
721
- } else {
722
- if (schema.type == 'integer') nativeType = 'integer'; //era number
723
- if (schema.type == 'string' && schema.format == null) nativeType = 'string';
724
- if (schema.type == 'string' && schema.format == 'date-time') nativeType = 'dateTime';
725
- if (schema.type == 'string' && schema.format == 'date-time-local-tz') nativeType = 'dateTime';
726
- if (schema.type == 'string' && schema.format == 'uuid') nativeType = 'string';
727
- if (schema.type == 'string' && schema.format == 'binary') nativeType = 'File';
728
- if (schema.type == 'number') nativeType = 'number';
729
- if (schema.type == 'boolean') nativeType = 'boolean';
730
- if (schema.type == 'object') nativeType = 'any';
731
- }
732
-
733
- if (nativeType.indexOf('n.d') == -1) {
734
- return nativeType;
735
- } else {
736
- logger.error("Unmanaged schema type", schema);
737
- throw new Error("Unmanaged schema type");
738
- }
739
- }
740
-
741
- getApiNameNormalized(apiName: string) {
742
- let normalizedApiName = apiName.replace('/api/v{version}/', '').replaceAll('/', '_');
743
-
744
- if (normalizedApiName.charAt(0) == '_') {
745
- normalizedApiName = normalizedApiName.slice(1);
746
- }
747
- return normalizedApiName;
748
- }
749
-
750
- toFirstLetterLowercase(value: string) {
751
- return value.charAt(0).toLowerCase() + value.slice(1);
752
- }
1
+ import fs from 'fs';
2
+ import { SwaggerComponent } from './models/swagger/swagger-component.js';
3
+ import { Swagger } from './models/swagger/swagger.js';
4
+ import { SwaggerMethod } from './models/swagger/swagger-method.js';
5
+ import { SwaggerSchema } from './models/swagger/swagger-schema.js';
6
+ import { ApiDto } from './models/api-dto.js';
7
+ import { TypeDto } from './models/type-dto.js';
8
+ import { ParameterDto } from './models/parameter-dto.js';
9
+ import { ApiNextJsWriter } from './generators-writers/nextjs/api-nextjs-writer.js';
10
+ import { ApiAngularWriter } from './generators-writers/angular/api-angular-writer.js';
11
+ import { ModelDto } from './models/model-dto.js';
12
+ import { SingleBar, Presets } from 'cli-progress';
13
+ import { ModelNextJsWriter } from './generators-writers/nextjs/model-nextjs-writer.js';
14
+ import { ModelAngularWriter } from './generators-writers/angular/model-angular-writer.js';
15
+ import { PropertyDto } from './models/property-dto.js';
16
+ import { EnumValueDto } from './models/enum-value-dto.js';
17
+ import { SwaggerContent } from './models/swagger/swagger-content.js';
18
+ import { CommandLineArgs, DateTimeLibrary, TargetGeneration } from './index.js';
19
+ import { ApiDartWriter } from './generators-writers/dart/api-dart-writer.js';
20
+ import { ModelDartWriter } from './generators-writers/dart/model-dart-writer.js';
21
+ import { Utils } from './generators-writers/utils.js';
22
+ import { logger } from './utils/logger.js';
23
+
24
+ const contentTypeApplicationJson = 'application/json';
25
+ const contentTypeMultipartFormData = 'multipart/form-data';
26
+
27
+ export class Generator {
28
+ private _swagger: Swagger;
29
+ private _commandLineArgs: CommandLineArgs;
30
+
31
+ private _apis: ApiDto[] = [];
32
+ private _models: ModelDto[] = [];
33
+
34
+ private _barApis = new SingleBar({
35
+ format: '{bar} {percentage}% | {message} {value}/{total} Elapsed: {duration_formatted}', // Formato della barra
36
+ barCompleteChar: '\u2588', // Carattere pieno per la barra
37
+ barIncompleteChar: '\u2591', // Carattere vuoto per la parte non completata
38
+ hideCursor: true, // Nascondi il cursore durante l'esecuzione
39
+ barsize: 20,
40
+ }, Presets.shades_classic);
41
+
42
+ private _barModels = new SingleBar({
43
+ format: '{bar} {percentage}% | {message} {value}/{total} Elapsed: {duration_formatted}', // Formato della barra
44
+ barCompleteChar: '\u2588', // Carattere pieno per la barra
45
+ barIncompleteChar: '\u2591', // Carattere vuoto per la parte non completata
46
+ hideCursor: true, // Nascondi il cursore durante l'esecuzione
47
+ barsize: 20,
48
+ }, Presets.shades_classic);
49
+
50
+ constructor(swagger: Swagger, commandLineArgs: CommandLineArgs) {
51
+ this._swagger = swagger;
52
+ this._commandLineArgs = commandLineArgs;
53
+ }
54
+
55
+ generate() {
56
+ logger.info('Starting Swagger API generation...');
57
+
58
+ Utils.clearDirectory(this._commandLineArgs.outputDirectory);
59
+
60
+ this.computeApi();
61
+ this.computeModel();
62
+
63
+ this.generateModel();
64
+ this.generateApi();
65
+
66
+ logger.success('Swagger generation completed successfully!');
67
+ }
68
+
69
+ computeApi() {
70
+ this._barApis.start(Object.getOwnPropertyNames(this._swagger.paths).length, 0);
71
+ for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.paths).length; index++) {
72
+ const apiName = Object.getOwnPropertyNames(this._swagger.paths)[index];
73
+ this._barApis.update(index, { message: `Apis parsing... ${apiName}` });
74
+ const apiSwaggerMethodKey = this._swagger.paths[apiName];
75
+ const apiMethod = Object.getOwnPropertyNames(apiSwaggerMethodKey)[0];
76
+ const apiSwaggerMethod = apiSwaggerMethodKey[apiMethod];
77
+
78
+ let apiDto: ApiDto = {
79
+ name: apiName,
80
+ url: apiName.replace('{version}', '1'),
81
+ method: apiMethod,
82
+ parameters: this.computeParameters(apiName, apiSwaggerMethod),
83
+ returnType: this.computeResponseType(apiSwaggerMethod),
84
+ swaggerMethodKey: apiSwaggerMethodKey,
85
+ swaggerMethod: apiSwaggerMethod,
86
+ haveRequest: apiSwaggerMethod.requestBody != null,
87
+ isMultiPart: apiSwaggerMethod.requestBody != null && apiSwaggerMethod.requestBody.content[contentTypeMultipartFormData] != null,
88
+ tag: apiSwaggerMethod.tags && apiSwaggerMethod.tags.length > 0 ? apiSwaggerMethod.tags[0] : 'default',
89
+ };
90
+ this._apis.push(apiDto);
91
+ }
92
+ }
93
+
94
+ computeModel() {
95
+ this._barModels.start(Object.getOwnPropertyNames(this._swagger.paths).length, 0);
96
+
97
+ let usedTypes: TypeDto[] = [];
98
+ let usedMultiPart: string[] = [];
99
+
100
+ for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.paths).length; index++) {
101
+ const apiName = Object.getOwnPropertyNames(this._swagger.paths)[index];
102
+ const apiSwaggerMethodKey = this._swagger.paths[apiName];
103
+ const apiMethod = Object.getOwnPropertyNames(apiSwaggerMethodKey)[0];
104
+ const apiSwaggerMethod = apiSwaggerMethodKey[apiMethod];
105
+
106
+ const multipartSchema = apiSwaggerMethod.requestBody?.content?.[contentTypeMultipartFormData]?.schema;
107
+ // Only create an ad-hoc request model when the multipart schema is inline.
108
+ // If the schema is a $ref, it's already part of components/schemas and will be generated there.
109
+ if (multipartSchema != null && multipartSchema.$ref == null) {
110
+ usedMultiPart.push(apiName);
111
+ }
112
+ }
113
+
114
+ for (let index = 0; index < this._apis.length; index++) {
115
+ if (this._apis[index].returnType && this._apis[index].returnType!.isTypeReference) {
116
+ usedTypes.push(this._apis[index].returnType!);
117
+ }
118
+
119
+ if (this._apis[index].parameters) {
120
+ this._apis[index].parameters.forEach(parameter => {
121
+ if (parameter.isTypeReference) {
122
+ usedTypes.push(parameter);
123
+ }
124
+ });
125
+ }
126
+ }
127
+
128
+ usedTypes = [...new Set(usedTypes.map(item => item))]; // distinct
129
+
130
+ this.retrieveNestedObjects(usedTypes);
131
+
132
+ let models = ``;
133
+ if (this._swagger.components != null
134
+ && this._swagger.components != undefined
135
+ && this._swagger.components.schemas != null
136
+ && this._swagger.components.schemas != undefined) {
137
+
138
+ for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.components.schemas).length; index++) {
139
+ const modelName = Object.getOwnPropertyNames(this._swagger.components.schemas)[index];
140
+
141
+ const swaggerComponent = this._swagger.components.schemas[modelName];
142
+
143
+ // Flutter target: support enums of type string/integer and inline property enums.
144
+ if (this._commandLineArgs.target === TargetGeneration.Flutter) {
145
+ const isEnumSchema = swaggerComponent.enum != null && (swaggerComponent.type === 'string' || swaggerComponent.type === 'integer');
146
+ this._models.push({
147
+ typeName: modelName,
148
+ modelType: isEnumSchema ? 'enum' : 'class',
149
+ name: modelName,
150
+ properties: (swaggerComponent.type === 'object') ? this.retrieveComponentPropertiesFlutter(modelName, swaggerComponent) : [],
151
+ enumValues: isEnumSchema ? this.retrieveEnumValuesFlexible(swaggerComponent.enum) : [],
152
+ });
153
+ continue;
154
+ }
155
+
156
+ this._models.push({
157
+ typeName: modelName,
158
+ modelType: swaggerComponent.type == 'integer' ? 'enum' : 'class',
159
+ name: modelName,
160
+ properties: (swaggerComponent.type == 'object') ? this.retrieveComponentProperties(swaggerComponent) : [],
161
+ enumValues: (swaggerComponent.type == 'integer') ? this.retrieveEnumValues(modelName, swaggerComponent) : [],
162
+ });
163
+ }
164
+
165
+ }
166
+
167
+ usedMultiPart.forEach(apiName => {
168
+ const swaggerMethod = this._swagger.paths[apiName];
169
+ const method = Object.getOwnPropertyNames(swaggerMethod)[0];
170
+ const swaggerMethodInfo = swaggerMethod[method];
171
+ const schema = swaggerMethodInfo.requestBody.content[contentTypeMultipartFormData].schema;
172
+
173
+ if (schema?.$ref != null) {
174
+ return;
175
+ }
176
+
177
+ this._models.push({
178
+ typeName: this.getApiNameNormalized(apiName),
179
+ modelType: 'class',
180
+ name: this.getApiNameNormalized(apiName),
181
+ properties: this.retrieveSchemaProperties(schema),
182
+ enumValues: [],
183
+ });
184
+ });
185
+ }
186
+
187
+ generateApi() {
188
+ this._barApis.update(this._apis.length, { message: `Api generating...` });
189
+
190
+ if (this._commandLineArgs.target == TargetGeneration.Angular) {
191
+ const apiWriter = new ApiAngularWriter(this._commandLineArgs);
192
+ apiWriter.write(this._apis);
193
+ } else if (this._commandLineArgs.target == TargetGeneration.Next) {
194
+ const apiWriter = new ApiNextJsWriter(this._commandLineArgs);
195
+ apiWriter.write(this._apis);
196
+ } else if (this._commandLineArgs.target == TargetGeneration.Flutter) {
197
+ const apiWriter = new ApiDartWriter(this._commandLineArgs);
198
+ apiWriter.write(this._apis, this._models);
199
+ }
200
+
201
+ this._barApis.update(this._apis.length, { message: `Api was generated successfully` });
202
+
203
+ this._barApis.stop();
204
+ }
205
+
206
+
207
+ generateModel() {
208
+ this._barModels.update(this._apis.length, { message: `Model generation...` });
209
+
210
+ if (this._commandLineArgs.target == TargetGeneration.Angular) {
211
+ const apiWriter = new ModelAngularWriter(this._commandLineArgs.outputDirectory);
212
+ apiWriter.write(this._models);
213
+ } else if (this._commandLineArgs.target == TargetGeneration.Next) {
214
+ const apiWriter = new ModelNextJsWriter(this._commandLineArgs.outputDirectory);
215
+ apiWriter.write(this._models);
216
+ } else if (this._commandLineArgs.target == TargetGeneration.Flutter) {
217
+ const apiWriter = new ModelDartWriter(this._commandLineArgs);
218
+ apiWriter.write(this._models);
219
+ }
220
+
221
+ this._barModels.update(this._apis.length, { message: `Model was generated successfully` });
222
+
223
+ this._barModels.stop();
224
+ }
225
+
226
+ computeParameters(apiName: string, swaggerMethod: SwaggerMethod | undefined) {
227
+ if (!apiName) return [];
228
+ if (!swaggerMethod || swaggerMethod == null) return [];
229
+
230
+ let parameters: ParameterDto[] = [];
231
+ try {
232
+ // Path params are always explicit parameters.
233
+ swaggerMethod.parameters?.filter(x => x.in == 'path').forEach(parameter => {
234
+ const name = this.toFirstLetterLowercase(parameter.name);
235
+ if (parameter.schema?.$ref != null) {
236
+ const type = this.retrieveType(parameter.schema);
237
+ parameters.push({
238
+ name,
239
+ typeName: type.typeName,
240
+ nullable: false,
241
+ swaggerParameter: parameter,
242
+ isQuery: false,
243
+ isEnum: this.isEnum(parameter.schema.$ref),
244
+ isTypeReference: false,
245
+ isNativeType: false,
246
+ isArray: false,
247
+ isVoid: false,
248
+ });
249
+ } else {
250
+ parameters.push({
251
+ name,
252
+ typeName: this.getNativeType(parameter.schema),
253
+ nullable: false,
254
+ swaggerParameter: parameter,
255
+ isQuery: false,
256
+ isEnum: false,
257
+ isTypeReference: false,
258
+ isNativeType: true,
259
+ isArray: false,
260
+ isVoid: false,
261
+ });
262
+ }
263
+ });
264
+
265
+ if (swaggerMethod.requestBody != null && swaggerMethod.requestBody.content[contentTypeMultipartFormData] != null) {
266
+ const schema = swaggerMethod.requestBody.content[contentTypeMultipartFormData].schema;
267
+ const type = this.retrieveType(schema);
268
+ parameters.unshift({
269
+ name: 'request',
270
+ typeName: type.typeName,
271
+ nullable: false,
272
+ isQuery: false,
273
+ isEnum: false,
274
+ isTypeReference: type.isTypeReference ?? true,
275
+ isNativeType: type.isNativeType ?? false,
276
+ isArray: type.isArray ?? false,
277
+ isVoid: type.isVoid ?? false,
278
+ });
279
+ } else {
280
+ if (swaggerMethod.requestBody != null) {
281
+ const type = this.retrieveType(swaggerMethod.requestBody.content[contentTypeApplicationJson].schema);
282
+ parameters.unshift({
283
+ name: 'request',
284
+ typeName: type.typeName, //swaggerMethod.requestBody.content[contentTypeApplicationJson].schema.$ref.replace('#/components/schemas/', ''),
285
+ nullable: false,
286
+ isQuery: false,
287
+ isEnum: false,
288
+ isTypeReference: true,
289
+ isNativeType: false,
290
+ isArray: false,
291
+ isVoid: false,
292
+ });
293
+ } else {
294
+ swaggerMethod.parameters?.filter(x => x.in == 'query').forEach(parameter => {
295
+ if (parameter.schema.$ref != null) {
296
+ const type = this.retrieveType(parameter.schema);
297
+ parameters.push({
298
+ name: this.toFirstLetterLowercase(parameter.name),
299
+ typeName: type.typeName, //parameter.schema.$ref.replace('#/components/schemas/', ''),
300
+ nullable: !parameter.required,
301
+ swaggerParameter: parameter,
302
+ isQuery: true,
303
+ isEnum: this.isEnum(parameter.schema.$ref),
304
+ isTypeReference: false,
305
+ isNativeType: false,
306
+ isArray: false,
307
+ isVoid: false,
308
+ });
309
+ } else {
310
+ parameters.push({
311
+ name: this.toFirstLetterLowercase(parameter.name),
312
+ typeName: this.getNativeType(parameter.schema),
313
+ nullable: !parameter.required,
314
+ swaggerParameter: parameter,
315
+ isQuery: true,
316
+ isEnum: false,
317
+ isTypeReference: false,
318
+ isNativeType: true,
319
+ isArray: false,
320
+ isVoid: false,
321
+ });
322
+ }
323
+ });
324
+ }
325
+ }
326
+ } catch (error) {
327
+ console.error(`\t\tError in ${apiName} - Attenzione forse hai dimenticato IActionResult e non hai tipizzato il tipo restituito dal servizio`);
328
+ }
329
+ return parameters;
330
+ }
331
+
332
+ computeResponseType(swaggerMethod: SwaggerMethod) {
333
+ if (swaggerMethod.responses[200] == null) {
334
+ return <TypeDto>{
335
+ typeName: 'void',
336
+ nullable: false,
337
+ isVoid: true,
338
+ }
339
+ }
340
+
341
+ return this.computeSwaggerContentType(swaggerMethod.responses[200].content[contentTypeApplicationJson]);
342
+ }
343
+
344
+ computeRequestBodyType(swaggerMethod: SwaggerMethod) {
345
+ if (swaggerMethod.requestBody == null) {
346
+ return <TypeDto>{
347
+ typeName: 'void',
348
+ nullable: false,
349
+ isVoid: true,
350
+ }
351
+ }
352
+
353
+ return this.computeSwaggerContentType(swaggerMethod.requestBody.content[contentTypeApplicationJson]);
354
+ }
355
+
356
+ computeSwaggerContentType(swaggerContent: SwaggerContent) {
357
+ try {
358
+ if (swaggerContent.schema.$ref != null)
359
+ return <TypeDto>{
360
+ typeName: this.getObjectName(swaggerContent.schema.$ref),
361
+ nullable: false,
362
+ isNativeType: false,
363
+ };
364
+ } catch (error) {
365
+ const errorMessage = "Make sure your API endpoint uses IActionResult with a typed return value";
366
+ logger.error(errorMessage, error);
367
+ throw new Error(errorMessage);
368
+ }
369
+
370
+ if (swaggerContent.schema.type != null) {
371
+ let schema = swaggerContent.schema;
372
+ if (schema.type == 'array') {
373
+ if (schema.items?.$ref != null) {
374
+ return <TypeDto>{
375
+ typeName: `${this.getObjectName(schema.items.$ref)}`,
376
+ nullable: false,
377
+ isNativeType: false,
378
+ isArray: true,
379
+ };
380
+ }
381
+ } else {
382
+ return <TypeDto>{
383
+ typeName: this.getNativeType(swaggerContent.schema),
384
+ nullable: false,
385
+ isNativeType: true,
386
+ };
387
+ }
388
+ // if (swaggerComponentProperty.items.$ref != null)
389
+ // return `${this.getObjectName(swaggerComponentProperty.items.$ref)}[]`;
390
+
391
+
392
+ } else {
393
+ return <TypeDto>{
394
+ typeName: this.getNativeType(swaggerContent.schema),
395
+ nullable: false,
396
+ isNativeType: true,
397
+ };
398
+ }
399
+ }
400
+
401
+ retrieveType(swaggerComponentProperty: SwaggerSchema): TypeDto {
402
+ if (swaggerComponentProperty.$ref != null) {
403
+ return {
404
+ typeName: swaggerComponentProperty.$ref.replace('#/components/schemas/', ''),
405
+ isTypeReference: true,
406
+ isNativeType: false,
407
+ nullable: swaggerComponentProperty.nullable ?? false,
408
+ isArray: false,
409
+ isVoid: false,
410
+ };
411
+ }
412
+
413
+ if (swaggerComponentProperty.allOf != null) {
414
+ const type = this.retrieveType(swaggerComponentProperty.allOf[0]); //TODO per ora prendo solo il primo, ma potrebbe essere un array di tipi
415
+ return {
416
+ ...type,
417
+ nullable: swaggerComponentProperty.nullable ?? false,
418
+ };
419
+ }
420
+
421
+ if (swaggerComponentProperty.type != null && swaggerComponentProperty.type == 'array') {
422
+ if (swaggerComponentProperty.items?.$ref != null) {
423
+ return {
424
+ typeName: `${this.getObjectName(swaggerComponentProperty.items.$ref)}`,
425
+ isTypeReference: true,
426
+ isNativeType: false,
427
+ nullable: swaggerComponentProperty.nullable ?? false,
428
+ isArray: true,
429
+ isVoid: false,
430
+ };
431
+ }
432
+ else {
433
+ // Array of native types (including binary/file)
434
+ const itemType = swaggerComponentProperty.items
435
+ ? this.getNativeType(swaggerComponentProperty.items)
436
+ : this.getNativeType(swaggerComponentProperty);
437
+ return {
438
+ typeName: itemType,
439
+ isTypeReference: false,
440
+ isNativeType: true,
441
+ nullable: swaggerComponentProperty.nullable ?? false,
442
+ isArray: true,
443
+ isVoid: false,
444
+ };
445
+ }
446
+ }
447
+
448
+ if (swaggerComponentProperty.type != null) {
449
+ return {
450
+ typeName: this.getNativeType(swaggerComponentProperty),
451
+ isTypeReference: false,
452
+ isNativeType: true,
453
+ nullable: swaggerComponentProperty.nullable ?? false,
454
+ isArray: false,
455
+ isVoid: false,
456
+ };
457
+ }
458
+ if (swaggerComponentProperty.type == null) {
459
+ return {
460
+ typeName: 'void',
461
+ isTypeReference: false,
462
+ isNativeType: false,
463
+ nullable: swaggerComponentProperty.nullable ?? false,
464
+ isArray: false,
465
+ isVoid: true,
466
+ };
467
+ }
468
+ console.error("unmanaged swaggerMethodInfo", swaggerComponentProperty);
469
+ throw new Error("unmanaged swaggerMethodInfo");
470
+ }
471
+
472
+ parametrizeObject(objectName: string) {
473
+ let component = this._swagger.components.schemas[this.getObjectName(objectName)];
474
+
475
+ if (component == null || component.properties == null) return ``;
476
+
477
+ console.debug(component.properties);
478
+
479
+ return ``;
480
+ }
481
+
482
+ isEnum(objectName: string) {
483
+ let component = this._swagger.components.schemas[this.getObjectName(objectName)];
484
+ return component.enum != null;
485
+ }
486
+
487
+ isDate(schema: SwaggerSchema) {
488
+ return schema.type == 'string' && schema.format == 'date-time';
489
+ }
490
+
491
+ getObjectName(ref: string) {
492
+ return ref.replace('#/components/schemas/', '');
493
+ }
494
+
495
+ retrieveComponentProperties(swaggerComponent: SwaggerComponent) {
496
+ if (swaggerComponent.properties == null) return [];
497
+
498
+ let properties: PropertyDto[] = [];
499
+ for (let index = 0; index < Object.getOwnPropertyNames(swaggerComponent.properties).length; index++) {
500
+ const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[index];
501
+ const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
502
+ const type = this.retrieveType(swaggerComponent.properties[propertyName]);
503
+ properties.push({
504
+ ...type,
505
+ name: propertyName,
506
+ nullable: required || type.nullable,
507
+ });
508
+ }
509
+
510
+ return properties;
511
+ }
512
+
513
+ private retrieveComponentPropertiesFlutter(parentModelName: string, swaggerComponent: SwaggerComponent) {
514
+ if (swaggerComponent.properties == null) return [];
515
+
516
+ let properties: PropertyDto[] = [];
517
+ for (let index = 0; index < Object.getOwnPropertyNames(swaggerComponent.properties).length; index++) {
518
+ const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[index];
519
+ const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
520
+ const schema = swaggerComponent.properties[propertyName];
521
+
522
+ // Inline enum on property.
523
+ const inlineEnum = schema?.enum;
524
+ if (inlineEnum != null && (schema.type === 'string' || schema.type === 'integer')) {
525
+ const enumTypeName = `${parentModelName}${Utils.toPascalCase(propertyName)}`;
526
+ this.ensureEnumModel(enumTypeName, inlineEnum);
527
+
528
+ properties.push({
529
+ typeName: enumTypeName,
530
+ isTypeReference: true,
531
+ isNativeType: false,
532
+ nullable: required || (schema.nullable ?? false),
533
+ isArray: false,
534
+ isVoid: false,
535
+ name: propertyName,
536
+ });
537
+ continue;
538
+ }
539
+
540
+ // Array of enums.
541
+ if (schema?.type === 'array' && schema.items?.enum != null && (schema.items.type === 'string' || schema.items.type === 'integer')) {
542
+ const enumTypeName = `${parentModelName}${Utils.toPascalCase(propertyName)}Item`;
543
+ this.ensureEnumModel(enumTypeName, schema.items.enum);
544
+
545
+ properties.push({
546
+ typeName: enumTypeName,
547
+ isTypeReference: true,
548
+ isNativeType: false,
549
+ nullable: required || (schema.nullable ?? false),
550
+ isArray: true,
551
+ isVoid: false,
552
+ name: propertyName,
553
+ });
554
+ continue;
555
+ }
556
+
557
+ const type = this.retrieveType(schema);
558
+ properties.push({
559
+ ...type,
560
+ name: propertyName,
561
+ nullable: required || type.nullable,
562
+ });
563
+ }
564
+
565
+ return properties;
566
+ }
567
+
568
+ private ensureEnumModel(typeName: string, enumValues: Array<string | number>) {
569
+ if (this._models.findIndex(m => m.typeName === typeName) !== -1) return;
570
+
571
+ this._models.push({
572
+ typeName,
573
+ modelType: 'enum',
574
+ name: typeName,
575
+ properties: [],
576
+ enumValues: this.retrieveEnumValuesFlexible(enumValues),
577
+ });
578
+ }
579
+
580
+ private retrieveEnumValuesFlexible(enumValues: Array<string | number> | undefined | null): EnumValueDto[] {
581
+ if (enumValues == null) return [];
582
+
583
+ const values: EnumValueDto[] = [];
584
+ for (let index = 0; index < enumValues.length; index++) {
585
+ const raw = enumValues[index];
586
+ if (raw == null) continue;
587
+
588
+ // Legacy format: "NAME-1" (kept for backward compatibility)
589
+ const asString = String(raw);
590
+ if (asString.includes('-')) {
591
+ const parts = asString.split('-');
592
+ const namePart = parts[0]?.trim();
593
+ const valuePart = parts.slice(1).join('-').trim();
594
+ if (namePart && valuePart) {
595
+ values.push({
596
+ name: namePart,
597
+ value: valuePart,
598
+ });
599
+ continue;
600
+ }
601
+ }
602
+
603
+ // Standard OpenAPI string/number enum values.
604
+ // Many APIs expose ALL_CAPS_UNDERSCORE values; normalize to lowerCamelCase.
605
+ const normalizedForName = asString.toLowerCase();
606
+ const enumCaseName = this.sanitizeEnumCaseName(
607
+ Utils.toFirstLetterLowercase(Utils.toPascalCase(normalizedForName))
608
+ );
609
+ const literal = typeof raw === 'number' ? String(raw) : JSON.stringify(asString);
610
+ values.push({
611
+ name: enumCaseName,
612
+ value: literal,
613
+ });
614
+ }
615
+
616
+ return values;
617
+ }
618
+
619
+ private sanitizeEnumCaseName(name: string): string {
620
+ if (!name) return 'value';
621
+ // Dart identifiers cannot start with a digit.
622
+ if (/^\d/.test(name)) return `v${name}`;
623
+ return name;
624
+ }
625
+
626
+ retrieveSchemaProperties(schema: SwaggerSchema) {
627
+ if (schema.properties == null) return [];
628
+
629
+ let properties: PropertyDto[] = [];
630
+
631
+ for (let index = 0; index < Object.getOwnPropertyNames(schema.properties).length; index++) {
632
+ const propertyName = Object.getOwnPropertyNames(schema.properties)[index];
633
+ const property = schema.properties[propertyName];
634
+ const type = this.retrieveType(property);
635
+ properties.push({
636
+ ...type,
637
+ name: propertyName,
638
+ nullable: type.nullable,
639
+ });
640
+ }
641
+
642
+ return properties;
643
+ }
644
+
645
+ retrieveEnumValues(name: string, swaggerCopmponent: SwaggerComponent) {
646
+ if (swaggerCopmponent.enum == null) return [];
647
+
648
+ let values: EnumValueDto[] = [];
649
+ for (let index = 0; index < swaggerCopmponent.enum.length; index++) {
650
+ try {
651
+ const item = String(swaggerCopmponent.enum[index]);
652
+ values.push({
653
+ name: item.split('-')[0].trim(),
654
+ value: item.split('-')[1].trim(),
655
+ });
656
+ } catch (error) {
657
+ logger.error(`Error parsing enum value at index ${index}:`, swaggerCopmponent.enum[index]);
658
+ }
659
+
660
+ }
661
+
662
+ return values;
663
+ }
664
+
665
+ retrieveNestedObjects(usedTypes: TypeDto[]) {
666
+ for (let i = 0; i < usedTypes.length; i++) {
667
+ const swaggerCopmponent = this._swagger.components.schemas[usedTypes[i].typeName];
668
+ if (!swaggerCopmponent) continue;
669
+ this.retrieveNestedObjectsRecursive(swaggerCopmponent, usedTypes);
670
+ }
671
+ }
672
+
673
+ retrieveNestedObjectsRecursive(swaggerComponent: SwaggerComponent, usedTypes: TypeDto[]) {
674
+ if (!swaggerComponent.properties) return;
675
+
676
+ for (let j = 0; j < Object.getOwnPropertyNames(swaggerComponent.properties).length; j++) {
677
+ const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[j];
678
+
679
+ try {
680
+ let nestedUsedType = '';
681
+
682
+ if (!swaggerComponent.properties[propertyName]) continue;
683
+
684
+ if (swaggerComponent.properties[propertyName].$ref) {
685
+ nestedUsedType = swaggerComponent.properties[propertyName].$ref!.replace('#/components/schemas/', '');
686
+ } else if (swaggerComponent.properties[propertyName].allOf && swaggerComponent.properties[propertyName].allOf![0] && swaggerComponent.properties[propertyName].allOf![0]!.$ref) {
687
+ nestedUsedType = swaggerComponent.properties[propertyName].allOf![0]!.$ref!.replace('#/components/schemas/', ''); //TODO Assuming allOf contains a single $ref
688
+ } else if (swaggerComponent.properties[propertyName].type == 'array' && swaggerComponent.properties[propertyName].items?.$ref) {
689
+ nestedUsedType = swaggerComponent.properties[propertyName].items!.$ref!.replace('#/components/schemas/', '');
690
+ }
691
+
692
+ if (nestedUsedType != '' && usedTypes.findIndex(x => x.typeName == nestedUsedType) == -1) {
693
+ let nested = this._swagger.components.schemas[nestedUsedType];
694
+ const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
695
+ usedTypes.push({
696
+ typeName: nestedUsedType,
697
+ isTypeReference: nested.type == 'object',
698
+ isNativeType: nested.type != 'object',
699
+ nullable: required,
700
+ isArray: false,
701
+ isVoid: false,
702
+ //potrebbe essere un enum
703
+ });
704
+ this.retrieveNestedObjectsRecursive(nested, usedTypes);
705
+ }
706
+ } catch (error) {
707
+ logger.error(`Error processing nested object for property ${propertyName}`, error);
708
+ }
709
+ }
710
+ }
711
+
712
+ getNativeType(schema: SwaggerSchema): string {
713
+ let nativeType = 'n.d.';
714
+
715
+ if (schema.$ref != null) {
716
+ logger.error('Unexpected $ref in getNativeType:', schema.$ref);
717
+ throw new Error(`Schema with $ref should not be processed as native type: ${schema.$ref}`);
718
+ } else if (schema.type == 'array' && schema.items != null) {
719
+ nativeType = this.getNativeType(schema.items);
720
+ nativeType += '[]';
721
+ } else {
722
+ if (schema.type == 'integer') nativeType = 'integer'; //era number
723
+ if (schema.type == 'string' && schema.format == null) nativeType = 'string';
724
+ if (schema.type == 'string' && schema.format == 'date-time') nativeType = 'dateTime';
725
+ if (schema.type == 'string' && schema.format == 'date-time-local-tz') nativeType = 'dateTime';
726
+ if (schema.type == 'string' && schema.format == 'uuid') nativeType = 'string';
727
+ if (schema.type == 'string' && schema.format == 'binary') nativeType = 'File';
728
+ if (schema.type == 'number') nativeType = 'number';
729
+ if (schema.type == 'boolean') nativeType = 'boolean';
730
+ if (schema.type == 'object') nativeType = 'any';
731
+ }
732
+
733
+ if (nativeType.indexOf('n.d') == -1) {
734
+ return nativeType;
735
+ } else {
736
+ logger.error("Unmanaged schema type", schema);
737
+ throw new Error("Unmanaged schema type");
738
+ }
739
+ }
740
+
741
+ getApiNameNormalized(apiName: string) {
742
+ let normalizedApiName = apiName.replace('/api/v{version}/', '').replaceAll('/', '_');
743
+
744
+ if (normalizedApiName.charAt(0) == '_') {
745
+ normalizedApiName = normalizedApiName.slice(1);
746
+ }
747
+ return normalizedApiName;
748
+ }
749
+
750
+ toFirstLetterLowercase(value: string) {
751
+ return value.charAt(0).toLowerCase() + value.slice(1);
752
+ }
753
753
  }