serverless-openapi-documenter 0.0.11 → 0.0.15
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 +47 -9
- package/package.json +2 -2
- package/src/definitionGenerator.js +22 -18
- package/src/openAPIGenerator.js +26 -18
- package/test/serverless 2/serverless.yml +19 -3
- package/test/serverless 3/serverless.yml +23 -13
package/README.md
CHANGED
|
@@ -15,13 +15,13 @@ Originally based off of: https://github.com/temando/serverless-openapi-documenta
|
|
|
15
15
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
|
-
This plugin works for Serverless 2.x and up.
|
|
18
|
+
This plugin works for Serverless 2.x and up and only supports node.js 14 and up.
|
|
19
19
|
|
|
20
20
|
To add this plugin to your package.json:
|
|
21
21
|
|
|
22
22
|
**Using npm:**
|
|
23
23
|
```bash
|
|
24
|
-
npm install --save-dev serverless-openapi-documenter
|
|
24
|
+
npm install --save-dev serverless-openapi-documenter
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Next you need to add the plugin to the `plugins` section of your `serverless.yml` file.
|
|
@@ -112,7 +112,6 @@ custom:
|
|
|
112
112
|
version: '1'
|
|
113
113
|
title: 'My API'
|
|
114
114
|
description: 'This is my API'
|
|
115
|
-
models: {}
|
|
116
115
|
externalDocumentation:
|
|
117
116
|
url: https://google.com
|
|
118
117
|
description: A link to google
|
|
@@ -125,8 +124,11 @@ custom:
|
|
|
125
124
|
externalDocumentation:
|
|
126
125
|
url: https://npmjs.com
|
|
127
126
|
description: A link to npm
|
|
127
|
+
models: {}
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
Mostly everything here is optional. A version from a UUID will be generated for you if you don't specify one, title will be the name of your service if you don't specify one.
|
|
131
|
+
|
|
130
132
|
These configurations can be quite verbose; you can separate it out into it's own file, such as `serverless.doc.yml` as below:
|
|
131
133
|
|
|
132
134
|
```yml
|
|
@@ -146,9 +148,10 @@ For more info on `serverless.yml` syntax, see their docs.
|
|
|
146
148
|
|
|
147
149
|
#### Models
|
|
148
150
|
|
|
149
|
-
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.
|
|
151
|
+
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.
|
|
150
152
|
|
|
151
|
-
The
|
|
153
|
+
The first way of writing the model is:
|
|
154
|
+
*required* directives for the models section are as follow:
|
|
152
155
|
|
|
153
156
|
* `name`: the name of the schema
|
|
154
157
|
* `description`: a description of the schema
|
|
@@ -180,6 +183,40 @@ custom:
|
|
|
180
183
|
type: "string"
|
|
181
184
|
```
|
|
182
185
|
|
|
186
|
+
The Second way of writing the models:
|
|
187
|
+
|
|
188
|
+
* `name`: the name of the schema
|
|
189
|
+
* `description`: a description of the schema
|
|
190
|
+
* `content`: an Object made up of the contentType and the schema, as shown below
|
|
191
|
+
|
|
192
|
+
```yml
|
|
193
|
+
custom:
|
|
194
|
+
documentation:
|
|
195
|
+
models:
|
|
196
|
+
- name: "ErrorResponse"
|
|
197
|
+
description: "This is an error"
|
|
198
|
+
content:
|
|
199
|
+
application/json:
|
|
200
|
+
schema: ${file(models/ErrorResponse.json)}
|
|
201
|
+
- name: "PutDocumentResponse"
|
|
202
|
+
description: "PUT Document response model (external reference example)"
|
|
203
|
+
content:
|
|
204
|
+
application/json:
|
|
205
|
+
schema: ${file(models/PutDocumentResponse.json)}
|
|
206
|
+
- name: "PutDocumentRequest"
|
|
207
|
+
description: "PUT Document request model (inline example)"
|
|
208
|
+
content:
|
|
209
|
+
application/json:
|
|
210
|
+
schema:
|
|
211
|
+
$schema: "http://json-schema.org/draft-04/schema#"
|
|
212
|
+
properties:
|
|
213
|
+
SomeObject:
|
|
214
|
+
type: "object"
|
|
215
|
+
properties:
|
|
216
|
+
SomeAttribute:
|
|
217
|
+
type: "string"
|
|
218
|
+
```
|
|
219
|
+
|
|
183
220
|
#### Functions
|
|
184
221
|
|
|
185
222
|
To define the documentation for a given function event, you need to create a `documentation` attribute for your http event in your `serverless.yml` file.
|
|
@@ -206,11 +243,12 @@ The `documentation` section of the event configuration can contain the following
|
|
|
206
243
|
```yml
|
|
207
244
|
functions:
|
|
208
245
|
createUser:
|
|
209
|
-
handler:
|
|
246
|
+
handler: handler.create
|
|
210
247
|
events:
|
|
211
248
|
- http:
|
|
212
|
-
path:
|
|
213
|
-
method:
|
|
249
|
+
path: create
|
|
250
|
+
method: post
|
|
251
|
+
summary:
|
|
214
252
|
documentation:
|
|
215
253
|
summary: "Create User"
|
|
216
254
|
description: "Creates a user and then sends a generated password email"
|
|
@@ -375,7 +413,7 @@ Please view the example [serverless.yml](test/serverless\ 2/serverless.yml).
|
|
|
375
413
|
|
|
376
414
|
## Notes on schemas
|
|
377
415
|
|
|
378
|
-
Schemas can be either: inline, in file or externally hosted. If they're inline or in file, the plugin will attempt to normalise the schema to [OpenAPI 3.0.X specification](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#schemaObject).
|
|
416
|
+
Schemas can be either: inline, in file or externally hosted. If they're inline or in file, the plugin will attempt to normalise the schema to [OpenAPI 3.0.X specification](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#schemaObject).
|
|
379
417
|
|
|
380
418
|
If they exist as an external reference, for instance:
|
|
381
419
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-openapi-documenter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@apidevtools/json-schema-ref-parser": "^9.0.9",
|
|
32
32
|
"chalk": "^4.1.2",
|
|
33
33
|
"js-yaml": "^4.1.0",
|
|
34
|
-
"json-schema-for-openapi": "^0.1.
|
|
34
|
+
"json-schema-for-openapi": "^0.1.5",
|
|
35
35
|
"oas-validator": "^5.0.8",
|
|
36
36
|
"openapi-to-postmanv2": "^3.2.0",
|
|
37
37
|
"swagger2openapi": "^7.0.8",
|
|
@@ -33,7 +33,7 @@ class DefinitionGenerator {
|
|
|
33
33
|
} catch (err) {
|
|
34
34
|
this.refParserOptions = {}
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async parse() {
|
|
@@ -42,7 +42,7 @@ class DefinitionGenerator {
|
|
|
42
42
|
.catch(err => {
|
|
43
43
|
throw err
|
|
44
44
|
})
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
if (this.serverless.service.custom.documentation.servers) {
|
|
47
47
|
const servers = this.createServers(this.serverless.service.custom.documentation.servers)
|
|
48
48
|
Object.assign(this.openAPI, {servers: servers})
|
|
@@ -73,11 +73,10 @@ class DefinitionGenerator {
|
|
|
73
73
|
async createPaths() {
|
|
74
74
|
const paths = {}
|
|
75
75
|
const httpFunctions = this.getHTTPFunctions()
|
|
76
|
-
|
|
77
76
|
for (const httpFunction of httpFunctions) {
|
|
78
77
|
for (const event of httpFunction.event) {
|
|
79
78
|
if (event?.http?.documentation || event?.httpApi?.documentation) {
|
|
80
|
-
const documentation = event
|
|
79
|
+
const documentation = event?.http?.documentation || event?.httpApi?.documentation
|
|
81
80
|
|
|
82
81
|
let opId
|
|
83
82
|
if (this.operationIds.includes(httpFunction.functionInfo.name) === false) {
|
|
@@ -87,11 +86,11 @@ class DefinitionGenerator {
|
|
|
87
86
|
opId = `${httpFunction.functionInfo.name}-${uuid()}`
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
const path = await this.createOperationObject(event
|
|
89
|
+
const path = await this.createOperationObject(event?.http?.method || event?.httpApi?.method, documentation, opId)
|
|
91
90
|
.catch(err => {
|
|
92
91
|
throw err
|
|
93
92
|
})
|
|
94
|
-
|
|
93
|
+
|
|
95
94
|
if (httpFunction.functionInfo?.summary)
|
|
96
95
|
path.summary = httpFunction.functionInfo.summary
|
|
97
96
|
|
|
@@ -103,10 +102,10 @@ class DefinitionGenerator {
|
|
|
103
102
|
path.servers = servers
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
let slashPath = event
|
|
105
|
+
let slashPath = event?.http?.path || event.httpApi?.path
|
|
107
106
|
const pathStart = new RegExp(/^\//, 'g')
|
|
108
107
|
if (pathStart.test(slashPath) === false) {
|
|
109
|
-
slashPath = `/${event
|
|
108
|
+
slashPath = `/${event?.http?.path||event.httpApi?.path}`
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
Object.assign(paths, {[slashPath]: path})
|
|
@@ -152,7 +151,7 @@ class DefinitionGenerator {
|
|
|
152
151
|
// const documentation = this.serverless.service.custom.documentation
|
|
153
152
|
// if (documentation.externalDocumentation) {
|
|
154
153
|
// // Object.assign(this.openAPI, {externalDocs: {...documentation.externalDocumentation}})
|
|
155
|
-
// return
|
|
154
|
+
// return
|
|
156
155
|
// }
|
|
157
156
|
}
|
|
158
157
|
|
|
@@ -240,7 +239,7 @@ class DefinitionGenerator {
|
|
|
240
239
|
obj.servers = servers
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
return {[method]: obj}
|
|
242
|
+
return {[method.toLowerCase()]: obj}
|
|
244
243
|
}
|
|
245
244
|
|
|
246
245
|
async createResponses(documentation) {
|
|
@@ -292,14 +291,19 @@ class DefinitionGenerator {
|
|
|
292
291
|
if (mediaTypeDocumentation.examples)
|
|
293
292
|
obj.examples = this.createExamples(mediaTypeDocumentation.examples)
|
|
294
293
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
294
|
+
let schema
|
|
295
|
+
if (mediaTypeDocumentation?.content) {
|
|
296
|
+
schema = mediaTypeDocumentation.content[contentKey].schema
|
|
297
|
+
} else if (mediaTypeDocumentation?.contentType && mediaTypeDocumentation.schema) {
|
|
298
|
+
schema = mediaTypeDocumentation.schema
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const schemaRef = await this.schemaCreator(schema, mediaTypeDocumentation.name)
|
|
302
|
+
.catch(err => {
|
|
303
|
+
throw err
|
|
304
|
+
})
|
|
305
|
+
obj.schema = {
|
|
306
|
+
$ref: schemaRef
|
|
303
307
|
}
|
|
304
308
|
|
|
305
309
|
Object.assign(mediaTypeObj, {[contentKey]: obj})
|
package/src/openAPIGenerator.js
CHANGED
|
@@ -83,10 +83,17 @@ class OpenAPIGenerator {
|
|
|
83
83
|
})
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
log(type = this.defaultLog,
|
|
86
|
+
log(type = this.defaultLog, str) {
|
|
87
87
|
switch(this.serverless.version[0]) {
|
|
88
88
|
case '2':
|
|
89
|
-
|
|
89
|
+
let colouredString = str
|
|
90
|
+
if (type === 'error') {
|
|
91
|
+
colouredString = chalk.bold.red(`✖ ${str}`)
|
|
92
|
+
} else if (type === 'success') {
|
|
93
|
+
colouredString = chalk.bold.green(`✓ ${str}`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.serverless.cli.log(colouredString)
|
|
90
97
|
break
|
|
91
98
|
|
|
92
99
|
case '3':
|
|
@@ -106,32 +113,32 @@ class OpenAPIGenerator {
|
|
|
106
113
|
|
|
107
114
|
await generator.parse()
|
|
108
115
|
.catch(err => {
|
|
109
|
-
this.log('error',
|
|
116
|
+
this.log('error', `ERROR: An error was thrown generating the OpenAPI v3 documentation`)
|
|
110
117
|
throw new this.serverless.classes.Error(err)
|
|
111
118
|
})
|
|
112
119
|
|
|
113
120
|
const valid = await generator.validate()
|
|
114
121
|
.catch(err => {
|
|
115
|
-
this.log('error',
|
|
122
|
+
this.log('error', `ERROR: An error was thrown validating the OpenAPI v3 documentation`)
|
|
116
123
|
throw new this.serverless.classes.Error(err)
|
|
117
124
|
})
|
|
118
125
|
|
|
119
126
|
if (valid)
|
|
120
|
-
this.log(
|
|
127
|
+
this.log('success', 'OpenAPI v3 Documentation Successfully Generated')
|
|
121
128
|
|
|
122
129
|
if (config.postmanCollection) {
|
|
123
130
|
const postmanGeneration = (err, result) => {
|
|
124
131
|
if (err) {
|
|
125
|
-
this.log('error',
|
|
132
|
+
this.log('error', `ERROR: An error was thrown when generating the postman collection`)
|
|
126
133
|
throw new this.serverless.classes.Error(err)
|
|
127
134
|
}
|
|
128
135
|
|
|
129
|
-
this.log(
|
|
136
|
+
this.log('success', 'postman collection v2 Documentation Successfully Generated')
|
|
130
137
|
try {
|
|
131
138
|
fs.writeFileSync(config.postmanCollection, JSON.stringify(result.output[0].data))
|
|
132
|
-
this.log(
|
|
139
|
+
this.log('success', 'postman collection v2 Documentation Successfully Written')
|
|
133
140
|
} catch (err) {
|
|
134
|
-
this.log('error',
|
|
141
|
+
this.log('error', `ERROR: An error was thrown whilst writing the postman collection`)
|
|
135
142
|
throw new this.serverless.classes.Error(err)
|
|
136
143
|
}
|
|
137
144
|
}
|
|
@@ -155,9 +162,9 @@ class OpenAPIGenerator {
|
|
|
155
162
|
}
|
|
156
163
|
try {
|
|
157
164
|
fs.writeFileSync(config.file, output);
|
|
158
|
-
this.log(
|
|
165
|
+
this.log('success', 'OpenAPI v3 Documentation Successfully Written')
|
|
159
166
|
} catch (err) {
|
|
160
|
-
this.log('error',
|
|
167
|
+
this.log('error', `ERROR: An error was thrown whilst writing the openAPI Documentation`)
|
|
161
168
|
throw new this.serverless.classes.Error(err)
|
|
162
169
|
}
|
|
163
170
|
}
|
|
@@ -177,7 +184,8 @@ class OpenAPIGenerator {
|
|
|
177
184
|
config.postmanCollection = this.serverless.processedInput.options.postmanCollection || null
|
|
178
185
|
|
|
179
186
|
if (['yaml', 'json'].indexOf(config.format.toLowerCase()) < 0) {
|
|
180
|
-
throw new Error('Invalid Output Format Specified - must be one of "yaml" or "json"');
|
|
187
|
+
// throw new Error('Invalid Output Format Specified - must be one of "yaml" or "json"');
|
|
188
|
+
throw new this.serverless.classes.Error('Invalid Output Format Specified - must be one of "yaml" or "json"')
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
config.file = this.serverless.processedInput.options.output ||
|
|
@@ -185,12 +193,12 @@ class OpenAPIGenerator {
|
|
|
185
193
|
|
|
186
194
|
this.log(
|
|
187
195
|
this.defaultLog,
|
|
188
|
-
`${chalk.bold.green('[OPTIONS]')}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
196
|
+
`${chalk.bold.green('[OPTIONS]')}
|
|
197
|
+
openApiVersion: "${chalk.bold.green(String(config.openApiVersion))}"
|
|
198
|
+
format: "${chalk.bold.green(config.format)}"
|
|
199
|
+
output file: "${chalk.bold.green(config.file)}"
|
|
200
|
+
indentation: "${chalk.bold.green(String(config.indent))}"
|
|
201
|
+
${config.postmanCollection ? `postman collection: ${chalk.bold.green(config.postmanCollection)}`: `\n\n`}`
|
|
194
202
|
)
|
|
195
203
|
|
|
196
204
|
return config
|
|
@@ -19,7 +19,7 @@ functions:
|
|
|
19
19
|
documentation:
|
|
20
20
|
summary: Create User
|
|
21
21
|
description: Creates a user and then sends a generated password email
|
|
22
|
-
tags:
|
|
22
|
+
tags:
|
|
23
23
|
- jesus
|
|
24
24
|
externalDocumentation:
|
|
25
25
|
url: https://bing.com
|
|
@@ -64,12 +64,28 @@ functions:
|
|
|
64
64
|
- http:
|
|
65
65
|
path: delete
|
|
66
66
|
method: delete
|
|
67
|
-
|
|
67
|
+
patchUser:
|
|
68
|
+
handler: handler.patch
|
|
69
|
+
events:
|
|
70
|
+
- httpApi:
|
|
71
|
+
path: /patch/
|
|
72
|
+
method: PATCH
|
|
73
|
+
documentation:
|
|
74
|
+
summary: Patch a User
|
|
75
|
+
description: Patch details about the user
|
|
76
|
+
tags:
|
|
77
|
+
- patching
|
|
78
|
+
methodResponses:
|
|
79
|
+
- statusCode: 200
|
|
80
|
+
responseBody:
|
|
81
|
+
description: A user object along with generated API Keys
|
|
82
|
+
responseModels:
|
|
83
|
+
application/json: PutDocumentResponse
|
|
68
84
|
custom:
|
|
69
85
|
documentation:
|
|
70
86
|
description: This is a description of what this does
|
|
71
87
|
version: 1.0.0
|
|
72
|
-
tags:
|
|
88
|
+
tags:
|
|
73
89
|
- name: jesus
|
|
74
90
|
description: jesus was a man
|
|
75
91
|
externalDocumentation:
|
|
@@ -12,11 +12,16 @@ functions:
|
|
|
12
12
|
handler: handler.create
|
|
13
13
|
events:
|
|
14
14
|
- http:
|
|
15
|
-
path: create
|
|
15
|
+
path: create/{username}
|
|
16
16
|
method: post
|
|
17
17
|
documentation:
|
|
18
18
|
summary: Create User
|
|
19
19
|
description: Creates a user and then sends a generated password email
|
|
20
|
+
tags:
|
|
21
|
+
- jesus
|
|
22
|
+
externalDocumentation:
|
|
23
|
+
url: https://bing.com
|
|
24
|
+
description: A link to bing
|
|
20
25
|
requestBody:
|
|
21
26
|
description: A user information object
|
|
22
27
|
requestModels:
|
|
@@ -54,25 +59,30 @@ functions:
|
|
|
54
59
|
|
|
55
60
|
custom:
|
|
56
61
|
documentation:
|
|
62
|
+
description: This is a description of what this does
|
|
63
|
+
version: 1.0.0
|
|
57
64
|
models:
|
|
58
65
|
- name: ErrorResponse
|
|
59
66
|
description: This is an error
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
content:
|
|
68
|
+
application/json:
|
|
69
|
+
schema: ${file(../models/ErrorResponse.json)}
|
|
62
70
|
|
|
63
71
|
- name: PutDocumentResponse
|
|
64
72
|
description: PUT Document response model (external reference example)
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
content:
|
|
74
|
+
application/json:
|
|
75
|
+
schema: ${file(../models/PutDocumentResponse.json)}
|
|
67
76
|
|
|
68
77
|
- name: PutDocumentRequest
|
|
69
78
|
description: PUT Document request model (inline example)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
SomeObject:
|
|
75
|
-
type: object
|
|
79
|
+
content:
|
|
80
|
+
application/json:
|
|
81
|
+
schema:
|
|
82
|
+
$schema: http://json-schema.org/draft-04/schema#
|
|
76
83
|
properties:
|
|
77
|
-
|
|
78
|
-
type:
|
|
84
|
+
SomeObject:
|
|
85
|
+
type: object
|
|
86
|
+
properties:
|
|
87
|
+
SomeAttribute:
|
|
88
|
+
type: string
|