@devlearning/swagger-generator 1.1.12 → 1.1.13

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.md CHANGED
@@ -67,6 +67,60 @@ npx swgen \
67
67
  | `--target` | `-t` | Framework target (`angular`, `next`, `flutter`) | ✅ | - |
68
68
  | `--dateTimeLibrary` | `-d` | Libreria date (`moment`, `date-fns`) | ❌ | `date-fns` |
69
69
  | `--package` | `-p` | Nome package (solo Dart/Flutter) | ❌ | - |
70
+ | `--api-client-name` | `-a` | Nome classe API unificata | ❌ | - |
71
+
72
+ ### 🆕 API Client Unificata
73
+
74
+ Di default, il generatore crea classi API separate raggruppate per tag Swagger. Con l'opzione `--api-client-name`, puoi generare una **singola classe unificata** contenente tutti i metodi API.
75
+
76
+ #### Esempio: Flutter con classe unificata
77
+
78
+ ```bash
79
+ npx swgen \
80
+ --url http://localhost:5000/swagger/v1/swagger.json \
81
+ --output lib/core/api \
82
+ --target flutter \
83
+ --package my_app \
84
+ --api-client-name ApiClient
85
+ ```
86
+
87
+ Genera:
88
+ ```dart
89
+ // lib/core/api/api_client.dart
90
+ class ApiClient {
91
+ // Tutti i metodi API in una singola classe
92
+ Future<User> getUser(String id) async { ... }
93
+ Future<List<Product>> getProducts() async { ... }
94
+ Future<void> createOrder(Order order) async { ... }
95
+ // ...
96
+ }
97
+ ```
98
+
99
+ #### Senza --api-client-name (comportamento predefinito)
100
+
101
+ Genera classi separate per tag:
102
+ ```dart
103
+ // lib/core/api/users_api.dart
104
+ class UsersApi { ... }
105
+
106
+ // lib/core/api/products_api.dart
107
+ class ProductsApi { ... }
108
+
109
+ // lib/core/api/orders_api.dart
110
+ class OrdersApi { ... }
111
+ ```
112
+
113
+ #### Quando usare la classe unificata?
114
+
115
+ - ✅ Progetti piccoli/medi con pochi endpoint
116
+ - ✅ Quando preferisci un singolo punto di accesso alle API
117
+ - ✅ Per semplificare la dependency injection
118
+
119
+ #### Quando usare classi separate?
120
+
121
+ - ✅ Progetti grandi con molti endpoint
122
+ - ✅ Quando vuoi separare le responsabilità per dominio
123
+ - ✅ Per team diversi che lavorano su moduli diversi
70
124
 
71
125
  ## 📁 Struttura File Generati
72
126
 
