Skip to content

Commit

Permalink
feat: support for arrays of mergeable objects in the “with” argument …
Browse files Browse the repository at this point in the history
…of “$merge”
  • Loading branch information
Alex Zaslavsky committed Oct 10, 2017
1 parent d847ec3 commit e84920a
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 93 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ rules:
valid-jsdoc: [ 2, { requireReturn: false } ]
no-invalid-this: 2
no-unused-vars: [ 2, { args: none } ]
no-console: 2
no-console: 0
block-scoped-var: 2
complexity: [ 2, 9 ]
curly: [ 2, multi-or-nest, consistent ]
Expand Down
49 changes: 0 additions & 49 deletions keywords/add_keyword.js

This file was deleted.

28 changes: 28 additions & 0 deletions keywords/generateMetaSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

module.exports = function generateMetaSchema(patchSchema) {
return {
"type": "object",
"required": [ "source", "with" ],
"additionalProperties": false,
"properties": {
"source": {
"anyOf": [
{
"type": "object",
"required": [ "$ref" ],
"additionalProperties": false,
"properties": {
"$ref": {
"type": "string",
"format": "uri"
}
}
},
{ "$ref": "http://json-schema.org/draft-06/schema#" }
]
},
"with": patchSchema
}
};
};
11 changes: 11 additions & 0 deletions keywords/getSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

var url = require('url');
module.exports = function getSchema(ajv, it, $ref) {
var id = it.baseId && it.baseId != '#'
? url.resolve(it.baseId, $ref)
: $ref;
var validate = ajv.getSchema(id);
if (validate) return validate.schema;
throw new ajv.constructor.MissingRefError(it.baseId, $ref);
};
33 changes: 29 additions & 4 deletions keywords/merge.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
'use strict';

var addKeyword = require('./add_keyword');
var jsonMergePatch = require('json-merge-patch');
var generateMetaSchema = require('./generateMetaSchema');
var getSchema = require('./getSchema');
var jsonPatch = require('json-merge-patch');

