Joi Validation: How to make values in nested json optional? - javascript

So I have a nested json something like below, which is a payload structure for api that I am writing
{"item_id":"1245",
"item_name":"asdffd",
"item_Code":"1244",
"attributes":[{"id":"it1","value":"1"},{"id":"it2","value":"1"}],
"itemUUID":"03741a30-3d62-11e8-b68b-17ec7a13337"}
My Joi validation on the payload is :
validate: {
payload: Joi.object({
item_id: Joi.string().required(),
item_name: Joi.string().required(),
placeId: Joi.string().allow('').allow(null),
itemUUID: Joi.string().allow('').allow(null),
item_Code: Joi.string().required().allow(null),
attributes: Joi.alternatives().try(attributeObjectSchema, attributesArraySchema).optional()
})
}
where
const attributeObjectSchema = Joi.object({
id: Joi.string().optional(),
value: Joi.string().optional()
}).optional();
and
const attributeArraySchema = Joi.array().items(customAttributeObjectSchema).optional();
My question is :
With the above Joi validation, if I edit my payload and send my attributes tag like below (i,e., with "values" as empty)
"attributes":[{"id":"CA1","value":""},{"id":"CA2","value":""}]
It throws an error saying:
"message": "child \"attributes\" fails because [\"attributes\" must be an object, \"attributes\" at position 0 fails because [child \"value\" fails because [\"value\" is not allowed to be empty]]]",
"validation": {
"source": "payload",
"keys": [
"attributes",
"attributes.0.value"
]
What am I doing wrong here? What do I need to do if I need Joi to accept the below:
"attributes":[{"id":"CA1","value":""},{"id":"CA2","value":""}]

Do something like this
attributeArraySchema.customAttributes = [];
attributeArraySchema.customAttributes = [
{"id":"CA1","value":""},
{"id":"CA2","value":""}
];

So I resolved this by Changing the following schema definition from
const attributeObjectSchema = Joi.object({
id: Joi.string().optional(),
value: Joi.string().optional()
}).optional();
To
const attributeObjectSchema = Joi.object({
id: Joi.string().optional(),
value: Joi.string().allow('').allow(null)
}).optional();

Related

Mongoose - Populating a nested array of objects not working

I have a collection called Orders that contains this schema:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
restaurant: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Restaurant',
required: true
},
dishes: [
{
dish: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Dish'
},
amount: Number
}
],
price: {
type: Number,
required: true
},
comment: {
type: String,
required: false
},
status: {
type: String,
enum: ['PROCESSING', 'CANCELLED', 'COMPLETED', 'ERROR'],
default: 'PROCESSING'
},
timestamp: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model('Order', orderSchema);
Inside my router, I have this code:
let orders = await Order.find({restaurant: restaurantID, status:'PROCESSING'}).populate('dishes._id').exec()
Order.find does not throw an exception, but it isnt working either.
I want the res.body to look like this:
{
"_id": "objectID",
"user": "objectID",
"restaurant": "objectID",
"dishes": [
{
"amount": number,
"dish": {
//dish object
}
},
...
],
//other order properties
},
...
]
But for some reason the dishes array looks like this:
"dishes": [
{
"amount": 1,
"_id": "6184e848e6d1974a0569783d"
}
],
What am I doing wrong?
I know that if populate() worked the res.body dishes array would not have a property called 'dish' and instead have a property called _id that would contain the dish object, but this shouldnt be hard to change once populate() works.
EDIT:
I realised that my createOrder route could be part of the problem since it ignores my schema and uses an id property for the objectID instead of dish. The array I save to the DB contains a property called id for the id instead of dish, but shouldnt my schema throw an exception when i try to save something like this to my database?
At first glance, I think the problem might be that you have a syntax problem.
Try
.populate('dishes').exec()
instead of
.populate('dishes._id').exec()

How to validate object constraint in a object using joi?

