@travetto/openapi 2.1.5 → 2.2.2
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 +4 -4
- package/bin/cli-openapi_client.ts +20 -11
- package/bin/cli-openapi_spec.ts +9 -5
- package/bin/generate.ts +1 -1
- package/package.json +6 -6
- package/src/config.ts +2 -2
- package/src/controller.ts +3 -3
- package/src/service.ts +4 -4
- package/src/spec-generate.ts +36 -23
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ All of the high level configurations can be found in the following structure:
|
|
|
20
20
|
**Code: Config: OpenAPI Configuration**
|
|
21
21
|
```typescript
|
|
22
22
|
import * as path from 'path';
|
|
23
|
-
import * as fs from 'fs';
|
|
23
|
+
import * as fs from 'fs/promises';
|
|
24
24
|
import { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/src/model/OpenApi';
|
|
25
25
|
|
|
26
26
|
import { Config } from '@travetto/config';
|
|
@@ -37,7 +37,7 @@ export class ApiInfoConfig {
|
|
|
37
37
|
license: LicenseObject = { name: AppManifest.info.license! };
|
|
38
38
|
termsOfService?: string;
|
|
39
39
|
title: string = AppManifest.info.name;
|
|
40
|
-
version
|
|
40
|
+
version: string = AppManifest.info.version ?? '0.0.0';
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -77,7 +77,7 @@ export class ApiSpecConfig {
|
|
|
77
77
|
*/
|
|
78
78
|
exposeAllSchemas: boolean = false;
|
|
79
79
|
|
|
80
|
-
async postConstruct() {
|
|
80
|
+
async postConstruct(): Promise<void> {
|
|
81
81
|
this.output = PathUtil.toUnix(this.output);
|
|
82
82
|
if (!this.output || this.output === '-') {
|
|
83
83
|
this.persist = false;
|
|
@@ -86,7 +86,7 @@ export class ApiSpecConfig {
|
|
|
86
86
|
if (!/[.](json|ya?ml)$/.test(this.output)) { // Assume a folder
|
|
87
87
|
this.output = PathUtil.resolveUnix(this.output, 'openapi.yml');
|
|
88
88
|
}
|
|
89
|
-
await fs.
|
|
89
|
+
await fs.mkdir(path.dirname(this.output), { recursive: true });
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -2,23 +2,31 @@ import * as path from 'path';
|
|
|
2
2
|
import * as fs from 'fs/promises';
|
|
3
3
|
import { readFileSync } from 'fs';
|
|
4
4
|
|
|
5
|
-
import { BasePlugin } from '@travetto/cli/src/plugin-base';
|
|
5
|
+
import { BasePlugin, OptionConfig } from '@travetto/cli/src/plugin-base';
|
|
6
6
|
import { AppCache, ExecUtil, PathUtil } from '@travetto/boot';
|
|
7
7
|
import { color } from '@travetto/cli/src/color';
|
|
8
8
|
|
|
9
|
-
const presets = JSON.parse(readFileSync(PathUtil.resolveUnix(__dirname, '..', 'resources', 'presets.json'), 'utf8'))
|
|
9
|
+
const presets: Record<string, [string, object] | [string]> = JSON.parse(readFileSync(PathUtil.resolveUnix(__dirname, '..', 'resources', 'presets.json'), 'utf8'));
|
|
10
10
|
|
|
11
|
+
type Options = {
|
|
12
|
+
extendedHelp: OptionConfig<boolean>;
|
|
13
|
+
props: OptionConfig<string[]>;
|
|
14
|
+
input: OptionConfig<string>;
|
|
15
|
+
output: OptionConfig<string>;
|
|
16
|
+
dockerImage: OptionConfig<string>;
|
|
17
|
+
watch: OptionConfig<boolean>;
|
|
18
|
+
};
|
|
11
19
|
/**
|
|
12
20
|
* CLI for generating the cli client
|
|
13
21
|
*/
|
|
14
|
-
export class OpenApiClientPlugin extends BasePlugin {
|
|
22
|
+
export class OpenApiClientPlugin extends BasePlugin<Options> {
|
|
15
23
|
name = 'openapi:client';
|
|
16
24
|
|
|
17
|
-
presetMap(prop?: object) {
|
|
25
|
+
presetMap(prop?: object): string {
|
|
18
26
|
return !prop || Object.keys(prop).length === 0 ? '' : Object.entries(prop).map(([k, v]) => `${k}=${v}`).join(',');
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
getListOfFormats() {
|
|
29
|
+
getListOfFormats(): string[] {
|
|
22
30
|
const json = AppCache.getOrSet('openapi-formats.json', () => {
|
|
23
31
|
const stdout = ExecUtil.execSync('docker', ['run', '--rm', this.cmd.dockerImage, 'list']);
|
|
24
32
|
const lines = stdout
|
|
@@ -31,10 +39,11 @@ export class OpenApiClientPlugin extends BasePlugin {
|
|
|
31
39
|
...lines.sort(),
|
|
32
40
|
]);
|
|
33
41
|
});
|
|
34
|
-
|
|
42
|
+
const list: string[] = JSON.parse(json);
|
|
43
|
+
return list;
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
getOptions() {
|
|
46
|
+
getOptions(): Options {
|
|
38
47
|
return {
|
|
39
48
|
extendedHelp: this.boolOption({ name: 'extended-help', short: 'x', desc: 'Show Extended Help' }),
|
|
40
49
|
props: this.listOption({ name: 'additional-properties', short: 'a', desc: 'Additional Properties' }),
|
|
@@ -45,7 +54,7 @@ export class OpenApiClientPlugin extends BasePlugin {
|
|
|
45
54
|
};
|
|
46
55
|
}
|
|
47
56
|
|
|
48
|
-
help() {
|
|
57
|
+
help(): string {
|
|
49
58
|
const presetLen = Math.max(...Object.keys(presets).map(x => x.length));
|
|
50
59
|
const presetEntries = Object
|
|
51
60
|
.entries(presets)
|
|
@@ -65,11 +74,11 @@ ${this.getListOfFormats().map(x => color`* ${{ input: x }}`).join('\n')} `;
|
|
|
65
74
|
return this.cmd.extendedHelp ? `${presetText}\n${formatText}` : presetText;
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
getArgs() {
|
|
77
|
+
getArgs(): string {
|
|
69
78
|
return '[format-or-preset]';
|
|
70
79
|
}
|
|
71
80
|
|
|
72
|
-
async action(format: string) {
|
|
81
|
+
async action(format: string): Promise<void> {
|
|
73
82
|
if (!format) {
|
|
74
83
|
this.showHelp(new Error('Format is required'));
|
|
75
84
|
}
|
|
@@ -90,7 +99,7 @@ ${this.getListOfFormats().map(x => color`* ${{ input: x }}`).join('\n')} `;
|
|
|
90
99
|
|
|
91
100
|
const args = [
|
|
92
101
|
'run',
|
|
93
|
-
'--user', `${process.geteuid()}:${process.getgid()}`,
|
|
102
|
+
'--user', `${process.geteuid?.()}:${process.getgid?.()}`,
|
|
94
103
|
'-v', `${this.cmd.output}:/workspace`,
|
|
95
104
|
'-v', `${path.dirname(this.cmd.input)}:/input`,
|
|
96
105
|
'-it',
|
package/bin/cli-openapi_spec.ts
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import { BasePlugin } from '@travetto/cli/src/plugin-base';
|
|
1
|
+
import { BasePlugin, OptionConfig } from '@travetto/cli/src/plugin-base';
|
|
2
2
|
import { ExecUtil } from '@travetto/boot';
|
|
3
3
|
import { EnvInit } from '@travetto/base/bin/init';
|
|
4
4
|
|
|
5
|
+
type Options = {
|
|
6
|
+
output: OptionConfig<string>;
|
|
7
|
+
};
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* CLI for outputting the open api spec to a local file
|
|
7
11
|
*/
|
|
8
|
-
export class OpenApiSpecPlugin extends BasePlugin {
|
|
12
|
+
export class OpenApiSpecPlugin extends BasePlugin<Options> {
|
|
9
13
|
name = 'openapi:spec';
|
|
10
14
|
|
|
11
|
-
getOptions() {
|
|
15
|
+
getOptions(): Options {
|
|
12
16
|
return { output: this.option({ desc: 'Output files', def: './openapi.yml' }) };
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
envInit() {
|
|
19
|
+
envInit(): void {
|
|
16
20
|
EnvInit.init({
|
|
17
21
|
debug: '0',
|
|
18
22
|
set: { API_SPEC_OUTPUT: this.cmd.output }
|
|
19
23
|
});
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
async action() {
|
|
26
|
+
async action(): Promise<void> {
|
|
23
27
|
const result = await ExecUtil.workerMain(require.resolve('./generate')).message;
|
|
24
28
|
|
|
25
29
|
if (this.cmd.output === '-' || !this.cmd.output) {
|
package/bin/generate.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/openapi",
|
|
3
3
|
"displayName": "OpenAPI Specification",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.2",
|
|
5
5
|
"description": "OpenAPI integration support for the travetto framework",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"rest",
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
"directory": "module/openapi"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@travetto/config": "^2.
|
|
32
|
-
"@travetto/rest": "^2.
|
|
33
|
-
"@travetto/schema": "^2.
|
|
34
|
-
"@travetto/yaml": "^2.
|
|
31
|
+
"@travetto/config": "^2.2.2",
|
|
32
|
+
"@travetto/rest": "^2.2.2",
|
|
33
|
+
"@travetto/schema": "^2.2.2",
|
|
34
|
+
"@travetto/yaml": "^2.2.2",
|
|
35
35
|
"openapi3-ts": "^2.0.2"
|
|
36
36
|
},
|
|
37
37
|
"optionalPeerDependencies": {
|
|
38
|
-
"@travetto/cli": "^2.
|
|
38
|
+
"@travetto/cli": "^2.2.2"
|
|
39
39
|
},
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public"
|
package/src/config.ts
CHANGED
|
@@ -16,7 +16,7 @@ export class ApiInfoConfig {
|
|
|
16
16
|
license: LicenseObject = { name: AppManifest.info.license! };
|
|
17
17
|
termsOfService?: string;
|
|
18
18
|
title: string = AppManifest.info.name;
|
|
19
|
-
version
|
|
19
|
+
version: string = AppManifest.info.version ?? '0.0.0';
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -56,7 +56,7 @@ export class ApiSpecConfig {
|
|
|
56
56
|
*/
|
|
57
57
|
exposeAllSchemas: boolean = false;
|
|
58
58
|
|
|
59
|
-
async postConstruct() {
|
|
59
|
+
async postConstruct(): Promise<void> {
|
|
60
60
|
this.output = PathUtil.toUnix(this.output);
|
|
61
61
|
if (!this.output || this.output === '-') {
|
|
62
62
|
this.persist = false;
|
package/src/controller.ts
CHANGED
|
@@ -14,13 +14,13 @@ export class OpenApiController {
|
|
|
14
14
|
service: OpenApiService;
|
|
15
15
|
|
|
16
16
|
@Get('openapi.json')
|
|
17
|
-
async getSpec() {
|
|
18
|
-
return this.service.spec
|
|
17
|
+
async getSpec(): Promise<object> {
|
|
18
|
+
return this.service.spec; // Force output to be simple
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
@Get(/openapi[.]ya?ml$/)
|
|
22
22
|
@SetHeaders({ 'Content-Type': 'text/vnd.yaml' })
|
|
23
|
-
async getYmlSpec() {
|
|
23
|
+
async getYmlSpec(): Promise<string> {
|
|
24
24
|
return YamlUtil.serialize(this.service.spec); // Force output to be simple
|
|
25
25
|
}
|
|
26
26
|
}
|
package/src/service.ts
CHANGED
|
@@ -32,7 +32,7 @@ export class OpenApiService {
|
|
|
32
32
|
/**
|
|
33
33
|
* Reset specification
|
|
34
34
|
*/
|
|
35
|
-
async resetSpec() {
|
|
35
|
+
async resetSpec(): Promise<void> {
|
|
36
36
|
delete this._spec;
|
|
37
37
|
if (this.apiSpecConfig.persist) {
|
|
38
38
|
await this.persist();
|
|
@@ -42,7 +42,7 @@ export class OpenApiService {
|
|
|
42
42
|
/**
|
|
43
43
|
* Initialize after schemas are readied
|
|
44
44
|
*/
|
|
45
|
-
async postConstruct() {
|
|
45
|
+
async postConstruct(): Promise<void> {
|
|
46
46
|
ControllerRegistry.on(() => this.resetSpec());
|
|
47
47
|
SchemaRegistry.on(() => this.resetSpec());
|
|
48
48
|
|
|
@@ -62,7 +62,7 @@ export class OpenApiService {
|
|
|
62
62
|
...this.apiHostConfig,
|
|
63
63
|
info: { ...this.apiInfoConfig },
|
|
64
64
|
...new SpecGenerator().generate(this.apiSpecConfig)
|
|
65
|
-
}
|
|
65
|
+
};
|
|
66
66
|
}
|
|
67
67
|
return this._spec;
|
|
68
68
|
}
|
|
@@ -70,7 +70,7 @@ export class OpenApiService {
|
|
|
70
70
|
/**
|
|
71
71
|
* Persist to local file
|
|
72
72
|
*/
|
|
73
|
-
async persist() {
|
|
73
|
+
async persist(): Promise<void> {
|
|
74
74
|
console.debug('Generating OpenAPI Spec', { output: this.apiSpecConfig.output });
|
|
75
75
|
|
|
76
76
|
const output = this.apiSpecConfig.output.endsWith('.json') ?
|
package/src/spec-generate.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Readable } from 'stream';
|
|
2
2
|
import {
|
|
3
3
|
SchemaObject, SchemasObject, ParameterObject, OperationObject,
|
|
4
|
-
RequestBodyObject, TagObject, PathItemObject
|
|
4
|
+
RequestBodyObject, TagObject, PathItemObject, OpenAPIObject
|
|
5
5
|
} from 'openapi3-ts/src/model/OpenApi';
|
|
6
6
|
|
|
7
7
|
import { ControllerRegistry, EndpointConfig, ControllerConfig, ParamConfig, EndpointIOType } from '@travetto/rest';
|
|
8
|
-
import { Class
|
|
8
|
+
import { Class } from '@travetto/base';
|
|
9
9
|
import { SchemaRegistry, FieldConfig } from '@travetto/schema';
|
|
10
10
|
import { AllViewⲐ } from '@travetto/schema/src/internal/types';
|
|
11
11
|
|
|
@@ -14,6 +14,10 @@ import { OpenApiController } from './controller';
|
|
|
14
14
|
|
|
15
15
|
const DEFINITION = '#/components/schemas';
|
|
16
16
|
|
|
17
|
+
function isFieldConfig(val: object): val is FieldConfig {
|
|
18
|
+
return !!val && 'owner' in val && 'type' in val;
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* Spec generation utilities
|
|
19
23
|
*/
|
|
@@ -27,7 +31,7 @@ export class SpecGenerator {
|
|
|
27
31
|
* Get type id
|
|
28
32
|
* @param cls
|
|
29
33
|
*/
|
|
30
|
-
#getTypeId(cls: Class) {
|
|
34
|
+
#getTypeId(cls: Class): string {
|
|
31
35
|
return cls.name?.replace('ᚕsyn', '');
|
|
32
36
|
}
|
|
33
37
|
|
|
@@ -35,7 +39,7 @@ export class SpecGenerator {
|
|
|
35
39
|
* Get tag name
|
|
36
40
|
* @param cls
|
|
37
41
|
*/
|
|
38
|
-
#getTypeTag(cls: Class) {
|
|
42
|
+
#getTypeTag(cls: Class): string {
|
|
39
43
|
return cls.name.replace(/(Rest|Controller)$/, '');
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -84,11 +88,13 @@ export class SpecGenerator {
|
|
|
84
88
|
/**
|
|
85
89
|
* Get the type for a given class
|
|
86
90
|
*/
|
|
87
|
-
#getType(
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
#getType(fieldOrClass: FieldConfig | Class): Record<string, unknown> {
|
|
92
|
+
let field: { type: Class<unknown>, precision?: [number, number | undefined] };
|
|
93
|
+
if (!isFieldConfig(fieldOrClass)) {
|
|
94
|
+
field = { type: fieldOrClass };
|
|
95
|
+
} else {
|
|
96
|
+
field = fieldOrClass;
|
|
90
97
|
}
|
|
91
|
-
field = field as FieldConfig;
|
|
92
98
|
const out: Record<string, unknown> = {};
|
|
93
99
|
// Handle nested types
|
|
94
100
|
if (SchemaRegistry.has(field.type)) {
|
|
@@ -138,7 +144,7 @@ export class SpecGenerator {
|
|
|
138
144
|
/**
|
|
139
145
|
* Process schema field
|
|
140
146
|
*/
|
|
141
|
-
#processSchemaField(field: FieldConfig, required: string[]) {
|
|
147
|
+
#processSchemaField(field: FieldConfig, required: string[]): SchemaObject {
|
|
142
148
|
let prop: SchemaObject = this.#getType(field);
|
|
143
149
|
|
|
144
150
|
if (field.examples) {
|
|
@@ -155,10 +161,10 @@ export class SpecGenerator {
|
|
|
155
161
|
prop.minLength = field.minlength.n;
|
|
156
162
|
}
|
|
157
163
|
if (field.min) {
|
|
158
|
-
prop.minimum = field.min.n
|
|
164
|
+
prop.minimum = typeof field.min.n === 'number' ? field.min.n : field.min.n.getTime();
|
|
159
165
|
}
|
|
160
166
|
if (field.max) {
|
|
161
|
-
prop.maximum = field.max.n
|
|
167
|
+
prop.maximum = typeof field.max.n === 'number' ? field.max.n : field.max.n.getTime();
|
|
162
168
|
}
|
|
163
169
|
if (field.enum) {
|
|
164
170
|
prop.enum = field.enum.values;
|
|
@@ -184,7 +190,7 @@ export class SpecGenerator {
|
|
|
184
190
|
/**
|
|
185
191
|
* Process schema class
|
|
186
192
|
*/
|
|
187
|
-
#processSchema(type?: string | Class) {
|
|
193
|
+
#processSchema(type?: string | Class): string | undefined {
|
|
188
194
|
if (type === undefined || typeof type === 'string') {
|
|
189
195
|
return undefined;
|
|
190
196
|
}
|
|
@@ -231,13 +237,13 @@ export class SpecGenerator {
|
|
|
231
237
|
} else if (body.type === Readable || body.type === Buffer) {
|
|
232
238
|
return {
|
|
233
239
|
content: {
|
|
234
|
-
[mime ?? 'application/
|
|
240
|
+
[mime ?? 'application/octet-stream']: { type: 'string', format: 'binary' }
|
|
235
241
|
},
|
|
236
242
|
description: ''
|
|
237
243
|
};
|
|
238
244
|
} else {
|
|
239
245
|
const typeId = this.#getTypeId(body.type);
|
|
240
|
-
const typeRef = SchemaRegistry.has(body.type) ? this.#getType(body.type) : { type: body.type.name.toLowerCase()
|
|
246
|
+
const typeRef = SchemaRegistry.has(body.type) ? this.#getType(body.type) : { type: body.type.name.toLowerCase() };
|
|
241
247
|
return {
|
|
242
248
|
content: {
|
|
243
249
|
[mime ?? 'application/json']: {
|
|
@@ -252,7 +258,11 @@ export class SpecGenerator {
|
|
|
252
258
|
/**
|
|
253
259
|
* Process endpoint parameter
|
|
254
260
|
*/
|
|
255
|
-
#processEndpointParam(ep: EndpointConfig, param: ParamConfig, field: FieldConfig)
|
|
261
|
+
#processEndpointParam(ep: EndpointConfig, param: ParamConfig, field: FieldConfig): (
|
|
262
|
+
{ requestBody: RequestBodyObject } |
|
|
263
|
+
{ parameters: ParameterObject[] } |
|
|
264
|
+
undefined
|
|
265
|
+
) {
|
|
256
266
|
if (param.location) {
|
|
257
267
|
if (param.location === 'body') {
|
|
258
268
|
return {
|
|
@@ -262,7 +272,7 @@ export class SpecGenerator {
|
|
|
262
272
|
return { parameters: this.#schemaToDotParams(param.location, field) };
|
|
263
273
|
} else if (param.location !== 'context') {
|
|
264
274
|
const epParam: ParameterObject = {
|
|
265
|
-
in: param.location
|
|
275
|
+
in: param.location,
|
|
266
276
|
name: param.name || param.location,
|
|
267
277
|
description: field.description,
|
|
268
278
|
required: !!field.required?.active || false,
|
|
@@ -278,7 +288,7 @@ export class SpecGenerator {
|
|
|
278
288
|
/**
|
|
279
289
|
* Process controller endpoint
|
|
280
290
|
*/
|
|
281
|
-
processEndpoint(ctrl: ControllerConfig, ep: EndpointConfig) {
|
|
291
|
+
processEndpoint(ctrl: ControllerConfig, ep: EndpointConfig): void {
|
|
282
292
|
|
|
283
293
|
const tagName = ctrl.class.name.replace(/(Rest|Controller)$/, '');
|
|
284
294
|
|
|
@@ -298,14 +308,17 @@ export class SpecGenerator {
|
|
|
298
308
|
const schema = SchemaRegistry.getMethodSchema(ep.class, ep.handlerName);
|
|
299
309
|
for (const field of schema) {
|
|
300
310
|
const res = this.#processEndpointParam(ep, ep.params[field.index!], field);
|
|
301
|
-
if (res
|
|
302
|
-
(
|
|
311
|
+
if (res) {
|
|
312
|
+
if ('parameters' in res) {
|
|
313
|
+
(op.parameters ??= []).push(...res.parameters);
|
|
314
|
+
} else {
|
|
315
|
+
op.requestBody ??= res.requestBody;
|
|
316
|
+
}
|
|
303
317
|
}
|
|
304
|
-
op.requestBody ??= res?.requestBody;
|
|
305
318
|
}
|
|
306
319
|
|
|
307
320
|
const epPath = (
|
|
308
|
-
!ep.path ? '/' : typeof ep.path === 'string' ?
|
|
321
|
+
!ep.path ? '/' : typeof ep.path === 'string' ? ep.path : ep.path.source
|
|
309
322
|
).replace(/:([A-Za-z0-9_]+)\b/g, (__, param) => `{${param}}`);
|
|
310
323
|
|
|
311
324
|
const key = `${ctrl.basePath}${epPath}`.replace(/[\/]+/g, '/');
|
|
@@ -324,7 +337,7 @@ export class SpecGenerator {
|
|
|
324
337
|
/**
|
|
325
338
|
* Process each controller
|
|
326
339
|
*/
|
|
327
|
-
processController(cls: Class) {
|
|
340
|
+
processController(cls: Class): void {
|
|
328
341
|
const ctrl = ControllerRegistry.get(cls);
|
|
329
342
|
|
|
330
343
|
this.#tags.push({
|
|
@@ -340,7 +353,7 @@ export class SpecGenerator {
|
|
|
340
353
|
/**
|
|
341
354
|
* Generate full specification
|
|
342
355
|
*/
|
|
343
|
-
generate(config: Partial<ApiSpecConfig> = {}) {
|
|
356
|
+
generate(config: Partial<ApiSpecConfig> = {}): Pick<OpenAPIObject, 'tags' | 'paths' | 'components'> {
|
|
344
357
|
|
|
345
358
|
for (const cls of ControllerRegistry.getClasses()) {
|
|
346
359
|
for (const ep of ControllerRegistry.get(cls).endpoints) {
|