I am new to nodeJS, so forgive me if my question sounds stupid,
I want to support conditional payload based on another key,
price: Joi.when('pricing', {
is: 'VARIABLE',
then: Joi.number()
.min(1)
.max(1000)
.required(),
otherwise: // prevent adding price in the payload
})
I want the user to provide price value if pricing is equal to 'VARIABLE' otherwhise to prevent user providing price in the payload.
From the Joi Documentation, we can use the Joi.forbidden() or Joi.any().forbidden() to disallow any keys in your schema. In your case, your final schema will be:
price: Joi.when('pricing', {
is: 'VARIABLE',
then: Joi.number().min(1).max(1000).required(),
otherwise: Joi.forbidden()
})
Related
I have this schema in my mongo db.
const movieSchema = new mongoose.Schema({
title: String,
year: Number,
score: Number,
rating: String
})
When I tried to delete collections with year greater than 1999, I have mistakenly put the condition as:
Movie.deleteMany({yeaer: {$gte: 1999}}).then(res => {console.log(res)})
This happened
And all of my data got deleted
If you omit the conditional property from deleteMany(), mongoose will delete all documents from the model. The same is true if you misspell the conditional. It's a rather dangerous default behaviour but you can guard yourself by disabling strict querying mode in mongoose:
mongoose.set('strictQuery', false)
I have an issue to create Joi schema for this input
{
projects: ["*","ABC123","ABC456"]
}
With the input above, it should throw an error.
I did try to use Joi.alternatives() like this
const schema = Joi.object({
projects:
Joi.array()
.items(
Joi.alternatives(
Joi.string().equal('*'),
Joi.string().regex(new RegExp(/^ABC_([0-9]{3,3})$/))
)
)
})
but it appears to allow both ["*"] and ["ABC123","DEF456"] together. I wanted it to be either ["*"] or ["ABC123","DEF456"], else it will be an error.
How can I actually achieve it by using Joi?
You can try something like this:
const schema = Joi.object({
projects: Joi.alternatives(
Joi.array().length(1).items(
Joi.string().equal('*')
),
Joi.array().items(
Joi.string().regex(/^ABC[0-9]{3}$/)
)
)
});
That is, havingh two alternative array schemas instead of having a single array schema with alternative schemas of elements.
The .length(1) is there to reject values like ["*", "*"] if you want to reject arrays like that, but could be omitted otherwise.
Also, the regex could be written in a simpler way so I simplified it, but I guess that it was just an example so that is not so important.
What is important though is that i removed the underscore ("_") from the regex, because a regex in your example didn't match values like "ABC123" in your example but e.g. "ABC_123".
So whenever I receive a string I want to store it as an array. But I've got no luck so far, i tried to do with cast and with transform. I just need some clarity to get the things going.
Is transform and cast the same thing? How to cast a string into an array using Yup?
const schema = yup.object().shape({
types: yup
.array('type must be an array.')
.of(
yup
.string('the array must contains only strings.')
.transform(value =>
typeof value === 'string' || value instanceof 'string'
? [value]
: value,
)
.matches(/(writer|artist)/, null),
)
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
name: yup
.string('name must be a string.')
.min(3, 'too short'),
});
let obj = {
name: 'Kentarou Kishima',
types: 'artist',
}
//returns ValidationError
obj = schema.cast(obj, { stripUnknown: true });
//trying to just validate results in the same error
schema
.validate(obj)
.then(() => {
next();
})
.catch(function (e) {
console.log(e);
return something;
});
ValidationError: types must be a array type, but the final value was: null (cast from the value "artist")
Edit:
I fixed minor typo btw.
Well, I removed the matches line and it keeps returning the same Error. So now I am thinking since it's receiving a string and not an array, when it goes into the transform function it is going to search for the array items to cast, but there's none because it got a string. So it's well likely that the transform function should be side-by-side with array() and not inside it.
The code looks like this now, but I'm still getting the Error with or without matches():
.array('types must be an array.')
.of(
yup
.string('the array must contains only strings.')
.matches(/(^writer$|^artist$)/) //I improved the regex pattern
)
.transform(value =>
typeof value === 'string' || value instanceof String
? [value]
: value,
)
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
To make things clearer, these are the type of input I am expecting:
let obj = {
name: 'Kentarou Kishima',
types: 'artist', //should cast
types: ['artist'], //should pass
types: ['artist', 'writer'], //should pass
types: '', //should reject
types: ['something'], //should reject
types: ['artist', 'something', 'writer'], //should reject
types: ['artist', 'artist'], // should reject, but i will put a test() later on.
}
The order of operations on the types property is out of order. It follows:
Ensure array element is string.
If the value is a string, convert it to an array containing itself.
If the array of the single string matches the regular expression, continue. If it does not, report null.
If you truly want an array of single-element arrays, then you can keep the transform, but you'll want to move the matches() above the transform() as the arrays that result from the transform() will never match the regular expression, and so matches() always returns your null.
const schema = yup.object().shape({
types: yup
.array('type must be an array.')
.of(
yup
.string('the array must contains only strings.')
.matches(/(writer|artist)/, null)
.transform(value =>
typeof value === 'string' || myVar instanceof 'string'
? [value]
: value,
),
)
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
name: yup
.string('name must be a string.')
.min(3, 'too short'),
});
After messing around with the documentation I found the answer. In this case is enough to use the ensure() function, basically it will take anything that is not an array and put it into an array. After that, the matches() function will reject anything that does not follow the regex pattern.
yup
.array('types must be an array.')
.of(
yup
.string('the array must contains only strings.')
.matches(/(^writer$|^artist$)/)
)
.ensure()
.min(1, 'Need to provide at least one type')
.max(2, 'Can not provide more than two types'),
Edit:
Just a disclaimer, the way it is with min(1) enabled, this property always will be required even though there's no required(), even specifying notRequired() in the object doesn't do the trick.
This is a known issue in yup
https://github.com/jquense/yup/issues/1267
In my case, I need this validator for my POST (all required) and PUT (at least one required) requests, in POST requests I use it the way it is, in PUT requests I dynamically add the rules checking for those present in the req.body.
I'm trying to use a new GraphQL server on a very old legacy code, where the column names have spaces, e.g: "Poke ball"
I've been trying to run this query:
query{{userItems{Poke ball}}}
and got this:
extensions: {code: "GRAPHQL_VALIDATION_FAILED",…}
locations: [{line: 1, column: 12}]
message: "Cannot query field "Poke" on type "UserItems"."
I've tried to use quotes with no luck, any idea if this is supported / workaround?
Thanks.
The GraphQL specification requires that names of things (fields, types, arguments, etc.) only contain letters, numbers and underscores. A field name cannot contain a space because spaces and other whitespace are used to separate individual tokens. In other words, one or more spaces or line returns are used to indicate that, for example, one field's name has terminated and another has begun.
If your underlying data layer is returning data with keys that contain spaces, you need to define a field with an allowed name (like pokeball) and then write a resolver for that field. For example:
const UserItems = new GraphQLObjectType({
name: "UserItems",
fields: () => ({
pokeball: {
type: Pokeball,
resolve: (parent) => {
// The parent value will be whatever the parent field resolved to.
// We look for a property named "Poke ball" on that value and return it.
return parent["Poke ball"];
},
},
...
}),
});
or in the schema, do this
directive #fetch(from : String!) on FIELD_DEFINITION
type Product {
Pokeball : String #fetch(from:"Poke ball")
}
I'm using the Joi library to validate an object. I want to make a certain property required when another optional property (at the same level of the same object) is of a certain type, e.g. string. The Joi docs show this example:
const schema = {
a: Joi.when('b', { is: true, then: Joi.required() }),
b: Joi.boolean()
};
However, rather than checking that b (for instance) is true, I'd like to check whether it is a string. I've tried this:
const schema = {
a: Joi.when('b', { is: Joi.string(), then: Joi.required() }),
};
But it doesn't seem to work. If I remove b completely from the object Joi still seems to expect a to be required. If b isn't in the object I don't want any validation placed on a.
I can't find any other examples of people doing this - can anyone help?
We managed to solve this using object.with. If one key is present (e.g. a), then its peers must be present too (e.g. b).
However, it's not ideal because while we were able to specify that a should be a Joi.string(), object.with is looking for its mere presence rather than its type. So if a non-string a is present a 'should be a string' error will be thrown for a. It should be perfectly fine for a not to be a string - all that should mean is that b is not mandatory. I hope that makes sense.