Skip to content

Commit

Permalink
refactor: $ref keyword
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Sep 12, 2020
1 parent 2d7fd9c commit 58bc64d
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 59 deletions.
4 changes: 3 additions & 1 deletion lib/ajv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,11 @@ export default class Ajv {
delete this._schemas[id]
delete this._refs[id]
}
return this
}
default:
throw new Error("ajv.removeSchema: invalid parameter")
}
return this
}

// add "vocabulary" - a collection of keywords
Expand Down
3 changes: 2 additions & 1 deletion lib/compile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export function compileSchema(this: Ajv, env: SchemaEnv): ValidateFunction {
}
}

function resolveRef(baseId: string, ref: string): Schema | ValidateFunction | void {
function resolveRef(baseId: string, ref: string): Schema | ValidateFunction | undefined {
ref = resolveUrl(baseId, ref)
// TODO root.refs check should be unnecessary, it is only needed because in some cases root is passed without refs (see type casts to SchemaEnv)
const schOrFunc = env.refs[ref] || env.root.refs[ref]
Expand All @@ -173,6 +173,7 @@ export function compileSchema(this: Ajv, env: SchemaEnv): ValidateFunction {
}

if (_sch !== undefined) return (env.refs[ref] = inlineOrCompile(_sch))
return
}

function inlineOrCompile(sch: SchemaEnv): Schema | ValidateFunction {
Expand Down
2 changes: 1 addition & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export interface SchemaCxt {
compositeRule?: boolean
createErrors?: boolean
opts: InstanceOptions
resolveRef: (baseId: string, ref: string) => Schema | ValidateFunction | void
resolveRef: (baseId: string, ref: string) => Schema | ValidateFunction | undefined
self: Ajv
}

Expand Down
91 changes: 36 additions & 55 deletions lib/vocabularies/core/ref.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,54 @@
import {CodeKeywordDefinition, Schema} from "../../types"
import {CodeKeywordDefinition, Schema, ValidateFunction} from "../../types"
import KeywordCxt from "../../compile/context"
import {MissingRefError} from "../../compile/error_classes"
import {applySubschema} from "../../compile/subschema"
import {callValidateCode} from "../util"
import {_, str, nil, Code, Name} from "../../compile/codegen"
import N from "../../compile/names"

// TODO remove these interfaces
type ResolvedRef = InlineResolvedRef | FuncResolvedRef

interface InlineResolvedRef {
code: Code
schema: Schema
inline: true
}

interface FuncResolvedRef {
code: Code
$async?: boolean
inline?: false
}

const def: CodeKeywordDefinition = {
keyword: "$ref",
schemaType: "string",
code(cxt: KeywordCxt) {
const {gen, schema, it} = cxt
const {resolveRef, allErrors, baseId, isRoot, root, opts, validateName, self} = it
const ref = getRef()
const passCxt = opts.passContext ? N.this : nil
if (ref === undefined) missingRef()
else if (ref.inline) applyRefSchema(ref)
else if (ref.$async || it.async) validateAsyncRef(ref.code)
else validateRef(ref.code)
if (schema === "#" || schema === "#/") return callRootRef()
const schOrFunc = resolveRef(baseId, schema)
if (schOrFunc === undefined) return missingRef()
if (typeof schOrFunc == "function") return callCompiledRef(schOrFunc)
return inlineRefSchema(schOrFunc)

function getRef(): ResolvedRef | undefined {
if (schema === "#" || schema === "#/") {
if (isRoot) return {code: validateName, $async: it.async}
const rootName = gen.scopeValue("root", {ref: root.localRoot})
return {
code: _`${rootName}.validate`,
$async: typeof root.schema == "object" && root.schema.$async === true,
}
}
function callRootRef(): void {
if (isRoot) return callRef(validateName, it.async)
const rootName = gen.scopeValue("root", {ref: root.localRoot})
return callRef(_`${rootName}.validate`, root.$async)
}

const schOrFunc = resolveRef(baseId, schema)
if (typeof schOrFunc == "function") {
const code = gen.scopeValue("validate", {ref: schOrFunc})
return {code, $async: schOrFunc.$async}
} else if (typeof schOrFunc == "boolean" || typeof schOrFunc == "object") {
const code = gen.scopeValue("schema", {ref: schOrFunc})
return {code, schema: schOrFunc, inline: true}
}
return undefined
function callCompiledRef(func: ValidateFunction): void {
const v = gen.scopeValue("validate", {ref: func})
return callRef(v, func.$async)
}

function callRef(v: Code, $async?: boolean): void {
if ($async || it.async) validateAsyncRef(v)
else validateSyncRef(v)
}

function inlineRefSchema(sch: Schema): void {
const schName = gen.scopeValue("schema", {ref: sch})
const valid = gen.name("valid")
applySubschema(
it,
{
schema: sch,
schemaPath: nil,
topSchemaRef: schName,
errSchemaPath: schema,
},
valid
)
cxt.ok(valid)
}

function missingRef(): void {
Expand All @@ -70,21 +66,6 @@ const def: CodeKeywordDefinition = {
}
}

function applyRefSchema(inlineRef: InlineResolvedRef): void {
const valid = gen.name("valid")
applySubschema(
it,
{
schema: inlineRef.schema,
schemaPath: nil,
topSchemaRef: inlineRef.code,
errSchemaPath: schema,
},
valid
)
cxt.ok(valid)
}

function validateAsyncRef(v: Code): void {
if (!it.async) throw new Error("async schema referenced by sync schema")
const valid = gen.let("valid")
Expand All @@ -102,7 +83,7 @@ const def: CodeKeywordDefinition = {
cxt.ok(valid)
}

function validateRef(v: Code): void {
function validateSyncRef(v: Code): void {
cxt.pass(callValidateCode(cxt, v, passCxt), () => addErrorsFrom(v))
}

Expand Down
2 changes: 1 addition & 1 deletion spec/resolve.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ describe("resolve", () => {
}

function testInlined(validate: ValidateFunction, expectedInlined) {
const inlined: any = !/scope\.validate/.test(validate.source?.code || "")
const inlined: any = !validate.source?.code.includes("scope.validate")
inlined.should.equal(expectedInlined)
}
})
Expand Down

0 comments on commit 58bc64d

Please sign in to comment.