@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.
- 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/generator.d.ts +4 -0
- package/dist/generator.js +118 -4
- 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.d.ts +1 -0
- package/dist/generators-writers/dart/model-dart-writer.js +15 -14
- 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 +20 -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/models/swagger/swagger-component.d.ts +2 -2
- package/dist/models/swagger/swagger-schema.d.ts +1 -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 -625
- 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 +212 -209
- 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 +20 -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 +93 -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 -20
- 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/dist/templates/api.mustache +0 -29
- package/dist/templates/model.mustache +0 -18
|
@@ -1,304 +1,304 @@
|
|
|
1
|
-
import fs, { writeFileSync } from 'fs';
|
|
2
|
-
import { ApiDto } from '../../models/api-dto.js';
|
|
3
|
-
import { Utils } from '../utils.js';
|
|
4
|
-
import { ParameterDto } from '../../models/parameter-dto.js';
|
|
5
|
-
import Mustache from 'mustache';
|
|
6
|
-
import { CommandLineArgs } from '../../index.js';
|
|
7
|
-
import { ModelDto } from '../../models/model-dto.js';
|
|
8
|
-
import { TypeDto } from '../../models/type-dto.js';
|
|
9
|
-
import { ImportDefinitionDart } from './models/import-definition-dart.js';
|
|
10
|
-
import { Normalizator } from './normalizator.js';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { fileURLToPath } from 'url';
|
|
13
|
-
import { logger } from '../../utils/logger.js';
|
|
14
|
-
|
|
15
|
-
interface ApiDefinitionDart {
|
|
16
|
-
package: string;
|
|
17
|
-
outputDirectory: string;
|
|
18
|
-
filename: string;
|
|
19
|
-
apiClassName: string;
|
|
20
|
-
imports: string[];
|
|
21
|
-
endpoints: EndpointDefinitionDart[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface EndpointDefinitionDart {
|
|
25
|
-
methodName: string;
|
|
26
|
-
httpMethod: HttpMethodDart;
|
|
27
|
-
path: string;
|
|
28
|
-
responseType: string;
|
|
29
|
-
isResponseNativeType: boolean;
|
|
30
|
-
haveRequest: boolean;
|
|
31
|
-
requestType?: string; // solo se haveRequest è true
|
|
32
|
-
queryParams?: Parameter[],
|
|
33
|
-
pathParams?: Parameter[],
|
|
34
|
-
isMultiPart: boolean;
|
|
35
|
-
multipartFields?: MultipartFieldDefinitionDart[];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface MultipartFieldDefinitionDart {
|
|
39
|
-
name: string;
|
|
40
|
-
isFile: boolean;
|
|
41
|
-
isArray: boolean;
|
|
42
|
-
nullable: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface Parameter {
|
|
46
|
-
name: string;
|
|
47
|
-
type: string; // "string", "int", ecc
|
|
48
|
-
nullable: boolean;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
type HttpMethodDart = 'get' | 'post' | 'put' | 'delete' | 'patch';
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
export class ApiDartWriter {
|
|
55
|
-
private _commandLineArgs: CommandLineArgs;
|
|
56
|
-
|
|
57
|
-
constructor(commandLineArgs: CommandLineArgs) {
|
|
58
|
-
this._commandLineArgs = commandLineArgs;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
write(apis: ApiDto[], models: ModelDto[]) {
|
|
62
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
63
|
-
const __dirname = path.dirname(__filename);
|
|
64
|
-
const templatePath = path.join(__dirname, 'templates', 'api.mustache');
|
|
65
|
-
const template = fs.readFileSync(templatePath, 'utf-8');
|
|
66
|
-
let importDirectory = this._commandLineArgs.outputDirectory;
|
|
67
|
-
if (importDirectory.startsWith('lib/')) {
|
|
68
|
-
importDirectory = importDirectory.slice('lib/'.length);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Se è specificato apiClientName, genera una singola classe unificata
|
|
72
|
-
if (this._commandLineArgs.apiClientName) {
|
|
73
|
-
this._writeUnifiedApi(apis, models, template, importDirectory);
|
|
74
|
-
} else {
|
|
75
|
-
// Altrimenti suddividi per tag come prima
|
|
76
|
-
this._writeApisByTag(apis, models, template, importDirectory);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private _writeUnifiedApi(apis: ApiDto[], models: ModelDto[], template: string, importDirectory: string) {
|
|
81
|
-
const apiClassName = this._commandLineArgs.apiClientName!;
|
|
82
|
-
const filename = Utils.toDartFileName(apiClassName);
|
|
83
|
-
|
|
84
|
-
console.log(`Api: Unified API Client - ${apiClassName}`);
|
|
85
|
-
|
|
86
|
-
var apiDefinition = <ApiDefinitionDart>{
|
|
87
|
-
package: this._commandLineArgs.package,
|
|
88
|
-
outputDirectory: this._commandLineArgs.outputDirectory,
|
|
89
|
-
filename: filename,
|
|
90
|
-
apiClassName: apiClassName,
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
var endpoints = <EndpointDefinitionDart[]>[];
|
|
94
|
-
var imports = <ImportDefinitionDart[]>[];
|
|
95
|
-
|
|
96
|
-
// Processa tutte le API senza raggruppamento
|
|
97
|
-
for (const api of apis) {
|
|
98
|
-
const endpointData = this._createEndpoint(api, models, imports, importDirectory);
|
|
99
|
-
endpoints.push(endpointData.endpoint);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
apiDefinition.endpoints = endpoints;
|
|
103
|
-
apiDefinition.imports = imports.map(i => i.import);
|
|
104
|
-
|
|
105
|
-
let destinationPath = `${this._commandLineArgs.outputDirectory}/api`;
|
|
106
|
-
|
|
107
|
-
Utils.ensureDirectorySync(`${destinationPath}`);
|
|
108
|
-
|
|
109
|
-
var result = Mustache.render(template, apiDefinition);
|
|
110
|
-
writeFileSync(`${destinationPath}/${apiDefinition.filename}.dart`, result, 'utf-8');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private _writeApisByTag(apis: ApiDto[], models: ModelDto[], template: string, importDirectory: string) {
|
|
114
|
-
const grouped = this._groupByTag(apis);
|
|
115
|
-
|
|
116
|
-
for (const [tag, apis] of Object.entries(grouped)) {
|
|
117
|
-
logger.progress(`Generating Dart API: ${tag}`);
|
|
118
|
-
|
|
119
|
-
let subPath = '';
|
|
120
|
-
let filename = '';
|
|
121
|
-
let apiClassName = '';
|
|
122
|
-
|
|
123
|
-
subPath = Utils.toDartFileName(tag);
|
|
124
|
-
filename = `${Utils.toDartFileName(tag + 'Api')}`;
|
|
125
|
-
apiClassName = `${Utils.toDartClassName(tag + 'Api')}`;
|
|
126
|
-
|
|
127
|
-
var apiDefinition = <ApiDefinitionDart>{
|
|
128
|
-
package: this._commandLineArgs.package,
|
|
129
|
-
outputDirectory: this._commandLineArgs.outputDirectory,
|
|
130
|
-
filename: filename,
|
|
131
|
-
apiClassName: apiClassName,
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
var endpoints = <EndpointDefinitionDart[]>[];
|
|
135
|
-
var imports = <ImportDefinitionDart[]>[];
|
|
136
|
-
|
|
137
|
-
for (const api of apis) {
|
|
138
|
-
const endpointData = this._createEndpoint(api, models, imports, importDirectory, tag);
|
|
139
|
-
endpoints.push(endpointData.endpoint);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
apiDefinition.endpoints = endpoints;
|
|
143
|
-
apiDefinition.imports = imports.map(i => i.import);
|
|
144
|
-
|
|
145
|
-
let destinationPath = `${this._commandLineArgs.outputDirectory}/${subPath}/api`;
|
|
146
|
-
|
|
147
|
-
Utils.ensureDirectorySync(`${destinationPath}`);
|
|
148
|
-
|
|
149
|
-
var result = Mustache.render(template, apiDefinition);
|
|
150
|
-
writeFileSync(`${destinationPath}/${apiDefinition.filename}.dart`, result, 'utf-8');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private _createEndpoint(api: ApiDto, models: ModelDto[], imports: ImportDefinitionDart[], importDirectory: string, tag?: string): { endpoint: EndpointDefinitionDart } {
|
|
155
|
-
var requestType: string | undefined = undefined;
|
|
156
|
-
requestType = api.haveRequest ? api.parameters[0]?.typeName || 'void' : undefined;
|
|
157
|
-
requestType = requestType?.split('.').pop()!
|
|
158
|
-
requestType = Utils.toDartClassName(requestType);
|
|
159
|
-
|
|
160
|
-
var responseType: string = 'void';
|
|
161
|
-
responseType = api.returnType ? api.returnType.typeName : 'void';
|
|
162
|
-
responseType = responseType?.split('.').pop()!;
|
|
163
|
-
if (api.returnType?.isNativeType) {
|
|
164
|
-
responseType = Normalizator.mapTsTypeToDart(responseType);
|
|
165
|
-
} else {
|
|
166
|
-
responseType = Utils.toDartClassName(responseType) ?? responseType;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
var methodName = Utils.getNormalizedApiPathDart(api.name);
|
|
170
|
-
if (tag && methodName.startsWith(Utils.toFirstLetterLowercase(tag))) {
|
|
171
|
-
methodName = methodName.slice(tag.length);
|
|
172
|
-
methodName = Utils.toFirstLetterLowercase(methodName);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// console.debug(`\tAPI - ${apiName} - ${apiMethod}`);
|
|
176
|
-
if (methodName.toLowerCase().indexOf("productsave") >= 0) {
|
|
177
|
-
debugger
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const pathParams: Parameter[] = (api.parameters ?? [])
|
|
181
|
-
.filter(p => p.name !== 'request' && p.isQuery === false)
|
|
182
|
-
.map(p => ({
|
|
183
|
-
name: p.name,
|
|
184
|
-
type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName)! : 'String',
|
|
185
|
-
nullable: false,
|
|
186
|
-
}));
|
|
187
|
-
|
|
188
|
-
// Build a Dart-interpolated path string (e.g. '/x/{id}' -> '/x/$id')
|
|
189
|
-
const pathExpression = api.url.replace(/\{([^}]+)\}/g, (_, rawName) => {
|
|
190
|
-
const varName = Utils.toFirstLetterLowercase(rawName);
|
|
191
|
-
return `\$${varName}`;
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const endpoint = <EndpointDefinitionDart>{
|
|
195
|
-
methodName: methodName,
|
|
196
|
-
httpMethod: api.method.toLowerCase() as HttpMethodDart,
|
|
197
|
-
path: pathExpression,
|
|
198
|
-
responseType: responseType,
|
|
199
|
-
isResponseNativeType: api.returnType ? api.returnType.isNativeType : true,
|
|
200
|
-
haveRequest: api.haveRequest,
|
|
201
|
-
requestType: requestType,
|
|
202
|
-
pathParams,
|
|
203
|
-
isMultiPart: api.isMultiPart === true,
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
if (api.parameters && api.parameters.length > 0 && !api.haveRequest) {
|
|
207
|
-
endpoint.queryParams = api.parameters
|
|
208
|
-
.filter(p => p.isQuery === true)
|
|
209
|
-
.map(p => ({
|
|
210
|
-
name: p.name,
|
|
211
|
-
type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName)! : 'String',
|
|
212
|
-
nullable: p.nullable
|
|
213
|
-
}));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (api.haveRequest && api.parameters[0]?.typeName) {
|
|
217
|
-
if (imports.findIndex(x => x.type.typeName == api.parameters[0]?.typeName) == -1) {
|
|
218
|
-
models.forEach(model => {
|
|
219
|
-
if (model.typeName === api.parameters[0]?.typeName) {
|
|
220
|
-
const normalizedInfo = Normalizator.getNormalizedInfo(model);
|
|
221
|
-
const importPath = this._buildImportPath(importDirectory, normalizedInfo.subPath, normalizedInfo.filename);
|
|
222
|
-
imports.push({
|
|
223
|
-
type: api.parameters[0],
|
|
224
|
-
import: `import 'package:${this._commandLineArgs.package}/${importPath}';`
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Multipart: include request model fields so template can build FormData
|
|
232
|
-
if (endpoint.isMultiPart && api.haveRequest && api.parameters[0]?.typeName) {
|
|
233
|
-
const requestModelTypeName = api.parameters[0].typeName;
|
|
234
|
-
const requestModel = models.find(m => m.typeName === requestModelTypeName);
|
|
235
|
-
if (requestModel) {
|
|
236
|
-
endpoint.multipartFields = requestModel.properties.map(p => ({
|
|
237
|
-
name: p.name,
|
|
238
|
-
isFile: p.typeName === 'File',
|
|
239
|
-
isArray: p.isArray === true,
|
|
240
|
-
nullable: p.nullable === true,
|
|
241
|
-
}));
|
|
242
|
-
} else {
|
|
243
|
-
endpoint.multipartFields = [];
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (api.returnType && !api.returnType.isNativeType) {
|
|
248
|
-
if (imports.findIndex(x => x.type.typeName == api.returnType!.typeName) == -1) {
|
|
249
|
-
models.forEach(model => {
|
|
250
|
-
if (model.typeName === api.returnType!.typeName) {
|
|
251
|
-
const normalizedInfo = Normalizator.getNormalizedInfo(model);
|
|
252
|
-
const importPath = this._buildImportPath(importDirectory, normalizedInfo.subPath, normalizedInfo.filename);
|
|
253
|
-
imports.push({
|
|
254
|
-
type: api.returnType!,
|
|
255
|
-
import: `import 'package:${this._commandLineArgs.package}/${importPath}';`
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return { endpoint };
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
private _buildImportPath(importDirectory: string, subPath: string, filename: string): string {
|
|
266
|
-
// Rimuove slash iniziali e finali da ogni parte
|
|
267
|
-
const parts = [importDirectory, subPath]
|
|
268
|
-
.filter(p => p && p.length > 0)
|
|
269
|
-
.map(p => p.replace(/^\/+|\/+$/g, ''));
|
|
270
|
-
|
|
271
|
-
// Aggiunge il filename alla fine
|
|
272
|
-
parts.push(`${filename}.dart`);
|
|
273
|
-
|
|
274
|
-
// Unisce con un singolo slash
|
|
275
|
-
return parts.join('/');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
private _toPascalCase(input: string): string {
|
|
279
|
-
return input
|
|
280
|
-
.replace(/[_\- ]+/g, ' ') // sostituisce underscore, dash e spazi con uno spazio singolo
|
|
281
|
-
.trim() // rimuove spazi iniziali/finali
|
|
282
|
-
.split(' ') // divide in parole
|
|
283
|
-
.map(word =>
|
|
284
|
-
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
|
285
|
-
)
|
|
286
|
-
.join('');
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
private _normalizeApiClassName(input: string): string {
|
|
290
|
-
return input
|
|
291
|
-
.replace(/\./g, '');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
private _groupByTag(apis: ApiDto[]) {
|
|
295
|
-
const groupedByRole = apis.reduce<Record<string, ApiDto[]>>((acc, api) => {
|
|
296
|
-
if (!acc[api.tag]) {
|
|
297
|
-
acc[api.tag] = [];
|
|
298
|
-
}
|
|
299
|
-
acc[api.tag].push(api);
|
|
300
|
-
return acc;
|
|
301
|
-
}, {});
|
|
302
|
-
return groupedByRole;
|
|
303
|
-
}
|
|
1
|
+
import fs, { writeFileSync } from 'fs';
|
|
2
|
+
import { ApiDto } from '../../models/api-dto.js';
|
|
3
|
+
import { Utils } from '../utils.js';
|
|
4
|
+
import { ParameterDto } from '../../models/parameter-dto.js';
|
|
5
|
+
import Mustache from 'mustache';
|
|
6
|
+
import { CommandLineArgs } from '../../index.js';
|
|
7
|
+
import { ModelDto } from '../../models/model-dto.js';
|
|
8
|
+
import { TypeDto } from '../../models/type-dto.js';
|
|
9
|
+
import { ImportDefinitionDart } from './models/import-definition-dart.js';
|
|
10
|
+
import { Normalizator } from './normalizator.js';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import { logger } from '../../utils/logger.js';
|
|
14
|
+
|
|
15
|
+
interface ApiDefinitionDart {
|
|
16
|
+
package: string;
|
|
17
|
+
outputDirectory: string;
|
|
18
|
+
filename: string;
|
|
19
|
+
apiClassName: string;
|
|
20
|
+
imports: string[];
|
|
21
|
+
endpoints: EndpointDefinitionDart[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface EndpointDefinitionDart {
|
|
25
|
+
methodName: string;
|
|
26
|
+
httpMethod: HttpMethodDart;
|
|
27
|
+
path: string;
|
|
28
|
+
responseType: string;
|
|
29
|
+
isResponseNativeType: boolean;
|
|
30
|
+
haveRequest: boolean;
|
|
31
|
+
requestType?: string; // solo se haveRequest è true
|
|
32
|
+
queryParams?: Parameter[],
|
|
33
|
+
pathParams?: Parameter[],
|
|
34
|
+
isMultiPart: boolean;
|
|
35
|
+
multipartFields?: MultipartFieldDefinitionDart[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface MultipartFieldDefinitionDart {
|
|
39
|
+
name: string;
|
|
40
|
+
isFile: boolean;
|
|
41
|
+
isArray: boolean;
|
|
42
|
+
nullable: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface Parameter {
|
|
46
|
+
name: string;
|
|
47
|
+
type: string; // "string", "int", ecc
|
|
48
|
+
nullable: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type HttpMethodDart = 'get' | 'post' | 'put' | 'delete' | 'patch';
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
export class ApiDartWriter {
|
|
55
|
+
private _commandLineArgs: CommandLineArgs;
|
|
56
|
+
|
|
57
|
+
constructor(commandLineArgs: CommandLineArgs) {
|
|
58
|
+
this._commandLineArgs = commandLineArgs;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
write(apis: ApiDto[], models: ModelDto[]) {
|
|
62
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
63
|
+
const __dirname = path.dirname(__filename);
|
|
64
|
+
const templatePath = path.join(__dirname, 'templates', 'api.mustache');
|
|
65
|
+
const template = fs.readFileSync(templatePath, 'utf-8');
|
|
66
|
+
let importDirectory = this._commandLineArgs.outputDirectory;
|
|
67
|
+
if (importDirectory.startsWith('lib/')) {
|
|
68
|
+
importDirectory = importDirectory.slice('lib/'.length);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Se è specificato apiClientName, genera una singola classe unificata
|
|
72
|
+
if (this._commandLineArgs.apiClientName) {
|
|
73
|
+
this._writeUnifiedApi(apis, models, template, importDirectory);
|
|
74
|
+
} else {
|
|
75
|
+
// Altrimenti suddividi per tag come prima
|
|
76
|
+
this._writeApisByTag(apis, models, template, importDirectory);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private _writeUnifiedApi(apis: ApiDto[], models: ModelDto[], template: string, importDirectory: string) {
|
|
81
|
+
const apiClassName = this._commandLineArgs.apiClientName!;
|
|
82
|
+
const filename = Utils.toDartFileName(apiClassName);
|
|
83
|
+
|
|
84
|
+
console.log(`Api: Unified API Client - ${apiClassName}`);
|
|
85
|
+
|
|
86
|
+
var apiDefinition = <ApiDefinitionDart>{
|
|
87
|
+
package: this._commandLineArgs.package,
|
|
88
|
+
outputDirectory: this._commandLineArgs.outputDirectory,
|
|
89
|
+
filename: filename,
|
|
90
|
+
apiClassName: apiClassName,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var endpoints = <EndpointDefinitionDart[]>[];
|
|
94
|
+
var imports = <ImportDefinitionDart[]>[];
|
|
95
|
+
|
|
96
|
+
// Processa tutte le API senza raggruppamento
|
|
97
|
+
for (const api of apis) {
|
|
98
|
+
const endpointData = this._createEndpoint(api, models, imports, importDirectory);
|
|
99
|
+
endpoints.push(endpointData.endpoint);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
apiDefinition.endpoints = endpoints;
|
|
103
|
+
apiDefinition.imports = imports.map(i => i.import);
|
|
104
|
+
|
|
105
|
+
let destinationPath = `${this._commandLineArgs.outputDirectory}/api`;
|
|
106
|
+
|
|
107
|
+
Utils.ensureDirectorySync(`${destinationPath}`);
|
|
108
|
+
|
|
109
|
+
var result = Mustache.render(template, apiDefinition);
|
|
110
|
+
writeFileSync(`${destinationPath}/${apiDefinition.filename}.dart`, result, 'utf-8');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private _writeApisByTag(apis: ApiDto[], models: ModelDto[], template: string, importDirectory: string) {
|
|
114
|
+
const grouped = this._groupByTag(apis);
|
|
115
|
+
|
|
116
|
+
for (const [tag, apis] of Object.entries(grouped)) {
|
|
117
|
+
logger.progress(`Generating Dart API: ${tag}`);
|
|
118
|
+
|
|
119
|
+
let subPath = '';
|
|
120
|
+
let filename = '';
|
|
121
|
+
let apiClassName = '';
|
|
122
|
+
|
|
123
|
+
subPath = Utils.toDartFileName(tag);
|
|
124
|
+
filename = `${Utils.toDartFileName(tag + 'Api')}`;
|
|
125
|
+
apiClassName = `${Utils.toDartClassName(tag + 'Api')}`;
|
|
126
|
+
|
|
127
|
+
var apiDefinition = <ApiDefinitionDart>{
|
|
128
|
+
package: this._commandLineArgs.package,
|
|
129
|
+
outputDirectory: this._commandLineArgs.outputDirectory,
|
|
130
|
+
filename: filename,
|
|
131
|
+
apiClassName: apiClassName,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
var endpoints = <EndpointDefinitionDart[]>[];
|
|
135
|
+
var imports = <ImportDefinitionDart[]>[];
|
|
136
|
+
|
|
137
|
+
for (const api of apis) {
|
|
138
|
+
const endpointData = this._createEndpoint(api, models, imports, importDirectory, tag);
|
|
139
|
+
endpoints.push(endpointData.endpoint);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
apiDefinition.endpoints = endpoints;
|
|
143
|
+
apiDefinition.imports = imports.map(i => i.import);
|
|
144
|
+
|
|
145
|
+
let destinationPath = `${this._commandLineArgs.outputDirectory}/${subPath}/api`;
|
|
146
|
+
|
|
147
|
+
Utils.ensureDirectorySync(`${destinationPath}`);
|
|
148
|
+
|
|
149
|
+
var result = Mustache.render(template, apiDefinition);
|
|
150
|
+
writeFileSync(`${destinationPath}/${apiDefinition.filename}.dart`, result, 'utf-8');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private _createEndpoint(api: ApiDto, models: ModelDto[], imports: ImportDefinitionDart[], importDirectory: string, tag?: string): { endpoint: EndpointDefinitionDart } {
|
|
155
|
+
var requestType: string | undefined = undefined;
|
|
156
|
+
requestType = api.haveRequest ? api.parameters[0]?.typeName || 'void' : undefined;
|
|
157
|
+
requestType = requestType?.split('.').pop()!
|
|
158
|
+
requestType = Utils.toDartClassName(requestType);
|
|
159
|
+
|
|
160
|
+
var responseType: string = 'void';
|
|
161
|
+
responseType = api.returnType ? api.returnType.typeName : 'void';
|
|
162
|
+
responseType = responseType?.split('.').pop()!;
|
|
163
|
+
if (api.returnType?.isNativeType) {
|
|
164
|
+
responseType = Normalizator.mapTsTypeToDart(responseType);
|
|
165
|
+
} else {
|
|
166
|
+
responseType = Utils.toDartClassName(responseType) ?? responseType;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
var methodName = Utils.getNormalizedApiPathDart(api.name);
|
|
170
|
+
if (tag && methodName.startsWith(Utils.toFirstLetterLowercase(tag))) {
|
|
171
|
+
methodName = methodName.slice(tag.length);
|
|
172
|
+
methodName = Utils.toFirstLetterLowercase(methodName);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// console.debug(`\tAPI - ${apiName} - ${apiMethod}`);
|
|
176
|
+
if (methodName.toLowerCase().indexOf("productsave") >= 0) {
|
|
177
|
+
debugger
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const pathParams: Parameter[] = (api.parameters ?? [])
|
|
181
|
+
.filter(p => p.name !== 'request' && p.isQuery === false)
|
|
182
|
+
.map(p => ({
|
|
183
|
+
name: p.name,
|
|
184
|
+
type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName)! : 'String',
|
|
185
|
+
nullable: false,
|
|
186
|
+
}));
|
|
187
|
+
|
|
188
|
+
// Build a Dart-interpolated path string (e.g. '/x/{id}' -> '/x/$id')
|
|
189
|
+
const pathExpression = api.url.replace(/\{([^}]+)\}/g, (_, rawName) => {
|
|
190
|
+
const varName = Utils.toFirstLetterLowercase(rawName);
|
|
191
|
+
return `\$${varName}`;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const endpoint = <EndpointDefinitionDart>{
|
|
195
|
+
methodName: methodName,
|
|
196
|
+
httpMethod: api.method.toLowerCase() as HttpMethodDart,
|
|
197
|
+
path: pathExpression,
|
|
198
|
+
responseType: responseType,
|
|
199
|
+
isResponseNativeType: api.returnType ? api.returnType.isNativeType : true,
|
|
200
|
+
haveRequest: api.haveRequest,
|
|
201
|
+
requestType: requestType,
|
|
202
|
+
pathParams,
|
|
203
|
+
isMultiPart: api.isMultiPart === true,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
if (api.parameters && api.parameters.length > 0 && !api.haveRequest) {
|
|
207
|
+
endpoint.queryParams = api.parameters
|
|
208
|
+
.filter(p => p.isQuery === true)
|
|
209
|
+
.map(p => ({
|
|
210
|
+
name: p.name,
|
|
211
|
+
type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName)! : 'String',
|
|
212
|
+
nullable: p.nullable
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (api.haveRequest && api.parameters[0]?.typeName) {
|
|
217
|
+
if (imports.findIndex(x => x.type.typeName == api.parameters[0]?.typeName) == -1) {
|
|
218
|
+
models.forEach(model => {
|
|
219
|
+
if (model.typeName === api.parameters[0]?.typeName) {
|
|
220
|
+
const normalizedInfo = Normalizator.getNormalizedInfo(model);
|
|
221
|
+
const importPath = this._buildImportPath(importDirectory, normalizedInfo.subPath, normalizedInfo.filename);
|
|
222
|
+
imports.push({
|
|
223
|
+
type: api.parameters[0],
|
|
224
|
+
import: `import 'package:${this._commandLineArgs.package}/${importPath}';`
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Multipart: include request model fields so template can build FormData
|
|
232
|
+
if (endpoint.isMultiPart && api.haveRequest && api.parameters[0]?.typeName) {
|
|
233
|
+
const requestModelTypeName = api.parameters[0].typeName;
|
|
234
|
+
const requestModel = models.find(m => m.typeName === requestModelTypeName);
|
|
235
|
+
if (requestModel) {
|
|
236
|
+
endpoint.multipartFields = requestModel.properties.map(p => ({
|
|
237
|
+
name: p.name,
|
|
238
|
+
isFile: p.typeName === 'File',
|
|
239
|
+
isArray: p.isArray === true,
|
|
240
|
+
nullable: p.nullable === true,
|
|
241
|
+
}));
|
|
242
|
+
} else {
|
|
243
|
+
endpoint.multipartFields = [];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (api.returnType && !api.returnType.isNativeType) {
|
|
248
|
+
if (imports.findIndex(x => x.type.typeName == api.returnType!.typeName) == -1) {
|
|
249
|
+
models.forEach(model => {
|
|
250
|
+
if (model.typeName === api.returnType!.typeName) {
|
|
251
|
+
const normalizedInfo = Normalizator.getNormalizedInfo(model);
|
|
252
|
+
const importPath = this._buildImportPath(importDirectory, normalizedInfo.subPath, normalizedInfo.filename);
|
|
253
|
+
imports.push({
|
|
254
|
+
type: api.returnType!,
|
|
255
|
+
import: `import 'package:${this._commandLineArgs.package}/${importPath}';`
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return { endpoint };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private _buildImportPath(importDirectory: string, subPath: string, filename: string): string {
|
|
266
|
+
// Rimuove slash iniziali e finali da ogni parte
|
|
267
|
+
const parts = [importDirectory, subPath]
|
|
268
|
+
.filter(p => p && p.length > 0)
|
|
269
|
+
.map(p => p.replace(/^\/+|\/+$/g, ''));
|
|
270
|
+
|
|
271
|
+
// Aggiunge il filename alla fine
|
|
272
|
+
parts.push(`${filename}.dart`);
|
|
273
|
+
|
|
274
|
+
// Unisce con un singolo slash
|
|
275
|
+
return parts.join('/');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private _toPascalCase(input: string): string {
|
|
279
|
+
return input
|
|
280
|
+
.replace(/[_\- ]+/g, ' ') // sostituisce underscore, dash e spazi con uno spazio singolo
|
|
281
|
+
.trim() // rimuove spazi iniziali/finali
|
|
282
|
+
.split(' ') // divide in parole
|
|
283
|
+
.map(word =>
|
|
284
|
+
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
|
285
|
+
)
|
|
286
|
+
.join('');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private _normalizeApiClassName(input: string): string {
|
|
290
|
+
return input
|
|
291
|
+
.replace(/\./g, '');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private _groupByTag(apis: ApiDto[]) {
|
|
295
|
+
const groupedByRole = apis.reduce<Record<string, ApiDto[]>>((acc, api) => {
|
|
296
|
+
if (!acc[api.tag]) {
|
|
297
|
+
acc[api.tag] = [];
|
|
298
|
+
}
|
|
299
|
+
acc[api.tag].push(api);
|
|
300
|
+
return acc;
|
|
301
|
+
}, {});
|
|
302
|
+
return groupedByRole;
|
|
303
|
+
}
|
|
304
304
|
}
|