-
Notifications
You must be signed in to change notification settings - Fork 362
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
WIP: Custom validator promises and options #1229
WIP: Custom validator promises and options #1229
Conversation
Can one of the admins verify this patch? To accept patch and trigger a build add comment ".ok\W+to\W+test." |
Can one of the admins verify this patch? |
2 similar comments
Can one of the admins verify this patch? |
Can one of the admins verify this patch? |
@slnode ok to test |
@PaddyMann thank you for the pull request. To make the review faster, could you please move the second part (Add Promise support for custom async validators) to a standalone pull request? |
Actually, if you don't mind rebasing the changes to preserve two commits, then it's ok to keep all changes in a single pull request. |
@@ -340,7 +340,20 @@ function validateCustom(attr, conf, err, options, done) { | |||
done = options; | |||
options = {}; | |||
} | |||
conf.customValidator.call(this, err, done); | |||
|
|||
if (conf.customValidator.length <= 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our usual convention in other places where we support both callback/promise based functions, is to check for the return type of the function, instead of relying on the number of arguments.
var result = conf.customValidator(...);
if (result.then && typeof result.then === 'function') {
// ...
}
My concern is that a custom validator accepting a single argument is a valid use case, see https://github.com/strongloop/loopback-datasource-juggler/pull/1229/files#diff-a1a277b8e694ce811867b2ac3e064ff8R626
User.validate('email', function(err) {
if (this.email === 'hello') err();
}, {code: 'invalid-email'});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually: when defining a validator, user can provide sync
flag to tell the framework whether the validator function is sync or async. Perhaps we can add another flag, e.g. promise
to distinguish between "old" custom validators using callbacks and the new Promise-based flavour?
Just an idea to consider.
if (conf.customValidator.length <= 1) { | ||
conf.customValidator.call(this, options) | ||
.then((isValid) => { | ||
if (!isValid) throw new Error(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAICT, there are several outcomes of a custom validator:
err()
is called - an error message is added by the validation frameworkerr(false)
is called - the validated object is marked as invalid, but no error message is added by the validation framework. See https://github.com/strongloop/loopback-datasource-juggler/pull/1229/files#diff-a1a277b8e694ce811867b2ac3e064ff8L638done()
is called - the property value is validdone(truthy)
is called - same as callingerr()
I would like to preserve these options in the Promise mode too, i.e.
- Indicate that the validation failed - items 1 and 4 above
- Indicate that the validation failed and the error message was already inserted - item 2 above
- Indicate that the validation passed
I think the cleanest solution is to expect the promise to resolve with a structured object instead of a single true/false value, although we can support boolean resolution to make simple cases simple to implement.
// the value is valid
return Promise.resolve();
// the value is invalid, a message should be added by the framework
return Promise.reject();
return Promise.reject(err);
throw unhandledError;
// the value is invalid, a message was already added by the validator
return Promise.reject({ addMessage: false });
Here is a code snippet illustrating a possible implementation of my proposal:
var result = conf.customValidator(...);
if (result.then && typeof result.then === 'function') {
result.then(
function onSuccess() { done(); },
function onFailure(reason) {
if (reason instanceof Error) {
done(err);
} else if (reason && reason.addMessage !== false) {
err(false);
} else {
err();
}
})
}
Thoughts?
done(); | ||
}); | ||
} else { | ||
conf.customValidator.call(this, err, done, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think passing the options after the done
callback is rather unusual, since the Node.js convention is to pass the callback function as the last arg.
I am proposing the following change:
if (conf.customValidator.length !== 4) {
// backwards compatibility with custom validators not accepting options
conf.customValidator.call(this, err, done);
} else {
conf.customValidator.call(this, options, err, done);
}
Thoughts?
I am proposing to add two tests - one for a sync custom validator, another for an async custom validator. Please add also few more tests for Promise-based validators, 1) to cover the different validation outcomes I described in my comment above, 2) to verify that options are passed through to the validator. |
Thanks @bajtos for the review - really appreciated and a few lessons I'll apply to any future PRs. I'll act on your suggestions :) |
@PaddyMann ping, what's the status of this pull request? |
It's on my list for next week - sorry for leaving it untouched for so long. |
Hi @bajtos, Just realized this isn't a requirement for our upcoming release and we're working to tight deadline, so I'm going to have to park this - sorry! I'll be happy to address it as and when we need the feature implemented. Paddy |
@PaddyMann no worries! Let's close this pull request for now, feel free to reopen it if/when you will need this feature in the future. |
cool - will do |
FYI, I've observed some discussions in the gitter channel of people requiring this fix |
Description
@bajtos recently added support for passing context through to remote methods, hooks, etc.
While the options were passed as far as
isValid
and evenvalidateCustom
, they weren't then being passed onto a user-defined custom validator function.This PR seeks to:
options
argument toconf.customValidator.call(this, err, done);
invalidations.js
options
which is a bug)I'm not clear on what tests are needed for passing through the
options
, and would appreciate any help here.Related issues
Checklist
guide