serverless-openapi-documenter 0.0.49 → 0.0.51

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
@@ -403,6 +403,7 @@ functions:
403
403
  - http:
404
404
  path: create
405
405
  method: post
406
+ cors: true
406
407
  summary:
407
408
  documentation:
408
409
  summary: "Create User"
@@ -645,6 +646,12 @@ responseHeaders:
645
646
  type: integer
646
647
  ```
647
648
 
649
+ ###### CORS
650
+
651
+ You can automatically generate CORS response headers by setting `cors` at the function level. Serverless allows you to modify how CORS is setup, so you can have the default options with `cors: true`, or you can modify the settings as shown in the [serverless documentation for CORS](https://www.serverless.com/framework/docs/providers/aws/events/apigateway#enabling-cors).
652
+
653
+ The generator will interpret your settings for CORS and automatically add the response headers. If for whatever reason you wish to override these, you can set them via the above `responseHeaders` setting and it'll apply your overrides.
654
+
648
655
  ## Example configuration
649
656
 
650
657
  Please view the example [serverless.yml](test/serverless-tests/serverless%202/serverless.yml).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serverless-openapi-documenter",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  "json-schema-for-openapi": "^0.3.2",
39
39
  "lodash.isequal": "^4.5.0",
40
40
  "oas-validator": "^5.0.8",
41
- "openapi-to-postmanv2": "^4.11.0",
41
+ "openapi-to-postmanv2": "^4.12.0",
42
42
  "swagger2openapi": "^7.0.8",
43
43
  "uuid": "^9.0.0"
44
44
  },
@@ -35,6 +35,24 @@ class DefinitionGenerator {
35
35
  securitySchemes: 'securitySchemes'
36
36
  }
37
37
 
38
+ this.DEFAULT_CORS_HEADERS = {
39
+ 'Access-Control-Allow-Origin': {
40
+ description: 'The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given origin.',
41
+ schema: {
42
+ type: 'string',
43
+ default: '*',
44
+ example: 'https://developer.mozilla.org'
45
+ }
46
+ },
47
+ 'Access-Control-Allow-Credentials': {
48
+ description: `The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to the frontend JavaScript code when the request's credentials mode (Request.credentials) is include`,
49
+ schema: {
50
+ type: 'boolean',
51
+ default: true
52
+ }
53
+ }
54
+ }
55
+
38
56
  try {
39
57
  this.refParserOptions = require(path.resolve('options', 'ref-parser.js'))
40
58
  } catch (err) {
@@ -123,6 +141,7 @@ class DefinitionGenerator {
123
141
  for (const httpFunction of httpFunctions) {
124
142
  for (const event of httpFunction.event) {
125
143
  if (event?.http?.documentation || event?.httpApi?.documentation) {
144
+ this.currentEvent = event?.http || event?.httpApi
126
145
  const documentation = event?.http?.documentation || event?.httpApi?.documentation
127
146
 
128
147
  this.currentFunctionName = httpFunction.functionInfo.name
@@ -328,14 +347,68 @@ class DefinitionGenerator {
328
347
  })
329
348
  }
330
349
 
350
+
351
+ const corsHeaders = await this.corsHeaders()
352
+ .catch(err => {
353
+ throw err;
354
+ })
355
+
356
+ if (obj.headers) {
357
+ for (const key in corsHeaders) {
358
+ if (!(key in obj.headers) && (obj.headers[key] = {})) {
359
+ obj.headers[key] = corsHeaders[key]
360
+ }
361
+ }
362
+ } else {
363
+ obj.headers = corsHeaders
364
+ }
365
+
331
366
  Object.assign(responses,{[response.statusCode]: obj})
332
367
  }
333
368
 
334
369
  return responses
335
370
  }
336
371
 
372
+ async corsHeaders() {
373
+ let headers = {}
374
+ if (this.currentEvent?.cors === true) {
375
+ headers = await this.createResponseHeaders(this.DEFAULT_CORS_HEADERS)
376
+ .catch(err => {
377
+ throw err;
378
+ })
379
+ } else if (this.currentEvent.cors) {
380
+ const newHeaders = {}
381
+ for (const key of Object.keys(this.DEFAULT_CORS_HEADERS)) {
382
+ if (key === 'Access-Control-Allow-Credentials' &&
383
+ this.currentEvent.cors.allowCredentials === undefined || this.currentEvent.cors?.allowCredentials === false) {
384
+ continue
385
+ }
386
+
387
+ const obj = JSON.parse(JSON.stringify(this.DEFAULT_CORS_HEADERS[key]))
388
+
389
+ if (key === 'Access-Control-Allow-Origin') {
390
+ if (this.currentEvent.cors?.origins || this.currentEvent.cors?.origin) {
391
+ obj.schema.example = this.currentEvent.cors?.origins?.toString() || this.currentEvent.cors?.origin?.toString()
392
+ } else if (this.currentEvent.cors?.allowedOrigins) {
393
+ obj.schema.example = this.currentEvent.cors.allowedOrigins.toString()
394
+ }
395
+ }
396
+
397
+ Object.assign(newHeaders, {[key]: obj})
398
+ }
399
+
400
+ headers = await this.createResponseHeaders(newHeaders)
401
+ .catch(err => {
402
+ throw err;
403
+ })
404
+ }
405
+
406
+ return headers;
407
+ }
408
+
337
409
  async createResponseHeaders(headers) {
338
410
  const obj = {}
411
+
339
412
  for (const header of Object.keys(headers)) {
340
413
  const newHeader = {}
341
414
  newHeader.description = headers[header].description || ''
@@ -12,7 +12,19 @@ class OpenAPIGenerator {
12
12
  this.logOutput = log;
13
13
  this.serverless = serverless
14
14
  this.options = options
15
- this.defaultLog = 'notice';
15
+
16
+ this.logTypes = {
17
+ NOTICE: 'notice',
18
+ DEBUG: 'debug',
19
+ ERROR: 'error',
20
+ WARNING: 'warning',
21
+ INFO: 'info',
22
+ VERBOSE: 'verbose',
23
+ SUCCESS: 'success',
24
+ }
25
+
26
+ this.defaultLog = this.logTypes.NOTICE;
27
+
16
28
  this.commands = {
17
29
  openapi: {
18
30
  commands: {
@@ -81,7 +93,7 @@ class OpenAPIGenerator {
81
93
  })
82
94
  }
83
95
 
84
- log(type = this.defaultLog, str) {
96
+ log(str, type = this.defaultLog) {
85
97
  switch(this.serverless.version[0]) {
86
98
  case '2':
87
99
  let colouredString = str
@@ -105,7 +117,7 @@ class OpenAPIGenerator {
105
117
  }
106
118
 
107
119
  async generate() {
108
- this.log(this.defaultLog, chalk.bold.underline('OpenAPI v3 Document Generation'))
120
+ this.log(chalk.bold.underline('OpenAPI v3 Document Generation'))
109
121
  this.processCliInput()
110
122
 
111
123
  const validOpenAPI = await this.generationAndValidation()
@@ -129,9 +141,9 @@ class OpenAPIGenerator {
129
141
  }
130
142
  try {
131
143
  fs.writeFileSync(this.config.file, output);
132
- this.log('success', 'OpenAPI v3 Documentation Successfully Written')
144
+ this.log('OpenAPI v3 Documentation Successfully Written', this.logTypes.SUCCESS)
133
145
  } catch (err) {
134
- this.log('error', `ERROR: An error was thrown whilst writing the openAPI Documentation`)
146
+ this.log(`ERROR: An error was thrown whilst writing the openAPI Documentation`, this.logTypes.ERROR)
135
147
  throw new this.serverless.classes.Error(err)
136
148
  }
137
149
  }
@@ -141,19 +153,19 @@ class OpenAPIGenerator {
141
153
 
142
154
  await generator.parse()
143
155
  .catch(err => {
144
- this.log('error', `ERROR: An error was thrown generating the OpenAPI v3 documentation`)
156
+ this.log(`ERROR: An error was thrown generating the OpenAPI v3 documentation`, this.logTypes.ERROR)
145
157
  throw new this.serverless.classes.Error(err)
146
158
  })
147
159
 
148
160
  await generator.validate()
149
161
  .catch(err => {
150
- this.log('error', `ERROR: An error was thrown validating the OpenAPI v3 documentation`)
162
+ this.log(`ERROR: An error was thrown validating the OpenAPI v3 documentation`, this.logTypes.ERROR)
151
163
  this.validationErrorDetails(err)
152
164
  throw new this.serverless.classes.Error(err)
153
165
  })
154
166
 
155
167
 
156
- this.log('success', 'OpenAPI v3 Documentation Successfully Generated')
168
+ this.log('OpenAPI v3 Documentation Successfully Generated', this.logTypes.SUCCESS)
157
169
 
158
170
  return generator.openAPI
159
171
  }
@@ -161,16 +173,16 @@ class OpenAPIGenerator {
161
173
  createPostman(openAPI) {
162
174
  const postmanGeneration = (err, result) => {
163
175
  if (err) {
164
- this.log('error', `ERROR: An error was thrown when generating the postman collection`)
176
+ this.log(`ERROR: An error was thrown when generating the postman collection`, this.logTypes.ERROR)
165
177
  throw new this.serverless.classes.Error(err)
166
178
  }
167
179
 
168
- this.log('success', 'postman collection v2 Documentation Successfully Generated')
180
+ this.log('postman collection v2 Documentation Successfully Generated', this.logTypes.SUCCESS)
169
181
  try {
170
182
  fs.writeFileSync(this.config.postmanCollection, JSON.stringify(result.output[0].data))
171
- this.log('success', 'postman collection v2 Documentation Successfully Written')
183
+ this.log('postman collection v2 Documentation Successfully Written', this.logTypes.SUCCESS)
172
184
  } catch (err) {
173
- this.log('error', `ERROR: An error was thrown whilst writing the postman collection`)
185
+ this.log(`ERROR: An error was thrown whilst writing the postman collection`, this.logTypes.ERROR)
174
186
  throw new this.serverless.classes.Error(err)
175
187
  }
176
188
  }
@@ -204,7 +216,6 @@ class OpenAPIGenerator {
204
216
  ((config.format === 'yaml') ? 'openapi.yml' : 'openapi.json');
205
217
 
206
218
  this.log(
207
- this.defaultLog,
208
219
  `${chalk.bold.green('[OPTIONS]')}
209
220
  openApiVersion: "${chalk.bold.green(String(config.openApiVersion))}"
210
221
  format: "${chalk.bold.green(config.format)}"
@@ -217,9 +228,9 @@ class OpenAPIGenerator {
217
228
  }
218
229
 
219
230
  validationErrorDetails(validationError) {
220
- this.log('error', `${chalk.bold.yellow('[VALIDATION]')} Failed to validate OpenAPI document: \n`);
221
- this.log('error', `${chalk.bold.yellow('Context:')} ${JSON.stringify(validationError.options.context[validationError.options.context.length-1], null, 2)}\n`);
222
- this.log('error', `${chalk.bold.yellow('Error Message:')} ${JSON.stringify(validationError.message, null, 2)}\n`);
231
+ this.log(`${chalk.bold.yellow('[VALIDATION]')} Failed to validate OpenAPI document: \n`, this.logTypes.ERROR);
232
+ this.log(`${chalk.bold.yellow('Context:')} ${JSON.stringify(validationError.options.context[validationError.options.context.length-1], null, 2)}\n`, this.logTypes.ERROR);
233
+ this.log(`${chalk.bold.yellow('Error Message:')} ${JSON.stringify(validationError.message, null, 2)}\n`, this.logTypes.ERROR);
223
234
  }
224
235
  }
225
236