Michael Scott
Michael Scott

Reputation: 589

Validating with dynamic context objects in Joi

I am needing to use a dynamic object for validation of a value. The object changes regularly so I am downloading it at runtime and saving it locally as a well-formed .json file. I need to feed these values into a call to Joi.validate (via the 'context' option) and verify that the items in an array match one of the key/value pairs in the context object.

// the defined schema
const schema = Joi.object().keys({
  'foo': Joi.array()
    .unique()
    .items(
      // these items need to match the keys/values from the context object
      Joi.object().keys({
        id: Joi.string()
          .required(), // this needs to be a key from the context object
        name: Joi.string()
          .required(), // this needs to be the value from the context object for the key defined by the above id property
      }),
    )
})

// the .json file with the context object looks as such
{
  "id of first thing": "name of first thing",
  "id of second thing": "name of second thing",
  ...
}

// validation is done like this:
Joi.validate(theThingsToValidate, schema, {context: objectFromTheJsonFile});

Upvotes: 0

Views: 3695

Answers (1)

Bali Balo
Bali Balo

Reputation: 3408

You could build your schema dynamically every time you detect that your file has changed. I do not think you need the context option in this case unless I am misunderstanding your issue.

In my sample code each of the elements to validate in the foo array are in the format

{
    id : 'id of thing',
    name: 'name of thing'
}

and must match one of the elements in the file in this format

{
  "id of first thing": "name of first thing",
  "id of second thing": "name of second thing",
  ...
}

Transforming the data in the file to build a list of all valid objects and passing it to Joi.array().items should correctly achieve what is expected.

const fs = require('fs');
const path = require('path');

let schema;
let lastUpdate = 0;

// Assumes your file exists and is correctly json-formatted
// You should add some error handling
// This might also need to be tweaked if you want it to work in an asynchronous way
function check(theThingsToValidate) {
    let jsonPath = path.resolve(__dirname, 'data.json');
    let fileStats = fs.statSync(jsonPath);
    if (!schema || fileStats.mtimeMs > lastUpdate) {
        lastUpdate = fileStats.mtimeMs;
        let fileData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
        schema = Joi.object().keys({
            'foo': Joi.array().unique().items(
                ...Object.keys(fileData).map(key => ({
                    id: key,
                    name: fileData[key]
                }))
            )
        });
    }

    return Joi.validate(theThingsToValidate, schema);
}

Note: mtimeMs has been added in Node 8, so you might need to change it if you are running a lower version.

Upvotes: 1

Related Questions