@trapi/swagger 1.0.0-alpha.11 → 1.0.0-alpha.12

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.
Files changed (57) hide show
  1. package/dist/config/index.d.ts +2 -2
  2. package/dist/config/index.js +24 -24
  3. package/dist/config/type.d.ts +82 -82
  4. package/dist/config/type.js +8 -8
  5. package/dist/config/utils.d.ts +2 -2
  6. package/dist/config/utils.js +53 -53
  7. package/dist/constants.d.ts +14 -14
  8. package/dist/constants.js +26 -26
  9. package/dist/generator/abstract.d.ts +34 -34
  10. package/dist/generator/abstract.js +251 -251
  11. package/dist/generator/index.d.ts +4 -4
  12. package/dist/generator/index.js +26 -26
  13. package/dist/generator/module.d.ts +13 -13
  14. package/dist/generator/module.js +34 -34
  15. package/dist/generator/v2/index.d.ts +1 -1
  16. package/dist/generator/v2/index.js +23 -23
  17. package/dist/generator/v2/module.d.ts +24 -24
  18. package/dist/generator/v2/module.js +516 -516
  19. package/dist/generator/v3/index.d.ts +1 -1
  20. package/dist/generator/v3/index.js +23 -23
  21. package/dist/generator/v3/module.d.ts +29 -29
  22. package/dist/generator/v3/module.js +498 -498
  23. package/dist/index.d.ts +7 -7
  24. package/dist/index.js +29 -29
  25. package/dist/metadata.d.ts +3 -3
  26. package/dist/metadata.js +13 -13
  27. package/dist/schema/constants.d.ts +27 -27
  28. package/dist/schema/constants.js +39 -39
  29. package/dist/schema/index.d.ts +4 -4
  30. package/dist/schema/index.js +26 -26
  31. package/dist/schema/type.d.ts +138 -138
  32. package/dist/schema/type.js +8 -8
  33. package/dist/schema/v2/constants.d.ts +7 -7
  34. package/dist/schema/v2/constants.js +17 -17
  35. package/dist/schema/v2/index.d.ts +2 -2
  36. package/dist/schema/v2/index.js +24 -24
  37. package/dist/schema/v2/type.d.ts +115 -115
  38. package/dist/schema/v2/type.js +8 -8
  39. package/dist/schema/v3/constants.d.ts +6 -6
  40. package/dist/schema/v3/constants.js +16 -16
  41. package/dist/schema/v3/index.d.ts +2 -2
  42. package/dist/schema/v3/index.js +24 -24
  43. package/dist/schema/v3/type.d.ts +158 -158
  44. package/dist/schema/v3/type.js +8 -8
  45. package/dist/type.d.ts +48 -48
  46. package/dist/type.js +8 -8
  47. package/dist/utils/character.d.ts +2 -2
  48. package/dist/utils/character.js +20 -20
  49. package/dist/utils/index.d.ts +4 -4
  50. package/dist/utils/index.js +26 -26
  51. package/dist/utils/object.d.ts +1 -1
  52. package/dist/utils/object.js +14 -14
  53. package/dist/utils/path.d.ts +1 -1
  54. package/dist/utils/path.js +20 -20
  55. package/dist/utils/value.d.ts +1 -1
  56. package/dist/utils/value.js +25 -25
  57. package/package.json +4 -4
