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.
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).
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: nulltest: { 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: nulltest: { 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: nulltest: { 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: nullAs 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.compileandajv.schema, but nothing seems to give the results I'm expecting.