@devlearning/swagger-generator 1.1.21 → 1.1.22
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/.vscode/launch.json +28 -28
- package/README-OLD.md +209 -209
- package/README.md +277 -277
- package/dist/api.constants.js +22 -22
- package/dist/generators-writers/angular/api-angular-writer.js +38 -38
- package/dist/generators-writers/angular/constants.js +24 -24
- package/dist/generators-writers/angular/model-angular-writer.js +6 -6
- package/dist/generators-writers/dart/model-dart-writer.js +11 -4
- package/dist/generators-writers/dart/templates/api.mustache +143 -143
- package/dist/generators-writers/dart/templates/enum.mustache +14 -14
- package/dist/generators-writers/dart/templates/model.mustache +23 -23
- package/dist/generators-writers/nextjs/api-nextjs-writer.js +12 -12
- package/dist/generators-writers/nextjs/constants.js +4 -4
- package/dist/generators-writers/nextjs/model-nextjs-writer.js +6 -6
- package/dist/generators-writers/utils.d.ts +1 -0
- package/dist/generators-writers/utils.js +16 -4
- package/dist/templates/api.mustache +29 -0
- package/dist/templates/model.mustache +18 -0
- package/package.json +49 -49
- package/src/api.constants.ts +26 -26
- package/src/generator-old.ts +449 -449
- package/src/generator.ts +752 -752
- package/src/generators-writers/angular/api-angular-writer.ts +187 -187
- package/src/generators-writers/angular/constants.ts +36 -36
- package/src/generators-writers/angular/model-angular-writer.ts +65 -65
- package/src/generators-writers/angular/normalizator.ts +41 -41
- package/src/generators-writers/dart/api-dart-writer.ts +303 -303
- package/src/generators-writers/dart/model-dart-writer.ts +226 -219
- package/src/generators-writers/dart/models/import-definition-dart.ts +5 -5
- package/src/generators-writers/dart/normalizator.ts +72 -72
- package/src/generators-writers/dart/templates/api.mustache +143 -143
- package/src/generators-writers/dart/templates/enum.mustache +14 -14
- package/src/generators-writers/dart/templates/model.mustache +23 -23
- package/src/generators-writers/nextjs/api-nextjs-writer.ts +157 -157
- package/src/generators-writers/nextjs/constants.ts +5 -5
- package/src/generators-writers/nextjs/model-nextjs-writer.ts +61 -61
- package/src/generators-writers/utils.ts +111 -93
- package/src/index.ts +103 -103
- package/src/models/api-dto.ts +17 -17
- package/src/models/enum-value-dto.ts +3 -3
- package/src/models/model-dto.ts +9 -9
- package/src/models/parameter-dto.ts +7 -7
- package/src/models/property-dto.ts +4 -4
- package/src/models/swagger/swagger-component-property.ts +11 -11
- package/src/models/swagger/swagger-component.ts +17 -17
- package/src/models/swagger/swagger-content.ts +4 -4
- package/src/models/swagger/swagger-info.ts +3 -3
- package/src/models/swagger/swagger-method.ts +7 -7
- package/src/models/swagger/swagger-schema.ts +21 -21
- package/src/models/swagger/swagger.ts +38 -38
- package/src/models/type-dto.ts +7 -7
- package/src/swagger-downloader.ts +46 -46
- package/src/utils/logger.ts +73 -73
- package/src/utils/swagger-validator.ts +89 -89
- 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
|
}
|