Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

joi.valid with object or array #1348

Closed
rightaway opened this issue Nov 4, 2017 · 12 comments
Closed

joi.valid with object or array #1348

rightaway opened this issue Nov 4, 2017 · 12 comments
Assignees
Labels
support Questions, discussions, and general support

Comments

@rightaway
Copy link

rightaway commented Nov 4, 2017

I want to validate that a value is exactly equal to something. But if I do

joi.valid([
  { a:1, b:2 },
  { a:3, b:4 },
])

it doesn't match that the object exactly equals the array of objects. With arrays, doing joi.valid([1, 2, 3 ]) checks that the values can be either 1, 2, or 3, rather than verifying that it's exactly equal to [1, 2, 3]. How can I make this work?

Could some kind of joi.deepEqual be added? It's useful for cases where let's say you're receiving something back from a JSON API and need to make sure that some of the keys meet some criteria, but other keys need to exactly equal something.

@WesTyler
Copy link
Contributor

WesTyler commented Nov 5, 2017

other keys need to exactly equal something.

Typically this can just be built out explicitly using object().keys(), with the .valid() values set on the keys themselves.

For your array of objects example above, you should be able to apply the same idea to .array(). Not sure if this meets the edge-case needs of your actual use case, but maybe it's a quick starting point:

const obj1Schema = Joi.object().keys({
    a: Joi.number().valid(1).required(),
    b: Joi.number().valid(2).required()
});
const obj2Schema = Joi.object().keys({
    a: Joi.number().valid(3).required(),
    b: Joi.number().valid(4).required()
});

const arraySchema = Joi.array().items([obj1Schema, obj2Schema]).unique().length(2);

arraySchema.validate([{a: 1, b: 2}]); // ValidationError: "value" must contain 2 items
arraySchema.validate([{a: 1, b: 2}, {a: 3, b: 5}]); // ValidationError: "value" at position 1 does not match any of the allowed types
arraySchema.validate([{a: 1, b: 2}, {a: 1, b: 2}]); // ValidationError: "value" position 1 contains a duplicate value
arraySchema.validate([{a: 1, b: 2}, {a: 3, b: 4}]); // error: null

In other words, I think usually if the problem can be solved with schema composition then we do not need to add new complexity to the Joi core api. I believe this falls into that category. :)

@WesTyler WesTyler added the support Questions, discussions, and general support label Nov 5, 2017
@WesTyler WesTyler self-assigned this Nov 5, 2017
@rightaway
Copy link
Author

rightaway commented Nov 5, 2017

Thanks, this gets me started!

I would like to suggest that deepEqual functionality would be very useful in core, because when wanting to assert that something exactly equals something else, the above solution is verbose. It would be great to see at a glance that something needs to equal exactly something, with the same keys and the same order and uniqueness and everything. If anything differs at all then the deepEqual check fails.

The following is a very concise way of asserting what is needed, and is very readable and understandable.

schema = joi.equals([
    { a: 1, b: 2 },
    { a: 3, b: 4 },
    { a: 5, b: 6 },
  ]),
})

While the following works but is less readable. And this is a fairly simple case. In my use cases I'll have larger and more complex objects I need to assert, which would lead to a lot of verbosity in the code.

schema = joi.array().items([
  joi.object().keys({
    a: joi.number().valid(1).required(),
    b: joi.number().valid(2).required(),
  }),
  joi.object().keys({
    a: joi.number().valid(3).required(),
    b: joi.number().valid(4).required(),
  }),
  joi.object().keys({
    a: joi.number().valid(5).required(),
    b: joi.number().valid(6).required(),
  }),
]).unique().length(2)

@Marsup
Copy link
Collaborator

Marsup commented Nov 6, 2017

Are you using it like assertion framework in your tests? Why not use tools made for that?

@hueniverse
Copy link
Contributor

First, you can use literal values. A schema of { a: 3 } will be compiled into Joi.object({ a: Joi.valid(3) }). Second, you can override the default to make all keys required with the presence option.

@rightaway
Copy link
Author

@hueniverse Where can you use that? I can't do so in valid(). And I can't use arrays of objects there either.

@Marsup It's not for tests, it's for ensuring that certain values returned from APIs exactly match various values, since some keys are fixed (based on the inputs), and the others change. However, if deepEqual functionality were available then an additional use case for joi could also for test assertions.

@WesTyler
Copy link
Contributor

WesTyler commented Nov 6, 2017

Where can you use that?

It's a shorthand for the .keys() notation

Joi.object().keys({a: 1}) is the same as Joi.object().keys({ a: Joi.number().valid(1) })

i just didn't use that notation in my original example because I didn't want to introduce another notation into the mix :P

A short version of the example above would be

schema = joi.array().items([
  joi.object().keys({
    a: 1,
    b: 2,
  }),
  joi.object().keys({
    a: 3,
    b: 4,
  }),
  joi.object().keys({
    a: 5,
    b: 6,
  }),
]).unique().length(2).options({ presence: 'required' })

@Marsup
Copy link
Collaborator

Marsup commented Nov 6, 2017

https://github.com/hapijs/joi/blob/master/API.md#compileschema contains a comprehensive example of all the cases supported.

@rightaway
Copy link
Author

@WesTyler Using that doesn't enforce that it's an exact match, in terms of order for example.

@Marsup The compile schema option interprets arrays as alternatives, rather than literal arrays, which is why a new deepEqual functionality would add something useful.

@Marsup
Copy link
Collaborator

Marsup commented Aug 3, 2018

This issue seems old enough that it's dead or solved by now, closing. Reopen if you still want to discuss it.

@Marsup Marsup closed this as completed Aug 3, 2018
@rightaway
Copy link
Author

Hi, I'd like to reopen the issue but can't do it since I wasn't the one who closed it.

I still think having some kind of deepEqual functionality would be very useful. I'm using joi to assert most things, and then I have to separately assert that for example an array exactly equals something else, which isn't a clean solution. It would be nice to have joi handle all the assertions I want to make.

@Marsup
Copy link
Collaborator

Marsup commented Aug 30, 2018

How about you implement it in an extension?

@lock
Copy link

lock bot commented Jan 9, 2020

This thread has been automatically locked due to inactivity. Please open a new issue for related bugs or questions following the new issue template instructions.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
support Questions, discussions, and general support
Projects
None yet
Development

No branches or pull requests

4 participants