Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set strictSchema by default to make silent errors less likely. #674

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ function build (schema, options) {
}
}

// we've added the schemas in 'validatorSchemasIds' to the validator.ajv
// (these schemas will actually be used during serialisation of if-else /
// allOf / oneOf constructs that require matching against a schema before
// serialisation), however OTHER referenced schemas (those not actually
// required during serialisation) have not been added to validator.ajv, so
// we cannot use the validator's AJV instance to actually validate the
// whole schema. Instead, create a new AJV instance with the same options,
// add all the referenced schemas, and then validate:
const ajv = Validator.createAJVInstanceWithOptions(options.ajv)

if (options.schema) {
for (const key in options.schema) {
const schema = options.schema[key]
ajv.addSchema(schema, getSchemaId(schema, key))
}
}
// compile the root schema with AJV (even though we don't use the result):
// this ensures early and consistent detection of errors in the schema
// based on the AJV options (for example options.ajv.strictSchema):
ajv.compile(schema)

if (options.debugMode) {
options.mode = 'debug'
}
Expand Down
43 changes: 24 additions & 19 deletions lib/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,7 @@ const clone = require('rfdc')({ proto: true })

class Validator {
constructor (ajvOptions) {
this.ajv = new Ajv({
...ajvOptions,
strictSchema: false,
validateSchema: false,
allowUnionTypes: true,
uriResolver: fastUri
})

ajvFormats(this.ajv)

this.ajv.addKeyword({
keyword: 'fjs_type',
type: 'object',
errors: false,
validate: (type, date) => {
return date instanceof Date
}
})

this.ajv = Validator.createAJVInstanceWithOptions(ajvOptions)
this._ajvSchemas = {}
this._ajvOptions = ajvOptions || {}
}
Expand Down Expand Up @@ -82,6 +64,29 @@ class Validator {
}
}

static createAJVInstanceWithOptions (ajvOptions) {
const ajv = new Ajv({
strictSchema: false,
validateSchema: false,
allowUnionTypes: true,
uriResolver: fastUri,
...ajvOptions
})

ajvFormats(ajv)

ajv.addKeyword({
keyword: 'fjs_type',
type: 'object',
errors: false,
validate: (type, date) => {
return date instanceof Date
}
})

return ajv
}

static restoreFromState (state) {
const validator = new Validator(state.ajvOptions)
for (const [id, ajvSchema] of Object.entries(state.ajvSchemas)) {
Expand Down
44 changes: 44 additions & 0 deletions test/strictSchema.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict'

const test = require('tap').test
const build = require('..')

test('throw on unknown keyword with strictSchema: true', (t) => {
t.plan(1)

const schema = {
type: 'object',
properties: {
a: { type: 'string' }
},
anUnknownKeyword: true
}

t.throws(() => {
build(schema, { ajv: { strictSchema: true } })
}, new Error('strict mode: unknown keyword: "anUnknownKeyword"'))
})

test('throw on unknown keyword in a referenced schema with strictSchema: true', (t) => {
t.plan(1)

const referencedSchemas = {
foo: {
properties: {
b: { type: 'string' }
},
anUnknownKeyword: true
}
}

const schema = {
type: 'object',
properties: {
a: { type: 'object', $ref: 'foo' }
}
}

t.throws(() => {
build(schema, { schema: referencedSchemas, ajv: { strictSchema: true } })
}, new Error('strict mode: unknown keyword: "anUnknownKeyword"'))
})
Loading