serverless-openapi-documenter 0.0.12 → 0.0.16
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/.github/workflows/node.yml +31 -0
- package/README.md +49 -9
- package/package.json +7 -2
- package/src/definitionGenerator.js +47 -23
- package/src/openAPIGenerator.js +26 -20
- package/test/.mocharc.js +9 -0
- package/test/helpers/ref-parser.js +5 -0
- package/test/helpers/serverless.js +19 -0
- package/test/json/complex.json +91 -0
- package/test/serverless 2/serverless.yml +19 -3
- package/test/serverless 3/serverless.yml +23 -13
- package/test/unit/definitionGenerator.spec.js +603 -0
- package/test/unit/openAPIGenerator.spec.js +10 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
+
|
|
4
|
+
name: Node.js CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ "main" ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ "main" ]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
node-version: [14.x, 16.x]
|
|
20
|
+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v3
|
|
24
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
25
|
+
uses: actions/setup-node@v3
|
|
26
|
+
with:
|
|
27
|
+
node-version: ${{ matrix.node-version }}
|
|
28
|
+
cache: 'npm'
|
|
29
|
+
- run: npm ci
|
|
30
|
+
- run: npm run build --if-present
|
|
31
|
+
- run: npm test
|
package/README.md
CHANGED
|
@@ -9,19 +9,21 @@
|
|
|
9
9
|
</a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
|
+

