ajv-validator / ajv-merge-patch Goto Github PK
View Code? Open in Web Editor NEW$merge and $patch keywords for Ajv JSON-Schema validator to extend schemas
Home Page: https://ajv.js.org
License: MIT License
$merge and $patch keywords for Ajv JSON-Schema validator to extend schemas
Home Page: https://ajv.js.org
License: MIT License
I saw in the tests how to add an attribute to the required keyword { "op": "add", "path": "/required/-", "value": "q" }
but I want to ask how can i remove a keyword from being required I tried
{ "op": "remove", "path": "/required/-q"} but it did not work i seem to get it wrong so if anyone can help I would be grateful.
Thank you
To reproduce:
git clone [email protected]:ajv-validator/ajv-merge-patch.git
cd ajv-merge-patch
npm i
npm test
Expected: all tests pass
Actual:
1) async schema loading
$merge
should load missing schemas:
TypeError: resolver.resolve is not a function
at resolveUrl (node_modules/ajv/dist/compile/resolve.js:90:21)
...
2) async schema loading
$patch
should load missing schemas:
TypeError: resolver.resolve is not a function
at resolveUrl (node_modules/ajv/dist/compile/resolve.js:90:21)
This is due to a change introduced in ajv
v8.10.0 which is pulled by the devDependency "ajv": "^8.2.0"
. Specifically,
This very short PR fixes this issue in the same technique as that commit in ajv
.
Or we not suppose to use merge / patch
for JSON schemas?
Hi,
If I try to merge schemas with "required" keyword, as result incorrect merge of "required" values.
{
"$merge": {
"source": {
"required": ["foo", "bar"],
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
}
},
"with": {
"required": ["baz"],
"properties": {
"baz": { "type": "number" }
}
}
}
}
{
"$merge": {
"source": {
"required": [
"baz"
],
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "string"
},
"baz": {
"type": "number"
}
}
},
"with": {
"required": [
"baz"
],
"properties": {
"baz": {
"type": "number"
}
}
}
}
}
const validate = ajv.compile(schema)
validate({"foo": "asd", "bar": "asd", "baz": 123})
true
validate({"foo": "asd", "baz": 123})
true
validate({"baz": 123})
true
validate({"foo": "asd"})
false
Can i get the raw json schema from a schema defined using merge or patch?
Hello,
I'm trying to build a mechanism to versioning default values for my app over ajv backed separated JSON files.
I couldnt generate default values from a merged schema. Please elaborate on what i'm missing or doing wrong.
You can find the sandbox here
My concerns are:
A simple example goes like this:
/schemas/v0/App.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://some.domain.com/schemas/v0/App.json#",
"title": "App Schema v0",
"description": "App Properties",
"type": "object",
"additionalProperties": false,
"definitions": {
"yesNo": {
"$ref": "https://some.domain.com/schemas/commonDefinitions.json#/definitions/yesNo"
},
"description": {
"$ref": "https://some.domain.com/schemas/commonDefinitions.json#/definitions/description"
},
"color": {
"$ref": "https://some.domain.com/schemas/commonDefinitions.json#/definitions/color"
},
"URL": {
"$ref": "https://some.domain.com/schemas/commonDefinitions.json#/definitions/color"
}
},
"properties": {
"appVersion": {
"type": "string",
"const": "0",
"default": "0"
},
"appBgColor": {
"$ref": "#/definitions/color",
"description": "App bg color",
"default": "#ffffff"
},
"name": {
"type": "string",
"default": "New App"
},
"description": {
"$ref": "#/definitions/description",
"default": "my app"
},
"logoURL": {
"$ref": "#/definitions/URL",
"description": "Logo asset URL",
"default": "https://source.unsplash.com/random/50x50"
}
},
"required": ["appVersion"]
}
/schemas/v1/App.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://some.domain.com/schemas/v1/App.json#",
"$merge": {
"source": {
"$ref": "https://some.domain.com/schemas/v0/App.json#"
},
"with": {
"properties": {
"appVersion": {
"const": "1",
"default": "1"
},
"showItemIcons": {
"$ref": "#/definitions/yesNo",
"default": "Nope"
}
}
}
}
}
/schemas/commonDefinitions.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://some.domain.com/schemas/commonDefinitions.json#",
"title": "App Schema Common Definitions",
"description": "Common Definitions",
"type": "object",
"additionalProperties": false,
"definitions": {
"yesNo": {
"type": "string",
"enum": ["Yes", "No"]
},
"alignment": {
"type": "string",
"enum": ["left", "center", "right"]
},
"description": {
"type": "string",
"maxLength": 5
},
"color": {
"type": "string"
},
"URL": {
"type": "string"
},
"empty": {
"type": "string",
"maxLength": 0
}
}
}
I couldn't make ajv to produce correct data with defaults on v1/App.json:
{}
However it's ok for v0/App.json:
{"appVersion":"0","appBgColor":"#ffffff","name":"New App","description":"my app","logoURL":"https://source.unsplash.com/random/50x50"}
Additional problems:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://www.jotform.com/portal/schemas/v1/App.json#",
"$merge": {
"source": {
"$ref": "../v0/App.json"
},
"with": {
......
}
}
MissingRefError
s if i dont include it to v0. What is the correct way to extend definitions
and required
?const Ajv = require('ajv');
const ajv = new Ajv({
allErrors: true,
format: 'full',
useDefaults: true,
coerceTypes: false,
errorDataPath: 'property',
sourceCode: false,
});
require('ajv-merge-patch')(ajv);
const base = {
type: 'object',
properties: {
operation: {
type: 'string',
enum: ['create', 'update', 'delete'],
},
id: {
type: 'string',
},
},
};
const schema = {
$merge: {
source: base,
with: {
type: 'object',
properties: {
test: {
type: 'integer',
default: 0,
},
},
}
}
};
const params = {
operation: 'delete'
};
const validate = ajv.compile(schema);
const isValidParams = validate(params);
console.log(params);
Actual :
{ operation: 'delete' }
Expected :
{ operation: 'delete' , test: 0}
I'm getting this warning withe the latest version.
I'm trying to use the library in the browser compiled by babel and webpack. When I run this code:
import Ajv from 'ajv';
import ajvMergePatch from 'ajv-merge-patch';
const ajvInstance = new Ajv({});
ajvMergePatch(ajvInstance);
I get the following error:
bootstrap 436b72c1f8f84619e6c0:802 Error: can't resolve reference http://json-schema.org/draft-06/schema# from id #
at Object.generate_ref [as code] (http://localhost/1.js:8952:22)
at Object.generate_validate [as validate] (http://localhost/1.js:9645:37)
at Object.generate_anyOf [as code] (http://localhost/1.js:7185:27)
at Object.generate_validate [as validate] (http://localhost/1.js:9718:37)
at Object.generate_properties [as code] (http://localhost/1.js:8636:26)
at generate_validate (http://localhost/1.js:9718:37)
at localCompile (http://localhost/1.js:5573:22)
at Ajv.compile (http://localhost/1.js:5541:13)
at _compile (http://localhost/1.js:4840:29)
at Ajv.compile (http://localhost/1.js:4626:34)
at Ajv.addKeyword (http://localhost/1.js:9930:40)
at webpackJsonp../lib/node_modules/ajv-merge-patch/keywords/add_keyword.js.module.exports (http://localhost/1.js:2230:7)
at webpackJsonp../lib/node_modules/ajv-merge-patch/keywords/merge.js.module.exports (http://localhost/1.js:2288:3)
at addKeywords (http://localhost/1.js:2214:3)
additionalProperties
is implemented, but allRequired
isn't. This would be helpful, as when the source spec for the object has allRequired
, the merged one allows missing keys.
Using ajv-merge-patch
I'm noticing a console warning.
I'm using:
[email protected]
[email protected]
I'm guessing that it's because ajv-merge-patch
currently uses a function signature for addKeyword where a keyword string is the first argument, but this is now a deprecated signature, according to ajv
's core module and this logging.
Following will crash with not very descriptive error Error: can't resolve reference http://json-schema.org/draft-07/schema# from id #
const AJV = require('ajv');
const ajv = new AJV({ meta: false });
require('ajv-merge-patch')(ajv);
If it works as intended it'll be good to document that AJV cannot be initialized with meta: false
, if we want to take advantage of this plugin
Hi,
Trying to use ajv-merge-patch in a scenario where my definitions are split up over multiple JSON files - lets say I have the following 4 files;
/system.json (all objects have these properties - however not always all of them required)
{
"id": "/system.json",
"type": "object",
"properties": {
"id": {
"type": "number"
}
}
}
/object/user.json (a specific object type - includes all from system.json)
{
"id": "/object/user.json",
"$merge": {
"source": {
"type": "object",
"properties": {
"firstname": {
"type": ["string", "null"]
}
}
},
"with": {
"$ref": "/system.json#"
}
}
}
/general/crud/create/request.json (template for incoming record create requests)
{
"id": "/general/crud/create/request.json",
"type": "object",
"properties": {
"userId": {
"type": "number"
},
"values": {
"type": "object"
}
},
"required": ["userId", "values"],
"additionalProperties": false
}
user/action/create/request.json (specific user create template - extends /general/crud/create/request.json above - making the values node specific with the user model above - adds one additional property)
{
"id": "/user/action/create/request.json",
"type": "object",
"$patch": {
"source": {
"$ref": "/general/crud/create/request.json#"
},
"with": [{
"op": "add",
"path": "/properties/values",
"value": {
"$merge": {
"source": {
"$ref": "/object/user.json#"
},
"with": {
"properties": {
"unencrypted_password": {
"type": "string"
}
},
"required": ["unencrypted_password"]
}
}
}
}]
}
}
Now what I would like to do is put an "additionalProperties: false" on the user model as other use cases besides create should not specify any additional properties - however if I do this currently my "unencrypted_password" will be rejected - injecting additionalProperties: true anywhere in the last schema does not seem to have any effect.
Am I just trying to do something stupid here, or is it just not possible to override additionalProperties when using patch/merge in its current state? Is this something on the roadmap to be done or something which would not even be considered if I wrote a PR for it?
Gives out an error:
error: uncaughtException: can't resolve reference http://json-schema.org/draft-06/schema#
Using the library in TypeScript v2.5
the following code produce the error:
import * as Ajv from 'ajv';
import * as AjvAsync from 'ajv-async';
import * as AjvKeywords from 'ajv-keywords';
import * as AjvMergePatch from 'ajv-merge-patch';
export const ajv = AjvAsync(new Ajv({
coerceTypes: 'array',
jsonPointers: true,
allErrors: true,
$data: true,
removeAdditional: true,
}));
AjvMergePatch(ajv);
AjvKeywords(ajv);
Please, update fast-json-patch
to the v3
.
Current used version v2
contains vulnerability.
npm audit
# npm audit report
fast-json-patch <3.1.1
Severity: high
Starcounter-Jack JSON-Patch Prototype Pollution vulnerability - https://github.com/advisories/GHSA-8gh8-hqwg-xf34
No fix available
node_modules/fast-json-patch
ajv-merge-patch *
Depends on vulnerable versions of fast-json-patch
node_modules/ajv-merge-patch
2 high severity vulnerabilities
Some issues need review, and may require choosing
a different dependency.
Hi,
When the "with" object contains definitions (like '"$ref": "#/definitions/JwtConfig"'), ajv throws an error.
I need to move the "with" "definitions" property to the main object :
{
$merge: {
source
with
},
definitions
}
I think it's an issue :)
Regards
Hi, is it possible to generate a complete, merged, schema definition from one with $merge directives in it? Thanks.
Hi,
I've started using JSON schema in a project and it's all working well until I've tried using merge patch. There doesn't seem to be too many examples out there regarding merge/patch for cases where the schema being modified has references inside it so I was hoping I could get some guidance. I have a test schema:
const Ajv = require("ajv")
const ajv = new Ajv({allErrors: true, v5: true})
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
require('ajv-merge-patch')(ajv);
console.log("Start with base schema ================================")
const a = {
"$id": "a.json#",
type: "object",
properties: {
size: {type: "number"}
},
required: ["size"],
additionalProperties: false
}
ajv.addSchema(a, "a.json")
A simple merge works okay
console.log("Simple Merge Test =====================================")
const b = {
"$id": "b.json#",
$merge: {
source: { "$ref": "a.json#" },
with: {
properties: {
length: { type: "number" }
},
required: ["length"],
additionalProperties: false
}
}
}
const bv = ajv.compile(b);
console.log(bv({ length: 3, size: 4}))
however, if I create a schema with a reference to the first one inside and try to extend the inner schema via the outer schema it fails:
const c = {
"$id": "c.json#",
type: "object",
properties: {
inner: { "$ref": "a.json#" }
}
}
console.log("Nested Ref Merge test ==================================")
const d = {
"$id": "d.json#",
$merge: {
source: { "$ref": "c.json#" },
with: {
properties: {
inner: {
properties: {
length: { type: "number" }
},
required: ["length"]
}
}
}
}
}
const dv = ajv.compile(d)
I believe this is due to refs not being replaced in the schema. Firstly I was wondering if what I am doing is odd or not - the main reason I was doing it this way is because in my actual scenario I have multiple properties based off of the same schema but only want to merge additional properties into some of them.
If it's not odd then how would I get the above to work?
Many thanks
node_modules/ajv/lib/keyword.js:30 throw new Error('Keyword ' + keyword + ' is not a valid identifier'); ^
It’s related witth this changes
Since the $merge
operation does not concatenate or intersect arrays, but rather overwrites them wholesale, it is basically nonsensical for the top-level node under the with
property of the $merge
schema to be an array. For example, this would not produce a valid schema post merge:
//Valid baseSchema
{
"$id": "baseSchema",
"type": "object",
"properties": {
"foo": {
"type": "number"
}
}
}
//Invalid merged schema
{
"$id": "schemaExtended",
"$merge": {
"source": { "$ref": "baseSchema.json#" },
"with": ["foo", "bar"]
}
}
}
Because this is the case, would it be possible to have the with
property support an array, which would then be interpreted as a series of consecutive merges?
{
"$id": "baseSchema",
"type": "object",
"properties": {
"foo": {
"type": "number"
}
}
}
//Cumbersome "nested" syntax for handling multiple merges, which is basically impossible to parse
{
"$id": "schemaExtended",
"$merge": {
"source": {
"$merge": {
"source": { "$ref": "baseSchema.json#" },
"with": {
"properties": {
"foo": {
"minimum": 0
},
"bar": {
"type": "string"
}
}
}
}
}
"with": {
"properties": {
"baz": {
"type": "boolean"
}
},
"additionalProperties": false
}
}
}
//Much cleaner syntax which mirrors the way _.extend and Object.assign work
{
"$id": "schemaExtended",
"$merge": {
"source": { "$ref": "baseSchema.json#" },
"with": [{
"properties": {
"foo": {
"minimum": 0
},
"bar": {
"type": "string"
}
}
}, {
"properties": {
"baz": {
"type": "boolean"
}
},
"additionalProperties": false
}]
}
}
Is there interest in implementing this? I doubt it would ever break existing functionality, since top-level arrays under with
already always produce invalid schemas, and I think it would be pretty easy to implement. If this feels like something worth doing, I can submit a quick PR.
Hi,
I'm not sure if this is an issue with my understand of the spec or not. I understand merge a lot better than patch and so I'd like to use it where possible and then patch things I can't fix with merge. However, I'm having difficulty applying a patch to a schema who's source I apply a merge:
deliver.json
{
"type": "object",
"definitions": {
"settings": {
"type": "object",
"properties": {
"country": {
"type": "array",
"items": { "type": "string" }
},
"client": {
"type": "array",
"items": {
"type": "string",
"enum": ["mobile", "desktop"]
}
}
},
"additionalProperties": false
}
},
"properties": {
"include": { "$ref": "#/definitions/settings" },
"exclude": { "$ref": "#/definitions/settings" },
"templateParameters": {
"type": "object",
"required": [],
"patternProperties": {
".*": { "type": "string" }
}
},
"expire": { "type": "string", "format": "date" }
},
"required": [],
"additionalProperties": false
}
and my new schema:
x.json
{
"$patch": {
"source" : {
"$merge": {
"source": { "$ref": "deliver.json" },
"with": {
"properties": {
"templateParameters": {
"properties": {
"link": { "type": "string" }
},
"required": ["link"]
}
},
"required": ["templateParameters"]
}
}
},
"with": [{
"op": "replace",
"path": "/properties/include/properties/client/items",
"value": { "type": "string", "enum": [ "alpha", "bravo" ] }
}]
}
}
I get the error message:
{ [OPERATION_PATH_UNRESOLVABLE: Cannot perform the operation at a path that does not exist]
message: 'Cannot perform the operation at a path that does not exist',
name: 'OPERATION_PATH_UNRESOLVABLE',
index: 0,
operation:
{ op: 'replace',
path: '/properties/include/properties/client/items',
value: { type: 'string', enum: [Array] } },
tree: { '$merge': { source: [Object], with: [Object] } } }
Is this expected? I'm not sure if this kind of thing is allowed under the specification but if it were then I think the order the transformations are applied would need to be altered.
Thanks
Given schema1
{
type: 'object',
properties: {
properties: {
foo: { type: "string" }
},
},
required: ["foo"]
}
And given schema2
{
$merge: {
source: schema1,
with: {
type: 'object',
properties: {
bar: { type: "string" }
},
required: ["baz"]
}
}
}
I expect foo
and bar
to be required in schema2
.
Instead, only bar
is required.
Is this a bug or a feature?
I am trying to merge/patch
schemas based on conditions with if/then/else
. Is this generally possible?
Here is a minimal reproduction CSB of what I am trying to achieve:
https://codesandbox.io/s/serene-cdn-0ugtt?file=/src/index.js
HI! I would very thankful if you upgrade json-merge-patch from 0.2.3 to 1.0.2 in ajv-merge-patch 4.1.0 version.
json-merge-patch has vulnerability and i haven’t ability upgrade ajv-merge-patch to 5.0.1 version.
Explanation
The json-merge-patch package is vulnerable to Prototype Pollution. The apply function in the apply.js file allows the addition or modification of object prototype properties via accessors such as proto or constructor.prototype. A remote attacker with the ability to apply patches or influence their content can exploit this vulnerability to modify the behavior of object prototypes which, depending on their use in the application, may result in a Denial of Service (DoS), Remote Code Execution (RCE), or other unexpected behavior.
While trying to compile and validate some object against an schema using the $merge
keyword, I'm always getting this as an error result:
{
"errors": [
{
"dataPath": "", // <-- Some proper validation error
"schemaPath":"#/required",
"params": {
"missingProperty": "bar"
},
"message": "should have required property 'bar'"
},
{
"dataPath": "", // <--- This always appears
"schemaPath":"#/$merge",
"params": {
"keyword": "$merge"
},
"message": "should pass \"$merge\" keyword validation"
}
]
}
The simplest use case can reproduce this:
const Ajv = require('ajv');
let ajv = new Ajv({ v5: true });
require('ajv-merge-patch/keywords/merge')(ajv);
let validate = ajv.compile({
$merge: {
source: {
required: ['bar'],
properties: {
bar: {
type: 'string'
}
},
additionalProperties: true
},
'with': {
properties: {
foo: {
type: 'string'
}
}
}
}
});
// Validate any object
validate({ foo: 42 });
console.log(JSON.stringify(validate.errors));
What am I doing wrong here?
Using ajv@^4.10.4
and ajv-merge-patch@^2.1.0
.
Updating ajv-merge-patch
devDependency to [email protected]
produces the following test failures:
1) async schema loading "before each" hook for "should load missing schemas":
can't resolve reference http://json-schema.org/draft-06/schema# from id #
2) errors missing $ref "before each" hook for "should throw exception if cannot resolve $ref":
can't resolve reference http://json-schema.org/draft-06/schema# from id #
3) keyword $merge "before each" hook for "should extend schema defined in $merge":
can't resolve reference http://json-schema.org/draft-06/schema# from id #
4) keyword $patch "before each" hook for "should extend schema defined in $patch":
can't resolve reference http://json-schema.org/draft-06/schema# from id #
I ran into these errors trying to use relative $ref
as the source
in my schema.
I'm trying to put this together with fastify. I added ajv-merge-path as described in the docs into the validation (https://github.com/fastify/fastify/blob/master/lib/validation.js#L157)
example case:
schemas/defs.json
{
"$id": "/defs.json",
"type": "object",
"definitions": {
"test": {
"type": "object",
"properties": {
"name": { "type": "string" }
}
}
}
}
schemas/foo.json
{
"$id": "/foo.json",
"type": "object",
"definitions": {
"bar": {
"type": "object",
"$merge": {
"source": {
"$ref": "/defs.json#/definitions/test"
},
"with": {
"properties": {
"id": { "type": "string" }
}
}
}
}
}
}
fastify.get('/defs.json', (request, reply) => {
reply.send(require('./schemas/defs'))
})
fastify.get('/foo.json', (request, reply) => {
reply.send(require('./schemas/foo'))
})
fastify.post(
'/',
{
schema: {
body: { $ref: '/foo.json#/definitions/bar' }
}
},
function(request, reply) {
reply.send(fastify.getSchemas())
}
)
properties
directly to foo
, everything works as expected, so the $merge doesn't work.Any ideas how I can make this work?
That will be possible when ajv-validator/ajv#47 is implemented
This should work:
{
id: 'schema.json#',
definitions: {
foo: {
properties: {
str: { type: 'string' }
}
}
},
$merge: {
source: { $ref: '#/definitions/foo' },
with: {
properties: {
int: { type: 'integer' }
}
}
}
}
At the moment it needs source: { $ref: 'schema.json#/definitions/foo' }
in order to work.
Shallow clone used now can still lead to the change of the source schema at some deeper levels
It seems as if there is an out-of-the-box compatibility issue with Ajv2020
instances of ajv.
When trying to apply the ajv-merge-patch
keywords, I get...
Error: can't resolve reference http://json-schema.org/draft-07/schema# from id # at Object.code (/Users/[redacted]/node_modules/ajv/dist/vocabularies/core/ref.js:21:19) at ...
...which looks like is related to code in /keywords/add_keyword.js
of this repo where this metaschema is referenced.
Code to reproduce...
import Ajv2020 from 'ajv/dist/2020.js';
import ajvMergePatch from 'ajv-merge-patch';
const ajv = new Ajv2020();
ajvMergePatch(ajv);
I was able to workaround with the following...
import { readFileSync } from 'fs';
import Ajv2020 from 'ajv/dist/2020.js';
import ajvMergePatch from 'ajv-merge-patch';
const ajv = new Ajv2020();
// required by ajv-merge-patch
const draft7MetaUrl = new URL('../node_modules/ajv/dist/refs/json-schema-draft-07.json', import.meta.url);
const draft7MetaSchema = JSON.parse(readFileSync(draft7MetaUrl, 'utf8'));
ajv.addMetaSchema(draft7MetaSchema);
ajvMergePatch(ajv);
...but figured I would open issue here to raise visibility.
I know that ajv
itself indicates there is no cross-compatibility between draft-2020-12 and draft-07 schema within same ajv
instance, however this does seems to work. At a minimum, I would find it unexpected that a developer using only draft-2020-12 (or draft-2019 for that matter) schemas within their application and wanting to leverage this extension would get such breakage, with reference to a schema they are not even using.
First of all: thank you for your work. I have a question that I can't figure out for the life of me. I wonder if you can figure out what I'm missing here?
I'm trying to patch a schema and then validate data against subschemas of the schema. It seems the patch is not applied to subschemas, but somehow applied to the root schema (without taking the source path defined in the patch into account).
// fhir.schema.test.json
{
"$id": "http://hl7.org/fhir/json-schema/4.0#",
"discriminator": {
"propertyName": "resourceType",
"mapping": {
"Account": "#/definitions/Account",
"Patient": "#/definitions/Patient"
}
},
"oneOf": [
{
"$ref": "#/definitions/Account"
},
{
"$ref": "#/definitions/Patient"
}
],
"definitions": {
"Account": {
"properties": {
"resourceType": {
"const": "Account"
}
},
"additionalProperties": false,
"required": [
"resourceType"
]
},
"Patient": {
"properties": {
"resourceType": {
"const": "Patient"
}
},
"additionalProperties": false,
"required": [
"resourceType"
]
}
}
}
const schemas = [
require('./fhir.schema.test.json'),
{
$patch: {
source: { $ref: '#/definitions/Patient' },
with: [
{ op: 'add', path: '/properties/q', value: { type: 'number' } },
{ op: 'add', path: '/required/-', value: 'q' }
]
}
}
];
const everything = Object.assign(...schemas);
ajv.addSchema(everything, 'key');
const rootSchema = ajv.getSchema(`key`);
const getSubSchema = definition => ajv.getSchema(`key#/definitions/${definition}`);
// TESTS AND EXPECTATIONS
const tests = [
{ resourceType: 'Account' }, // Expect true (no q necessary in 'Account')
{ resourceType: 'Patient', q: 1 }, // Expect true
{ resourceType: 'Patient', q: 'abc' }, // Expect false (q is wrong type)
{ resourceType: 'Patient' }, // Expect false (missing q)
];
console.log('rootSchema', inspect(rootSchema.schema, { depth: null }));
tests.forEach(test => {
const subSchema = getSubSchema(test.resourceType);
console.log('test:', test);
console.log('rootSchema isValid:', rootSchema(test));
console.log('rootSchema errors:', rootSchema.errors);
console.log('subSchema:', inspect(subSchema.schema, { depth: null }));
console.log('subSchema isValid:', subSchema(test));
console.log('subSchema errors:', subSchema.errors);
});
output:
rootSchema {
'$id': 'http://hl7.org/fhir/json-schema/4.0#',
discriminator: {
propertyName: 'resourceType',
mapping: {
Account: '#/definitions/Account',
Patient: '#/definitions/Patient'
}
},
oneOf: [
{ '$ref': '#/definitions/Account' },
{ '$ref': '#/definitions/Patient' }
],
definitions: {
Account: {
properties: { resourceType: { const: 'Account' } },
additionalProperties: false,
required: [ 'resourceType' ]
},
Patient: {
properties: { resourceType: { const: 'Patient' } },
additionalProperties: false,
required: [ 'resourceType' ]
}
},
'$patch': {
source: { '$ref': '#/definitions/Patient' },
with: [
{ op: 'add', path: '/properties/q', value: { type: 'number' } },
{ op: 'add', path: '/required/-', value: 'q' }
]
}
}
test: { resourceType: 'Account' }
rootSchema isValid: false
rootSchema errors: [
{
keyword: 'const',
dataPath: '.resourceType',
schemaPath: '#/properties/resourceType/const',
params: { allowedValue: 'Patient' }, # <-- WHY? IT'S AN 'Account'?
message: 'should be equal to constant'
},
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'q' },
message: "should have required property 'q'" # <-- NO IT SHOULD NOT
},
{
keyword: '$patch',
dataPath: '',
schemaPath: '#/$patch',
params: { keyword: '$patch' },
message: 'should pass "$patch" keyword validation'
}
]
subSchema: {
properties: { resourceType: { const: 'Account' } },
additionalProperties: false,
required: [ 'resourceType' ]
}
subSchema isValid: true
subSchema errors: null
test: { resourceType: 'Patient', q: 1 }
rootSchema isValid: false
rootSchema errors: [
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'q' },
message: "should have required property 'q'" # <-- IT HAS PROPERTY q!
},
{
keyword: '$patch',
dataPath: '',
schemaPath: '#/$patch',
params: { keyword: '$patch' },
message: 'should pass "$patch" keyword validation'
}
]
subSchema: {
properties: { resourceType: { const: 'Patient' } },
additionalProperties: false,
required: [ 'resourceType' ]
}
subSchema isValid: true
subSchema errors: null
test: { resourceType: 'Patient', q: 'abc' }
rootSchema isValid: false
rootSchema errors: [
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'q' },
message: "should have required property 'q'" # <-- IT HAS PROPERTY q!
},
{
keyword: '$patch',
dataPath: '',
schemaPath: '#/$patch',
params: { keyword: '$patch' },
message: 'should pass "$patch" keyword validation'
}
]
subSchema: {
properties: { resourceType: { const: 'Patient' } },
additionalProperties: false,
required: [ 'resourceType' ]
}
subSchema isValid: true # <-- WHY? q HAS THE WRONG TYPE
subSchema errors: null
test: { resourceType: 'Patient' }
rootSchema isValid: false
rootSchema errors: [
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'q' },
message: "should have required property 'q'"
},
{
keyword: '$patch',
dataPath: '',
schemaPath: '#/$patch',
params: { keyword: '$patch' },
message: 'should pass "$patch" keyword validation'
}
]
subSchema: {
properties: { resourceType: { const: 'Patient' } },
additionalProperties: false,
required: [ 'resourceType' ]
}
subSchema isValid: true # <-- WHY? q IS MISSING!
subSchema errors: null
As you can see, testing against the rootSchema doens't differentiate between the various definitions, but testing against the subSchemas doesn't apply the patch.
I have tried various combinations of ajv.compile
and ajv.schema
, but nothing seems to give the results I'm expecting.
For example, if I have the following two schemas:
{
"$id": "baseSchema",
"type": "object",
"properties": {
"foo": {
"type": "number"
}
}
}
{
"$id": "schemaExtended",
"$merge": {
"source": { "$ref": "schemaExtended.json#" },
"with": {
"properties": {
"foo": {
"minimum": 0
},
"bar": {
"type": "string"
}
}
}
}
}
Is there anyway to output the result of the merge operation? Something like ajv.getMergedSchema(schemaExtended);
, which would produce:
{
"$id": "schemaExtended",
"type": "object",
"properties": {
"foo": {
"type": "number",
"minimum": 0
}
"bar": {
"type": "string"
}
}
}
The reason I ask is that most other json-schema implementations do not support merge-patch, so it would be nice if there was some method by which JSON schemas that use these keywords could be converted to regular JSON schemas.
Let's admit a simple base schema like this one:
const SnakeSchema = {
type: 'object',
properties: {
name: { type: 'string' },
foo_bar: { type: 'string' },
},
} as const;
I want the type inference to work within my application, so I use the JTDDataType
utility.
import { JTDDataType } from 'ajv/dist/jtd';
const SnakeSchema = { /* ... */ } as const;
type Snake = JTDDataType<typeof SnakeSchema>;
Now, let's write a simple patch:
const KebabSchema = {
$patch: {
source: SnakeSchema,
with: [
{ op: 'move', from: '/properties/foo_bar', to: '/properties/fooBar' },
],
},
} as const;
The only way I found to get the infered type is by doing this:
type Kebab = Omit<Snake, 'foo_bar'> & {
fooBar: SnakeSchema['foo_bar'];
As it is not really DRY, I was wondering if there is something I miss in the docs... If not, maybe is there something that can be done in the project?
See: GHSA-8gh8-hqwg-xf34
@epoberezkin would it be possible to upgrade fast-json-patch
to 3.1.1
by chance?
Describe the bug
With ajv option set to strict mode the ajv-merge-patch package does not work. I receive the error:
strict mode: required property "value" is not defined at "#/properties/with/items/anyOf/0" (strictRequired)
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Package should work even with strict mode on
Do you have a proposed solution?
In this line a property is required which is not defined, after removing it locally everything worked as expected.
ajv-merge-patch/keywords/patch.js
Line 19 in 0a4c44f
Additional context and screenshots
Add any other context about the problem, and screenshots if applicable.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.