serverless-openapi-documenter 0.0.1 → 0.0.4

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.
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: "[BUG]"
5
+ labels: ''
6
+ assignees: JaredCE
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ ```
16
+ serverless.yml
17
+ ...
18
+ ```
19
+ **Expected behavior**
20
+ A clear and concise description of what you expected to happen.
21
+
22
+ **Desktop (please complete the following information):**
23
+ - Serverless version: [e.g. 2.73.3]
24
+ - serverless-openapi-documenter version [e.g. 0.0.2]
25
+
26
+ **Additional context**
27
+ Add any other context about the problem here.
package/README.md CHANGED
@@ -4,56 +4,81 @@ This will generate an OpenAPI V3 (up to v3.0.3) file for you from your serverles
4
4
 
5
5
  Originally based off of: https://github.com/temando/serverless-openapi-documentation
6
6
 
7
+ ## Install
8
+
9
+ This plugin works for Serverless 2.x and up.
10
+
11
+ To add this plugin to your package.json:
12
+
13
+ **Using npm:**
14
+ ```bash
15
+ npm install --save-dev serverless-openapi-documenter
16
+ ```
17
+
18
+ Next you need to add the plugin to the `plugins` section of your `serverless.yml` file.
19
+
20
+ ```yml
21
+ plugins:
22
+ - serverless-openapi-documenter
23
+ ```
24
+
25
+ > Note: Add this plugin _after_ `serverless-offline` to prevent issues with `String.replaceAll` being overridden incorrectly.
26
+
7
27
  ## Adding documentation to serverless
8
28
 
9
29
  To Run: `serverless openapi generate -o openapi.json -f json -a 3.0.3 -p postman.json`
10
30
 
11
31
  Options:
12
32
 
13
- | Option: | What It Does: |
14
- |--------------|---------------|
15
- | -o filename | What filename the OpenAPI documentation should output under |
16
- | -f filetype | Whether to output the OpenAPI documentation as json or yaml |
17
- | -a OpenAPI Version | The version of OpenAPI v3 to conform too |
18
- | -p postman Collection filename | Whether to generate a postman collection from the OpenAPI v3 documentation and the filename to call it, json only |
19
-
33
+ ```
34
+ --output -o What filename the OpenAPI documentation should output under. Default: openapi.json
35
+ --format -f Whether to output the OpenAPI documentation as json or yaml. Default: json
36
+ --indent -i File indentation in spaces. Default: 2
37
+ --openApiVersion -a OpenAPI version to generate for. Default: 3.0.0
38
+ --postmanCollection -p Will generate a postman collection (from the generated openAPI documentation), in json only, if passed in. Default postman.json
39
+ ```
20
40
 
21
41
  ### OpenAPI Mapping
22
42
 