@@ -1,517 +1,517 @@
1
- "use strict";
2
- /*
3
- * Copyright (c) 2021-2023.
4
- * Author Peter Placzek (tada5hi)
5
- * For the full copyright and license information,
6
- * view the LICENSE file that was distributed with this source code.
7
- */
8
- var __importDefault = (this && this.__importDefault) || function (mod) {
9
- return (mod && mod.__esModule) ? mod : { "default": mod };
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.V2Generator = void 0;
13
- const metadata_1 = require("@trapi/metadata");
14
- const node_path_1 = __importDefault(require("node:path"));
15
- const node_url_1 = require("node:url");
16
- const smob_1 = require("smob");
17
- const schema_1 = require("../../schema");
18
- const utils_1 = require("../../utils");
19
- const abstract_1 = require("../abstract");
20
- class V2Generator extends abstract_1.AbstractSpecGenerator {
21
- async build() {
22
- if (typeof this.spec !== 'undefined') {
23
- return this.spec;
24
- }
25
- let spec = {
26
- definitions: this.buildSchemasForReferenceTypes(),
27
- info: this.buildInfo(),
28
- paths: this.buildPaths(),
29
- swagger: '2.0',
30
- };
31
- spec.securityDefinitions = this.config.securityDefinitions ?
32
- V2Generator.translateSecurityDefinitions(this.config.securityDefinitions) :
33
- {};
34
- if (this.config.consumes) {
35
- spec.consumes = this.config.consumes;
36
- }
37
- if (this.config.produces) {
38
- spec.produces = this.config.produces;
39
- }
40
- if (this.config.servers &&
41
- this.config.servers.length > 0) {
42
- const url = new node_url_1.URL(this.config.servers[0].url, 'http://localhost:3000/');
43
- spec.host = url.host;
44
- if (url.pathname) {
45
- spec.basePath = url.pathname;
46
- }
47
- }
48
- if (this.config.specificationExtra) {
49
- spec = (0, smob_1.merge)(spec, this.config.specificationExtra);
50
- }
51
- this.spec = spec;
52
- await this.save();
53
- return spec;
54
- }
55
- static translateSecurityDefinitions(securityDefinitions) {
56
- const definitions = {};
57
- const keys = Object.keys(securityDefinitions);
58
- for (let i = 0; i < keys.length; i++) {
59
- const securityDefinition = securityDefinitions[keys[i]];
60
- switch (securityDefinition.type) {
61
- case 'http':
62
- if (securityDefinition.schema === 'basic') {
63
- definitions[keys[i]] = {
64
- type: 'basic',
65
- };
66
- }
67
- break;
68
- case 'apiKey':
69
- definitions[keys[i]] = securityDefinition;
70
- break;
71
- case 'oauth2':
72
- if (securityDefinition.flows.implicit) {
73
- definitions[`${keys[i]}Implicit`] = {
74
- type: 'oauth2',
75
- flow: 'implicit',
76
- authorizationUrl: securityDefinition.flows.implicit.authorizationUrl,
77
- scopes: securityDefinition.flows.implicit.scopes,
78
- };
79
- }
80
- if (securityDefinition.flows.password) {
81
- definitions[`${keys[i]}Implicit`] = {
82
- type: 'oauth2',
83
- flow: 'password',
84
- tokenUrl: securityDefinition.flows.password.tokenUrl,
85
- scopes: securityDefinition.flows.password.scopes,
86
- };
87
- }
88
- if (securityDefinition.flows.authorizationCode) {
89
- definitions[`${keys[i]}AccessCode`] = {
90
- type: 'oauth2',
91
- flow: 'accessCode',
92
- tokenUrl: securityDefinition.flows.authorizationCode.tokenUrl,
93
- authorizationUrl: securityDefinition.flows.authorizationCode.authorizationUrl,
94
- scopes: securityDefinition.flows.authorizationCode.scopes,
95
- };
96
- }
97
- if (securityDefinition.flows.clientCredentials) {
98
- definitions[`${keys[i]}Application`] = {
99
- type: 'oauth2',
100
- flow: 'application',
101
- tokenUrl: securityDefinition.flows.clientCredentials.tokenUrl,
102
- scopes: securityDefinition.flows.clientCredentials.scopes,
103
- };
104
- }
105
- break;
106
- }
107
- }
108
- return definitions;
109
- }
110
- buildSchemaForRefObject(referenceType) {
111
- const required = referenceType.properties
112
- .filter((p) => p.required && !this.isUndefinedProperty(p))
113
- .map((p) => p.name);
114
- const output = {
115
- description: referenceType.description,
116
- properties: this.buildProperties(referenceType.properties),
117
- required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
118
- type: schema_1.DataTypeName.OBJECT,
119
- };
120
- if (referenceType.additionalProperties) {
121
- output.additionalProperties = true;
122
- }
123
- if (referenceType.example) {
124
- output.example = referenceType.example;
125
- }
126
- return output;
127
- }
128
- buildSchemaForRefEnum(referenceType) {
129
- const output = {
130
- description: referenceType.description,
131
- enum: referenceType.members,
132
- type: this.decideEnumType(referenceType.members),
133
- };
134
- if (referenceType.memberNames !== undefined && referenceType.members.length === referenceType.memberNames.length) {
135
- output['x-enum-varnames'] = referenceType.memberNames;
136
- }
137
- return output;
138
- }
139
- buildSchemaForRefAlias(referenceType) {
140
- const swaggerType = this.getSchemaForType(referenceType.type);
141
- const format = referenceType.format;
142
- return {
143
- ...swaggerType,
144
- default: referenceType.default || swaggerType.default,
145
- example: referenceType.example,
146
- format: format || swaggerType.format,
147
- description: referenceType.description,
148
- ...this.transformValidators(referenceType.validators),
149
- };
150
- }
151
- /*
152
- Path & Parameter ( + utils)
153
- */
154
- buildPaths() {
155
- const output = {};
156
- const unique = (input) => [...new Set(input)];
157
- this.metadata.controllers.forEach((controller) => {
158
- controller.methods.forEach((method) => {
159
- let fullPath = node_path_1.default.posix.join('/', (controller.path ? controller.path : ''), method.path);
160
- fullPath = (0, utils_1.normalizePathParameters)(fullPath);
161
- method.consumes = unique([...controller.consumes, ...method.consumes]);
162
- method.produces = unique([...controller.produces, ...method.produces]);
163
- method.tags = unique([...controller.tags, ...method.tags]);
164
- method.security = method.security || controller.security;
165
- // todo: unique for objects
166
- method.responses = unique([...controller.responses, ...method.responses]);
167
- output[fullPath] = output[fullPath] || {};
168
- output[fullPath][method.method] = this.buildMethod(method);
169
- });
170
- });
171
- return output;
172
- }
173
- buildMethod(method) {
174
- const output = this.buildOperation(method);
175
- output.consumes = this.buildMethodConsumes(method);
176
- output.description = method.description;
177
- if (method.summary) {
178
- output.summary = method.summary;
179
- }
180
- if (method.deprecated) {
181
- output.deprecated = method.deprecated;
182
- }
183
- if (method.tags.length) {
184
- output.tags = method.tags;
185
- }
186
- if (method.security) {
187
- output.security = method.security;
188
- }
189
- const parameters = this.groupParameters(method.parameters);
190
- output.parameters = [
191
- ...(parameters[metadata_1.ParameterSource.PATH] || []),
192
- ...(parameters[metadata_1.ParameterSource.QUERY_PROP] || []),
193
- ...(parameters[metadata_1.ParameterSource.HEADER] || []),
194
- ...(parameters[metadata_1.ParameterSource.FORM_DATA] || []),
195
- ].map((p) => this.buildParameter(p));
196
- // ignore ParameterSource.QUERY!
197
- // ------------------------------------------------------
198
- const bodyParameters = (parameters[metadata_1.ParameterSource.BODY] || []);
199
- if (bodyParameters.length > 1) {
200
- throw new Error('Only one body parameter allowed per controller method.');
201
- }
202
- const bodyParameter = bodyParameters.length > 0 ?
203
- this.buildParameter(bodyParameters[0]) :
204
- undefined;
205
- const bodyPropParams = parameters[metadata_1.ParameterSource.BODY_PROP] || [];
206
- if (bodyPropParams.length > 0) {
207
- const schema = {
208
- type: schema_1.DataTypeName.OBJECT,
209
- title: 'Body',
210
- properties: {},
211
- };
212
- const required = [];
213
- for (let i = 0; i < bodyPropParams.length; i++) {
214
- const bodyProp = this.getSchemaForType(bodyPropParams[i].type);
215
- bodyProp.default = bodyPropParams[i].default;
216
- bodyProp.description = bodyPropParams[i].description;
217
- bodyProp.example = bodyPropParams[i].examples;
218
- if (bodyProp.required) {
219
- required.push(bodyPropParams[i].name);
220
- }
221
- schema.properties[bodyPropParams[i].name] = bodyProp;
222
- }
223
- if (bodyParameter &&
224
- bodyParameter.in === schema_1.ParameterSourceV2.BODY) {
225
- if (bodyParameter.schema.type === schema_1.DataTypeName.OBJECT) {
226
- bodyParameter.schema.properties = {
227
- ...(bodyParameter.schema.properties || {}),
228
- ...schema.properties,
229
- };
230
- bodyParameter.schema.required = [
231
- ...(bodyParameter.schema.required || []),
232
- ...required,
233
- ];
234
- }
235
- else {
236
- bodyParameter.schema = schema;
237
- }
238
- output.parameters.push(bodyParameter);
239
- }
240
- else {
241
- const parameter = {
242
- in: schema_1.ParameterSourceV2.BODY,
243
- name: 'body',
244
- schema,
245
- };
246
- if (required.length) {
247
- parameter.schema.required = required;
248
- }
249
- output.parameters.push(parameter);
250
- }
251
- }
252
- else if (bodyParameter) {
253
- output.parameters.push(bodyParameter);
254
- }
255
- for (let i = 0; i < method.extensions.length; i++) {
256
- output[method.extensions[i].key] = method.extensions[i].value;
257
- }
258
- return output;
259
- }
260
- transformParameterSource(source) {
261
- if (source === metadata_1.ParameterSource.BODY) {
262
- return schema_1.ParameterSourceV2.BODY;
263
- }
264
- if (source === metadata_1.ParameterSource.FORM_DATA) {
265
- return schema_1.ParameterSourceV2.FORM_DATA;
266
- }
267
- if (source === metadata_1.ParameterSource.HEADER) {
268
- return schema_1.ParameterSourceV2.HEADER;
269
- }
270
- if (source === metadata_1.ParameterSource.PATH) {
271
- return schema_1.ParameterSourceV2.PATH;
272
- }
273
- if (source === metadata_1.ParameterSource.QUERY || source === metadata_1.ParameterSource.QUERY_PROP) {
274
- return schema_1.ParameterSourceV2.QUERY;
275
- }
276
- return undefined;
277
- }
278
- buildParameter(input) {
279
- const sourceIn = this.transformParameterSource(input.in);
280
- if (!sourceIn) {
281
- throw new Error(`The parameter source "${input.in}" is not valid for generating a document.`);
282
- }
283
- const parameter = {
284
- description: input.description,
285
- in: sourceIn,
286
- name: input.name,
287
- required: input.required,
288
- };
289
- if (input.in !== metadata_1.ParameterSource.BODY &&
290
- (0, metadata_1.isRefEnumType)(input.type)) {
291
- input.type = {
292
- typeName: metadata_1.TypeName.ENUM,
293
- members: input.type.members,
294
- };
295
- }
296
- const parameterType = this.getSchemaForType(input.type);
297
- if (parameter.in !== schema_1.ParameterSourceV2.BODY &&
298
- parameterType.format) {
299
- parameter.format = parameterType.format;
300
- }
301
- // collectionFormat, might be valid for all parameters (if value != multi)
302
- if ((parameter.in === schema_1.ParameterSourceV2.FORM_DATA || parameter.in === schema_1.ParameterSourceV2.QUERY) &&
303
- (input.type.typeName === metadata_1.TypeName.ARRAY || parameterType.type === schema_1.DataTypeName.ARRAY)) {
304
- parameter.collectionFormat = input.collectionFormat || this.config.collectionFormat || 'multi';
305
- }
306
- if (parameter.in === schema_1.ParameterSourceV2.BODY) {
307
- if ((input.type.typeName === metadata_1.TypeName.ARRAY || parameterType.type === schema_1.DataTypeName.ARRAY)) {
308
- parameter.schema = {
309
- items: parameterType.items,
310
- type: schema_1.DataTypeName.ARRAY,
311
- };
312
- }
313
- else if (input.type.typeName === metadata_1.TypeName.ANY) {
314
- parameter.schema = { type: schema_1.DataTypeName.OBJECT };
315
- }
316
- else {
317
- parameter.schema = parameterType;
318
- }
319
- parameter.schema = {
320
- ...parameter.schema,
321
- ...this.transformValidators(input.validators),
322
- };
323
- return parameter;
324
- }
325
- // todo: this is eventually illegal
326
- (0, smob_1.merge)(parameter, this.transformValidators(input.validators));
327
- if (input.type.typeName === metadata_1.TypeName.ANY) {
328
- parameter.type = schema_1.DataTypeName.STRING;
329
- }
330
- else if (parameterType.type) {
331
- parameter.type = parameterType.type;
332
- }
333
- if (parameterType.items) {
334
- parameter.items = parameterType.items;
335
- }
336
- if (parameterType.enum) {
337
- parameter.enum = parameterType.enum;
338
- }
339
- if (typeof input.default !== 'undefined') {
340
- parameter.default = input.default;
341
- }
342
- return parameter;
343
- }
344
- buildMethodConsumes(method) {
345
- if (method.consumes &&
346
- method.consumes.length > 0) {
347
- return method.consumes;
348
- }
349
- if (this.hasFileParams(method)) {
350
- return ['multipart/form-data'];
351
- }
352
- if (this.hasFormParams(method)) {
353
- return ['application/x-www-form-urlencoded'];
354
- }
355
- if (this.supportsBodyParameters(method.method)) {
356
- return ['application/json'];
357
- }
358
- return [];
359
- }
360
- hasFileParams(method) {
361
- return method.parameters.some((p) => (p.in === metadata_1.ParameterSource.FORM_DATA && p.type.typeName === 'file'));
362
- }
363
- hasFormParams(method) {
364
- return method.parameters.some((p) => (p.in === metadata_1.ParameterSource.FORM_DATA));
365
- }
366
- supportsBodyParameters(method) {
367
- return ['post', 'put', 'patch'].some((m) => m === method);
368
- }
369
- /*
370
- Swagger Type ( + utils)
371
- */
372
- getSchemaForEnumType(enumType) {
373
- const type = this.decideEnumType(enumType.members);
374
- const nullable = !!enumType.members.includes(null);
375
- return {
376
- type,
377
- enum: enumType.members.map((member) => (0, utils_1.transformValueTo)(type, member)),
378
- 'x-nullable': nullable,
379
- };
380
- }
381
- getSchemaForIntersectionType(type) {
382
- // tslint:disable-next-line:no-shadowed-variable
383
- const properties = type.members.reduce((acc, type) => {
384
- if ((0, metadata_1.isRefObjectType)(type)) {
385
- const refType = this.metadata.referenceTypes[type.refName];
386
- const props = refType &&
387
- refType.properties &&
388
- refType.properties.reduce((pAcc, prop) => ({
389
- ...pAcc,
390
- [prop.name]: this.getSchemaForType(prop.type),
391
- }), {});
392
- return { ...acc, ...props };
393
- }
394
- return { ...acc };
395
- }, {});
396
- return { type: schema_1.DataTypeName.OBJECT, properties };
397
- }
398
- getSchemaForReferenceType(referenceType) {
399
- return { $ref: `#/definitions/${referenceType.refName}` };
400
- }
401
- getSchemaForUnionType(type) {
402
- const members = [];
403
- const enumTypeMember = { typeName: metadata_1.TypeName.ENUM, members: [] };
404
- for (let i = 0; i < type.members.length; i++) {
405
- const member = type.members[i];
406
- if ((0, metadata_1.isEnumType)(member)) {
407
- enumTypeMember.members.push(...member.members);
408
- }
409
- if (!(0, metadata_1.isAnyType)(member) &&
410
- !(0, metadata_1.isUndefinedType)(member) &&
411
- !(0, metadata_1.isEnumType)(member)) {
412
- members.push(member);
413
- }
414
- }
415
- if (members.length === 0 &&
416
- enumTypeMember.members.length > 0) {
417
- return this.getSchemaForEnumType(enumTypeMember);
418
- }
419
- const isNullEnum = enumTypeMember.members.every((member) => member === null);
420
- if (members.length === 1) {
421
- if (isNullEnum) {
422
- const memberType = this.getSchemaForType(members[0]);
423
- if (memberType.$ref) {
424
- return memberType;
425
- }
426
- memberType['x-nullable'] = true;
427
- return memberType;
428
- }
429
- if (enumTypeMember.members.length === 0) {
430
- return this.getSchemaForType(members[0]);
431
- }
432
- }
433
- return { type: schema_1.DataTypeName.OBJECT, ...(isNullEnum ? { 'x-nullable': true } : {}) };
434
- }
435
- buildProperties(properties) {
436
- const output = {};
437
- properties.forEach((property) => {
438
- const swaggerType = this.getSchemaForType(property.type);
439
- swaggerType.description = property.description;
440
- swaggerType.example = property.example;
441
- swaggerType.format = property.format || swaggerType.format;
442
- if (!(0, utils_1.hasOwnProperty)(swaggerType, '$ref') || !swaggerType.$ref) {
443
- swaggerType.description = property.description;
444
- }
445
- if (property.deprecated) {
446
- swaggerType['x-deprecated'] = true;
447
- }
448
- if (property.extensions) {
449
- for (let i = 0; i < property.extensions.length; i++) {
450
- swaggerType[property.extensions[i].key] = property.extensions[i].value;
451
- }
452
- }
453
- const extensions = this.transformExtensions(property.extensions);
454
- const validators = this.transformValidators(property.validators);
455
- output[property.name] = {
456
- ...swaggerType,
457
- ...validators,
458
- ...extensions,
459
- };
460
- });
461
- return output;
462
- }
463
- buildOperation(method) {
464
- const operation = {
465
- operationId: this.getOperationId(method.name),
466
- consumes: method.consumes || [],
467
- produces: method.produces || [],
468
- responses: {},
469
- security: method.security || [],
470
- };
471
- const produces = [];
472
- method.responses.forEach((res) => {
473
- operation.responses[res.status] = {
474
- description: res.description,
475
- };
476
- if (res.schema &&
477
- !(0, metadata_1.isVoidType)(res.schema)) {
478
- if (res.produces) {
479
- produces.push(...res.produces);
480
- }
481
- else if ((0, metadata_1.isBinaryType)(res.schema)) {
482
- produces.push('application/octet-stream');
483
- }
484
- operation.responses[res.status].schema = this.getSchemaForType(res.schema);
485
- }
486
- if (res.examples &&
487
- res.examples.length > 0) {
488
- const example = res.examples[0];
489
- if (example.value) {
490
- operation.responses[res.status].examples = { 'application/json': example.value };
491
- }
492
- }
493
- });
494
- if (operation.consumes.length === 0) {
495
- const hasBody = method.parameters
496
- .some((parameter) => parameter.in === metadata_1.ParameterSource.BODY || parameter.in === metadata_1.ParameterSource.BODY_PROP);
497
- if (hasBody) {
498
- operation.consumes.push('application/json');
499
- }
500
- const hasFormData = method.parameters
501
- .some((parameter) => parameter.in === metadata_1.ParameterSource.FORM_DATA);
502
- if (hasFormData) {
503
- operation.consumes.push('multipart/form-data');
504
- }
505
- }
506
- if (operation.produces.length === 0 &&
507
- produces.length > 0) {
508
- operation.produces = [...new Set(produces)];
509
- }
510
- if (operation.produces.length === 0) {
511
- operation.produces = ['application/json'];
512
- }
513
- return operation;
514
- }
515
- }
516
- exports.V2Generator = V2Generator;
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2021-2023.
4
+ * Author Peter Placzek (tada5hi)
5
+ * For the full copyright and license information,
6
+ * view the LICENSE file that was distributed with this source code.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.V2Generator = void 0;
13
+ const metadata_1 = require("@trapi/metadata");
14
+ const node_path_1 = __importDefault(require("node:path"));
15
+ const node_url_1 = require("node:url");
16
+ const smob_1 = require("smob");
17
+ const schema_1 = require("../../schema");
18
+ const utils_1 = require("../../utils");
19
+ const abstract_1 = require("../abstract");
20
+ class V2Generator extends abstract_1.AbstractSpecGenerator {
21
+ async build() {
22
+ if (typeof this.spec !== 'undefined') {
23
+ return this.spec;
24
+ }
25
+ let spec = {
26
+ definitions: this.buildSchemasForReferenceTypes(),
27
+ info: this.buildInfo(),
28
+ paths: this.buildPaths(),
29
+ swagger: '2.0',
30
+ };
31
+ spec.securityDefinitions = this.config.securityDefinitions ?
32
+ V2Generator.translateSecurityDefinitions(this.config.securityDefinitions) :
33
+ {};
34
+ if (this.config.consumes) {
35
+ spec.consumes = this.config.consumes;
36
+ }
37
+ if (this.config.produces) {
38
+ spec.produces = this.config.produces;
39
+ }
40
+ if (this.config.servers &&
41
+ this.config.servers.length > 0) {
42
+ const url = new node_url_1.URL(this.config.servers[0].url, 'http://localhost:3000/');
43
+ spec.host = url.host;
44
+ if (url.pathname) {
45
+ spec.basePath = url.pathname;
46
+ }
47
+ }
48
+ if (this.config.specificationExtra) {
49
+ spec = (0, smob_1.merge)(spec, this.config.specificationExtra);
50
+ }
51
+ this.spec = spec;
52
+ await this.save();
53
+ return spec;
54
+ }
55
+ static translateSecurityDefinitions(securityDefinitions) {
56
+ const definitions = {};
57
+ const keys = Object.keys(securityDefinitions);
58
+ for (let i = 0; i < keys.length; i++) {
59
+ const securityDefinition = securityDefinitions[keys[i]];
60
+ switch (securityDefinition.type) {
61
+ case 'http':
62
+ if (securityDefinition.schema === 'basic') {
63
+ definitions[keys[i]] = {
64
+ type: 'basic',
65
+ };
66
+ }
67
+ break;
68
+ case 'apiKey':
69
+ definitions[keys[i]] = securityDefinition;
70
+ break;
71
+ case 'oauth2':
72
+ if (securityDefinition.flows.implicit) {
73
+ definitions[`${keys[i]}Implicit`] = {
74
+ type: 'oauth2',
75
+ flow: 'implicit',
76
+ authorizationUrl: securityDefinition.flows.implicit.authorizationUrl,
77
+ scopes: securityDefinition.flows.implicit.scopes,
78
+ };
79
+ }
80
+ if (securityDefinition.flows.password) {
81
+ definitions[`${keys[i]}Implicit`] = {
82
+ type: 'oauth2',
83
+ flow: 'password',
84
+ tokenUrl: securityDefinition.flows.password.tokenUrl,
85
+ scopes: securityDefinition.flows.password.scopes,
86
+ };
87
+ }
88
+ if (securityDefinition.flows.authorizationCode) {
89
+ definitions[`${keys[i]}AccessCode`] = {
90
+ type: 'oauth2',
91
+ flow: 'accessCode',
92
+ tokenUrl: securityDefinition.flows.authorizationCode.tokenUrl,
93
+ authorizationUrl: securityDefinition.flows.authorizationCode.authorizationUrl,
94
+ scopes: securityDefinition.flows.authorizationCode.scopes,
95
+ };
96
+ }
97
+ if (securityDefinition.flows.clientCredentials) {
98
+ definitions[`${keys[i]}Application`] = {
99
+ type: 'oauth2',
100
+ flow: 'application',
101
+ tokenUrl: securityDefinition.flows.clientCredentials.tokenUrl,
102
+ scopes: securityDefinition.flows.clientCredentials.scopes,
103
+ };
104
+ }
105
+ break;
106
+ }
107
+ }
108
+ return definitions;
109
+ }
110
+ buildSchemaForRefObject(referenceType) {
111
+ const required = referenceType.properties
112
+ .filter((p) => p.required && !this.isUndefinedProperty(p))
113
+ .map((p) => p.name);
114
+ const output = {
115
+ description: referenceType.description,
116
+ properties: this.buildProperties(referenceType.properties),
117
+ required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
118
+ type: schema_1.DataTypeName.OBJECT,
119
+ };
120
+ if (referenceType.additionalProperties) {
121
+ output.additionalProperties = true;
122
+ }
123
+ if (referenceType.example) {
124
+ output.example = referenceType.example;
125
+ }
126
+ return output;
127
+ }
128
+ buildSchemaForRefEnum(referenceType) {
129
+ const output = {
130
+ description: referenceType.description,
131
+ enum: referenceType.members,
132
+ type: this.decideEnumType(referenceType.members),
133
+ };
134
+ if (referenceType.memberNames !== undefined && referenceType.members.length === referenceType.memberNames.length) {
135
+ output['x-enum-varnames'] = referenceType.memberNames;
136
+ }
137
+ return output;
138
+ }
139
+ buildSchemaForRefAlias(referenceType) {
140
+ const swaggerType = this.getSchemaForType(referenceType.type);
141
+ const format = referenceType.format;
142
+ return {
143
+ ...swaggerType,
144
+ default: referenceType.default || swaggerType.default,
145
+ example: referenceType.example,
146
+ format: format || swaggerType.format,
147
+ description: referenceType.description,
148
+ ...this.transformValidators(referenceType.validators),
149
+ };
150
+ }
151
+ /*
152
+ Path & Parameter ( + utils)
153
+ */
154
+ buildPaths() {
155
+ const output = {};
156
+ const unique = (input) => [...new Set(input)];
157
+ this.metadata.controllers.forEach((controller) => {
158
+ controller.methods.forEach((method) => {
159
+ let fullPath = node_path_1.default.posix.join('/', (controller.path ? controller.path : ''), method.path);
160
+ fullPath = (0, utils_1.normalizePathParameters)(fullPath);
161
+ method.consumes = unique([...controller.consumes, ...method.consumes]);
162
+ method.produces = unique([...controller.produces, ...method.produces]);
163
+ method.tags = unique([...controller.tags, ...method.tags]);
164
+ method.security = method.security || controller.security;
165
+ // todo: unique for objects
166
+ method.responses = unique([...controller.responses, ...method.responses]);
167
+ output[fullPath] = output[fullPath] || {};
168
+ output[fullPath][method.method] = this.buildMethod(method);
169
+ });
170
+ });
171
+ return output;
172
+ }
173
+ buildMethod(method) {
174
+ const output = this.buildOperation(method);
175
+ output.consumes = this.buildMethodConsumes(method);
176
+ output.description = method.description;
177
+ if (method.summary) {
178
+ output.summary = method.summary;
179
+ }
180
+ if (method.deprecated) {
181
+ output.deprecated = method.deprecated;
182
+ }
183
+ if (method.tags.length) {
184
+ output.tags = method.tags;
185
+ }
186
+ if (method.security) {
187
+ output.security = method.security;
188
+ }
189
+ const parameters = this.groupParameters(method.parameters);
190
+ output.parameters = [
191
+ ...(parameters[metadata_1.ParameterSource.PATH] || []),
192
+ ...(parameters[metadata_1.ParameterSource.QUERY_PROP] || []),
193
+ ...(parameters[metadata_1.ParameterSource.HEADER] || []),
194
+ ...(parameters[metadata_1.ParameterSource.FORM_DATA] || []),
195
+ ].map((p) => this.buildParameter(p));
196
+ // ignore ParameterSource.QUERY!
197
+ // ------------------------------------------------------
198
+ const bodyParameters = (parameters[metadata_1.ParameterSource.BODY] || []);
199
+ if (bodyParameters.length > 1) {
200
+ throw new Error('Only one body parameter allowed per controller method.');
201
+ }
202
+ const bodyParameter = bodyParameters.length > 0 ?
203
+ this.buildParameter(bodyParameters[0]) :
204
+ undefined;
205
+ const bodyPropParams = parameters[metadata_1.ParameterSource.BODY_PROP] || [];
206
+ if (bodyPropParams.length > 0) {
207
+ const schema = {
208
+ type: schema_1.DataTypeName.OBJECT,
209
+ title: 'Body',
210
+ properties: {},
211
+ };
212
+ const required = [];
213
+ for (let i = 0; i < bodyPropParams.length; i++) {
214
+ const bodyProp = this.getSchemaForType(bodyPropParams[i].type);
215
+ bodyProp.default = bodyPropParams[i].default;
216
+ bodyProp.description = bodyPropParams[i].description;
217
+ bodyProp.example = bodyPropParams[i].examples;
218
+ if (bodyProp.required) {
219
+ required.push(bodyPropParams[i].name);
220
+ }
221
+ schema.properties[bodyPropParams[i].name] = bodyProp;
222
+ }
223
+ if (bodyParameter &&
224
+ bodyParameter.in === schema_1.ParameterSourceV2.BODY) {
225
+ if (bodyParameter.schema.type === schema_1.DataTypeName.OBJECT) {
226
+ bodyParameter.schema.properties = {
227
+ ...(bodyParameter.schema.properties || {}),
228
+ ...schema.properties,
229
+ };
230
+ bodyParameter.schema.required = [
231
+ ...(bodyParameter.schema.required || []),
232
+ ...required,
233
+ ];
234
+ }
235
+ else {
236
+ bodyParameter.schema = schema;
237
+ }
238
+ output.parameters.push(bodyParameter);
239
+ }
240
+ else {
241
+ const parameter = {
242
+ in: schema_1.ParameterSourceV2.BODY,
243
+ name: 'body',
244
+ schema,
245
+ };
246
+ if (required.length) {
247
+ parameter.schema.required = required;
248
+ }
249
+ output.parameters.push(parameter);
250
+ }
251
+ }
252
+ else if (bodyParameter) {
253
+ output.parameters.push(bodyParameter);
254
+ }
255
+ for (let i = 0; i < method.extensions.length; i++) {
256
+ output[method.extensions[i].key] = method.extensions[i].value;
257
+ }
258
+ return output;
259
+ }
260
+ transformParameterSource(source) {
261
+ if (source === metadata_1.ParameterSource.BODY) {
262
+ return schema_1.ParameterSourceV2.BODY;
263
+ }
264
+ if (source === metadata_1.ParameterSource.FORM_DATA) {
265
+ return schema_1.ParameterSourceV2.FORM_DATA;
266
+ }
267
+ if (source === metadata_1.ParameterSource.HEADER) {
268
+ return schema_1.ParameterSourceV2.HEADER;
269
+ }
270
+ if (source === metadata_1.ParameterSource.PATH) {
271
+ return schema_1.ParameterSourceV2.PATH;
272
+ }
273
+ if (source === metadata_1.ParameterSource.QUERY || source === metadata_1.ParameterSource.QUERY_PROP) {
274
+ return schema_1.ParameterSourceV2.QUERY;
275
+ }
276
+ return undefined;
277
+ }
278
+ buildParameter(input) {
279
+ const sourceIn = this.transformParameterSource(input.in);
280
+ if (!sourceIn) {
281
+ throw new Error(`The parameter source "${input.in}" is not valid for generating a document.`);
282
+ }
283
+ const parameter = {
284
+ description: input.description,
285
+ in: sourceIn,
286
+ name: input.name,
287
+ required: input.required,
288
+ };
289
+ if (input.in !== metadata_1.ParameterSource.BODY &&
290
+ (0, metadata_1.isRefEnumType)(input.type)) {
291
+ input.type = {
292
+ typeName: metadata_1.TypeName.ENUM,
293
+ members: input.type.members,
294
+ };
295
+ }
296
+ const parameterType = this.getSchemaForType(input.type);
297
+ if (parameter.in !== schema_1.ParameterSourceV2.BODY &&
298
+ parameterType.format) {
299
+ parameter.format = parameterType.format;
300
+ }
301
+ // collectionFormat, might be valid for all parameters (if value != multi)
302
+ if ((parameter.in === schema_1.ParameterSourceV2.FORM_DATA || parameter.in === schema_1.ParameterSourceV2.QUERY) &&
303
+ (input.type.typeName === metadata_1.TypeName.ARRAY || parameterType.type === schema_1.DataTypeName.ARRAY)) {
304
+ parameter.collectionFormat = input.collectionFormat || this.config.collectionFormat || 'multi';
305
+ }
306
+ if (parameter.in === schema_1.ParameterSourceV2.BODY) {
307
+ if ((input.type.typeName === metadata_1.TypeName.ARRAY || parameterType.type === schema_1.DataTypeName.ARRAY)) {
308
+ parameter.schema = {
309
+ items: parameterType.items,
310
+ type: schema_1.DataTypeName.ARRAY,
311
+ };
312
+ }
313
+ else if (input.type.typeName === metadata_1.TypeName.ANY) {
314
+ parameter.schema = { type: schema_1.DataTypeName.OBJECT };
315
+ }
316
+ else {
317
+ parameter.schema = parameterType;
318
+ }
319
+ parameter.schema = {
320
+ ...parameter.schema,
321
+ ...this.transformValidators(input.validators),
322
+ };
323
+ return parameter;
324
+ }
325
+ // todo: this is eventually illegal
326
+ (0, smob_1.merge)(parameter, this.transformValidators(input.validators));
327
+ if (input.type.typeName === metadata_1.TypeName.ANY) {
328
+ parameter.type = schema_1.DataTypeName.STRING;
329
+ }
330
+ else if (parameterType.type) {
331
+ parameter.type = parameterType.type;
332
+ }
333
+ if (parameterType.items) {
334
+ parameter.items = parameterType.items;
335
+ }
336
+ if (parameterType.enum) {
337
+ parameter.enum = parameterType.enum;
338
+ }
339
+ if (typeof input.default !== 'undefined') {
340
+ parameter.default = input.default;
341
+ }
342
+ return parameter;
343
+ }
344
+ buildMethodConsumes(method) {
345
+ if (method.consumes &&
346
+ method.consumes.length > 0) {
347
+ return method.consumes;
348
+ }
349
+ if (this.hasFileParams(method)) {
350
+ return ['multipart/form-data'];
351
+ }
352
+ if (this.hasFormParams(method)) {
353
+ return ['application/x-www-form-urlencoded'];
354
+ }
355
+ if (this.supportsBodyParameters(method.method)) {
356
+ return ['application/json'];
357
+ }
358
+ return [];
359
+ }
360
+ hasFileParams(method) {
361
+ return method.parameters.some((p) => (p.in === metadata_1.ParameterSource.FORM_DATA && p.type.typeName === 'file'));
362
+ }
363
+ hasFormParams(method) {
364
+ return method.parameters.some((p) => (p.in === metadata_1.ParameterSource.FORM_DATA));
365
+ }
366
+ supportsBodyParameters(method) {
367
+ return ['post', 'put', 'patch'].some((m) => m === method);
368
+ }
369
+ /*
370
+ Swagger Type ( + utils)
371
+ */
372
+ getSchemaForEnumType(enumType) {
373
+ const type = this.decideEnumType(enumType.members);
374
+ const nullable = !!enumType.members.includes(null);
375
+ return {
376
+ type,
377
+ enum: enumType.members.map((member) => (0, utils_1.transformValueTo)(type, member)),
378
+ 'x-nullable': nullable,
379
+ };
380
+ }
381
+ getSchemaForIntersectionType(type) {
382
+ // tslint:disable-next-line:no-shadowed-variable
383
+ const properties = type.members.reduce((acc, type) => {
384
+ if ((0, metadata_1.isRefObjectType)(type)) {
385
+ const refType = this.metadata.referenceTypes[type.refName];
386
+ const props = refType &&
387
+ refType.properties &&
388
+ refType.properties.reduce((pAcc, prop) => ({
389
+ ...pAcc,
390
+ [prop.name]: this.getSchemaForType(prop.type),
391
+ }), {});
392
+ return { ...acc, ...props };
393
+ }
394
+ return { ...acc };
395
+ }, {});
396
+ return { type: schema_1.DataTypeName.OBJECT, properties };
397
+ }
398
+ getSchemaForReferenceType(referenceType) {
399
+ return { $ref: `#/definitions/${referenceType.refName}` };
400
+ }
401
+ getSchemaForUnionType(type) {
402
+ const members = [];
403
+ const enumTypeMember = { typeName: metadata_1.TypeName.ENUM, members: [] };
404
+ for (let i = 0; i < type.members.length; i++) {
405
+ const member = type.members[i];
406
+ if ((0, metadata_1.isEnumType)(member)) {
407
+ enumTypeMember.members.push(...member.members);
408
+ }
409
+ if (!(0, metadata_1.isAnyType)(member) &&
410
+ !(0, metadata_1.isUndefinedType)(member) &&
411
+ !(0, metadata_1.isEnumType)(member)) {
412
+ members.push(member);
413
+ }
414
+ }
415
+ if (members.length === 0 &&
416
+ enumTypeMember.members.length > 0) {
417
+ return this.getSchemaForEnumType(enumTypeMember);
418
+ }
419
+ const isNullEnum = enumTypeMember.members.every((member) => member === null);
420
+ if (members.length === 1) {
421
+ if (isNullEnum) {
422
+ const memberType = this.getSchemaForType(members[0]);
423
+ if (memberType.$ref) {
424
+ return memberType;
425
+ }
426
+ memberType['x-nullable'] = true;
427
+ return memberType;
428
+ }
429
+ if (enumTypeMember.members.length === 0) {
430
+ return this.getSchemaForType(members[0]);
431
+ }
432
+ }
433
+ return { type: schema_1.DataTypeName.OBJECT, ...(isNullEnum ? { 'x-nullable': true } : {}) };
434
+ }
435
+ buildProperties(properties) {
436
+ const output = {};
437
+ properties.forEach((property) => {
438
+ const swaggerType = this.getSchemaForType(property.type);
439
+ swaggerType.description = property.description;
440
+ swaggerType.example = property.example;
441
+ swaggerType.format = property.format || swaggerType.format;
442
+ if (!(0, utils_1.hasOwnProperty)(swaggerType, '$ref') || !swaggerType.$ref) {
443
+ swaggerType.description = property.description;
444
+ }
445
+ if (property.deprecated) {
446
+ swaggerType['x-deprecated'] = true;
447
+ }
448
+ if (property.extensions) {
449
+ for (let i = 0; i < property.extensions.length; i++) {
450
+ swaggerType[property.extensions[i].key] = property.extensions[i].value;
451
+ }
452
+ }
453
+ const extensions = this.transformExtensions(property.extensions);
454
+ const validators = this.transformValidators(property.validators);
455
+ output[property.name] = {
456
+ ...swaggerType,
457
+ ...validators,
458
+ ...extensions,
459
+ };
460
+ });
461
+ return output;
462
+ }
463
+ buildOperation(method) {
464
+ const operation = {
465
+ operationId: this.getOperationId(method.name),
466
+ consumes: method.consumes || [],
467
+ produces: method.produces || [],
468
+ responses: {},
469
+ security: method.security || [],
470
+ };
471
+ const produces = [];
472
+ method.responses.forEach((res) => {
473
+ operation.responses[res.status] = {
474
+ description: res.description,
475
+ };
476
+ if (res.schema &&
477
+ !(0, metadata_1.isVoidType)(res.schema)) {
478
+ if (res.produces) {
479
+ produces.push(...res.produces);
480
+ }
481
+ else if ((0, metadata_1.isBinaryType)(res.schema)) {
482
+ produces.push('application/octet-stream');
483
+ }
484
+ operation.responses[res.status].schema = this.getSchemaForType(res.schema);
485
+ }
486
+ if (res.examples &&
487
+ res.examples.length > 0) {
488
+ const example = res.examples[0];
489
+ if (example.value) {
490
+ operation.responses[res.status].examples = { 'application/json': example.value };
491
+ }
492
+ }
493
+ });
494
+ if (operation.consumes.length === 0) {
495
+ const hasBody = method.parameters
496
+ .some((parameter) => parameter.in === metadata_1.ParameterSource.BODY || parameter.in === metadata_1.ParameterSource.BODY_PROP);
497
+ if (hasBody) {
498
+ operation.consumes.push('application/json');
499
+ }
500
+ const hasFormData = method.parameters
501
+ .some((parameter) => parameter.in === metadata_1.ParameterSource.FORM_DATA);
502
+ if (hasFormData) {
503
+ operation.consumes.push('multipart/form-data');
504
+ }
505
+ }
506
+ if (operation.produces.length === 0 &&
507
+ produces.length > 0) {
508
+ operation.produces = [...new Set(produces)];
509
+ }
510
+ if (operation.produces.length === 0) {
511
+ operation.produces = ['application/json'];
512
+ }
513
+ return operation;
514
+ }
515
+ }
516
+ exports.V2Generator = V2Generator;
517
517
  //# sourceMappingURL=module.js.map