mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-09 20:57:44 +00:00
fix: schema property:example should be validated (#375)
* fix: schema property:example should be validated * chore: schema-example separate rule * test: example-schema: string, number, int * chore: example object in schema test + minor rule fix * chore: support v3.1.0 * chore: rewrite schema-example-type rule using ajv * update tests for 3.1 * chore: no-invalid-parameter-examples rule * fix: reporting * chore: added disallowAdditionalProperties * docs: rules no-invalid parameter & schema examples
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
apiDefinitions:
|
||||
main: ./openapi.yaml
|
||||
|
||||
lint:
|
||||
rules:
|
||||
no-invalid-schema-examples: error
|
||||
extends: []
|
||||
@@ -0,0 +1,37 @@
|
||||
openapi: '3.0.0'
|
||||
info:
|
||||
version: v0
|
||||
title: my_Api
|
||||
description: my_api
|
||||
contact:
|
||||
name: my_api
|
||||
license:
|
||||
name: Proprietary
|
||||
url: https://my_api.com
|
||||
servers:
|
||||
- url: https://my_api.com
|
||||
|
||||
paths:
|
||||
/my_post:
|
||||
post:
|
||||
operationId: my_post
|
||||
description: my_post
|
||||
summary: my_post
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
my_list:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
items:
|
||||
type: string
|
||||
example:
|
||||
test
|
||||
responses:
|
||||
'200':
|
||||
description: My 200 response
|
||||
@@ -0,0 +1,28 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`E2E lint no-invalid-schema-examples-array-error 1`] = `
|
||||
|
||||
validating /openapi.yaml...
|
||||
[1] openapi.yaml:34:21 at #/paths/~1my_post/post/requestBody/content/application~1json/schema/properties/my_list/example
|
||||
|
||||
Example value must conform to the schema: type must be array.
|
||||
|
||||
32 | type: string
|
||||
33 | example:
|
||||
34 | test
|
||||
| ^^^^
|
||||
35 | responses:
|
||||
36 | '200':
|
||||
|
||||
referenced from openapi.yaml:29:19
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
/openapi.yaml: validated in <test>ms
|
||||
|
||||
❌ Validation failed with 1 error.
|
||||
run \`openapi lint --generate-ignore-file\` to add all problems to the ignore file.
|
||||
|
||||
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
apiDefinitions:
|
||||
main: ./openapi.yaml
|
||||
|
||||
lint:
|
||||
rules:
|
||||
no-invalid-schema-examples: error
|
||||
extends: []
|
||||
@@ -0,0 +1,79 @@
|
||||
openapi: '3.0.0'
|
||||
info:
|
||||
version: v0
|
||||
title: my_Api
|
||||
description: my_api
|
||||
contact:
|
||||
name: my_api
|
||||
license:
|
||||
name: Proprietary
|
||||
url: https://my_api.com
|
||||
servers:
|
||||
- url: https://my_api.com
|
||||
|
||||
paths:
|
||||
/pet:
|
||||
parameters:
|
||||
- name: Test
|
||||
schema:
|
||||
example:
|
||||
property: 42
|
||||
type: object
|
||||
properties:
|
||||
property:
|
||||
type: string
|
||||
/my_post:
|
||||
post:
|
||||
operationId: my_post
|
||||
description: my post
|
||||
summary: my_post
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: ID
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Test'
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Test:
|
||||
type: object
|
||||
example: test example
|
||||
properties:
|
||||
my_list:
|
||||
type: string
|
||||
example: 50
|
||||
nested:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Dog'
|
||||
- type: object
|
||||
example: dog
|
||||
properties:
|
||||
huntingSkill:
|
||||
type: string
|
||||
example: 100
|
||||
nested_schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Category'
|
||||
Dog:
|
||||
type: object
|
||||
example: test dog example
|
||||
properties:
|
||||
my_list:
|
||||
type: string
|
||||
example: 32
|
||||
Category:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: number
|
||||
description: Category ID
|
||||
example: category example
|
||||
@@ -0,0 +1,139 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`E2E lint no-invalid-schema-examples-nested-error 1`] = `
|
||||
|
||||
validating /openapi.yaml...
|
||||
[1] openapi.yaml:20:23 at #/paths/~1pet/parameters/0/schema/example/property
|
||||
|
||||
Example value must conform to the schema: \`property\` property type must be string.
|
||||
|
||||
18 | schema:
|
||||
19 | example:
|
||||
20 | property: 42
|
||||
| ^^
|
||||
21 | type: object
|
||||
22 | properties:
|
||||
|
||||
referenced from openapi.yaml:19:11
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[2] openapi.yaml:53:20 at #/components/schemas/Test/properties/my_list/example
|
||||
|
||||
Example value must conform to the schema: type must be string.
|
||||
|
||||
51 | my_list:
|
||||
52 | type: string
|
||||
53 | example: 50
|
||||
| ^^
|
||||
54 | nested:
|
||||
55 | allOf:
|
||||
|
||||
referenced from openapi.yaml:52:11
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[3] openapi.yaml:72:20 at #/components/schemas/Dog/properties/my_list/example
|
||||
|
||||
Example value must conform to the schema: type must be string.
|
||||
|
||||
70 | my_list:
|
||||
71 | type: string
|
||||
72 | example: 32
|
||||
| ^^
|
||||
73 | Category:
|
||||
74 | type: object
|
||||
|
||||
referenced from openapi.yaml:71:11
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[4] openapi.yaml:68:16 at #/components/schemas/Dog/example
|
||||
|
||||
Example value must conform to the schema: type must be object.
|
||||
|
||||
66 | Dog:
|
||||
67 | type: object
|
||||
68 | example: test dog example
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
69 | properties:
|
||||
70 | my_list:
|
||||
|
||||
referenced from openapi.yaml:67:7
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[5] openapi.yaml:62:28 at #/components/schemas/Test/properties/nested/allOf/1/properties/huntingSkill/example
|
||||
|
||||
Example value must conform to the schema: type must be string.
|
||||
|
||||
60 | huntingSkill:
|
||||
61 | type: string
|
||||
62 | example: 100
|
||||
| ^^^
|
||||
63 | nested_schema:
|
||||
64 | oneOf:
|
||||
|
||||
referenced from openapi.yaml:61:19
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[6] openapi.yaml:79:20 at #/components/schemas/Category/properties/id/example
|
||||
|
||||
Example value must conform to the schema: type must be number.
|
||||
|
||||
77 | type: number
|
||||
78 | description: Category ID
|
||||
79 | example: category example
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
80 |
|
||||
|
||||
referenced from openapi.yaml:77:11
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[7] openapi.yaml:58:24 at #/components/schemas/Test/properties/nested/allOf/1/example
|
||||
|
||||
Example value must conform to the schema: type must be object.
|
||||
|
||||
56 | - $ref: '#/components/schemas/Dog'
|
||||
57 | - type: object
|
||||
58 | example: dog
|
||||
| ^^^
|
||||
59 | properties:
|
||||
60 | huntingSkill:
|
||||
|
||||
referenced from openapi.yaml:57:15
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[8] openapi.yaml:49:16 at #/components/schemas/Test/example
|
||||
|
||||
Example value must conform to the schema: type must be object.
|
||||
|
||||
47 | Test:
|
||||
48 | type: object
|
||||
49 | example: test example
|
||||
| ^^^^^^^^^^^^
|
||||
50 | properties:
|
||||
51 | my_list:
|
||||
|
||||
referenced from openapi.yaml:48:7
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
/openapi.yaml: validated in <test>ms
|
||||
|
||||
❌ Validation failed with 8 errors.
|
||||
run \`openapi lint --generate-ignore-file\` to add all problems to the ignore file.
|
||||
|
||||
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
apiDefinitions:
|
||||
main: ./openapi.yaml
|
||||
|
||||
lint:
|
||||
rules:
|
||||
no-invalid-schema-examples: error
|
||||
extends: []
|
||||
@@ -0,0 +1,44 @@
|
||||
openapi: '3.1.0'
|
||||
info:
|
||||
version: v0
|
||||
title: my_Api
|
||||
description: my_api
|
||||
contact:
|
||||
name: my_api
|
||||
license:
|
||||
name: Proprietary
|
||||
url: https://my_api.com
|
||||
servers:
|
||||
- url: https://my_api.com
|
||||
|
||||
paths:
|
||||
/my_post:
|
||||
post:
|
||||
operationId: my_post
|
||||
description: my_post
|
||||
summary: my_post
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
one:
|
||||
description: type array
|
||||
type:
|
||||
- integer
|
||||
- string
|
||||
example:
|
||||
- test
|
||||
two:
|
||||
description: two
|
||||
type:
|
||||
- string
|
||||
examples:
|
||||
- test
|
||||
- 25
|
||||
responses:
|
||||
'200':
|
||||
description: My 200 response
|
||||
@@ -0,0 +1,46 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`E2E lint no-invalid-schema-examples-oas3.1-error 1`] = `
|
||||
|
||||
validating /openapi.yaml...
|
||||
[1] openapi.yaml:34:21 at #/paths/~1my_post/post/requestBody/content/application~1json/schema/properties/one/example
|
||||
|
||||
Example value must conform to the schema: type must be integer,string.
|
||||
|
||||
32 | - string
|
||||
33 | example:
|
||||
34 | - test
|
||||
| ^^^^^^
|
||||
35 | two:
|
||||
| ^
|
||||
36 | description: two
|
||||
37 | type:
|
||||
|
||||
referenced from openapi.yaml:29:19
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[2] openapi.yaml:41:23 at #/paths/~1my_post/post/requestBody/content/application~1json/schema/properties/two/examples/1
|
||||
|
||||
Example value must conform to the schema: type must be string.
|
||||
|
||||
39 | examples:
|
||||
40 | - test
|
||||
41 | - 25
|
||||
| ^^
|
||||
42 | responses:
|
||||
43 | '200':
|
||||
|
||||
referenced from openapi.yaml:36:19
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
/openapi.yaml: validated in <test>ms
|
||||
|
||||
❌ Validation failed with 2 errors.
|
||||
run \`openapi lint --generate-ignore-file\` to add all problems to the ignore file.
|
||||
|
||||
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
apiDefinitions:
|
||||
main: ./openapi.yaml
|
||||
|
||||
lint:
|
||||
rules:
|
||||
no-invalid-schema-examples: error
|
||||
extends: []
|
||||
@@ -0,0 +1,44 @@
|
||||
openapi: '3.1.0'
|
||||
info:
|
||||
version: v0
|
||||
title: my_Api
|
||||
description: my_api
|
||||
contact:
|
||||
name: my_api
|
||||
license:
|
||||
name: Proprietary
|
||||
url: https://my_api.com
|
||||
servers:
|
||||
- url: https://my_api.com
|
||||
|
||||
paths:
|
||||
/my_post:
|
||||
post:
|
||||
operationId: my_post
|
||||
description: my_post
|
||||
summary: my_post
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
one:
|
||||
description: type array
|
||||
type:
|
||||
- integer
|
||||
- string
|
||||
example: test
|
||||
two:
|
||||
description: two
|
||||
type:
|
||||
- integer
|
||||
- string
|
||||
examples:
|
||||
- test
|
||||
- 25
|
||||
responses:
|
||||
'200':
|
||||
description: My 200 response
|
||||
11
__tests__/lint/no-invalid-schema-examples-oas3.1/snapshot.js
Normal file
11
__tests__/lint/no-invalid-schema-examples-oas3.1/snapshot.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`E2E lint no-invalid-schema-examples-oas3.1 1`] = `
|
||||
|
||||
validating /openapi.yaml...
|
||||
/openapi.yaml: validated in <test>ms
|
||||
|
||||
Woohoo! Your OpenAPI definition is valid. 🎉
|
||||
|
||||
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
apiDefinitions:
|
||||
main: ./openapi.yaml
|
||||
|
||||
lint:
|
||||
rules:
|
||||
no-invalid-schema-examples: error
|
||||
extends: []
|
||||
@@ -0,0 +1,40 @@
|
||||
openapi: '3.0.0'
|
||||
info:
|
||||
version: v0
|
||||
title: my_Api
|
||||
description: my_api
|
||||
contact:
|
||||
name: my_api
|
||||
license:
|
||||
name: Proprietary
|
||||
url: https://my_api.com
|
||||
servers:
|
||||
- url: https://my_api.com
|
||||
|
||||
paths:
|
||||
/my_post:
|
||||
post:
|
||||
operationId: my_post
|
||||
description: my_post
|
||||
summary: my_post
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
prop_string:
|
||||
type: string
|
||||
example: 56
|
||||
prop_number:
|
||||
type: number
|
||||
example: test
|
||||
prop_integer:
|
||||
type: integer
|
||||
example: 5.34
|
||||
|
||||
responses:
|
||||
'200':
|
||||
description: My 200 response
|
||||
@@ -0,0 +1,60 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`E2E lint no-invalid-schema-examples-string-number-error 1`] = `
|
||||
|
||||
validating /openapi.yaml...
|
||||
[1] openapi.yaml:30:28 at #/paths/~1my_post/post/requestBody/content/application~1json/schema/properties/prop_string/example
|
||||
|
||||
Example value must conform to the schema: type must be string.
|
||||
|
||||
28 | prop_string:
|
||||
29 | type: string
|
||||
30 | example: 56
|
||||
| ^^
|
||||
31 | prop_number:
|
||||
32 | type: number
|
||||
|
||||
referenced from openapi.yaml:29:19
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[2] openapi.yaml:33:28 at #/paths/~1my_post/post/requestBody/content/application~1json/schema/properties/prop_number/example
|
||||
|
||||
Example value must conform to the schema: type must be number.
|
||||
|
||||
31 | prop_number:
|
||||
32 | type: number
|
||||
33 | example: test
|
||||
| ^^^^
|
||||
34 | prop_integer:
|
||||
35 | type: integer
|
||||
|
||||
referenced from openapi.yaml:32:19
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
[3] openapi.yaml:36:28 at #/paths/~1my_post/post/requestBody/content/application~1json/schema/properties/prop_integer/example
|
||||
|
||||
Example value must conform to the schema: type must be integer.
|
||||
|
||||
34 | prop_integer:
|
||||
35 | type: integer
|
||||
36 | example: 5.34
|
||||
| ^^^^
|
||||
37 |
|
||||
38 | responses:
|
||||
|
||||
referenced from openapi.yaml:35:19
|
||||
|
||||
Error was generated by the no-invalid-schema-examples rule.
|
||||
|
||||
|
||||
/openapi.yaml: validated in <test>ms
|
||||
|
||||
❌ Validation failed with 3 errors.
|
||||
run \`openapi lint --generate-ignore-file\` to add all problems to the ignore file.
|
||||
|
||||
|
||||
`;
|
||||
7
__tests__/lint/no-invalid-schema-examples/.redocly.yaml
Normal file
7
__tests__/lint/no-invalid-schema-examples/.redocly.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiDefinitions:
|
||||
main: ./openapi.yaml
|
||||
|
||||
lint:
|
||||
rules:
|
||||
no-invalid-schema-examples: error
|
||||
extends: []
|
||||
79
__tests__/lint/no-invalid-schema-examples/openapi.yaml
Normal file
79
__tests__/lint/no-invalid-schema-examples/openapi.yaml
Normal file
@@ -0,0 +1,79 @@
|
||||
openapi: '3.0.0'
|
||||
info:
|
||||
version: v0
|
||||
title: my_Api
|
||||
description: my_api
|
||||
contact:
|
||||
name: my_api
|
||||
license:
|
||||
name: Proprietary
|
||||
url: https://my_api.com
|
||||
servers:
|
||||
- url: https://my_api.com
|
||||
|
||||
paths:
|
||||
/pet:
|
||||
parameters:
|
||||
- name: Test
|
||||
schema:
|
||||
example:
|
||||
property: prop
|
||||
type: object
|
||||
properties:
|
||||
property:
|
||||
type: string
|
||||
/my_post:
|
||||
post:
|
||||
operationId: my_post
|
||||
description: my post
|
||||
summary: my_post
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: ID
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Test'
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Test:
|
||||
type: object
|
||||
properties:
|
||||
my_list:
|
||||
type: string
|
||||
example: list
|
||||
nested:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Dog'
|
||||
- type: object
|
||||
example:
|
||||
pet: dog
|
||||
properties:
|
||||
huntingSkill:
|
||||
type: string
|
||||
example: skill
|
||||
nested_schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Category'
|
||||
Dog:
|
||||
type: object
|
||||
example:
|
||||
status: 400
|
||||
properties:
|
||||
my_list:
|
||||
type: string
|
||||
example: my list
|
||||
Category:
|
||||
properties:
|
||||
id:
|
||||
type: number
|
||||
description: Category ID
|
||||
example: 45.78
|
||||
11
__tests__/lint/no-invalid-schema-examples/snapshot.js
Normal file
11
__tests__/lint/no-invalid-schema-examples/snapshot.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`E2E lint no-invalid-schema-examples 1`] = `
|
||||
|
||||
validating /openapi.yaml...
|
||||
/openapi.yaml: validated in <test>ms
|
||||
|
||||
Woohoo! Your OpenAPI definition is valid. 🎉
|
||||
|
||||
|
||||
`;
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`E2E spec-error-if-minimum-not-correct 1`] = `
|
||||
exports[`E2E lint spec-error-if-minimum-not-correct 1`] = `
|
||||
|
||||
No configurations were defined in extends -- using built in recommended configuration by default.
|
||||
|
||||
|
||||
@@ -242,6 +242,29 @@ lint:
|
||||
- application/json
|
||||
```
|
||||
|
||||
### no-invalid-schema-examples
|
||||
|
||||
Verifies that schema example value conforms to the schema. Disallows additional properties by default.
|
||||
|
||||
```yaml
|
||||
lint:
|
||||
no-invalid-schema-examples:
|
||||
severity: error
|
||||
disallowAdditionalProperties: false
|
||||
```
|
||||
|
||||
|
||||
### no-invalid-parameter-examples
|
||||
|
||||
Verifies that parameter example value conforms to the schema. Disallows additional properties by default.
|
||||
|
||||
```yaml
|
||||
lint:
|
||||
no-invalid-parameter-examples:
|
||||
severity: error
|
||||
disallowAdditionalProperties: false
|
||||
```
|
||||
|
||||
## Recommended config
|
||||
|
||||
There are three built-in configurations:
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"preview": "npm run cli preview-docs resources/pets.yaml",
|
||||
"benchmark": "node --expose-gc --noconcurrent_sweeping --predictable packages/core/src/benchmark/benchmark.js",
|
||||
"webpack-bundle": "webpack --config webpack.config.ts",
|
||||
"upload": "node scripts/archive-and-upload-bundle.js",
|
||||
"lint_test": "npm run cli lint -- --config resources/config.yaml --format stylish"
|
||||
"upload": "node scripts/archive-and-upload-bundle.js"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
||||
@@ -38,6 +38,8 @@ export default {
|
||||
},
|
||||
'request-mime-type': 'error',
|
||||
spec: 'error',
|
||||
'no-invalid-schema-examples': 'error',
|
||||
'no-invalid-parameter-examples': 'error',
|
||||
},
|
||||
oas3_0Rules: {
|
||||
'no-invalid-media-type-examples': 'error',
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { UserContext } from '../../walk';
|
||||
import { Oas3Parameter } from '../../typings/openapi';
|
||||
import { validateExample } from '../utils';
|
||||
|
||||
export const NoInvalidParameterExamples: any = (opts: any) => {
|
||||
const disallowAdditionalProperties = opts.disallowAdditionalProperties ?? true;
|
||||
return {
|
||||
Parameter: {
|
||||
leave(parameter: Oas3Parameter, ctx: UserContext) {
|
||||
if (parameter.example) {
|
||||
validateExample(
|
||||
parameter.example,
|
||||
parameter.schema!,
|
||||
ctx.location.child('example'),
|
||||
ctx,
|
||||
disallowAdditionalProperties,
|
||||
);
|
||||
}
|
||||
|
||||
if (parameter.examples) {
|
||||
for (const [key, example] of Object.entries(parameter.examples)) {
|
||||
if ('value' in example) {
|
||||
validateExample(
|
||||
example.value,
|
||||
parameter.schema!,
|
||||
ctx.location.child(['examples', key]),
|
||||
ctx,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
27
packages/core/src/rules/common/no-invalid-schema-examples.ts
Normal file
27
packages/core/src/rules/common/no-invalid-schema-examples.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { UserContext } from '../../walk';
|
||||
import { Oas3_1Schema } from '../../typings/openapi';
|
||||
import { validateExample } from '../utils';
|
||||
|
||||
export const NoInvalidSchemaExamples: any = (opts: any) => {
|
||||
const disallowAdditionalProperties = opts.disallowAdditionalProperties ?? true;
|
||||
return {
|
||||
Schema: {
|
||||
leave(schema: Oas3_1Schema, ctx: UserContext) {
|
||||
if (schema.examples) {
|
||||
for (const example of schema.examples) {
|
||||
validateExample(
|
||||
example,
|
||||
schema,
|
||||
ctx.location.child(['examples', schema.examples.indexOf(example)]),
|
||||
ctx,
|
||||
disallowAdditionalProperties,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (schema.example) {
|
||||
validateExample(schema.example, schema, ctx.location.child('example'), ctx, false);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,4 +1,6 @@
|
||||
import { OasSpec } from '../common/spec';
|
||||
import { NoInvalidSchemaExamples } from '../common/no-invalid-schema-examples';
|
||||
import { NoInvalidParameterExamples } from '../common/no-invalid-parameter-examples';
|
||||
import { InfoDescription } from '../common/info-description';
|
||||
import { InfoContact } from '../common/info-contact';
|
||||
import { InfoLicense } from '../common/info-license-url';
|
||||
@@ -41,6 +43,8 @@ import { InfoDescriptionOverride } from '../common/info-description-override';
|
||||
|
||||
export const rules = {
|
||||
spec: OasSpec as Oas2Rule,
|
||||
'no-invalid-schema-examples': NoInvalidSchemaExamples,
|
||||
'no-invalid-parameter-examples': NoInvalidParameterExamples,
|
||||
'info-description': InfoDescription as Oas2Rule,
|
||||
'info-contact': InfoContact as Oas2Rule,
|
||||
'info-license': InfoLicense as Oas2Rule,
|
||||
|
||||
@@ -47,6 +47,8 @@ import { OperationDescriptionOverride } from '../common/operation-description-ov
|
||||
import { TagDescriptionOverride } from '../common/tag-description-override';
|
||||
import { InfoDescriptionOverride } from '../common/info-description-override';
|
||||
import { PathExcludesPatterns } from '../common/path-excludes-patterns';
|
||||
import { NoInvalidSchemaExamples } from '../common/no-invalid-schema-examples';
|
||||
import { NoInvalidParameterExamples } from '../common/no-invalid-parameter-examples';
|
||||
|
||||
export const rules = {
|
||||
spec: OasSpec,
|
||||
@@ -93,6 +95,8 @@ export const rules = {
|
||||
'request-mime-type': RequestMimeType,
|
||||
'response-mime-type': ResponseMimeType,
|
||||
'path-segment-plural': PathSegmentPlural,
|
||||
'no-invalid-schema-examples': NoInvalidSchemaExamples,
|
||||
'no-invalid-parameter-examples': NoInvalidParameterExamples,
|
||||
} as Oas3RuleSet;
|
||||
|
||||
export const preprocessors = {};
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import { Oas3Rule } from '../../visitors';
|
||||
import { validateJsonSchema } from '../ajv';
|
||||
import { Location, isRef } from '../../ref-utils';
|
||||
import { Oas3Example } from '../../typings/openapi';
|
||||
import { validateExample } from '../utils';
|
||||
import { UserContext } from '../../walk';
|
||||
|
||||
export const ValidContentExamples: Oas3Rule = (opts) => {
|
||||
const disallowAdditionalProperties = opts.disallowAdditionalProperties ?? true;
|
||||
|
||||
return {
|
||||
MediaType: {
|
||||
leave(mediaType, { report, location, resolve }) {
|
||||
leave(mediaType, ctx: UserContext) {
|
||||
const { location, resolve } = ctx;
|
||||
if (!mediaType.schema) return;
|
||||
|
||||
if (mediaType.example) {
|
||||
validateExample(mediaType.example, location.child('example'));
|
||||
validateExample(
|
||||
mediaType.example,
|
||||
mediaType.schema,
|
||||
location.child('example'),
|
||||
ctx,
|
||||
disallowAdditionalProperties,
|
||||
);
|
||||
} else if (mediaType.examples) {
|
||||
for (const exampleName of Object.keys(mediaType.examples)) {
|
||||
let example = mediaType.examples[exampleName];
|
||||
@@ -23,40 +30,13 @@ export const ValidContentExamples: Oas3Rule = (opts) => {
|
||||
dataLoc = resolved.location.child('value');
|
||||
example = resolved.node;
|
||||
}
|
||||
|
||||
validateExample(example.value, dataLoc);
|
||||
}
|
||||
}
|
||||
|
||||
function validateExample(example: any, dataLoc: Location) {
|
||||
try {
|
||||
const { valid, errors } = validateJsonSchema(
|
||||
example,
|
||||
mediaType.schema!,
|
||||
location.child('schema'),
|
||||
dataLoc.pointer,
|
||||
resolve,
|
||||
validateExample(
|
||||
example.value,
|
||||
mediaType.schema,
|
||||
dataLoc,
|
||||
ctx,
|
||||
disallowAdditionalProperties,
|
||||
);
|
||||
if (!valid) {
|
||||
for (let error of errors) {
|
||||
report({
|
||||
message: `Example value must conform to the schema: ${error.message}.`,
|
||||
location: {
|
||||
...new Location(dataLoc.source, error.instancePath),
|
||||
reportOnKey: error.keyword === 'additionalProperties',
|
||||
},
|
||||
from: location,
|
||||
suggest: error.suggest,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
report({
|
||||
message: `Example validation errored: ${e.message}.`,
|
||||
location: location.child('schema'),
|
||||
from: location
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import levenshtein = require('js-levenshtein');
|
||||
import { UserContext } from '../walk';
|
||||
import { Location } from '../ref-utils';
|
||||
import { validateJsonSchema } from './ajv';
|
||||
import { Oas3Schema, Referenced } from '../typings/openapi';
|
||||
|
||||
export function oasTypeOf(value: unknown) {
|
||||
if (Array.isArray(value)) {
|
||||
@@ -20,7 +23,7 @@ export function oasTypeOf(value: unknown) {
|
||||
*/
|
||||
export function matchesJsonSchemaType(value: unknown, type: string, nullable: boolean): boolean {
|
||||
if (nullable && value === null) {
|
||||
return value === null
|
||||
return value === null;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
@@ -80,3 +83,41 @@ export function getSuggest(given: string, variants: string[]): string[] {
|
||||
// if (bestMatch.distance <= 4) return bestMatch.string;
|
||||
return distances.map((d) => d.variant);
|
||||
}
|
||||
|
||||
export function validateExample(
|
||||
example: any,
|
||||
schema: Referenced<Oas3Schema>,
|
||||
dataLoc: Location,
|
||||
{ resolve, location, report }: UserContext,
|
||||
disallowAdditionalProperties: boolean,
|
||||
) {
|
||||
try {
|
||||
const { valid, errors } = validateJsonSchema(
|
||||
example,
|
||||
schema,
|
||||
location.child('schema'),
|
||||
dataLoc.pointer,
|
||||
resolve,
|
||||
disallowAdditionalProperties,
|
||||
);
|
||||
if (!valid) {
|
||||
for (let error of errors) {
|
||||
report({
|
||||
message: `Example value must conform to the schema: ${error.message}.`,
|
||||
location: {
|
||||
...new Location(dataLoc.source, error.instancePath),
|
||||
reportOnKey: error.keyword === 'additionalProperties',
|
||||
},
|
||||
from: location,
|
||||
suggest: error.suggest,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
report({
|
||||
message: `Example validation errored: ${e.message}.`,
|
||||
location: location.child('schema'),
|
||||
from: location,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,10 @@ export interface Oas3Schema {
|
||||
xml?: Oas3Xml;
|
||||
}
|
||||
|
||||
export interface Oas3_1Schema extends Oas3Schema {
|
||||
examples?: any[];
|
||||
}
|
||||
|
||||
export interface Oas3Discriminator {
|
||||
propertyName: string;
|
||||
mapping?: { [name: string]: string };
|
||||
|
||||
Reference in New Issue
Block a user