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 +7 -0
- package/package.json +2 -2
- package/src/definitionGenerator.js +73 -0
- package/src/openAPIGenerator.js +27 -16
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.
|
|
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.
|
|
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 || ''
|
package/src/openAPIGenerator.js
CHANGED
|
@@ -12,7 +12,19 @@ class OpenAPIGenerator {
|
|
|
12
12
|
this.logOutput = log;
|
|
13
13
|
this.serverless = serverless
|
|
14
14
|
this.options = options
|
|
15
|
-
|
|
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
|
|
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(
|
|
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('
|
|
144
|
+
this.log('OpenAPI v3 Documentation Successfully Written', this.logTypes.SUCCESS)
|
|
133
145
|
} catch (err) {
|
|
134
|
-
this.log(
|
|
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(
|
|
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(
|
|
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('
|
|
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(
|
|
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('
|
|
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('
|
|
183
|
+
this.log('postman collection v2 Documentation Successfully Written', this.logTypes.SUCCESS)
|
|
172
184
|
} catch (err) {
|
|
173
|
-
this.log(
|
|
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(
|
|
221
|
-
this.log(
|
|
222
|
-
this.log(
|
|
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
|
|