23
- | OpenAPI field | Serverless field |
24
- |--------------------|------------------------------------------------------------------------------------|
25
- | info.title | service |
26
- | info.description | custom.documentation.description || blank string |
27
- | info.version | custom.documentation.version || random v4 uuid if not provided |
28
- | path[path] | functions.functions.events.[http||httpApi].path |
29
- | path[path].summary | functions.functions.summary |
30
- | path[path].description | functions.functions.description |
31
- | path[path].[operation] | functions.functions.[http||httpApi].method |
32
- | path[path].[operation].summary | functions.functions.[http||httpApi].documentation.summary |
33
- | path[path].[operation].description | functions.functions.[http||httpApi].documentation.description |
34
- | path[path].[operation].operationId | functions.functions.[http||httpApi].documentation.operationId || functionName |
35
- | path[path].[operation].deprecated | functions.functions.[http||httpApi].documentation.deprecated |
36
- | path[path].[operation].parameters | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params |
37
- | path[path].[operation].parameters.name | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.name |
38
- | path[path].[operation].parameters.in | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params |
39
- | path[path].[operation].parameters.description | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.description |
40
- | path[path].[operation].parameters.required | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.required |
41
- | path[path].[operation].parameters.deprecated | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.deprecated |
42
- | path[path].[operation].parameters.allowEmptyValue | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.allowEmptyValue |
43
- | path[path].[operation].parameters.style | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.style |
44
- | path[path].[operation].parameters.explode | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.explode |
45
- | path[path].[operation].parameters.allowReserved | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.allowReserved |
46
- | path[path].[operation].parameters.schema | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.schema |
47
- | path[path].[operation].parameters.example | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.example |
48
- | path[path].[operation].parameters.examples | functions.functions.[http||httpApi].documentation.[path|query|cookie|header]Params.examples |
49
- | path[path].[operation].requestBody | functions.functions.[http||httpApi].documentation.requestBody |
50
- | path[path].[operation].requestBody.description | functions.functions.[http||httpApi].documentation.requestBody.description |
51
- | path[path].[operation].requestBody.required | functions.functions.[http||httpApi].documentation.requestBody.required |
52
- | path[path].[operation].requestBody.content | functions.functions.[http||httpApi].documentation.requestModels[contentType].name Links to custom.documentation.models.name |
53
- | path[path].[operation].responses | functions.functions.[http||httpApi].documentation.methodResponses |
54
- | path[path].[operation].requestBody.[statusCode] | functions.functions.[http||httpApi].documentation.methodResponses[statusCode] |
55
- | path[path].[operation].requestBody.[statusCode].description | functions.functions.[http||httpApi].documentation.methodResponses[statusCode].responseBody.description |
56
- | path[path].[operation].requestBody.[statusCode].content | functions.functions.[http||httpApi].documentation.methodResponses[statusCode].responseModels[contentType] Links to custom.documentation.models.name |
43
+ | OpenAPI field | Serverless field |
44
+ |--------------------------|------------------------------------------------------------------------------------|
45
+ | info.title | custom.documentation.title OR service |
46
+ | info.description | custom.documentation.description OR blank string |
47
+ | info.version | custom.documentation.version OR random v4 uuid if not provided |
48
+ | externalDocs.description | custom.documentation.externalDocumentation.description |
49
+ | externalDocs.url | custom.documenation.externalDocumentation.url |
50
+ | path[path] | functions.functions.events.[http OR httpApi].path |
51
+ | path[path].summary | functions.functions.summary |
52
+ | path[path].description | functions.functions.description |
53
+ | path[path].[operation] | functions.functions.[http OR httpApi].method |
54
+ | path[path].[operation].summary | functions.functions.[http OR httpApi].documentation.summary |
55
+ | path[path].[operation].description | functions.functions.[http OR httpApi].documentation.description |
56
+ | path[path].[operation].operationId | functions.functions.[http OR httpApi].documentation.operationId OR functionName |
57
+ | path[path].[operation].deprecated | functions.functions.[http OR httpApi].documentation.deprecated |
58
+ | path[path].[operation].externalDocs.description | functions.functions.[http OR httpApi].documentation.externalDocumentation.description |
59
+ | path[path].[operation].externalDocs.url | functions.functions.[http OR httpApi].documentation.externalDocumentation.url |
60
+ | path[path].[operation].deprecated | functions.functions.[http OR httpApi].documentation.deprecated |
61
+ | path[path].[operation].parameters | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params |
62
+ | path[path].[operation].parameters.name | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.name |
63
+ | path[path].[operation].parameters.in | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params |
64
+ | path[path].[operation].parameters.description | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.description |
65
+ | path[path].[operation].parameters.required | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.required |
66
+ | path[path].[operation].parameters.deprecated | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.deprecated |
67
+ | path[path].[operation].parameters.allowEmptyValue | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.allowEmptyValue |
68
+ | path[path].[operation].parameters.style | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.style |
69
+ | path[path].[operation].parameters.explode | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.explode |
70
+ | path[path].[operation].parameters.allowReserved | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.allowReserved |
71
+ | path[path].[operation].parameters.schema | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.schema |
72
+ | path[path].[operation].parameters.example | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.example |
73
+ | path[path].[operation].parameters.examples | functions.functions.[http OR httpApi].documentation.[path|query|cookie|header]Params.examples |
74
+ | path[path].[operation].requestBody | functions.functions.[http OR httpApi].documentation.requestBody |
75
+ | path[path].[operation].requestBody.description | functions.functions.[http OR httpApi].documentation.requestBody.description |
76
+ | path[path].[operation].requestBody.required | functions.functions.[http OR httpApi].documentation.requestBody.required |
77
+ | path[path].[operation].requestBody.content | functions.functions.[http OR httpApi].documentation.requestModels[contentType].name Links to custom.documentation.models.name |
78
+ | path[path].[operation].responses | functions.functions.[http OR httpApi].documentation.methodResponses |
79
+ | path[path].[operation].requestBody.[statusCode] | functions.functions.[http OR httpApi].documentation.methodResponses[statusCode] |
80
+ | path[path].[operation].requestBody.[statusCode].description | functions.functions.[http OR httpApi].documentation.methodResponses[statusCode].responseBody.description |
81
+ | path[path].[operation].requestBody.[statusCode].content | functions.functions.[http OR httpApi].documentation.methodResponses[statusCode].responseModels[contentType] Links to custom.documentation.models.name |
57
82
 
