@devlearning/swagger-generator 1.1.16 → 1.1.17

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 (58) 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.js +22 -22
  5. package/dist/generator.d.ts +4 -0
  6. package/dist/generator.js +118 -4
  7. package/dist/generators-writers/angular/api-angular-writer.js +38 -38
  8. package/dist/generators-writers/angular/constants.js +24 -24
  9. package/dist/generators-writers/angular/model-angular-writer.js +6 -6
  10. package/dist/generators-writers/dart/model-dart-writer.d.ts +1 -0
  11. package/dist/generators-writers/dart/model-dart-writer.js +15 -14
  12. package/dist/generators-writers/dart/templates/api.mustache +143 -143
  13. package/dist/generators-writers/dart/templates/enum.mustache +14 -14
  14. package/dist/generators-writers/dart/templates/model.mustache +20 -23
  15. package/dist/generators-writers/nextjs/api-nextjs-writer.js +12 -12
  16. package/dist/generators-writers/nextjs/constants.js +4 -4
  17. package/dist/generators-writers/nextjs/model-nextjs-writer.js +6 -6
  18. package/dist/models/swagger/swagger-component.d.ts +2 -2
  19. package/dist/models/swagger/swagger-schema.d.ts +1 -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 -625
  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 +212 -209
  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 +20 -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 +93 -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 -20
  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
  57. package/dist/templates/api.mustache +0 -29
  58. package/dist/templates/model.mustache +0 -18
