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 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
- type: object
308
- properties:
309
- message:
310
- type: string
311
- code:
312
- type: integer
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
- type: array
320
- items: *ErrorItem
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.32",
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.1.0",
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'}]