serverless-openapi-documenter 0.0.83 → 0.0.90
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
|
@@ -44,11 +44,11 @@ To Run: `serverless openapi generate -o openapi.json -f json -a 3.0.3 -p postman
|
|
|
44
44
|
Options:
|
|
45
45
|
|
|
46
46
|
```
|
|
47
|
-
--output -o What filename the OpenAPI
|
|
48
|
-
--format -f Whether to output the OpenAPI
|
|
47
|
+
--output -o What filename the OpenAPI Description should output under. Default: openapi.json
|
|
48
|
+
--format -f Whether to output the OpenAPI Description as json or yaml. Default: json
|
|
49
49
|
--indent -i File indentation in spaces. Default: 2
|
|
50
50
|
--openApiVersion -a OpenAPI version to generate for. Default: 3.0.0
|
|
51
|
-
--postmanCollection -p Will generate a postman collection (from the generated
|
|
51
|
+
--postmanCollection -p Will generate a postman collection (from the generated OpenAPI Description), in json only, if passed in. Default postman.json
|
|
52
52
|
--validationWarn -w Warn about validation errors only. Will write the OpenAPI file if generation is successful. Default: false
|
|
53
53
|
```
|
|
54
54
|
|
|
@@ -71,6 +71,10 @@ Options:
|
|
|
71
71
|
- [CORS](#cors)
|
|
72
72
|
- [OWASP Secure Headers](#owasp)
|
|
73
73
|
|
|
74
|
+
#### Validation
|
|
75
|
+
|
|
76
|
+
- [Validation](#validator)
|
|
77
|
+
|
|
74
78
|
### OpenAPI Mapping
|
|
75
79
|
|
|
76
80
|
| OpenAPI field | Serverless field |
|
|
@@ -133,7 +137,7 @@ Options:
|
|
|
133
137
|
|
|
134
138
|
### Configuration
|
|
135
139
|
|
|
136
|
-
To configure this plugin to generate valid OpenAPI
|
|
140
|
+
To configure this plugin to generate a valid OpenAPI Description, there are two places you'll need to modify in your `serverless.yml` file, the `custom` variables section and the `http/httpApi` event section for each given function in your service.
|
|
137
141
|
|
|
138
142
|
The `custom` section of your `serverless.yml` can be configured as below:
|
|
139
143
|
|
|
@@ -934,6 +938,12 @@ methodResponse:
|
|
|
934
938
|
|
|
935
939
|
This will set the `Cache-Control` Response Header to have a value of "no-store" rather than any value the OWASP Secure Headers Project recommends.
|
|
936
940
|
|
|
941
|
+
## Validator
|
|
942
|
+
|
|
943
|
+
Validation for the OpenAPI Description is now (as of 0.0.90) done by [Redocly](https://redocly.com/). This is a slightly less opinionated validator for an OpenAPI Description, it should result in less errors around "YAML Anchors". It's also a maintained library, and has support for OpenAPI 3.1.0 which I hope to be able to support very soon.
|
|
944
|
+
|
|
945
|
+
I am making use of https://www.npmjs.com/package/@redocly/openapi-core, which I have been warned is likely to change. If you notice anything going wrong with validation of your OpenAPI Description, feel free to open an issue here. I make active use of this library, so will hopefully come across those issues too.
|
|
946
|
+
|
|
937
947
|
## Example configuration
|
|
938
948
|
|
|
939
949
|
Please view the example [serverless.yml](test/serverless-tests/best/serverless.yml).
|
|
@@ -975,7 +985,7 @@ We use the plugin [JSON Schema $Ref Parser](https://apitools.dev/json-schema-ref
|
|
|
975
985
|
}
|
|
976
986
|
```
|
|
977
987
|
|
|
978
|
-
Where the definition "link" refers to a schema held in a directory that the resolver does not know about, we will not be able to fully resolve the schema which will likely cause errors in validation of the
|
|
988
|
+
Where the definition "link" refers to a schema held in a directory that the resolver does not know about, we will not be able to fully resolve the schema which will likely cause errors in validation of the OpenAPI 3.0.X Description.
|
|
979
989
|
|
|
980
990
|
Because of the dependency we use to parse externally linked schemas, we can supply our own options to resolve schemas that are more difficult than a straight forward example.
|
|
981
991
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-openapi-documenter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.90",
|
|
4
4
|
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@apidevtools/json-schema-ref-parser": "^9.1.0",
|
|
41
|
+
"@redocly/openapi-core": "^1.2.0",
|
|
41
42
|
"chalk": "^4.1.2",
|
|
42
43
|
"js-yaml": "^4.1.0",
|
|
43
44
|
"json-schema-for-openapi": "^0.4.1",
|
|
44
45
|
"lodash.isequal": "^4.5.0",
|
|
45
|
-
"
|
|
46
|
-
"openapi-to-postmanv2": "^4.17.0",
|
|
46
|
+
"openapi-to-postmanv2": "^4.18.0",
|
|
47
47
|
"swagger2openapi": "^7.0.8",
|
|
48
48
|
"uuid": "^9.0.0"
|
|
49
49
|
},
|
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
|
|
5
|
+
const {
|
|
6
|
+
Config,
|
|
7
|
+
lintFromString,
|
|
8
|
+
stringifyYaml,
|
|
9
|
+
createConfig,
|
|
10
|
+
} = require("@redocly/openapi-core");
|
|
5
11
|
const isEqual = require("lodash.isequal");
|
|
6
|
-
const validator = require("oas-validator");
|
|
7
12
|
const { v4: uuid } = require("uuid");
|
|
8
13
|
|
|
9
14
|
const SchemaHandler = require("./schemaHandler");
|
|
@@ -971,9 +976,35 @@ class DefinitionGenerator {
|
|
|
971
976
|
}
|
|
972
977
|
|
|
973
978
|
async validate() {
|
|
974
|
-
|
|
979
|
+
const config = await createConfig({
|
|
980
|
+
apis: {},
|
|
981
|
+
// styleguide: {
|
|
982
|
+
rules: {
|
|
983
|
+
spec: "error",
|
|
984
|
+
"path-parameters-defined": "error",
|
|
985
|
+
"operation-2xx-response": "error",
|
|
986
|
+
"operation-4xx-response": "error",
|
|
987
|
+
"operation-operationId-unique": "error",
|
|
988
|
+
"path-declaration-must-exist": "error",
|
|
989
|
+
},
|
|
990
|
+
// },
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
const apiDesc = stringifyYaml(this.openAPI);
|
|
994
|
+
|
|
995
|
+
const results = await lintFromString({
|
|
996
|
+
source: apiDesc,
|
|
997
|
+
config: config,
|
|
998
|
+
}).catch((err) => {
|
|
999
|
+
console.error(err);
|
|
975
1000
|
throw err;
|
|
976
1001
|
});
|
|
1002
|
+
|
|
1003
|
+
if (results.length) {
|
|
1004
|
+
throw results;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
return true;
|
|
977
1008
|
}
|
|
978
1009
|
}
|
|
979
1010
|
|
package/src/openAPIGenerator.js
CHANGED
|
@@ -30,7 +30,7 @@ class OpenAPIGenerator {
|
|
|
30
30
|
commands: {
|
|
31
31
|
generate: {
|
|
32
32
|
lifecycleEvents: ["serverless"],
|
|
33
|
-
usage: "Generate OpenAPI v3
|
|
33
|
+
usage: "Generate OpenAPI v3 Description",
|
|
34
34
|
options: {
|
|
35
35
|
output: {
|
|
36
36
|
usage: "Output file location [default: openapi.json|yml]",
|
|
@@ -60,7 +60,7 @@ class OpenAPIGenerator {
|
|
|
60
60
|
},
|
|
61
61
|
validationWarn: {
|
|
62
62
|
usage:
|
|
63
|
-
"Only warn about validation errors of the OpenAPI
|
|
63
|
+
"Only warn about validation errors of the OpenAPI Description, write the file if parsing is successful [default: false]",
|
|
64
64
|
shortcut: "w",
|
|
65
65
|
type: "boolean",
|
|
66
66
|
},
|
|
@@ -144,7 +144,7 @@ class OpenAPIGenerator {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
async generate() {
|
|
147
|
-
this.log(chalk.bold.underline("OpenAPI v3
|
|
147
|
+
this.log(chalk.bold.underline("OpenAPI v3 Description Generation"));
|
|
148
148
|
this.processCliInput();
|
|
149
149
|
|
|
150
150
|
const validOpenAPI = await this.generationAndValidation().catch((err) => {
|
|
@@ -168,12 +168,12 @@ class OpenAPIGenerator {
|
|
|
168
168
|
try {
|
|
169
169
|
fs.writeFileSync(this.config.file, output);
|
|
170
170
|
this.log(
|
|
171
|
-
"OpenAPI v3
|
|
171
|
+
"OpenAPI v3 Description Successfully Written",
|
|
172
172
|
this.logTypes.SUCCESS
|
|
173
173
|
);
|
|
174
174
|
} catch (err) {
|
|
175
175
|
this.log(
|
|
176
|
-
`ERROR: An error was thrown whilst writing the
|
|
176
|
+
`ERROR: An error was thrown whilst writing the OpenAPI Description`,
|
|
177
177
|
this.logTypes.ERROR
|
|
178
178
|
);
|
|
179
179
|
throw new this.serverless.classes.Error(err);
|
|
@@ -183,33 +183,37 @@ class OpenAPIGenerator {
|
|
|
183
183
|
async generationAndValidation() {
|
|
184
184
|
const generator = new DefinitionGenerator(this.serverless);
|
|
185
185
|
|
|
186
|
-
this.log(`Generating OpenAPI
|
|
186
|
+
this.log(`Generating OpenAPI Description`, this.logTypes.NOTICE);
|
|
187
187
|
await generator.parse().catch((err) => {
|
|
188
188
|
this.log(
|
|
189
|
-
`ERROR: An error was thrown generating the OpenAPI v3
|
|
189
|
+
`ERROR: An error was thrown generating the OpenAPI v3 Description`,
|
|
190
190
|
this.logTypes.ERROR
|
|
191
191
|
);
|
|
192
192
|
throw new this.serverless.classes.Error(err);
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
-
this.log(
|
|
196
|
-
`Validating generated OpenAPI documentation`,
|
|
197
|
-
this.logTypes.NOTICE
|
|
198
|
-
);
|
|
195
|
+
this.log(`Validating generated OpenAPI Description`, this.logTypes.NOTICE);
|
|
199
196
|
|
|
200
197
|
await generator.validate().catch((err) => {
|
|
201
198
|
this.log(
|
|
202
|
-
`ERROR: An error was thrown validating the OpenAPI v3
|
|
199
|
+
`ERROR: An error was thrown validating the OpenAPI v3 Description`,
|
|
203
200
|
this.logTypes.ERROR
|
|
204
201
|
);
|
|
202
|
+
|
|
205
203
|
this.validationErrorDetails(err);
|
|
204
|
+
|
|
206
205
|
if (this.config.validationWarn === false) {
|
|
207
|
-
|
|
206
|
+
let message = "Error validating OpenAPI Description:\r\n";
|
|
207
|
+
for (const errorMessage of err) {
|
|
208
|
+
message += `${errorMessage.message}\r\n`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
throw new this.serverless.classes.Error(message);
|
|
208
212
|
}
|
|
209
213
|
});
|
|
210
214
|
|
|
211
215
|
this.log(
|
|
212
|
-
"OpenAPI v3
|
|
216
|
+
"OpenAPI v3 Description Successfully Generated",
|
|
213
217
|
this.logTypes.SUCCESS
|
|
214
218
|
);
|
|
215
219
|
|
|
@@ -305,27 +309,22 @@ class OpenAPIGenerator {
|
|
|
305
309
|
this.log(
|
|
306
310
|
`${chalk.bold.yellow(
|
|
307
311
|
"[VALIDATION]"
|
|
308
|
-
)}
|
|
309
|
-
this.logTypes.ERROR
|
|
310
|
-
);
|
|
311
|
-
this.log(
|
|
312
|
-
`${chalk.bold.yellow("Context:")} ${JSON.stringify(
|
|
313
|
-
validationError.options.context[
|
|
314
|
-
validationError.options.context.length - 1
|
|
315
|
-
],
|
|
316
|
-
null,
|
|
317
|
-
2
|
|
318
|
-
)}\n`,
|
|
319
|
-
this.logTypes.ERROR
|
|
320
|
-
);
|
|
321
|
-
this.log(
|
|
322
|
-
`${chalk.bold.yellow("Error Message:")} ${JSON.stringify(
|
|
323
|
-
validationError.message,
|
|
324
|
-
null,
|
|
325
|
-
2
|
|
326
|
-
)}\n`,
|
|
312
|
+
)} Validation errors found in OpenAPI Description: \n`,
|
|
327
313
|
this.logTypes.ERROR
|
|
328
314
|
);
|
|
315
|
+
|
|
316
|
+
for (const error of validationError) {
|
|
317
|
+
this.log(
|
|
318
|
+
`${chalk.bold.yellow("Message:")} ${error.message}`,
|
|
319
|
+
this.logTypes.ERROR
|
|
320
|
+
);
|
|
321
|
+
for (const location of error.location) {
|
|
322
|
+
this.log(
|
|
323
|
+
`${chalk.bold.yellow("found at location:")} ${location.pointer}`,
|
|
324
|
+
this.logTypes.ERROR
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
329
328
|
}
|
|
330
329
|
}
|
|
331
330
|
|
|
@@ -1,30 +1,48 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
2
|
+
"custom": {
|
|
3
|
+
"documentation": {
|
|
4
|
+
"title": "test-service",
|
|
5
|
+
"models": [
|
|
6
|
+
{
|
|
7
|
+
"name": "SuccessResponse",
|
|
8
|
+
"description": "Success response",
|
|
9
|
+
"content": {
|
|
10
|
+
"application/json": {
|
|
11
|
+
"schema": {
|
|
12
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"SomeObject": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"SomeAttribute": {
|
|
19
|
+
"type": "string"
|
|
20
|
+
}
|
|
25
21
|
}
|
|
22
|
+
}
|
|
26
23
|
}
|
|
27
|
-
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "ErrorResponse",
|
|
30
|
+
"description": "Error response",
|
|
31
|
+
"content": {
|
|
32
|
+
"application/json": {
|
|
33
|
+
"schema": {
|
|
34
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": {
|
|
37
|
+
"error": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
28
44
|
}
|
|
45
|
+
]
|
|
29
46
|
}
|
|
47
|
+
}
|
|
30
48
|
}
|
|
@@ -1,35 +1,44 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"methodResponses": [
|
|
20
|
-
{
|
|
21
|
-
"statusCode": 200,
|
|
22
|
-
"responseBody": {
|
|
23
|
-
"description": "A user object along with generated API Keys"
|
|
24
|
-
},
|
|
25
|
-
"responseModels": {
|
|
26
|
-
"application/json": "SuccessResponse"
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
}
|
|
2
|
+
"createUser": {
|
|
3
|
+
"name": "createUser",
|
|
4
|
+
"handler": "handler.create",
|
|
5
|
+
"events": [
|
|
6
|
+
{
|
|
7
|
+
"http": {
|
|
8
|
+
"path": "find/{name}",
|
|
9
|
+
"method": "get",
|
|
10
|
+
"documentation": {
|
|
11
|
+
"pathParams": [
|
|
12
|
+
{
|
|
13
|
+
"name": "name",
|
|
14
|
+
"schema": {
|
|
15
|
+
"type": "string"
|
|
31
16
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"methodResponses": [
|
|
20
|
+
{
|
|
21
|
+
"statusCode": 200,
|
|
22
|
+
"responseBody": {
|
|
23
|
+
"description": "A user object along with generated API Keys"
|
|
24
|
+
},
|
|
25
|
+
"responseModels": {
|
|
26
|
+
"application/json": "SuccessResponse"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"statusCode": 400,
|
|
31
|
+
"responseBody": {
|
|
32
|
+
"description": "An Error"
|
|
33
|
+
},
|
|
34
|
+
"responseModels": {
|
|
35
|
+
"application/json": "ErrorResponse"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
35
44
|
}
|
|
@@ -187,8 +187,12 @@ describe("OpenAPIGenerator", () => {
|
|
|
187
187
|
const validOpenAPIDocument = await openAPIGenerator
|
|
188
188
|
.generationAndValidation()
|
|
189
189
|
.catch((err) => {
|
|
190
|
-
expect(err.message).to.be.equal(
|
|
191
|
-
|
|
190
|
+
// expect(err.message).to.be.equal(
|
|
191
|
+
// `Error validating OpenAPI Description:\r\nThe operation does not define the path parameter \`{name}\` expected by path \`/find/{name}\`.`
|
|
192
|
+
// );
|
|
193
|
+
expect(err).to.have.property("message");
|
|
194
|
+
expect(err.message).to.include(
|
|
195
|
+
"Error validating OpenAPI Description:"
|
|
192
196
|
);
|
|
193
197
|
});
|
|
194
198
|
|