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 documentation should output under. Default: openapi.json
48
- --format -f Whether to output the OpenAPI documentation as json or yaml. Default: json
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 openAPI documentation), in json only, if passed in. Default postman.json
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 documentation 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.
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 openAPI 3.0.X specification.
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.83",
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
- "oas-validator": "^5.0.8",
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
- return await validator.validateInner(this.openAPI, {}).catch((err) => {
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
 
@@ -30,7 +30,7 @@ class OpenAPIGenerator {
30
30
  commands: {
31
31
  generate: {
32
32
  lifecycleEvents: ["serverless"],
33
- usage: "Generate OpenAPI v3 Documentation",
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 document, write the file if parsing is successful [default: false]",
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 Document Generation"));
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 Documentation Successfully Written",
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 openAPI Documentation`,
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 documentation`, this.logTypes.NOTICE);
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 documentation`,
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 documentation`,
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
- throw new this.serverless.classes.Error(err);
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 Documentation Successfully Generated",
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
- )} Failed to validate OpenAPI document: \n`,
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
- "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
- "properties": {
14
- "SomeObject": {
15
- "type": "object",
16
- "properties": {
17
- "SomeAttribute": {
18
- "type": "string"
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
- "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"
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
- "AssertionError: Templated parameter name not found"
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