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 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 up and only supports node.js 14 and up.
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
- | externalDocs.description | custom.documentation.externalDocumentation.description |
93
- | externalDocs.url | custom.documentation.externalDocumentation.url |
94
- | security | custom.documentation.security |
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` need 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)) and a url.
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.103",
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.21.0",
53
+ "openapi-to-postmanv2": "^4.24.0",
50
54
  "swagger2openapi": "^7.0.8",
51
- "uuid": "^9.0.1"
55
+ "uuid": "^10.0.0"
52
56
  },
53
57
  "engines": {
54
- "node": ">=14"
58
+ "node": ">=16"
55
59
  },
56
60
  "devDependencies": {
57
- "chai": "^4.3.10",
58
- "mocha": "^10.4.0",
59
- "nock": "^13.5.4",
60
- "sinon": "^18.0.0"
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
- // for (const key of Object.keys(documentation)) {
171
- // if (/^[x\-]/i.test(key)) {
172
- // Object.assign(info, { [key]: documentation[key] });
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 });
@@ -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(