Joi when sibling then add extra rules at root - joi

I have a complex validation which changes depending on the a value in the JSON.
{ type: 'a', thing: 1, foo: 'abc' }
{ type: 'b', thing: 2, bar: 123 }
I want to validate that if the type is a, then use one set of siblings, if b then use another set of siblings
I would like to use the when switch, but cant work out how to do this at the root.
Joi.object({
type: Joi.string().valid('a','b').required(),
thing: Joi.number().required()
}).when('type', {
switch: [
{ is: 'a', then: Joi.object({ foo: Joi.string() }) },
{ is: 'b', then: Joi.object({ bar: Joi.number() }) },
],
otherwise: Joi.forbidden(),
});
However this gives the following error:
Error: Invalid reference exceeds the schema root: ref:type
This kinda makes sense as an error but I don't know how to restructure this to get it to apply the selector at the root.
Im using latest JOI (16.0.1)

This can be resolved by prefixing the key name passed to .when() with a ., to denote the key as being relative to the object being validated:
Joi.object({
type: Joi.string().valid('a','b').required(),
thing: Joi.number().required()
})
.when('.type', { /* <-- prefix with . */
switch : [
{ is: 'a', then: Joi.object({ foo: Joi.string() }) },
{ is: 'b', then: Joi.object({ bar: Joi.number() }) },]
})
Here's a working example - hope that helps :-)

Related

eslint indent rule indents decorated members

In order to enable indents for chained methods:
await PostModel
.findOne({
author: user.user,
_id: id,
})
.populate('tickets', 'title status');
I have added the following MemberExpression to my eslintrc, as per eslint docs
indent": ["error", "tab", { "MemberExpression": 1 }],
but now I am experiencing problem with decorators, which get indented although my preference is to have them aligned with the member.
#prop({ type: String, required: true })
password: string;
Is there a way to address those two cases without a conflict?
I got the same problem as you did, and found this comment help, give it a try?
{
rules: {
// ...
"indent": ["error", 2, { "ignoredNodes": ["PropertyDefinition"] }]
}
}
According to this issue, you can partially disable the indent rule for decorators:
indent: [
'error',
'tab',
{
MemberExpression: 1,
ignoredNodes: [
'FunctionExpression > .params[decorators.length > 0]',
'FunctionExpression > .params > :matches(Decorator, :not(:first-child))',
'ClassBody.body > PropertyDefinition[decorators.length > 0] > .key',
],
},
],
This works for me.
#bujhmt's answer actually solved my problem, however, I needed to make some changes to it.
Before:
export abstract class MyAbstractClass extends AnotherClass {
#Column()
name!: string;
^^^^--> Expected indentation of 8 spaces, found 4
#Column()
address!: string;
^^^^--> Expected indentation of 8 spaces, found 4
}
Then I changed my .eslintrc rule to this:
"indent": [
`error`,
4,
{
"ignoredNodes": [
`FunctionExpression > .params[decorators.length > 0]`,
`FunctionExpression > .params > :matches(Decorator, :not(:first-child))`,
`ClassBody.body > PropertyDefinition[decorators.length > 0] > .key`,
],
},
],
And the errors were gone.

Mongodb empty object Object.keys(myObj).length return wrong values

