@trapi/swagger 0.2.10 → 0.2.14
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/specification/abstract.js +2 -2
- package/dist/specification/abstract.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/package.json +10 -7
- package/src/config/index.ts +0 -8
- package/src/config/utils/index.ts +0 -8
- package/src/config/utils/validator.ts +0 -94
- package/src/index.ts +0 -12
- package/src/metadata.ts +0 -58
- package/src/specification/abstract.ts +0 -250
- package/src/specification/index.ts +0 -11
- package/src/specification/type.ts +0 -328
- package/src/specification/utils.ts +0 -47
- package/src/specification/v2/index.ts +0 -463
- package/src/specification/v2/type.ts +0 -92
- package/src/specification/v3/index.ts +0 -448
- package/src/specification/v3/type.ts +0 -129
- package/src/type.ts +0 -14
- package/src/utils.ts +0 -28
- package/test/data/metadata.json +0 -1
- package/test/jest.config.js +0 -43
- package/test/tsconfig.json +0 -28
- package/test/unit/index.ts +0 -425
- package/tsconfig.build.json +0 -11
- package/tsconfig.json +0 -9
- package/writable/.gitignore +0 -3
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2021.
|
|
3
|
-
* Author Peter Placzek (tada5hi)
|
|
4
|
-
* For the full copyright and license information,
|
|
5
|
-
* view the LICENSE file that was distributed with this source code.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {URL} from "url";
|
|
9
|
-
import {hasOwnProperty, normalizePathParameters} from "@trapi/metadata-utils";
|
|
10
|
-
import {Method, Parameter, Property, Resolver, Response} from "@trapi/metadata";
|
|
11
|
-
import {Specification} from "../type";
|
|
12
|
-
import {SpecificationV3} from "./type";
|
|
13
|
-
import {removeFinalCharacter, removeRepeatingCharacter} from "../utils";
|
|
14
|
-
import {AbstractSpecGenerator} from "../abstract";
|
|
15
|
-
|
|
16
|
-
export class Version3SpecGenerator extends AbstractSpecGenerator<SpecificationV3.Spec, SpecificationV3.Schema> {
|
|
17
|
-
public getSwaggerSpec(): SpecificationV3.Spec {
|
|
18
|
-
return this.build();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public build() : SpecificationV3.Spec {
|
|
22
|
-
if(typeof this.spec !== 'undefined') {
|
|
23
|
-
return this.spec;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let spec: SpecificationV3.Spec = {
|
|
27
|
-
components: this.buildComponents(),
|
|
28
|
-
info: this.buildInfo(),
|
|
29
|
-
openapi: '3.0.0',
|
|
30
|
-
paths: this.buildPaths(),
|
|
31
|
-
servers: this.buildServers(),
|
|
32
|
-
tags: [],
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
if (this.config.specificationExtra) {
|
|
36
|
-
spec = require('merge').recursive(spec, this.config.specificationExtra);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return spec;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private buildComponents() {
|
|
43
|
-
const components = {
|
|
44
|
-
examples: {},
|
|
45
|
-
headers: {},
|
|
46
|
-
parameters: {},
|
|
47
|
-
requestBodies: {},
|
|
48
|
-
responses: {},
|
|
49
|
-
schemas: this.buildSchema(),
|
|
50
|
-
securitySchemes: {},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
if (this.config.securityDefinitions) {
|
|
54
|
-
components.securitySchemes = Version3SpecGenerator.translateSecurityDefinitions(this.config.securityDefinitions);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return components;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private static translateSecurityDefinitions(securityDefinitions: Specification.SecurityDefinitions) : Record<string, SpecificationV3.Security> {
|
|
61
|
-
const security : Record<string, SpecificationV3.Security> = {};
|
|
62
|
-
|
|
63
|
-
// tslint:disable-next-line:forin
|
|
64
|
-
for(const name in securityDefinitions) {
|
|
65
|
-
const securityDefinition : Specification.SecurityDefinition = securityDefinitions[name];
|
|
66
|
-
|
|
67
|
-
switch (securityDefinition.type) {
|
|
68
|
-
case "http":
|
|
69
|
-
security[name] = securityDefinition;
|
|
70
|
-
break;
|
|
71
|
-
case "oauth2":
|
|
72
|
-
security[name] = securityDefinition;
|
|
73
|
-
break;
|
|
74
|
-
case "apiKey":
|
|
75
|
-
security[name] = securityDefinition;
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return security;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private buildPaths() {
|
|
84
|
-
const paths: { [pathName: string]: Specification.Path<SpecificationV3.Operation, SpecificationV3.Parameter> } = {};
|
|
85
|
-
|
|
86
|
-
this.metadata.controllers.forEach(controller => {
|
|
87
|
-
// construct documentation using all methods except @Hidden
|
|
88
|
-
controller.methods
|
|
89
|
-
.filter(method => !method.hidden)
|
|
90
|
-
.forEach(method => {
|
|
91
|
-
let path = removeFinalCharacter(removeRepeatingCharacter(`/${controller.path}/${method.path}`, '/'), '/');
|
|
92
|
-
path = normalizePathParameters(path);
|
|
93
|
-
paths[path] = paths[path] || {};
|
|
94
|
-
this.buildMethod(controller.name, method, paths[path]);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
return paths;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private buildMethod(controllerName: string, method: Method, pathObject: any) {
|
|
102
|
-
const pathMethod: SpecificationV3.Operation = (pathObject[method.method] = this.buildOperation(controllerName, method));
|
|
103
|
-
pathMethod.description = method.description;
|
|
104
|
-
pathMethod.summary = method.summary;
|
|
105
|
-
pathMethod.tags = method.tags;
|
|
106
|
-
|
|
107
|
-
// Use operationId tag otherwise fallback to generated. Warning: This doesn't check uniqueness.
|
|
108
|
-
pathMethod.operationId = method.operationId || pathMethod.operationId;
|
|
109
|
-
|
|
110
|
-
if (method.deprecated) {
|
|
111
|
-
pathMethod.deprecated = method.deprecated;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (method.security) {
|
|
115
|
-
pathMethod.security = method.security as any[];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const bodyParams = method.parameters.filter(p => p.in === 'body');
|
|
119
|
-
const formParams = method.parameters.filter(p => p.in === 'formData');
|
|
120
|
-
|
|
121
|
-
pathMethod.parameters = method.parameters
|
|
122
|
-
.filter(p => {
|
|
123
|
-
return ['body', 'formData', 'request', 'body-prop', 'res'].indexOf(p.in) === -1;
|
|
124
|
-
})
|
|
125
|
-
.map(p => this.buildParameter(p));
|
|
126
|
-
|
|
127
|
-
if (bodyParams.length > 1) {
|
|
128
|
-
throw new Error('Only one body parameter allowed per controller method.');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (bodyParams.length > 0 && formParams.length > 0) {
|
|
132
|
-
throw new Error('Either body parameter or form parameters allowed per controller method - not both.');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (bodyParams.length > 0) {
|
|
136
|
-
pathMethod.requestBody = this.buildRequestBody(bodyParams[0]);
|
|
137
|
-
} else if (formParams.length > 0) {
|
|
138
|
-
pathMethod.requestBody = this.buildRequestBodyWithFormData(formParams);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
method.extensions.forEach(ext => (pathMethod[ext.key] = ext.value));
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private buildRequestBodyWithFormData(parameters: Parameter[]): SpecificationV3.RequestBody {
|
|
145
|
-
const required: string[] = [];
|
|
146
|
-
const properties: { [propertyName: string]: SpecificationV3.Schema } = {};
|
|
147
|
-
for (const parameter of parameters) {
|
|
148
|
-
const mediaType = this.buildMediaType(parameter);
|
|
149
|
-
properties[parameter.name] = mediaType.schema!;
|
|
150
|
-
if (parameter.required) {
|
|
151
|
-
required.push(parameter.name);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return {
|
|
155
|
-
required: required.length > 0,
|
|
156
|
-
content: {
|
|
157
|
-
'multipart/form-data': {
|
|
158
|
-
schema: {
|
|
159
|
-
type: 'object',
|
|
160
|
-
properties: properties,
|
|
161
|
-
// An empty list required: [] is not valid.
|
|
162
|
-
// If all properties are optional, do not specify the required keyword.
|
|
163
|
-
...(required && required.length && {required: required}),
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private buildRequestBody(parameter: Parameter): SpecificationV3.RequestBody {
|
|
171
|
-
const mediaType = this.buildMediaType(parameter);
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
description: parameter.description,
|
|
175
|
-
required: parameter.required,
|
|
176
|
-
content: {
|
|
177
|
-
'application/json': mediaType,
|
|
178
|
-
},
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
private buildMediaType(parameter: Parameter): SpecificationV3.MediaType {
|
|
183
|
-
const mediaType: SpecificationV3.MediaType = {
|
|
184
|
-
schema: this.getSwaggerType(parameter.type),
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
this.buildFromParameterExamples(mediaType, parameter);
|
|
188
|
-
|
|
189
|
-
return mediaType;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
protected buildOperation(controllerName: string, method: Method): SpecificationV3.Operation {
|
|
193
|
-
const swaggerResponses: { [name: string]: SpecificationV3.Response } = {};
|
|
194
|
-
|
|
195
|
-
method.responses.forEach((res: Response) => {
|
|
196
|
-
const name : string = res.status ?? 'default';
|
|
197
|
-
// no string key
|
|
198
|
-
swaggerResponses[name] = {
|
|
199
|
-
description: res.description,
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
if (res.schema && !Resolver.isVoidType(res.schema)) {
|
|
203
|
-
const contentKey : string = 'application/json';
|
|
204
|
-
swaggerResponses[name].content = {
|
|
205
|
-
[contentKey]: {
|
|
206
|
-
schema: this.getSwaggerType(res.schema) as SpecificationV3.Schema,
|
|
207
|
-
} as SpecificationV3.Schema,
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
if (res.examples) {
|
|
211
|
-
swaggerResponses[name].content[contentKey]['examples'] = {
|
|
212
|
-
default: {
|
|
213
|
-
value: res.examples
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (res.headers) {
|
|
220
|
-
const headers: { [name: string]: SpecificationV3.Header } = {};
|
|
221
|
-
if (res.headers.typeName === 'refObject') {
|
|
222
|
-
headers[res.headers.refName] = {
|
|
223
|
-
schema: this.getSwaggerTypeForReferenceType(res.headers) as SpecificationV3.Schema,
|
|
224
|
-
description: res.headers.description,
|
|
225
|
-
};
|
|
226
|
-
} else if (res.headers.typeName === 'nestedObjectLiteral') {
|
|
227
|
-
res.headers.properties.forEach((each: Property) => {
|
|
228
|
-
headers[each.name] = {
|
|
229
|
-
schema: this.getSwaggerType(each.type) as SpecificationV3.Schema,
|
|
230
|
-
description: each.description,
|
|
231
|
-
required: each.required,
|
|
232
|
-
};
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
swaggerResponses[res.name].headers = headers;
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
operationId: this.getOperationId(method.name),
|
|
242
|
-
responses: swaggerResponses,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
private buildParameter(source: Parameter): SpecificationV3.Parameter {
|
|
247
|
-
const parameter : SpecificationV3.Parameter = {
|
|
248
|
-
description: source.description,
|
|
249
|
-
in: source.in as Specification.ParameterInType,
|
|
250
|
-
name: source.name,
|
|
251
|
-
required: source.required,
|
|
252
|
-
schema: {
|
|
253
|
-
default: source.default,
|
|
254
|
-
format: undefined,
|
|
255
|
-
},
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
if (source.deprecated) {
|
|
259
|
-
parameter.deprecated = true;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const parameterType = this.getSwaggerType(source.type);
|
|
263
|
-
if (parameterType.format) {
|
|
264
|
-
parameter.schema.format = parameterType.format;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
hasOwnProperty(parameterType, '$ref') &&
|
|
269
|
-
parameterType.$ref
|
|
270
|
-
) {
|
|
271
|
-
parameter.schema = parameterType as SpecificationV3.Schema;
|
|
272
|
-
return parameter;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (source.type.typeName === 'any') {
|
|
276
|
-
parameter.schema.type = 'string';
|
|
277
|
-
} else {
|
|
278
|
-
if (parameterType.type) {
|
|
279
|
-
parameter.schema.type = parameterType.type as Specification.DataType;
|
|
280
|
-
}
|
|
281
|
-
parameter.schema.items = parameterType.items;
|
|
282
|
-
parameter.schema.enum = parameterType.enum;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
this.buildFromParameterExamples(parameter, source);
|
|
286
|
-
|
|
287
|
-
return parameter;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
private buildFromParameterExamples(
|
|
291
|
-
parameter: SpecificationV3.Parameter | SpecificationV3.MediaType,
|
|
292
|
-
sourceParameter: Parameter
|
|
293
|
-
) {
|
|
294
|
-
if (
|
|
295
|
-
(Array.isArray(sourceParameter.example) && sourceParameter.example.length === 1) ||
|
|
296
|
-
typeof sourceParameter.example === 'undefined'
|
|
297
|
-
) {
|
|
298
|
-
parameter.example = Array.isArray(sourceParameter.example) && sourceParameter.example.length === 1 ? sourceParameter.example[0] : undefined;
|
|
299
|
-
} else {
|
|
300
|
-
parameter.examples = {};
|
|
301
|
-
sourceParameter.example.forEach((example, index) =>
|
|
302
|
-
Object.assign(parameter.examples, {
|
|
303
|
-
[`Example ${index + 1}`]: { value: example } as Specification.Example,
|
|
304
|
-
}),
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
return parameter;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private buildServers() : SpecificationV3.Server[] {
|
|
311
|
-
const url = new URL(this.config.host || 'http://localhost:3000/');
|
|
312
|
-
let host : string = (url.host + url.pathname).replace(/([^:]\/)\/+/g, "$1");
|
|
313
|
-
host = host.substr(-1, 1) === '/' ? host.substr(0, host.length -1) : host;
|
|
314
|
-
|
|
315
|
-
return [
|
|
316
|
-
{
|
|
317
|
-
url: host,
|
|
318
|
-
}
|
|
319
|
-
];
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
private buildSchema() {
|
|
323
|
-
const schema: { [name: string]: SpecificationV3.Schema } = {};
|
|
324
|
-
Object.keys(this.metadata.referenceTypes).map(typeName => {
|
|
325
|
-
const referenceType = this.metadata.referenceTypes[typeName];
|
|
326
|
-
|
|
327
|
-
if (referenceType.typeName === 'refObject') {
|
|
328
|
-
const required = referenceType.properties.filter(p => p.required).map(p => p.name);
|
|
329
|
-
schema[referenceType.refName] = {
|
|
330
|
-
description: referenceType.description,
|
|
331
|
-
properties: this.buildProperties(referenceType.properties),
|
|
332
|
-
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
|
|
333
|
-
type: 'object',
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
if (referenceType.additionalProperties) {
|
|
337
|
-
schema[referenceType.refName].additionalProperties = this.getSwaggerType(referenceType.additionalProperties);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (referenceType.example) {
|
|
341
|
-
schema[referenceType.refName].example = referenceType.example;
|
|
342
|
-
}
|
|
343
|
-
} else if (referenceType.typeName === 'refEnum') {
|
|
344
|
-
const enumTypes = this.determineTypesUsedInEnum(referenceType.members);
|
|
345
|
-
|
|
346
|
-
if (enumTypes.size === 1) {
|
|
347
|
-
schema[referenceType.refName] = {
|
|
348
|
-
description: referenceType.description,
|
|
349
|
-
enum: referenceType.members,
|
|
350
|
-
type: enumTypes.has('string') ? 'string' : 'number',
|
|
351
|
-
};
|
|
352
|
-
if (referenceType.memberNames !== undefined && referenceType.members.length === referenceType.memberNames.length) {
|
|
353
|
-
schema[referenceType.refName]['x-enum-varnames'] = referenceType.memberNames;
|
|
354
|
-
}
|
|
355
|
-
} else {
|
|
356
|
-
schema[referenceType.refName] = {
|
|
357
|
-
description: referenceType.description,
|
|
358
|
-
anyOf: [
|
|
359
|
-
{
|
|
360
|
-
type: 'number',
|
|
361
|
-
enum: referenceType.members.filter(e => typeof e === 'number'),
|
|
362
|
-
},
|
|
363
|
-
{
|
|
364
|
-
type: 'string',
|
|
365
|
-
enum: referenceType.members.filter(e => typeof e === 'string'),
|
|
366
|
-
},
|
|
367
|
-
],
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
} else if (referenceType.typeName === 'refAlias') {
|
|
371
|
-
const swaggerType = this.getSwaggerType(referenceType.type);
|
|
372
|
-
const format = referenceType.format as Specification.DataFormat;
|
|
373
|
-
const validators = Object.keys(referenceType.validators)
|
|
374
|
-
.filter(key => {
|
|
375
|
-
return !key.startsWith('is') && key !== 'minDate' && key !== 'maxDate';
|
|
376
|
-
})
|
|
377
|
-
.reduce((acc, key) => {
|
|
378
|
-
return {
|
|
379
|
-
...acc,
|
|
380
|
-
[key]: referenceType.validators[key].value,
|
|
381
|
-
};
|
|
382
|
-
}, {});
|
|
383
|
-
|
|
384
|
-
schema[referenceType.refName] = {
|
|
385
|
-
...(swaggerType as SpecificationV3.Schema),
|
|
386
|
-
default: referenceType.default || swaggerType.default,
|
|
387
|
-
example: referenceType.example,
|
|
388
|
-
format: format || swaggerType.format,
|
|
389
|
-
description: referenceType.description,
|
|
390
|
-
...validators,
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (referenceType.deprecated) {
|
|
395
|
-
schema[referenceType.refName].deprecated = true;
|
|
396
|
-
}
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
return schema;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
protected getSwaggerTypeForIntersectionType(type: Resolver.IntersectionType) : SpecificationV3.Schema {
|
|
403
|
-
return { allOf: type.members.map((x: Resolver.Type) => this.getSwaggerType(x)) };
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
protected buildProperties<T>(properties: Property[]): Record<string, SpecificationV3.Schema> {
|
|
407
|
-
const result: { [propertyName: string]: SpecificationV3.Schema } = {};
|
|
408
|
-
|
|
409
|
-
properties.forEach(property => {
|
|
410
|
-
const swaggerType = this.getSwaggerType(property.type) as SpecificationV3.Schema;
|
|
411
|
-
const format = property.format as Specification.DataFormat;
|
|
412
|
-
swaggerType.description = property.description;
|
|
413
|
-
swaggerType.example = property.example;
|
|
414
|
-
swaggerType.format = format || swaggerType.format;
|
|
415
|
-
|
|
416
|
-
if (!swaggerType.$ref) {
|
|
417
|
-
swaggerType.default = property.default;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (property.deprecated) {
|
|
421
|
-
swaggerType.deprecated = true;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
result[property.name] = swaggerType;
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
return result;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
protected getSwaggerTypeForEnumType(enumType: Resolver.EnumType): SpecificationV3.Schema {
|
|
431
|
-
const types = this.determineTypesUsedInEnum(enumType.members);
|
|
432
|
-
|
|
433
|
-
if (types.size === 1) {
|
|
434
|
-
const type = types.values().next().value;
|
|
435
|
-
const nullable = !!enumType.members.includes(null);
|
|
436
|
-
return { type: type, enum: enumType.members.map(member => (member === null ? null : String(member))), nullable: nullable };
|
|
437
|
-
} else {
|
|
438
|
-
const valuesDelimited = Array.from(types).join(',');
|
|
439
|
-
throw new Error(`Enums can only have string or number values, but enum had ${valuesDelimited}`);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
protected getSwaggerTypeForReferenceType(referenceType: Resolver.ReferenceType): SpecificationV3.Schema {
|
|
444
|
-
return {
|
|
445
|
-
$ref: `#/components/schemas/${referenceType.refName}`
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2021.
|
|
3
|
-
* Author Peter Placzek (tada5hi)
|
|
4
|
-
* For the full copyright and license information,
|
|
5
|
-
* view the LICENSE file that was distributed with this source code.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {Specification} from "../type";
|
|
9
|
-
|
|
10
|
-
export namespace SpecificationV3 {
|
|
11
|
-
import BaseParameter = Specification.BaseParameter;
|
|
12
|
-
|
|
13
|
-
export interface Spec extends Specification.BaseSpec {
|
|
14
|
-
openapi: '3.0.0';
|
|
15
|
-
servers: Server[];
|
|
16
|
-
components: Components;
|
|
17
|
-
paths: Paths;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Server
|
|
21
|
-
export interface Server {
|
|
22
|
-
url: string;
|
|
23
|
-
description?: string;
|
|
24
|
-
variables?: Record<string, Variable>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface Variable {
|
|
28
|
-
enum?: string[];
|
|
29
|
-
description?: string;
|
|
30
|
-
default: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Components
|
|
34
|
-
export interface Components {
|
|
35
|
-
callbacks?: { [name: string]: any };
|
|
36
|
-
examples?: { [name: string]: Specification.Example | string };
|
|
37
|
-
headers?: { [name: string]: any };
|
|
38
|
-
links?: { [name: string]: any };
|
|
39
|
-
parameters?: { [name: string]: Parameter };
|
|
40
|
-
requestBodies?: { [name: string]: any };
|
|
41
|
-
responses?: { [name: string]: Response };
|
|
42
|
-
schemas?: { [name: string]: Schema };
|
|
43
|
-
securitySchemes?: { [name: string]: Security };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// Paths
|
|
48
|
-
export interface Paths {
|
|
49
|
-
[key: string] : Specification.Path<Operation, Parameter>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type Parameter = Specification.Parameter<Schema>;
|
|
53
|
-
|
|
54
|
-
export interface Operation extends Specification.BaseOperation<Parameter, Response, Security> {
|
|
55
|
-
requestBody?: RequestBody;
|
|
56
|
-
[key: string]: unknown;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface Response extends Specification.BaseResponse {
|
|
60
|
-
content?: { [name: string]: Schema & Specification.Examples };
|
|
61
|
-
headers?: { [name: string]: Header };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type Header = Partial<Pick<BaseParameter<Schema>, 'name' | 'in'>> & Omit<BaseParameter<Schema>, 'name' | 'in'>;
|
|
65
|
-
|
|
66
|
-
export interface RequestBody {
|
|
67
|
-
content: { [name: string]: MediaType };
|
|
68
|
-
description?: string;
|
|
69
|
-
required?: boolean;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface MediaType {
|
|
73
|
-
schema?: Schema;
|
|
74
|
-
example?: unknown;
|
|
75
|
-
examples?: { [name: string]: Specification.Example | string };
|
|
76
|
-
encoding?: { [name: string]: any };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// tslint:disable-next-line:no-shadowed-variable
|
|
80
|
-
export interface Schema extends Specification.BaseSchema<Schema> {
|
|
81
|
-
nullable?: boolean;
|
|
82
|
-
anyOf?: Schema[];
|
|
83
|
-
allOf?: Schema[];
|
|
84
|
-
deprecated?: boolean;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// tslint:disable-next-line:no-shadowed-variable
|
|
88
|
-
export interface BasicSecurity extends Specification.BaseSecurity {
|
|
89
|
-
type: 'http';
|
|
90
|
-
schema: 'basic';
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface OAuth2Security extends Specification.BaseSecurity {
|
|
94
|
-
type: 'oauth2';
|
|
95
|
-
flows: {
|
|
96
|
-
implicit?: OAuth2ImplicitFlow,
|
|
97
|
-
password?: OAuth2PasswordFlow,
|
|
98
|
-
authorizationCode?: OAuth2AuthorizationCodeFlow,
|
|
99
|
-
clientCredentials?: OAuth2ClientCredentialsFlow
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface Oauth2BaseFlow {
|
|
104
|
-
scopes?: Record<string, string>;
|
|
105
|
-
refreshUrl?: string;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface OAuth2ImplicitFlow extends Oauth2BaseFlow{
|
|
109
|
-
authorizationUrl: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export interface OAuth2PasswordFlow extends Oauth2BaseFlow{
|
|
113
|
-
tokenUrl: string;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export interface OAuth2AuthorizationCodeFlow extends Oauth2BaseFlow{
|
|
117
|
-
authorizationUrl: string;
|
|
118
|
-
tokenUrl: string;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface OAuth2ClientCredentialsFlow extends Oauth2BaseFlow{
|
|
122
|
-
tokenUrl: string;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export type Security =
|
|
126
|
-
BasicSecurity |
|
|
127
|
-
OAuth2Security |
|
|
128
|
-
Specification.ApiKeySecurity;
|
|
129
|
-
}
|
package/src/type.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2021.
|
|
3
|
-
* Author Peter Placzek (tada5hi)
|
|
4
|
-
* For the full copyright and license information,
|
|
5
|
-
* view the LICENSE file that was distributed with this source code.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type SwaggerDocFormatType = 'yaml' | 'json';
|
|
9
|
-
|
|
10
|
-
export interface SwaggerDocFormatData {
|
|
11
|
-
path: string;
|
|
12
|
-
name: string;
|
|
13
|
-
content?: string;
|
|
14
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2021.
|
|
3
|
-
* Author Peter Placzek (tada5hi)
|
|
4
|
-
* For the full copyright and license information,
|
|
5
|
-
* view the LICENSE file that was distributed with this source code.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {CompilerOptions} from 'typescript';
|
|
9
|
-
import {Config as MetadataConfig, createMetadataGenerator} from "@trapi/metadata";
|
|
10
|
-
import {createSpecGenerator, Specification} from "./specification";
|
|
11
|
-
import {SwaggerDocFormatData, SwaggerDocFormatType} from "./type";
|
|
12
|
-
|
|
13
|
-
export async function generateDocumentation(
|
|
14
|
-
config: {
|
|
15
|
-
metadata: MetadataConfig,
|
|
16
|
-
swagger: Specification.Config
|
|
17
|
-
},
|
|
18
|
-
tsConfig?: CompilerOptions | boolean
|
|
19
|
-
): Promise<Record<SwaggerDocFormatType, SwaggerDocFormatData>> {
|
|
20
|
-
const metadataGenerator = createMetadataGenerator(config.metadata, tsConfig);
|
|
21
|
-
|
|
22
|
-
const metadata = metadataGenerator.generate();
|
|
23
|
-
|
|
24
|
-
const specGenerator = createSpecGenerator(metadata, config.swagger);
|
|
25
|
-
|
|
26
|
-
specGenerator.build();
|
|
27
|
-
return await specGenerator.save();
|
|
28
|
-
}
|