package/dist/generator.js CHANGED
@@ -126,11 +126,11 @@ export class Generator {
126
126
  generateApi() {
127
127
  this._barApis.update(this._apis.length, { message: `Api generating...` });
128
128
  if (this._commandLineArgs.target == TargetGeneration.Angular) {
129
- const apiWriter = new ApiAngularWriter(this._commandLineArgs.outputDirectory);
129
+ const apiWriter = new ApiAngularWriter(this._commandLineArgs);
130
130
  apiWriter.write(this._apis);
131
131
  }
132
132
  else if (this._commandLineArgs.target == TargetGeneration.Next) {
133
- const apiWriter = new ApiNextJsWriter(this._commandLineArgs.outputDirectory);
133
+ const apiWriter = new ApiNextJsWriter(this._commandLineArgs);
134
134
  apiWriter.write(this._apis);
135
135
  }
136
136
  else if (this._commandLineArgs.target == TargetGeneration.Flutter) {
@@ -1,7 +1,8 @@
1
1
  import { ApiDto } from '../../models/api-dto.js';
2
+ import { CommandLineArgs } from '@src/index.js';
2
3
  export declare class ApiAngularWriter {
3
- private _outputDirectory;
4
- constructor(_outputDirectory: string);
4
+ private _commandLineArgs;
5
+ constructor(commandLineArgs: CommandLineArgs);
5
6
  write(apis: ApiDto[]): void;
6
7
  private _apiString;
7
8
  private _parameters;
@@ -12,4 +13,5 @@ export declare class ApiAngularWriter {
12
13
  private _queryParametersStatement;
13
14
  private _requestPreparation;
14
15
  private _writeFile;
16
+ private _getApiPre;
15
17
  }
@@ -1,11 +1,11 @@
1
1
  import fs from 'fs';
2
- import { API_POST, API_PRE } from './constants.js';
2
+ import { API_POST } from './constants.js';
3
3
  import { Utils } from '../utils.js';
4
4
  import { Normalizator } from './normalizator.js';
5
5
  export class ApiAngularWriter {
6
- _outputDirectory;
7
- constructor(_outputDirectory) {
8
- this._outputDirectory = _outputDirectory;
6
+ _commandLineArgs;
7
+ constructor(commandLineArgs) {
8
+ this._commandLineArgs = commandLineArgs;
9
9
  }
10
10
  write(apis) {
11
11
  let apiString = '';
@@ -120,8 +120,35 @@ export class ApiAngularWriter {
120
120
  }
121
121
  }
122
122
  _writeFile(apis) {
123
- fs.writeFileSync(this._outputDirectory + "/api.autogenerated.ts", `${API_PRE}
123
+ const className = this._commandLineArgs.apiClientName || 'ApiAutogeneratedService';
124
+ const apiPre = this._getApiPre(className);
125
+ fs.writeFileSync(this._commandLineArgs.outputDirectory + "/api.autogenerated.ts", `${apiPre}
124
126
  ${apis}
125
127
  ${API_POST}`, { flag: 'w' });
126
128
  }
129
+ _getApiPre(className) {
130
+ return `import { HttpClient } from '@angular/common/http';
131
+ import { Observable, catchError, map } from 'rxjs';
132
+ import * as Models from './model.autogenerated';
133
+ import { HttpHeaders } from "@angular/common/http";
134
+
135
+ export const httpOptions = {
136
+ headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
137
+ };
138
+
139
+ export const httpOptionsMultipart = {};
140
+
141
+ export abstract class ${className} {
142
+ constructor(
143
+ public _http: HttpClient,
144
+ public _baseUrl: string,
145
+ ) { }
146
+
147
+ protected abstract _momentToString(moment: moment.Moment): string;
148
+ protected abstract _handleRequest<T>(request: T): T;
149
+ protected abstract _handleMultipart<T>(request: T): FormData;
150
+ protected abstract _handleResponse<T>(response: T): T;
151
+ protected abstract _handleError(error: any, obs: any): Observable<never>;
152
+ `;
153
+ }
127
154
  }
@@ -5,6 +5,9 @@ export declare class ApiDartWriter {
5
5
  private _commandLineArgs;
6
6
  constructor(commandLineArgs: CommandLineArgs);
7
7
  write(apis: ApiDto[], models: ModelDto[]): void;
8
+ private _writeUnifiedApi;
9
+ private _writeApisByTag;
10
+ private _createEndpoint;
8
11
  private _toPascalCase;
9
12
  private _normalizeApiClassName;
10
13
  private _groupByTag;
@@ -19,6 +19,40 @@ export class ApiDartWriter {
19
19
  if (importDirectory.startsWith('lib/')) {
20
20
  importDirectory = importDirectory.slice('lib/'.length);
21
21
  }
22
+ // Se è specificato apiClientName, genera una singola classe unificata
23
+ if (this._commandLineArgs.apiClientName) {
24
+ this._writeUnifiedApi(apis, models, template, importDirectory);
25
+ }
26
+ else {
27
+ // Altrimenti suddividi per tag come prima
28
+ this._writeApisByTag(apis, models, template, importDirectory);
29
+ }
30
+ }
31
+ _writeUnifiedApi(apis, models, template, importDirectory) {
32
+ const apiClassName = this._commandLineArgs.apiClientName;
33
+ const filename = Utils.toDartFileName(apiClassName);
34
+ console.log(`Api: Unified API Client - ${apiClassName}`);
35
+ var apiDefinition = {
36
+ package: this._commandLineArgs.package,
37
+ outputDirectory: this._commandLineArgs.outputDirectory,
38
+ filename: filename,
39
+ apiClassName: apiClassName,
40
+ };
41
+ var endpoints = [];
42
+ var imports = [];
43
+ // Processa tutte le API senza raggruppamento
44
+ for (const api of apis) {
45
+ const endpointData = this._createEndpoint(api, models, imports, importDirectory);
46
+ endpoints.push(endpointData.endpoint);
47
+ }
48
+ apiDefinition.endpoints = endpoints;
49
+ apiDefinition.imports = imports.map(i => i.import);
50
+ let destinationPath = `${this._commandLineArgs.outputDirectory}/api`;
51
+ Utils.ensureDirectorySync(`${destinationPath}`);
52
+ var result = Mustache.render(template, apiDefinition);
53
+ writeFileSync(`${destinationPath}/${apiDefinition.filename}.dart`, result, 'utf-8');
54
+ }
55
+ _writeApisByTag(apis, models, template, importDirectory) {
22
56
  const grouped = this._groupByTag(apis);
23
57
  for (const [tag, apis] of Object.entries(grouped)) {
24
58
  logger.progress(`Generating Dart API: ${tag}`);
@@ -37,68 +71,8 @@ export class ApiDartWriter {
37
71
  var endpoints = [];
38
72
  var imports = [];
39
73
  for (const api of apis) {
40
- var requestType = undefined;
41
- requestType = api.haveRequest ? api.parameters[0]?.typeName || 'void' : undefined;
42
- requestType = requestType?.split('.').pop();
43
- requestType = Utils.toDartClassName(requestType);
44
- var responseType = 'void';
45
- responseType = api.returnType ? api.returnType.typeName : 'void';
46
- responseType = responseType?.split('.').pop();
47
- if (api.returnType?.isNativeType) {
48
- responseType = Normalizator.mapTsTypeToDart(responseType);
49
- }
50
- else {
51
- responseType = Utils.toDartClassName(responseType) ?? responseType;
52
- }
53
- var methodName = Utils.getNormalizedApiPathDart(api.name);
54
- if (methodName.startsWith(Utils.toFirstLetterLowercase(tag))) {
55
- methodName = methodName.slice(tag.length);
56
- methodName = Utils.toFirstLetterLowercase(methodName);
57
- }
58
- const endpoint = {
59
- methodName: methodName,
60
- httpMethod: api.method.toLowerCase(),
61
- path: api.url,
62
- responseType: responseType,
63
- isResponseNativeType: api.returnType ? api.returnType.isNativeType : true,
64
- haveRequest: api.haveRequest,
65
- requestType: requestType,
66
- };
67
- if (api.parameters && api.parameters.length > 0 && !api.haveRequest) {
68
- endpoint.queryParams = api.parameters
69
- .map(p => ({
70
- name: p.name,
71
- type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName) : 'String',
72
- nullable: p.nullable
73
- }));
74
- }
75
- if (api.haveRequest && api.parameters[0]?.typeName) {
76
- if (imports.findIndex(x => x.type.typeName == api.parameters[0]?.typeName) == -1) {
77
- models.forEach(model => {
78
- if (model.typeName === api.parameters[0]?.typeName) {
79
- const normalizedInfo = Normalizator.getNormalizedInfo(model);
80
- imports.push({
81
- type: api.parameters[0],
82
- import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
83
- });
84
- }
85
- });
86
- }
87
- }
88
- if (api.returnType && !api.returnType.isNativeType) {
89
- if (imports.findIndex(x => x.type.typeName == api.returnType.typeName) == -1) {
90
- models.forEach(model => {
91
- if (model.typeName === api.returnType.typeName) {
92
- const normalizedInfo = Normalizator.getNormalizedInfo(model);
93
- imports.push({
94
- type: api.returnType,
95
- import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
96
- });
97
- }
98
- });
99
- }
100
- }
101
- endpoints.push(endpoint);
74
+ const endpointData = this._createEndpoint(api, models, imports, importDirectory, tag);
75
+ endpoints.push(endpointData.endpoint);
102
76
  }
103
77
  apiDefinition.endpoints = endpoints;
104
78
  apiDefinition.imports = imports.map(i => i.import);
@@ -108,6 +82,74 @@ export class ApiDartWriter {
108
82
  writeFileSync(`${destinationPath}/${apiDefinition.filename}.dart`, result, 'utf-8');
109
83
  }
110
84
  }
85
+ _createEndpoint(api, models, imports, importDirectory, tag) {
86
+ var requestType = undefined;
87
+ requestType = api.haveRequest ? api.parameters[0]?.typeName || 'void' : undefined;
88
+ requestType = requestType?.split('.').pop();
89
+ requestType = Utils.toDartClassName(requestType);
90
+ var responseType = 'void';
91
+ responseType = api.returnType ? api.returnType.typeName : 'void';
92
+ responseType = responseType?.split('.').pop();
93
+ if (api.returnType?.isNativeType) {
94
+ responseType = Normalizator.mapTsTypeToDart(responseType);
95
+ }
96
+ else {
97
+ responseType = Utils.toDartClassName(responseType) ?? responseType;
98
+ }
99
+ var methodName = Utils.getNormalizedApiPathDart(api.name);
100
+ if (tag && methodName.startsWith(Utils.toFirstLetterLowercase(tag))) {
101
+ methodName = methodName.slice(tag.length);
102
+ methodName = Utils.toFirstLetterLowercase(methodName);
103
+ }
104
+ // console.debug(`\tAPI - ${apiName} - ${apiMethod}`);
105
+ if (methodName.toLowerCase().indexOf("productsave") >= 0) {
106
+ debugger;
107
+ }
108
+ const endpoint = {
109
+ methodName: methodName,
110
+ httpMethod: api.method.toLowerCase(),
111
+ path: api.url,
112
+ responseType: responseType,
113
+ isResponseNativeType: api.returnType ? api.returnType.isNativeType : true,
114
+ haveRequest: api.haveRequest,
115
+ requestType: requestType,
116
+ };
117
+ if (api.parameters && api.parameters.length > 0 && !api.haveRequest) {
118
+ endpoint.queryParams = api.parameters
119
+ .map(p => ({
120
+ name: p.name,
121
+ type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName) : 'String',
122
+ nullable: p.nullable
123
+ }));
124
+ }
125
+ if (api.haveRequest && api.parameters[0]?.typeName) {
126
+ if (imports.findIndex(x => x.type.typeName == api.parameters[0]?.typeName) == -1) {
127
+ models.forEach(model => {
128
+ if (model.typeName === api.parameters[0]?.typeName) {
129
+ const normalizedInfo = Normalizator.getNormalizedInfo(model);
130
+ imports.push({
131
+ type: api.parameters[0],
132
+ import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
133
+ });
134
+ }
135
+ });
136
+ }
137
+ }
138
+ if (api.returnType && !api.returnType.isNativeType) {
139
+ if (imports.findIndex(x => x.type.typeName == api.returnType.typeName) == -1) {
140
+ models.forEach(model => {
141
+ if (model.typeName === api.returnType.typeName) {
142
+ const normalizedInfo = Normalizator.getNormalizedInfo(model);
143
+ imports.push({
144
+ type: api.returnType,
145
+ import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
146
+ });
147
+ }
148
+ });
149
+ }
150
+ }
151
+ return { endpoint };
152
+ }
111
153
  _toPascalCase(input) {
112
154
  return input
113
155
  .replace(/[_\- ]+/g, ' ') // sostituisce underscore, dash e spazi con uno spazio singolo
@@ -1,7 +1,8 @@
1
1
  import { ApiDto } from '../../models/api-dto.js';
2
+ import { CommandLineArgs } from '../../index.js';
2
3
  export declare class ApiNextJsWriter {
3
- private _outputDirectory;
4
- constructor(_outputDirectory: string);
4
+ private _commandLineArgs;
5
+ constructor(commandLineArgs: CommandLineArgs);
5
6
  write(apis: ApiDto[]): void;
6
7
  private _apiString;
7
8
  private _parameters;
@@ -2,9 +2,9 @@ import fs from 'fs';
2
2
  import { API_PRE } from './constants.js';
3
3
  import { Utils } from '../utils.js';
4
4
  export class ApiNextJsWriter {
5
- _outputDirectory;
6
- constructor(_outputDirectory) {
7
- this._outputDirectory = _outputDirectory;
5
+ _commandLineArgs;
6
+ constructor(commandLineArgs) {
7
+ this._commandLineArgs = commandLineArgs;
8
8
  }
9
9
  write(apis) {
10
10
  let apiString = '';
@@ -121,7 +121,7 @@ ${preparation}const response = await axios.${method}<${returnTypeString}>(\`${ap
121
121
  `;
122
122
  }
123
123
  _writeFile(apis) {
124
- fs.writeFileSync(this._outputDirectory + "/api.autogenerated.ts", `${API_PRE}
124
+ fs.writeFileSync(this._commandLineArgs.outputDirectory + "/api.autogenerated.ts", `${API_PRE}
125
125
  ${apis}`, { flag: 'w' });
126
126
  }
127
127
  }
package/dist/index.d.ts CHANGED
@@ -14,4 +14,5 @@ export interface CommandLineArgs {
14
14
  target: TargetGeneration;
15
15
  dateTimeLibrary?: DateTimeLibrary;
16
16
  package?: string;
17
+ apiClientName?: string;
17
18
  }
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ export var TargetGeneration;
16
16
  })(TargetGeneration || (TargetGeneration = {}));
17
17
  const argv = yargs(hideBin(process.argv))
18
18
  .scriptName("swagger-gen")
19
- .usage('Usage: $0 --url <swaggerJsonUrl> --output <outputDirectory> --target <target> [--dateTimeLibrary <dateTimeLibrary>] [--package <name>]')
19
+ .usage('Usage: $0 --url <swaggerJsonUrl> --output <outputDirectory> --target <target> [--dateTimeLibrary <dateTimeLibrary>] [--package <name>] [--api-client-name <className>]')
20
20
  .option('url', {
21
21
  alias: 'u',
22
22
  type: 'string',
@@ -47,6 +47,11 @@ const argv = yargs(hideBin(process.argv))
47
47
  alias: 'p',
48
48
  type: 'string',
49
49
  describe: 'For Dart: package name for Dart project',
50
+ })
51
+ .option('api-client-name', {
52
+ alias: 'a',
53
+ type: 'string',
54
+ describe: 'Name for the unified API client class (all services in one class)',
50
55
  })
51
56
  .help()
52
57
  .parseSync();
@@ -56,6 +61,7 @@ const commandLineArgs = {
56
61
  target: parseTargetGeneration(argv.target),
57
62
  dateTimeLibrary: parseDateTimeLibrary(argv.dateTimeLibrary),
58
63
  package: argv.package ?? '',
64
+ apiClientName: argv['api-client-name'],
59
65
  };
60
66
  const swaggerDownloader = new SwaggerDownloader();
61
67
  swaggerDownloader.download(new URL(commandLineArgs.swaggerJsonUrl))
@@ -0,0 +1,29 @@
1
+ import 'package:{{package}}/core/di/injector.dart';
2
+ import 'package:dio/dio.dart';
3
+ {{#imports}}
4
+ {{{.}}}
5
+ {{/imports}}
6
+
7
+ class {{apiClassName}} {
8
+ final Dio _dio;
9
+
10
+ {{apiClassName}}() : _dio = getIt<Dio>();
11
+
12
+ {{#endpoints}}
13
+ Future<{{responseType}}> {{methodName}}({{#haveRequest}}{{requestType}} request{{/haveRequest}}) async {
14
+ final response = await _dio.{{httpMethod}}(
15
+ '{{{path}}}',
16
+ {{#haveRequest}}
17
+ {{#isGet}}
18
+ queryParameters: request.toJson(),
19
+ {{/isGet}}
20
+ {{^isGet}}
21
+ data: request.toJson(),
22
+ {{/isGet}}
23
+ {{/haveRequest}}
24
+ );
25
+ return {{responseType}}.fromJson(response.data);
26
+ }
27
+
28
+ {{/endpoints}}
29
+ }
@@ -0,0 +1,18 @@
1
+ import 'package:freezed_annotation/freezed_annotation.dart';
2
+ {{#imports}}
3
+ {{{.}}}
4
+ {{/imports}}
5
+
6
+ part '{{filename}}.freezed.dart';
7
+ part '{{filename}}.g.dart';
8
+
9
+ @freezed
10
+ abstract class {{modelName}} with _${{modelName}} {
11
+ const factory {{modelName}}({
12
+ {{#fields}}
13
+ {{required}}{{type}}{{nullable}} {{name}},
14
+ {{/fields}}
15
+ }) = _{{modelName}};
16
+
17
+ factory {{modelName}}.fromJson(Map<String, dynamic> json) => _${{modelName}}FromJson(json);
18
+ }
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@devlearning/swagger-generator",
3
- "version": "1.1.12",
3
+ "version": "1.1.13",
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
8
  "build": "npx tsc && npm run copy-templates",
9
9
  "debug-angular": "npx tsx src/index.ts --url http://localhost:5208/swagger/v1/swagger.json --output autogen --target angular --dateTimeLibrary moment",
10
+ "debug-angular-unified": "npx tsx src/index.ts --url http://localhost:5208/swagger/v1/swagger.json --output autogen --target angular --dateTimeLibrary moment --api-client-name ApiClient",
10
11
  "debug-nextjs": "npx tsx src/index.ts --url http://localhost:7550/swagger/ApiGateway/swagger.json --output autogen --target next --dateTimeLibrary date-fns",
11
- "debug-flutter": "npx tsx src/index.ts --url http://localhost:7550/swagger/ApiGateway/swagger.json --output autogen --target flutter --package coqudo_app",
12
+ "debug-flutter": "npx tsx src/index.ts --url http://localhost:7550/swagger/v1/swagger.json --output autogen --target flutter --package app",
13
+ "debug-flutter-unified": "npx tsx src/index.ts --url http://localhost:7550/swagger/v1/swagger.json --output autogen --target flutter --package app --api-client-name ApiClient",
12
14
  "copy-templates": "copyfiles -u 3 \"src/generators-writers/dart/templates/**/*\" dist/generators-writers/dart",
13
15
  "prepublishOnly": "npm run build",
14
16
  "deploy": "npm publish"
package/src/generator.ts CHANGED
@@ -168,10 +168,10 @@ export class Generator {
168
168
  this._barApis.update(this._apis.length, { message: `Api generating...` });
169
169
 
170
170
  if (this._commandLineArgs.target == TargetGeneration.Angular) {
171
- const apiWriter = new ApiAngularWriter(this._commandLineArgs.outputDirectory);
171
+ const apiWriter = new ApiAngularWriter(this._commandLineArgs);
172
172
  apiWriter.write(this._apis);
173
173
  } else if (this._commandLineArgs.target == TargetGeneration.Next) {
174
- const apiWriter = new ApiNextJsWriter(this._commandLineArgs.outputDirectory);
174
+ const apiWriter = new ApiNextJsWriter(this._commandLineArgs);
175
175
  apiWriter.write(this._apis);
176
176
  } else if (this._commandLineArgs.target == TargetGeneration.Flutter) {
177
177
  const apiWriter = new ApiDartWriter(this._commandLineArgs);
@@ -4,12 +4,13 @@ import { ApiDto } from '../../models/api-dto.js';
4
4
  import { Utils } from '../utils.js';
5
5
  import { ParameterDto } from '../../models/parameter-dto.js';
6
6
  import { Normalizator } from './normalizator.js';
7
+ import { CommandLineArgs } from '@src/index.js';
7
8
 
8
9
  export class ApiAngularWriter {
9
- private _outputDirectory: string;
10
+ private _commandLineArgs: CommandLineArgs;
10
11
 
11
- constructor(_outputDirectory: string) {
12
- this._outputDirectory = _outputDirectory;
12
+ constructor(commandLineArgs: CommandLineArgs) {
13
+ this._commandLineArgs = commandLineArgs;
13
14
  }
14
15
 
15
16
  write(apis: ApiDto[]) {
@@ -149,10 +150,39 @@ export class ApiAngularWriter {
149
150
  }
150
151
 
151
152
  private _writeFile(apis: string) {
152
- fs.writeFileSync(this._outputDirectory + "/api.autogenerated.ts",
153
- `${API_PRE}
153
+ const className = this._commandLineArgs.apiClientName || 'ApiAutogeneratedService';
154
+ const apiPre = this._getApiPre(className);
155
+
156
+ fs.writeFileSync(this._commandLineArgs.outputDirectory + "/api.autogenerated.ts",
157
+ `${apiPre}
154
158
  ${apis}
155
159
  ${API_POST}`,
156
160
  { flag: 'w' });
157
161
  }
162
+
163
+ private _getApiPre(className: string): string {
164
+ return `import { HttpClient } from '@angular/common/http';
165
+ import { Observable, catchError, map } from 'rxjs';
166
+ import * as Models from './model.autogenerated';
167
+ import { HttpHeaders } from "@angular/common/http";
168
+
169
+ export const httpOptions = {
170
+ headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
171
+ };
172
+
173
+ export const httpOptionsMultipart = {};
174
+
175
+ export abstract class ${className} {
176
+ constructor(
177
+ public _http: HttpClient,
178
+ public _baseUrl: string,
179
+ ) { }
180
+
181
+ protected abstract _momentToString(moment: moment.Moment): string;
182
+ protected abstract _handleRequest<T>(request: T): T;
183
+ protected abstract _handleMultipart<T>(request: T): FormData;
184
+ protected abstract _handleResponse<T>(response: T): T;
185
+ protected abstract _handleError(error: any, obs: any): Observable<never>;
186
+ `;
187
+ }
158
188
  }
@@ -58,6 +58,49 @@ export class ApiDartWriter {
58
58
  importDirectory = importDirectory.slice('lib/'.length);
59
59
  }
60
60
 
61
+ // Se è specificato apiClientName, genera una singola classe unificata
62
+ if (this._commandLineArgs.apiClientName) {
63
+ this._writeUnifiedApi(apis, models, template, importDirectory);
64
+ } else {
65
+ // Altrimenti suddividi per tag come prima
66
+ this._writeApisByTag(apis, models, template, importDirectory);
67
+ }
68
+ }
69
+
70
+ private _writeUnifiedApi(apis: ApiDto[], models: ModelDto[], template: string, importDirectory: string) {
71
+ const apiClassName = this._commandLineArgs.apiClientName!;
72
+ const filename = Utils.toDartFileName(apiClassName);
73
+
74
+ console.log(`Api: Unified API Client - ${apiClassName}`);
75
+
76
+ var apiDefinition = <ApiDefinitionDart>{
77
+ package: this._commandLineArgs.package,
78
+ outputDirectory: this._commandLineArgs.outputDirectory,
79
+ filename: filename,
80
+ apiClassName: apiClassName,
81
+ }
82
+
83
+ var endpoints = <EndpointDefinitionDart[]>[];
84
+ var imports = <ImportDefinitionDart[]>[];
85
+
86
+ // Processa tutte le API senza raggruppamento
87
+ for (const api of apis) {
88
+ const endpointData = this._createEndpoint(api, models, imports, importDirectory);
89
+ endpoints.push(endpointData.endpoint);
90
+ }
91
+
92
+ apiDefinition.endpoints = endpoints;
93
+ apiDefinition.imports = imports.map(i => i.import);
94
+
95
+ let destinationPath = `${this._commandLineArgs.outputDirectory}/api`;
96
+
97
+ Utils.ensureDirectorySync(`${destinationPath}`);
98
+
99
+ var result = Mustache.render(template, apiDefinition);
100
+ writeFileSync(`${destinationPath}/${apiDefinition.filename}.dart`, result, 'utf-8');
101
+ }
102
+
103
+ private _writeApisByTag(apis: ApiDto[], models: ModelDto[], template: string, importDirectory: string) {
61
104
  const grouped = this._groupByTag(apis);
62
105
 
63
106
  for (const [tag, apis] of Object.entries(grouped)) {
@@ -82,76 +125,8 @@ export class ApiDartWriter {
82
125
  var imports = <ImportDefinitionDart[]>[];
83
126
 
84
127
  for (const api of apis) {
85
- var requestType: string | undefined = undefined;
86
- requestType = api.haveRequest ? api.parameters[0]?.typeName || 'void' : undefined;
87
- requestType = requestType?.split('.').pop()!
88
- requestType = Utils.toDartClassName(requestType);
89
-
90
-
91
-
92
- var responseType: string = 'void';
93
- responseType = api.returnType ? api.returnType.typeName : 'void';
94
- responseType = responseType?.split('.').pop()!;
95
- if (api.returnType?.isNativeType) {
96
- responseType = Normalizator.mapTsTypeToDart(responseType);
97
- } else {
98
- responseType = Utils.toDartClassName(responseType) ?? responseType;
99
- }
100
-
101
- var methodName = Utils.getNormalizedApiPathDart(api.name);
102
- if (methodName.startsWith(Utils.toFirstLetterLowercase(tag))) {
103
- methodName = methodName.slice(tag.length);
104
- methodName = Utils.toFirstLetterLowercase(methodName);
105
- }
106
-
107
- const endpoint = <EndpointDefinitionDart>{
108
- methodName: methodName,
109
- httpMethod: api.method.toLowerCase() as HttpMethodDart,
110
- path: api.url,
111
- responseType: responseType,
112
- isResponseNativeType: api.returnType ? api.returnType.isNativeType : true,
113
- haveRequest: api.haveRequest,
114
- requestType: requestType,
115
- };
116
-
117
- if (api.parameters && api.parameters.length > 0 && !api.haveRequest) {
118
- endpoint.queryParams = api.parameters
119
- .map(p => ({
120
- name: p.name,
121
- type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName)! : 'String',
122
- nullable: p.nullable
123
- }));
124
- }
125
-
126
- if (api.haveRequest && api.parameters[0]?.typeName) {
127
- if (imports.findIndex(x => x.type.typeName == api.parameters[0]?.typeName) == -1) {
128
- models.forEach(model => {
129
- if (model.typeName === api.parameters[0]?.typeName) {
130
- const normalizedInfo = Normalizator.getNormalizedInfo(model);
131
- imports.push({
132
- type: api.parameters[0],
133
- import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
134
- });
135
- }
136
- });
137
- }
138
- }
139
-
140
- if (api.returnType && !api.returnType.isNativeType) {
141
- if (imports.findIndex(x => x.type.typeName == api.returnType!.typeName) == -1) {
142
- models.forEach(model => {
143
- if (model.typeName === api.returnType!.typeName) {
144
- const normalizedInfo = Normalizator.getNormalizedInfo(model);
145
- imports.push({
146
- type: api.returnType!,
147
- import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
148
- });
149
- }
150
- });
151
- }
152
- }
153
-
154
- endpoints.push(endpoint);
128
+ const endpointData = this._createEndpoint(api, models, imports, importDirectory, tag);
129
+ endpoints.push(endpointData.endpoint);
155
130
  }
156
131
 
157
132
  apiDefinition.endpoints = endpoints;
@@ -166,6 +141,82 @@ export class ApiDartWriter {
166
141
  }
167
142
  }
168
143
 
144
+ private _createEndpoint(api: ApiDto, models: ModelDto[], imports: ImportDefinitionDart[], importDirectory: string, tag?: string): { endpoint: EndpointDefinitionDart } {
145
+ var requestType: string | undefined = undefined;
146
+ requestType = api.haveRequest ? api.parameters[0]?.typeName || 'void' : undefined;
147
+ requestType = requestType?.split('.').pop()!
148
+ requestType = Utils.toDartClassName(requestType);
149
+
150
+ var responseType: string = 'void';
151
+ responseType = api.returnType ? api.returnType.typeName : 'void';
152
+ responseType = responseType?.split('.').pop()!;
153
+ if (api.returnType?.isNativeType) {
154
+ responseType = Normalizator.mapTsTypeToDart(responseType);
155
+ } else {
156
+ responseType = Utils.toDartClassName(responseType) ?? responseType;
157
+ }
158
+
159
+ var methodName = Utils.getNormalizedApiPathDart(api.name);
160
+ if (tag && methodName.startsWith(Utils.toFirstLetterLowercase(tag))) {
161
+ methodName = methodName.slice(tag.length);
162
+ methodName = Utils.toFirstLetterLowercase(methodName);
163
+ }
164
+
165
+ // console.debug(`\tAPI - ${apiName} - ${apiMethod}`);
166
+ if (methodName.toLowerCase().indexOf("productsave") >= 0) {
167
+ debugger
168
+ }
169
+
170
+ const endpoint = <EndpointDefinitionDart>{
171
+ methodName: methodName,
172
+ httpMethod: api.method.toLowerCase() as HttpMethodDart,
173
+ path: api.url,
174
+ responseType: responseType,
175
+ isResponseNativeType: api.returnType ? api.returnType.isNativeType : true,
176
+ haveRequest: api.haveRequest,
177
+ requestType: requestType,
178
+ };
179
+
180
+ if (api.parameters && api.parameters.length > 0 && !api.haveRequest) {
181
+ endpoint.queryParams = api.parameters
182
+ .map(p => ({
183
+ name: p.name,
184
+ type: p.typeName ? Normalizator.mapTsTypeToDart(p.typeName)! : 'String',
185
+ nullable: p.nullable
186
+ }));
187
+ }
188
+
189
+ if (api.haveRequest && api.parameters[0]?.typeName) {
190
+ if (imports.findIndex(x => x.type.typeName == api.parameters[0]?.typeName) == -1) {
191
+ models.forEach(model => {
192
+ if (model.typeName === api.parameters[0]?.typeName) {
193
+ const normalizedInfo = Normalizator.getNormalizedInfo(model);
194
+ imports.push({
195
+ type: api.parameters[0],
196
+ import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
197
+ });
198
+ }
199
+ });
200
+ }
201
+ }
202
+
203
+ if (api.returnType && !api.returnType.isNativeType) {
204
+ if (imports.findIndex(x => x.type.typeName == api.returnType!.typeName) == -1) {
205
+ models.forEach(model => {
206
+ if (model.typeName === api.returnType!.typeName) {
207
+ const normalizedInfo = Normalizator.getNormalizedInfo(model);
208
+ imports.push({
209
+ type: api.returnType!,
210
+ import: `import 'package:${this._commandLineArgs.package}/${importDirectory}/${normalizedInfo.subPath}/${normalizedInfo.filename}.dart';`
211
+ });
212
+ }
213
+ });
214
+ }
215
+ }
216
+
217
+ return { endpoint };
218
+ }
219
+
169
220
  private _toPascalCase(input: string): string {
170
221
  return input
171
222
  .replace(/[_\- ]+/g, ' ') // sostituisce underscore, dash e spazi con uno spazio singolo
@@ -3,12 +3,13 @@ import { API_PRE } from './constants.js';
3
3
  import { ApiDto } from '../../models/api-dto.js';
4
4
  import { Utils } from '../utils.js';
5
5
  import { ParameterDto } from '../../models/parameter-dto.js';
6
+ import { CommandLineArgs } from '../../index.js';
6
7
 
7
8
  export class ApiNextJsWriter {
8
- private _outputDirectory: string;
9
+ private _commandLineArgs: CommandLineArgs;
9
10
 
10
- constructor(_outputDirectory: string) {
11
- this._outputDirectory = _outputDirectory;
11
+ constructor(commandLineArgs: CommandLineArgs) {
12
+ this._commandLineArgs = commandLineArgs;
12
13
  }
13
14
 
14
15
  write(apis: ApiDto[]) {
@@ -149,7 +150,7 @@ ${preparation}const response = await axios.${method}<${returnTypeString}>(\`${ap
149
150
 
150
151
 
151
152
  private _writeFile(apis: string) {
152
- fs.writeFileSync(this._outputDirectory + "/api.autogenerated.ts",
153
+ fs.writeFileSync(this._commandLineArgs.outputDirectory + "/api.autogenerated.ts",
153
154
  `${API_PRE}
154
155
  ${apis}`,
155
156
  { flag: 'w' });
package/src/index.ts CHANGED
@@ -22,11 +22,12 @@ export interface CommandLineArgs {
22
22
  target: TargetGeneration;
23
23
  dateTimeLibrary?: DateTimeLibrary;
24
24
  package?: string;
25
+ apiClientName?: string;
25
26
  }
26
27
 
27
28
  const argv = yargs(hideBin(process.argv))
28
29
  .scriptName("swagger-gen")
29
- .usage('Usage: $0 --url <swaggerJsonUrl> --output <outputDirectory> --target <target> [--dateTimeLibrary <dateTimeLibrary>] [--package <name>]')
30
+ .usage('Usage: $0 --url <swaggerJsonUrl> --output <outputDirectory> --target <target> [--dateTimeLibrary <dateTimeLibrary>] [--package <name>] [--api-client-name <className>]')
30
31
  .option('url', {
31
32
  alias: 'u',
32
33
  type: 'string',
@@ -58,6 +59,11 @@ const argv = yargs(hideBin(process.argv))
58
59
  type: 'string',
59
60
  describe: 'For Dart: package name for Dart project',
60
61
  })
62
+ .option('api-client-name', {
63
+ alias: 'a',
64
+ type: 'string',
65
+ describe: 'Name for the unified API client class (all services in one class)',
66
+ })
61
67
  .help()
62
68
  .parseSync();
63
69
 
@@ -67,6 +73,7 @@ const commandLineArgs: CommandLineArgs = {
67
73
  target: parseTargetGeneration(argv.target),
68
74
  dateTimeLibrary: parseDateTimeLibrary(argv.dateTimeLibrary),
69
75
  package: argv.package ?? '',
76
+ apiClientName: argv['api-client-name'],
70
77
  };
71
78
 
72
79
  const swaggerDownloader = new SwaggerDownloader();
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};