I have an object inside collection called "childLimit".
Schema:
user: { name: {type: String}, childLimit: {dailyLimit: {type: Number} }
Now if I haven't inset any info in this object (childLimit) while creating user so it should not be there as mongo work, that fine. but when I console this it return {} empty object which is fine.
But when I console.log(Object.values(childLimit)) it console [ true, undefined, undefined, undefined ] which is strange????
Object.keys(childLimit).length return 2 but it should be 0??
Now how can I check if the object is empty?? OR if the object exist??
This seems to be about Mongoose, am I right?
Just log the keys which you count, and you will see something like follows: Mongoose-internal properties, but not your document's keys:
console.log(Object.keys(doc));
// [ '$__', 'isNew', 'errors', '$locals', '$op', '_doc' ]
What you actually want are the properties of the document. You can do this as follows:
console.log(Object.keys(doc.toObject()));
// [ 'foo', 'bar', 'baz' ]
Or alternatively (this doesn't seem to be a public API though!):
console.log(Object.keys(doc._doc));
// [ 'foo', 'bar', 'baz' ]
Note that sometimes, both can return different results.

JOI validation when condition not working as expected

I have the following JOI schema defined;
schema = Joi.object({
flag: Joi.boolean()
.required(),
toolDetail: Joi.array().items(
Joi.object({
amount: Joi.number()
.min(0)
.max(499.99)
.precision(2)
.options({ convert: false })
.when('flag', {
is: true,
then: Joi.number().required(),
otherwise: Joi.number()
.strip()
.optional()
.allow(''),
}),
tool: Joi.string()
.min(0)
.max(500)
.when('flag', {
is: true,
then: Joi.string().required(),
otherwise: Joi.string()
.strip()
.optional()
.allow(''),
}),
}),
),
});
I have confirmed that the flag is being set correctly, but it seems that 'amount' and 'tool' is always hitting the otherwise condition, regardless of the value set in the 'flag'.
Is there something incorrect in my definition?
Works as expected once I added 4 dots prefix to the flag
.when('....flag', {
https://joi.dev/api/?v=17.2.1#relative-references

Joi validate presence of a key or another one

I'm using Joi for http body validation. I want to allow keyA to be present OR a keyB to be present and required but not both. I don't find in documentation what I'm looking for and it seems that a cycling loop appear in my schema.
const messageSchema = Joi.object().keys({
keyA: Joi.when('keyB', { is: Joi.exist(), then: Joi.forbidden(), otherwise: Joi.string().required() }),
keyB: Joi.when('keyA', { is: Joi.exist(), then: Joi.forbidden(), otherwise: Joi.string().uri().required() }),
});
Any idea ?
Finally found what I was looking for :
const messageSchema = Joi.object().keys({
keyA: Joi.string(),
keyB: Joi.string().uri(),
}).or('keyA', 'keyB');

MongoDB - "The dollar ($) prefixed field \'$$hashKey\' in \'fieldName".$$hashKey\' is not valid for storage.'"

While trying to update a document I'm getting the above error for the field timesToDisplay.
MongoDB version 2.6.7.
The whole model:
msg = {
'name': '',
'template': '',
'displayDurInMilliSec': 0,
'timesToDisplay': [],
'images': [],
'texts': [],
'screen': []
}
I guess I will be getting the same error with the other 3 array fields.
I've tried using $set but sill getting the same error.
The code:
function updateMessage(msg) {
var conditions = {_id: msg._id}
, update = { 'name': msg.name,
'template': msg.template,
'displayDurInMilliSec': msg.displayDurInMilliSec,
'timesToDisplay': msg.timesToDisplay,
'images': msg.images,
'texts': msg.texts,
'screen': msg.screen
}
messageModel.update(conditions, update, callback);
function callback(err, numAffected) {
if (!err) console.log(numAffected)
else console.log(err)
}
}
EDIT: The msg parameter is a document in itself:
{ _id: '557d58abd54955480db6694f',
name: 'msg99',
timesToDisplay: [ { startDate: '2015-06-19T21:00:00.000Z',
'$$hashKey': 'object:214',
endDate: '2015-06-25T21:00:00.000Z',
daysOfTheWeek: [Object],
startTimeOfDay: '11',
endTimeOfDay: '13' } ],
images: [],
texts: [],
screen: [ 1 ],
'$$hashKey': 'object:54',
displayDurInMilliSec: '40189',
template: 'templates/Template2.html' }
The $$hashkey field is added by AngularJS when working with ngRepeat or ngOptions. In the case of ngRepeat you can change the repeat string by appending track by $index to it. For using ngOptions you'll have to filter out that field yourself. AngularJS provides a quick solution for filtering it out: angular.toJson. This will filter out all fields prefixed with two dollar signs. Check out the documentation.
I realize that this isn't a MongoDB answer, but this specific error ($$hashkey), is usually due to AngularJS.