@hazeljs/swagger 0.8.6 → 0.8.7
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 +78 -516
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/openapi-document.test.d.ts +2 -0
- package/dist/openapi-document.test.d.ts.map +1 -0
- package/dist/openapi-document.test.js +42 -0
- package/dist/swagger-config.d.ts +5 -0
- package/dist/swagger-config.d.ts.map +1 -0
- package/dist/swagger-config.js +15 -0
- package/dist/swagger.controller.d.ts +2 -1
- package/dist/swagger.controller.d.ts.map +1 -1
- package/dist/swagger.controller.js +44 -85
- package/dist/swagger.controller.test.js +41 -156
- package/dist/swagger.module.d.ts +7 -0
- package/dist/swagger.module.d.ts.map +1 -1
- package/dist/swagger.module.js +16 -0
- package/dist/swagger.service.d.ts +14 -27
- package/dist/swagger.service.d.ts.map +1 -1
- package/dist/swagger.service.js +143 -162
- package/dist/swagger.service.test.js +164 -22
- package/dist/swagger.types.d.ts +52 -1
- package/dist/swagger.types.d.ts.map +1 -1
- package/package.json +4 -8
package/dist/index.js
CHANGED
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getOperationMetadata = exports.getSwaggerMetadata = exports.ApiOperation = exports.Swagger = exports.SwaggerService = exports.SwaggerModule = void 0;
|
|
7
|
+
exports.createOpenApiDocument = createOpenApiDocument;
|
|
8
|
+
const swagger_service_1 = require("./swagger.service");
|
|
7
9
|
var swagger_module_1 = require("./swagger.module");
|
|
8
10
|
Object.defineProperty(exports, "SwaggerModule", { enumerable: true, get: function () { return swagger_module_1.SwaggerModule; } });
|
|
9
|
-
var
|
|
10
|
-
Object.defineProperty(exports, "SwaggerService", { enumerable: true, get: function () { return
|
|
11
|
+
var swagger_service_2 = require("./swagger.service");
|
|
12
|
+
Object.defineProperty(exports, "SwaggerService", { enumerable: true, get: function () { return swagger_service_2.SwaggerService; } });
|
|
11
13
|
var swagger_decorator_1 = require("./swagger.decorator");
|
|
12
14
|
Object.defineProperty(exports, "Swagger", { enumerable: true, get: function () { return swagger_decorator_1.Swagger; } });
|
|
13
15
|
Object.defineProperty(exports, "ApiOperation", { enumerable: true, get: function () { return swagger_decorator_1.ApiOperation; } });
|
|
14
16
|
Object.defineProperty(exports, "getSwaggerMetadata", { enumerable: true, get: function () { return swagger_decorator_1.getSwaggerMetadata; } });
|
|
15
17
|
Object.defineProperty(exports, "getOperationMetadata", { enumerable: true, get: function () { return swagger_decorator_1.getOperationMetadata; } });
|
|
18
|
+
/** Build an OpenAPI document without serving HTTP (e.g. CI export). */
|
|
19
|
+
function createOpenApiDocument(rootModule, options) {
|
|
20
|
+
return new swagger_service_1.SwaggerService().generateAutoSpec(rootModule, options);
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-document.test.d.ts","sourceRoot":"","sources":["../src/openapi-document.test.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
require("reflect-metadata");
|
|
13
|
+
const index_1 = require("./index");
|
|
14
|
+
const core_1 = require("@hazeljs/core");
|
|
15
|
+
describe('createOpenApiDocument', () => {
|
|
16
|
+
it('returns a spec from the app module', () => {
|
|
17
|
+
let PingController = class PingController {
|
|
18
|
+
ping() {
|
|
19
|
+
return 'pong';
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
__decorate([
|
|
23
|
+
(0, core_1.Get)(),
|
|
24
|
+
__metadata("design:type", Function),
|
|
25
|
+
__metadata("design:paramtypes", []),
|
|
26
|
+
__metadata("design:returntype", String)
|
|
27
|
+
], PingController.prototype, "ping", null);
|
|
28
|
+
PingController = __decorate([
|
|
29
|
+
(0, core_1.Controller)({ path: '/ping' })
|
|
30
|
+
], PingController);
|
|
31
|
+
Reflect.defineMetadata('hazel:controller', { path: '/ping' }, PingController);
|
|
32
|
+
Reflect.defineMetadata('hazel:routes', [{ propertyKey: 'ping', path: '', method: 'GET' }], PingController);
|
|
33
|
+
let AppModule = class AppModule {
|
|
34
|
+
};
|
|
35
|
+
AppModule = __decorate([
|
|
36
|
+
(0, core_1.HazelModule)({ controllers: [PingController], imports: [] })
|
|
37
|
+
], AppModule);
|
|
38
|
+
const spec = (0, index_1.createOpenApiDocument)(AppModule, { title: 'CI', version: '0.0.1' });
|
|
39
|
+
expect(spec.info.title).toBe('CI');
|
|
40
|
+
expect(spec.paths['/ping']?.get).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SwaggerModuleOptions } from './swagger.types';
|
|
2
|
+
export declare function mergeSwaggerModuleOptions(options: SwaggerModuleOptions): void;
|
|
3
|
+
export declare function replaceSwaggerModuleOptions(options: SwaggerModuleOptions): void;
|
|
4
|
+
export declare function getSwaggerModuleOptions(): SwaggerModuleOptions;
|
|
5
|
+
//# sourceMappingURL=swagger-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swagger-config.d.ts","sourceRoot":"","sources":["../src/swagger-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAI5D,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAE7E;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAE/E;AAED,wBAAgB,uBAAuB,IAAI,oBAAoB,CAE9D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeSwaggerModuleOptions = mergeSwaggerModuleOptions;
|
|
4
|
+
exports.replaceSwaggerModuleOptions = replaceSwaggerModuleOptions;
|
|
5
|
+
exports.getSwaggerModuleOptions = getSwaggerModuleOptions;
|
|
6
|
+
let swaggerModuleOptions = {};
|
|
7
|
+
function mergeSwaggerModuleOptions(options) {
|
|
8
|
+
swaggerModuleOptions = { ...swaggerModuleOptions, ...options };
|
|
9
|
+
}
|
|
10
|
+
function replaceSwaggerModuleOptions(options) {
|
|
11
|
+
swaggerModuleOptions = { ...options };
|
|
12
|
+
}
|
|
13
|
+
function getSwaggerModuleOptions() {
|
|
14
|
+
return { ...swaggerModuleOptions };
|
|
15
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { SwaggerService } from './swagger.service';
|
|
2
2
|
import { RequestContext, Type } from '@hazeljs/core';
|
|
3
|
-
import { SwaggerSpec } from './swagger.
|
|
3
|
+
import type { SwaggerSpec } from './swagger.types';
|
|
4
4
|
export declare class SwaggerController {
|
|
5
5
|
private swaggerService;
|
|
6
6
|
private static rootModule;
|
|
7
7
|
constructor(swaggerService: SwaggerService);
|
|
8
8
|
static setRootModule(module: Type<unknown>): void;
|
|
9
|
+
private static emptySpec;
|
|
9
10
|
getSpec(_context: RequestContext): Promise<SwaggerSpec>;
|
|
10
11
|
getDocs(_context: RequestContext): Promise<string>;
|
|
11
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swagger.controller.d.ts","sourceRoot":"","sources":["../src/swagger.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"swagger.controller.d.ts","sourceRoot":"","sources":["../src/swagger.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,qBAca,iBAAiB;IAGhB,OAAO,CAAC,cAAc;IAFlC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAgB;gBAErB,cAAc,EAAE,cAAc;IAElD,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;IAKjD,OAAO,CAAC,MAAM,CAAC,SAAS;IAuClB,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAkDvD,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;CAqEzD"}
|
|
@@ -16,115 +16,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
16
16
|
exports.SwaggerController = void 0;
|
|
17
17
|
const core_1 = require("@hazeljs/core");
|
|
18
18
|
const swagger_service_1 = require("./swagger.service");
|
|
19
|
-
const core_2 = require("@hazeljs/core");
|
|
20
|
-
const core_3 = __importDefault(require("@hazeljs/core"));
|
|
19
|
+
const core_2 = __importDefault(require("@hazeljs/core"));
|
|
21
20
|
const swagger_decorator_1 = require("./swagger.decorator");
|
|
21
|
+
const swagger_config_1 = require("./swagger-config");
|
|
22
22
|
let SwaggerController = SwaggerController_1 = class SwaggerController {
|
|
23
23
|
constructor(swaggerService) {
|
|
24
24
|
this.swaggerService = swaggerService;
|
|
25
25
|
}
|
|
26
26
|
static setRootModule(module) {
|
|
27
|
-
|
|
27
|
+
core_2.default.debug(`Setting root module for SwaggerController: ${module.name}`);
|
|
28
28
|
SwaggerController_1.rootModule = module;
|
|
29
29
|
}
|
|
30
|
+
static emptySpec(description) {
|
|
31
|
+
return {
|
|
32
|
+
openapi: '3.0.0',
|
|
33
|
+
info: {
|
|
34
|
+
title: 'API Documentation',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
description,
|
|
37
|
+
},
|
|
38
|
+
paths: {},
|
|
39
|
+
components: {
|
|
40
|
+
schemas: {},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
30
44
|
async getSpec(_context) {
|
|
31
45
|
try {
|
|
32
46
|
if (!SwaggerController_1.rootModule) {
|
|
33
|
-
|
|
34
|
-
return
|
|
35
|
-
openapi: '3.0.0',
|
|
36
|
-
info: {
|
|
37
|
-
title: 'API Documentation',
|
|
38
|
-
version: '1.0.0',
|
|
39
|
-
description: 'No root module provided',
|
|
40
|
-
},
|
|
41
|
-
paths: {},
|
|
42
|
-
components: {
|
|
43
|
-
schemas: {},
|
|
44
|
-
},
|
|
45
|
-
};
|
|
47
|
+
core_2.default.warn('No root module provided');
|
|
48
|
+
return SwaggerController_1.emptySpec('No root module provided');
|
|
46
49
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
? {
|
|
53
|
-
controllers: moduleMetadata.controllers?.length || 0,
|
|
54
|
-
imports: moduleMetadata.imports?.length || 0,
|
|
55
|
-
providers: moduleMetadata.providers?.length || 0,
|
|
56
|
-
}
|
|
57
|
-
: null);
|
|
58
|
-
const controllers = new Set();
|
|
59
|
-
// Helper function to recursively collect controllers from modules
|
|
60
|
-
const collectControllers = (moduleRef) => {
|
|
61
|
-
const moduleName = typeof moduleRef === 'function'
|
|
62
|
-
? moduleRef.name
|
|
63
|
-
: moduleRef.module?.name;
|
|
64
|
-
core_3.default.debug(`Collecting controllers from module: ${moduleName}`);
|
|
65
|
-
const metadata = (0, core_2.getModuleMetadata)(moduleRef);
|
|
66
|
-
if (!metadata) {
|
|
67
|
-
core_3.default.warn(`No metadata found for module: ${moduleName}`);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Add controllers from current module
|
|
71
|
-
if (metadata.controllers) {
|
|
72
|
-
const validControllers = metadata.controllers.filter((c) => c && typeof c === 'function');
|
|
73
|
-
core_3.default.debug(`${moduleName} controllers:`, validControllers.map((c) => typeof c === 'function' ? c.name : undefined));
|
|
74
|
-
validControllers.forEach((controller) => controllers.add(controller));
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
core_3.default.debug(`No controllers found in module: ${moduleName}`);
|
|
78
|
-
}
|
|
79
|
-
// Recursively process imported modules (Type or DynamicModule)
|
|
80
|
-
if (metadata.imports) {
|
|
81
|
-
const validModules = metadata.imports.filter((m) => m && (typeof m === 'function' || (typeof m === 'object' && 'module' in m)));
|
|
82
|
-
core_3.default.debug(`${moduleName} imported modules:`, validModules.map((m) => typeof m === 'function' ? m.name : m.module?.name));
|
|
83
|
-
validModules.forEach((moduleRef) => collectControllers(moduleRef));
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
core_3.default.debug(`No imports found in module: ${moduleName}`);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
// Start collecting controllers from the root module
|
|
90
|
-
collectControllers(SwaggerController_1.rootModule);
|
|
91
|
-
const controllerArray = Array.from(controllers);
|
|
92
|
-
core_3.default.debug('Total controllers found:', controllerArray.length);
|
|
93
|
-
core_3.default.debug('Controller names:', controllerArray.map((c) => c.name));
|
|
94
|
-
if (controllerArray.length === 0) {
|
|
95
|
-
core_3.default.warn('No valid controllers found');
|
|
50
|
+
core_2.default.debug('Root module:', SwaggerController_1.rootModule.name);
|
|
51
|
+
const opts = (0, swagger_config_1.getSwaggerModuleOptions)();
|
|
52
|
+
const spec = this.swaggerService.generateAutoSpec(SwaggerController_1.rootModule, opts);
|
|
53
|
+
if (Object.keys(spec.paths).length === 0) {
|
|
54
|
+
core_2.default.warn('No routes found for OpenAPI document');
|
|
96
55
|
return {
|
|
97
|
-
|
|
56
|
+
...spec,
|
|
98
57
|
info: {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
description: 'No controllers found',
|
|
102
|
-
},
|
|
103
|
-
paths: {},
|
|
104
|
-
components: {
|
|
105
|
-
schemas: {},
|
|
58
|
+
...spec.info,
|
|
59
|
+
description: spec.info.description || 'No routes found',
|
|
106
60
|
},
|
|
107
61
|
};
|
|
108
62
|
}
|
|
109
|
-
const spec = this.swaggerService.generateSpec(controllerArray);
|
|
110
63
|
return spec;
|
|
111
64
|
}
|
|
112
65
|
catch (error) {
|
|
113
66
|
if (process.env.NODE_ENV !== 'test') {
|
|
114
|
-
|
|
67
|
+
core_2.default.error('Error generating Swagger spec:', error);
|
|
115
68
|
}
|
|
116
69
|
throw error;
|
|
117
70
|
}
|
|
118
71
|
}
|
|
119
72
|
async getDocs(_context) {
|
|
120
|
-
|
|
121
|
-
|
|
73
|
+
const opts = (0, swagger_config_1.getSwaggerModuleOptions)();
|
|
74
|
+
const cdn = opts.swaggerUiCdnBase?.replace(/\/$/, '') ?? 'https://unpkg.com/swagger-ui-dist@5.11.0';
|
|
75
|
+
let prefix = opts.globalPrefix?.trim() ?? '';
|
|
76
|
+
if (prefix && !prefix.startsWith('/')) {
|
|
77
|
+
prefix = `/${prefix}`;
|
|
78
|
+
}
|
|
79
|
+
prefix = prefix.replace(/\/$/, '');
|
|
80
|
+
const specPath = prefix ? `${prefix}/swagger/spec` : '/swagger/spec';
|
|
81
|
+
return `<!DOCTYPE html>
|
|
122
82
|
<html>
|
|
123
83
|
<head>
|
|
124
84
|
<title>API Documentation</title>
|
|
125
85
|
<meta charset="utf-8"/>
|
|
126
86
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
127
|
-
<link rel="stylesheet" type="text/css" href="
|
|
87
|
+
<link rel="stylesheet" type="text/css" href="${cdn}/swagger-ui.css" />
|
|
128
88
|
<style>
|
|
129
89
|
body {
|
|
130
90
|
margin: 0;
|
|
@@ -146,19 +106,18 @@ let SwaggerController = SwaggerController_1 = class SwaggerController {
|
|
|
146
106
|
<div class="loading">Loading API Documentation...</div>
|
|
147
107
|
</div>
|
|
148
108
|
|
|
149
|
-
<script src="
|
|
150
|
-
<script src="
|
|
109
|
+
<script src="${cdn}/swagger-ui-bundle.js"></script>
|
|
110
|
+
<script src="${cdn}/swagger-ui-standalone-preset.js"></script>
|
|
151
111
|
<script>
|
|
152
|
-
// Basic error handling
|
|
153
112
|
window.onerror = function(msg, url, line) {
|
|
154
|
-
document.getElementById('swagger-ui').innerHTML =
|
|
113
|
+
document.getElementById('swagger-ui').innerHTML =
|
|
155
114
|
'<div style="color: red; padding: 20px;">Error: ' + msg + '<br>at line ' + line + '</div>';
|
|
156
115
|
return false;
|
|
157
116
|
};
|
|
158
117
|
|
|
159
|
-
|
|
118
|
+
const specPath = ${JSON.stringify(specPath)};
|
|
160
119
|
const ui = SwaggerUIBundle({
|
|
161
|
-
url: window.location.origin +
|
|
120
|
+
url: window.location.origin + specPath,
|
|
162
121
|
dom_id: '#swagger-ui',
|
|
163
122
|
deepLinking: true,
|
|
164
123
|
presets: [
|
|
@@ -175,7 +134,7 @@ let SwaggerController = SwaggerController_1 = class SwaggerController {
|
|
|
175
134
|
</script>
|
|
176
135
|
</body>
|
|
177
136
|
</html>
|
|
178
|
-
|
|
137
|
+
`;
|
|
179
138
|
}
|
|
180
139
|
};
|
|
181
140
|
exports.SwaggerController = SwaggerController;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const swagger_controller_1 = require("./swagger.controller");
|
|
4
|
-
const
|
|
5
|
-
// Mock the logger and getModuleMetadata
|
|
4
|
+
const swagger_module_1 = require("./swagger.module");
|
|
6
5
|
jest.mock('@hazeljs/core', () => {
|
|
7
6
|
const actual = jest.requireActual('@hazeljs/core');
|
|
8
7
|
return {
|
|
9
8
|
...actual,
|
|
10
9
|
__esModule: true,
|
|
11
|
-
getModuleMetadata: jest.fn(),
|
|
12
10
|
default: {
|
|
13
11
|
error: jest.fn(),
|
|
14
12
|
debug: jest.fn(),
|
|
@@ -23,89 +21,71 @@ describe('SwaggerController', () => {
|
|
|
23
21
|
let controller;
|
|
24
22
|
let swaggerService;
|
|
25
23
|
beforeEach(() => {
|
|
24
|
+
swagger_module_1.SwaggerModule.configure({}, true);
|
|
26
25
|
swaggerService = {
|
|
27
|
-
|
|
26
|
+
generateAutoSpec: jest.fn(),
|
|
28
27
|
};
|
|
29
28
|
controller = new swagger_controller_1.SwaggerController(swaggerService);
|
|
30
|
-
// Reset all mocks before each test
|
|
31
29
|
jest.clearAllMocks();
|
|
30
|
+
swagger_controller_1.SwaggerController.rootModule = undefined;
|
|
32
31
|
});
|
|
33
32
|
it('should be defined', () => {
|
|
34
33
|
expect(controller).toBeDefined();
|
|
35
34
|
});
|
|
36
|
-
it('should return swagger spec', async () => {
|
|
35
|
+
it('should return swagger spec from generateAutoSpec', async () => {
|
|
36
|
+
class AppModule {
|
|
37
|
+
}
|
|
38
|
+
swagger_controller_1.SwaggerController.setRootModule(AppModule);
|
|
37
39
|
const mockSpec = {
|
|
38
40
|
openapi: '3.0.0',
|
|
39
41
|
info: {
|
|
40
42
|
title: 'API Documentation',
|
|
41
43
|
version: '1.0.0',
|
|
42
|
-
description: '
|
|
44
|
+
description: 'Test',
|
|
43
45
|
},
|
|
44
|
-
paths: {},
|
|
46
|
+
paths: { '/x': { get: { summary: 'x' } } },
|
|
45
47
|
components: {
|
|
46
48
|
schemas: {},
|
|
47
49
|
},
|
|
48
50
|
};
|
|
49
|
-
swaggerService.
|
|
51
|
+
swaggerService.generateAutoSpec.mockReturnValue(mockSpec);
|
|
50
52
|
const spec = await controller.getSpec({});
|
|
51
53
|
expect(spec).toEqual(mockSpec);
|
|
54
|
+
expect(swaggerService.generateAutoSpec).toHaveBeenCalledWith(AppModule, expect.any(Object));
|
|
52
55
|
});
|
|
53
56
|
describe('getSpec', () => {
|
|
54
|
-
it('should
|
|
57
|
+
it('should call generateAutoSpec with root module', async () => {
|
|
55
58
|
const mockSpec = {
|
|
56
59
|
openapi: '3.0.0',
|
|
57
60
|
info: {
|
|
58
|
-
title: 'API
|
|
61
|
+
title: 'API',
|
|
59
62
|
version: '1.0.0',
|
|
60
|
-
description: 'No root module provided',
|
|
61
|
-
},
|
|
62
|
-
paths: {},
|
|
63
|
-
components: {
|
|
64
|
-
schemas: {},
|
|
65
63
|
},
|
|
64
|
+
paths: { '/a': { get: { summary: 'a' } } },
|
|
65
|
+
components: { schemas: { Error: { type: 'object' } } },
|
|
66
66
|
};
|
|
67
|
-
// Set up the root module
|
|
68
67
|
class TestModule {
|
|
69
68
|
}
|
|
70
69
|
swagger_controller_1.SwaggerController.setRootModule(TestModule);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
controllers: [class TestController {
|
|
74
|
-
}],
|
|
75
|
-
imports: [],
|
|
76
|
-
});
|
|
77
|
-
swaggerService.generateSpec.mockReturnValue(mockSpec);
|
|
78
|
-
const context = {};
|
|
79
|
-
const result = await controller.getSpec(context);
|
|
70
|
+
swaggerService.generateAutoSpec.mockReturnValue(mockSpec);
|
|
71
|
+
const result = await controller.getSpec({});
|
|
80
72
|
expect(result).toEqual(mockSpec);
|
|
81
|
-
expect(swaggerService.
|
|
73
|
+
expect(swaggerService.generateAutoSpec).toHaveBeenCalledWith(TestModule, expect.any(Object));
|
|
82
74
|
});
|
|
83
75
|
it('should handle errors', async () => {
|
|
84
76
|
const error = new Error('Test error');
|
|
85
|
-
swaggerService.
|
|
77
|
+
swaggerService.generateAutoSpec.mockImplementation(() => {
|
|
86
78
|
throw error;
|
|
87
79
|
});
|
|
88
|
-
// Set up the root module
|
|
89
80
|
class TestModule {
|
|
90
81
|
}
|
|
91
82
|
swagger_controller_1.SwaggerController.setRootModule(TestModule);
|
|
92
|
-
|
|
93
|
-
core_1.getModuleMetadata.mockReturnValue({
|
|
94
|
-
controllers: [class TestController {
|
|
95
|
-
}],
|
|
96
|
-
imports: [],
|
|
97
|
-
});
|
|
98
|
-
const context = {};
|
|
99
|
-
await expect(controller.getSpec(context)).rejects.toThrow('Test error');
|
|
100
|
-
expect(swaggerService.generateSpec).toHaveBeenCalled();
|
|
83
|
+
await expect(controller.getSpec({})).rejects.toThrow('Test error');
|
|
101
84
|
});
|
|
102
85
|
});
|
|
103
86
|
describe('getSpec edge cases', () => {
|
|
104
87
|
it('should return default spec when no root module is set', async () => {
|
|
105
|
-
|
|
106
|
-
swagger_controller_1.SwaggerController.rootModule = undefined;
|
|
107
|
-
const context = {};
|
|
108
|
-
const result = await controller.getSpec(context);
|
|
88
|
+
const result = await controller.getSpec({});
|
|
109
89
|
expect(result).toEqual({
|
|
110
90
|
openapi: '3.0.0',
|
|
111
91
|
info: {
|
|
@@ -119,132 +99,37 @@ describe('SwaggerController', () => {
|
|
|
119
99
|
},
|
|
120
100
|
});
|
|
121
101
|
});
|
|
122
|
-
it('should
|
|
123
|
-
class TestModule {
|
|
124
|
-
}
|
|
125
|
-
swagger_controller_1.SwaggerController.setRootModule(TestModule);
|
|
126
|
-
core_1.getModuleMetadata.mockReturnValue(null);
|
|
127
|
-
const context = {};
|
|
128
|
-
const result = await controller.getSpec(context);
|
|
129
|
-
expect(result).toEqual({
|
|
130
|
-
openapi: '3.0.0',
|
|
131
|
-
info: {
|
|
132
|
-
title: 'API Documentation',
|
|
133
|
-
version: '1.0.0',
|
|
134
|
-
description: 'No controllers found',
|
|
135
|
-
},
|
|
136
|
-
paths: {},
|
|
137
|
-
components: {
|
|
138
|
-
schemas: {},
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
it('should handle module with no controllers', async () => {
|
|
143
|
-
class TestModule {
|
|
144
|
-
}
|
|
145
|
-
swagger_controller_1.SwaggerController.setRootModule(TestModule);
|
|
146
|
-
core_1.getModuleMetadata.mockReturnValue({
|
|
147
|
-
controllers: [],
|
|
148
|
-
imports: [],
|
|
149
|
-
});
|
|
150
|
-
const context = {};
|
|
151
|
-
const result = await controller.getSpec(context);
|
|
152
|
-
expect(result).toEqual({
|
|
153
|
-
openapi: '3.0.0',
|
|
154
|
-
info: {
|
|
155
|
-
title: 'API Documentation',
|
|
156
|
-
version: '1.0.0',
|
|
157
|
-
description: 'No controllers found',
|
|
158
|
-
},
|
|
159
|
-
paths: {},
|
|
160
|
-
components: {
|
|
161
|
-
schemas: {},
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
it('should handle module with no imports', async () => {
|
|
102
|
+
it('should annotate description when spec has no paths', async () => {
|
|
166
103
|
class TestModule {
|
|
167
104
|
}
|
|
168
|
-
class TestController {
|
|
169
|
-
}
|
|
170
105
|
swagger_controller_1.SwaggerController.setRootModule(TestModule);
|
|
171
|
-
|
|
172
|
-
controllers: [TestController],
|
|
173
|
-
imports: undefined,
|
|
174
|
-
});
|
|
175
|
-
swaggerService.generateSpec.mockReturnValue({
|
|
106
|
+
swaggerService.generateAutoSpec.mockReturnValue({
|
|
176
107
|
openapi: '3.0.0',
|
|
177
|
-
info: { title: '
|
|
108
|
+
info: { title: 'T', version: '1' },
|
|
178
109
|
paths: {},
|
|
179
110
|
components: { schemas: {} },
|
|
180
111
|
});
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
expect(
|
|
184
|
-
});
|
|
185
|
-
it('should recursively collect controllers from imported modules', async () => {
|
|
186
|
-
class RootModule {
|
|
187
|
-
}
|
|
188
|
-
class ImportedModule {
|
|
189
|
-
}
|
|
190
|
-
class RootController {
|
|
191
|
-
}
|
|
192
|
-
class ImportedController {
|
|
193
|
-
}
|
|
194
|
-
swagger_controller_1.SwaggerController.setRootModule(RootModule);
|
|
195
|
-
core_1.getModuleMetadata.mockImplementation((moduleType) => {
|
|
196
|
-
if (moduleType === RootModule) {
|
|
197
|
-
return {
|
|
198
|
-
controllers: [RootController],
|
|
199
|
-
imports: [ImportedModule],
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
if (moduleType === ImportedModule) {
|
|
203
|
-
return {
|
|
204
|
-
controllers: [ImportedController],
|
|
205
|
-
imports: [],
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
return null;
|
|
209
|
-
});
|
|
210
|
-
swaggerService.generateSpec.mockReturnValue({
|
|
211
|
-
openapi: '3.0.0',
|
|
212
|
-
info: { title: 'Test API', version: '1.0.0' },
|
|
213
|
-
paths: {},
|
|
214
|
-
components: { schemas: {} },
|
|
215
|
-
});
|
|
216
|
-
const context = {};
|
|
217
|
-
await controller.getSpec(context);
|
|
218
|
-
expect(swaggerService.generateSpec).toHaveBeenCalledWith(expect.arrayContaining([RootController, ImportedController]));
|
|
219
|
-
});
|
|
220
|
-
it('should filter out invalid controllers', async () => {
|
|
221
|
-
class TestModule {
|
|
222
|
-
}
|
|
223
|
-
class ValidController {
|
|
224
|
-
}
|
|
225
|
-
swagger_controller_1.SwaggerController.setRootModule(TestModule);
|
|
226
|
-
core_1.getModuleMetadata.mockReturnValue({
|
|
227
|
-
controllers: [ValidController, null, undefined, {}],
|
|
228
|
-
imports: [],
|
|
229
|
-
});
|
|
230
|
-
swaggerService.generateSpec.mockReturnValue({
|
|
231
|
-
openapi: '3.0.0',
|
|
232
|
-
info: { title: 'Test API', version: '1.0.0' },
|
|
233
|
-
paths: {},
|
|
234
|
-
components: { schemas: {} },
|
|
235
|
-
});
|
|
236
|
-
const context = {};
|
|
237
|
-
await controller.getSpec(context);
|
|
238
|
-
expect(swaggerService.generateSpec).toHaveBeenCalledWith([ValidController]);
|
|
112
|
+
const result = await controller.getSpec({});
|
|
113
|
+
expect(result.paths).toEqual({});
|
|
114
|
+
expect(result.info.description).toBe('No routes found');
|
|
239
115
|
});
|
|
240
116
|
});
|
|
241
117
|
describe('getDocs', () => {
|
|
242
|
-
it('should return Swagger UI HTML', async () => {
|
|
243
|
-
const
|
|
244
|
-
const result = await controller.getDocs(context);
|
|
118
|
+
it('should return Swagger UI HTML with default CDN', async () => {
|
|
119
|
+
const result = await controller.getDocs({});
|
|
245
120
|
expect(result).toContain('<!DOCTYPE html>');
|
|
246
121
|
expect(result).toContain('API Documentation');
|
|
247
|
-
expect(result).toContain('
|
|
122
|
+
expect(result).toContain('https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css');
|
|
123
|
+
expect(result).toContain('"/swagger/spec"');
|
|
124
|
+
});
|
|
125
|
+
it('should use globalPrefix and custom CDN when configured', async () => {
|
|
126
|
+
swagger_module_1.SwaggerModule.configure({
|
|
127
|
+
globalPrefix: '/api',
|
|
128
|
+
swaggerUiCdnBase: 'https://cdn.example.com/ui',
|
|
129
|
+
}, true);
|
|
130
|
+
const result = await controller.getDocs({});
|
|
131
|
+
expect(result).toContain('https://cdn.example.com/ui/swagger-ui.css');
|
|
132
|
+
expect(result).toContain('"/api/swagger/spec"');
|
|
248
133
|
});
|
|
249
134
|
});
|
|
250
135
|
});
|
package/dist/swagger.module.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Type } from '@hazeljs/core';
|
|
2
|
+
import type { SwaggerModuleOptions } from './swagger.types';
|
|
2
3
|
export declare class SwaggerModule {
|
|
4
|
+
/**
|
|
5
|
+
* Document + UI options.
|
|
6
|
+
* @param replaceAll When true, replaces all options (use to reset). Default merges shallowly with previous `configure` calls.
|
|
7
|
+
*/
|
|
8
|
+
static configure(options: SwaggerModuleOptions, replaceAll?: boolean): void;
|
|
9
|
+
static getOptions(): SwaggerModuleOptions;
|
|
3
10
|
static setRootModule(rootModule: Type<unknown>): void;
|
|
4
11
|
}
|
|
5
12
|
//# sourceMappingURL=swagger.module.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swagger.module.d.ts","sourceRoot":"","sources":["../src/swagger.module.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"swagger.module.d.ts","sourceRoot":"","sources":["../src/swagger.module.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAO5D,qBAKa,aAAa;IACxB;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,oBAAoB,EAAE,UAAU,UAAQ,GAAG,IAAI;IAQzE,MAAM,CAAC,UAAU,IAAI,oBAAoB;IAIzC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;CAItD"}
|
package/dist/swagger.module.js
CHANGED
|
@@ -14,7 +14,23 @@ const core_1 = require("@hazeljs/core");
|
|
|
14
14
|
const swagger_service_1 = require("./swagger.service");
|
|
15
15
|
const swagger_controller_1 = require("./swagger.controller");
|
|
16
16
|
const core_2 = __importDefault(require("@hazeljs/core"));
|
|
17
|
+
const swagger_config_1 = require("./swagger-config");
|
|
17
18
|
let SwaggerModule = class SwaggerModule {
|
|
19
|
+
/**
|
|
20
|
+
* Document + UI options.
|
|
21
|
+
* @param replaceAll When true, replaces all options (use to reset). Default merges shallowly with previous `configure` calls.
|
|
22
|
+
*/
|
|
23
|
+
static configure(options, replaceAll = false) {
|
|
24
|
+
if (replaceAll) {
|
|
25
|
+
(0, swagger_config_1.replaceSwaggerModuleOptions)(options);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
(0, swagger_config_1.mergeSwaggerModuleOptions)(options);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
static getOptions() {
|
|
32
|
+
return (0, swagger_config_1.getSwaggerModuleOptions)();
|
|
33
|
+
}
|
|
18
34
|
static setRootModule(rootModule) {
|
|
19
35
|
core_2.default.debug('SwaggerModule: Setting root module:', rootModule.name);
|
|
20
36
|
swagger_controller_1.SwaggerController.setRootModule(rootModule);
|