Comments (8)
Thank you @andrewhathaway for your question.
Actually, v8n
doesn't have async/promise-based validation yet.
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.
from v8n.
@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?
from v8n.
Sure, there are a few approaches that we could come up with, but as of right now...
To define an async-validation rule
v8n.extend({
usernameAvailable: () =>
username =>
// Showing that you must return a Promise, this may be given by
// another that returns a promise
new Promise(resolve => {
// Make an HTTP req, or something
const request = this.makeSomeRequest(username)
.then(exists =>
resolve(exists));
})
})
This is similar to your current API for defining rules. However it returns a Promise, and could be simplified to the following:
v8n.extend({
usernameAvailable: () =>
username =>
this.makeSomeRequest(username)
.then(exists => !exists); // Returns `true` if username exists, so flip the value to fail validation
})
Usage
We'd need to be able to distinguish between standard synchronous validations, and the new async validations. Therefore, I propose that testAsync
would be added and would run all validation methods asynchronously, returning a Promise.
v8n
.string()
.minLength(1)
.usernameAvailable()
.testAsync('andrewhathaway')
.then(valid => {});
*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. :)
from v8n.
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 Promise.all()
makes sense here, we would need to throw or reject false results, which would require a catch in the validation function the user adds or a passed Promise, which would need to be implemented in every single validation, essentially making the whole library async by default.
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 Promise.all()
won't finish unless all validations resolve and won't fail unless they actually reject.
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 Promise.all()
to never finish unless usernameAvailable
rejects.
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 Promise.all()
without an additional wrapper Promise.
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 Promise.all()
. The resolve and reject values don't really matter, since the result is really our whole interest. We would then need to somehow verify false is not in the array.
// 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.
from v8n.
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 asyncTest
we need to "wrap" no promised-based rules, maybe just using Promise.resolve
function. And we should return a Promise that resolves to true
or false
.
But for the asyncCheck
we need to return a Promise which resolves when everything is valid and rejects with the failed rule. So that we keep the API consistent.
from v8n.
I would argue we should reject to false and resolve to true, since false is basically an exception.
from v8n.
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.
from v8n.
I've made a pull request (#34) for us to discuss and review a solution. The documentation is also not completed yet.
from v8n.
Related Issues (20)
- min() and max() rules in guide HOT 1
- JSON Schema HOT 2
- Version 1.3.0 - v8n().optional() and v8n().string() bug HOT 2
- Bug: nested async rules HOT 1
- Possibility to validate in relation to other rules HOT 4
- Optimise builds depending on consumer capability HOT 5
- Alternating true/false validation on a value HOT 2
- All causes = null when validating v8n().schema using testAll HOT 5
- Schema testing against an object that extends the schema HOT 10
- Publish dist folder to git HOT 2
- Can we have a full example for async rules? HOT 2
- Improve support for CI/CD HOT 2
- Improve schema rule with dependent validations and strict mode
- Using optionality and asynchronous rules in the same chain HOT 6
- Logo has bad contrast on GitHub in dark mode HOT 6
- Doumentation does not deploy with GitHub actions
- Security concern HOT 2
- How to properly extend with typescript? HOT 2
- [Question] How to validate value of property against another property? HOT 4
- v8n import typescript - has no call signatures HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from v8n.