Jascript removes value of Joi class - javascript

I'm using Typescript and have this, I'm trying to create Joi validation object.
Example input :
{
key: 'name',
validate: Joi.required().options({
language: {
any: {
required: 'is required'
},
string: {
min: 'must be at least 3 Characters'
},
},
}),
}
And a method :
getObjects(inputs: Array<Input>) {
const newObject = {};
inputs.forEach(input => {
newObject[input.key] = input.validate;
});
return newObject;
}
But when checking newObject, it's only keys, and validation is undefined.
I realise this has something to do with the loop because this works.
newObject[inputs[0].key] = inputs[0].validate;
newObject[inputs[1].key] = inputs[1].validate;

Related

Yup validation with dynamic keys in an object

I have an object for validation that might look like this:
const exampleObject = {
foo: {
entries: {
'785bac64-c6ce-4878-bfb8-9cf5b32e2438': {
name: 'First object',
},
'117450da-315b-4676-ad23-edd94a4b6b51': {
name: 'Second object',
},
},
},
}
The keys of the entries object are dynamic (uuids). I want to validate that the name property in any of those objects is not an empty string. However, entries is not required, it is only required that if there are any entries, they cannot contain an empty string. How can I do this with Yup?
const exampleObjectValidation = Yup.object().shape({
foo: Yup.object({
entries: Yup.object({
/* what goes here ? */
})
})
})
Here's how I did it:
const exampleObjectValidation = Yup.object()
.shape({
foo: Yup.object({
entries: Yup.lazy((value) => {
if (!isEmpty(value)) {
const validationObject = { name: Yup.string().required('Item cannot be empty') }
const newEntries = Object.keys(value).reduce(
(acc, val) => ({
...acc,
[val]: Yup.object(validationObject),
}),
{}
)
return Yup.object().shape(newEntries)
}
return Yup.mixed().notRequired()
}),
}),
})
.nullable()
.notRequired()

Joi, add extension type with argument

I want to add splitArray type to Joi which converts string to an array using method Array.split. As you know, Array.split can split by argument, and I want to pass this argument during creation of the schema and not during it's usage.
So, currently I have this extension that allows me to split string to an array during validation with the use of helpers:
const splitArrayExt = joi => ({
base: joi.array(),
coerce: (value, helpers) => ({
value: value.split ? value.split(helpers.prefs.split ? helpers.prefs.split : /\s+/) : value
}),
type: 'splitArray',
)}
The problems is - I have to pass { split: <ANOTHER SPLIT ARG> } every time I call validate.
Is it possible to just do something like this:
const JoiBase = require('joi');
...
const Joi = JoiBase.extend(splitArrayExt);
const schema1 = Joi.splitArray().splitBy('|').items(Joi.string())
const schema2 = Joi.splitArray().splitBy(',').items(Joi.string())
schema1.validate('A|B,C') // values: [ 'A', 'B,C' ]
schema2.validate('A|B,C') // values: [ 'A|B', 'C' ]
So, anyway, I've figured it out:
const customJoi = Joi.extend(joi => ({
type: 'splitArray', // add type with custom name
base: joi.array(), // let's base it on Joi.array
messages: { // this is required to yell if we didn't get a string before parse
'split.base': '"value" must be a string',
},
coerce(value, helpers) { // this executes before 'validate' method is called, so we can convert string to an array
if (typeof(value) !== 'string') {
return { errors: helpers.error('split.base') };
}
const rule = helpers.schema.$_getRule('splitBy'); // this is needed to determent delimiter (default: /\s+/)
return { value: value.split(rule ? rule.args.delimiter : /\s+/) };
},
rules: {
splitBy: { // add new rule to set different delimiter from default
convert: true,
method(delimiter) {
return this.$_addRule({ name: 'splitBy', args: { delimiter } });
},
},
}
}));
...
// and voilà
customJoi.splitArray().items(customJoi.number()).validate('1 2\n3\t4')
// [ 1, 2, 3, 4 ]
customJoi.splitArray().splitBy('|').validate('A|B,C');
// [ 'A', 'B,C' ]
customJoi.splitArray().splitBy(',').validate('A|B,C');
// [ 'A|B', 'C' ]

Filter an object properties by the value of a property

