ajv-cli icon indicating copy to clipboard operation
ajv-cli copied to clipboard

how to compile/validate group of json-schema files that might reference each other in v4+

Open rajpaulbagga opened this issue 3 years ago • 14 comments

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?

rajpaulbagga avatar Jul 19 '21 20:07 rajpaulbagga

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.

xmedeko avatar Jul 27 '21 11:07 xmedeko

not a bad idea, it should be relatively straightforward to do. PR is welcome.

epoberezkin avatar Aug 01 '21 07:08 epoberezkin

I'm happy to try a contribution here!

matanlurey avatar Oct 03 '21 21:10 matanlurey

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 avatar Dec 06 '21 13:12 vmaurin

@vmaurin I agree, my previous comment is wrong. Please, comment also PR #190

xmedeko avatar Dec 06 '21 13:12 xmedeko

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

vmaurin avatar Dec 06 '21 14:12 vmaurin

I am not a maintainer, but I agree starting a new PR is better.

xmedeko avatar Dec 06 '21 14:12 xmedeko

Any workaround for this?

safareli avatar Feb 04 '22 10:02 safareli

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 ?

vmaurin avatar Feb 04 '22 11:02 vmaurin

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 addSchema method.

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);

AndyOGo avatar Aug 07 '22 09:08 AndyOGo

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.

krystof-k avatar Aug 14 '23 20:08 krystof-k

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 avatar Nov 15 '23 21:11 jameswinglmi

@jameswinglmi It is working even with double asterisk for me:

ajv validate -r "./**/!(schema).schema.json" -s schema.schema.json -d file.json

krystof-k avatar Nov 15 '23 21:11 krystof-k

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.

jameswinglmi avatar Nov 16 '23 15:11 jameswinglmi