58
83
 
59
84
  ### Configuration
@@ -69,6 +94,9 @@ custom:
69
94
  title: 'My API'
70
95
  description: 'This is my API'
71
96
  models: {}
97
+ externalDocumentation:
98
+ url: https://google.com
99
+ description: A link to google
72
100
  ```
73
101
 
74
102
  These configurations can be quite verbose; you can separate it out into it's own file, such as `serverless.doc.yml` as below:
@@ -158,6 +186,9 @@ functions:
158
186
  documentation:
159
187
  summary: "Create User"
160
188
  description: "Creates a user and then sends a generated password email"
189
+ externalDocumentation:
190
+ url: https://bing.com
191
+ description: A link to bing
161
192
  requestBody:
162
193
  description: "A user information object"
163
194
  requestModels:
@@ -310,27 +341,7 @@ requestHeaders:
310
341
 
311
342
  ## Example configuration
312
343
 
313
- Please view the example [serverless.yml](test/serverless 2/serverless.yml).
314
-
315
- ## Install
316
-
317
- This plugin works for Serverless 2.x and up.
318
-
319
- To add this plugin to your package.json:
320
-
321
- **Using npm:**
322
- ```bash
323
- npm install serverless-openapi-documenter --save-dev
324
- ```
325
-
326
- Next you need to add the plugin to the `plugins` section of your `serverless.yml` file.
327
-
328
- ```yml
329
- plugins:
330
- - serverless-openapi-documenter
331
- ```
332
-
333
- > Note: Add this plugin _after_ `serverless-offline` to prevent issues with `String.replaceAll` being overridden incorrectly.
344
+ Please view the example [serverless.yml](test/serverless\ 2/serverless.yml).
334
345
 
335
346
  ## License
336
347
 
package/package.json CHANGED
@@ -1,13 +1,23 @@
1
1
  {
2
2
  "name": "serverless-openapi-documenter",
3
- "version": "0.0.1",
3
+ "version": "0.0.4",
4
4
  "description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
5
5
  "main": "index.js",
6
+ "keywords": ["serverless", "serverless2", "serverless3", "openAPI", "openAPIv3", "openAPI3", "PostmanCollections", "Postman-Collections"],
6
7
  "scripts": {
7
8
  "test": "echo \"Error: no test specified\" && exit 1"
8
9
  },
9
- "author": "",
10
- "license": "ISC",
10
+ "author": {
11
+ "name": "Jared Evans"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/JaredCE/serverless-openapi-documenter.git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/JaredCE/serverless-openapi-documenter/issues"
19
+ },
20
+ "license": "MIT",
11
21
  "devDependencies": {
12
22
  "serverless": "^3.17.0"
13
23
  },
@@ -22,11 +22,14 @@ class DefinitionGenerator {
22
22
  this.openAPI = {
23
23
  openapi: this.version,
24
24
  }
25
+
26
+ this.operationIds = []
25
27
  }
26
28
 
27
29
  parse() {
28
30
  this.createInfo()
29
31
  this.createPaths()
32
+ this.createExternalDocumentation()
30
33
  }
31
34
 
32
35
  createInfo() {
@@ -34,7 +37,7 @@ class DefinitionGenerator {
34
37
  const documentation = this.serverless.service.custom.documentation;
35
38
 
36
39
  const info = {
37
- title: service.service,
40
+ title: documentation?.title || service.service,
38
41
  description: documentation?.description || '',
39
42
  version: documentation?.version || uuid(),
40
43
  }
@@ -50,20 +53,41 @@ class DefinitionGenerator {
50
53
  if (event?.http?.documentation || event?.httpApi?.documentation) {
51
54
  const documentation = event.http.documentation || event.httpApi.documentation
52
55
 
53
- const path = this.createOperationObject(event.http.method || event.httpApi.method, documentation, httpFunction.functionInfo.name)
56
+ let opId
57
+ if (this.operationIds.includes(httpFunction.functionInfo.name) === false) {
58
+ opId = httpFunction.functionInfo.name
59
+ this.operationIds.push(opId)
60
+ } else {
61
+ opId = `${httpFunction.functionInfo.name}-${uuid()}`
62
+ }
63
+
64
+ const path = this.createOperationObject(event.http.method || event.httpApi.method, documentation, opId)
54
65
  if (httpFunction.functionInfo?.summary)
55
66
  path.summary = httpFunction.functionInfo.summary
56
67
 
57
68
  if (httpFunction.functionInfo?.description)
58
69
  path.description = httpFunction.functionInfo.description
59
70
 
60
- Object.assign(paths, {[`/${event.http.path}`]: path})
71
+ let slashPath = event.http.path
72
+ const pathStart = new RegExp(/^\//, 'g')
73
+ if (pathStart.test(slashPath) === false) {
74
+ slashPath = `/${event.http.path}`
75
+ }
76
+
77
+ Object.assign(paths, {[slashPath]: path})
61
78
  }
62
79
  }
63
80
  }
64
81
  Object.assign(this.openAPI, {paths})
65
82
  }
66
83
 
84
+ createExternalDocumentation() {
85
+ const documentation = this.serverless.service.custom.documentation
86
+ if (documentation.externalDocumentation) {
87
+ Object.assign(this.openAPI, {externalDocs: {...documentation.externalDocumentation}})
88
+ }
89
+ }
90
+
67
91
  createOperationObject(method, documentation, name = uuid()) {
68
92
  const obj = {
69
93
  summary: documentation.summary || '',
@@ -93,8 +117,12 @@ class DefinitionGenerator {
93
117
  obj.parameters = obj.parameters.concat(paramObject)
94
118
  }
95
119
 
120
+ if (documentation.externalDocumentation) {
121
+ obj.externalDocs = documentation.externalDocumentation
122
+ }
123
+
96
124
  if (Object.keys(documentation).includes('deprecated'))
97
- obj[method].deprecated = documentation.deprecated
125
+ obj.deprecated = documentation.deprecated
98
126
 
99
127
  if (documentation.requestBody)
100
128
  obj.requestBody = this.createRequestBody(documentation)
@@ -19,6 +19,9 @@ functions:
19
19
  documentation:
20
20
  summary: Create User
21
21
  description: Creates a user and then sends a generated password email
22
+ externalDocumentation:
23
+ url: https://bing.com
24
+ description: A link to bing
22
25
  requestBody:
23
26
  description: A user information object
24
27
  requestModels:
@@ -64,6 +67,9 @@ custom:
64
67
  documentation:
65
68
  description: This is a description of what this does
66
69
  version: 1.0.0
70
+ externalDocumentation:
71
+ url: https://google.com
72
+ description: A link to google
67
73
  models:
68
74
  - name: ErrorResponse
69
75
  description: This is an error