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
Support for Async Validations #25
Comments
Thank you @andrewhathaway for your question. Actually, I've been thinking about that, and this is a really nice feature I want to include as soon as possible. I'm trying to figure out a way to implement that without increasing too much size in the final release file and keep the same simple fluent API. |
@andrewhathaway What kind of syntax would you like for it? Could you provide a concrete example of a validation and the syntax you'd expect? |
Sure, there are a few approaches that we could come up with, but as of right now... To define an async-validation rule
This is similar to your current API for defining rules. However it returns a Promise, and could be simplified to the following:
UsageWe'd need to be able to distinguish between standard synchronous validations, and the new async validations. Therefore, I propose that
*Note: * To keep a similar API to what you have right now, you'd maybe need to use Proxymise internally, to handle chaining of promise calls. Saying that, because validation rules are functions that return functions, you probably could get away without it. :) |
No idea how this would be implemented properly. I've been experimenting, but no solution is ideal. Ideally an async validation should be written like this, where it simply returns the result: v8n.extend({
usernameAvailable: () => username =>
asyncUsernameFunction(username).then(exists => !exists)
}); All the validations should happen parallel, since order doesn't matter. But the issue with that is that we can't evaluate the result of the async function at validation time and therefore not properly reject or resolve it. Since Disregarding inverting the result for the moment, this would be a simple Promise wrapping function for async testing. Of course this would rely on validations to use reject and resolve, since testAsync(value) {
const rules = this.chain.map(rule => {
return new Promise((resolve, reject) => {
resolve(rule.fn(value, resolve, reject));
});
});
return Promise.all(rules);
}, v8n.extend({
usernameAvailable: () => (username, resolve, reject) =>
asyncUsernameFunction(username).then(exists => {
if (exists) reject(); // Reject if the username is taken
resolve(); // Otherwise we resolve
})
}); expect(
v8n()
.usernameAvailable()
.testAsync("random")
).resolves.toBe(undefined); // Given that username is not taken, this would PASS The above would work, if it was the only validation. Adding any of the others will cause expect(
v8n()
.string()
.usernameAvailable() // Imagine it's not taken
.testAsync("random")
).resolves.toBe(undefined); // Will never resolve since string() doesn't implement resolve() Another option might be to simply wrap the rule functions into testAsync(value) {
const rules = this.chain.map(rule => {
return rule.fn(value);
});
return Promise.all(rules);
}, This will always resolve, since even the async validations simply return either true or false. Now that means we always receive an array back from // The username is not taken, this should resolve
expect(
v8n()
.string()
.usernameAvailable()
.testAsync("random")
).resolves.toBe(undefined); // FAIL, resolves to [true, true]
// The username is taken, this should reject
expect(
v8n()
.string()
.usernameAvailable()
.testAsync("andrewhathaway")
).rejects.toBe(undefined); // FAIL, resolves to [true, false] As I understand we want to resolve when all the validations pass and reject if any fails. Sadly as per the explanation above I can't figure out a way to do that parallel. |
Actually, we do need to keep the validation in sequence, because in a case like: v8n()
.email() // Synchronous test if the email is valid
.not.exist() // Asynchronous test if email does not exists in a server
.asyncTest("this_is_not_an_email")
.then(valid => {
// valid == false
}); it should first test if the value is actually a valid email so that we can avoid sending a request to the server. For the But for the |
I would argue we should reject to false and resolve to true, since false is basically an exception. |
I'm now working on a solution for this feature, and I'm going to push that to a separated branch later, so we can discuss more the process I'm using to perform the async validations. |
I've made a pull request (#34) for us to discuss and review a solution. The documentation is also not completed yet. |
This looks like a really nice tool, however I don't see any support or documentation on async/promise-based validation.
Is this something that this project plans on supporting or is this outside of the plan? Classic use-case being username validation, for example!
The text was updated successfully, but these errors were encountered: