@rsdk/cli.cmd.autodoc 6.0.0-next.1 → 6.0.0-next.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/CHANGELOG.md +382 -0
- package/TODO.MD +24 -0
- package/dist/commands/autodoc.cmd.js +8 -10
- package/dist/commands/autodoc.cmd.js.map +1 -1
- package/dist/commands/openapi.variation.d.ts +2 -1
- package/dist/commands/openapi.variation.js +47 -25
- package/dist/commands/openapi.variation.js.map +1 -1
- package/dist/generators/file-generator.interface.d.ts +1 -1
- package/dist/generators/openapi-json/config.schema.d.ts +55 -0
- package/dist/generators/openapi-json/config.schema.js +36 -0
- package/dist/generators/openapi-json/config.schema.js.map +1 -0
- package/dist/generators/openapi-json/index.d.ts +1 -0
- package/dist/generators/openapi-json/index.js +1 -0
- package/dist/generators/openapi-json/index.js.map +1 -1
- package/dist/generators/openapi-json/openapi-json.generator.d.ts +9 -11
- package/dist/generators/openapi-json/openapi-json.generator.js +12 -89
- package/dist/generators/openapi-json/openapi-json.generator.js.map +1 -1
- package/dist/generators/openapi-json/schemas/config.schema.d.ts +55 -0
- package/dist/generators/openapi-json/schemas/config.schema.js +36 -0
- package/dist/generators/openapi-json/schemas/config.schema.js.map +1 -0
- package/dist/generators/openapi-json/schemas/index.d.ts +1 -0
- package/dist/generators/openapi-json/schemas/index.js +18 -0
- package/dist/generators/openapi-json/schemas/index.js.map +1 -0
- package/jest.config.e2e.js +1 -0
- package/jest.config.js +1 -0
- package/jest.config.unit.js +1 -0
- package/package.json +15 -27
- package/src/base/app.loader.ts +39 -0
- package/src/base/document.file.ts +33 -0
- package/src/base/index.ts +2 -0
- package/src/commands/autodoc.cmd.ts +93 -0
- package/src/commands/openapi.variation.ts +172 -0
- package/src/generators/autodoc-md/autodoc-md.generator.ts +56 -0
- package/src/generators/autodoc-md/formatters.ts +9 -0
- package/src/generators/autodoc-md/fragments/document-fragment.abstract.ts +6 -0
- package/src/generators/autodoc-md/fragments/extractor/document.extractor.ts +73 -0
- package/src/generators/autodoc-md/fragments/implementations/additional-sources.fragment.ts +41 -0
- package/src/generators/autodoc-md/fragments/implementations/app-metadata.fragment.ts +18 -0
- package/src/generators/autodoc-md/fragments/implementations/autodoc.fragment.ts +30 -0
- package/src/generators/autodoc-md/fragments/implementations/config-section.fragment.ts +148 -0
- package/src/generators/autodoc-md/fragments/implementations/header.fragment.ts +18 -0
- package/src/generators/autodoc-md/fragments/implementations/plugin.fragment.ts +21 -0
- package/src/generators/autodoc-md/fragments/implementations/transports.fragment.ts +36 -0
- package/src/generators/autodoc-md/fragments/index.ts +9 -0
- package/src/generators/autodoc-md/index.ts +1 -0
- package/src/generators/file-generator.interface.ts +22 -0
- package/src/generators/index.ts +4 -0
- package/src/generators/openapi-json/config.schema.spec.ts +265 -0
- package/src/generators/openapi-json/config.schema.ts +37 -0
- package/src/generators/openapi-json/index.ts +2 -0
- package/src/generators/openapi-json/openapi-json.generator.ts +53 -0
- package/src/generators/openapi-json/schemas/config.schema.spec.ts +265 -0
- package/src/generators/openapi-json/schemas/config.schema.ts +37 -0
- package/src/generators/openapi-json/schemas/index.ts +1 -0
- package/src/generators/rsdk-json/index.ts +1 -0
- package/src/generators/rsdk-json/rsdk-json.generator.ts +24 -0
- package/src/index.ts +1 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { TypeBoxValidator } from '@rsdk/common';
|
|
2
|
+
import { parse } from 'yaml';
|
|
3
|
+
|
|
4
|
+
import { OpenApiConfigSchema } from './config.schema';
|
|
5
|
+
|
|
6
|
+
describe('OpenAPI Config Schema', () => {
|
|
7
|
+
const validator = new TypeBoxValidator(OpenApiConfigSchema);
|
|
8
|
+
|
|
9
|
+
it('should validate minimal config', () => {
|
|
10
|
+
const config = parse(`
|
|
11
|
+
out: openapi.json
|
|
12
|
+
title: Test API
|
|
13
|
+
version: 1.0.0
|
|
14
|
+
description: Test API Description
|
|
15
|
+
`);
|
|
16
|
+
|
|
17
|
+
const { isValid, errors } = validator.check(config);
|
|
18
|
+
|
|
19
|
+
expect(isValid).toBe(true);
|
|
20
|
+
expect(errors).toHaveLength(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should validate minimal config', () => {
|
|
24
|
+
const config = parse(`
|
|
25
|
+
out: openapi.json
|
|
26
|
+
title: Test API
|
|
27
|
+
version: '1.0.0'
|
|
28
|
+
description: Test API Description
|
|
29
|
+
`);
|
|
30
|
+
|
|
31
|
+
const { isValid, errors } = validator.check(config);
|
|
32
|
+
|
|
33
|
+
expect(isValid).toBe(true);
|
|
34
|
+
expect(errors).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should validate config with zones', () => {
|
|
38
|
+
const config = parse(`
|
|
39
|
+
out: openapi.json
|
|
40
|
+
title: Test API
|
|
41
|
+
version: '1.0.0'
|
|
42
|
+
description: Test API Description
|
|
43
|
+
includeZones:
|
|
44
|
+
- public
|
|
45
|
+
- api:web
|
|
46
|
+
excludeZones:
|
|
47
|
+
- __internal
|
|
48
|
+
`);
|
|
49
|
+
|
|
50
|
+
const { isValid, errors } = validator.check(config);
|
|
51
|
+
|
|
52
|
+
expect(isValid).toBe(true);
|
|
53
|
+
expect(errors).toHaveLength(0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should validate config without optional fields', () => {
|
|
57
|
+
const config = parse(`
|
|
58
|
+
out: openapi.json
|
|
59
|
+
version: '1.0.0'
|
|
60
|
+
`);
|
|
61
|
+
|
|
62
|
+
const { isValid, errors } = validator.check(config);
|
|
63
|
+
|
|
64
|
+
expect(isValid).toBe(true);
|
|
65
|
+
expect(errors).toHaveLength(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('Security Schemes', () => {
|
|
69
|
+
it('should validate API Key auth', () => {
|
|
70
|
+
const config = parse(`
|
|
71
|
+
out: openapi.json
|
|
72
|
+
title: Test API
|
|
73
|
+
version: '1.0.0'
|
|
74
|
+
description: Test API Description
|
|
75
|
+
security:
|
|
76
|
+
ApiKeyAuth:
|
|
77
|
+
type: apiKey
|
|
78
|
+
in: header
|
|
79
|
+
name: x-api-key
|
|
80
|
+
description: API Key Authentication
|
|
81
|
+
`);
|
|
82
|
+
|
|
83
|
+
const { isValid, errors } = validator.check(config);
|
|
84
|
+
|
|
85
|
+
expect(isValid).toBe(true);
|
|
86
|
+
expect(errors).toHaveLength(0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should validate Bearer auth', () => {
|
|
90
|
+
const config = parse(`
|
|
91
|
+
out: openapi.json
|
|
92
|
+
title: Test API
|
|
93
|
+
version: '1.0.0'
|
|
94
|
+
description: Test API Description
|
|
95
|
+
security:
|
|
96
|
+
BearerAuth:
|
|
97
|
+
type: http
|
|
98
|
+
scheme: bearer
|
|
99
|
+
bearerFormat: JWT
|
|
100
|
+
description: JWT Bearer Authentication
|
|
101
|
+
`);
|
|
102
|
+
|
|
103
|
+
const { isValid, errors } = validator.check(config);
|
|
104
|
+
|
|
105
|
+
expect(isValid).toBe(true);
|
|
106
|
+
expect(errors).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should validate Basic auth', () => {
|
|
110
|
+
const config = parse(`
|
|
111
|
+
out: openapi.json
|
|
112
|
+
title: Test API
|
|
113
|
+
version: '1.0.0'
|
|
114
|
+
description: Test API Description
|
|
115
|
+
security:
|
|
116
|
+
BasicAuth:
|
|
117
|
+
type: http
|
|
118
|
+
scheme: basic
|
|
119
|
+
description: Basic Authentication
|
|
120
|
+
`);
|
|
121
|
+
|
|
122
|
+
const { isValid, errors } = validator.check(config);
|
|
123
|
+
|
|
124
|
+
expect(isValid).toBe(true);
|
|
125
|
+
expect(errors).toHaveLength(0);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should validate OAuth2 auth', () => {
|
|
129
|
+
const config = parse(`
|
|
130
|
+
out: openapi.json
|
|
131
|
+
title: Test API
|
|
132
|
+
version: '1.0.0'
|
|
133
|
+
description: Test API Description
|
|
134
|
+
security:
|
|
135
|
+
OAuth2Auth:
|
|
136
|
+
type: oauth2
|
|
137
|
+
flows:
|
|
138
|
+
implicit:
|
|
139
|
+
authorizationUrl: https://auth.example.com/oauth/authorize
|
|
140
|
+
scopes:
|
|
141
|
+
read: Read access
|
|
142
|
+
write: Write access
|
|
143
|
+
`);
|
|
144
|
+
|
|
145
|
+
const { isValid, errors } = validator.check(config);
|
|
146
|
+
|
|
147
|
+
for (const error of errors) {
|
|
148
|
+
console.log(error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
expect(isValid).toBe(true);
|
|
152
|
+
expect(errors).toHaveLength(0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should validate multiple security schemes', () => {
|
|
156
|
+
const config = parse(`
|
|
157
|
+
out: openapi.json
|
|
158
|
+
title: Test API
|
|
159
|
+
version: '1.0.0'
|
|
160
|
+
description: Test API Description
|
|
161
|
+
security:
|
|
162
|
+
BearerAuth:
|
|
163
|
+
type: http
|
|
164
|
+
scheme: bearer
|
|
165
|
+
bearerFormat: JWT
|
|
166
|
+
description: JWT Bearer Authentication
|
|
167
|
+
|
|
168
|
+
ApiKeyAuth:
|
|
169
|
+
type: apiKey
|
|
170
|
+
in: header
|
|
171
|
+
name: x-api-key
|
|
172
|
+
description: API Key Authentication
|
|
173
|
+
|
|
174
|
+
OAuth2Auth:
|
|
175
|
+
type: oauth2
|
|
176
|
+
description: OAuth2 Authentication
|
|
177
|
+
flows:
|
|
178
|
+
implicit:
|
|
179
|
+
authorizationUrl: https://auth.example.com/oauth/authorize
|
|
180
|
+
scopes:
|
|
181
|
+
read: Read access
|
|
182
|
+
write: Write access
|
|
183
|
+
`);
|
|
184
|
+
|
|
185
|
+
const { isValid, errors } = validator.check(config);
|
|
186
|
+
|
|
187
|
+
expect(isValid).toBe(true);
|
|
188
|
+
expect(errors).toHaveLength(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should validate cookie-based auth', () => {
|
|
192
|
+
const config = parse(`
|
|
193
|
+
out: openapi/openapi.web.json
|
|
194
|
+
includeZones:
|
|
195
|
+
- api:web
|
|
196
|
+
title: Web API
|
|
197
|
+
version: '1.3'
|
|
198
|
+
description: Web API methods
|
|
199
|
+
security:
|
|
200
|
+
SessionCookie:
|
|
201
|
+
type: apiKey
|
|
202
|
+
in: cookie
|
|
203
|
+
name: sessionId
|
|
204
|
+
description: Cookie-based authentication
|
|
205
|
+
`);
|
|
206
|
+
|
|
207
|
+
const { isValid, errors } = validator.check(config);
|
|
208
|
+
|
|
209
|
+
expect(isValid).toBe(true);
|
|
210
|
+
expect(errors).toHaveLength(0);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('Validation Errors', () => {
|
|
215
|
+
it('should fail on invalid security type', () => {
|
|
216
|
+
const config = parse(`
|
|
217
|
+
out: openapi.json
|
|
218
|
+
title: Test API
|
|
219
|
+
version: 1.0.0
|
|
220
|
+
description: Test API Description
|
|
221
|
+
security:
|
|
222
|
+
InvalidAuth:
|
|
223
|
+
type: invalid
|
|
224
|
+
`);
|
|
225
|
+
|
|
226
|
+
const { isValid, errors } = validator.check(config);
|
|
227
|
+
|
|
228
|
+
expect(isValid).toBe(false);
|
|
229
|
+
expect(errors).toHaveLength(2);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should fail on missing required fields', () => {
|
|
233
|
+
const config = parse(`
|
|
234
|
+
out: openapi.json
|
|
235
|
+
title: Test API
|
|
236
|
+
description: Test API Description
|
|
237
|
+
`);
|
|
238
|
+
|
|
239
|
+
const { isValid, errors } = validator.check(config);
|
|
240
|
+
|
|
241
|
+
expect(isValid).toBe(true);
|
|
242
|
+
expect(errors).toHaveLength(0);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should fail on invalid OAuth2 flow configuration', () => {
|
|
246
|
+
const config = parse(`
|
|
247
|
+
out: openapi.json
|
|
248
|
+
title: Test API
|
|
249
|
+
version: 1.0.0
|
|
250
|
+
description: Test API Description
|
|
251
|
+
security:
|
|
252
|
+
- type: oauth2
|
|
253
|
+
schemaName: OAuth2Auth
|
|
254
|
+
flows:
|
|
255
|
+
implicit:
|
|
256
|
+
scopes: invalid
|
|
257
|
+
`);
|
|
258
|
+
|
|
259
|
+
const { isValid, errors } = validator.check(config);
|
|
260
|
+
|
|
261
|
+
expect(isValid).toBe(false);
|
|
262
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OpenApiOptions } from '@rsdk/http.openapi';
|
|
2
|
+
import type { Static } from '@sinclair/typebox';
|
|
3
|
+
import { Type } from '@sinclair/typebox';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration schema for OpenAPI document generation.
|
|
7
|
+
* @example
|
|
8
|
+
* {
|
|
9
|
+
* "out": "openapi.json",
|
|
10
|
+
* "app": "./dist/app.js",
|
|
11
|
+
* "title": "My API",
|
|
12
|
+
* "version": "1.0.0",
|
|
13
|
+
* "description": "API Description",
|
|
14
|
+
* "includeZones": ["public"],
|
|
15
|
+
* "excludeZones": ["internal"],
|
|
16
|
+
* "security": {
|
|
17
|
+
* "jwt": {
|
|
18
|
+
* "type": "http",
|
|
19
|
+
* "scheme": "bearer",
|
|
20
|
+
* "bearerFormat": "JWT",
|
|
21
|
+
* "name": "JWT Auth"
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
export const OpenApiConfigSchema = Type.Intersect([
|
|
27
|
+
Type.Partial(OpenApiOptions),
|
|
28
|
+
Type.Object({
|
|
29
|
+
/** Output file path for the generated OpenAPI document */
|
|
30
|
+
out: Type.Optional(Type.String()),
|
|
31
|
+
|
|
32
|
+
/** Path to the application file (usually `dist/app.js`) */
|
|
33
|
+
app: Type.Optional(Type.String()),
|
|
34
|
+
}),
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
export type OpenApiConfigSchema = Static<typeof OpenApiConfigSchema>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { PlatformApp, PlatformContext } from '@rsdk/core';
|
|
2
|
+
import { OpenApiGenerator } from '@rsdk/http.openapi';
|
|
3
|
+
import { LoggerFactory } from '@rsdk/logging';
|
|
4
|
+
|
|
5
|
+
import { FileGenerator } from '../file-generator.interface';
|
|
6
|
+
|
|
7
|
+
import type { OpenApiConfigSchema } from './config.schema';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Here we don't need `out` and `app`,
|
|
11
|
+
* but `title`, `version` and `description` are required.
|
|
12
|
+
*/
|
|
13
|
+
export type OpenApiGeneratorOptions = Omit<OpenApiConfigSchema, 'out' | 'app'> &
|
|
14
|
+
Required<Pick<OpenApiConfigSchema, 'title' | 'version' | 'description'>>;
|
|
15
|
+
|
|
16
|
+
export class OpenApiFileGenerator extends FileGenerator {
|
|
17
|
+
private readonly context: PlatformContext;
|
|
18
|
+
private readonly options: Partial<OpenApiGeneratorOptions>;
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
app: PlatformApp,
|
|
22
|
+
options: Partial<OpenApiGeneratorOptions> = {},
|
|
23
|
+
) {
|
|
24
|
+
super(LoggerFactory.create(OpenApiFileGenerator));
|
|
25
|
+
this.context = app.context;
|
|
26
|
+
this.options = options;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected async getContent(): Promise<string> {
|
|
30
|
+
this.logger.info('Generate openapi starting');
|
|
31
|
+
|
|
32
|
+
const options = await this.resolveOptions(this.context, this.options);
|
|
33
|
+
const generator = new OpenApiGenerator(this.context, options);
|
|
34
|
+
const document = await generator.generate();
|
|
35
|
+
|
|
36
|
+
this.logger.info('Generate openapi finished');
|
|
37
|
+
return JSON.stringify(document, null, 2);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private async resolveOptions(
|
|
41
|
+
context: PlatformContext,
|
|
42
|
+
passed: Partial<OpenApiGeneratorOptions>,
|
|
43
|
+
): Promise<OpenApiGeneratorOptions> {
|
|
44
|
+
const appMetadata = await context.getMetadata();
|
|
45
|
+
const defaults: OpenApiGeneratorOptions = {
|
|
46
|
+
title: appMetadata.name,
|
|
47
|
+
version: appMetadata.version,
|
|
48
|
+
description: appMetadata.description,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return { ...defaults, ...passed };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { TypeBoxValidator } from '@rsdk/common';
|
|
2
|
+
import { parse } from 'yaml';
|
|
3
|
+
|
|
4
|
+
import { OpenApiConfigSchema } from './config.schema';
|
|
5
|
+
|
|
6
|
+
describe('OpenAPI Config Schema', () => {
|
|
7
|
+
const validator = new TypeBoxValidator(OpenApiConfigSchema);
|
|
8
|
+
|
|
9
|
+
it('should validate minimal config', () => {
|
|
10
|
+
const config = parse(`
|
|
11
|
+
out: openapi.json
|
|
12
|
+
title: Test API
|
|
13
|
+
version: 1.0.0
|
|
14
|
+
description: Test API Description
|
|
15
|
+
`);
|
|
16
|
+
|
|
17
|
+
const { isValid, errors } = validator.check(config);
|
|
18
|
+
|
|
19
|
+
expect(isValid).toBe(true);
|
|
20
|
+
expect(errors).toHaveLength(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should validate minimal config', () => {
|
|
24
|
+
const config = parse(`
|
|
25
|
+
out: openapi.json
|
|
26
|
+
title: Test API
|
|
27
|
+
version: '1.0.0'
|
|
28
|
+
description: Test API Description
|
|
29
|
+
`);
|
|
30
|
+
|
|
31
|
+
const { isValid, errors } = validator.check(config);
|
|
32
|
+
|
|
33
|
+
expect(isValid).toBe(true);
|
|
34
|
+
expect(errors).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should validate config with zones', () => {
|
|
38
|
+
const config = parse(`
|
|
39
|
+
out: openapi.json
|
|
40
|
+
title: Test API
|
|
41
|
+
version: '1.0.0'
|
|
42
|
+
description: Test API Description
|
|
43
|
+
includeZones:
|
|
44
|
+
- public
|
|
45
|
+
- api:web
|
|
46
|
+
excludeZones:
|
|
47
|
+
- __internal
|
|
48
|
+
`);
|
|
49
|
+
|
|
50
|
+
const { isValid, errors } = validator.check(config);
|
|
51
|
+
|
|
52
|
+
expect(isValid).toBe(true);
|
|
53
|
+
expect(errors).toHaveLength(0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should validate config without optional fields', () => {
|
|
57
|
+
const config = parse(`
|
|
58
|
+
out: openapi.json
|
|
59
|
+
version: '1.0.0'
|
|
60
|
+
`);
|
|
61
|
+
|
|
62
|
+
const { isValid, errors } = validator.check(config);
|
|
63
|
+
|
|
64
|
+
expect(isValid).toBe(true);
|
|
65
|
+
expect(errors).toHaveLength(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('Security Schemes', () => {
|
|
69
|
+
it('should validate API Key auth', () => {
|
|
70
|
+
const config = parse(`
|
|
71
|
+
out: openapi.json
|
|
72
|
+
title: Test API
|
|
73
|
+
version: '1.0.0'
|
|
74
|
+
description: Test API Description
|
|
75
|
+
security:
|
|
76
|
+
ApiKeyAuth:
|
|
77
|
+
type: apiKey
|
|
78
|
+
in: header
|
|
79
|
+
name: x-api-key
|
|
80
|
+
description: API Key Authentication
|
|
81
|
+
`);
|
|
82
|
+
|
|
83
|
+
const { isValid, errors } = validator.check(config);
|
|
84
|
+
|
|
85
|
+
expect(isValid).toBe(true);
|
|
86
|
+
expect(errors).toHaveLength(0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should validate Bearer auth', () => {
|
|
90
|
+
const config = parse(`
|
|
91
|
+
out: openapi.json
|
|
92
|
+
title: Test API
|
|
93
|
+
version: '1.0.0'
|
|
94
|
+
description: Test API Description
|
|
95
|
+
security:
|
|
96
|
+
BearerAuth:
|
|
97
|
+
type: http
|
|
98
|
+
scheme: bearer
|
|
99
|
+
bearerFormat: JWT
|
|
100
|
+
description: JWT Bearer Authentication
|
|
101
|
+
`);
|
|
102
|
+
|
|
103
|
+
const { isValid, errors } = validator.check(config);
|
|
104
|
+
|
|
105
|
+
expect(isValid).toBe(true);
|
|
106
|
+
expect(errors).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should validate Basic auth', () => {
|
|
110
|
+
const config = parse(`
|
|
111
|
+
out: openapi.json
|
|
112
|
+
title: Test API
|
|
113
|
+
version: '1.0.0'
|
|
114
|
+
description: Test API Description
|
|
115
|
+
security:
|
|
116
|
+
BasicAuth:
|
|
117
|
+
type: http
|
|
118
|
+
scheme: basic
|
|
119
|
+
description: Basic Authentication
|
|
120
|
+
`);
|
|
121
|
+
|
|
122
|
+
const { isValid, errors } = validator.check(config);
|
|
123
|
+
|
|
124
|
+
expect(isValid).toBe(true);
|
|
125
|
+
expect(errors).toHaveLength(0);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should validate OAuth2 auth', () => {
|
|
129
|
+
const config = parse(`
|
|
130
|
+
out: openapi.json
|
|
131
|
+
title: Test API
|
|
132
|
+
version: '1.0.0'
|
|
133
|
+
description: Test API Description
|
|
134
|
+
security:
|
|
135
|
+
OAuth2Auth:
|
|
136
|
+
type: oauth2
|
|
137
|
+
flows:
|
|
138
|
+
implicit:
|
|
139
|
+
authorizationUrl: https://auth.example.com/oauth/authorize
|
|
140
|
+
scopes:
|
|
141
|
+
read: Read access
|
|
142
|
+
write: Write access
|
|
143
|
+
`);
|
|
144
|
+
|
|
145
|
+
const { isValid, errors } = validator.check(config);
|
|
146
|
+
|
|
147
|
+
for (const error of errors) {
|
|
148
|
+
console.log(error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
expect(isValid).toBe(true);
|
|
152
|
+
expect(errors).toHaveLength(0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should validate multiple security schemes', () => {
|
|
156
|
+
const config = parse(`
|
|
157
|
+
out: openapi.json
|
|
158
|
+
title: Test API
|
|
159
|
+
version: '1.0.0'
|
|
160
|
+
description: Test API Description
|
|
161
|
+
security:
|
|
162
|
+
BearerAuth:
|
|
163
|
+
type: http
|
|
164
|
+
scheme: bearer
|
|
165
|
+
bearerFormat: JWT
|
|
166
|
+
description: JWT Bearer Authentication
|
|
167
|
+
|
|
168
|
+
ApiKeyAuth:
|
|
169
|
+
type: apiKey
|
|
170
|
+
in: header
|
|
171
|
+
name: x-api-key
|
|
172
|
+
description: API Key Authentication
|
|
173
|
+
|
|
174
|
+
OAuth2Auth:
|
|
175
|
+
type: oauth2
|
|
176
|
+
description: OAuth2 Authentication
|
|
177
|
+
flows:
|
|
178
|
+
implicit:
|
|
179
|
+
authorizationUrl: https://auth.example.com/oauth/authorize
|
|
180
|
+
scopes:
|
|
181
|
+
read: Read access
|
|
182
|
+
write: Write access
|
|
183
|
+
`);
|
|
184
|
+
|
|
185
|
+
const { isValid, errors } = validator.check(config);
|
|
186
|
+
|
|
187
|
+
expect(isValid).toBe(true);
|
|
188
|
+
expect(errors).toHaveLength(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should validate cookie-based auth', () => {
|
|
192
|
+
const config = parse(`
|
|
193
|
+
out: openapi/openapi.web.json
|
|
194
|
+
includeZones:
|
|
195
|
+
- api:web
|
|
196
|
+
title: Web API
|
|
197
|
+
version: '1.3'
|
|
198
|
+
description: Web API methods
|
|
199
|
+
security:
|
|
200
|
+
SessionCookie:
|
|
201
|
+
type: apiKey
|
|
202
|
+
in: cookie
|
|
203
|
+
name: sessionId
|
|
204
|
+
description: Cookie-based authentication
|
|
205
|
+
`);
|
|
206
|
+
|
|
207
|
+
const { isValid, errors } = validator.check(config);
|
|
208
|
+
|
|
209
|
+
expect(isValid).toBe(true);
|
|
210
|
+
expect(errors).toHaveLength(0);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('Validation Errors', () => {
|
|
215
|
+
it('should fail on invalid security type', () => {
|
|
216
|
+
const config = parse(`
|
|
217
|
+
out: openapi.json
|
|
218
|
+
title: Test API
|
|
219
|
+
version: 1.0.0
|
|
220
|
+
description: Test API Description
|
|
221
|
+
security:
|
|
222
|
+
InvalidAuth:
|
|
223
|
+
type: invalid
|
|
224
|
+
`);
|
|
225
|
+
|
|
226
|
+
const { isValid, errors } = validator.check(config);
|
|
227
|
+
|
|
228
|
+
expect(isValid).toBe(false);
|
|
229
|
+
expect(errors).toHaveLength(2);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should fail on missing required fields', () => {
|
|
233
|
+
const config = parse(`
|
|
234
|
+
out: openapi.json
|
|
235
|
+
title: Test API
|
|
236
|
+
description: Test API Description
|
|
237
|
+
`);
|
|
238
|
+
|
|
239
|
+
const { isValid, errors } = validator.check(config);
|
|
240
|
+
|
|
241
|
+
expect(isValid).toBe(true);
|
|
242
|
+
expect(errors).toHaveLength(0);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should fail on invalid OAuth2 flow configuration', () => {
|
|
246
|
+
const config = parse(`
|
|
247
|
+
out: openapi.json
|
|
248
|
+
title: Test API
|
|
249
|
+
version: 1.0.0
|
|
250
|
+
description: Test API Description
|
|
251
|
+
security:
|
|
252
|
+
- type: oauth2
|
|
253
|
+
schemaName: OAuth2Auth
|
|
254
|
+
flows:
|
|
255
|
+
implicit:
|
|
256
|
+
scopes: invalid
|
|
257
|
+
`);
|
|
258
|
+
|
|
259
|
+
const { isValid, errors } = validator.check(config);
|
|
260
|
+
|
|
261
|
+
expect(isValid).toBe(false);
|
|
262
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OpenApiOptions } from '@rsdk/http.openapi';
|
|
2
|
+
import type { Static } from '@sinclair/typebox';
|
|
3
|
+
import { Type } from '@sinclair/typebox';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration schema for OpenAPI document generation.
|
|
7
|
+
* @example
|
|
8
|
+
* {
|
|
9
|
+
* "out": "openapi.json",
|
|
10
|
+
* "app": "./dist/app.js",
|
|
11
|
+
* "title": "My API",
|
|
12
|
+
* "version": "1.0.0",
|
|
13
|
+
* "description": "API Description",
|
|
14
|
+
* "includeZones": ["public"],
|
|
15
|
+
* "excludeZones": ["internal"],
|
|
16
|
+
* "security": {
|
|
17
|
+
* "jwt": {
|
|
18
|
+
* "type": "http",
|
|
19
|
+
* "scheme": "bearer",
|
|
20
|
+
* "bearerFormat": "JWT",
|
|
21
|
+
* "name": "JWT Auth"
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
export const OpenApiConfigSchema = Type.Intersect([
|
|
27
|
+
Type.Partial(OpenApiOptions),
|
|
28
|
+
Type.Object({
|
|
29
|
+
/** Output file path for the generated OpenAPI document */
|
|
30
|
+
out: Type.Optional(Type.String()),
|
|
31
|
+
|
|
32
|
+
/** Path to the application file (usually `dist/app.js`) */
|
|
33
|
+
app: Type.Optional(Type.String()),
|
|
34
|
+
}),
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
export type OpenApiConfigSchema = Static<typeof OpenApiConfigSchema>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './config.schema';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './rsdk-json.generator';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PlatformApp } from '@rsdk/core';
|
|
2
|
+
import { LoggerFactory } from '@rsdk/logging';
|
|
3
|
+
import type { Resource } from '@rsdk/metadata';
|
|
4
|
+
|
|
5
|
+
import { FileGenerator } from '../file-generator.interface';
|
|
6
|
+
|
|
7
|
+
export class RsdkFileGenerator extends FileGenerator {
|
|
8
|
+
constructor(private readonly app: PlatformApp) {
|
|
9
|
+
super(LoggerFactory.create(RsdkFileGenerator));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected async getContent(): Promise<string> {
|
|
13
|
+
const rsdkMetadata = await this.getAllResources();
|
|
14
|
+
const metadata = await this.app.getMetadata();
|
|
15
|
+
|
|
16
|
+
return JSON.stringify({ metadata, rsdkMetadata }, null, 2);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private async getAllResources(): Promise<Resource<unknown>[]> {
|
|
20
|
+
const provider = await this.app.context.getRsdkMetadataProvider();
|
|
21
|
+
|
|
22
|
+
return provider.get();
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AutodocCommand as default } from './commands/autodoc.cmd';
|