ajv-cli
ajv-cli copied to clipboard
how to compile/validate group of json-schema files that might reference each other in v4+
Scenario: Directory with several JSON-schema files, some of which might $reference the others. Each schema file defines a unique $id property.
I want to compile/validate them all at once
This command worked in ajv-cli v3.x:
ajv -s 'src/models/*.json' -r 'src/models/*.json'
But with 4.x and 5.0, I get errors like this:
error: schema with key or id "https://mycompany.com/this/schema/model.json" already exists
But the only file that has that id is the file itself in that directory. It would seem that the -r flag is not excluding the file's self when checking for duplicate ids in that referenced list.
How can I accomplish validating a group of schemas like this with the newer version?
I agree, the -r should omit file specified by the -s flag.
My scenario is
ajv -s 'src/models/root.json' -r 'src/models/*.json' -d 'data.json'
and got the error schema with key or id ...already exists. I have to specify -r multiple times, which is uncomfortable.
Same problem with ajv compile.
not a bad idea, it should be relatively straightforward to do. PR is welcome.
I'm happy to try a contribution here!
I am not sure the approach to filter out file will be the proper one. With glob, there is an order from which files are processed, thus depending on the order, depending schema might not be there yet. A more reliable fix would be to load everything with -r and then, on the -s files/schemas check if they are loaded or not yet
@vmaurin I agree, my previous comment is wrong. Please, comment also PR #190
It seems an implementation using addUsedSchema would be more adapted to solve the issue, so appart from the tests, should we start a new PR ? I am doing experimentation with it, I can eventually open a PR if it is successful
I am not a maintainer, but I agree starting a new PR is better.
Any workaround for this?
On my side, I ended up by using ajv directly, here my script with ajv 8.6.0 and ajv-formats 2.1.1
const Ajv = require('ajv');
const fs = require('fs');
const path = require('path');
const addFormats = require('ajv-formats')
const readdirSyncRecursive = require('fs-readdir-recursive')
const schemasBasePath = path.join(__dirname, 'public', 'specs');
const schemaExtension = '.json';
const availableSchemas = readdirSyncRecursive(schemasBasePath)
.filter(file => file.endsWith(schemaExtension))
.map(file => path.resolve(schemasBasePath, file))
.map((schemaPath) => {
return readFile(schemaPath);
})
.reduce((schemas, schema) => {
schemas.push(schema);
return schemas;
}, []);
const ajv = new Ajv({
allErrors: true,
verbose: true,
schemas: availableSchemas,
addUsedSchema: false,
strict: true,
validateSchema: true
});
addFormats(ajv);
availableSchemas.map((schema) => {
console.log(`Compiling ${schema.$id} ...`);
ajv.compile(schema);
});
function readFile(file) {
try {
return json = JSON.parse(fs.readFileSync(file, {
encoding: 'UTF-8'
}));
} catch (err) {
console.error('error: ' + err.message);
process.exit(2);
}
}
I am not sure then how it should look on the CLI tool options ?
AJV is an awesome tool!
I used to validate multiple schemas with the CLI, but somehow this feature got broken :(
Edit:
I tried to track it down.
ajv-cli doesn't support the addUsedSchema option.
Though that would not help, as it does not affect addSchema method.
This option does not affect
addSchemamethod.
I believe this can be fixed with a oneliner, here https://github.com/ajv-validator/ajv-cli/blob/58d6f074720d5dc0773c3786320b7d35718060cd/src/commands/compile.ts#L52-L54
By replacing it with:
const id = sch === null || sch === void 0 ? void 0 : sch.$id;
if (!ajv.getSchema(id || file)) {
ajv.addSchema(sch, id ? undefined : file);
}
const validate = ajv.getSchema(id || file);
It's possible to use a exclude glob pattern as a workaround:
ajv validate -r "./!(schema).schema.json" -s schema.schema.json -d file.json
At least working for me.
The workaround from @krystof-k looks great, except I have a recursive glob:
ajv validate --spec=draft2020 -r "./**/*.json" -s "./subschemas/structures/branch.json" -d "file.json"
So, I think that would exclude my targeted filename at every level, even if I could get the (extended?) glob pattern to work (which I haven't yet, since it seems it still won't solve my issue):
ajv validate --spec=draft2020 -r "./subschemas/**/!(branch).json" -s "./subschemas/structures/branch.json" -d "file.json" bash: !: event not found
Is there a workaround for this use case? It looks like there was a PR for this, which was not merged?
@jameswinglmi It is working even with double asterisk for me:
ajv validate -r "./**/!(schema).schema.json" -s schema.schema.json -d file.json
In fact I had failed to enable extended glob patters with shopt -s extglob. But that was really the ancillary issue. The use case is the recursion, in that it will exclude the file with the same name of the targeted-to-exclude file when it is found in other directories than the targeted one. For example, say I have a "metadata.json" file in each directory, containing info regarding the files in that particular directory, and I want to validate one of them -- ./my/path/metadata.json. The workaround above will also exclude ./my/metadata.json and ./my/path/further/metadata.json, and ./my/other/path/metadata.json.
I've been trying to find a way to exclude just a single file, rather than a pattern that could match many.