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 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 *required* directives for the models section are as follow:
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: "handler.create"
246
+ handler: handler.create
210
247
  events:
211
248
  - http:
212
- path: "create"
213
- method: "post"
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.11",
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.4",
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.http.documentation || event.httpApi.documentation
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.http.method || event.httpApi.method, documentation, opId)
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.http.path
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.http.path}`
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
- if (mediaTypeDocumentation.content[contentKey].schema) {
296
- const schemaRef = await this.schemaCreator(mediaTypeDocumentation.content[contentKey].schema, mediaTypeDocumentation.name)
297
- .catch(err => {
298
- throw err
299
- })
300
- obj.schema = {
301
- $ref: schemaRef
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})
@@ -83,10 +83,17 @@ class OpenAPIGenerator {
83
83
  })
84
84
  }
85
85
 
86
- log(type = this.defaultLog, ...str) {
86
+ log(type = this.defaultLog, str) {
87
87
  switch(this.serverless.version[0]) {
88
88
  case '2':
89
- this.serverless.cli.log(str)
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', chalk.bold.red(`ERROR: An error was thrown generating the OpenAPI v3 documentation`))
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', chalk.bold.red(`ERROR: An error was thrown validating the OpenAPI v3 documentation`))
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(this.defaultLog, chalk.bold.green('OpenAPI v3 Documentation Successfully Generated'))
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', chalk.bold.red(`ERROR: An error was thrown when generating the postman collection`))
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(this.defaultLog, chalk.bold.green('postman collection v2 Documentation Successfully Generated'))
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(this.defaultLog, chalk.bold.green('postman collection v2 Documentation Successfully Written'))
139
+ this.log('success', 'postman collection v2 Documentation Successfully Written')
133
140
  } catch (err) {
134
- this.log('error', chalk.bold.red(`ERROR: An error was thrown whilst writing the postman collection`))
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(this.defaultLog, chalk.bold.green('OpenAPI v3 Documentation Successfully Written'))
165
+ this.log('success', 'OpenAPI v3 Documentation Successfully Written')
159
166
  } catch (err) {
160
- this.log('error', chalk.bold.red(`ERROR: An error was thrown whilst writing the openAPI Documentation`))
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
- ` openApiVersion: "${chalk.bold.red(String(config.openApiVersion))}"`,
190
- ` format: "${chalk.bold.red(config.format)}"`,
191
- ` output file: "${chalk.bold.red(config.file)}"`,
192
- ` indentation: "${chalk.bold.red(String(config.indent))}"`,
193
- ` ${config.postmanCollection ? `postman collection: ${chalk.bold.red(config.postmanCollection)}`: `\n\n`}`
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
- contentType: application/json
61
- schema: ${file(models/ErrorResponse.json)}
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
- contentType: application/json
66
- schema: ${file(models/PutDocumentResponse.json)}
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
- contentType: application/json
71
- schema:
72
- $schema: http://json-schema.org/draft-04/schema#
73
- properties:
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
- SomeAttribute:
78
- type: string
84
+ SomeObject:
85
+ type: object
86
+ properties:
87
+ SomeAttribute:
88
+ type: string