busypeoples / spected

Validation library
MIT License
703 stars 32 forks source link

[Question] dealing with parent children relationship #100

Open davidchase opened 6 years ago

davidchase commented 6 years ago

👋this library has been awesome for validating properties both static and dynamic which is great so thanks!

my question has there been any thoughts when for example you need to find duplicates

from the docs:

const input = {
  id: 4,
  users: {
    one: {firstName: 'foobar', lastName: 'action'},
    two: {firstName: 'foo', lastName: 'bar'},
    three: {firstName: 'foobar', lastName: 'Action'},
  }
}

const validationRules = {
  id: [[ notEmpty, notEmptyMsg('id') ]],
  users: map(always(userSpec)),
}

we can easily validate the children individual properties such as firstName, lastName

but in order to find the duplicates between each users.one.firstName, users.two.firstName, etc child we need the actual users object to compare each child

the only thing i can think of is maybe updating the validationRules for users to pre-process the input and then pass the function to merge with the existing userSpec

dont know if anyone else had a similar experience so i thought i would ask

cheers

busypeoples commented 6 years ago

Hi! Thanks for the feedback! Will look into this, either today or tomorrow.

davidchase commented 6 years ago

sounds good thanks :)

busypeoples commented 6 years ago

@davidchase I will finally have the time to look into the this, this week!

davidchase commented 6 years ago

@busypeoples cool sounds good, let me know what you come up with :)

busypeoples commented 6 years ago

Here's a possible way to tackle the problem. But we probably will need something better than this in the long run.

// predicates

const notEmpty = compose(not, isEmpty);
const hasCapitalLetter = a => /[A-Z]/.test(a);
const isGreaterThan = curry((len, a) => a > len);
const isLengthGreaterThan = len => compose(isGreaterThan(len), prop("length"));
const duplicate = (all, item) => filter(i => item === i, all).length <= 1;
const noDuplicate = all => (a, data) => duplicate(all, a);
// error messages

const notEmptyMsg = field => `${field} should not be empty.`;
const minimumMsg = (field, len) =>
  `Minimum ${field} length of ${len} is required.`;
const capitalLetterMsg = field =>
  `${field} should contain at least one uppercase letter.`;

const input = {
  id: 4,
  users: {
    one: { firstName: "foobar", lastName: "barA" },
    two: { firstName: "foo", lastName: "barA" },
    three: { firstName: "foobar", lastName: "other-Input" }
  }
};

const names = pluck("lastName", Object.values(input.users));

const userSpec = {
  firstName: [[isLengthGreaterThan(5), minimumMsg("firstName", 6)]],
  lastName: [
    [hasCapitalLetter, capitalLetterMsg],
    [noDuplicate(names), "Duplicate Last Name found."]
  ]
};

const validationRules = {
  id: [[notEmpty, notEmptyMsg("id")]],
  users: map(always(userSpec))
};

spected(validationRules, input);

https://codesandbox.io/s/1v69583wx7

davidchase commented 6 years ago

yeah thats a good one, i tried it out with a multimap structure in order to avoid passing in the context twice. so i compose the noDuplicates with a simple hash + array multimap.