What version of Ajv are you using? Does the issue happen if you use the latest version?
6.10.2
Problem
I'm trying to workaround the restrictions explained here: #1039. So I am trying to define an async custom keyword typehint instead of using format.
Your code
async function validateEpsg(x) {
// Something asynchronous would happen here, but removed to keep the example simple.
if (x > 4000 && x < 10000) {
return true;
}
throw new Ajv.ValidationError([{
message: "Invalid EPSG code specified."
}]);
};
var validate = async function() {
var jv = new Ajv({schemaId: 'auto', format: 'full'});
jv.addKeyword('typehint', {
dependencies: [
"type"
],
compile: (typehint, schema) => {
return ('integer' === schema.type);
},
validate: async (schema, data) => {
return await validateEpsg();
},
async: true,
errors: true
});
var schema = {
"$async": true,
"schema": "http://json-schema.org/draft-07/schema#",
type: "integer",
typehint: "epsg-code"
};
try {
await jv.validate(schema, 0);
console.log("Should never happen!");
} catch (error) {
console.log(error);
}
try {
await jv.validate(schema, 4326);
console.log("Should happen!");
} catch (error) {
console.log(error);
}
}
validate();
Validation result, data AFTER validation, error messages
When running the code above, I get the error message: Cannot create property 'errors' on boolean 'true'
What results did you expect?
First try block should log the error, second try block should print "Should happen!".
What is wrong with my code?
You need to use only "validate" inside keyword definition, otherwise the function returned by "compile" is used.
Also, when you use "errors": true, ajv expects that you define the errors yourself, and you don't (which is the source of errors).
Please review custom keyword docs: https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords
Thanks a lot, @epoberezkin. This error drove me nuts... works well now.
You need to use only "validate" inside keyword definition, otherwise the function returned by "compile" is used.
Is that documented somewhere? Couldn't find any information.
Also, when you use "errors": true, ajv expects that you define the errors yourself, and you don't (which is the source of errors).
Not quite sure how to define them, but I'm simply throwing ajv.ValidationError in validateEpsg, which seems to work?!
https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md - it defines 4 types of keywords. Additional validate function can be used as a fallback for custom keywords that support $data.
Possibly, docs can be improved a bit. Many working examples in Ajv-keywords can be helpful.
Throwing a validation error should work in async keywords that use ES6 async “validate” function, and may work in other cases - I don’t remember how it’s implemented. The docs suggest another way of returning validation errors I think...
I still don't always get how to define custom keywords.
var subtypes = {
'band-name': {type: 'string'},
'bounding-box': {type: 'object'}
};
jsv.addKeyword("subtype", {
dependencies: [
"type"
],
metaSchema: {
type: "string",
enum: Object.keys(subtypes)
},
compile: function (subtype, schema) {
if (schema.type != subtypes[subtype].type) {
return false;
// throw "Subtype '"+subtype+"' not allowed for type '"+schema.type+"'.";
}
return true;
},
errors: false
});
@epoberezkin Why is this returning cannot create property 'errors' on boolean 'true'? The code looks exactly like the range example in the documentation. It works when removing the compile function.
The compile function must return function. Please see docs, tests and keyword implementations in ajv-keywords package. You may also submit a question to stackoverflow. I cannot provide any more support to create custom keywords.
Thank you. Embarrassing, how have I missed that it returns a function and not a boolean?