I am trying object validation with the help of joi.
I want to validate object as a constraint inside an object like
let login = {
value: 0/1,
slots: [{ label: '', value: '24 hr'}, { label: '', value: '24 hr'}]
}
Here login is an object and inside it slots is also an object. So if I write like the following
const schema = Joi.object().keys({
value : Joi.number.required(),
slots : Joi.string.required()
});
would it be correct for object data type or should I replace string data type with object data type?
I want to validate object type as a constraint.
Your slots key needs to be an array of objects:
const schema = Joi.object().keys({
value: Joi.number().required(),
slots: Joi.array().items(
Joi.object().keys({
label: Joi.string().required().allow(''),
value: Joi.string().required()
})
)
})
This way, the following object will be valid:
const obj = {
value: 1,
slots: [
{
label: '',
value: '24 hr'
}
]
}

Mongoose + MongoDB - Cast to ObjectID failed for value after try save with POSTMAN

I'm trying to save an object that references other 2 objects in mongoDB, but I'm not getting it. Whenever I try, I get this message.
For this, this using POSTMAN to test the API that I am creating.
{
"message": "Order validation failed: payments.0.credit_card.card: Cast to ObjectID failed for value \"{ number: '4898308633754712',\n holder_name: 'Test test',\n exp_month: 1,\n exp_year: 2022,\n cvv: '1234' }\" at path \"credit_card.card\", customer: Cast to ObjectID failed for value \"{ name: 'Test Test', email: 'test#gmail.com' }\" at path \"customer\""
}
The json object I'm trying to save in mongoDB:
{
"items": [{
"name": "Plano s",
"amount": 12345,
"description": "Descrição do Plano Sanarflix",
"quantity": 1
}],
"customer": {
"name": "Test Test",
"email": "test#gmail.com"
},
"payments": [{
"payment_method": "credit_card",
"credit_card": {
"installments":1,
"capture": true,
"card": {
"number": "4898308633754712",
"holder_name": "Test Test",
"exp_month": 1,
"exp_year": 2022,
"cvv": "1234"
}
}
}]
}
This is the model order I defined:
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Model for order
const schema = new Schema({
customer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Customer',
required: function(){
return this.customer_id;
}
},
items: [{
amount: {
type: Number,
required: true
},
description: {
type: String,
trim: true
},
quantity: {
type: Number,
required: true
}
}],
payments: [{
payment_method: {
type: String,
required: true,
trim: true
},
credit_card: {
installments: {
type: Number,
default: 1
},
capture: {
type: Boolean,
default: true
},
card: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Card'
}
},
}]
});
module.exports = mongoose.model('Order', schema);
What is wrong with it? Because when I import the same json to MongoDB with Studio3 Mongo Manager, I can save it and see the object in the correct way.
an object that references other 2 objects, yes, ObjectId does references, yet you're passing the whole objects.
Three options:
pass the id of customer and card objects (if they exist already)
insert these new data in their respective collections then insert your order using the created IDs (likely the solution)
rewrite your schema to have only the Order collection that would hold everything (not really great).
Also, having card numbers transiting through your server is really dangerous (legally speaking), especially if you're to store them... check out how the payment service you're implementing handles transactions: on any decent service the server never sees banking data, discharging your company of any liability.

How to get array value from the other array in react native?

