ajv-validator / ajv-keywords

Additional JSON-Schema keywords for Ajv JSON validator
https://ajv.js.org
MIT License
257 stars 49 forks source link

uniqueItemProperties on non-required keys fails with multiple items with undefined keys #115

Open JamesJansson opened 4 years ago

JamesJansson commented 4 years ago

Using uniqueItemProperties: ['id'] where id is not required should (in my opinion, maybe it should be a separate function) allow multiple elements that have undefined id values. e.g.

[{id: 'a', val: 1}] - should pass

[{id: 'a', val: 1}, {id: 'b', val: 2}] - should pass

[{id: 'a', val: 1}, {id: 'a', val: 2}] - should fail

[{id: 'a', val: 1}, {val: 2}] - should pass

[{val: 1}, {val: 2}] - should pass (but doesn't under current uniqueItemProperties)

I have created a runkit here (code below as well): https://runkit.com/jamesjansson/ajv-uniqueitemproperties-on-non-required-keys

const Ajv = require("ajv");
const ajvKeywords = require("ajv-keywords")

function validateJson(json, schema) {
  const ajv = new Ajv({
    verbose: true,
    allErrors: true,
    useDefaults: true,
    format: 'full',
    $data: true,
  });
  ajvKeywords(ajv);
  const validate = ajv.compile(schema);
  const isValid = validate(json);
  return {
    isValid,
    errors: validate.errors,
  };
}

const jsonSchema = {
  type: 'array',
  uniqueItemProperties: ['id'],
  items: {
    type: 'object',
    properties: {
      id: { type: 'string', format: 'uuid' },
      otherData: { type: 'string' },
    },
    required: ['otherData'],
    additionalProperties: false,
  },
};

console.log('it should succeed with a single item with no id')
const testData = [
    {
      otherData: 'stuff',
    },
];
const validation1 = validateJson(testData, jsonSchema);
console.log(validation1)

console.log('it should succeed with a two items with no id')
const testData2 = [
    {
        otherData: 'stuff0',
    },
    {
        otherData: 'stuff1',
    },
];
const validation2 = validateJson(testData2, jsonSchema);
console.log(validation2)
Sumbersss commented 4 years ago

I join you on your comment, uniqueItemProperties should not behave like a required, only check if item is uniq, if not present it should pass

epoberezkin commented 4 years ago

What is the use case for this behaviour?

Sumbersss commented 4 years ago

I have the same issue : I have an array of objects which contain some properties required and some properties that must be uniq. If an uniq property isn't present in the object the validation fails. It shouldn't, uniq property is different than required property.

epoberezkin commented 4 years ago

As I wrote, before changing or extending the current behaviour, it would help to understand what is the real life data scenario when such data model is needed. This feature is created to validate the list of objects with unique IDs, such as database IDs, for example - they would be required properties. What is the use case for having unique non-required properties in the list of objects?

Also, the analogous keyword "uniqueItems" would not validate non-unique "missing" items (missing items in JSON would be nulls - no other way you can represent it in JSON - and the validation would fail). So following this logic, the current behaviour makes more sense - you can only have one object where property that has to be unique is missing.

In any case you can define a slightly modified version of these keyword in your code using this implementation as a starting point.

Sumbersss commented 4 years ago

I get what you mean, indeed it makes sense if you compare this behaviour with database validation.

JamesJansson commented 4 years ago

This functionality would be useful in our system. We never let the user create ids, but we may need ways for our FE to determine exactly what item has been updated. As such our API allows an optional tempId field that allows the FE to keep track of which item is being updated and referenced before an entry and its ID is created in the BE.

mebibou commented 3 years ago

I'm working on a project where we use JSON files for storing our data. Some data contains arrays where we need to make sure the items are unique based on some keys, but those keys are not always specified, in which case only the present keys should be used to validate the item, which is quite logic to me. At the very least there should be a uniqueRequiredItemProperties along that should do what the current one does, shouldn't it?

nunico commented 3 years ago

We also have use cases where a user-defined object can have optional properties which need to be unique.

Best example: User-Directory sync. A client wants to onboard their users to a service and sync those user-data. Some clients might want to provide an external reference with their data to provide a mapping.

Also, the example in the documentation won't work either.

o-nix commented 1 year ago

My use-case: workflow steps. They might be "just" code definitions on their own, but sometimes they might have IDs, which must be unique, this way you can refer to a previous step in one of the next steps. TL;DR: the property itself is not required, but must be unique when present.

sapzs commented 7 months ago

Another usecase: We validate loadbalancer rules, which optionally include a priority. IF the priority is given, it must be unique across the LB. It is optional, so when no value is given, our backend generates priorities based on the rule order.