imbrn/v8n

All causes = null when validating v8n().schema using testAll

maxgalbu opened this issue · 5 comments

function recursiveFindCause(validationError, parentField = "") {
        for (const cause of validationError.cause) {
            if (Array.isArray(cause.cause)) {
                recursiveFindCause(cause, cause.target);
            } else {
                console.log("%s%s cause: %s", parentField ? parentField + "." : "",
                    cause.target, JSON.stringify(cause.cause));
            }
        }
    }

const validJsonSchema = v8n().schema({
            placement_code: v8n().string(),
            section_code: v8n().string(),
            device_code: v8n().string(),
            country_code: v8n().string(),
            language_code: v8n().string(),
            ad_blocker: v8n().boolean(),
            params: v8n().schema({
                token: v8n().string(),
                currency_code: v8n().string(),
                user_country_code: v8n().string(),
                user_id: v8n().string(),
                check_in_date: v8n().string(),
                check_out_date: v8n().string(),
                destination: v8n().string(),
                destination_latitude: v8n().string(),
                destination_longitude: v8n().string(),
                rooms: v8n().array().every.schema({
                    adults: v8n().number(),
                    children: v8n().number(),
                }),
                cbuster: v8n().string(),
                custom_fields: v8n().schema({})
            }),
            is_test: v8n().boolean(),
            partner: v8n().schema({
                name: v8n().string(),
                iframe_blocked: v8n().passesAnyOf(v8n().number(), v8n().boolean()),
                network_code: v8n().string(),
                code: v8n().string(),
                net_value: v8n().string(),
                virtual_value: v8n().string(),
            }),
            client_code: v8n().string(),
        });

        const validationErrors = validJsonSchema.testAll(json);
        if (validationErrors.length) {
            recursiveFindCause(validationErrors[0]);
        }

When json is { "placement_code": "hello", "device_code": true, "params": 1 }, this code outputs:

[Thu Oct 29 2020 08:01:46] [LOG]    section_code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    device_code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    country_code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    language_code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    ad_blocker cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.token cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.currency_code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.user_country_code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.user_id cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.check_in_date cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.check_out_date cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.destination cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.destination_latitude cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.destination_longitude cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.rooms cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    params.cbuster cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    is_test cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    partner.name cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    partner.iframe_blocked cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    partner.network_code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    partner.code cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    partner.net_value cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    partner.virtual_value cause: null
[Thu Oct 29 2020 08:01:46] [LOG]    client_code cause: null

What's wrong with that code?

imbrn commented

Hey @maxgalbu

Actually, there's nothing wrong with your code.

The cause of a ValidationError is just an Error instance, that could happen during the validation process. What's happening, in this case, is that you are recursively going to the leaf cause, and at that level there's no value for the cause in a normal validation process (without excpetions). You can check the https://imbrn.github.io/v8n/api/#validationerror for more details about that.

But in your case, maybe you can use the cause.rule.name to see what validation rule has failed.

Hi @imbrn,
seeing what validation rule has failed could help, but I'd like to know why it failed, e.g. params is an integer instead of an object. Something that can help a person in spotting the issue.

The rule could tell me params should be an object but then i need to go and see the type of params myself in order to see what's wrong...

While I see what you're saying, @maxgalbu, v8n doesn't actually check that schema properties are objects. The validation doesn't fail because params is 1, but rather because params.tokens is undefined.

What would happen if params.token is integer instead of string? would that result in cause != null?

Well one of the nested causes you get with that would be this:

...
[
  {
    "rule": {
      "name": "string",
      "args": [],
      "modifiers": []
    },
    "value": 1,
    "cause": null,
    "target": "token"
  }
]
...

To explain the above: The cause here is null, because this error is not caused by some other error. This error described exactly what went wrong with the target. The target key token was validated using a rule called string. Its value was 1. There is no further cause to this, but you are trying to display this error's cause in your function. This is already the root cause. In words: There was an error validating the value 1 for the key 'token' using the string rule.

So you do get the information that it validated a string with a value of 1 and failed. But that is only one of the reasons it fails. A schema validation works like an interface, as I mentioned in #179, so all the keys defined in it must match whatever validation is given. You don't define any keys as optional, so validating your large schema against a very small subset of it will cause lots of errors and nested errors. v8n never gives any error messages, it only gives ValidationError objects, which you can interpret for error messages. If the error above would be the only one, it would be easy to tell that the reason for a failure was an integer for a string validation.

I'm not sure what you're asking for then. You have lots of non-null causes, but the inner most nested causes will always be null, because the root cause obviously doesn't have another cause.