In this code I want the value in centreValues to come from some other array
const centreValues=[
{
title:'Type',
value:centreDetail.location_type
},
{
title:'Address',
value:centreDetail.address1+centreDetail.address2
},
{
title:'City',
value:centreDetail.city
},
{
title:'State/Province',
value:centreDetail.state
},
{
title:'Postal/Zipcode',
value:centreDetail.zip
},
{
title:'Phone',
value:centreDetail.phone
},
]
my centreDetails is json like this:
centreDetails={
location_type:'some Value',
address1: 'some Value',
address2:'some Value',
....
}
I want to bring these values in centreValues array.How could I do that?
its a easy JS object and array scenario. According to your provided array it seems that you expect to have centreDetail. see my below example
const centreDetail = {
location_type: "something",
city: "something",
state: "something",
zip: "something",
phone: "something",
}
now you can call the following above object in your array
const centreValues=[
{
title:'Type',
value:centreDetail.location_type
},
{
title:'City',
value:centreDetail.city
},
{
title:'State/Province',
value:centreDetail.state
},
{
title:'Postal/Zipcode',
value:centreDetail.zip
},
{
title:'Phone',
value:centreDetail.phone
},
]
EDIT: you added json in your question now. therefor, you need a loop. use for loop or while to go through the each index of array and added in your other array. you can also use map for that also
EDIT according to your comment. are you sure about that. see i typed this all in console. it seems to be working.
Here is how you can achieve it :
const centreDetails = {
location_type:'my location',
address1: 'an address',
address2: 'another address',
phone: '516546548',
city: 'Wakanda'
}
const centreValues = Object.entries(centreDetails).map(([title, value]) => ({ title, value}))
console.log(centreValues)
You will have to convert your object into an array made out of pairs of key and values which is made using Object.entries()
Then you only have and create your desired structure using map on your array
EDIT
You can apply an additional filter function if you only want certain fields :
const centreDetails = {
location_type: 'my location',
address1: 'an address',
address2: 'another address',
phone: '516546548',
city: 'Wakanda'
}
const desiredValues = ["location_type", "address2", "city"]
const centreValues = Object.entries(centreDetails)
.filter(([title, value]) => !!desiredValues.includes(title)) //Checks if the value exists
.map(([title, value]) => ({ title, value}))
console.log(centreValues)
EDIT 2 :
If you want to have a different alias here's a way to do it :
const centreDetails = {
location_type: 'my location',
address1: 'an address',
address2: 'another address',
phone: '516546548',
city: 'Wakanda'
}
const desiredValues = {
"location_type" : "location",
"address2": "this guy\'s second address",
"city": "Hey, he lives here !"
}
const centreValues = Object.entries(centreDetails)
.filter(([title, value]) => desiredValues[title])
.map(([title, value]) => ({ title : desiredValues[title], value}))
console.log(centreValues)
to get all values of centreDetail as object result
centreValues.map(x=>x.value.centreDetail)
To transfer to another array, manage the state like this:
centreValues.map(x=> this.setState({ centreDetailArray: [...this.state.centreDetailArray, x.value.centreDetail] })
Then you can get the result from state like this:
<View>
{this.state.centreDetailArray.map(x=><Text>{x}</Text>)}
</View>

Reading the nested object in the nested object

My problem is reading properties of nested object, which is inside other nested object.
GraphQL
type Mapping {
id: ID!
partnerSegmentId: ID!
ctSegmentId: CtSegment!
}
type PartnerSegment {
id: ID!
name: String!
platformId: Int!
partner: Partner!
}
type Partner {
id: ID!
name: String!
}
Once I try to query it like:
{
allMappings {
partnerSegmentId {
id
name
partner {
id
}
}
}
}
I recieve:
{
"data": {
"allMappings": [
null
]
},
"errors": [
{
"message": "Cannot return null for non-nullable field Partner.name.",
"locations": [
{
"line": 8,
"column": 9
}
],
"path": [
"allMappings",
0,
"partnerSegmentId",
"partner",
"name"
]
}
]
}
Mapping schema
const mappingSchema = new mongoose.Schema(
{
partnerSegmentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'PartnerSegment',
required: [true, 'Mapping must have partner segment id.']
},
ctSegmentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'CtSegment',
required: [true, 'Mapping must have CT segment id.']
}
},
{ timestamps: true }
);
I tried to read separately Partner, PartnerSegment and Mapping models. All works fine. Any idea where i should search source of the problem? I've checked mongodb docs and ids looks okay. I suppose it's fault of my model.
If you would like to take a closer look it's project repo.
SOLUTION:
Garbage Id in the return value was caused by not working populate in the nested entity. The way I managed to solve the problem:
const allMappings = () =>
Mapping.find({})
.populate('user')
.populate('ctSegment')
.populate({
path: 'partnerSegment',
populate: {
path: 'partner'
}
})
.exec();

Categories