for example i have an object with validation rules
validationRules = {
planType: {
group: 'personalData',
required: true,
messages: {
required: 'Required Field'
}
},
name: {
group: 'personalData',
required: true,
pattern: /\w+\s+\w+/,
messages: {
required: 'Required Field',
pattern: 'Insert first and last names'
}
},
}
I need to validate a form wizard by steps so I need to make a function just to validate each step
function isStepValid() {
console.log(lastActiveNav);
const currentStep = lastActiveNav.getAttribute('data-pane');
var stepRules = validationRules.filter(currentStep); // wont work cause not an array
console.log(stepRules); // this is the value in the group property in the validationRules
for (let key in validationRules) {
}
}
I want to loop only through the properties with the value in the group property that match. Sadly i can only find an answer using an array with filter.
const currentStep = lastActiveNav.getAttribute('data-pane');
Object.keys(validationRules)
.map(key => validationRules[key])
.filter(validationRule => validationRule.group === currentStep)
.forEach(validationRule => { // forEach/map/reduce depending what you want to do
// Code here
})

How can i check if object is following exactly a certain type format in javascript

I have to check if some items object follow a certain format as below. These items are input to a component and I want to check the validity of the input.
I already wrote some code to check for the validity of the items, but I want to know if there could be a better way to write it?
Thanks!
{
main: {
id: string,
name: string,
},
drilldowns: [
{
id: string,
name: string,
elements: [
{
id: string,
name: string,
}
],
}
],
}
export const isValidItem = (item) => {
if (!item.main || (item.main && !item.main.id))
return false;
if (item.drilldowns) {
const invalidDrilldowns = item.drilldowns.filter(drilldown => {
const invalidDrilldownElements =
drilldown.elements &&
drilldown.elements.filter(element => {
return !element.id;
});
return (
!drilldown.id &&
!drilldown.elements &&
invalidDrilldownElements.length !== 0
);
});
return invalidDrilldowns.length === 0;
}
return true;
};

Extend Joi with a custom type for populated strings

I want to create a custom Joi type for populatedStrings by using .extend(..) to create a type based on joi.string() which:
Trims the string
Changes the value to undefined if the trimmed string === '' so the validated output won't contain the key at all
Overrides .required() so it acts on the trimmed string and creates errors using my own language. When .required() is set on my type it means that it requires a string which does not only contain white space or is empty
My attempt so far which is close:
const StandardJoi = require("joi");
const Joi = StandardJoi.extend(joi => ({
base: joi.string(),
name: "populatedString",
language: {
required: "needs to be a a string containing non whitespace characters"
},
pre(value, state, options) {
value = value.trim();
return value === "" ? undefined : value;
},
rules: [
{
name: "required",
validate(params, value, state, options) {
if (value === undefined) {
return this.createError(
"populatedString.required",
{ v: value },
state,
options
);
}
return value;
}
}
]
}));
Examples of it working
Joi.populatedString().validate(" x "); // $.value === 'x'
Joi.populatedString().validate(" "); // $.value === undefined
// $.error.details
//
// [ { message: '"value" needs to be a a string containing non whitespace characters',​​​​​
// ​​​​​ path: [],​​​​​
// ​​​​​ type: 'populatedString.required',​​​​​
// ​​​​​ context: { v: undefined, key: undefined, label: 'value' } } ]​​​​​
Joi.populatedString()
.required()
.validate(" ");
// $.value
//
// { inObj1: 'o' }
Joi.object()
.keys({
inObj1: Joi.populatedString()
})
.validate({ inObj1: " o " });
But it does not fail (as it should) for
// ​​​​​{ error: null, value: {}, then: [λ: then], catch: [λ: catch] }​​​​​
Joi.object()
.keys({
inObj2: Joi.populatedString(),
inObj3: Joi.populatedString().required()
})
.validate({ inObj2: " " });
Even though inObj3 is .required() and not supplied it doesn't fail.
I managed to solve it:
const BaseJoi = require("joi");
const Joi = BaseJoi.extend(joi => ({
base: joi.string(),
name: "populatedString",
language: {
required: "needs to be a a string containing non whitespace characters"
},
pre(value, state, options) {
value = value.trim();
return value === "" ? undefined : value;
},
rules: [
{
name: "required",
setup(params) {
return this.options({ presence: "required" });
},
validate(params, value, state, options) {
if (value === undefined) {
return this.createError(
"populatedString.required",
{ v: value },
state,
options
);
}
return value;
}
}
]
}));
The fix was to add setup and let it set the option presence = required if required() was called.

Categories

Resources