Skip to content

Commit e7bc009

Browse files
dfeufelepoberezkin
andauthoredJan 15, 2022
resolve references before validating the discriminator (#1815)
* resolve references before validating the discriminator fixes #1554 * prettier style * adjust according to comments, add some doc * resolve schema from SchemaEnv * simplify code, change comment * add import * Update lib/vocabularies/discriminator/index.ts * let to conts * Update lib/vocabularies/discriminator/index.ts * update error message * fix regexp in the test Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
1 parent ec96c87 commit e7bc009

File tree

3 files changed

+91
-5
lines changed

3 files changed

+91
-5
lines changed
 

‎docs/json-schema.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ There are following requirements and limitations of using `discriminator` keywor
992992
- `mapping` in discriminator object is not supported.
993993
- [oneOf](#oneof) keyword must be present in the same schema.
994994
- discriminator property should be [requried](#required) either on the top level, as in the example, or in all `oneOf` subschemas.
995-
- each `oneOf` subschema must have [properties](#properties) keyword with discriminator property.
995+
- each `oneOf` subschema must have [properties](#properties) keyword with discriminator property. The subschemas should be either inlined or included as direct references (only `$ref` keyword without any extra keywords is allowed).
996996
- schema for discriminator property in each `oneOf` subschema must be [const](#const) or [enum](#enum), with unique values across all subschemas.
997997

998998
Not meeting any of these requirements would fail schema compilation.

‎lib/vocabularies/discriminator/index.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type {CodeKeywordDefinition, AnySchemaObject, KeywordErrorDefinition} fro
22
import type {KeywordCxt} from "../../compile/validate"
33
import {_, getProperty, Name} from "../../compile/codegen"
44
import {DiscrError, DiscrErrorObj} from "../discriminator/types"
5+
import {resolveRef, SchemaEnv} from "../../compile"
6+
import {schemaHasRulesButRef} from "../../compile/util"
57

68
export type DiscriminatorError = DiscrErrorObj<DiscrError.Tag> | DiscrErrorObj<DiscrError.Mapping>
79

@@ -62,10 +64,16 @@ const def: CodeKeywordDefinition = {
6264
const topRequired = hasRequired(parentSchema)
6365
let tagRequired = true
6466
for (let i = 0; i < oneOf.length; i++) {
65-
const sch = oneOf[i]
66-
const propSch = sch.properties?.[tagName]
67+
let sch = oneOf[i]
68+
if (sch?.$ref && !schemaHasRulesButRef(sch, it.self.RULES)) {
69+
sch = resolveRef.call(it.self, it.schemaEnv, it.baseId, sch?.$ref)
70+
if (sch instanceof SchemaEnv) sch = sch.schema
71+
}
72+
const propSch = sch?.properties?.[tagName]
6773
if (typeof propSch != "object") {
68-
throw new Error(`discriminator: oneOf schemas must have "properties/${tagName}"`)
74+
throw new Error(
75+
`discriminator: oneOf subschemas (or referenced schemas) must have "properties/${tagName}"`
76+
)
6977
}
7078
tagRequired = tagRequired && (topRequired || hasRequired(sch))
7179
addMappings(propSch, i)

‎spec/discriminator.spec.ts

+79-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,84 @@ describe("discriminator keyword", function () {
8181
})
8282
})
8383

84+
describe("validation with referenced schemas", () => {
85+
const definitions1 = {
86+
schema1: {
87+
properties: {
88+
foo: {const: "x"},
89+
a: {type: "string"},
90+
},
91+
required: ["foo", "a"],
92+
},
93+
schema2: {
94+
properties: {
95+
foo: {enum: ["y", "z"]},
96+
b: {type: "string"},
97+
},
98+
required: ["foo", "b"],
99+
},
100+
}
101+
const mainSchema1 = {
102+
type: "object",
103+
discriminator: {propertyName: "foo"},
104+
oneOf: [
105+
{
106+
$ref: "#/definitions/schema1",
107+
},
108+
{
109+
$ref: "#/definitions/schema2",
110+
},
111+
],
112+
}
113+
114+
const definitions2 = {
115+
schema1: {
116+
properties: {
117+
foo: {const: "x"},
118+
a: {type: "string"},
119+
},
120+
required: ["a"],
121+
},
122+
schema2: {
123+
properties: {
124+
foo: {enum: ["y", "z"]},
125+
b: {type: "string"},
126+
},
127+
required: ["b"],
128+
},
129+
}
130+
const mainSchema2 = {
131+
type: "object",
132+
discriminator: {propertyName: "foo"},
133+
required: ["foo"],
134+
oneOf: [
135+
{
136+
$ref: "#/definitions/schema1",
137+
},
138+
{
139+
$ref: "#/definitions/schema2",
140+
},
141+
],
142+
}
143+
144+
const schema = [
145+
{definitions: definitions1, ...mainSchema1},
146+
{definitions: definitions2, ...mainSchema2},
147+
]
148+
149+
it("should validate data", () => {
150+
assertValid(schema, {foo: "x", a: "a"})
151+
assertValid(schema, {foo: "y", b: "b"})
152+
assertValid(schema, {foo: "z", b: "b"})
153+
assertInvalid(schema, {})
154+
assertInvalid(schema, {foo: 1})
155+
assertInvalid(schema, {foo: "bar"})
156+
assertInvalid(schema, {foo: "x", b: "b"})
157+
assertInvalid(schema, {foo: "y", a: "a"})
158+
assertInvalid(schema, {foo: "z", a: "a"})
159+
})
160+
})
161+
84162
describe("valid schemas", () => {
85163
it("should have oneOf", () => {
86164
invalidSchema(
@@ -97,7 +175,7 @@ describe("discriminator keyword", function () {
97175
required: ["foo"],
98176
oneOf: [{properties: {}}],
99177
},
100-
/discriminator: oneOf schemas must have "properties\/foo"/
178+
/discriminator: oneOf subschemas \(or referenced schemas\) must have "properties\/foo"/
101179
)
102180
})
103181

0 commit comments

Comments
 (0)
Please sign in to comment.