serverless-openapi-documenter 0.0.32 → 0.0.33
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 +108 -15
- package/package.json +2 -2
- package/src/definitionGenerator.js +153 -0
- package/test/unit/definitionGenerator.spec.js +382 -1
package/README.md
CHANGED
|
@@ -59,15 +59,16 @@ Options:
|
|
|
59
59
|
| info.title | custom.documentation.title OR service |
|
|
60
60
|
| info.description | custom.documentation.description OR blank string |
|
|
61
61
|
| info.version | custom.documentation.version OR random v4 uuid if not provided |
|
|
62
|
-
| info.termsOfService | custom.documentation.termsOfService
|
|
63
|
-
| info.contact | custom.documentation.contact
|
|
64
|
-
| info.contact.name | custom.documentation.contact.name OR blank string
|
|
65
|
-
| info.contact.url | custom.documentation.contact.url if provided
|
|
66
|
-
| info.license | custom.documentation.license
|
|
67
|
-
| info.license.name | custom.documentation.license.name OR blank string
|
|
68
|
-
| info.license.url | custom.documentation.license.url if provided
|
|
62
|
+
| info.termsOfService | custom.documentation.termsOfService |
|
|
63
|
+
| info.contact | custom.documentation.contact |
|
|
64
|
+
| info.contact.name | custom.documentation.contact.name OR blank string |
|
|
65
|
+
| info.contact.url | custom.documentation.contact.url if provided |
|
|
66
|
+
| info.license | custom.documentation.license |
|
|
67
|
+
| info.license.name | custom.documentation.license.name OR blank string |
|
|
68
|
+
| info.license.url | custom.documentation.license.url if provided |
|
|
69
69
|
| externalDocs.description | custom.documentation.externalDocumentation.description |
|
|
70
70
|
| externalDocs.url | custom.documentation.externalDocumentation.url |
|
|
71
|
+
| security | custom.documentation.security |
|
|
71
72
|
| servers[].description | custom.documentation.servers.description |
|
|
72
73
|
| servers[].url | custom.documentation.servers.url |
|
|
73
74
|
| servers[].variables | custom.documentation.servers.variables |
|
|
@@ -89,6 +90,7 @@ Options:
|
|
|
89
90
|
| path[path].[operation].externalDocs.url | functions.functions.[http OR httpApi].documentation.externalDocumentation.url |
|
|
90
91
|
| path[path].[operation].servers[].description | functions.functions.[http OR httpApi].documentation.servers.description |
|
|
91
92
|
| path[path].[operation].servers[].url | functions.functions.[http OR httpApi].documentation.servers.url |
|
|
93
|
+
| path[path].[operation].security | functions.functions.[http OR httpApi].documentation.security |
|
|
92
94
|
| path[path].[operation].deprecated | functions.functions.[http OR httpApi].documentation.deprecated |
|
|
93
95
|
| path[path].[operation].parameters | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params |
|
|
94
96
|
| path[path].[operation].parameters.name | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.name |
|
|
@@ -219,6 +221,40 @@ functions:
|
|
|
219
221
|
|
|
220
222
|
For more info on `serverless.yml` syntax, see their docs.
|
|
221
223
|
|
|
224
|
+
#### securitySchemes
|
|
225
|
+
|
|
226
|
+
You can provide optional Security Schemes:
|
|
227
|
+
|
|
228
|
+
```yml
|
|
229
|
+
custom:
|
|
230
|
+
documentation:
|
|
231
|
+
securitySchemes:
|
|
232
|
+
my_api_key:
|
|
233
|
+
type: apiKey
|
|
234
|
+
name: api_key
|
|
235
|
+
in: header
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
It accepts all available Security Schemes and follows the specification: https://spec.openapis.org/oas/v3.0.3#security-scheme-object
|
|
239
|
+
|
|
240
|
+
#### Security on each operation
|
|
241
|
+
|
|
242
|
+
To apply an overall security scheme to all of your operations without having to add the documentation to each one, you can write it like:
|
|
243
|
+
|
|
244
|
+
```yml
|
|
245
|
+
custom:
|
|
246
|
+
documentation:
|
|
247
|
+
securitySchemes:
|
|
248
|
+
my_api_key:
|
|
249
|
+
type: apiKey
|
|
250
|
+
name: api_key
|
|
251
|
+
in: header
|
|
252
|
+
security:
|
|
253
|
+
- my_api_key: []
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
This will apply the requirement of each operation requiring your `my_api_key` security scheme, [you can override this](#security).
|
|
257
|
+
|
|
222
258
|
#### Models
|
|
223
259
|
|
|
224
260
|
There are two ways to write the Models. Models contain additional information that you can use to define schemas for endpoints. You must define the *content type* for each schema that you provide in the models.
|
|
@@ -304,20 +340,20 @@ custom:
|
|
|
304
340
|
content:
|
|
305
341
|
application/json:
|
|
306
342
|
schema: &ErrorItem
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
343
|
+
type: object
|
|
344
|
+
properties:
|
|
345
|
+
message:
|
|
346
|
+
type: string
|
|
347
|
+
code:
|
|
348
|
+
type: integer
|
|
313
349
|
|
|
314
350
|
- name: "PutDocumentResponse"
|
|
315
351
|
description: "PUT Document response model (external reference example)"
|
|
316
352
|
content:
|
|
317
353
|
application/json:
|
|
318
354
|
schema:
|
|
319
|
-
|
|
320
|
-
|
|
355
|
+
type: array
|
|
356
|
+
items: *ErrorItem
|
|
321
357
|
```
|
|
322
358
|
|
|
323
359
|
`&ErrorItem` in the above example creates a node anchor (&ErrorItem) to the `ErrorResponse` schema which then can be used in the `PutDocumentResponse` schema via the reference (*ErrorItem). The node anchor needs to be declared first before it can be used elsewhere via the reference, swapping the above example around would result in an error.
|
|
@@ -340,6 +376,7 @@ The `documentation` section of the event configuration can contain the following
|
|
|
340
376
|
* `pathParams`: a list of path parameters (see [pathParams](#pathparams) below)
|
|
341
377
|
* `cookieParams`: a list of cookie parameters (see [cookieParams](#cookieparams) below)
|
|
342
378
|
* `headerParams`: a list of headers (see [headerParams](#headerparams---request-headers) below)
|
|
379
|
+
* `security`: The security requirement to apply (see [security](#security) below)
|
|
343
380
|
* `methodResponses`: an array of response models and applicable status codes
|
|
344
381
|
* `statusCode`: applicable http status code (ie. 200/404/500 etc.)
|
|
345
382
|
* `responseBody`: contains description of the response
|
|
@@ -480,6 +517,62 @@ headerParams:
|
|
|
480
517
|
type: "string"
|
|
481
518
|
```
|
|
482
519
|
|
|
520
|
+
#### `security`
|
|
521
|
+
|
|
522
|
+
The `security` property allows you to specify the [Security Scheme](#securityschemes) to apply to the HTTP Request. If you have applied an `security` ([see Security on each operation](#security-on-each-operation)) then you can either leave this field off, or to override it with a different scheme you can write it like:
|
|
523
|
+
|
|
524
|
+
```yml
|
|
525
|
+
custom:
|
|
526
|
+
documentation:
|
|
527
|
+
securitySchemes:
|
|
528
|
+
my_api_key:
|
|
529
|
+
type: apiKey
|
|
530
|
+
name: api_key
|
|
531
|
+
in: header
|
|
532
|
+
petstore_auth:
|
|
533
|
+
type: oauth2
|
|
534
|
+
flows:
|
|
535
|
+
implicit:
|
|
536
|
+
authorizationUrl: https://example.com/api/oauth/dialog
|
|
537
|
+
scopes:
|
|
538
|
+
write:pets: modify pets in your account
|
|
539
|
+
read:pets: read your pets
|
|
540
|
+
security:
|
|
541
|
+
- my_api_key: []
|
|
542
|
+
|
|
543
|
+
functions:
|
|
544
|
+
getData:
|
|
545
|
+
events:
|
|
546
|
+
- http:
|
|
547
|
+
documentation:
|
|
548
|
+
security:
|
|
549
|
+
- petstore_auth:
|
|
550
|
+
- write:pets
|
|
551
|
+
- read:pets
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
If you have specified an `security` at the document root, but this HTTP Request should not apply any security schemes, you should set security to be an array with an empty object:
|
|
555
|
+
|
|
556
|
+
```yml
|
|
557
|
+
custom:
|
|
558
|
+
documentation:
|
|
559
|
+
securitySchemes:
|
|
560
|
+
my_api_key:
|
|
561
|
+
type: apiKey
|
|
562
|
+
name: api_key
|
|
563
|
+
in: header
|
|
564
|
+
security:
|
|
565
|
+
- my_api_key: []
|
|
566
|
+
|
|
567
|
+
functions:
|
|
568
|
+
getData:
|
|
569
|
+
events:
|
|
570
|
+
- http:
|
|
571
|
+
documentation:
|
|
572
|
+
security:
|
|
573
|
+
- {}
|
|
574
|
+
```
|
|
575
|
+
|
|
483
576
|
#### `requestModels`
|
|
484
577
|
|
|
485
578
|
The `requestModels` property allows you to define models for the HTTP Request of the function event. You can define a different model for each different `Content-Type`. You can define a reference to the relevant request model named in the `models` section of your configuration (see [Defining Models](#models) section).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-openapi-documenter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"chai": "^4.3.7",
|
|
45
|
-
"mocha": "^10.
|
|
45
|
+
"mocha": "^10.2.0",
|
|
46
46
|
"sinon": "^15.0.0"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -39,6 +39,15 @@ class DefinitionGenerator {
|
|
|
39
39
|
|
|
40
40
|
async parse() {
|
|
41
41
|
this.createInfo()
|
|
42
|
+
|
|
43
|
+
if (this.serverless.service.custom.documentation.securitySchemes) {
|
|
44
|
+
this.createSecuritySchemes(this.serverless.service.custom.documentation.securitySchemes)
|
|
45
|
+
|
|
46
|
+
if (this.serverless.service.custom.documentation.security) {
|
|
47
|
+
this.openAPI.security = this.serverless.service.custom.documentation.security
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
await this.createPaths()
|
|
43
52
|
.catch(err => {
|
|
44
53
|
throw err
|
|
@@ -265,6 +274,10 @@ class DefinitionGenerator {
|
|
|
265
274
|
obj.externalDocs = documentation.externalDocumentation
|
|
266
275
|
}
|
|
267
276
|
|
|
277
|
+
if (Object.keys(documentation).includes('security')) {
|
|
278
|
+
obj.security = documentation.security
|
|
279
|
+
}
|
|
280
|
+
|
|
268
281
|
if (Object.keys(documentation).includes('deprecated'))
|
|
269
282
|
obj.deprecated = documentation.deprecated
|
|
270
283
|
|
|
@@ -523,6 +536,146 @@ class DefinitionGenerator {
|
|
|
523
536
|
}
|
|
524
537
|
}
|
|
525
538
|
|
|
539
|
+
addToComponents(type, schema, name) {
|
|
540
|
+
const schemaObj = {
|
|
541
|
+
[name]: schema
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (this.openAPI?.components) {
|
|
545
|
+
if (this.openAPI.components[type]) {
|
|
546
|
+
Object.assign(this.openAPI.components[type], schemaObj)
|
|
547
|
+
} else {
|
|
548
|
+
Object.assign(this.openAPI.components, {[type]: schemaObj})
|
|
549
|
+
}
|
|
550
|
+
} else {
|
|
551
|
+
const components = {
|
|
552
|
+
components: {
|
|
553
|
+
[type]: schemaObj
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
Object.assign(this.openAPI, components)
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
createSecuritySchemes(securitySchemes) {
|
|
562
|
+
for (const scheme of Object.keys(securitySchemes)) {
|
|
563
|
+
const securityScheme = securitySchemes[scheme]
|
|
564
|
+
const schema = {}
|
|
565
|
+
|
|
566
|
+
if (securityScheme.description)
|
|
567
|
+
schema.description = securityScheme.description
|
|
568
|
+
|
|
569
|
+
switch(securityScheme.type.toLowerCase()) {
|
|
570
|
+
case 'apikey':
|
|
571
|
+
const apiKeyScheme = this.createAPIKeyScheme(securityScheme)
|
|
572
|
+
schema.type = 'apiKey'
|
|
573
|
+
Object.assign(schema, apiKeyScheme)
|
|
574
|
+
break;
|
|
575
|
+
|
|
576
|
+
case 'http':
|
|
577
|
+
const HTTPScheme = this.createHTTPScheme(securityScheme)
|
|
578
|
+
schema.type = 'http'
|
|
579
|
+
Object.assign(schema, HTTPScheme)
|
|
580
|
+
break;
|
|
581
|
+
|
|
582
|
+
case 'openidconnect':
|
|
583
|
+
const openIdConnectScheme = this.createOpenIDConnectScheme(securityScheme)
|
|
584
|
+
schema.type = 'openIdConnect'
|
|
585
|
+
Object.assign(schema, openIdConnectScheme)
|
|
586
|
+
break;
|
|
587
|
+
|
|
588
|
+
case 'oauth2':
|
|
589
|
+
const oAuth2Scheme = this.createOAuth2Scheme(securityScheme)
|
|
590
|
+
schema.type = 'oauth2'
|
|
591
|
+
Object.assign(schema, oAuth2Scheme)
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
this.addToComponents('securitySchemes', schema, scheme)
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
createAPIKeyScheme(securitySchema) {
|
|
600
|
+
const schema = {}
|
|
601
|
+
if (securitySchema.name)
|
|
602
|
+
schema.name = securitySchema.name
|
|
603
|
+
else
|
|
604
|
+
throw new Error('Security Scheme for "apiKey" requires the name of the header, query or cookie parameter to be used')
|
|
605
|
+
|
|
606
|
+
if (securitySchema.in)
|
|
607
|
+
schema.in = securitySchema.in
|
|
608
|
+
else
|
|
609
|
+
throw new Error('Security Scheme for "apiKey" requires the location of the API key: header, query or cookie parameter')
|
|
610
|
+
|
|
611
|
+
return schema
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
createHTTPScheme(securitySchema) {
|
|
615
|
+
const schema = {}
|
|
616
|
+
|
|
617
|
+
if (securitySchema.scheme)
|
|
618
|
+
schema.scheme = securitySchema.scheme
|
|
619
|
+
else
|
|
620
|
+
throw new Error('Security Scheme for "http" requires scheme')
|
|
621
|
+
|
|
622
|
+
if (securitySchema.bearerFormat)
|
|
623
|
+
schema.bearerFormat = securitySchema.bearerFormat
|
|
624
|
+
|
|
625
|
+
return schema
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
createOpenIDConnectScheme(securitySchema) {
|
|
629
|
+
const schema = {}
|
|
630
|
+
if (securitySchema.openIdConnectUrl)
|
|
631
|
+
schema.openIdConnectUrl = securitySchema.openIdConnectUrl
|
|
632
|
+
else
|
|
633
|
+
throw new Error('Security Scheme for "openIdConnect" requires openIdConnectUrl')
|
|
634
|
+
|
|
635
|
+
return schema
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
createOAuth2Scheme(securitySchema) {
|
|
639
|
+
const schema = {}
|
|
640
|
+
if (securitySchema.flows) {
|
|
641
|
+
const flows = this.createOAuthFlows(securitySchema.flows)
|
|
642
|
+
Object.assign(schema, {flows: flows})
|
|
643
|
+
} else
|
|
644
|
+
throw new Error('Security Scheme for "oauth2" requires flows')
|
|
645
|
+
|
|
646
|
+
return schema
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
createOAuthFlows(flows) {
|
|
650
|
+
const obj = {}
|
|
651
|
+
for (const flow of Object.keys(flows)) {
|
|
652
|
+
const schema = {}
|
|
653
|
+
if (["implicit", 'authorizationCode'].includes(flow))
|
|
654
|
+
if (flows[flow].authorizationUrl)
|
|
655
|
+
schema.authorizationUrl = flows[flow].authorizationUrl
|
|
656
|
+
else
|
|
657
|
+
throw new Error(`oAuth2 ${flow} flow requires an authorizationUrl`)
|
|
658
|
+
|
|
659
|
+
if (['password', 'clientCredentials', 'authorizationCode'].includes(flow)) {
|
|
660
|
+
if (flows[flow].tokenUrl)
|
|
661
|
+
schema.tokenUrl = flows[flow].tokenUrl
|
|
662
|
+
else
|
|
663
|
+
throw new Error(`oAuth2 ${flow} flow requires a tokenUrl`)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (flows[flow].refreshUrl)
|
|
667
|
+
schema.refreshUrl = flows[flow].refreshUrl
|
|
668
|
+
|
|
669
|
+
if (flows[flow].scopes)
|
|
670
|
+
schema.scopes = flows[flow].scopes
|
|
671
|
+
else
|
|
672
|
+
throw new Error(`oAuth2 ${flow} flow requires scopes`)
|
|
673
|
+
|
|
674
|
+
Object.assign(obj, {[flow]: schema})
|
|
675
|
+
}
|
|
676
|
+
return obj
|
|
677
|
+
}
|
|
678
|
+
|
|
526
679
|
createExamples(examples) {
|
|
527
680
|
const examplesObj = {}
|
|
528
681
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('fs').promises
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const sinon = require('sinon')
|
|
6
|
-
const $RefParser = require("@apidevtools/json-schema-ref-parser")
|
|
6
|
+
const $RefParser = require("@apidevtools/json-schema-ref-parser")
|
|
7
7
|
const expect = require('chai').expect
|
|
8
8
|
|
|
9
9
|
const serverlessMock = require('../helpers/serverless')
|
|
@@ -271,6 +271,387 @@ describe('DefinitionGenerator', () => {
|
|
|
271
271
|
});
|
|
272
272
|
});
|
|
273
273
|
|
|
274
|
+
describe('createSecuritySchemes', () => {
|
|
275
|
+
describe('API Keys', () => {
|
|
276
|
+
it('should add an API Key security scheme to components', function() {
|
|
277
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
278
|
+
'api_key': {
|
|
279
|
+
type: 'apiKey',
|
|
280
|
+
name: 'Authorization',
|
|
281
|
+
in: 'header'
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
286
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
287
|
+
|
|
288
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
289
|
+
expect(definitionGenerator.openAPI.components).to.be.an('object')
|
|
290
|
+
expect(definitionGenerator.openAPI.components).to.have.property('securitySchemes')
|
|
291
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.be.an('object')
|
|
292
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.have.property('api_key')
|
|
293
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.api_key).to.have.property('type')
|
|
294
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.api_key.type).to.be.equal('apiKey')
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should throw an error when name is missing from an API Key scheme', function() {
|
|
298
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
299
|
+
'api_key': {
|
|
300
|
+
type: 'apiKey',
|
|
301
|
+
in: 'header'
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
306
|
+
expect(() => {
|
|
307
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
308
|
+
}).to.throw('Security Scheme for "apiKey" requires the name of the header, query or cookie parameter to be used')
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should throw an error when in is missing from an API Key scheme', function() {
|
|
312
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
313
|
+
'api_key': {
|
|
314
|
+
type: 'apiKey',
|
|
315
|
+
name: 'Authorization',
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
320
|
+
expect(() => {
|
|
321
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
322
|
+
}).to.throw('Security Scheme for "apiKey" requires the location of the API key: header, query or cookie parameter')
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe('HTTP', () => {
|
|
327
|
+
it('should add an HTTP security scheme to components', function() {
|
|
328
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
329
|
+
'http_key': {
|
|
330
|
+
type: 'http',
|
|
331
|
+
scheme: 'basic'
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
336
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
337
|
+
|
|
338
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
339
|
+
expect(definitionGenerator.openAPI.components).to.be.an('object')
|
|
340
|
+
expect(definitionGenerator.openAPI.components).to.have.property('securitySchemes')
|
|
341
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.be.an('object')
|
|
342
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.have.property('http_key')
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should throw an error when scheme is missing from an HTTP scheme', function() {
|
|
346
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
347
|
+
'http_key': {
|
|
348
|
+
type: 'http',
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
353
|
+
expect(() => {
|
|
354
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
355
|
+
}).to.throw('Security Scheme for "http" requires scheme')
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe('openIdConnect', () => {
|
|
360
|
+
it('should add an openIdConnect security scheme to components', function() {
|
|
361
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
362
|
+
'openIdConnect_key': {
|
|
363
|
+
type: 'openIdConnect',
|
|
364
|
+
openIdConnectUrl: 'http://example.com'
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
369
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
370
|
+
|
|
371
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
372
|
+
expect(definitionGenerator.openAPI.components).to.be.an('object')
|
|
373
|
+
expect(definitionGenerator.openAPI.components).to.have.property('securitySchemes')
|
|
374
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.be.an('object')
|
|
375
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.have.property('openIdConnect_key')
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should throw an error when openIdConnectUrl is missing from an openIdConnect scheme', function() {
|
|
379
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
380
|
+
'openIdConnect_key': {
|
|
381
|
+
type: 'openIdConnect',
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
386
|
+
expect(() => {
|
|
387
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
388
|
+
}).to.throw('Security Scheme for "openIdConnect" requires openIdConnectUrl')
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('oauth2', () => {
|
|
393
|
+
it('should add an oauth2 security scheme to components', function() {
|
|
394
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
395
|
+
'oAuth2_key': {
|
|
396
|
+
type: 'oauth2',
|
|
397
|
+
flows: {
|
|
398
|
+
implicit: {
|
|
399
|
+
authorizationUrl: 'http://example.org/api/oauth/dialog',
|
|
400
|
+
scopes: {
|
|
401
|
+
'write:pets': 'modify pets in your account',
|
|
402
|
+
'read:pets': 'read your pets'
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
410
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
411
|
+
|
|
412
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
413
|
+
expect(definitionGenerator.openAPI.components).to.be.an('object')
|
|
414
|
+
expect(definitionGenerator.openAPI.components).to.have.property('securitySchemes')
|
|
415
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.be.an('object')
|
|
416
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.have.property('oAuth2_key')
|
|
417
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key).to.be.an('object')
|
|
418
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key).to.have.property('type')
|
|
419
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key).to.have.property('flows')
|
|
420
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key.flows).to.be.an('object')
|
|
421
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key.flows).to.have.property('implicit')
|
|
422
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key.flows.implicit).to.be.an('object')
|
|
423
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key.flows.implicit).to.have.property('scopes')
|
|
424
|
+
expect(definitionGenerator.openAPI.components.securitySchemes.oAuth2_key.flows.implicit.scopes).to.be.an('object')
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('should throw an error when flows is missing from an oauth2 scheme', function() {
|
|
428
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
429
|
+
'oAuth2_key': {
|
|
430
|
+
type: 'oauth2',
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
435
|
+
expect(() => {
|
|
436
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
437
|
+
}).to.throw('Security Scheme for "oauth2" requires flows')
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('should throw an error when authorizationUrl is missing from an oauth2 implicit flow scheme', function() {
|
|
441
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
442
|
+
'oAuth2_key': {
|
|
443
|
+
type: 'oauth2',
|
|
444
|
+
flows: {
|
|
445
|
+
implicit: {
|
|
446
|
+
scopes: {
|
|
447
|
+
'write:pets': 'modify pets in your account',
|
|
448
|
+
'read:pets': 'read your pets'
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
456
|
+
expect(() => {
|
|
457
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
458
|
+
}).to.throw('oAuth2 implicit flow requires an authorizationUrl')
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('should throw an error when authorizationUrl is missing from an oauth2 authorizationCode flow scheme', function() {
|
|
462
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
463
|
+
'oAuth2_key': {
|
|
464
|
+
type: 'oauth2',
|
|
465
|
+
flows: {
|
|
466
|
+
authorizationCode: {
|
|
467
|
+
tokenUrl: 'http://example.com',
|
|
468
|
+
scopes: {
|
|
469
|
+
'write:pets': 'modify pets in your account',
|
|
470
|
+
'read:pets': 'read your pets'
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
478
|
+
expect(() => {
|
|
479
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
480
|
+
}).to.throw('oAuth2 authorizationCode flow requires an authorizationUrl')
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should throw an error when tokenUrl is missing from an oauth2 authorizationCode flow scheme', function() {
|
|
484
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
485
|
+
'oAuth2_key': {
|
|
486
|
+
type: 'oauth2',
|
|
487
|
+
flows: {
|
|
488
|
+
authorizationCode: {
|
|
489
|
+
authorizationUrl: 'http://example.org/api/oauth/dialog',
|
|
490
|
+
scopes: {
|
|
491
|
+
'write:pets': 'modify pets in your account',
|
|
492
|
+
'read:pets': 'read your pets'
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
500
|
+
expect(() => {
|
|
501
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
502
|
+
}).to.throw('oAuth2 authorizationCode flow requires a tokenUrl')
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('should throw an error when tokenUrl is missing from an oauth2 password flow scheme', function() {
|
|
506
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
507
|
+
'oAuth2_key': {
|
|
508
|
+
type: 'oauth2',
|
|
509
|
+
flows: {
|
|
510
|
+
password: {
|
|
511
|
+
scopes: {
|
|
512
|
+
'write:pets': 'modify pets in your account',
|
|
513
|
+
'read:pets': 'read your pets'
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
521
|
+
expect(() => {
|
|
522
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
523
|
+
}).to.throw('oAuth2 password flow requires a tokenUrl')
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it('should throw an error when tokenUrl is missing from an oauth2 clientCredentials flow scheme', function() {
|
|
527
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
528
|
+
'oAuth2_key': {
|
|
529
|
+
type: 'oauth2',
|
|
530
|
+
flows: {
|
|
531
|
+
clientCredentials: {
|
|
532
|
+
scopes: {
|
|
533
|
+
'write:pets': 'modify pets in your account',
|
|
534
|
+
'read:pets': 'read your pets'
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
542
|
+
expect(() => {
|
|
543
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
544
|
+
}).to.throw('oAuth2 clientCredentials flow requires a tokenUrl')
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it('should throw an error when scopes is missing from an oauth2 clientCredentials flow scheme', function() {
|
|
548
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
549
|
+
'oAuth2_key': {
|
|
550
|
+
type: 'oauth2',
|
|
551
|
+
flows: {
|
|
552
|
+
clientCredentials: {
|
|
553
|
+
tokenUrl: 'http://example.com',
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
560
|
+
expect(() => {
|
|
561
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
562
|
+
}).to.throw('oAuth2 clientCredentials flow requires scopes')
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it('should throw an error when scopes is missing from an oauth2 authorizationCode flow scheme', function() {
|
|
566
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
567
|
+
'oAuth2_key': {
|
|
568
|
+
type: 'oauth2',
|
|
569
|
+
flows: {
|
|
570
|
+
authorizationCode: {
|
|
571
|
+
tokenUrl: 'http://example.com',
|
|
572
|
+
authorizationUrl: 'http://example.org/api/oauth/dialog',
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
579
|
+
expect(() => {
|
|
580
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
581
|
+
}).to.throw('oAuth2 authorizationCode flow requires scopes')
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('should throw an error when scopes is missing from an oauth2 password flow scheme', function() {
|
|
585
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
586
|
+
'oAuth2_key': {
|
|
587
|
+
type: 'oauth2',
|
|
588
|
+
flows: {
|
|
589
|
+
password: {
|
|
590
|
+
tokenUrl: 'http://example.com',
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
597
|
+
expect(() => {
|
|
598
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
599
|
+
}).to.throw('oAuth2 password flow requires scopes')
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('should throw an error when scopes is missing from an oauth2 implicit flow scheme', function() {
|
|
603
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
604
|
+
'oAuth2_key': {
|
|
605
|
+
type: 'oauth2',
|
|
606
|
+
flows: {
|
|
607
|
+
implicit: {
|
|
608
|
+
authorizationUrl: 'http://example.org/api/oauth/dialog',
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
615
|
+
expect(() => {
|
|
616
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
617
|
+
}).to.throw('oAuth2 implicit flow requires scopes')
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
describe('Multiple Schemes', () => {
|
|
622
|
+
it('should add an oauth2 and an apiKey security scheme to components', function() {
|
|
623
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
624
|
+
'oAuth2_key': {
|
|
625
|
+
type: 'oauth2',
|
|
626
|
+
flows: {
|
|
627
|
+
implicit: {
|
|
628
|
+
authorizationUrl: 'http://example.org/api/oauth/dialog',
|
|
629
|
+
scopes: {
|
|
630
|
+
'write:pets': 'modify pets in your account',
|
|
631
|
+
'read:pets': 'read your pets'
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
'api_key': {
|
|
637
|
+
type: 'apiKey',
|
|
638
|
+
name: 'Authorization',
|
|
639
|
+
in: 'header'
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
644
|
+
definitionGenerator.createSecuritySchemes(mockServerless.service.custom.documentation.securitySchemes)
|
|
645
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
646
|
+
expect(definitionGenerator.openAPI.components).to.be.an('object')
|
|
647
|
+
expect(definitionGenerator.openAPI.components).to.have.property('securitySchemes')
|
|
648
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.be.an('object')
|
|
649
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.have.property('oAuth2_key')
|
|
650
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.have.property('api_key')
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
274
655
|
describe('createTags', () => {
|
|
275
656
|
it('should add tags to the openAPI object correctly', function() {
|
|
276
657
|
mockServerless.service.custom.documentation.tags = [{name: 'tag1'}]
|