@hazeljs/swagger 0.3.1 → 0.4.0
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/swagger.service.d.ts +12 -0
- package/dist/swagger.service.d.ts.map +1 -1
- package/dist/swagger.service.js +215 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ export { SwaggerModule } from './swagger.module';
|
|
|
5
5
|
export { SwaggerService, type SwaggerSpec } from './swagger.service';
|
|
6
6
|
export { Swagger, ApiOperation, getSwaggerMetadata, getOperationMetadata, } from './swagger.decorator';
|
|
7
7
|
export type { SwaggerOptions, SwaggerOperation, SwaggerSchema } from './swagger.types';
|
|
8
|
+
export type { AutoSwaggerOptions } from './swagger.service';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGvF,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { SwaggerOperation, SwaggerSchema } from './swagger.types';
|
|
2
2
|
import { Type } from '@hazeljs/core';
|
|
3
|
+
export interface AutoSwaggerOptions {
|
|
4
|
+
title?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
version?: string;
|
|
7
|
+
autoGenerateOperations?: boolean;
|
|
8
|
+
}
|
|
3
9
|
export interface SwaggerSpec {
|
|
4
10
|
openapi: string;
|
|
5
11
|
info: {
|
|
@@ -18,6 +24,12 @@ export interface SwaggerSpec {
|
|
|
18
24
|
}
|
|
19
25
|
export declare class SwaggerService {
|
|
20
26
|
private spec;
|
|
27
|
+
generateAutoSpec(moduleType: Type<unknown>, options?: AutoSwaggerOptions): SwaggerSpec;
|
|
28
|
+
private processControllerAuto;
|
|
29
|
+
private processRouteAuto;
|
|
30
|
+
private generateAutoOperation;
|
|
31
|
+
private extractPathParameters;
|
|
32
|
+
private addDefaultSchemas;
|
|
21
33
|
generateSpec(controllers: Type<unknown>[]): SwaggerSpec;
|
|
22
34
|
private normalizePath;
|
|
23
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swagger.service.d.ts","sourceRoot":"","sources":["../src/swagger.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGlE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"swagger.service.d.ts","sourceRoot":"","sources":["../src/swagger.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGlE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrC,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACxD,UAAU,EAAE;QACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;KACxC,CAAC;IACF,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAQD,qBACa,cAAc;IACzB,OAAO,CAAC,IAAI,CAOV;IAGF,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW;IAqCtF,OAAO,CAAC,qBAAqB;IA8B7B,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,qBAAqB;IAkG7B,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,iBAAiB;IAyCzB,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,WAAW;IAsGvD,OAAO,CAAC,aAAa;CAStB"}
|
package/dist/swagger.service.js
CHANGED
|
@@ -13,6 +13,7 @@ exports.SwaggerService = void 0;
|
|
|
13
13
|
const core_1 = require("@hazeljs/core");
|
|
14
14
|
const swagger_decorator_1 = require("./swagger.decorator");
|
|
15
15
|
const core_2 = __importDefault(require("@hazeljs/core"));
|
|
16
|
+
const core_3 = require("@hazeljs/core");
|
|
16
17
|
let SwaggerService = class SwaggerService {
|
|
17
18
|
constructor() {
|
|
18
19
|
this.spec = {
|
|
@@ -24,6 +25,220 @@ let SwaggerService = class SwaggerService {
|
|
|
24
25
|
},
|
|
25
26
|
};
|
|
26
27
|
}
|
|
28
|
+
// Auto-generate spec from module without explicit Swagger decorators
|
|
29
|
+
generateAutoSpec(moduleType, options) {
|
|
30
|
+
try {
|
|
31
|
+
core_2.default.debug('Auto-generating Swagger spec from module:', moduleType.name);
|
|
32
|
+
// Reset spec
|
|
33
|
+
this.spec = {
|
|
34
|
+
openapi: '3.0.0',
|
|
35
|
+
info: {
|
|
36
|
+
title: options?.title || 'HazelJS API',
|
|
37
|
+
description: options?.description || 'Auto-generated API documentation',
|
|
38
|
+
version: options?.version || '1.0.0',
|
|
39
|
+
},
|
|
40
|
+
paths: {},
|
|
41
|
+
components: {
|
|
42
|
+
schemas: {},
|
|
43
|
+
},
|
|
44
|
+
tags: [],
|
|
45
|
+
};
|
|
46
|
+
const controllers = (0, core_3.collectControllersFromModule)(moduleType);
|
|
47
|
+
// Process each controller
|
|
48
|
+
controllers.forEach((controller) => {
|
|
49
|
+
this.processControllerAuto(controller, options?.autoGenerateOperations !== false);
|
|
50
|
+
});
|
|
51
|
+
// Add default error schemas
|
|
52
|
+
this.addDefaultSchemas();
|
|
53
|
+
core_2.default.debug('Auto-generated Swagger specification completed');
|
|
54
|
+
return this.spec;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
core_2.default.error('Failed to auto-generate Swagger specification:', error);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
processControllerAuto(controller, autoGenerateOps) {
|
|
62
|
+
if (!controller)
|
|
63
|
+
return;
|
|
64
|
+
// Get controller path from metadata
|
|
65
|
+
const controllerMetadata = Reflect.getMetadata('hazel:controller', controller) || {};
|
|
66
|
+
const basePath = controllerMetadata.path || '';
|
|
67
|
+
// Get API tags from metadata
|
|
68
|
+
const apiTags = Reflect.getMetadata('hazel:api:tags', controller) || [];
|
|
69
|
+
// Add controller as a tag if not already present
|
|
70
|
+
if (!this.spec.tags)
|
|
71
|
+
this.spec.tags = [];
|
|
72
|
+
const controllerTag = {
|
|
73
|
+
name: apiTags.length > 0 ? apiTags[0] : controller.name,
|
|
74
|
+
description: `${controller.name} endpoints`,
|
|
75
|
+
};
|
|
76
|
+
if (!this.spec.tags.find((tag) => tag.name === controllerTag.name)) {
|
|
77
|
+
this.spec.tags.push(controllerTag);
|
|
78
|
+
}
|
|
79
|
+
// Get route metadata
|
|
80
|
+
const routes = Reflect.getMetadata('hazel:routes', controller) || [];
|
|
81
|
+
// Process each route
|
|
82
|
+
routes.forEach((route) => {
|
|
83
|
+
this.processRouteAuto(controller, route, basePath, controllerTag.name, autoGenerateOps);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
processRouteAuto(controller, route, basePath, tag, autoGenerateOps) {
|
|
87
|
+
const { path, method, propertyKey } = route;
|
|
88
|
+
const fullPath = this.normalizePath(`${basePath}${path}`);
|
|
89
|
+
// Check for existing operation metadata
|
|
90
|
+
let operation = (0, swagger_decorator_1.getOperationMetadata)(controller.prototype, propertyKey);
|
|
91
|
+
// Auto-generate operation if not found and auto-generation is enabled
|
|
92
|
+
if (!operation && autoGenerateOps) {
|
|
93
|
+
operation = this.generateAutoOperation(method, propertyKey, tag, route);
|
|
94
|
+
}
|
|
95
|
+
if (!operation)
|
|
96
|
+
return;
|
|
97
|
+
// Add operation to paths
|
|
98
|
+
const pathItem = this.spec.paths[fullPath] || {};
|
|
99
|
+
pathItem[method.toLowerCase()] = {
|
|
100
|
+
...operation,
|
|
101
|
+
tags: operation.tags || [tag],
|
|
102
|
+
};
|
|
103
|
+
this.spec.paths[fullPath] = pathItem;
|
|
104
|
+
}
|
|
105
|
+
generateAutoOperation(method, propertyKey, tag, route) {
|
|
106
|
+
const methodName = String(propertyKey);
|
|
107
|
+
const isGetMethod = method.toLowerCase() === 'get';
|
|
108
|
+
const isPostMethod = method.toLowerCase() === 'post';
|
|
109
|
+
const isPutMethod = method.toLowerCase() === 'put';
|
|
110
|
+
const isDeleteMethod = method.toLowerCase() === 'delete';
|
|
111
|
+
// Generate summary based on method name
|
|
112
|
+
let summary = '';
|
|
113
|
+
if (methodName.includes('create') || isPostMethod) {
|
|
114
|
+
summary = `Create new resource`;
|
|
115
|
+
}
|
|
116
|
+
else if (methodName.includes('update') || isPutMethod) {
|
|
117
|
+
summary = `Update resource`;
|
|
118
|
+
}
|
|
119
|
+
else if (methodName.includes('delete') || isDeleteMethod) {
|
|
120
|
+
summary = `Delete resource`;
|
|
121
|
+
}
|
|
122
|
+
else if (methodName.includes('find') || isGetMethod) {
|
|
123
|
+
summary = `Get resource(s)`;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
summary = `${method.toUpperCase()} ${methodName}`;
|
|
127
|
+
}
|
|
128
|
+
const operation = {
|
|
129
|
+
summary,
|
|
130
|
+
description: `Auto-generated ${method.toUpperCase()} operation`,
|
|
131
|
+
tags: [tag],
|
|
132
|
+
responses: {
|
|
133
|
+
'200': {
|
|
134
|
+
description: 'Successful response',
|
|
135
|
+
content: {
|
|
136
|
+
'application/json': {
|
|
137
|
+
schema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
data: {
|
|
141
|
+
type: 'object',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
// Add parameters for path variables - need to extract from the route path
|
|
151
|
+
const pathParams = this.extractPathParameters(route.path);
|
|
152
|
+
if (pathParams.length > 0) {
|
|
153
|
+
operation.parameters = pathParams.map((param) => ({
|
|
154
|
+
name: param,
|
|
155
|
+
in: 'path',
|
|
156
|
+
required: true,
|
|
157
|
+
schema: { type: 'string' },
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
// Add request body for POST/PUT/PATCH
|
|
161
|
+
if (isPostMethod || isPutMethod) {
|
|
162
|
+
operation.requestBody = {
|
|
163
|
+
required: true,
|
|
164
|
+
content: {
|
|
165
|
+
'application/json': {
|
|
166
|
+
schema: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// Add common error responses
|
|
174
|
+
if (operation.responses) {
|
|
175
|
+
operation.responses['400'] = {
|
|
176
|
+
description: 'Bad request',
|
|
177
|
+
content: {
|
|
178
|
+
'application/json': {
|
|
179
|
+
schema: { type: 'object' },
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
operation.responses['500'] = {
|
|
184
|
+
description: 'Internal server error',
|
|
185
|
+
content: {
|
|
186
|
+
'application/json': {
|
|
187
|
+
schema: { type: 'object' },
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return operation;
|
|
193
|
+
}
|
|
194
|
+
extractPathParameters(path) {
|
|
195
|
+
const params = [];
|
|
196
|
+
const paramRegex = /:([^/]+)/g;
|
|
197
|
+
let match;
|
|
198
|
+
while ((match = paramRegex.exec(path)) !== null) {
|
|
199
|
+
params.push(match[1]);
|
|
200
|
+
}
|
|
201
|
+
return params;
|
|
202
|
+
}
|
|
203
|
+
addDefaultSchemas() {
|
|
204
|
+
// Add common error schemas
|
|
205
|
+
this.spec.components.schemas.Error = {
|
|
206
|
+
type: 'object',
|
|
207
|
+
properties: {
|
|
208
|
+
error: {
|
|
209
|
+
type: 'object',
|
|
210
|
+
properties: {
|
|
211
|
+
message: { type: 'string' },
|
|
212
|
+
statusCode: { type: 'number' },
|
|
213
|
+
timestamp: { type: 'string', format: 'date-time' },
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
this.spec.components.schemas.ValidationError = {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
error: {
|
|
222
|
+
type: 'object',
|
|
223
|
+
properties: {
|
|
224
|
+
message: { type: 'string' },
|
|
225
|
+
statusCode: { type: 'number' },
|
|
226
|
+
timestamp: { type: 'string', format: 'date-time' },
|
|
227
|
+
errors: {
|
|
228
|
+
type: 'array',
|
|
229
|
+
items: {
|
|
230
|
+
type: 'object',
|
|
231
|
+
properties: {
|
|
232
|
+
field: { type: 'string' },
|
|
233
|
+
message: { type: 'string' },
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
27
242
|
generateSpec(controllers) {
|
|
28
243
|
try {
|
|
29
244
|
if (!Array.isArray(controllers)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hazeljs/swagger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Swagger/OpenAPI documentation module for HazelJS framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@hazeljs/core": ">=0.2.0-beta.0"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "3cde5425a60eed497a777795724c93a926c69155"
|
|
55
55
|
}
|