imbrn/v8n

Test to validate that two fields of a schema are equals

alexisab opened this issue ยท 5 comments

It would be nice if we could check that two fields are equals. Something like that :

v8n().schema({
  password: v8n().string().minLength(8).maxLength(40),
  confirmPassword: v8n().equalTo('password')
})

What do you think about ?

imbrn commented

That's definitely a useful validation check. But we need to figure out a way to make it work in a clean way.

We should not couple a rule to its environment, like:

v8n().equalTo('password')

Because this would make our equalTo very tied to the schema, and we do not want something that.

What I think we could do is to make the rule schema to also work with a function as argument. That functio would take the validated object as its argument and return the schema object, like this:

v8n().schema(validatedObj => {
  password: v8n().string().minLength(8),
  confirmPassword: v8n().equal(validatedObj.password)
});

This looks much better to me. But it's definitely something we need to think about before implementing that.

It's exactly what I've done to validate my schema. I've used a function and the equal rule, it works pretty well.

I think this can be achieved with current rules very well.

@alexisab Could you provide a sample of what you did?

In fact I've just encapsulate my validation logic in a function :

in validate.js

const _ = require('lodash')
const v8n = require('v8n')

const validate = (obj, specs) => {
    try {
        specs = specs instanceof Function ? specs(obj) : specs

        const validation = v8n().schema(specs)

        validation.check(obj)

        return obj
    } catch (error) {
        const details = error.cause
            .map(c => _.pick(c, 'rule.name', 'rule.args', 'target'))
            .map(d => ({
                target: d.target,
                rule: d.rule.name,
                requirements: d.rule.args,
            }))

        throw {
            status: 400,
            details,
        }
    }
}

module.exports = { validate }

In my signin controller :

const { accessId, password } = validate(
        ctx.request.body,
        User.getValidShape(true)
    )

where User.getValidShape function is :

const getValidShape = (withConfirm = false) => {
    const baseShape = {
        accessId: v8n().string(),
        password: v8n()
            .string()
            .minLength(8)
            .maxLength(40),
    }

    if (withConfirm) {
        return obj => ({
            ...baseShape,
            confirmPassword: v8n().equal(obj.password),
        })
    }

    return baseShape
}

Seems good! Closing this for now, I don't believe this is necessary as a standalone feature.