Skip to content

Commit

Permalink
feat: remove schemaId option (and support for draft-04 "id" in schemas)
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Jul 22, 2020
1 parent 197a9e8 commit b1bb27e
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 111 deletions.
18 changes: 3 additions & 15 deletions README.md
Expand Up @@ -2,7 +2,7 @@

# Ajv: Another JSON Schema Validator

The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07.
The fastest JSON Schema validator for Node.js and browser. Supports draft-06/07 (draft-04 is supported in v6).

[![Build Status](https://travis-ci.org/ajv-validator/ajv.svg?branch=master)](https://travis-ci.org/ajv-validator/ajv)
[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv)
Expand Down Expand Up @@ -63,14 +63,7 @@ Thank you
ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json"))
```

To use Ajv with draft-04 schemas in addition to explicitly adding meta-schema you also need to use option schemaId:

```javascript
var ajv = new Ajv({schemaId: "id"})
// If you want to use both draft-04 and draft-06/07 schemas:
// var ajv = new Ajv({schemaId: 'auto'});
ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-04.json"))
```
**Please note**: use Ajv v6 if you need draft-04 support - v7 does NOT support it.

## Contents

Expand Down Expand Up @@ -128,7 +121,7 @@ Performance of different validators by [json-schema-benchmark](https://github.co

## Features

- Ajv implements full JSON Schema [draft-06/07](http://json-schema.org/) and draft-04 standards:
- Ajv implements full JSON Schema [draft-06/07](http://json-schema.org/) standards (draft-04 is supported in v6):
- all validation keywords (see [JSON Schema validation keywords](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md))
- full support of remote refs (remote schemas have to be added with `addSchema` or compiled to be available)
- support of circular references between schemas
Expand Down Expand Up @@ -1122,7 +1115,6 @@ Defaults:
schemas: {},
logger: undefined,
// referenced schema options:
schemaId: '$id',
missingRefs: true,
extendRefs: 'ignore', // recommended 'fail'
loadSchema: undefined, // function(uri: string): Promise {}
Expand Down Expand Up @@ -1183,10 +1175,6 @@ Defaults:

##### Referenced schema options

- _schemaId_: this option defines which keywords are used as schema URI. Option value:
- `"$id"` (default) - only use `$id` keyword as schema URI (as specified in JSON Schema draft-06/07), ignore `id` keyword (if it is present a warning will be logged).
- `"id"` - only use `id` keyword as schema URI (as specified in JSON Schema draft-04), ignore `$id` keyword (if it is present a warning will be logged).
- `"auto"` - use both `$id` and `id` keywords as schema URI. If both are present (in the same schema object) and different the exception will be thrown during schema compilation.
- _missingRefs_: handling of missing referenced schemas. Option values:
- `true` (default) - if the reference cannot be resolved during compilation the exception is thrown. The thrown error has properties `missingRef` (with hash fragment) and `missingSchema` (without it). Both properties are resolved relative to the current base id (usually schema id, unless it was substituted).
- `"ignore"` - to log error during compilation and always pass validation.
Expand Down
1 change: 0 additions & 1 deletion lib/ajv.d.ts
Expand Up @@ -184,7 +184,6 @@ declare namespace ajv {
keywords?: object
unknownFormats?: true | string[] | "ignore"
schemas?: Array<object> | object
schemaId?: "$id" | "id" | "auto"
missingRefs?: true | "ignore" | "fail"
extendRefs?: true | "ignore" | "fail"
loadSchema?: (
Expand Down
40 changes: 6 additions & 34 deletions lib/ajv.js
Expand Up @@ -67,7 +67,8 @@ function Ajv(opts) {
this._loadingSchemas = {}
this._compilations = []
this.RULES = rules()
this._getId = chooseGetId(opts)
if (opts.schemaId !== undefined && opts.schemaId !== "$id")
throw new Error("option schemaId is not supported from v7")

opts.loopRequired = opts.loopRequired || Infinity
if (opts.errorDataPath == "property") opts._errorDataPathProperty = true
Expand Down Expand Up @@ -134,7 +135,7 @@ function addSchema(schema, key, _skipValidation, _meta) {
this.addSchema(schema[i], undefined, _skipValidation, _meta)
return this
}
var id = this._getId(schema)
var id = schema.$id
if (id !== undefined && typeof id != "string")
throw new Error("schema id must be string")
key = resolve.normalizeId(key || id)
Expand Down Expand Up @@ -187,7 +188,7 @@ function defaultMeta(self) {
var meta = self._opts.meta
self._opts.defaultMeta =
typeof meta == "object"
? self._getId(meta) || meta
? meta.$id || meta
: self.getSchema(META_SCHEMA_ID)
? META_SCHEMA_ID
: undefined
Expand Down Expand Up @@ -267,7 +268,7 @@ function removeSchema(schemaKeyRef) {
var serialize = this._opts.serialize
var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef
this._cache.del(cacheKey)
var id = this._getId(schemaKeyRef)
var id = schemaKeyRef.$id
if (id) {
id = resolve.normalizeId(id)
delete this._schemas[id]
Expand Down Expand Up @@ -298,7 +299,7 @@ function _addSchema(schema, skipValidation, meta, shouldAddSchema) {

shouldAddSchema = shouldAddSchema || this._opts.addUsedSchema !== false

var id = resolve.normalizeId(this._getId(schema))
var id = resolve.normalizeId(schema.$id)
if (id && shouldAddSchema) checkUnique(this, id)

var willValidate = this._opts.validateSchema !== false && !skipValidation
Expand Down Expand Up @@ -372,35 +373,6 @@ function _compile(schemaObj, root) {
}
}

function chooseGetId(opts) {
switch (opts.schemaId) {
case "auto":
return _get$IdOrId
case "id":
return _getId
default:
return _get$Id
}
}

/* @this Ajv */
function _getId(schema) {
if (schema.$id) this.logger.warn("schema $id ignored", schema.$id)
return schema.id
}

/* @this Ajv */
function _get$Id(schema) {
if (schema.id) this.logger.warn("schema id ignored", schema.id)
return schema.$id
}

function _get$IdOrId(schema) {
if (schema.$id && schema.id && schema.$id != schema.id)
throw new Error("schema $id is different from id")
return schema.$id || schema.id
}

/**
* Convert array of error message objects to string
* @this Ajv
Expand Down
12 changes: 6 additions & 6 deletions lib/compile/resolve.js
Expand Up @@ -70,7 +70,7 @@ function resolveSchema(root, ref) {
/* jshint validthis: true */
var p = URI.parse(ref),
refPath = _getFullPath(p),
baseId = getFullPath(this._getId(root.schema))
baseId = getFullPath(root.schema.$id)
if (Object.keys(root.schema).length === 0 || refPath !== baseId) {
var id = normalizeId(refPath)
var refVal = this._refs[id]
Expand All @@ -91,7 +91,7 @@ function resolveSchema(root, ref) {
}
}
if (!root.schema) return
baseId = getFullPath(this._getId(root.schema))
baseId = getFullPath(root.schema.$id)
}
return getJsonPointer.call(this, p, baseId, root.schema, root)
}
Expand All @@ -104,7 +104,7 @@ function resolveRecursive(root, ref, parsedRef) {
var schema = res.schema
var baseId = res.baseId
root = res.root
var id = this._getId(schema)
var id = schema.$id
if (id) baseId = resolveUrl(baseId, id)
return getJsonPointer.call(this, parsedRef, baseId, schema, root)
}
Expand Down Expand Up @@ -132,7 +132,7 @@ function getJsonPointer(parsedRef, baseId, schema, root) {
if (schema === undefined) break
var id
if (!PREVENT_SCOPE_CHANGE[part]) {
id = this._getId(schema)
id = schema.$id
if (id) baseId = resolveUrl(baseId, id)
if (schema.$ref) {
var $ref = resolveUrl(baseId, schema.$ref)
Expand Down Expand Up @@ -236,7 +236,7 @@ function resolveUrl(baseId, id) {

/* @this Ajv */
function resolveIds(schema) {
var schemaId = normalizeId(this._getId(schema))
var schemaId = normalizeId(schema.$id)
var baseIds = {"": schemaId}
var fullPaths = {"": getFullPath(schemaId, false)}
var localRefs = {}
Expand All @@ -252,7 +252,7 @@ function resolveIds(schema) {
keyIndex
) {
if (jsonPtr === "") return
var id = self._getId(sch)
var id = sch.$id
var baseId = baseIds[parentJsonPtr]
var fullPath = fullPaths[parentJsonPtr] + "/" + parentKeyword
if (keyIndex !== undefined) {
Expand Down
4 changes: 2 additions & 2 deletions lib/dot/validate.jst
Expand Up @@ -17,7 +17,7 @@
{{
var $async = it.schema.$async === true
, $refKeywords = it.util.schemaHasRulesExcept(it.schema, it.RULES.all, '$ref')
, $id = it.self._getId(it.schema);
, $id = it.schema.$id;
}}

{{
Expand Down Expand Up @@ -77,7 +77,7 @@
, $lvl = it.level = 0
, $dataLvl = it.dataLevel = 0
, $data = 'data';
it.rootId = it.resolve.fullPath(it.self._getId(it.root.schema));
it.rootId = it.resolve.fullPath(it.root.schema.$id);
it.baseId = it.baseId || it.rootId;
delete it.isTop;

Expand Down
2 changes: 1 addition & 1 deletion spec/issues/521_wrong_warning_id_property.spec.js
Expand Up @@ -5,7 +5,7 @@ require("../chai").should()

describe('issue #521, incorrect warning with "id" property', function () {
it("should not log warning", function () {
var ajv = new Ajv({schemaId: "$id"})
var ajv = new Ajv()
var consoleWarn = console.warn
console.warn = function () {
throw new Error("should not log warning")
Expand Down
67 changes: 15 additions & 52 deletions spec/options/schemaId.spec.js
Expand Up @@ -3,67 +3,30 @@
var Ajv = require("../ajv")
var should = require("../chai").should()

describe("schemaId option", function () {
describe('= "$id" (default)', function () {
it("should use $id and ignore id", function () {
test(new Ajv())
test(new Ajv({schemaId: "$id"}))

function test(ajv) {
ajv.addSchema({$id: "mySchema1", type: "string"})
var validate = ajv.getSchema("mySchema1")
validate("foo").should.equal(true)
validate(1).should.equal(false)

validate = ajv.compile({id: "mySchema2", type: "string"})
should.not.exist(ajv.getSchema("mySchema2"))
}
describe("removed schemaId option", function () {
it('should throw error if schemaId option is used and it is not equal to "$id"', function () {
new Ajv()
new Ajv({schemaId: "$id"})
should.throw(function () {
new Ajv({schemaId: "id"})
})
})

describe('= "id"', function () {
it("should use id and ignore $id", function () {
var ajv = new Ajv({schemaId: "id", meta: false})

ajv.addSchema({id: "mySchema1", type: "string"})
var validate = ajv.getSchema("mySchema1")
validate("foo").should.equal(true)
validate(1).should.equal(false)

validate = ajv.compile({$id: "mySchema2", type: "string"})
should.not.exist(ajv.getSchema("mySchema2"))
should.throw(function () {
new Ajv({schemaId: "auto"})
})
})

describe('= "auto"', function () {
it("should use both id and $id", function () {
var ajv = new Ajv({schemaId: "auto"})
it("should use $id and ignore id", function () {
test(new Ajv())
test(new Ajv({schemaId: "$id"}))

function test(ajv) {
ajv.addSchema({$id: "mySchema1", type: "string"})
var validate = ajv.getSchema("mySchema1")
validate("foo").should.equal(true)
validate(1).should.equal(false)

ajv.addSchema({id: "mySchema2", type: "string"})
validate = ajv.getSchema("mySchema2")
validate("foo").should.equal(true)
validate(1).should.equal(false)
})

it("should throw if both id and $id are available and different", function () {
var ajv = new Ajv({schemaId: "auto"})

ajv.compile({
id: "mySchema",
$id: "mySchema",
})

should.throw(function () {
ajv.compile({
id: "mySchema1",
$id: "mySchema2",
})
})
})
validate = ajv.compile({id: "mySchema2", type: "string"})
should.not.exist(ajv.getSchema("mySchema2"))
}
})
})

0 comments on commit b1bb27e

Please sign in to comment.