Search code examples
javascriptjoihapi

Hapi Joi - validate concatenated keys


I have a request body like this one:

bod = {
file: '123654',
extension: 'bbb',
...
}

I'm using hapi joi to validate it's fields:

let valSchema = {};
valSchema.play = Joi.object().keys({
    file: Joi.string().required().message("Invalid name. Please check documentation."),
    extension: Joi.string().empty().required().message("No extension sent. check the docs..."),
});

module.exports.valSchema = valSchema;

The problem is that one of the validations needs to check if the "$file:$extension" is in an array such as:

validConcatenations = [
    "1111:aaa","111123:bbb", "12312:aab",
    "21323:zzzs" ... ];

I would need to do this:

if (data.file && settings.fileWithExtensions.indexOf(`${data.file}:${data.extension}`) < 0) {
        return errors.invalidSampleRate;
    }

using joi...Is this possible using it? using with?


Solution

  • It is possible, all you need to do is a custom validation as the example below.

    VERSION 16.1.8 or greater

    'use strict';
    
    const Joi = require('@hapi/joi');
    
    const customValidation = function (value, helpers) {
        const validConcatenations = [
            "1111:aaa", "111123:bbb", "12312:aab", "21323:zzzs"];
        if (!validConcatenations.includes(`${value.file}:${value.extension}`)) {
            throw Error(`Wrong concatenation ${value.file}:${value.extension}`)
        }
        return value;
    };
    
    const schema = Joi.object().keys({
        file: Joi.string().required(),
        extension: Joi.string().empty().required(),
    }).custom(customValidation);
    
    const wrongData = { file: '123654', extension: 'bbb' };
    const niceData = { file: '1111', extension: 'aaa' };
    
    console.log(schema.validate(wrongData));
    console.log(schema.validate(niceData));
    

    Outputs:

    { value: { file: '123654', extension: 'bbb' },
      error: 
       { ValidationError: "value" failed custom validation because Wrong concatenation 123654:bbb
         _original: { file: '123654', extension: 'bbb' },
         details: [ [Object] ] } }
    { value: { file: '1111', extension: 'aaa' } }
    

    Looking at the docs it is possible to do the below solution since version 12.1.0

    VERSION 12.1.0 or greater

    'use strict';
    
    const _Joi = require('@hapi/joi');
    
    const custom = {
        base: _Joi.object(),
        name: 'custom',
        language: {
            concatenation: 'is a invalid concatenation',
        },
        rules: [
            {
                name: 'concatenation',
                validate(params, value, state, options) {
                    const validConcatenations = [
                        "1111:aaa", "111123:bbb", "12312:aab", "21323:zzzs"];
                    if (!validConcatenations.includes(`${value.file}:${value.extension}`)) {
                        return this.createError('custom.concatenation', {v: value}, state, options);
                    }
                    return value;
                }
            }
        ]
    };
    
    const Joi = _Joi.extend(custom);
    
    const schema = Joi.custom().keys({
        file: Joi.string().required(),
        extension: Joi.string().empty().required(),
    }).concatenation();
    
    const wrongData = { file: '123654', extension: 'bbb' };
    const niceData = { file: '1111', extension: 'aaa' };
    
    console.log(schema.validate(wrongData));
    console.log(schema.validate(niceData));
    

    Outputs:

    { error: 
       { ValidationError: "value" is a invalid concatenation
        at Object.exports.process (/tmp/test/node_modules/@hapi/joi/lib/errors.js:202:19)
        at type._validateWithOptions (/tmp/test/node_modules/@hapi/joi/lib/types/any/index.js:762:31)
        at type.validate (/tmp/test/node_modules/@hapi/joi/lib/types/any/index.js:796:21)
        at Object.<anonymous> (/tmp/test/index.js:36:20)
        at Module._compile (module.js:653:30)
        at Object.Module._extensions..js (module.js:664:10)
        at Module.load (module.js:566:32)
        at tryModuleLoad (module.js:506:12)
        at Function.Module._load (module.js:498:3)
        at Function.Module.runMain (module.js:694:10)
         isJoi: true,
         name: 'ValidationError',
         details: [ [Object] ],
         _object: { file: '123654', extension: 'bbb' },
         annotate: [Function] },
      value: { file: '123654', extension: 'bbb' },
      then: [Function: then],
      catch: [Function: catch] }
    { error: null,
      value: { file: '1111', extension: 'aaa' },
      then: [Function: then],
      catch: [Function: catch] }