package/dist/generator.js CHANGED
@@ -103,6 +103,18 @@ export class Generator {
103
103
  for (let index = 0; index < Object.getOwnPropertyNames(this._swagger.components.schemas).length; index++) {
104
104
  const modelName = Object.getOwnPropertyNames(this._swagger.components.schemas)[index];
105
105
  const swaggerComponent = this._swagger.components.schemas[modelName];
106
+ // Flutter target: support enums of type string/integer and inline property enums.
107
+ if (this._commandLineArgs.target === TargetGeneration.Flutter) {
108
+ const isEnumSchema = swaggerComponent.enum != null && (swaggerComponent.type === 'string' || swaggerComponent.type === 'integer');
109
+ this._models.push({
110
+ typeName: modelName,
111
+ modelType: isEnumSchema ? 'enum' : 'class',
112
+ name: modelName,
113
+ properties: (swaggerComponent.type === 'object') ? this.retrieveComponentPropertiesFlutter(modelName, swaggerComponent) : [],
114
+ enumValues: isEnumSchema ? this.retrieveEnumValuesFlexible(swaggerComponent.enum) : [],
115
+ });
116
+ continue;
117
+ }
106
118
  this._models.push({
107
119
  typeName: modelName,
108
120
  modelType: swaggerComponent.type == 'integer' ? 'enum' : 'class',
@@ -427,7 +439,7 @@ export class Generator {
427
439
  let properties = [];
428
440
  for (let index = 0; index < Object.getOwnPropertyNames(swaggerComponent.properties).length; index++) {
429
441
  const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[index];
430
- const required = swaggerComponent.required && swaggerComponent.required.includes(propertyName);
442
+ const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
431
443
  const type = this.retrieveType(swaggerComponent.properties[propertyName]);
432
444
  properties.push({
433
445
  ...type,
@@ -437,6 +449,107 @@ export class Generator {
437
449
  }
438
450
  return properties;
439
451
  }
452
+ retrieveComponentPropertiesFlutter(parentModelName, swaggerComponent) {
453
+ if (swaggerComponent.properties == null)
454
+ return [];
455
+ let properties = [];
456
+ for (let index = 0; index < Object.getOwnPropertyNames(swaggerComponent.properties).length; index++) {
457
+ const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[index];
458
+ const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
459
+ const schema = swaggerComponent.properties[propertyName];
460
+ // Inline enum on property.
461
+ const inlineEnum = schema?.enum;
462
+ if (inlineEnum != null && (schema.type === 'string' || schema.type === 'integer')) {
463
+ const enumTypeName = `${parentModelName}${Utils.toPascalCase(propertyName)}`;
464
+ this.ensureEnumModel(enumTypeName, inlineEnum);
465
+ properties.push({
466
+ typeName: enumTypeName,
467
+ isTypeReference: true,
468
+ isNativeType: false,
469
+ nullable: required || (schema.nullable ?? false),
470
+ isArray: false,
471
+ isVoid: false,
472
+ name: propertyName,
473
+ });
474
+ continue;
475
+ }
476
+ // Array of enums.
477
+ if (schema?.type === 'array' && schema.items?.enum != null && (schema.items.type === 'string' || schema.items.type === 'integer')) {
478
+ const enumTypeName = `${parentModelName}${Utils.toPascalCase(propertyName)}Item`;
479
+ this.ensureEnumModel(enumTypeName, schema.items.enum);
480
+ properties.push({
481
+ typeName: enumTypeName,
482
+ isTypeReference: true,
483
+ isNativeType: false,
484
+ nullable: required || (schema.nullable ?? false),
485
+ isArray: true,
486
+ isVoid: false,
487
+ name: propertyName,
488
+ });
489
+ continue;
490
+ }
491
+ const type = this.retrieveType(schema);
492
+ properties.push({
493
+ ...type,
494
+ name: propertyName,
495
+ nullable: required || type.nullable,
496
+ });
497
+ }
498
+ return properties;
499
+ }
500
+ ensureEnumModel(typeName, enumValues) {
501
+ if (this._models.findIndex(m => m.typeName === typeName) !== -1)
502
+ return;
503
+ this._models.push({
504
+ typeName,
505
+ modelType: 'enum',
506
+ name: typeName,
507
+ properties: [],
508
+ enumValues: this.retrieveEnumValuesFlexible(enumValues),
509
+ });
510
+ }
511
+ retrieveEnumValuesFlexible(enumValues) {
512
+ if (enumValues == null)
513
+ return [];
514
+ const values = [];
515
+ for (let index = 0; index < enumValues.length; index++) {
516
+ const raw = enumValues[index];
517
+ if (raw == null)
518
+ continue;
519
+ // Legacy format: "NAME-1" (kept for backward compatibility)
520
+ const asString = String(raw);
521
+ if (asString.includes('-')) {
522
+ const parts = asString.split('-');
523
+ const namePart = parts[0]?.trim();
524
+ const valuePart = parts.slice(1).join('-').trim();
525
+ if (namePart && valuePart) {
526
+ values.push({
527
+ name: namePart,
528
+ value: valuePart,
529
+ });
530
+ continue;
531
+ }
532
+ }
533
+ // Standard OpenAPI string/number enum values.
534
+ // Many APIs expose ALL_CAPS_UNDERSCORE values; normalize to lowerCamelCase.
535
+ const normalizedForName = asString.toLowerCase();
536
+ const enumCaseName = this.sanitizeEnumCaseName(Utils.toFirstLetterLowercase(Utils.toPascalCase(normalizedForName)));
537
+ const literal = typeof raw === 'number' ? String(raw) : JSON.stringify(asString);
538
+ values.push({
539
+ name: enumCaseName,
540
+ value: literal,
541
+ });
542
+ }
543
+ return values;
544
+ }
545
+ sanitizeEnumCaseName(name) {
546
+ if (!name)
547
+ return 'value';
548
+ // Dart identifiers cannot start with a digit.
549
+ if (/^\d/.test(name))
550
+ return `v${name}`;
551
+ return name;
552
+ }
440
553
  retrieveSchemaProperties(schema) {
441
554
  if (schema.properties == null)
442
555
  return [];
@@ -459,9 +572,10 @@ export class Generator {
459
572
  let values = [];
460
573
  for (let index = 0; index < swaggerCopmponent.enum.length; index++) {
461
574
  try {
575
+ const item = String(swaggerCopmponent.enum[index]);
462
576
  values.push({
463
- name: swaggerCopmponent.enum[index].split('-')[0].trim(),
464
- value: swaggerCopmponent.enum[index].split('-')[1].trim(),
577
+ name: item.split('-')[0].trim(),
578
+ value: item.split('-')[1].trim(),
465
579
  });
466
580
  }
467
581
  catch (error) {
@@ -498,7 +612,7 @@ export class Generator {
498
612
  }
499
613
  if (nestedUsedType != '' && usedTypes.findIndex(x => x.typeName == nestedUsedType) == -1) {
500
614
  let nested = this._swagger.components.schemas[nestedUsedType];
501
- const required = swaggerComponent.required && swaggerComponent.required.includes(propertyName);
615
+ const required = !!(swaggerComponent.required && swaggerComponent.required.includes(propertyName));
502
616
  usedTypes.push({
503
617
  typeName: nestedUsedType,
504
618
  isTypeReference: nested.type == 'object',
@@ -27,14 +27,14 @@ export class ApiAngularWriter {
27
27
  if (apiNameNormalized.includes('TicketFile')) {
28
28
  debugger;
29
29
  }
30
- let apiString = `
31
- public ${apiNameNormalized}(${parametersString}): Observable<${returnTypeString}> {
32
- ${queryParametersPreparation}${requestPreparation}return this._http.${method}<${returnTypeString}>(\`\${this._baseUrl}${api.url}${queryParameters}\`${haveRequest ? ', wrappedRequest' : ''}, ${httpOptions})
33
- .pipe(
34
- map(x => this._handleResponse(x)),
35
- catchError((err, obs) => this._handleError(err, obs, skipErrorHandling))
36
- );
37
- }
30
+ let apiString = `
31
+ public ${apiNameNormalized}(${parametersString}): Observable<${returnTypeString}> {
32
+ ${queryParametersPreparation}${requestPreparation}return this._http.${method}<${returnTypeString}>(\`\${this._baseUrl}${api.url}${queryParameters}\`${haveRequest ? ', wrappedRequest' : ''}, ${httpOptions})
33
+ .pipe(
34
+ map(x => this._handleResponse(x)),
35
+ catchError((err, obs) => this._handleError(err, obs, skipErrorHandling))
36
+ );
37
+ }
38
38
  `;
39
39
  return apiString;
40
40
  }
@@ -68,21 +68,21 @@ export class ApiAngularWriter {
68
68
  _queryParametersPreparationStatement(parameter) {
69
69
  if (parameter.nullable) {
70
70
  if (Utils.isDate(parameter.swaggerParameter?.schema)) {
71
- return `let ${parameter.name}Param: string = ${parameter.name} != null && ${parameter.name} != undefined && ${parameter.name}.isValid() ? encodeURIComponent(this._momentToString(${parameter.name})) : '';
71
+ return `let ${parameter.name}Param: string = ${parameter.name} != null && ${parameter.name} != undefined && ${parameter.name}.isValid() ? encodeURIComponent(this._momentToString(${parameter.name})) : '';
72
72
  `;
73
73
  }
74
74
  else {
75
- return `let ${parameter.name}Param: string = ${parameter.name} != null && ${parameter.name} != undefined ? encodeURIComponent('' + ${parameter.name}) : '';
75
+ return `let ${parameter.name}Param: string = ${parameter.name} != null && ${parameter.name} != undefined ? encodeURIComponent('' + ${parameter.name}) : '';
76
76
  `;
77
77
  }
78
78
  }
79
79
  else {
80
80
  if (Utils.isDate(parameter.swaggerParameter?.schema)) {
81
- return `let ${parameter.name}Param: string = encodeURIComponent(this._momentToString(${parameter.name}));
81
+ return `let ${parameter.name}Param: string = encodeURIComponent(this._momentToString(${parameter.name}));
82
82
  `;
83
83
  }
84
84
  else {
85
- return `let ${parameter.name}Param: string = encodeURIComponent('' + ${parameter.name});
85
+ return `let ${parameter.name}Param: string = encodeURIComponent('' + ${parameter.name});
86
86
  `;
87
87
  }
88
88
  }
@@ -111,44 +111,44 @@ export class ApiAngularWriter {
111
111
  return '';
112
112
  }
113
113
  if (api.isMultiPart) {
114
- return `let wrappedRequest = this._handleMultipart(request);
114
+ return `let wrappedRequest = this._handleMultipart(request);
115
115
  `;
116
116
  }
117
117
  else {
118
- return `let wrappedRequest = this._handleRequest(request);
118
+ return `let wrappedRequest = this._handleRequest(request);
119
119
  `;
120
120
  }
121
121
  }
122
122
  _writeFile(apis) {
123
123
  const className = this._commandLineArgs.apiClientName || 'ApiAutogeneratedService';
124
124
  const apiPre = this._getApiPre(className);
125
- fs.writeFileSync(this._commandLineArgs.outputDirectory + "/api.autogenerated.ts", `${apiPre}
126
- ${apis}
125
+ fs.writeFileSync(this._commandLineArgs.outputDirectory + "/api.autogenerated.ts", `${apiPre}
126
+ ${apis}
127
127
  ${API_POST}`, { flag: 'w' });
128
128
  }
129
129
  _getApiPre(className) {
130
- return `import { HttpClient } from '@angular/common/http';
131
- import { Observable, catchError, map } from 'rxjs';
132
- import * as Models from './model.autogenerated';
133
- import { HttpHeaders } from "@angular/common/http";
134
-
135
- export const httpOptions = {
136
- headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
137
- };
138
-
139
- export const httpOptionsMultipart = {};
140
-
141
- export abstract class ${className} {
142
- constructor(
143
- public _http: HttpClient,
144
- public _baseUrl: string,
145
- ) { }
146
-
147
- protected abstract _momentToString(moment: moment.Moment): string;
148
- protected abstract _handleRequest<T>(request: T): T;
149
- protected abstract _handleMultipart<T>(request: T): FormData;
150
- protected abstract _handleResponse<T>(response: T): T;
151
- protected abstract _handleError(error: any, obs: any): Observable<never>;
130
+ return `import { HttpClient } from '@angular/common/http';
131
+ import { Observable, catchError, map } from 'rxjs';
132
+ import * as Models from './model.autogenerated';
133
+ import { HttpHeaders } from "@angular/common/http";
134
+
135
+ export const httpOptions = {
136
+ headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
137
+ };
138
+
139
+ export const httpOptionsMultipart = {};
140
+
141
+ export abstract class ${className} {
142
+ constructor(
143
+ public _http: HttpClient,
144
+ public _baseUrl: string,
145
+ ) { }
146
+
147
+ protected abstract _momentToString(moment: moment.Moment): string;
148
+ protected abstract _handleRequest<T>(request: T): T;
149
+ protected abstract _handleMultipart<T>(request: T): FormData;
150
+ protected abstract _handleResponse<T>(response: T): T;
151
+ protected abstract _handleError(error: any, obs: any): Observable<never>;
152
152
  `;
153
153
  }
154
154
  }
@@ -1,28 +1,28 @@
1
- export const API_PRE = `import { HttpClient } from '@angular/common/http';
2
- import { Observable, catchError, map } from 'rxjs';
3
- import * as Models from './model.autogenerated';
4
- import { HttpHeaders } from "@angular/common/http";
5
-
6
- export const httpOptions = {
7
- headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
8
- };
9
-
10
- export const httpOptionsMultipart = {};
11
-
12
- export abstract class ApiAutogeneratedService {
13
- constructor(
14
- public _http: HttpClient,
15
- public _baseUrl: string,
16
- ) { }
17
-
18
- protected abstract _momentToString(moment: moment.Moment): string;
19
- protected abstract _handleRequest<T>(request: T): T;
20
- protected abstract _handleMultipart<T>(request: T): FormData;
21
- protected abstract _handleResponse<T>(response: T): T;
22
- protected abstract _handleError<T>(error: any, obs: Observable<T>, skipErrorHandling: boolean): Observable<never>;
1
+ export const API_PRE = `import { HttpClient } from '@angular/common/http';
2
+ import { Observable, catchError, map } from 'rxjs';
3
+ import * as Models from './model.autogenerated';
4
+ import { HttpHeaders } from "@angular/common/http";
5
+
6
+ export const httpOptions = {
7
+ headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
8
+ };
9
+
10
+ export const httpOptionsMultipart = {};
11
+
12
+ export abstract class ApiAutogeneratedService {
13
+ constructor(
14
+ public _http: HttpClient,
15
+ public _baseUrl: string,
16
+ ) { }
17
+
18
+ protected abstract _momentToString(moment: moment.Moment): string;
19
+ protected abstract _handleRequest<T>(request: T): T;
20
+ protected abstract _handleMultipart<T>(request: T): FormData;
21
+ protected abstract _handleResponse<T>(response: T): T;
22
+ protected abstract _handleError<T>(error: any, obs: Observable<T>, skipErrorHandling: boolean): Observable<never>;
23
23
  `;
24
24
  export const API_POST = `}`;
25
- export const MODEL_PRE = `import * as moment from 'moment';
25
+ export const MODEL_PRE = `import * as moment from 'moment';
26
26
  `;
27
- export const MODEL_POST = `
27
+ export const MODEL_POST = `
28
28
  `;
@@ -15,10 +15,10 @@ export class ModelAngularWriter {
15
15
  this._writeFile(modelString);
16
16
  }
17
17
  _modelString(model) {
18
- let modelString = `
19
- export ${model.modelType} ${Utils.toPascalCase(model.name)} {
20
- ${this._properties(model)}${this._enumValues(model)}
21
- }
18
+ let modelString = `
19
+ export ${model.modelType} ${Utils.toPascalCase(model.name)} {
20
+ ${this._properties(model)}${this._enumValues(model)}
21
+ }
22
22
  `;
23
23
  return modelString;
24
24
  }
@@ -41,8 +41,8 @@ ${this._properties(model)}${this._enumValues(model)}
41
41
  return enumValuesString.trimEnd();
42
42
  }
43
43
  _writeFile(models) {
44
- fs.writeFileSync(this._outputDirectory + "/model.autogenerated.ts", `${MODEL_PRE}
45
- ${models}
44
+ fs.writeFileSync(this._outputDirectory + "/model.autogenerated.ts", `${MODEL_PRE}
45
+ ${models}
46
46
  ${MODEL_POST}`, { flag: 'w' });
47
47
  }
48
48
  }
@@ -6,5 +6,6 @@ export declare class ModelDartWriter {
6
6
  write(models: ModelDto[]): void;
7
7
  writeClasses(models: ModelDto[]): void;
8
8
  writeEnums(models: ModelDto[]): void;
9
+ private _inferDartEnumValueType;
9
10
  private _buildImportPath;
10
11
  }
@@ -47,28 +47,16 @@ export class ModelDartWriter {
47
47
  var fieldTypeName = Normalizator.mapTsTypeToDart(property.typeName);
48
48
  fieldTypeName = Normalizator.getNormalizedTypeName(fieldTypeName);
49
49
  const isFileField = property.typeName === 'File';
50
- const isFileArrayField = isFileField && property.isArray === true;
51
50
  const jsonKeyAnnotation = isFileField
52
51
  ? "@JsonKey(includeFromJson: false, includeToJson: false)"
53
52
  : undefined;
54
- const defaultAnnotation = isFileArrayField
55
- ? "@Default(<File>[])"
56
- : undefined;
57
- // File fields are excluded from JSON serialization; they must not be `required` for fromJson.
58
- const nullable = isFileField && !property.isArray
59
- ? '?'
60
- : (property.nullable && !property.isArray ? '?' : '');
61
- const required = isFileField
62
- ? ''
63
- : (property.nullable && !property.isArray ? '' : 'required ');
64
53
  dartModel.fields.push({
65
54
  name: property.name,
66
55
  type: property.isArray ? `List<${fieldTypeName}>` : fieldTypeName,
67
56
  typeName: property.typeName,
68
- nullable,
69
- required,
57
+ nullable: property.nullable && !property.isArray ? '?' : '',
58
+ required: property.nullable && !property.isArray ? '' : 'required ',
70
59
  jsonKeyAnnotation,
71
- defaultAnnotation,
72
60
  });
73
61
  if (property.isTypeReference) {
74
62
  if (imports.findIndex(x => x.type.typeName == property.typeName) == -1) {
@@ -104,10 +92,12 @@ export class ModelDartWriter {
104
92
  }
105
93
  models.forEach(model => {
106
94
  const normalizedInfo = Normalizator.getNormalizedInfo(model);
95
+ const valueType = this._inferDartEnumValueType(model.enumValues);
107
96
  const dartModel = {
108
97
  enumName: normalizedInfo.modelName,
109
98
  path: this._commandLineArgs.outputDirectory,
110
99
  fields: [],
100
+ valueType,
111
101
  };
112
102
  model.enumValues.forEach(enumItem => {
113
103
  dartModel.fields.push({
@@ -124,6 +114,17 @@ export class ModelDartWriter {
124
114
  writeFileSync(`${destinationPath}/${normalizedInfo.filename}.dart`, result, 'utf-8');
125
115
  });
126
116
  }
117
+ _inferDartEnumValueType(enumValues) {
118
+ // If values are unquoted and numeric, assume int; otherwise String.
119
+ // Generator will quote string enums using JSON.stringify ("...").
120
+ if (!enumValues || enumValues.length === 0)
121
+ return 'String';
122
+ const allNumeric = enumValues.every(v => {
123
+ const trimmed = (v.value ?? '').trim();
124
+ return /^-?\d+$/.test(trimmed);
125
+ });
126
+ return allNumeric ? 'int' : 'String';
127
+ }
127
128
  _buildImportPath(importDirectory, subPath, filename) {
128
129
  // Rimuove slash iniziali e finali da ogni parte
129
130
  const parts = [importDirectory, subPath]