Mongoose automatically change the type of the value - javascript

In mongoose.model, I have chosen the type of name to be a string and the type of age to be a number, but when I enter a number as the value of name, I don't get an error and the same thing happens when I use something like '18' as the value of age.
Here is the code:
const User = mongoose.model('User', {
name: { type: String },
age: { type: Number }
});
const me = new User({
name: 12,
age: '18'
});
me.save().then(() => console.log(me)).catch(error => console.log(error));

Mongoose casts the values to the corresponding type, if it fails a CastError is thrown, from the doc:
Before running validators, Mongoose attempts to coerce values to the
correct type. This process is called casting the document. If casting
fails for a given path, the error.errors object will contain a CastError object.
You can try this by given age the value 'aa' for example.
If you want to override this behavior you can use one of the following options:
Disable casting globally: mongoose.Number.cast(false)
Disable casting just for a given path:
age: {
type: Number,
cast: false // Disable casting just for this path
},
Use a custom function:
age: {
type: Number,
cast: v => { return typeof v === 'number' && !isNaN(v) ? Number(v) : v; } // Override casting just for this path
}

Related

yup.js object validation, allow any key but values must be string or string array

I am using https://github.com/jquense/yup#yup
I want to have an object validation schema for:
subObjectField: {
[thisKeyCanBeAnyString]: string | string[] // allow string or array of strings
}
I cannot find an example or a starting point to achieve this, any ideas?
I've put together a function which makes this easy:
export const objectOf = (schema) => ({
name: 'objectOf',
exclusive: false,
message: "Object values don't match the given schema",
test: value => {
return value === null || Object.values(value).every(schema.isValidSync(value));
}
});
example:
yup.object().test(objectOf(yup.number())).nullable()
this successfully passes for null and for objects of numbers like { foo: 52, bar: -12 }

Get the value of another field for validation in Yup Schema

I am using Formik with Yup for validation and TypeScript
I have a field that needs to validate based on the value of another field.
The first field is called price and the second field is called tips. The max tip value is 10% of the what ever the price entered is.
I tried to create validation for this using the following:
tips: yup.number()
.min(0, `Minimum tip is $0`)
.max( parseFloat(yup.ref('price'))* 0.1, "Maximum tip is 10% of the price.");
however this doesn't compile because yup.ref returns a Ref. How can I get the value of the price field in this validation?
number.max cannot reference other field and calculate with it at validation.
If you want to do this, you need to implement own schema with mixed.test.
Here is a example.
tips: number()
.min(0, `Minimum tip is $0`)
.test({
name: 'max',
exclusive: false,
params: { },
message: '${path} must be less than 10% of the price',
test: function (value) {
// You can access the price field with `this.parent`.
return value <= parseFloat(this.parent.price * 0.1)
},
}),
Here is doc.
You can check and try how it works here.
I hope this will help you.
if you don't want to use this.parent to access the other properties values you can use the context.
tips: number()
.min(0, `Minimum tip is $0`)
.test(
'max',
'${path} must be less than 10% of the price',
(value, context) => value <= parseFloat(context.parent.price * 0.1),
),
// OR
tips: number()
.min(0, `Minimum tip is $0`)
.test({
name: 'max',
exclusive: false,
params: { },
message: '${path} must be less than 10% of the price',
test: (value, context) => value <= parseFloat(context.parent.price * 0.1),
}),
For referencing other field value we can use this.parent or ctx.parent in case if our value is not nested.
object({
title: string(),
price: string().test('test-name', 'test-message', (value, ctx) => {
let title = ctx.parent.title;
}),
foo: string()
.test('test-name1', 'test-message1', function (value) {
let title = this.parent.title
})
})
but if we have nested value parent is going to give parent of nested
value. in this case parent is not going to work if we want to access
very parent value. we can access parent value with ctx.from. ctx.from contains parents from bottom to top. for example:
object({
title: string(),
ourObject: object({
param1: string(),
param2: string()
.test('test-name', 'test-msg', (value, ctx) => {
let title = ctx.from[ctx.from.length - 1].value.title
})
})
})
or we can easily access any data we want with providing context to schema when validating
object({
price: string(),
foo: string()
.test('test-name', 'test-message', (value, ctx) => {
let arr = ctx.context.arr;
})
})
.validate({ price: 5, foo: 'boo' }, { context: { arr: [1, 2] } })
.then(() => ...)
.catch((err) => ...)
doc
LIKE SO
.test((val,obj)=>{
console.log(val,obj.parent.otherField)
})
val=>current field value
obj=>object of the whole schema
EXAMPLE::::
date: yup.
string()
.required()
.test('type', 'Date should be future date', (val) => {
console.log(val);
}),
time: yup
.string()
.required()
.test('type', 'Time should be future time', (val,obj) => {
console.log(val,'in time val',obj.parent.date,'date')
}),

Flow: Inference error in for-of loop

