-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow custom Ajv instance (closes #115) #116
base: main
Are you sure you want to change the base?
Conversation
@pstadler Thanks for opening this PR — it looks great! I'm going to check out the branch and give it a manual test before merging. Please can you add a description to this PR that references your original issue and explains the purpose of this feature? |
done 👍🏻 |
Ping 👋🏻 |
@pstadler I've been quite overloaded recently, so haven't been able to give this the testing time it needs. Thanks for being so patient, I'll do my best to pick it up soon! |
no problem. sometimes people forget. take your time. 👍🏻 |
Manual testing
|
@pstadler I've run into an issue while manually testing this PR, but I'm not quite sure what's going wrong: I've created a minimal JavaScript application (code) and then installed const Ajv2019 = require("ajv/dist/2019");
const ajv = new Ajv2019(); When I run the application I receive the following error from Ajv:
Do you have any idea what's happening? |
From what I can tell, this I've confirmed that with a minimal reproduction too: const Ajv2019 = require("ajv/dist/2019");
const AjvCore = require("ajv/dist/core").default;
const ajv = new Ajv2019();
console.log("Ajv2019 instanceof AjvCore:", Ajv2019 instanceof AjvCore);
// > Ajv2019 instanceof AjvCore: false |
Just tried your sample app. It works for me:
|
Your check is wrong, you need to replace: console.log("Ajv2019 instanceof AjvCore:", Ajv2019 instanceof AjvCore); with console.log("Ajv2019 instanceof AjvCore:", ajv instanceof AjvCore); |
Okay, I think the instaceof check could indeed fail with different ajv package versions and needs to be changed to check if the provided argument is any kind of class, not the bundled AjvCore. |
Good catch on the bug in my minimal reproduction. I made the replacement you suggested and it was
How about making this check instead? if (typeof ajvOptions?.compile === "function") { |
65853b0
to
bd6c04d
Compare
This seems to be the right approach. Change amended. Please note that eslint |
@@ -23,7 +23,7 @@ | |||
"scripts": { | |||
"test": "npm run test:unit && npm run test:types", | |||
"test:unit": "tap", | |||
"test:types": "tsc --noEmit --strict src/index.d.ts", | |||
"test:types": "tsc --noEmit --strict --target ES2018 --moduleResolution node --resolveJsonModule src/index.d.ts", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the --target
value here be updated to match the ecmaVersion
in the ESLint config (2020
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear why --moduleResolution node
and --resolveJsonModule
are needed here. Could you please explain?
import Ajv, { ErrorObject, Options as AjvOptions } from "ajv"; | ||
import { ErrorObject, Options as AjvOptions } from "ajv"; | ||
import AjvCore from "ajv/lib/core"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been testing this PR with a TypeScript application and I've run into a type error, which I think is caused by this using the AjvCore
type instead of the Ajv
type. Official Ajv plugins use the Ajv
type, for example here in ajv-formats
.
With this TypeScript code:
import addFormats from "ajv-formats";
import { Validator, ValidationError } from "express-json-validator-middleware";
const validator = new Validator({
coerceTypes: true,
});
addFormats(validator.ajv, ["date-time"])
.addKeyword("kind")
.addKeyword("modifier");
I receive this type error when running tsc
:
src/lib/middleware/validation/index.ts:9:12 - error TS2345: Argument of type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/core").default' is not assignable to parameter of type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/core").default'.
Types of property 'opts' are incompatible.
Type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/core").InstanceOptions' is not assignable to type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/core").InstanceOptions'.
Type 'InstanceOptions' is not assignable to type 'CurrentOptions'.
Types of property 'keywords' are incompatible.
Type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/types/index").Vocabulary | undefined' is not assignable to type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/types/index").Vocabulary | undefined'.
Type 'Vocabulary' is not assignable to type '(string | KeywordDefinition)[]'.
Type 'string | import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/types/index").KeywordDefinition' is not assignable to type 'string | import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/types/index").KeywordDefinition'.
Type 'CodeKeywordDefinition' is not assignable to type 'string | KeywordDefinition'.
Type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/types/index").CodeKeywordDefinition' is not assignable to type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/types/index").CodeKeywordDefinition'.
Types of property 'code' are incompatible.
Type '(cxt: import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/compile/validate/index").KeywordCxt, ruleType?: string | undefined) => void' is not assignable to type '(cxt: import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/index").KeywordCxt, ruleType?: string | undefined) => void'.
Types of parameters 'cxt' and 'cxt' are incompatible.
Type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/index").KeywordCxt' is not assignable to type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/compile/validate/index").KeywordCxt'.
The types of 'gen._scope' are incompatible between these types.
Type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/ajv-formats/node_modules/ajv/dist/compile/codegen/scope").Scope' is not assignable to type 'import("/home/simonplend/dev/github/simonteaching/api-project/node_modules/express-json-validator-middleware/node_modules/ajv/lib/compile/codegen/scope").Scope'.
Property '_names' is protected but type 'Scope' is not a class derived from 'Scope'.
9 addFormats(validator.ajv, ["date-time"])
~~~~~~~~~~~~~
Can this be changed back to importing the Ajv
type, and then that type be used in the constructor instead of AjvCore
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great overall! There are two things to mention:
-
I've tested these changes with a JavaScript application and it works as expected, but I'm getting a type error when testing it with a TypeScript application (see my comment on the types).
-
Please add an explanation on how to set a custom Ajv instance to the beginning of the Ajv instance section in the README. A short description and the code example from the PR description should suffice.
Hey @pstadler, I think this PR is almost ready to merge - let me know if there's anything I can do to help you wrap it up. |
@pstadler Any chance you'd be up for wrapping up this PR? No problem if not, I can just close it if you don't have the time or inclination. |
Previously,
Validator
would only take an object containing Ajv options. With this PR, we've added the possibility to pass a custom Ajv instance instead, for example one of the 2019-09 draft version[1]:[1] https://ajv.js.org/json-schema.html#draft-2019-09