Joi: required oneOf field to be true - joi

I am using Joi for my validation schema.
I would like that at least one of theses values should be true.
a=true b=true //valid
a=true b=true //valid
a=false b=true //valid
a=false b=false //invalid
I tried this:
but i get a dependency error..
Joi.object({
a: Joi.boolean().when('b', {
is: true,
then: Joi.optional(),
otherwise: Joi.required(),
},
b: Joi.boolean().when('a', {
is: true,
then: Joi.optional(),
otherwise: Joi.required(),
},
});
Thanks!

Recently, the following code satisfied my problem in a similar situation:
Joi.object({
a: Joi.bool().required(),
b: Joi.bool().when('a', {
is: false,
then: Joi.invalid(false),
}).required(),
});
You might want to play around with required() option needed in a different manner.

Related

MongoDB field only accepts 3 special values

slider_value: {
type: Number,
required: false,
},
This is the Mongoose schema for one of the fields in my MongoDB model.
It may only accept the integer values of 1, 4, and 10.
How can this validator be specified in the schema?
If you only need to store either one of these three values, storing them as a string, and validating using the enum key would be reasonable. For example that could look like this:
{
slider_value: {
type: String,
enum: ["1", "4", "10"],
},
}
Alternatively, if it is a requirement to store them in form of an int, you could use a custom validator to check a value before it's saved. That would look like this:
{
slider_value: {
type: Number,
validate: {
validator: value => value === 1 || value === 4 || value === 10,
message: props => `${props.value} is invalid for slider_value`,
},
},
}
For more details on custom validators and validation in mongoose in generell, here are the mongoose validation docs.

Mongoose lean() is turning a number into a timestamp

I have a prop called duration which is declared as a number in the mongoose schema with the purpose of storing a duration in seconds:
const mySchema = new mongoose.Schema(
{
...
duration: { type: Number, required: true }
...
},
{ timestamps: true },
)
After using the method findOne() and applying the lean() method, the prop duration is returned as a timestamp when it was set as a number. It happens when the number is greater than 1000.
const myVideo = await Models.Video.findOne({ _id: videoId })
.populate({ path: 'segment', populate: { path: 'type' } })
.lean()
.exec()
When I set: { "duration": 6000 } I get: { "duration": "1970-01-01T00:00:06.000Z" }
WHAT I'VE TRIED SO FAR
Besides trying to find the source of the issue, this is what I tried in the code:
I tried upgrading the Mongoose version from 5.9.15 to 5.12.7 to see if a fix was added for this but nothing changed.
Tried removing the { timestamp: true } from the schema, didn't work either.
Also tried adding other props or options like { lean: true } but at the end the result wasn't that different because I did stopped getting the timestamp but the returned object was a mongoose document instead of a plain old javascript object.
MY TEMPORARY SOLUTION
The temporary solution that I found for this was removing the lean() from the chain, but I still couldn't understand what was causing this.

Conditional validation with joi

I try to implement conditional validation with Joi. I have an endpoint that can accept either:
{
from: 'abc'
}
or
{
type: 'some-type'
}
If the type field isn't present, the from field is mandatory and if the from field isn't present, the type field is mandatory. The type can only accept a set of value.
I tried the following approach without success:
type: joi.alternatives().conditional('from', { is: joi.string().empty(), then: joi.string().required().valid('val1', 'val2'), otherwise: joi.optional() })
.messages({
'any.valid': 'type.not.supported.value',
'any.required': 'type.required'
}),
from: joi.alternatives().conditional('type', { is: joi.string().empty(), then: joi.required(), otherwise: joi.optional() })
.messages({
'any.valid': 'from.not.supported.value',
'any.required': 'from.required'
})
Thanks for your help!
Thierry
What you describe sounds like an or constraint.
...a relationship between keys where one of the peers is required (and more than one is allowed)
The following schema would work:
joi.object().keys({
type: joi.string().valid('val1', 'val2'),
from: joi.string()
}).or('type', 'from');

Mongoose {$exists: false} not working, why?

I have the following query:
const messageRules = await MessageRule.findOne({
reservationLength: {$exists: false}
});
on the following schema:
const MessageRule = new Schema(
{
...,
reservationLength: {type: Number, default: 1},
...
}
);
And the query returns a document with:
{
...,
reservationLength: 1,
...
}
I'm going crazy here. Does it have something to do with the default setting in my schema? Any other ideas?
Its a bug i've encountered with mongoose several times already and i did not find too much information about it (granted i decided not to waste time exploring it).
It occurs with all Default value'd fields, mongoose just automatically sets these values to their defaulted value on the return call (if you check the actual document in the database it will not have this field set).
One easy fix to ease the nerve is to add lean() to the call:
const messageRules = await MessageRule.findOne({
reservationLength: {$exists: false}
}).lean();
For some reason this ends up fixing the bug (debatably feature ???)

Sails js should not return password and email

I am trying to create CRUD app in sails js, and i am able to post data to my DB what i noticed is when i insert data on success sails return whole object. But if we don't want certain fields in response then how can we restrict it. Please help thanks.
module.exports = {
attributes : {
username : {
type: 'string',
required: true
},
password : {
type: 'string',
required: true
},
email : {
type: 'string',
required: true,
unique: true
}
},
toJson: function() {
var obj = this.toObject();
delete obj.password;
return obj;
},
beforeCreate: function(attribute, callback) {
console.log(attribute.password);
require('bcrypt').hash(attribute.password, 10, function(err, encryptedPassword) {
sails.log(err);
attribute.password = encryptedPassword;
sails.log(encryptedPassword);
callback();
});
}
};
#arbuthnott is partly correct above -- you do need toJSON rather than toJson -- but more importantly, the function needs to go inside the attributes dictionary, since it is an instance method:
attributes : {
username : {
type: 'string',
required: true
},
password : {
type: 'string',
required: true
},
email : {
type: 'string',
required: true,
unique: true
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
}
I think the responses through sails default REST api for models runs them through .toJSON before returning, so you are doing this the right way.
However, you may have a case issue, like you should define .toJSON with uppercase instead of .toJson. Try making that switch and see if it solves your problem.
UPDATE
Sounds like this is not solving your issue. The sails docs from here say:
The real power of toJSON relies on the fact every model instance sent out via res.json is first passed through toJSON. Instead of writing custom code for every controller action that uses a particular model (including the "out of the box" blueprints), you can manipulate outgoing records by simply overriding the default toJSON function in your model. You would use this to keep private data like email addresses and passwords from being sent back to every client.
That sounds pretty explicitly like what we are trying to do, so maybe this is a sails bug. Perhaps it applies to find, but not create. Is that password returned when simply finding an existing user?
If you must, a sure way around this would be to override the default create action in your UserController:
create: function(req, res) {
User.create(req.body).exec(function(err, user) {
if (err) {
return res.json(err);
}
// explicitly call your own toJSON() to be sure
return res.send(user.toJSON());
});
},
This isn't ideal, especially if you have many model properties you want to hide in many api calls. But it will get the job done.
password: { type: 'string', required: true, protected: true }
protected:true is now deprecated on sails v1.0
You can use instead of that customToJSON
customToJSON: function() {
// Return a shallow copy of this record with the password and ssn removed.
return _.omit(this, ['password', 'ssn'])
}
password: { type: 'string', required: true, protected: true }
You can do this also.