@devlearning/swagger-generator 1.1.9 → 1.1.11
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/README-OLD.md +210 -0
- package/README.md +198 -185
- package/dist/generator.js +12 -28
- package/dist/generators-writers/dart/api-dart-writer.js +2 -5
- package/dist/generators-writers/dart/templates/api.mustache +35 -6
- package/dist/swagger-downloader.js +30 -3
- package/dist/utils/logger.d.ts +25 -0
- package/dist/utils/logger.js +63 -0
- package/dist/utils/swagger-validator.d.ts +19 -0
- package/dist/utils/swagger-validator.js +76 -0
- package/package.json +4 -2
- package/src/generator.ts +13 -32
- package/src/generators-writers/dart/api-dart-writer.ts +2 -6
- package/src/generators-writers/dart/templates/api.mustache +35 -6
- package/src/swagger-downloader.ts +38 -4
- package/src/utils/logger.ts +73 -0
- package/src/utils/swagger-validator.ts +89 -0
package/dist/generator.js
CHANGED
|
@@ -7,6 +7,7 @@ import { TargetGeneration } from './index.js';
|
|
|
7
7
|
import { ApiDartWriter } from './generators-writers/dart/api-dart-writer.js';
|
|
8
8
|
import { ModelDartWriter } from './generators-writers/dart/model-dart-writer.js';
|
|
9
9
|
import { Utils } from './generators-writers/utils.js';
|
|
10
|
+
import { logger } from './utils/logger.js';
|
|
10
11
|
const contentTypeApplicationJson = 'application/json';
|
|
11
12
|
const contentTypeMultipartFormData = 'multipart/form-data';
|
|
12
13
|
export class Generator {
|
|
@@ -33,13 +34,13 @@ export class Generator {
|
|
|
33
34
|
this._commandLineArgs = commandLineArgs;
|
|
34
35
|
}
|
|
35
36
|
generate() {
|
|
36
|
-
|
|
37
|
+
logger.info('Starting Swagger API generation...');
|
|
37
38
|
Utils.clearDirectory(this._commandLineArgs.outputDirectory);
|
|
38
39
|
this.computeApi();
|
|
39
40
|
this.computeModel();
|
|
40
41
|
this.generateModel();
|
|
41
42
|
this.generateApi();
|
|
42
|
-
|
|
43
|
+
logger.success('Swagger generation completed successfully!');
|
|
43
44
|
}
|
|
44
45
|
computeApi() {
|
|
45
46
|
this._barApis.start(Object.getOwnPropertyNames(this._swagger.paths).length, 0);
|
|
@@ -73,18 +74,6 @@ export class Generator {
|
|
|
73
74
|
const apiSwaggerMethodKey = this._swagger.paths[apiName];
|
|
74
75
|
const apiMethod = Object.getOwnPropertyNames(apiSwaggerMethodKey)[0];
|
|
75
76
|
const apiSwaggerMethod = apiSwaggerMethodKey[apiMethod];
|
|
76
|
-
// const parametersRefType = apiSwaggerMethod.parameters?.filter(x => x.in == 'query' && x.schema?.$ref != null).map(x => x.schema.$ref.replace('#/components/schemas/', ''));
|
|
77
|
-
// if (parametersRefType) {
|
|
78
|
-
// usedTypes = usedTypes.concat(parametersRefType);
|
|
79
|
-
// }
|
|
80
|
-
// const responseRefType = this.computeRequestBodyType(apiSwaggerMethod!);
|
|
81
|
-
// if (responseRefType && !responseRefType.isVoid && !responseRefType.isNativeType) {
|
|
82
|
-
// usedTypes = usedTypes.concat(responseRefType.typeName);
|
|
83
|
-
// }
|
|
84
|
-
// const contentRefType = this.computeResponseType(apiSwaggerMethod!);
|
|
85
|
-
// if (contentRefType && !contentRefType.isVoid && !contentRefType.isNativeType) {
|
|
86
|
-
// usedTypes = usedTypes.concat(contentRefType.typeName);
|
|
87
|
-
// }
|
|
88
77
|
if (apiSwaggerMethod.requestBody?.content[contentTypeMultipartFormData]?.schema != null) {
|
|
89
78
|
usedMultiPart.push(apiName);
|
|
90
79
|
}
|
|
@@ -92,17 +81,11 @@ export class Generator {
|
|
|
92
81
|
for (let index = 0; index < this._apis.length; index++) {
|
|
93
82
|
if (this._apis[index].returnType && this._apis[index].returnType.isTypeReference) {
|
|
94
83
|
usedTypes.push(this._apis[index].returnType);
|
|
95
|
-
// if (this._apis[index].returnType!.typeName.startsWith("hotac_")) {
|
|
96
|
-
// debugger
|
|
97
|
-
// }
|
|
98
84
|
}
|
|
99
85
|
if (this._apis[index].parameters) {
|
|
100
86
|
this._apis[index].parameters.forEach(parameter => {
|
|
101
87
|
if (parameter.isTypeReference) {
|
|
102
88
|
usedTypes.push(parameter);
|
|
103
|
-
// if (parameter.typeName.startsWith("hotac_")) {
|
|
104
|
-
// debugger
|
|
105
|
-
// }
|
|
106
89
|
}
|
|
107
90
|
});
|
|
108
91
|
}
|
|
@@ -280,8 +263,8 @@ export class Generator {
|
|
|
280
263
|
};
|
|
281
264
|
}
|
|
282
265
|
catch (error) {
|
|
283
|
-
const errorMessage = "
|
|
284
|
-
|
|
266
|
+
const errorMessage = "Make sure your API endpoint uses IActionResult with a typed return value";
|
|
267
|
+
logger.error(errorMessage, error);
|
|
285
268
|
throw new Error(errorMessage);
|
|
286
269
|
}
|
|
287
270
|
if (swaggerContent.schema.type != null) {
|
|
@@ -438,7 +421,7 @@ export class Generator {
|
|
|
438
421
|
});
|
|
439
422
|
}
|
|
440
423
|
catch (error) {
|
|
441
|
-
|
|
424
|
+
logger.error(`Error parsing enum value at index ${index}:`, swaggerCopmponent.enum[index]);
|
|
442
425
|
}
|
|
443
426
|
}
|
|
444
427
|
return values;
|
|
@@ -455,8 +438,8 @@ export class Generator {
|
|
|
455
438
|
if (!swaggerComponent.properties)
|
|
456
439
|
return;
|
|
457
440
|
for (let j = 0; j < Object.getOwnPropertyNames(swaggerComponent.properties).length; j++) {
|
|
441
|
+
const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[j];
|
|
458
442
|
try {
|
|
459
|
-
const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[j];
|
|
460
443
|
let nestedUsedType = '';
|
|
461
444
|
if (!swaggerComponent.properties[propertyName])
|
|
462
445
|
continue;
|
|
@@ -485,14 +468,15 @@ export class Generator {
|
|
|
485
468
|
}
|
|
486
469
|
}
|
|
487
470
|
catch (error) {
|
|
488
|
-
|
|
471
|
+
logger.error(`Error processing nested object for property ${propertyName}`, error);
|
|
489
472
|
}
|
|
490
473
|
}
|
|
491
474
|
}
|
|
492
475
|
getNativeType(schema) {
|
|
493
476
|
let nativeType = 'n.d.';
|
|
494
477
|
if (schema.$ref != null) {
|
|
495
|
-
|
|
478
|
+
logger.error('Unexpected $ref in getNativeType:', schema.$ref);
|
|
479
|
+
throw new Error(`Schema with $ref should not be processed as native type: ${schema.$ref}`);
|
|
496
480
|
}
|
|
497
481
|
else if (schema.type == 'array' && schema.items != null) {
|
|
498
482
|
nativeType = this.getNativeType(schema.items);
|
|
@@ -522,8 +506,8 @@ export class Generator {
|
|
|
522
506
|
return nativeType;
|
|
523
507
|
}
|
|
524
508
|
else {
|
|
525
|
-
|
|
526
|
-
throw new Error("
|
|
509
|
+
logger.error("Unmanaged schema type", schema);
|
|
510
|
+
throw new Error("Unmanaged schema type");
|
|
527
511
|
}
|
|
528
512
|
}
|
|
529
513
|
getApiNameNormalized(apiName) {
|
|
@@ -4,6 +4,7 @@ import Mustache from 'mustache';
|
|
|
4
4
|
import { Normalizator } from './normalizator.js';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import { logger } from '@src/utils/logger.js';
|
|
7
8
|
export class ApiDartWriter {
|
|
8
9
|
_commandLineArgs;
|
|
9
10
|
constructor(commandLineArgs) {
|
|
@@ -20,7 +21,7 @@ export class ApiDartWriter {
|
|
|
20
21
|
}
|
|
21
22
|
const grouped = this._groupByTag(apis);
|
|
22
23
|
for (const [tag, apis] of Object.entries(grouped)) {
|
|
23
|
-
|
|
24
|
+
logger.progress(`Generating Dart API: ${tag}`);
|
|
24
25
|
let subPath = '';
|
|
25
26
|
let filename = '';
|
|
26
27
|
let apiClassName = '';
|
|
@@ -54,10 +55,6 @@ export class ApiDartWriter {
|
|
|
54
55
|
methodName = methodName.slice(tag.length);
|
|
55
56
|
methodName = Utils.toFirstLetterLowercase(methodName);
|
|
56
57
|
}
|
|
57
|
-
// console.debug(`\tAPI - ${apiName} - ${apiMethod}`);
|
|
58
|
-
if (methodName.toLowerCase().indexOf("productsave") >= 0) {
|
|
59
|
-
debugger;
|
|
60
|
-
}
|
|
61
58
|
const endpoint = {
|
|
62
59
|
methodName: methodName,
|
|
63
60
|
httpMethod: api.method.toLowerCase(),
|
|
@@ -30,7 +30,7 @@ class {{apiClassName}} {
|
|
|
30
30
|
{{/haveRequest}}
|
|
31
31
|
);
|
|
32
32
|
{{#isResponseNativeType}}
|
|
33
|
-
return
|
|
33
|
+
return _parseNative<{{responseType}}>(response.data);
|
|
34
34
|
{{/isResponseNativeType}}
|
|
35
35
|
{{^isResponseNativeType}}
|
|
36
36
|
return {{responseType}}.fromJson(response.data);
|
|
@@ -39,11 +39,40 @@ class {{apiClassName}} {
|
|
|
39
39
|
|
|
40
40
|
{{/endpoints}}
|
|
41
41
|
|
|
42
|
-
T
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
T _parseNative<T>(dynamic data) {
|
|
43
|
+
if (data == null) {
|
|
44
|
+
throw Exception('Cannot parse null data as $T');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (T == String) {
|
|
48
|
+
return data.toString() as T;
|
|
49
|
+
}
|
|
50
|
+
if (T == int) {
|
|
51
|
+
if (data is int) return data as T;
|
|
52
|
+
if (data is double) return data.toInt() as T;
|
|
53
|
+
return int.parse(data.toString()) as T;
|
|
54
|
+
}
|
|
55
|
+
if (T == double) {
|
|
56
|
+
if (data is double) return data as T;
|
|
57
|
+
if (data is int) return data.toDouble() as T;
|
|
58
|
+
return double.parse(data.toString()) as T;
|
|
59
|
+
}
|
|
60
|
+
if (T == bool) {
|
|
61
|
+
if (data is bool) return data as T;
|
|
62
|
+
if (data is String) {
|
|
63
|
+
final lower = data.toLowerCase();
|
|
64
|
+
if (lower == 'true' || lower == '1') return true as T;
|
|
65
|
+
if (lower == 'false' || lower == '0') return false as T;
|
|
66
|
+
}
|
|
67
|
+
if (data is int) return (data != 0) as T;
|
|
68
|
+
throw Exception('Cannot convert $data to bool');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Se il tipo è dynamic o Object, ritorna direttamente
|
|
72
|
+
if (T == dynamic || T == Object) {
|
|
73
|
+
return data as T;
|
|
74
|
+
}
|
|
75
|
+
|
|
47
76
|
throw UnsupportedError('Unsupported native type $T');
|
|
48
77
|
}
|
|
49
78
|
}
|
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
import fetch from 'node-fetch';
|
|
2
|
+
import { logger } from './utils/logger.js';
|
|
3
|
+
import { SwaggerValidator } from './utils/swagger-validator.js';
|
|
2
4
|
const settings = { method: "Get" };
|
|
3
5
|
export class SwaggerDownloader {
|
|
4
6
|
async download(swaggerJsonUrl) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
try {
|
|
8
|
+
logger.progress(`Downloading Swagger from: ${swaggerJsonUrl}`);
|
|
9
|
+
const response = await fetch(swaggerJsonUrl, settings);
|
|
10
|
+
if (!response.ok) {
|
|
11
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
12
|
+
}
|
|
13
|
+
const json = await response.json();
|
|
14
|
+
// Validate the downloaded swagger document
|
|
15
|
+
if (!SwaggerValidator.isValidSwaggerDocument(json)) {
|
|
16
|
+
throw new Error('Downloaded document is not a valid Swagger/OpenAPI specification');
|
|
17
|
+
}
|
|
18
|
+
// Normalize openapi field to openApi (camelCase) if needed
|
|
19
|
+
const jsonAny = json;
|
|
20
|
+
if (jsonAny.openapi && !jsonAny.openApi) {
|
|
21
|
+
jsonAny.openApi = jsonAny.openapi;
|
|
22
|
+
}
|
|
23
|
+
if (jsonAny.swagger && !jsonAny.openApi) {
|
|
24
|
+
jsonAny.openApi = jsonAny.swagger;
|
|
25
|
+
}
|
|
26
|
+
const swagger = jsonAny;
|
|
27
|
+
SwaggerValidator.validate(swagger);
|
|
28
|
+
logger.success(`Swagger document downloaded successfully`);
|
|
29
|
+
return swagger;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger.error(`Failed to download Swagger from ${swaggerJsonUrl}`, error);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
8
35
|
}
|
|
9
36
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized logging utility for the Swagger Generator
|
|
3
|
+
* Provides consistent logging with levels and formatting
|
|
4
|
+
*/
|
|
5
|
+
export declare enum LogLevel {
|
|
6
|
+
DEBUG = 0,
|
|
7
|
+
INFO = 1,
|
|
8
|
+
WARN = 2,
|
|
9
|
+
ERROR = 3,
|
|
10
|
+
SILENT = 4
|
|
11
|
+
}
|
|
12
|
+
export declare class Logger {
|
|
13
|
+
private static instance;
|
|
14
|
+
private logLevel;
|
|
15
|
+
private constructor();
|
|
16
|
+
static getInstance(): Logger;
|
|
17
|
+
setLogLevel(level: LogLevel): void;
|
|
18
|
+
debug(message: string, ...args: any[]): void;
|
|
19
|
+
info(message: string, ...args: any[]): void;
|
|
20
|
+
warn(message: string, ...args: any[]): void;
|
|
21
|
+
error(message: string, error?: any): void;
|
|
22
|
+
success(message: string): void;
|
|
23
|
+
progress(message: string): void;
|
|
24
|
+
}
|
|
25
|
+
export declare const logger: Logger;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized logging utility for the Swagger Generator
|
|
3
|
+
* Provides consistent logging with levels and formatting
|
|
4
|
+
*/
|
|
5
|
+
export var LogLevel;
|
|
6
|
+
(function (LogLevel) {
|
|
7
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
8
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
9
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
10
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
11
|
+
LogLevel[LogLevel["SILENT"] = 4] = "SILENT";
|
|
12
|
+
})(LogLevel || (LogLevel = {}));
|
|
13
|
+
export class Logger {
|
|
14
|
+
static instance;
|
|
15
|
+
logLevel = LogLevel.INFO;
|
|
16
|
+
constructor() { }
|
|
17
|
+
static getInstance() {
|
|
18
|
+
if (!Logger.instance) {
|
|
19
|
+
Logger.instance = new Logger();
|
|
20
|
+
}
|
|
21
|
+
return Logger.instance;
|
|
22
|
+
}
|
|
23
|
+
setLogLevel(level) {
|
|
24
|
+
this.logLevel = level;
|
|
25
|
+
}
|
|
26
|
+
debug(message, ...args) {
|
|
27
|
+
if (this.logLevel <= LogLevel.DEBUG) {
|
|
28
|
+
console.debug(`[DEBUG] ${message}`, ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
info(message, ...args) {
|
|
32
|
+
if (this.logLevel <= LogLevel.INFO) {
|
|
33
|
+
console.info(`[INFO] ${message}`, ...args);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
warn(message, ...args) {
|
|
37
|
+
if (this.logLevel <= LogLevel.WARN) {
|
|
38
|
+
console.warn(`[WARN] ${message}`, ...args);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
error(message, error) {
|
|
42
|
+
if (this.logLevel <= LogLevel.ERROR) {
|
|
43
|
+
if (error) {
|
|
44
|
+
console.error(`[ERROR] ${message}`, error);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.error(`[ERROR] ${message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
success(message) {
|
|
52
|
+
if (this.logLevel <= LogLevel.INFO) {
|
|
53
|
+
console.info(`✓ ${message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
progress(message) {
|
|
57
|
+
if (this.logLevel <= LogLevel.INFO) {
|
|
58
|
+
console.info(`→ ${message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Export singleton instance
|
|
63
|
+
export const logger = Logger.getInstance();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Swagger } from '../models/swagger/swagger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates Swagger/OpenAPI specification
|
|
4
|
+
* Ensures the document has required fields before processing
|
|
5
|
+
*/
|
|
6
|
+
export declare class SwaggerValidator {
|
|
7
|
+
/**
|
|
8
|
+
* Validates a Swagger document structure
|
|
9
|
+
* @param swagger - The Swagger document to validate
|
|
10
|
+
* @throws Error if validation fails
|
|
11
|
+
*/
|
|
12
|
+
static validate(swagger: any): void;
|
|
13
|
+
/**
|
|
14
|
+
* Checks if the Swagger document is likely valid without throwing
|
|
15
|
+
* @param obj - Object to check
|
|
16
|
+
* @returns true if it looks like a valid Swagger document
|
|
17
|
+
*/
|
|
18
|
+
static isValidSwaggerDocument(obj: any): obj is Swagger;
|
|
19
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { logger } from './logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates Swagger/OpenAPI specification
|
|
4
|
+
* Ensures the document has required fields before processing
|
|
5
|
+
*/
|
|
6
|
+
export class SwaggerValidator {
|
|
7
|
+
/**
|
|
8
|
+
* Validates a Swagger document structure
|
|
9
|
+
* @param swagger - The Swagger document to validate
|
|
10
|
+
* @throws Error if validation fails
|
|
11
|
+
*/
|
|
12
|
+
static validate(swagger) {
|
|
13
|
+
const errors = [];
|
|
14
|
+
// Check required top-level fields (support both openapi and openApi)
|
|
15
|
+
const version = swagger.openapi || swagger.swagger || swagger.openApi;
|
|
16
|
+
if (!version) {
|
|
17
|
+
errors.push('Missing openapi/swagger version field');
|
|
18
|
+
}
|
|
19
|
+
if (!swagger.paths || Object.keys(swagger.paths).length === 0) {
|
|
20
|
+
errors.push('Missing or empty paths object');
|
|
21
|
+
}
|
|
22
|
+
if (!swagger.info) {
|
|
23
|
+
errors.push('Missing info object');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
if (!swagger.info.title) {
|
|
27
|
+
errors.push('Missing info.title');
|
|
28
|
+
}
|
|
29
|
+
if (!swagger.info.version) {
|
|
30
|
+
errors.push('Missing info.version');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Check components
|
|
34
|
+
if (!swagger.components && !swagger.definitions) {
|
|
35
|
+
logger.warn('No components/definitions found - model generation may be limited');
|
|
36
|
+
}
|
|
37
|
+
// Validate paths structure
|
|
38
|
+
if (swagger.paths) {
|
|
39
|
+
for (const [path, methods] of Object.entries(swagger.paths)) {
|
|
40
|
+
if (!methods || typeof methods !== 'object') {
|
|
41
|
+
errors.push(`Invalid path definition for: ${path}`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const validMethods = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head'];
|
|
45
|
+
const pathMethods = Object.keys(methods);
|
|
46
|
+
if (pathMethods.length === 0) {
|
|
47
|
+
errors.push(`No HTTP methods defined for path: ${path}`);
|
|
48
|
+
}
|
|
49
|
+
for (const method of pathMethods) {
|
|
50
|
+
if (!validMethods.includes(method.toLowerCase())) {
|
|
51
|
+
logger.warn(`Unexpected HTTP method '${method}' in path: ${path}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// If there are errors, throw
|
|
57
|
+
if (errors.length > 0) {
|
|
58
|
+
const errorMessage = `Swagger validation failed:\n${errors.map(e => ` - ${e}`).join('\n')}`;
|
|
59
|
+
logger.error(errorMessage);
|
|
60
|
+
throw new Error(errorMessage);
|
|
61
|
+
}
|
|
62
|
+
logger.success('Swagger document validation passed');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Checks if the Swagger document is likely valid without throwing
|
|
66
|
+
* @param obj - Object to check
|
|
67
|
+
* @returns true if it looks like a valid Swagger document
|
|
68
|
+
*/
|
|
69
|
+
static isValidSwaggerDocument(obj) {
|
|
70
|
+
return obj &&
|
|
71
|
+
typeof obj === 'object' &&
|
|
72
|
+
(obj.openApi || obj.openapi || obj.swagger) &&
|
|
73
|
+
obj.paths &&
|
|
74
|
+
typeof obj.paths === 'object';
|
|
75
|
+
}
|
|
76
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devlearning/swagger-generator",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"description": "Swagger generator apis and models for Angular and NextJS",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
+
"build": "npx tsc && npm run copy-templates",
|
|
8
9
|
"debug-angular": "npx tsx src/index.ts --url http://localhost:5208/swagger/v1/swagger.json --output autogen --target angular --dateTimeLibrary moment",
|
|
9
10
|
"debug-nextjs": "npx tsx src/index.ts --url http://localhost:7550/swagger/ApiGateway/swagger.json --output autogen --target next --dateTimeLibrary date-fns",
|
|
10
11
|
"debug-flutter": "npx tsx src/index.ts --url http://localhost:7550/swagger/ApiGateway/swagger.json --output autogen --target flutter --package coqudo_app",
|
|
11
12
|
"copy-templates": "copyfiles -u 3 \"src/generators-writers/dart/templates/**/*\" dist/generators-writers/dart",
|
|
12
|
-
"
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"deploy": "npm publish"
|
|
13
15
|
},
|
|
14
16
|
"bin": {
|
|
15
17
|
"swgen": "./dist/index.js"
|
package/src/generator.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { CommandLineArgs, DateTimeLibrary, TargetGeneration } from './index.js';
|
|
|
19
19
|
import { ApiDartWriter } from './generators-writers/dart/api-dart-writer.js';
|
|
20
20
|
import { ModelDartWriter } from './generators-writers/dart/model-dart-writer.js';
|
|
21
21
|
import { Utils } from './generators-writers/utils.js';
|
|
22
|
+
import { logger } from './utils/logger.js';
|
|
22
23
|
|
|
23
24
|
const contentTypeApplicationJson = 'application/json';
|
|
24
25
|
const contentTypeMultipartFormData = 'multipart/form-data';
|
|
@@ -52,7 +53,7 @@ export class Generator {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
generate() {
|
|
55
|
-
|
|
56
|
+
logger.info('Starting Swagger API generation...');
|
|
56
57
|
|
|
57
58
|
Utils.clearDirectory(this._commandLineArgs.outputDirectory);
|
|
58
59
|
|
|
@@ -62,7 +63,7 @@ export class Generator {
|
|
|
62
63
|
this.generateModel();
|
|
63
64
|
this.generateApi();
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
logger.success('Swagger generation completed successfully!');
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
computeApi() {
|
|
@@ -102,21 +103,6 @@ export class Generator {
|
|
|
102
103
|
const apiMethod = Object.getOwnPropertyNames(apiSwaggerMethodKey)[0];
|
|
103
104
|
const apiSwaggerMethod = apiSwaggerMethodKey[apiMethod];
|
|
104
105
|
|
|
105
|
-
// const parametersRefType = apiSwaggerMethod.parameters?.filter(x => x.in == 'query' && x.schema?.$ref != null).map(x => x.schema.$ref.replace('#/components/schemas/', ''));
|
|
106
|
-
// if (parametersRefType) {
|
|
107
|
-
// usedTypes = usedTypes.concat(parametersRefType);
|
|
108
|
-
// }
|
|
109
|
-
|
|
110
|
-
// const responseRefType = this.computeRequestBodyType(apiSwaggerMethod!);
|
|
111
|
-
// if (responseRefType && !responseRefType.isVoid && !responseRefType.isNativeType) {
|
|
112
|
-
// usedTypes = usedTypes.concat(responseRefType.typeName);
|
|
113
|
-
// }
|
|
114
|
-
|
|
115
|
-
// const contentRefType = this.computeResponseType(apiSwaggerMethod!);
|
|
116
|
-
// if (contentRefType && !contentRefType.isVoid && !contentRefType.isNativeType) {
|
|
117
|
-
// usedTypes = usedTypes.concat(contentRefType.typeName);
|
|
118
|
-
// }
|
|
119
|
-
|
|
120
106
|
if (apiSwaggerMethod.requestBody?.content[contentTypeMultipartFormData]?.schema != null) {
|
|
121
107
|
usedMultiPart.push(apiName);
|
|
122
108
|
}
|
|
@@ -125,18 +111,12 @@ export class Generator {
|
|
|
125
111
|
for (let index = 0; index < this._apis.length; index++) {
|
|
126
112
|
if (this._apis[index].returnType && this._apis[index].returnType!.isTypeReference) {
|
|
127
113
|
usedTypes.push(this._apis[index].returnType!);
|
|
128
|
-
// if (this._apis[index].returnType!.typeName.startsWith("hotac_")) {
|
|
129
|
-
// debugger
|
|
130
|
-
// }
|
|
131
114
|
}
|
|
132
115
|
|
|
133
116
|
if (this._apis[index].parameters) {
|
|
134
117
|
this._apis[index].parameters.forEach(parameter => {
|
|
135
118
|
if (parameter.isTypeReference) {
|
|
136
119
|
usedTypes.push(parameter);
|
|
137
|
-
// if (parameter.typeName.startsWith("hotac_")) {
|
|
138
|
-
// debugger
|
|
139
|
-
// }
|
|
140
120
|
}
|
|
141
121
|
});
|
|
142
122
|
}
|
|
@@ -328,8 +308,8 @@ export class Generator {
|
|
|
328
308
|
isNativeType: false,
|
|
329
309
|
};
|
|
330
310
|
} catch (error) {
|
|
331
|
-
const errorMessage = "
|
|
332
|
-
|
|
311
|
+
const errorMessage = "Make sure your API endpoint uses IActionResult with a typed return value";
|
|
312
|
+
logger.error(errorMessage, error);
|
|
333
313
|
throw new Error(errorMessage);
|
|
334
314
|
}
|
|
335
315
|
|
|
@@ -502,7 +482,7 @@ export class Generator {
|
|
|
502
482
|
value: swaggerCopmponent.enum[index].split('-')[1].trim(),
|
|
503
483
|
});
|
|
504
484
|
} catch (error) {
|
|
505
|
-
|
|
485
|
+
logger.error(`Error parsing enum value at index ${index}:`, swaggerCopmponent.enum[index]);
|
|
506
486
|
}
|
|
507
487
|
|
|
508
488
|
}
|
|
@@ -522,9 +502,9 @@ export class Generator {
|
|
|
522
502
|
if (!swaggerComponent.properties) return;
|
|
523
503
|
|
|
524
504
|
for (let j = 0; j < Object.getOwnPropertyNames(swaggerComponent.properties).length; j++) {
|
|
505
|
+
const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[j];
|
|
506
|
+
|
|
525
507
|
try {
|
|
526
|
-
const propertyName = Object.getOwnPropertyNames(swaggerComponent.properties)[j];
|
|
527
|
-
|
|
528
508
|
let nestedUsedType = '';
|
|
529
509
|
|
|
530
510
|
if (!swaggerComponent.properties[propertyName]) continue;
|
|
@@ -552,7 +532,7 @@ export class Generator {
|
|
|
552
532
|
this.retrieveNestedObjectsRecursive(nested, usedTypes);
|
|
553
533
|
}
|
|
554
534
|
} catch (error) {
|
|
555
|
-
|
|
535
|
+
logger.error(`Error processing nested object for property ${propertyName}`, error);
|
|
556
536
|
}
|
|
557
537
|
}
|
|
558
538
|
}
|
|
@@ -561,7 +541,8 @@ export class Generator {
|
|
|
561
541
|
let nativeType = 'n.d.';
|
|
562
542
|
|
|
563
543
|
if (schema.$ref != null) {
|
|
564
|
-
|
|
544
|
+
logger.error('Unexpected $ref in getNativeType:', schema.$ref);
|
|
545
|
+
throw new Error(`Schema with $ref should not be processed as native type: ${schema.$ref}`);
|
|
565
546
|
} else if (schema.type == 'array' && schema.items != null) {
|
|
566
547
|
nativeType = this.getNativeType(schema.items);
|
|
567
548
|
nativeType += '[]';
|
|
@@ -580,8 +561,8 @@ export class Generator {
|
|
|
580
561
|
if (nativeType.indexOf('n.d') == -1) {
|
|
581
562
|
return nativeType;
|
|
582
563
|
} else {
|
|
583
|
-
|
|
584
|
-
throw new Error("
|
|
564
|
+
logger.error("Unmanaged schema type", schema);
|
|
565
|
+
throw new Error("Unmanaged schema type");
|
|
585
566
|
}
|
|
586
567
|
}
|
|
587
568
|
|
|
@@ -10,6 +10,7 @@ import { ImportDefinitionDart } from './models/import-definition-dart.js';
|
|
|
10
10
|
import { Normalizator } from './normalizator.js';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { fileURLToPath } from 'url';
|
|
13
|
+
import { logger } from '@src/utils/logger.js';
|
|
13
14
|
|
|
14
15
|
interface ApiDefinitionDart {
|
|
15
16
|
package: string;
|
|
@@ -60,7 +61,7 @@ export class ApiDartWriter {
|
|
|
60
61
|
const grouped = this._groupByTag(apis);
|
|
61
62
|
|
|
62
63
|
for (const [tag, apis] of Object.entries(grouped)) {
|
|
63
|
-
|
|
64
|
+
logger.progress(`Generating Dart API: ${tag}`);
|
|
64
65
|
|
|
65
66
|
let subPath = '';
|
|
66
67
|
let filename = '';
|
|
@@ -103,11 +104,6 @@ export class ApiDartWriter {
|
|
|
103
104
|
methodName = Utils.toFirstLetterLowercase(methodName);
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
// console.debug(`\tAPI - ${apiName} - ${apiMethod}`);
|
|
107
|
-
if (methodName.toLowerCase().indexOf("productsave") >= 0) {
|
|
108
|
-
debugger
|
|
109
|
-
}
|
|
110
|
-
|
|
111
107
|
const endpoint = <EndpointDefinitionDart>{
|
|
112
108
|
methodName: methodName,
|
|
113
109
|
httpMethod: api.method.toLowerCase() as HttpMethodDart,
|
|
@@ -30,7 +30,7 @@ class {{apiClassName}} {
|
|
|
30
30
|
{{/haveRequest}}
|
|
31
31
|
);
|
|
32
32
|
{{#isResponseNativeType}}
|
|
33
|
-
return
|
|
33
|
+
return _parseNative<{{responseType}}>(response.data);
|
|
34
34
|
{{/isResponseNativeType}}
|
|
35
35
|
{{^isResponseNativeType}}
|
|
36
36
|
return {{responseType}}.fromJson(response.data);
|
|
@@ -39,11 +39,40 @@ class {{apiClassName}} {
|
|
|
39
39
|
|
|
40
40
|
{{/endpoints}}
|
|
41
41
|
|
|
42
|
-
T
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
T _parseNative<T>(dynamic data) {
|
|
43
|
+
if (data == null) {
|
|
44
|
+
throw Exception('Cannot parse null data as $T');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (T == String) {
|
|
48
|
+
return data.toString() as T;
|
|
49
|
+
}
|
|
50
|
+
if (T == int) {
|
|
51
|
+
if (data is int) return data as T;
|
|
52
|
+
if (data is double) return data.toInt() as T;
|
|
53
|
+
return int.parse(data.toString()) as T;
|
|
54
|
+
}
|
|
55
|
+
if (T == double) {
|
|
56
|
+
if (data is double) return data as T;
|
|
57
|
+
if (data is int) return data.toDouble() as T;
|
|
58
|
+
return double.parse(data.toString()) as T;
|
|
59
|
+
}
|
|
60
|
+
if (T == bool) {
|
|
61
|
+
if (data is bool) return data as T;
|
|
62
|
+
if (data is String) {
|
|
63
|
+
final lower = data.toLowerCase();
|
|
64
|
+
if (lower == 'true' || lower == '1') return true as T;
|
|
65
|
+
if (lower == 'false' || lower == '0') return false as T;
|
|
66
|
+
}
|
|
67
|
+
if (data is int) return (data != 0) as T;
|
|
68
|
+
throw Exception('Cannot convert $data to bool');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Se il tipo è dynamic o Object, ritorna direttamente
|
|
72
|
+
if (T == dynamic || T == Object) {
|
|
73
|
+
return data as T;
|
|
74
|
+
}
|
|
75
|
+
|
|
47
76
|
throw UnsupportedError('Unsupported native type $T');
|
|
48
77
|
}
|
|
49
78
|
}
|