Skip to content

Commit

Permalink
fix: add JSON Schema 2019-09+ validation keyword - dependentRequired (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremyfiel authored Jul 3, 2024
1 parent aefb024 commit f807944
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .changeset/hungry-bears-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@redocly/openapi-core": patch
"@redocly/cli": patch
---

Added JSON Schema draft 2019-09+ validation keyword - `dependentRequired`.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
openapi: 3.1.0
info:
title: test json schema validation keyword - dependentRequired
version: 1.0.0
paths:
'/thing':
get:
summary: a sample api
responses:
'200':
description: OK
content:
'application/json':
schema:
$ref: '#/components/schemas/test_schema'
examples:
dependentRequired_passing:
summary: an example schema
value: { 'name': 'bobby', 'age': 25 }
dependentRequired_failing:
summary: an example schema
value: { 'name': 'jennie' }
components:
schemas:
test_schema:
type: object
properties:
name:
type: string
age:
type: number
dependentRequired:
name:
- age
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apis:
main:
root: ./openapi.yaml

rules:
spec: error
no-invalid-media-type-examples: error
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`E2E lint spec-json-schema-validation-dependentRequired 1`] = `
validating /openapi.yaml...
[1] openapi.yaml:22:26 at #/paths/~1thing/get/responses/200/content/application~1json/examples/dependentRequired_failing/value
Example value must conform to the schema: must have property age when property name is present.
20 | dependentRequired_failing:
21 | summary: an example schema
22 | value: { 'name': 'jennie' }
| ^^^^^^^^^^^^^^^^^^^^
23 | components:
24 | schemas:
referenced from openapi.yaml:14:15 at #/paths/~1thing/get/responses/200/content/application~1json
Error was generated by the no-invalid-media-type-examples rule.
/openapi.yaml: validated in <test>ms
❌ Validation failed with 1 error.
run \`redocly lint --generate-ignore-file\` to add all problems to the ignore file.
`;
124 changes: 124 additions & 0 deletions packages/core/src/__tests__/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1593,4 +1593,128 @@ describe('lint', () => {
expect(result[0]).toHaveProperty('ignored', true);
expect(result[0]).toHaveProperty('ruleId', 'operation-operationId');
});

it('should throw an error for dependentRequired not expected here - OAS 3.0.x', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.0.3
info:
title: test json schema validation keyword - dependentRequired
version: 1.0.0
paths:
'/thing':
get:
summary: a sample api
responses:
'200':
description: OK
content:
'application/json':
schema:
$ref: '#/components/schemas/test_schema'
examples:
dependentRequired_passing:
summary: an example schema
value: { "name": "bobby", "age": 25}
dependentRequired_failing:
summary: an example schema
value: { "name": "jennie"}
components:
schemas:
test_schema:
type: object
properties:
name:
type: string
age:
type: number
dependentRequired:
name:
- age
`,
''
);

const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml');

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await makeConfig({ spec: 'error' }, undefined, configFilePath),
});

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
[
{
"from": {
"pointer": "#/paths/~1thing/get/responses/200/content/application~1json/schema",
"source": "",
},
"location": [
{
"pointer": "#/components/schemas/test_schema/dependentRequired",
"reportOnKey": true,
"source": "",
},
],
"message": "Property \`dependentRequired\` is not expected here.",
"ruleId": "spec",
"severity": "error",
"suggest": [],
},
]
`);
});

it('should not throw an error for dependentRequired not expected here - OAS 3.1.x', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.1.0
info:
title: test json schema validation keyword - dependentRequired
version: 1.0.0
paths:
'/thing':
get:
summary: a sample api
responses:
'200':
description: OK
content:
'application/json':
schema:
$ref: '#/components/schemas/test_schema'
examples:
dependentRequired_passing:
summary: an example schema
value: { "name": "bobby", "age": 25}
dependentRequired_failing:
summary: an example schema
value: { "name": "jennie"}
components:
schemas:
test_schema:
type: object
properties:
name:
type: string
age:
type: number
dependentRequired:
name:
- age
`,
''
);

const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml');

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await makeConfig({ spec: 'error' }, undefined, configFilePath),
});

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
});
});
7 changes: 7 additions & 0 deletions packages/core/src/types/oas3_1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const Schema: NodeType = {
then: 'Schema',
else: 'Schema',
dependentSchemas: listOf('Schema'),
dependentRequired: 'DependentRequired',
prefixItems: listOf('Schema'),
contains: 'Schema',
minContains: { type: 'integer', minimum: 0 },
Expand Down Expand Up @@ -266,6 +267,11 @@ const SecurityScheme: NodeType = {
extensionsPrefix: 'x-',
};

const DependentRequired: NodeType = {
properties: {},
additionalProperties: { type: 'array', items: { type: 'string' } },
};

export const Oas3_1Types: Record<Oas3_1NodeType, NodeType> = {
...Oas3Types,
Info,
Expand All @@ -277,4 +283,5 @@ export const Oas3_1Types: Record<Oas3_1NodeType, NodeType> = {
NamedPathItems: mapOf('PathItem'),
SecurityScheme,
Operation,
DependentRequired,
};
1 change: 1 addition & 0 deletions packages/core/src/types/redocly-yaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ const oas3_1NodeTypesList = [
'NamedPathItems',
'SecurityScheme',
'Operation',
'DependentRequired',
] as const;

export type Oas3_1NodeType = typeof oas3_1NodeTypesList[number];
Expand Down

1 comment on commit f807944

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report

St.
Category Percentage Covered / Total
🟡 Statements 77.26% 4506/5832
🟡 Branches 67.78% 2495/3681
🟡 Functions 70.98% 751/1058
🟡 Lines 77.47% 4242/5476

Test suite run success

750 tests passing in 105 suites.

Report generated by 🧪jest coverage report action from f807944

Please sign in to comment.