|
|
13
|
+
|
|
12
14
|
This will generate an OpenAPI V3 (up to v3.0.3) file for you from your serverless file. It can optionally generate a Postman Collection V2 from the OpenAPI file for you too.
|
|
13
15
|
|
|
14
16
|
Originally based off of: https://github.com/temando/serverless-openapi-documentation
|
|
15
17
|
|
|
16
18
|
## Install
|
|
17
19
|
|
|
18
|
-
This plugin works for Serverless 2.x and up.
|
|
20
|
+
This plugin works for Serverless 2.x and up and only supports node.js 14 and up.
|
|
19
21
|
|
|
20
22
|
To add this plugin to your package.json:
|
|
21
23
|
|
|
22
24
|
**Using npm:**
|
|
23
25
|
```bash
|
|
24
|
-
npm install --save-dev serverless-openapi-documenter
|
|
26
|
+
npm install --save-dev serverless-openapi-documenter
|
|
25
27
|
```
|
|
26
28
|
|
|
27
29
|
Next you need to add the plugin to the `plugins` section of your `serverless.yml` file.
|
|
@@ -112,7 +114,6 @@ custom:
|
|
|
112
114
|
version: '1'
|
|
113
115
|
title: 'My API'
|
|
114
116
|
description: 'This is my API'
|
|
115
|
-
models: {}
|
|
116
117
|
externalDocumentation:
|
|
117
118
|
url: https://google.com
|
|
118
119
|
description: A link to google
|
|
@@ -125,8 +126,11 @@ custom:
|
|
|
125
126
|
externalDocumentation:
|
|
126
127
|
url: https://npmjs.com
|
|
127
128
|
description: A link to npm
|
|
129
|
+
models: {}
|
|
128
130
|
```
|
|
129
131
|
|
|
132
|
+
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.
|
|
133
|
+
|
|
130
134
|
These configurations can be quite verbose; you can separate it out into it's own file, such as `serverless.doc.yml` as below:
|
|
131
135
|
|
|
132
136
|
```yml
|
|
@@ -146,9 +150,10 @@ For more info on `serverless.yml` syntax, see their docs.
|
|
|
146
150
|
|
|
147
151
|
#### Models
|
|
148
152
|
|
|
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.
|
|
153
|
+
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
154
|
|
|
151
|
-
The
|
|
155
|
+
The first way of writing the model is:
|
|
156
|
+
*required* directives for the models section are as follow:
|
|
152
157
|
|
|
153
158
|
* `name`: the name of the schema
|
|
154
159
|
* `description`: a description of the schema
|
|
@@ -180,6 +185,40 @@ custom:
|
|
|
180
185
|
type: "string"
|
|
181
186
|
```
|
|
182
187
|
|
|
188
|
+
The Second way of writing the models:
|
|
189
|
+
|
|
190
|
+
* `name`: the name of the schema
|
|
191
|
+
* `description`: a description of the schema
|
|
192
|
+
* `content`: an Object made up of the contentType and the schema, as shown below
|
|
193
|
+
|
|
194
|
+
```yml
|
|
195
|
+
custom:
|
|
196
|
+
documentation:
|
|
197
|
+
models:
|
|
198
|
+
- name: "ErrorResponse"
|
|
199
|
+
description: "This is an error"
|
|
200
|
+
content:
|
|
201
|
+
application/json:
|
|
202
|
+
schema: ${file(models/ErrorResponse.json)}
|
|
203
|
+
- name: "PutDocumentResponse"
|
|
204
|
+
description: "PUT Document response model (external reference example)"
|
|
205
|
+
content:
|
|
206
|
+
application/json:
|
|
207
|
+
schema: ${file(models/PutDocumentResponse.json)}
|
|
208
|
+
- name: "PutDocumentRequest"
|
|
209
|
+
description: "PUT Document request model (inline example)"
|
|
210
|
+
content:
|
|
211
|
+
application/json:
|
|
212
|
+
schema:
|
|
213
|
+
$schema: "http://json-schema.org/draft-04/schema#"
|
|
214
|
+
properties:
|
|
215
|
+
SomeObject:
|
|
216
|
+
type: "object"
|
|
217
|
+
properties:
|
|
218
|
+
SomeAttribute:
|
|
219
|
+
type: "string"
|
|
220
|
+
```
|
|
221
|
+
|
|
183
222
|
#### Functions
|
|
184
223
|
|
|
185
224
|
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 +245,12 @@ The `documentation` section of the event configuration can contain the following
|
|
|
206
245
|
```yml
|
|
207
246
|
functions:
|
|
208
247
|
createUser:
|
|
209
|
-
handler:
|
|
248
|
+
handler: handler.create
|
|
210
249
|
events:
|
|
211
250
|
- http:
|
|
212
|
-
path:
|
|
213
|
-
method:
|
|
251
|
+
path: create
|
|
252
|
+
method: post
|
|
253
|
+
summary:
|
|
214
254
|
documentation:
|
|
215
255
|
summary: "Create User"
|
|
216
256
|
description: "Creates a user and then sends a generated password email"
|
|
@@ -375,7 +415,7 @@ Please view the example [serverless.yml](test/serverless\ 2/serverless.yml).
|
|
|
375
415
|
|
|
376
416
|
## Notes on schemas
|
|
377
417
|
|
|
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).
|
|
418
|
+
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
419
|
|
|
380
420
|
If they exist as an external reference, for instance:
|
|
381
421
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-openapi-documenter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"Postman-Collections"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "
|
|
17
|
+
"test": "mocha --config './test/.mocharc.js'"
|
|
18
18
|
},
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Jared Evans"
|
|
@@ -39,5 +39,10 @@
|
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=14"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"chai": "^4.3.6",
|
|
45
|
+
"mocha": "^10.0.0",
|
|
46
|
+
"sinon": "^14.0.0"
|
|
42
47
|
}
|
|
43
48
|
}
|
|
@@ -9,7 +9,7 @@ const $RefParser = require("@apidevtools/json-schema-ref-parser");
|
|
|
9
9
|
|
|
10
10
|
class DefinitionGenerator {
|
|
11
11
|
constructor(serverless, options = {}) {
|
|
12
|
-
this.version = serverless
|
|
12
|
+
this.version = serverless?.processedInput?.options?.openApiVersion || '3.0.0'
|
|
13
13
|
|
|
14
14
|
this.serverless = serverless
|
|
15
15
|
this.httpKeys = {
|
|
@@ -27,13 +27,14 @@ class DefinitionGenerator {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
this.operationIds = []
|
|
30
|
+
this.schemaIDs = []
|
|
30
31
|
|
|
31
32
|
try {
|
|
32
33
|
this.refParserOptions = require(path.resolve('options', 'ref-parser.js'))
|
|
33
34
|
} catch (err) {
|
|
34
35
|
this.refParserOptions = {}
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
+
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
async parse() {
|
|
@@ -42,7 +43,7 @@ class DefinitionGenerator {
|
|
|
42
43
|
.catch(err => {
|
|
43
44
|
throw err
|
|
44
45
|
})
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
if (this.serverless.service.custom.documentation.servers) {
|
|
47
48
|
const servers = this.createServers(this.serverless.service.custom.documentation.servers)
|
|
48
49
|
Object.assign(this.openAPI, {servers: servers})
|
|
@@ -73,11 +74,10 @@ class DefinitionGenerator {
|
|
|
73
74
|
async createPaths() {
|
|
74
75
|
const paths = {}
|
|
75
76
|
const httpFunctions = this.getHTTPFunctions()
|
|
76
|
-
|
|
77
77
|
for (const httpFunction of httpFunctions) {
|
|
78
78
|
for (const event of httpFunction.event) {
|
|
79
79
|
if (event?.http?.documentation || event?.httpApi?.documentation) {
|
|
80
|
-
const documentation = event
|
|
80
|
+
const documentation = event?.http?.documentation || event?.httpApi?.documentation
|
|
81
81
|
|
|
82
82
|
let opId
|
|
83
83
|
if (this.operationIds.includes(httpFunction.functionInfo.name) === false) {
|
|
@@ -87,11 +87,11 @@ class DefinitionGenerator {
|
|
|
87
87
|
opId = `${httpFunction.functionInfo.name}-${uuid()}`
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const path = await this.createOperationObject(event
|
|
90
|
+
const path = await this.createOperationObject(event?.http?.method || event?.httpApi?.method, documentation, opId)
|
|
91
91
|
.catch(err => {
|
|
92
92
|
throw err
|
|
93
93
|
})
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
if (httpFunction.functionInfo?.summary)
|
|
96
96
|
path.summary = httpFunction.functionInfo.summary
|
|
97
97
|
|
|
@@ -103,10 +103,10 @@ class DefinitionGenerator {
|
|
|
103
103
|
path.servers = servers
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
let slashPath = event
|
|
106
|
+
let slashPath = event?.http?.path || event.httpApi?.path
|
|
107
107
|
const pathStart = new RegExp(/^\//, 'g')
|
|
108
108
|
if (pathStart.test(slashPath) === false) {
|
|
109
|
-
slashPath = `/${event
|
|
109
|
+
slashPath = `/${event?.http?.path||event.httpApi?.path}`
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
Object.assign(paths, {[slashPath]: path})
|
|
@@ -152,7 +152,7 @@ class DefinitionGenerator {
|
|
|
152
152
|
// const documentation = this.serverless.service.custom.documentation
|
|
153
153
|
// if (documentation.externalDocumentation) {
|
|
154
154
|
// // Object.assign(this.openAPI, {externalDocs: {...documentation.externalDocumentation}})
|
|
155
|
-
// return
|
|
155
|
+
// return
|
|
156
156
|
// }
|
|
157
157
|
}
|
|
158
158
|
|
|
@@ -240,7 +240,7 @@ class DefinitionGenerator {
|
|
|
240
240
|
obj.servers = servers
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
return {[method]: obj}
|
|
243
|
+
return {[method.toLowerCase()]: obj}
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
async createResponses(documentation) {
|
|
@@ -292,14 +292,19 @@ class DefinitionGenerator {
|
|
|
292
292
|
if (mediaTypeDocumentation.examples)
|
|
293
293
|
obj.examples = this.createExamples(mediaTypeDocumentation.examples)
|
|
294
294
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
295
|
+
let schema
|
|
296
|
+
if (mediaTypeDocumentation?.content) {
|
|
297
|
+
schema = mediaTypeDocumentation.content[contentKey].schema
|
|
298
|
+
} else if (mediaTypeDocumentation?.contentType && mediaTypeDocumentation.schema) {
|
|
299
|
+
schema = mediaTypeDocumentation.schema
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const schemaRef = await this.schemaCreator(schema, mediaTypeDocumentation.name)
|
|
303
|
+
.catch(err => {
|
|
304
|
+
throw err
|
|
305
|
+
})
|
|
306
|
+
obj.schema = {
|
|
307
|
+
$ref: schemaRef
|
|
303
308
|
}
|
|
304
309
|
|
|
305
310
|
Object.assign(mediaTypeObj, {[contentKey]: obj})
|
|
@@ -381,14 +386,33 @@ class DefinitionGenerator {
|
|
|
381
386
|
|
|
382
387
|
if (typeof schema !== 'string' && Object.keys(schema).length > 0) {
|
|
383
388
|
const convertedSchema = SchemaConvertor.convert(schema)
|
|
389
|
+
|
|
390
|
+
let schemaName = name
|
|
391
|
+
if (this.schemaIDs.includes(schemaName))
|
|
392
|
+
schemaName = `${name}-${uuid()}`
|
|
393
|
+
|
|
394
|
+
this.schemaIDs.push(schemaName)
|
|
395
|
+
|
|
384
396
|
for (const key of Object.keys(convertedSchema.schemas)) {
|
|
385
397
|
if (key === 'main' || key.split('-')[0] === 'main') {
|
|
386
|
-
|
|
398
|
+
let ref = `#/components/schemas/`
|
|
399
|
+
|
|
400
|
+
if (this.openAPI?.components?.schemas?.[name]) {
|
|
401
|
+
if (JSON.stringify(convertedSchema.schemas[key]) === JSON.stringify(this.openAPI.components.schemas[name])) {
|
|
402
|
+
return `${ref}${name}`
|
|
403
|
+
}
|
|
404
|
+
}
|
|
387
405
|
|
|
388
|
-
addToComponents(convertedSchema.schemas[key],
|
|
389
|
-
return ref
|
|
406
|
+
addToComponents(convertedSchema.schemas[key], schemaName)
|
|
407
|
+
return `${ref}${schemaName}`
|
|
390
408
|
} else {
|
|
391
|
-
|
|
409
|
+
if (this.openAPI?.components?.schemas?.[key]) {
|
|
410
|
+
if (JSON.stringify(convertedSchema.schemas[key]) !== JSON.stringify(this.openAPI.components.schemas[key])) {
|
|
411
|
+
addToComponents(convertedSchema.schemas[key], key)
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
addToComponents(convertedSchema.schemas[key], key)
|
|
415
|
+
}
|
|
392
416
|
}
|
|
393
417
|
}
|
|
394
418
|
} else {
|
package/src/openAPIGenerator.js
CHANGED
|
@@ -54,7 +54,6 @@ class OpenAPIGenerator {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
this.hooks = {
|
|
57
|
-
// 'before:deploy': this.beforeDeploy.bind(this),
|
|
58
57
|
'openapi:generate:serverless': this.generate.bind(this),
|
|
59
58
|
};
|
|
60
59
|
|
|
@@ -76,17 +75,23 @@ class OpenAPIGenerator {
|
|
|
76
75
|
|
|
77
76
|
this.serverless.configSchemaHandler.defineFunctionProperties('aws', {
|
|
78
77
|
properties: {
|
|
79
|
-
// description: {type: 'string'},
|
|
80
78
|
summary: {type: 'string'},
|
|
81
79
|
servers: {anyOf: [{type:'object'}, {type:'array'}]},
|
|
82
80
|
}
|
|
83
81
|
})
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
log(type = this.defaultLog,
|
|
84
|
+
log(type = this.defaultLog, str) {
|
|
87
85
|
switch(this.serverless.version[0]) {
|
|
88
86
|
case '2':
|
|
89
|
-
|
|
87
|
+
let colouredString = str
|
|
88
|
+
if (type === 'error') {
|
|
89
|
+
colouredString = chalk.bold.red(`✖ ${str}`)
|
|
90
|
+
} else if (type === 'success') {
|
|
91
|
+
colouredString = chalk.bold.green(`✓ ${str}`)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.serverless.cli.log(colouredString)
|
|
90
95
|
break
|
|
91
96
|
|
|
92
97
|
case '3':
|
|
@@ -106,32 +111,32 @@ class OpenAPIGenerator {
|
|
|
106
111
|
|
|
107
112
|
await generator.parse()
|
|
108
113
|
.catch(err => {
|
|
109
|
-
this.log('error',
|
|
114
|
+
this.log('error', `ERROR: An error was thrown generating the OpenAPI v3 documentation`)
|
|
110
115
|
throw new this.serverless.classes.Error(err)
|
|
111
116
|
})
|
|
112
117
|
|
|
113
118
|
const valid = await generator.validate()
|
|
114
119
|
.catch(err => {
|
|
115
|
-
this.log('error',
|
|
120
|
+
this.log('error', `ERROR: An error was thrown validating the OpenAPI v3 documentation`)
|
|
116
121
|
throw new this.serverless.classes.Error(err)
|
|
117
122
|
})
|
|
118
123
|
|
|
119
124
|
if (valid)
|
|
120
|
-
this.log(
|
|
125
|
+
this.log('success', 'OpenAPI v3 Documentation Successfully Generated')
|
|
121
126
|
|
|
122
127
|
if (config.postmanCollection) {
|
|
123
128
|
const postmanGeneration = (err, result) => {
|
|
124
129
|
if (err) {
|
|
125
|
-
this.log('error',
|
|
130
|
+
this.log('error', `ERROR: An error was thrown when generating the postman collection`)
|
|
126
131
|
throw new this.serverless.classes.Error(err)
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
this.log(
|
|
134
|
+
this.log('success', 'postman collection v2 Documentation Successfully Generated')
|
|
130
135
|
try {
|
|
131
136
|
fs.writeFileSync(config.postmanCollection, JSON.stringify(result.output[0].data))
|
|
132
|
-
this.log(
|
|
137
|
+
this.log('success', 'postman collection v2 Documentation Successfully Written')
|
|
133
138
|
} catch (err) {
|
|
134
|
-
this.log('error',
|
|
139
|
+
this.log('error', `ERROR: An error was thrown whilst writing the postman collection`)
|
|
135
140
|
throw new this.serverless.classes.Error(err)
|
|
136
141
|
}
|
|
137
142
|
}
|
|
@@ -155,9 +160,9 @@ class OpenAPIGenerator {
|
|
|
155
160
|
}
|
|
156
161
|
try {
|
|
157
162
|
fs.writeFileSync(config.file, output);
|
|
158
|
-
this.log(
|
|
163
|
+
this.log('success', 'OpenAPI v3 Documentation Successfully Written')
|
|
159
164
|
} catch (err) {
|
|
160
|
-
this.log('error',
|
|
165
|
+
this.log('error', `ERROR: An error was thrown whilst writing the openAPI Documentation`)
|
|
161
166
|
throw new this.serverless.classes.Error(err)
|
|
162
167
|
}
|
|
163
168
|
}
|
|
@@ -177,7 +182,8 @@ class OpenAPIGenerator {
|
|
|
177
182
|
config.postmanCollection = this.serverless.processedInput.options.postmanCollection || null
|
|
178
183
|
|
|
179
184
|
if (['yaml', 'json'].indexOf(config.format.toLowerCase()) < 0) {
|
|
180
|
-
throw new Error('Invalid Output Format Specified - must be one of "yaml" or "json"');
|
|
185
|
+
// throw new Error('Invalid Output Format Specified - must be one of "yaml" or "json"');
|
|
186
|
+
throw new this.serverless.classes.Error('Invalid Output Format Specified - must be one of "yaml" or "json"')
|
|
181
187
|
}
|
|
182
188
|
|
|
183
189
|
config.file = this.serverless.processedInput.options.output ||
|
|
@@ -185,12 +191,12 @@ class OpenAPIGenerator {
|
|
|
185
191
|
|
|
186
192
|
this.log(
|
|
187
193
|
this.defaultLog,
|
|
188
|
-
`${chalk.bold.green('[OPTIONS]')}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
`${chalk.bold.green('[OPTIONS]')}
|
|
195
|
+
openApiVersion: "${chalk.bold.green(String(config.openApiVersion))}"
|
|
196
|
+
format: "${chalk.bold.green(config.format)}"
|
|
197
|
+
output file: "${chalk.bold.green(config.file)}"
|
|
198
|
+
indentation: "${chalk.bold.green(String(config.indent))}"
|
|
199
|
+
${config.postmanCollection ? `postman collection: ${chalk.bold.green(config.postmanCollection)}`: `\n\n`}`
|
|
194
200
|
)
|
|
195
201
|
|
|
196
202
|
return config
|
package/test/.mocharc.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
processedInput: {
|
|
5
|
+
options: {
|
|
6
|
+
openApiVersion: '3.0.1'
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
service: {
|
|
10
|
+
service: 'myAPI',
|
|
11
|
+
custom: {
|
|
12
|
+
documentation: {
|
|
13
|
+
title: 'My new API',
|
|
14
|
+
description: 'This API does things',
|
|
15
|
+
version: '0.0.1'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "JSON Schema for CSR Framework",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-04/schema",
|
|
4
|
+
"definitions": {
|
|
5
|
+
"log": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"title": "Logger options",
|
|
8
|
+
"properties": {
|
|
9
|
+
"channel": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"default": "console"
|
|
12
|
+
},
|
|
13
|
+
"path": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"default": "../src/logs"
|
|
16
|
+
},
|
|
17
|
+
"template": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"message": {
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
"file": {
|
|
24
|
+
"type": "string"
|
|
25
|
+
},
|
|
26
|
+
"dir": {
|
|
27
|
+
"type": "string"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"telegram": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"bot_token": {
|
|
35
|
+
"type": "string"
|
|
36
|
+
},
|
|
37
|
+
"chat_id": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"required": ["chat_id", "bot_token"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"template": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"properties": {
|
|
48
|
+
"path": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
},
|
|
51
|
+
"cache": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"database": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"patternProperties": {
|
|
59
|
+
".*": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"properties": {
|
|
62
|
+
"provider": {
|
|
63
|
+
"type": "string"
|
|
64
|
+
},
|
|
65
|
+
"user": {
|
|
66
|
+
"type": "string"
|
|
67
|
+
},
|
|
68
|
+
"password": {
|
|
69
|
+
"type": "string"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"required": ["provider"]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"id": "https://json.schemastore.org/csr",
|
|
78
|
+
"properties": {
|
|
79
|
+
"log": {
|
|
80
|
+
"$ref": "#/definitions/log"
|
|
81
|
+
},
|
|
82
|
+
"template": {
|
|
83
|
+
"$ref": "#/definitions/template"
|
|
84
|
+
},
|
|
85
|
+
"database": {
|
|
86
|
+
"$ref": "#/definitions/database"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"title": "CSR Framework schema",
|
|
90
|
+
"type": "object"
|
|
91
|
+
}
|
|
@@ -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
|
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs').promises
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const sinon = require('sinon')
|
|
6
|
+
const $RefParser = require("@apidevtools/json-schema-ref-parser");
|
|
7
|
+
const expect = require('chai').expect
|
|
8
|
+
|
|
9
|
+
const serverlessMock = require('../helpers/serverless')
|
|
10
|
+
const DefinitionGenerator = require('../../src/definitionGenerator')
|
|
11
|
+
|
|
12
|
+
describe('DefinitionGenerator', () => {
|
|
13
|
+
let mockServerless
|
|
14
|
+
const v4 = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i);
|
|
15
|
+
beforeEach(function() {
|
|
16
|
+
mockServerless = JSON.parse(JSON.stringify(serverlessMock))
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('constructor', () => {
|
|
20
|
+
it('should return a definitionGenerator', function() {
|
|
21
|
+
const expected = new DefinitionGenerator({}, {})
|
|
22
|
+
expect(expected).to.be.an.instanceOf(DefinitionGenerator)
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should default to version 3.0.0 of openAPI when openAPI version is not passed in', function() {
|
|
26
|
+
let expected = new DefinitionGenerator({}, {})
|
|
27
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
28
|
+
|
|
29
|
+
let serverlessObj = {
|
|
30
|
+
processedInput: {}
|
|
31
|
+
}
|
|
32
|
+
expected = new DefinitionGenerator(serverlessObj, {})
|
|
33
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
34
|
+
|
|
35
|
+
serverlessObj.processedInput = {
|
|
36
|
+
options: {}
|
|
37
|
+
}
|
|
38
|
+
expected = new DefinitionGenerator(serverlessObj, {})
|
|
39
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
40
|
+
|
|
41
|
+
serverlessObj.processedInput.options = {
|
|
42
|
+
test: 'abc'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
expected = new DefinitionGenerator(serverlessObj, {})
|
|
46
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
47
|
+
|
|
48
|
+
serverlessObj.processedInput.options = {
|
|
49
|
+
openApiVersion: null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
expected = new DefinitionGenerator(serverlessObj, {})
|
|
53
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
54
|
+
|
|
55
|
+
serverlessObj.processedInput.options = {
|
|
56
|
+
openApiVersion: undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
expected = new DefinitionGenerator(serverlessObj, {})
|
|
60
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
61
|
+
|
|
62
|
+
serverlessObj.processedInput.options = {
|
|
63
|
+
openapiVersion: undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expected = new DefinitionGenerator(serverlessObj, {})
|
|
67
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should respect the version of openAPI when passed in', function() {
|
|
71
|
+
let serverlessObj = {
|
|
72
|
+
processedInput: {
|
|
73
|
+
options: {
|
|
74
|
+
openApiVersion: '3.0.0'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let expected = new DefinitionGenerator(serverlessObj, {})
|
|
79
|
+
expect(expected.version).to.be.equal('3.0.0')
|
|
80
|
+
|
|
81
|
+
serverlessObj = {
|
|
82
|
+
processedInput: {
|
|
83
|
+
options: {
|
|
84
|
+
openApiVersion: '3.0.1'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
expected = new DefinitionGenerator(serverlessObj, {})
|
|
89
|
+
expect(expected.version).to.be.equal('3.0.1')
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should correctly resolve the RefParserOptions', async function() {
|
|
93
|
+
let expected = new DefinitionGenerator({}, {})
|
|
94
|
+
expect(expected.refParserOptions).to.be.an('object')
|
|
95
|
+
expect(expected.refParserOptions).to.be.empty
|
|
96
|
+
|
|
97
|
+
await fs.mkdir(path.resolve('options'))
|
|
98
|
+
.catch(err => {
|
|
99
|
+
console.error(err)
|
|
100
|
+
throw err
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
await fs.copyFile(path.resolve('test/helpers/ref-parser.js'), path.resolve('options/ref-parser.js'))
|
|
104
|
+
.catch(err => {
|
|
105
|
+
console.error(err)
|
|
106
|
+
throw err
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
expected = new DefinitionGenerator({}, {})
|
|
110
|
+
expect(expected.refParserOptions).to.be.an('object')
|
|
111
|
+
expect(expected.refParserOptions).to.have.property('continueOnError')
|
|
112
|
+
|
|
113
|
+
await fs.rm(path.resolve('options/ref-parser.js'))
|
|
114
|
+
.catch(err => {
|
|
115
|
+
console.error(err)
|
|
116
|
+
throw err
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
await fs.rmdir(path.resolve('options'))
|
|
120
|
+
.catch(err => {
|
|
121
|
+
console.error(err)
|
|
122
|
+
throw err
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('createInfo', () => {
|
|
128
|
+
it('should create openAPI info object correctly', function() {
|
|
129
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
130
|
+
definitionGenerator.createInfo()
|
|
131
|
+
|
|
132
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
133
|
+
expect(definitionGenerator.openAPI.info).to.be.an('object')
|
|
134
|
+
expect(definitionGenerator.openAPI.info).to.deep.equal(mockServerless.service.custom.documentation)
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should use the service name when documentation title has not been supplied', function() {
|
|
138
|
+
delete mockServerless.service.custom.documentation.title
|
|
139
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
140
|
+
definitionGenerator.createInfo()
|
|
141
|
+
|
|
142
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
143
|
+
expect(definitionGenerator.openAPI.info).to.be.an('object')
|
|
144
|
+
expect(definitionGenerator.openAPI.info.title).to.be.equal(mockServerless.service.service)
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should use the service name when documentation description has not been supplied', function() {
|
|
148
|
+
delete mockServerless.service.custom.documentation.description
|
|
149
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
150
|
+
definitionGenerator.createInfo()
|
|
151
|
+
|
|
152
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
153
|
+
expect(definitionGenerator.openAPI.info).to.be.an('object')
|
|
154
|
+
expect(definitionGenerator.openAPI.info.description).to.be.equal('')
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should use an empty string when documentation description has not been supplied', function() {
|
|
158
|
+
delete mockServerless.service.custom.documentation.description
|
|
159
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
160
|
+
definitionGenerator.createInfo()
|
|
161
|
+
|
|
162
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
163
|
+
expect(definitionGenerator.openAPI.info).to.be.an('object')
|
|
164
|
+
expect(definitionGenerator.openAPI.info.description).to.be.equal('')
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should generate a uuid for version when documentation version has not been supplied', function() {
|
|
168
|
+
delete mockServerless.service.custom.documentation.version
|
|
169
|
+
|
|
170
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
171
|
+
definitionGenerator.createInfo()
|
|
172
|
+
|
|
173
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
174
|
+
expect(definitionGenerator.openAPI.info).to.be.an('object')
|
|
175
|
+
expect(v4.test(definitionGenerator.openAPI.info.version)).to.be.true
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('createTags', () => {
|
|
180
|
+
it('should add tags to the openAPI object correctly', function() {
|
|
181
|
+
mockServerless.service.custom.documentation.tags = [{name: 'tag1'}]
|
|
182
|
+
|
|
183
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
184
|
+
definitionGenerator.createTags()
|
|
185
|
+
|
|
186
|
+
expect(definitionGenerator.openAPI).to.be.an('object')
|
|
187
|
+
expect(definitionGenerator.openAPI.tags).to.be.an('array')
|
|
188
|
+
expect(definitionGenerator.openAPI.tags[0].name).to.be.equal('tag1')
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should not add tags when they are not defined', function() {
|
|
192
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
193
|
+
expect(() => {
|
|
194
|
+
definitionGenerator.createTags()
|
|
195
|
+
}).to.throw()
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('schemaCreator', () => {
|
|
200
|
+
describe('schemas that are objects', () => {
|
|
201
|
+
it('should add a simple schema to the components object', async function() {
|
|
202
|
+
const simpleSchema = {
|
|
203
|
+
type: 'string'
|
|
204
|
+
}
|
|
205
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
206
|
+
const expected = await definitionGenerator.schemaCreator(simpleSchema, 'simpleSchema')
|
|
207
|
+
.catch((err) => {
|
|
208
|
+
console.error(err)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('simpleSchema')
|
|
212
|
+
expect(JSON.stringify(definitionGenerator.openAPI.components.schemas.simpleSchema)).to.equal(JSON.stringify(simpleSchema))
|
|
213
|
+
expect(expected).to.equal('#/components/schemas/simpleSchema')
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should add a complex schema to the components object', async function() {
|
|
217
|
+
const complexSchema = {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
error: {
|
|
221
|
+
type: 'string'
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
226
|
+
const expected = await definitionGenerator.schemaCreator(complexSchema, 'main')
|
|
227
|
+
.catch((err) => {
|
|
228
|
+
console.error(err)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('main')
|
|
232
|
+
expect(JSON.stringify(definitionGenerator.openAPI.components.schemas.main)).to.equal(JSON.stringify(complexSchema))
|
|
233
|
+
expect(expected).to.equal('#/components/schemas/main')
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should add each definition of a complex schema to the components object', async function() {
|
|
237
|
+
const complexSchema = {
|
|
238
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
239
|
+
"title": "JSON API Schema",
|
|
240
|
+
"description": "This is a blah blah for responses in the JSON API format. For more, see http://jsonapi.org",
|
|
241
|
+
"type": "object",
|
|
242
|
+
"required": [
|
|
243
|
+
"errors"
|
|
244
|
+
],
|
|
245
|
+
"properties": {
|
|
246
|
+
"errors": {
|
|
247
|
+
"type": "array",
|
|
248
|
+
"items": {
|
|
249
|
+
"$ref": "#/definitions/error"
|
|
250
|
+
},
|
|
251
|
+
"uniqueItems": true
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"definitions": {
|
|
255
|
+
"error": {
|
|
256
|
+
"type": "object",
|
|
257
|
+
"properties": {
|
|
258
|
+
"id": {
|
|
259
|
+
"description": "A unique identifier for this particular occurrence of the problem.",
|
|
260
|
+
"type": "string"
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
267
|
+
const expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
268
|
+
.catch((err) => {
|
|
269
|
+
console.error(err)
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
273
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
274
|
+
expect(expected).to.equal('#/components/schemas/PutRequest')
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it(`should not overwrite an object that already exists in the components if they're the same`, async function() {
|
|
278
|
+
const complexSchema = {
|
|
279
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
280
|
+
"title": "JSON API Schema",
|
|
281
|
+
"description": "This is a blah blah for responses in the JSON API format. For more, see http://jsonapi.org",
|
|
282
|
+
"type": "object",
|
|
283
|
+
"required": [
|
|
284
|
+
"errors"
|
|
285
|
+
],
|
|
286
|
+
"properties": {
|
|
287
|
+
"errors": {
|
|
288
|
+
"type": "array",
|
|
289
|
+
"items": {
|
|
290
|
+
"$ref": "#/definitions/error"
|
|
291
|
+
},
|
|
292
|
+
"uniqueItems": true
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"definitions": {
|
|
296
|
+
"error": {
|
|
297
|
+
"type": "object",
|
|
298
|
+
"properties": {
|
|
299
|
+
"id": {
|
|
300
|
+
"description": "A unique identifier for this particular occurrence of the problem.",
|
|
301
|
+
"type": "string"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
308
|
+
let expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
309
|
+
.catch((err) => {
|
|
310
|
+
console.error(err)
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
314
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
315
|
+
expect(expected).to.equal('#/components/schemas/PutRequest')
|
|
316
|
+
|
|
317
|
+
expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
318
|
+
.catch((err) => {
|
|
319
|
+
console.error(err)
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
323
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
324
|
+
expect(expected).to.equal('#/components/schemas/PutRequest')
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it(`should create a new object for a similarly named object but with different properties`, async function() {
|
|
328
|
+
const complexSchema = {
|
|
329
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
330
|
+
"title": "JSON API Schema",
|
|
331
|
+
"description": "This is a blah blah for responses in the JSON API format. For more, see http://jsonapi.org",
|
|
332
|
+
"type": "object",
|
|
333
|
+
"required": [
|
|
334
|
+
"errors"
|
|
335
|
+
],
|
|
336
|
+
"properties": {
|
|
337
|
+
"errors": {
|
|
338
|
+
"type": "array",
|
|
339
|
+
"items": {
|
|
340
|
+
"$ref": "#/definitions/error"
|
|
341
|
+
},
|
|
342
|
+
"uniqueItems": true
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
"definitions": {
|
|
346
|
+
"error": {
|
|
347
|
+
"type": "object",
|
|
348
|
+
"properties": {
|
|
349
|
+
"id": {
|
|
350
|
+
"description": "A unique identifier for this particular occurrence of the problem.",
|
|
351
|
+
"type": "string"
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
358
|
+
let expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
359
|
+
.catch((err) => {
|
|
360
|
+
console.error(err)
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
364
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
365
|
+
expect(expected).to.equal('#/components/schemas/PutRequest')
|
|
366
|
+
|
|
367
|
+
complexSchema.properties.cheese = {
|
|
368
|
+
type: 'string'
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
372
|
+
.catch((err) => {
|
|
373
|
+
console.error(err)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
377
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
378
|
+
|
|
379
|
+
let newSchemaStr = expected.split('/')
|
|
380
|
+
expect(v4.test(newSchemaStr[newSchemaStr.length-1].split('PutRequest-')[1])).to.be.true
|
|
381
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property(newSchemaStr[newSchemaStr.length-1])
|
|
382
|
+
|
|
383
|
+
// expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
384
|
+
// .catch((err) => {
|
|
385
|
+
// console.error(err)
|
|
386
|
+
// })
|
|
387
|
+
|
|
388
|
+
// expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
389
|
+
// expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
390
|
+
// console.log(expected)
|
|
391
|
+
// expect(expected)
|
|
392
|
+
// expect(expected).to.equal('#/components/schemas/PutRequest1')
|
|
393
|
+
|
|
394
|
+
complexSchema.properties.wine = {
|
|
395
|
+
type: 'string'
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
399
|
+
.catch((err) => {
|
|
400
|
+
console.error(err)
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
404
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
405
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property(newSchemaStr[newSchemaStr.length-1])
|
|
406
|
+
newSchemaStr = expected.split('/')
|
|
407
|
+
expect(v4.test(newSchemaStr[newSchemaStr.length-1].split('PutRequest-')[1])).to.be.true
|
|
408
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property(newSchemaStr[newSchemaStr.length-1])
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it(`should create a new object for a differently named object but with same properties`, async function() {
|
|
412
|
+
const complexSchema = {
|
|
413
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
414
|
+
"title": "JSON API Schema",
|
|
415
|
+
"description": "This is a blah blah for responses in the JSON API format. For more, see http://jsonapi.org",
|
|
416
|
+
"type": "object",
|
|
417
|
+
"required": [
|
|
418
|
+
"errors"
|
|
419
|
+
],
|
|
420
|
+
"properties": {
|
|
421
|
+
"errors": {
|
|
422
|
+
"type": "array",
|
|
423
|
+
"items": {
|
|
424
|
+
"$ref": "#/definitions/error"
|
|
425
|
+
},
|
|
426
|
+
"uniqueItems": true
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
"definitions": {
|
|
430
|
+
"error": {
|
|
431
|
+
"type": "object",
|
|
432
|
+
"properties": {
|
|
433
|
+
"id": {
|
|
434
|
+
"description": "A unique identifier for this particular occurrence of the problem.",
|
|
435
|
+
"type": "string"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
442
|
+
let expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
443
|
+
.catch((err) => {
|
|
444
|
+
console.error(err)
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
448
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
449
|
+
expect(expected).to.equal('#/components/schemas/PutRequest')
|
|
450
|
+
|
|
451
|
+
expected = await definitionGenerator.schemaCreator(complexSchema, 'ContactPutRequest')
|
|
452
|
+
.catch((err) => {
|
|
453
|
+
console.error(err)
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
457
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
458
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('ContactPutRequest')
|
|
459
|
+
expect(expected).to.equal('#/components/schemas/ContactPutRequest')
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('should not create an object that already references schemas in components', async function() {
|
|
463
|
+
const simpleSchema = {
|
|
464
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
465
|
+
"title": "JSON API Schema",
|
|
466
|
+
"description": "This is a blah blah for responses in the JSON API format. For more, see http://jsonapi.org",
|
|
467
|
+
"type": "object",
|
|
468
|
+
"properties": {
|
|
469
|
+
"meta": {
|
|
470
|
+
"type": "string",
|
|
471
|
+
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
476
|
+
let expected = await definitionGenerator.schemaCreator(simpleSchema, 'meta')
|
|
477
|
+
.catch((err) => {
|
|
478
|
+
console.error(err)
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
expect(expected).to.equal('#/components/schemas/meta')
|
|
482
|
+
|
|
483
|
+
const complexSchema = {
|
|
484
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
485
|
+
"title": "JSON API Schema",
|
|
486
|
+
"description": "This is a blah blah for responses in the JSON API format. For more, see http://jsonapi.org",
|
|
487
|
+
"type": "object",
|
|
488
|
+
"required": [
|
|
489
|
+
"errors"
|
|
490
|
+
],
|
|
491
|
+
"properties": {
|
|
492
|
+
"errors": {
|
|
493
|
+
"type": "array",
|
|
494
|
+
"items": {
|
|
495
|
+
"$ref": "#/definitions/error"
|
|
496
|
+
},
|
|
497
|
+
"uniqueItems": true
|
|
498
|
+
},
|
|
499
|
+
"meta": {
|
|
500
|
+
"$ref": "#/definitions/meta"
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
"definitions": {
|
|
504
|
+
"error": {
|
|
505
|
+
"type": "object",
|
|
506
|
+
"properties": {
|
|
507
|
+
"id": {
|
|
508
|
+
"description": "A unique identifier for this particular occurrence of the problem.",
|
|
509
|
+
"type": "string"
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
expected = await definitionGenerator.schemaCreator(complexSchema, 'PutRequest')
|
|
517
|
+
.catch((err) => {
|
|
518
|
+
console.error(err)
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('PutRequest')
|
|
522
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('error')
|
|
523
|
+
expect(expected).to.equal('#/components/schemas/PutRequest')
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
describe('schemas that are urls', () => {
|
|
528
|
+
it('should attempt to download a schema and convert it', async function() {
|
|
529
|
+
const simpleSchema = 'https:///google.com/build/LicensedMember.json'
|
|
530
|
+
const LicensedMemberJSON = require('../json/complex.json')
|
|
531
|
+
|
|
532
|
+
const stub = sinon.stub($RefParser, 'dereference').resolves(LicensedMemberJSON)
|
|
533
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
534
|
+
const expected = await definitionGenerator.schemaCreator(simpleSchema, 'LicensedMember')
|
|
535
|
+
.catch((err) => {
|
|
536
|
+
console.error(err)
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('LicensedMember')
|
|
540
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('log')
|
|
541
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('template')
|
|
542
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('database')
|
|
543
|
+
expect(expected).to.equal('#/components/schemas/LicensedMember')
|
|
544
|
+
|
|
545
|
+
stub.restore()
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('should take a mix of schemas', async function() {
|
|
549
|
+
const complexSchema = 'https:///google.com/build/LicensedMember.json'
|
|
550
|
+
const LicensedMemberJSON = require('../json/complex.json')
|
|
551
|
+
|
|
552
|
+
const stub = sinon.stub($RefParser, 'dereference').resolves(LicensedMemberJSON)
|
|
553
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
554
|
+
let expected = await definitionGenerator.schemaCreator(complexSchema, 'LicensedMember')
|
|
555
|
+
.catch((err) => {
|
|
556
|
+
console.error(err)
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
const simpleSchema = {
|
|
560
|
+
type: "object",
|
|
561
|
+
properties: {
|
|
562
|
+
UUID: {
|
|
563
|
+
$ref: "#/definitions/log"
|
|
564
|
+
},
|
|
565
|
+
name: {
|
|
566
|
+
type: "string"
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
definitions: {}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
expected = await definitionGenerator.schemaCreator(simpleSchema, 'simpleSchema')
|
|
573
|
+
.catch((err) => {
|
|
574
|
+
console.error(err)
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('LicensedMember')
|
|
578
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('log')
|
|
579
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('template')
|
|
580
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('database')
|
|
581
|
+
expect(definitionGenerator.openAPI.components.schemas).to.have.property('simpleSchema')
|
|
582
|
+
expect(expected).to.equal('#/components/schemas/simpleSchema')
|
|
583
|
+
|
|
584
|
+
stub.restore()
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('should throw an error when a url can not be resolved', async function() {
|
|
588
|
+
const simpleSchema = 'https:///google.com/build/LicensedMember.json'
|
|
589
|
+
|
|
590
|
+
const stub = sinon.stub($RefParser, 'dereference').rejects(new Error())
|
|
591
|
+
const definitionGenerator = new DefinitionGenerator(mockServerless)
|
|
592
|
+
const expected = await definitionGenerator.schemaCreator(simpleSchema, 'simpleSchema')
|
|
593
|
+
.catch((err) => {
|
|
594
|
+
console.error(err)
|
|
595
|
+
})
|
|
596
|
+
|
|
597
|
+
expect(expected).to.be.undefined
|
|
598
|
+
|
|
599
|
+
stub.restore()
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
});
|