Skip to content

Commit

Permalink
Merge dabb373 into 45583fd
Browse files Browse the repository at this point in the history
  • Loading branch information
z6n9n committed May 1, 2023
2 parents 45583fd + dabb373 commit 82a2a3e
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 5 deletions.
38 changes: 33 additions & 5 deletions lib/vocabularies/discriminator/index.ts
@@ -1,7 +1,7 @@
import type {CodeKeywordDefinition, AnySchemaObject, KeywordErrorDefinition} from "../../types"
import type {AnySchemaObject, CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, getProperty, Name} from "../../compile/codegen"
import {DiscrError, DiscrErrorObj} from "../discriminator/types"
import {DiscrError, DiscrErrorObj} from "./types"
import {resolveRef, SchemaEnv} from "../../compile"
import {schemaHasRulesButRef} from "../../compile/util"

Expand Down Expand Up @@ -69,18 +69,46 @@ const def: CodeKeywordDefinition = {
sch = resolveRef.call(it.self, it.schemaEnv.root, it.baseId, sch?.$ref)
if (sch instanceof SchemaEnv) sch = sch.schema
}
const propSch = sch?.properties?.[tagName]
if (typeof propSch != "object") {
let propSch = sch?.properties?.[tagName]
let hasSubSchRequired = false
if (!propSch && sch?.allOf) {
const {hasRequired, propertyObject} = mapDiscriminatorFromAllOf(propSch, sch)
hasSubSchRequired = hasRequired
propSch = propertyObject
}
if (!propSch || typeof propSch != "object") {
throw new Error(
`discriminator: oneOf subschemas (or referenced schemas) must have "properties/${tagName}"`
)
}
tagRequired = tagRequired && (topRequired || hasRequired(sch))
tagRequired = tagRequired && (topRequired || hasRequired(sch) || hasSubSchRequired)
addMappings(propSch, i)
}
if (!tagRequired) throw new Error(`discriminator: "${tagName}" must be required`)
return oneOfMapping

function mapDiscriminatorFromAllOf(
propSch: any,
sch: any
): {hasRequired: boolean; propertyObject: any} {
let subSchObj: any = null
for (const subSch of sch.allOf) {
if (subSch?.properties) {
propSch = subSch.properties[tagName]
subSchObj = subSch
} else if (subSch?.$ref) {
subSchObj = resolveRef.call(it.self, it.schemaEnv.root, it.baseId, subSch.$ref)
if (subSchObj instanceof SchemaEnv) subSchObj = subSchObj.schema
propSch = subSchObj?.properties?.[tagName]
}
if (propSch) {
//found discriminator mapping in one of the allOf objects, stop searching
return {hasRequired: hasRequired(subSchObj), propertyObject: propSch}
}
}
return {hasRequired: false, propertyObject: null}
}

function hasRequired({required}: AnySchemaObject): boolean {
return Array.isArray(required) && required.includes(tagName)
}
Expand Down
78 changes: 78 additions & 0 deletions spec/discriminator.spec.ts
Expand Up @@ -159,6 +159,84 @@ describe("discriminator keyword", function () {
})
})

describe("validation with referenced schemas and allOf", () => {
const definitions = {
baseObject: {
properties: {
base: {type: "string"},
},
required: ["base"],
},
schema1: {
properties: {
foo: {const: "x"},
a: {type: "string"},
},
required: ["foo", "a"],
},
schema2: {
properties: {
foo: {enum: ["y", "w"]},
b: {type: "string"},
},
required: ["foo", "b"],
},
schema1Object: {
allOf: [
{
$ref: "#/definitions/baseObject",
},
{
$ref: "#/definitions/schema1",
},
],
},
}
const mainSchema = {
type: "object",
discriminator: {propertyName: "foo"},
oneOf: [
{
//referenced allOf
$ref: "#/definitions/schema1Object",
},
{
//inline allOf
allOf: [
{
$ref: "#/definitions/baseObject",
},
{
$ref: "#/definitions/schema2",
},
],
},
{
//plain object
properties: {
base: {type: "string"},
foo: {const: "z"},
c: {type: "string"},
},
required: ["base", "foo", "c"],
},
],
}

const schema = [{definitions: definitions, ...mainSchema}]

it("should validate data", () => {
assertValid(schema, {foo: "x", a: "a", base: "base"})
assertValid(schema, {foo: "y", b: "b", base: "base"})
assertValid(schema, {foo: "z", c: "c", base: "base"})
assertInvalid(schema, {})
assertInvalid(schema, {foo: 1})
assertInvalid(schema, {foo: "bar"})
assertInvalid(schema, {foo: "x", b: "b"})
assertInvalid(schema, {foo: "y", a: "a"})
})
})

describe("validation with deeply referenced schemas", () => {
const schema = [
{
Expand Down

0 comments on commit 82a2a3e

Please sign in to comment.