Skip to content

Conditional validation#658

Merged
gustavohenke merged 9 commits intomasterfrom
conditional-validation
Jun 27, 2019
Merged

Conditional validation#658
gustavohenke merged 9 commits intomasterfrom
conditional-validation

Conversation

@gustavohenke
Copy link
Copy Markdown
Member

@gustavohenke gustavohenke commented Nov 25, 2018

Closes #439.

OLD suggested API API looks like this at the moment:
checkIf(check('id').exists(), check('password').isSomething())
  • Runs validation chains as conditions
  • Runs oneOf() as condition
  • Runs any middleware as validations
  • Docs

NEW suggested API looks like this:

body('oldPassword')
  // if the new password is provided...
  .if((value, { req }) => req.body.newPassword)
  // OR
  .if(body('newPassword').exists())
  // ...then the old password must be too...
  .not().empty()
  // ...and they must not be equal.
  .custom((value, { req }) => value !== req.body.newPassword)

@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage remained the same at 100.0% when pulling b9438fa on conditional-validation into 61a36f5 on master.

2 similar comments
@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage remained the same at 100.0% when pulling b9438fa on conditional-validation into 61a36f5 on master.

@coveralls

This comment has been minimized.

@coveralls

This comment has been minimized.

@NickColley
Copy link
Copy Markdown

This is a really useful feature, thanks @gustavohenke for contributing. Hope this gets merged soon so we can remove our custom validators :)

@hicaro
Copy link
Copy Markdown

hicaro commented Mar 8, 2019

Very neat! Thanks for working on this @gustavohenke! Awesome job 🥇.

Does anybody know when it will be merged?

@AyushG3112
Copy link
Copy Markdown

Any estimate on a merge and a release soon please?

@abhisekp
Copy link
Copy Markdown

abhisekp commented Apr 23, 2019

Can this solve the issue where I've a single schema to check for both an object in the body and an array of objects in the body by using wildcards?

e.g.
I've a schema for a single object and I extend it to check for array of objects using the following function. I'm checking it using oneOf([checkSchema(schema), checkSchema(arraySchema)]);. But the tragedy is it sometimes, does not work. So it could be great, if the body is checked for if array, then run the arraySchema. Otherwise, run the normal schema.

/* eslint-disable no-param-reassign */
import _ from 'lodash';

/**
 * Convert a normal schema to an array schema for express-validator schema object
 *
 * @param {Object} schema Express Validator Schema
 */
export const convertToArraySchema = schema => _.transform(
  schema,
  (result, value, key) => {
    if (/\bbody\b/.test(`${value.in}`)) {
      result[`*.${key}`] = value;
    } else {
      result[key] = value;
    }
  },
  {},
);

export default convertToArraySchema;

EDIT: This works for me 😸
It was giving me error for arraySchema because I was trying to get another property of the object using req[location]. I had to do it like this in my custom validator...

const parentObject = Array.isArray(req[location])
          ? _.get(req[location], path.replace(/\.?[^.]+$/, ''))
          : req[location];
const { eventTypes } = parentObject;

^^ this works for both body being an object and an array of object.
instead of
const { eventTypes } = req[location] which works if body is an object (not array)

@AyushG3112
Copy link
Copy Markdown

pinging again

@Hum4n01d
Copy link
Copy Markdown

Please merge this 😄

@gustavohenke gustavohenke force-pushed the conditional-validation branch from 6da0542 to 35b3c41 Compare June 24, 2019 21:11
@gustavohenke
Copy link
Copy Markdown
Member Author

gustavohenke commented Jun 24, 2019

Hello all.
This has been refactored now that version 6 is out.

The new proposal looks like this:

body('oldPassword')
  // if the new password is provided...
  .if((value, { req }) => req.body.newPassword)
  // OR
  .if(body('newPassword').exists())
  // ...then the old password must be too...
  .not().empty()
  // ...and they must not be equal.
  .custom((value, { req }) => value !== req.body.newPassword)

This might not be as powerful as the old one for now.
It is however nicer, and closer to what I'd like for express-validator's future.

@Hum4n01d
Copy link
Copy Markdown

Hum4n01d commented Jun 25, 2019 via email

@gustavohenke
Copy link
Copy Markdown
Member Author

I ended up using the validator library directly and writing the logic with if statements manually for each request. Are there any reasons to use this library instead?

Hi @Hum4n01d!
express-validator provides a nice declarative API to abstract validation and sanitisation logic away from your route handlers. It makes it easier to standardise the way you produce errors and how you report them.

You can surely achieve all of this on your own, but you will come across code that you have to maintain and test.

And one core thing to express-validator that most users don't realise: validator requires every value to be a string, so how you convert those when you are writing your own thing is something else to care.

@gustavohenke gustavohenke merged commit 378fee1 into master Jun 27, 2019
@gustavohenke gustavohenke deleted the conditional-validation branch June 27, 2019 21:17
@gustavohenke
Copy link
Copy Markdown
Member Author

👋 this has been published to v6.1.0 🚢

@gustavohenke gustavohenke mentioned this pull request Jul 29, 2019
@gwh-cpnet
Copy link
Copy Markdown

gwh-cpnet commented Aug 12, 2019

I do not think the code works as documented, at least on 6.1.1

body('oldPassword')
  .if((value, { req }) => req.body.newPassword)
  .not().empty()
  .custom((value, { req }) => value !== req.body.newPassword)

works.

But

body('oldPassword')
  .if(body('newPassword').exists())
  .not().empty()
  .custom((value, { req }) => value !== req.body.newPassword)

would raise exception on validationResult(req).throw(), if newPassword not exists. And the exception said it took newPassword existenace seriously.

@gustavohenke
Copy link
Copy Markdown
Member Author

Hey @gwh-cpnet! What values are you testing this validator with?

@gwh-cpnet
Copy link
Copy Markdown

I think it is Undefined, @gustavohenke

@gwh-cpnet
Copy link
Copy Markdown

gwh-cpnet commented Aug 12, 2019

okay. Here is my production code that let me raise the issue.

api.post([
  body('driver').exists().isIn(['sqlite', 'mariadb', 'db2']),     
  body('uid').if(body('driver').isIn(['mariadb', 'db2'])).exists().trim(),
], ...);

above would raise exception, if driver === sqlite. but

api.post([
  body('driver').exists().isIn(['sqlite', 'mariadb', 'db2']),
  body('uid').if((value, { req }) => ['mariadb', 'db2'].includes(req.body.driver)).exists().trim(),
], ...);

this one works as expected.

As you can see, what I need is uid field is only required when driver === mariadb or db2. Thank you very much for your quick response. @gustavohenke These two sections should behaivior the same, right?

@gwh-cpnet
Copy link
Copy Markdown

gwh-cpnet commented Aug 12, 2019

@gustavohenke I have made a mocha test script for my point. checkout it out: #762

@lock
Copy link
Copy Markdown

lock bot commented Oct 11, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 11, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conditional Validation

8 participants