I have a code this is trying to validate object with attributes id and _name for specified type. Attribute id should be a number and name should be a string as declared in FooT type.
function number(value: mixed): number {
if (typeof value === "number") return value
throw new TypeError("number required")
}
function string(value: mixed): string {
if (typeof value === "string") return value
throw new TypeError("string required")
}
function objectOf(attrs, value) {
const obj = {}
for (const key of Object.keys(attrs)) {
const typeFn = attrs[key]
obj[key] = typeFn(value[key])
}
return obj
}
type FooT = {
id: number,
name: string
}
const fooT: FooT = objectOf(
{
id: number,
name: string
},
{
id: 1,
name: "Foo"
}
)
Running flow shows this error. For some reason inferred return type of typeFn is not correctly determined in for-of loop when accessing object attribute values dynamically.
Cannot assign objectOf(...) to fooT because:
• string [1] is incompatible with number [2] in property id.
• number [3] is incompatible with string [4] in property name.
[3] 3│ function number(value: mixed): number {
:
[1] 8│ function string(value: mixed): string {
:
[2] 22│ id: number,
[4] 23│ name: string
24│ }
25│
26│ const fooT: FooT = objectOf(
27│ {
28│ id: number,
29│ name: string
30│ },
31│ {
32│ id: 1,
33│ name: "Foo"
34│ }
35│ )
36│
Is this an issue with flow or am I missing something?
It looks like you are running into issue #935 Type of object value not inferred correctly in for-in loop. You should be able to use the suppress_comment config and just put $FlowFixMe in the code to tell Flow to ignore that.

SimpleSchema match any type but null

I'm planning to make a collection to hold different app-wide settings, like, say, amount of logged in users today, Google analytics tracking ID, etc. So I made a schema like this:
options_schema = new SimpleSchema({
key: {
type: String,
unique: true
},
value: {
},
modified: {
type: Date
}
});
Now the main problem is that I want value to be of any type: Number, String, Date, or even custom Objects. Though it has to be present, can't be null.
But of course it gets angry about not specifying the type. Is there a workaround for this?
You can use Match patterns for your fields' type which allow you to do pretty much anything :
const notNullPattern = Match.Where(val => val !== null)
value : {
type : notNullPattern
}
(See Arrow functions)
Note that this will allow everything but null, including undefined.
Defining patterns this way allow you to use them everywhere in your application including in check :
check({
key : 'the key',
modified : Date.now(),
value : {} // or [], 42, false, 'hello ground', ...
}, optionsSchema)
Match.test(undefined, notNullPattern) //true
Match.test({}, notNullPattern) //true
Match.test(null, notNullPattern) //false
A more general solution to exclude one value would simply be:
const notValuePattern =
unwantedValue => Match.Where(val => val !== unwantedValue))
The use of which is similar to the above:
Match.test(42, notValuePattern(null)) // true
Note that due to the use of the identity operator === it will notably fail for NaN:
Match.test(NaN, notValuePattern(NaN)) // true :(
A solution could be:
const notValuePattern =
unwantedValue => Match.Where(val => Number.isNaN(unwantedValue)?
!Number.isNaN(val)
: val !== unwantedValue
)
Should you want a solution to exclude some specific values in a schema (kind of the contrary of Match.OneOf), you could use the following:
const notOneOfPattern = (...unwantedValues) =>
Match.Where(val => !unwantedValues.includes(val)
)
This uses Array.prototype.includes and the ... spread operator. Use as follow:
Match.test(42, notOneOfPattern('self-conscious whale', 43)) // true
Match.test('tuna', notOneOfPattern('tyranny', 'tuna')) // false
Match.test('evil', notOneOfPattern('Plop', 'kittens')) // true
const disallowedValues = ['coffee', 'unicorns', 'bug-free software']
Match.test('bad thing', notOneOfPattern(...disallowedValues)) // true

mongoose - Cast to number failed for value "[object Object]" at path

I have a schema that looks like this:
module.exports = mongoose.model('Buyer',{
username: String,
password: String,
email: String,
url: String,
id: String,
firstName: String,
lastName: String,
credit: Number,
});
I am trying to add to their credit whenever they submit a Stripe payment, but when I do, I get the error that is in the title:
Cast to number failed for value "[object Object]" at path
Here is where I try to update credit:
var query = Buyer.findOne({'_id': req.user._id});
query.exec(function(err,buyer){
buyer.credit = { credit: parseInt(req.body.amount,10) };
buyer.markModified('credit');
buyer.save(function (err) {
if (!err) {
//--NO ERROR
} else {
return console.log(err);
}
});
});
I'm not sure how to add to existing credit, instead of overwriting it as I'm doing above, but I suppose I'll ask that in another question after looking further into it.
Anyways, I don't understand what is going on with my error. I tried doing it without the parseInt but noticed it was a string.
Change this line:
buyer.credit = { credit: parseInt(req.body.amount,10) };
To:
buyer.credit = req.body.amount;
You just want to set buyer.credit to the value, not another object with its own credit field. You don't need to the parseInt because Mongoose will do that for you based on your definition of credit as a Number in the schema.
You can also remove the buyer.markModified('credit'); line.

Categories

Resources