I'm trying to set a default value of a property based on a global context passed through at validate
and an existing property of the object being validated. I just can't seem to use the value of the property of the object as a key for the global context.
const context = {
letters: {
a: 1,
b: 2,
c: 3,
},
};
const s = Joi.object().keys({
letter: Joi.string().valid(...['a', 'b', 'c']),
num: Joi.number().default(Joi.expression('{$letters}.{letter}'))
})
const test = {
letter: 'a',
}
console.log(s.validate(test, { context }));
Simple example above. I have an object called letters
that I place into the context. The schema will look for letter
, then try to set a default value for num
using the global letters as the object to pull from and the letter passed in as the key. The closest I can get is { value: { letter: 'a', num: '[object Object].a' } }
Some of the below examples that would work
Joi.expression(`{$letters[.a]}`)
Joi.expression(`{{$letters.b}}`)
Joi.expression(`{{$letters[.c]}}`)
But the problem here is we can't use variable like letter
in place of a or b or c.
But we can achieve the what is required with custom function
// https://stackoverflow.com/a/6491621/14475852
Object.byString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
for (var i = 0, n = a.length; i < n; ++i) {
var k = a[i];
if (k in o) {
o = o[k];
} else {
return;
}
}
return o;
};
function customEvaluator(expression) {
return function(parent, helpers) {
let global = expression[0] == '$';
let exp = '';
for (var i = global ? 1 : 0, n = expression.length; i < n; ++i) {
if (expression[i] == '#') {
let part = expression.slice(i + 1);
let end = -1;
[' ', '.', '[', ']', '{', '}'].forEach(i => {
let tmp = part.indexOf(i);
if (end == -1 && tmp != -1) {
end = tmp;
}
});
end = end == -1 ? part.length : end;
exp += parent[part.slice(0, end)];
i += end;
} else {
exp += expression[i];
}
}
return Object.byString(global ? helpers.prefs.context : parent, exp);
};
}
const Joi = joi; // exported as joi in the browser bundle
const s = Joi.object().keys({
letter: Joi.string().valid(...["a", "b", "c"]),
num: Joi
.number()
.default(customEvaluator('$letters.#letter')),
});
const context = {
letters: {
a: 1,
b: 2,
c: 3,
},
};
console.log(s.validate({ letter: "a" }, { context }));
console.log(s.validate({ letter: "b" }, { context }));
console.log(s.validate({ letter: "a", num: 4 }, { context }));
<script src="https://cdn.jsdelivr.net/npm/joi@17.4.2/dist/joi-browser.min.js"></script>