module.exports = function(ajv) {
addKeyword(ajv, '$merge', jsonMergePatch, { "type": "object" });
module.exports = function merge(ajv) {
ajv.addKeyword('$merge', {
macro: function (schema, parentSchema, it) {
var source = schema.source;
var patches = schema.with instanceof Array ? schema.with : [schema.with];
if (source.$ref) source = JSON.parse(JSON.stringify(getSchema(ajv, it, source.$ref)));
patches.forEach(function(patch) {
if (patch.$ref) patch = getSchema(ajv, it, patch.$ref);
jsonPatch.apply(source, patch, true);
});
return source;
},
metaSchema: generateMetaSchema({
"oneOf": [
{
"type": "object"
},
{
"type": "array",
"items": {
"type": "object"
}
}
]
})
});
};
63 changes: 37 additions & 26 deletions keywords/patch.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,45 @@
'use strict';

var addKeyword = require('./add_keyword');
var generateMetaSchema = require('./generateMetaSchema');
var getSchema = require('./getSchema');
var jsonPatch = require('fast-json-patch/src/json-patch');

module.exports = function(ajv) {
addKeyword(ajv, '$patch', jsonPatch, {
"type": "array",
"items": {
"type": "object",
"required": [ "op", "path" ],
"properties": {
"op": { "type": "string" },
"path": { "type": "string", "format": "json-pointer" }
},
"anyOf": [
{
"properties": { "op": { "enum": [ "add", "replace", "test" ] } },
"required": [ "value" ]
module.exports = function (ajv) {
ajv.addKeyword('$patch', {
macro: function (schema, parentSchema, it) {
var source = schema.source;
var patch = schema.with;
if (source.$ref) source = JSON.parse(JSON.stringify(getSchema(ajv, it, source.$ref)));
if (patch.$ref) patch = getSchema(ajv, it, patch.$ref);
jsonPatch.applyPatch(source, patch, true);
return source;
},
metaSchema: generateMetaSchema({
"type": "array",
"items": {
"type": "object",
"required": [ "op", "path" ],
"properties": {
"op": { "type": "string" },
"path": { "type": "string", "format": "json-pointer" }
},
{
"properties": { "op": { "enum": [ "remove" ] } }
},
{
"properties": {
"op": { "enum": [ "move", "copy" ] },
"from": { "type": "string", "format": "json-pointer" }
"anyOf": [
{
"properties": { "op": { "enum": [ "add", "replace", "test" ] } },
"required": [ "value" ]
},
{
"properties": { "op": { "enum": [ "remove" ] } }
},
"required": [ "from" ]
}
]
}
{
"properties": {
"op": { "enum": [ "move", "copy" ] },
"from": { "type": "string", "format": "json-pointer" }
},
"required": [ "from" ]
}
]
}
})
});
};
12 changes: 10 additions & 2 deletions spec/async.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ describe('async schema loading', function() {
"$merge": {
"source": { "$ref": "obj.json#" },
"with": {
"properties": { "q": { "type": "number" } }
"properties": {
"q": {
"type": "number"
},
"r": {
"type": "boolean"
}
}
}
}
};
Expand All @@ -35,7 +42,8 @@ describe('async schema loading', function() {
"$patch": {
"source": { "$ref": "obj.json#" },
"with": [
{ "op": "add", "path": "/properties/q", "value": { "type": "number" } }
{ "op": "add", "path": "/properties/q", "value": { "type": "number" } },
{ "op": "add", "path": "/properties/r", "value": { "type": "boolean" } }
]
}
};
Expand Down
9 changes: 8 additions & 1 deletion spec/errors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ describe('errors', function() {
"$merge": {
"source": { "$ref": "obj.json#" },
"with": {
"properties": { "q": { "type": "number" } }
"properties": {
"q": {
"type": "number"
},
"r": {
"type": "boolean"
}
}
}
}
};
Expand Down
91 changes: 86 additions & 5 deletions spec/merge.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ describe('keyword $merge', function() {
"additionalProperties": false
},
"with": {
"properties": { "q": { "type": "number" } }
"properties": {
"q": {
"type": "number"
},
"r": {
"type": "boolean"
}
}
}
}
};
Expand All @@ -53,7 +60,14 @@ describe('keyword $merge', function() {
"$merge": {
"source": { "$ref": "obj.json#" },
"with": {
"properties": { "q": { "type": "number" } }
"properties": {
"q": {
"type": "number"
},
"r": {
"type": "boolean"
}
}
}
}
};
Expand All @@ -79,7 +93,14 @@ describe('keyword $merge', function() {
"$merge": {
"source": { "$ref": "#/definitions/source" },
"with": {
"properties": { "q": { "type": "number" } }
"properties": {
"q": {
"type": "number"
},
"r": {
"type": "boolean"
}
}
}
}
};
Expand All @@ -101,7 +122,14 @@ describe('keyword $merge', function() {

var patchSchema = {
"type": "object",
"properties": { "q": { "type": "number" } },
"properties": {
"q": {
"type": "number"
},
"r": {
"type": "boolean"
}
},
"additionalProperties": false
};

Expand Down Expand Up @@ -137,7 +165,14 @@ describe('keyword $merge', function() {
"definitions": {
"patch":{
"type": "object",
"properties": { "q": { "type": "number" } },
"properties": {
"q": {
"type": "number"
},
"r": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
Expand All @@ -151,4 +186,50 @@ describe('keyword $merge', function() {
test(validate, '$merge');
}
});

it('should extend schema with an array of merging schemas', function() {
ajvInstances.forEach(testMerge);

function testMerge(ajv) {
var sourceSchema = {
"type": "object",
"properties": { "p": { "type": "string" } },
"additionalProperties": false
};

ajv.addSchema(sourceSchema, "obj1.json#");

var schema = {
"id": "obj2.json#",
"definitions": {
"patch":{
"type": "object",
"properties": {
"q": {
"type": "number"
}
},
"additionalProperties": false
}
},
"$merge": {
"source": { "$ref": "obj1.json#" },
"with": [
{ "$ref": "#/definitions/patch" },
{
"type": "object",
"properties": {
"r": {
"type": "boolean"
}
}
}
]
}
};

var validate = ajv.compile(schema);
test(validate, '$merge');
}
});
});
Loading

0 comments on commit e84920a

Please sign in to comment.