serverless-openapi-documenter 0.0.103 → 0.0.106
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 +77 -17
- package/package.json +12 -8
- package/src/definitionGenerator.js +64 -7
- package/src/schemaHandler.js +3 -3
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Originally based off of: https://github.com/temando/serverless-openapi-documenta
|
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
21
|
-
This plugin works for Serverless 2.x and
|
|
21
|
+
This plugin works for Serverless (2.x, 3.x and 4.x) and only supports node.js 14 and up.
|
|
22
22
|
|
|
23
23
|
To add this plugin to your package.json:
|
|
24
24
|
|
|
@@ -79,26 +79,30 @@ Options:
|
|
|
79
79
|
|
|
80
80
|
| OpenAPI field | Serverless field |
|
|
81
81
|
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
-
| info.title | custom.documentation.title OR service
|
|
83
|
-
| info.description | custom.documentation.description OR blank string
|
|
84
|
-
| info.version | custom.documentation.version OR random v4 uuid if not provided
|
|
85
|
-
| info.termsOfService | custom.documentation.termsOfService
|
|
82
|
+
| info.title | `custom.documentation.title` OR service |
|
|
83
|
+
| info.description | `custom.documentation.description` OR blank string |
|
|
84
|
+
| info.version | `custom.documentation.version` OR random v4 uuid if not provided |
|
|
85
|
+
| info.termsOfService | `custom.documentation.termsOfService` |
|
|
86
86
|
| info.contact | custom.documentation.contact |
|
|
87
|
-
| info.contact.name | custom.documentation.contact.name OR blank string
|
|
88
|
-
| info.contact.url | custom.documentation.contact.url if provided
|
|
87
|
+
| info.contact.name | `custom.documentation.contact.name` OR blank string |
|
|
88
|
+
| info.contact.url | `custom.documentation.contact.url` if provided |
|
|
89
|
+
| info.contact.x- | `custom.documentation.contact.x-` extended specifications provided |
|
|
89
90
|
| info.license | custom.documentation.license |
|
|
90
|
-
| info.license.name | custom.documentation.license.name OR blank string
|
|
91
|
-
| info.license.url | custom.documentation.license.url if provided
|
|
92
|
-
|
|
|
93
|
-
| externalDocs.
|
|
94
|
-
|
|
|
91
|
+
| info.license.name | `custom.documentation.license.name` OR blank string |
|
|
92
|
+
| info.license.url | `custom.documentation.license.url` if provided |
|
|
93
|
+
| info.license.x- | `custom.documentation.license.x-` if extended specifications provided provided |
|
|
94
|
+
| externalDocs.description | `custom.documentation.externalDocumentation.description ` |
|
|
95
|
+
| externalDocs.url | `custom.documentation.externalDocumentation.url` |
|
|
96
|
+
| x-tagGroups | `custom.documentation.x-tagGroups` if provided |
|
|
97
|
+
| security | `custom.documentation.security` |
|
|
95
98
|
| servers[].description | custom.documentation.servers.description |
|
|
96
99
|
| servers[].url | custom.documentation.servers.url |
|
|
97
100
|
| servers[].variables | custom.documentation.servers.variables |
|
|
98
|
-
| tags[].name | custom.documentation.tags.name
|
|
99
|
-
| tags[].description | custom.documentation.tags.description
|
|
100
|
-
| tags[].externalDocs.url | custom.documentation.tags.externalDocumentation.url
|
|
101
|
-
| tags[].externalDocs.description | custom.documentation.tags.externalDocumentation.description
|
|
101
|
+
| tags[].name | `custom.documentation.tags.name` |
|
|
102
|
+
| tags[].description | `custom.documentation.tags.description` |
|
|
103
|
+
| tags[].externalDocs.url | `custom.documentation.tags.externalDocumentation.url` |
|
|
104
|
+
| tags[].externalDocs.description | `custom.documentation.tags.externalDocumentation.description` |
|
|
105
|
+
| tags[].externalDocs.x- | `custom.documentation.tags.externalDocumentation.x-` if extended specifications provided |
|
|
102
106
|
| path[path] | functions.functions.events.[http OR httpApi].path |
|
|
103
107
|
| path[path].servers[].description | functions.functions.servers.description |
|
|
104
108
|
| path[path].servers[].url | functions.functions.servers.url |
|
|
@@ -189,7 +193,9 @@ custom:
|
|
|
189
193
|
email: John@example.com
|
|
190
194
|
```
|
|
191
195
|
|
|
192
|
-
These fields are optional, though `url` and `email`
|
|
196
|
+
These fields are optional, though `url` needs to in the form of a URL and `email` needs to be in the format of an email address (ed: what that might be, I'm not 100% sure... go read the email RFC(s)).
|
|
197
|
+
|
|
198
|
+
This can be extended using the `^x-` specification extension.
|
|
193
199
|
|
|
194
200
|
#### License
|
|
195
201
|
|
|
@@ -205,6 +211,8 @@ custom:
|
|
|
205
211
|
|
|
206
212
|
Name is required but `url` is optional and must be in the format of a url.
|
|
207
213
|
|
|
214
|
+
This can be extended using the `^x-` specification extension.
|
|
215
|
+
|
|
208
216
|
#### Extended Fields
|
|
209
217
|
|
|
210
218
|
You can also add extended fields to the documentation object:
|
|
@@ -225,6 +233,58 @@ custom:
|
|
|
225
233
|
|
|
226
234
|
`other-field` here will not make it to the generated OpenAPI schema.
|
|
227
235
|
|
|
236
|
+
Currently extended specification fields defined under the `documentation` tag will sit under the OpenAPI `info` object e.g.
|
|
237
|
+
|
|
238
|
+
```yml
|
|
239
|
+
custom:
|
|
240
|
+
documentation:
|
|
241
|
+
title: myService
|
|
242
|
+
x-other-field: This is an extended field
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
converts to:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"info": {
|
|
250
|
+
"title": "myService",
|
|
251
|
+
"x-other-field": "This is an extended field"
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
An exception to this is Redocly `x-tagGroups`. If defined, they will sit at the root level of the OpenAPI specification, e.g.
|
|
257
|
+
|
|
258
|
+
```yml
|
|
259
|
+
custom:
|
|
260
|
+
documentation:
|
|
261
|
+
title: myService
|
|
262
|
+
x-other-field: This is an extended field
|
|
263
|
+
x-tagGroups:
|
|
264
|
+
- name: Customers
|
|
265
|
+
tags:
|
|
266
|
+
- Customers
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
converts to:
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"info": {
|
|
274
|
+
"title": "myService",
|
|
275
|
+
"x-other-field": "This is an extended field"
|
|
276
|
+
},
|
|
277
|
+
"x-tagGroups": [
|
|
278
|
+
{
|
|
279
|
+
"name": "Customers",
|
|
280
|
+
"tags": ["Customers"]
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### Moving documentation to a separate file
|
|
287
|
+
|
|
228
288
|
These configurations can be quite verbose; you can separate it out into it's own file, such as `serverless.doc.yml` as below:
|
|
229
289
|
|
|
230
290
|
```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.106",
|
|
4
4
|
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -9,12 +9,16 @@
|
|
|
9
9
|
"Serverless 2",
|
|
10
10
|
"serverless3",
|
|
11
11
|
"Serverless 3",
|
|
12
|
+
"Serverless4",
|
|
13
|
+
"Serverless 4",
|
|
12
14
|
"serverless framework",
|
|
13
15
|
"serverless framework plugin",
|
|
14
16
|
"serverless plugin",
|
|
17
|
+
"swagger",
|
|
15
18
|
"openAPI",
|
|
16
19
|
"openAPIv3",
|
|
17
20
|
"openAPI3",
|
|
21
|
+
"Postman",
|
|
18
22
|
"PostmanCollections",
|
|
19
23
|
"Postman-Collections",
|
|
20
24
|
"Postman Collections",
|
|
@@ -46,17 +50,17 @@
|
|
|
46
50
|
"js-yaml": "^4.1.0",
|
|
47
51
|
"json-schema-for-openapi": "^0.5.0",
|
|
48
52
|
"lodash.isequal": "^4.5.0",
|
|
49
|
-
"openapi-to-postmanv2": "^4.
|
|
53
|
+
"openapi-to-postmanv2": "^4.24.0",
|
|
50
54
|
"swagger2openapi": "^7.0.8",
|
|
51
|
-
"uuid": "^
|
|
55
|
+
"uuid": "^10.0.0"
|
|
52
56
|
},
|
|
53
57
|
"engines": {
|
|
54
|
-
"node": ">=
|
|
58
|
+
"node": ">=16"
|
|
55
59
|
},
|
|
56
60
|
"devDependencies": {
|
|
57
|
-
"chai": "^4.
|
|
58
|
-
"mocha": "^10.
|
|
59
|
-
"nock": "^13.5.
|
|
60
|
-
"sinon": "^
|
|
61
|
+
"chai": "^4.5.0",
|
|
62
|
+
"mocha": "^10.7.3",
|
|
63
|
+
"nock": "^13.5.5",
|
|
64
|
+
"sinon": "^19.0.2"
|
|
61
65
|
}
|
|
62
66
|
}
|
|
@@ -154,6 +154,13 @@ class DefinitionGenerator {
|
|
|
154
154
|
if (documentation.contact.url) contactObj.url = documentation.contact.url;
|
|
155
155
|
|
|
156
156
|
contactObj.email = documentation.contact.email || "";
|
|
157
|
+
|
|
158
|
+
const extendedSpec = this.extendSpecification(documentation.contact);
|
|
159
|
+
|
|
160
|
+
if (Object.keys(extendedSpec).length) {
|
|
161
|
+
Object.assign(contactObj, extendedSpec);
|
|
162
|
+
}
|
|
163
|
+
|
|
157
164
|
Object.assign(info, { contact: contactObj });
|
|
158
165
|
}
|
|
159
166
|
|
|
@@ -164,14 +171,22 @@ class DefinitionGenerator {
|
|
|
164
171
|
if (documentation.license.url)
|
|
165
172
|
licenseObj.url = documentation.license.url || "";
|
|
166
173
|
|
|
174
|
+
const extendedSpec = this.extendSpecification(documentation.license);
|
|
175
|
+
|
|
176
|
+
if (Object.keys(extendedSpec).length) {
|
|
177
|
+
Object.assign(licenseObj, extendedSpec);
|
|
178
|
+
}
|
|
179
|
+
|
|
167
180
|
Object.assign(info, { license: licenseObj });
|
|
168
181
|
}
|
|
169
182
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
183
|
+
if (documentation["x-tagGroups"]) {
|
|
184
|
+
Object.assign(this.openAPI, {
|
|
185
|
+
"x-tagGroups": documentation["x-tagGroups"],
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
delete documentation["x-tagGroups"];
|
|
189
|
+
}
|
|
175
190
|
|
|
176
191
|
const extendedSpec = this.extendSpecification(documentation);
|
|
177
192
|
|
|
@@ -237,6 +252,29 @@ class DefinitionGenerator {
|
|
|
237
252
|
const serverDoc = servers;
|
|
238
253
|
const newServers = [];
|
|
239
254
|
|
|
255
|
+
const variableManipulation = (variables) => {
|
|
256
|
+
for (const key of Object.keys(variables)) {
|
|
257
|
+
if (variables[key].enum) {
|
|
258
|
+
const strEnum = variables[key].enum.map((enumVar) =>
|
|
259
|
+
enumVar.toString()
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
variables[key].enum = strEnum;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (variables[key].default)
|
|
266
|
+
variables[key].default = variables[key].default.toString();
|
|
267
|
+
|
|
268
|
+
const extendedSpec = this.extendSpecification(variables[key]);
|
|
269
|
+
|
|
270
|
+
if (Object.keys(extendedSpec).length) {
|
|
271
|
+
Object.assign(variables[key], extendedSpec);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return variables;
|
|
276
|
+
};
|
|
277
|
+
|
|
240
278
|
if (Array.isArray(serverDoc)) {
|
|
241
279
|
for (const server of serverDoc) {
|
|
242
280
|
const obj = {
|
|
@@ -248,7 +286,13 @@ class DefinitionGenerator {
|
|
|
248
286
|
}
|
|
249
287
|
|
|
250
288
|
if (server.variables) {
|
|
251
|
-
obj.variables = server.variables;
|
|
289
|
+
obj.variables = variableManipulation(server.variables);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const extendedSpec = this.extendSpecification(server);
|
|
293
|
+
|
|
294
|
+
if (Object.keys(extendedSpec).length) {
|
|
295
|
+
Object.assign(obj, extendedSpec);
|
|
252
296
|
}
|
|
253
297
|
|
|
254
298
|
newServers.push(obj);
|
|
@@ -263,7 +307,13 @@ class DefinitionGenerator {
|
|
|
263
307
|
}
|
|
264
308
|
|
|
265
309
|
if (servers.variables) {
|
|
266
|
-
obj.variables = servers.variables;
|
|
310
|
+
obj.variables = variableManipulation(servers.variables);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const extendedSpec = this.extendSpecification(servers);
|
|
314
|
+
|
|
315
|
+
if (Object.keys(extendedSpec).length) {
|
|
316
|
+
Object.assign(obj, extendedSpec);
|
|
267
317
|
}
|
|
268
318
|
|
|
269
319
|
newServers.push(obj);
|
|
@@ -297,6 +347,13 @@ class DefinitionGenerator {
|
|
|
297
347
|
tag.externalDocumentation
|
|
298
348
|
);
|
|
299
349
|
}
|
|
350
|
+
|
|
351
|
+
const extendedSpec = this.extendSpecification(tag);
|
|
352
|
+
|
|
353
|
+
if (Object.keys(extendedSpec).length) {
|
|
354
|
+
Object.assign(obj, extendedSpec);
|
|
355
|
+
}
|
|
356
|
+
|
|
300
357
|
tags.push(obj);
|
|
301
358
|
}
|
|
302
359
|
Object.assign(this.openAPI, { tags: tags });
|
package/src/schemaHandler.js
CHANGED
|
@@ -68,10 +68,10 @@ class SchemaHandler {
|
|
|
68
68
|
).catch((err) => {
|
|
69
69
|
if (err.errors) {
|
|
70
70
|
for (const error of err?.errors) {
|
|
71
|
-
this.__HTTPError(error);
|
|
71
|
+
this.__HTTPError(error, model);
|
|
72
72
|
}
|
|
73
73
|
} else {
|
|
74
|
-
this.__HTTPError(err);
|
|
74
|
+
this.__HTTPError(err, model);
|
|
75
75
|
}
|
|
76
76
|
return modelSchema;
|
|
77
77
|
});
|
|
@@ -228,7 +228,7 @@ class SchemaHandler {
|
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
__HTTPError(error) {
|
|
231
|
+
__HTTPError(error, model) {
|
|
232
232
|
if (error.message.includes("HTTP ERROR")) {
|
|
233
233
|
// throw err;
|
|
234
234
|
throw new Error(
|