atlassian / better-ajv-errors

JSON Schema validation for Human 👨‍🎤
https://atlassian.github.io/better-ajv-errors/
Other
232 stars 44 forks source link

Misleading error when describing deeply nested `anyOf` failure #153

Open broofa opened 2 years ago

broofa commented 2 years ago

See minimal, verifiable test case, below.

Briefly, the case below is failing due to the type of the defaultConfigPath property being incorrect (boolean instead of string).

Expected Result:

I would expect to see an error message along the lines of "'defaultConfigPath' must be a string, not a boolean" in this case. Or at least an error that summarizes the errors for each of the anyOf item failure conditions.

Actual Result:

BAE describes the error for the second anyOf condition, but not the first:

CleanShot 2022-06-24 at 16 31 06@2x

Editorial:

If BAE has to choose just one of many errors passed to it, would it make sense to select error with the deepest schemaPath? Or maybe instancePath? Intuition tells me that the "deeper" an error is within a data structure, the more likely it is to be something the user can meaningfully act upon.

[
  {
    //  This is the error I would most expect/like to see in this case
    instancePath: '/processor/defaultConfigPath',
    schemaPath: '#/properties/processor/anyOf/0/properties/defaultConfigPath/type',
    keyword: 'type',
    params: { type: 'string' },
    message: 'must be string'
  },
  {
    // This error, while technically correct, isn't all that relevant since the data doesn't
    // match any properties in this sub-schema
    instancePath: '/processor',
    schemaPath: '#/properties/processor/anyOf/1/required',
    keyword: 'required',
    params: { missingProperty: 'defaultConfigPackageKey' },
    message: "must have required property 'defaultConfigPackageKey'"
  },
  {
    // This error is least likely to be meaningful.  And fixing one of the above errors will fix this error. 
    instancePath: '/processor',
    schemaPath: '#/properties/processor/anyOf',
    keyword: 'anyOf',
    params: {},
    message: 'must match a schema in anyOf'
  }
]

Minimal, stand-alone test case

#!/usr/bin/env node

import Ajv from 'ajv';
import betterAjvErrors from 'better-ajv-errors';

const SCHEMA = {
  $schema: 'http://json-schema.org/draft-07/schema#',
  type: 'object',
  properties: {
    processor: {
      anyOf: [
        {
          additionalProperties: false,
          type: 'object',
          properties: {
            defaultConfig: {
              type: 'object',
              properties: {},
              additionalProperties: true
            },
            defaultConfigPath: {
              type: 'string'
            }
          },
          required: ['defaultConfigPath']
        },
        {
          additionalProperties: false,
          type: 'object',
          properties: {
            defaultConfigPackageKey: {
              type: 'string'
            }
          },
          required: ['defaultConfigPackageKey']
        }
      ]
    }
  }
};

const DATA_TO_VALIDATE = `{
  "stuff": "... we don't care about",
  "processor":{
    "defaultConfigPath": true
  }
}`;

const validate = new Ajv().compile(SCHEMA)

const data = JSON.parse(DATA_TO_VALIDATE);
if (!validate(data) && validate.errors) {
  console.log(
    betterAjvErrors(validate.schema, data, validate.errors, { json: DATA_TO_VALIDATE })
  );
}