tgriesser / checkit

simple, flexible validations for node and the browser
MIT License
223 stars 53 forks source link

Nested Validations #76

Open nmagerko opened 8 years ago

nmagerko commented 8 years ago

I may have missed it in the documentation, but is there a way to check an object with nested objects contained within it, or is this not possible? For example, if I have an object like:

{
  "key1": "example",
  "key2": {
    "key3: "example",
    "key4": "example"
  }
}

Would I be able to write a CheckIt validator to validate the entire object at once, even the keys under key2? The only alternative seems to be to validate the contained objects individually, which would make my code less general than I had hoped.

If this is not possible, perhaps it would be a useful enhancement.

sboehler commented 8 years ago

As far as I understand, it is possible to define validations as follows:

const rules = new Checkit({
  "key2.key3": ['required', 'string'],
  "key2.key4": ['required', 'string']
});

This is not in the docs but has apparently been merged :https://github.com/tgriesser/checkit/pull/22

nmagerko commented 8 years ago

I'll try that in a bit. If it works, I hope it gets into the docs. I have custom validators doing this currently in my project, and it's not very pretty.

rhys-vdw commented 8 years ago

I hope it gets into the docs

I'll take a PR.

nmagerko commented 8 years ago

Sure. In the meantime, any idea if there's support for validating lists of objects (somewhat related to this)? I know there's an array validator, but I haven't come across anything for validating the objects inside of a list automatically. Something like:

{
  "list": [{
    "item1": "good",
    "item2": "bad"
   }]
}

I have a feeling a custom validator is the way to go for that case, though.

rhys-vdw commented 8 years ago

any idea if there's support for validating lists of objects (somewhat related to this)?

@nmagerko I'm sorry, I have only used this library once a few years ago. I'm only a collaborator because I've worked on other of @tgriesser's projects. I'm happy to help merge PR's if you find bugs or have new features that makes sense for Checkit.

sboehler commented 8 years ago

@nmagerko I recently had the same use case, validating a nested array of objects. The following code validates a 'transaction', which has a property 'entries' containing an array of entry objects. Entries again contain an array of bookings, which are again recursively validated (not shown here, but the principle is the same).

I solved it by writing a custom validation function for the objects contained in the array, which called Promise.map with a corresponding Checkit object. The catch clause is useful to translate between a CheckitError and a simple Error object, which is expected by Checkit. It would be nice if Checkit understood its own CheckitErrors when thrown by a validator...yet another opportunity for a PR I guess.

const entryValidations = new Checkit({
    bookings: [
        'required', 
        'array', 
        'exactLength:2', 
        (bookings, context) => Promise.map(bookings, b => bookingValidations.run(b, context))
        .catch(err => { throw new Error(err.toString(true)); })
    ]
});

const transactionValidations = new Checkit({
    date: ['required', x => Date.parse(x)],
    user_id: ['required', 'integer', (val, context) => context.user.admin || context.user.id === val],
    description: ['required', 'string'],
    entries: [
        'required', 
        'array', 
        (entries, context) => Promise.map(entries, e => entryValidations.run(e, context)) 
        .catch(err => { throw new Error(JSON.stringify(err.toJSON())); })
    ]
});
rahatarmanahmed commented 7 years ago

Noticed a bug in this feature, thought I'd share it here instead of making an issue since it's undocumented:

const Checkit = require('checkit')

const validator = new Checkit({
    'a.b': ['required', 'url']
})

const [err, valid] = validator.validateSync({})

console.log(err.toJSON())

This gives you:

{
    "a.b": [
         "Cannot read property 'b' of undefined",
         "Cannot read property 'b' of undefined"
    ]
}