parse an object inside an API request in nodejs - json

I am trying to sort through some data regarding seat vacancies using nodejs. The response from the API is like this
{
"data":[
{
"date":"2016-10-01",
"timeslots":[
{
"from_time":"10:00",
"to_time":"18:00",
"vacancies":2400
},
{
"from_time":"18:00",
"to_time":"21:00",
"vacancies":900
}
]
},
{
"date":"2016-10-02",
"timeslots":[
{
"from_time":"10:00",
"to_time":"18:00",
"vacancies":2400
},
{
"from_time":"18:00",
"to_time":"21:00",
"vacancies":900
}
]
},
{
"date":"2016-10-03",
"timeslots":[
{
"from_time":"10:00",
"to_time":"18:00",
"vacancies":2400
},
{
"from_time":"18:00",
"to_time":"21:00",
"vacancies":900
}
]
},
{
"date":"2016-10-04",
"timeslots":[
{
"from_time":"10:00",
"to_time":"18:00",
"vacancies":2400
},
{
"from_time":"18:00",
"to_time":"21:00",
"vacancies":900
}
]
},
{
"date":"2016-10-05",
"timeslots":[
{
"from_time":"10:00",
"to_time":"18:00",
"vacancies":2400
},
{
"from_time":"18:00",
"to_time":"21:00",
"vacancies":900
}
]
}
]
}
And I am trying to process this using the code below
app.get('/fetchdata/:ticketid/:fromdate/:todate', function (req, res){
var id = req.params.ticketid;
var fromdate = req.params.fromdate;
var todate = req.params.fromdate;
var key = id+fromdate+todate;
var username = 'foo';
var password = 'foobar';
var slot_url = "https://"+username+":"+password+"#boo.boobar.com/1/timeslots?productId="+id+"&fromDate="+fromdate+"&toDate="+todate;
var result = {};
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
request({
url: slot_url,
json: true,
headers: headers
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
var data = [];
try {
body.forEach(function (entry) {
var temp = {};
temp['date'] = entry["date"];
data = data.concat(temp);
});
result['data'] = data;
result['response'] = 1;
result['message'] = 'Capacity list fetched successfully!';
//client.set(key, JSON.stringify(data));
res.json(result);
}
catch (err) {
result['response'] = 0;
result['message'] = 'No data found!';
result['exception'] = err.message;
res.json(result);
}
}
else
{
console.log("Something went wrong!! "+error.message);
}
})
});
Now I keep getting the response {"response":0,"message":"No data found!","exception":"Object #<Object> has no method 'forEach'"}
I understand that this is because the timeslots key inside the data key is an object but I am unable to parse this. What changes should be done to my code to be able to parse this content.
My ultimate objective is to sort out this data according to the dates and create a json like this
{
"2016-10-01":[
{
"from_time":"10:00",
"to_time":"10:30",
"capacity":-147
},
{
"from_time":"10:30",
"to_time":"11:00",
"capacity":1
}
]
}

body is the root object, so it looks like you're wanting to iterate over body.data instead (e.g. body.data.forEach(...)), which is an array.

Related

Can't use $elemMatch"

When i am trying to hit the api for this part of code on postman i get error of Can't use $elemMatch I search but can't find any solution for that:-
const accessChat = asyncHandler(async (req, res) => {
const { userId } = req.body;
if (!userId) {
return res.status(400).json({err:'UserId param not sent with request'});
}
var isChat = await Chat.find({
isGroupChat: false,
$and: [
{ users: { $elemMatch: { $eq: req.user._id } } },
{ users: { $elemMatch: { $eq: userId } } },
],
})
.populate("users", "-password")
.populate("latestMessage");
isChat = await User.populate(isChat, {
path: "latestMessage.sender",
select: "name pic email",
});
if (isChat.length > 0) {
res.send(isChat[0]);
} else {
var chatData = {
chatName: "sender",
isGroupChat: false,
users: [req.user._id, userId],
};
try {
const createdChat = await Chat.create(chatData);
const FullChat = await Chat.findOne({ _id: createdChat._id }).populate(
"users",
"-password"
);
res.status(200).json(FullChat);
} catch (error) {
res.status(400);
throw new Error(error.message);
}
}
});
Error image is attached
https://i.stack.imgur.com/HXvTR.png

Retrieving data from MongoDB ánd MySQL simultaneously

I am trying to retrieve data from my MongoDB database which stores chat conversations. This works fine and returns what I want. However, I only save userIDs in MongoDB, so I need to query profile picture, username etc from my MySQL database. I tried the following:
app.get('/api/retrieveAllChats', (req, res) => {
var Conversation = mongoose.model('Conversation', ConversationSchema);
var ChatMessage = mongoose.model('Message', ChatMessageSchema);
var userID = req.query.userID.toString()
var members = []
var conversationData = []
var retrieveAllChats = new Promise(function(resolve, reject) {
Conversation.aggregate([{ $match: { "members.uID": userID } }, { $lookup: { foreignField: "c_ID", from: "messages", localField: "_id", as: "messages" } }, { "$unwind": "$messages" }, { "$sort": { "messages.t": -1 } }, { "$group": { "_id": "$_id", "lastMessage": { "$first": "$messages" }, "allFields": { "$first": "$$ROOT" } } }, { "$replaceRoot": { "newRoot": { "$mergeObjects": [ "$allFields", { "lastMessage": "$lastMessage" } ] } } }, { "$project": { "messages": 0 } }], function (err, conversations) {
if (err) return handleError(err);
conversations.forEach((conversation, i) => {
return new Promise(function (resolveConversations, rejectConversations) {
var membersPromise = conversation.members.forEach((member, x) => {
return new Promise(function (resolveUserData, rejectUserData) {
getUserData(member["uID"], function(userData) {
members.push({userID: member["uID"], joinDate: member["j"], userName: userData["userName"], userDisplayName: userData["userDisplayName"], userVerified: userData["userVerified"], userProfilePicURL: userData["userProfilePicURL"]})
console.log("userData: ", userData)
conversations[i].members[x].userData = userData
conversationData = conversations
resolveUserData({userID: member["uID"], joinDate: member["j"], userName: userData["userName"], userDisplayName: userData["userDisplayName"], userVerified: userData["userVerified"], userProfilePicURL: userData["userProfilePicURL"]})
})
})
})
resolveConversations()
})
})
resolve()
})
}).catch(error => {
console.log(error)
res.json({ errorCode: 500 })
})
retrieveAllChats.then(function() {
res.header("Content-Type",'application/json');
res.send(JSON.stringify(conversationData, null, 4));
})
})
However, the conversationData array is always empty. So I need a way to resolve the retrieveAllChats promise and pass the data I added to the existing conversations object to return it with all information I need. Any ideas on how I can do this? (getUserData is a function to retrieve the MySQL data, this one works fine and returns what I want)
You are trying to do async operation inside forEach which wouldn't work. You need to either use for...of or Promise.all.
Also, you can make this code much cimpler by using .exec() at the end of running any query or aggregation as that is supported by mongoose. Something like this should work. Make sure you change your routte line to this to tell it is an async function
app.get("/api/retrieveAllChats", async (req, res) => {
core logic
const conversions = await Conversation.aggregate([{"$match": {"members.uID": userID}}, {"$lookup": {"foreignField": "c_ID", "from": "messages", "localField": "_id", "as": "messages"}}, {"$unwind": "$messages"}, {"$sort": {"messages.t": -1}}, {"$group": {"_id": "$_id", "lastMessage": {"$first": "$messages"}, "allFields": {"$first": "$$ROOT"}}}, {"$replaceRoot": {"newRoot": {"$mergeObjects": ["$allFields", {"lastMessage": "$lastMessage"}]}}}, {"$project": {"messages": 0}}]);
for(const conversation of conversations) {
for(const member of conversation.members) {
// add your promise call here and either await it or use then to get the promise value.
}
}

ExpressJS set the Depth of JSON Parsing

I want to set the depth of JSON parsing in Express middleware express.json().
For example, if I would set the option to parse the depth=1, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: "[object Object]" }
-- or --
When I set depth=2, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: { '$ne': 'user#example.com' } }
And so on,
In this case, there will be no issue of default depth, as the developer will be aware of how many nesting they will allow while development.
PS: It will prevent the application from being vulnerable to NoSQL Injection.
Just write you own middleware:
const get_depth = (obj) => {
let depth = 0
for(const key in obj) {
if( obj[key] instanceof Object ) {
depth = Math.max(get_depth(obj[key]), depth)
}
}
return depth+1
}
const depth_limit = 2
const limit_depth = function(req, res, next) {
if( get_depth(req.body) > depth_limit ) throw new Error("Possible NoSQL Injection")
next()
}
app.use(limit_depth)
Or, if you prefer "[object Object]":
let limit_depth = (obj, current_depth, limit) => {
for(const key in obj) {
if( obj[key] instanceof Object ) {
if( current_depth+1 === limit ) {
obj[key] = "[object Object]" // or something similar
}
else limit_depth(obj[key], current_depth+1, limit)
}
}
}
app.use(function(req, res, next) { limit_depth(req.body, 0, depth_limit); next() })
I write down the query, Maximum 6-8 depth goes. when use lookup inside the lookup.
const [result] = await Collection.aggregate([
{ $match:statusObj },
{
$project:{
_id:1,
name:1
}
},
{
$lookup:{
from:"articles",
let: { "cat_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$category_id", "$$cat_id"] },
{ $eq: ["$isDeleted", false] },
{ $eq: ["$type", type] }
]
}
}
},
{
$lookup:{
from:"view_articles",
let: { "article_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$article_id", "$$article_id"] },
{ $eq: ["$isDeleted", false] }
]
}
}
}
],
as:"viewCount"
}
},
{
$addFields:{
noOfViewCount : { $size:"$viewCount"}
}
} ],
as:"articleCategoryData"
}
},
{
$addFields: {
postCount: {$size:"$articleCategoryData" },
tempsArray: { $map:
{
input: "$articleCategoryData",
as: "tempData",
in: { $add: "$$tempData.noOfViewCount" }
}
},
},
},
{
$addFields: {
viewCount:{ $sum:"$tempsArray" }
},
},
{
$project:{
_id: 1,
name: 1,
postCount: 1,
viewCount: 1
}
},
{
$facet: {
count: [
{
$count: "total"
}
],
result: [{ $match: {} }, { $skip: skipRecord }, { $limit: limit }]
}
}
]);
you can set depth to 10. If you feel JSON is coming wrong then increase it :)
In case anyone who doesn't want to change the value of req.body, can use this function from here
function serializer(payload: any, cdepth: number, options: Options): void {
const main: any = {}
const maxDepth = typeof options.maxNestingLevel == 'number' ? (options.maxNestingLevel == 0 ? 1 : options.maxNestingLevel) : 1
for (const key in payload) {
// check for object
if (payload[key] instanceof Object) {
// check if depth is limited, replace if needed
if (cdepth === maxDepth) {
main[key] = options.replaceWith
} else {
// serialize the nested
main[key] = serializer(payload[key], cdepth + 1, options)
}
} else {
// add to main object if not to be checked
main[key] = payload[key]
}
}
return main
}

JSON Transformation to required format

We are working on a Middleware platform where we are required to respond to consumer with a JSON data in a particular format.
The Data we get from south bound API is a key value pair and this needs to be mapped to an understandable format for the consumer
We tried json-path, ObjectMapper but none of them is giving us the expected result for transforming
Respnse from backend API
{
"details": [
{
"name": "x.y.z.name","value": "TR-54695"
},
{
"name": "a.b.c.standards","value": "DOCSIS"
},
{
"name": "x.x.x.hversion","value": "10"
},
{
"name": "x.x.x.sversion","value": "9.1.116V"
},
{
"name": "x.x.x.uptime","value": "8000"
},
{
"name": "x.x.x.accessallowed","value": "true"
},
]
}
To be transformed to
{
"myData": {
"myInfo": {
"productClass": "TR-54695",
"supportedStandards": "DOCSIS",
"hardwareVersion": "10",
"softwareVersion": "9.1.116V",
"modemMacAddress": "",
"upTime": "8000",
"modemNetworkAccessAllowed": true
}
}
}
Do not like manual work, so here generated demo using 2 functions.
Mind ticking accept button under voting in case you like some answer.
function translate(src, mapping) {
var dst = { "myData": { "myInfo": { "modemMacAddress": "" } } }
//in case order matters:
dst = { "myData": { "myInfo": { "productClass": "", "supportedStandards": "", "hardwareVersion": "", "softwareVersion": "", "modemMacAddress": "", "upTime": "", "modemNetworkAccessAllowed": undefined } } }
var trueFalse = { "false": false, "true": true };
src = src.details;
for (var i = 0; i < src.length; i++) {
dst.myData.myInfo[mapping[src[i].name]] = trueFalse[src[i].value] || src[i].value;
}
return dst;
}
function generateMapping(src, dst) {
src = src.details;
var backLinks = {}, rename2 = {};
for (var i = 0; i < src.length; i++) {
backLinks[src[i].value] = src[i].name;
}
dst = dst.myData.myInfo;
for (var i in dst) {
rename2[backLinks[dst[i]]] = i;
}
return rename2;
}
var src = {
"details": [
{ "name": "x.y.z.name", "value": "TR-54695" },
{ "name": "a.b.c.standards", "value": "DOCSIS" },
{ "name": "x.x.x.hversion", "value": "10" },
{ "name": "x.x.x.sversion", "value": "9.1.116V" },
{ "name": "x.x.x.uptime", "value": "8000" },
{ "name": "x.x.x.accessallowed", "value": "true" },
]
}
var dst = {
"myData": {
"myInfo": {
"productClass": "TR-54695",
"supportedStandards": "DOCSIS",
"hardwareVersion": "10",
"softwareVersion": "9.1.116V",
"modemMacAddress": "",
"upTime": "8000",
"modemNetworkAccessAllowed": true
}
}
}
var mapping = generateMapping(src, dst);
// var mapping = {
// "x.y.z.name": "productClass",
// "a.b.c.standards": "supportedStandards",
// "x.x.x.hversion": "hardwareVersion",
// "x.x.x.sversion": "softwareVersion",
// "undefined": "modemMacAddress",
// "x.x.x.uptime": "upTime",
// "x.x.x.accessallowed": "modemNetworkAccessAllowed"
// }
var result = translate(src, mapping);
console.log(JSON.stringify(result, null, 2));
console.log(JSON.stringify(mapping, null, 2));
You can use below code and use codesandbox link (check console output ) for exact response and this link for key:value pair.
let response = {
details: [
{
name: "x.y.z.name",
value: "TR-54695"
},
{
name: "a.b.c.standards",
value: "DOCSIS"
},
{
name: "x.x.x.hversion",
value: "10"
},
{
name: "x.x.x.sversion",
value: "9.1.116V"
},
{
name: "x.x.x.uptime",
value: "8000"
},
{
name: "x.x.x.accessallowed",
value: "true"
}
]
};
// convert function for key value pair
function convertResponse(responseData) {
let output = { myData: { myInfo: {} } };
let outputRef = output.myData.myInfo;
responseData.forEach(element => {
outputRef[element.name] = element.value
});
return output;
}
// OR convert Function for getting exact same output
function convertResponse(responseData) {
let output = { myData: { myInfo: {} } };
let outputRef = output.myData.myInfo;
responseData.forEach(element => {
if (element.name === "x.y.z.name") {
outputRef.productClass = element.value;
} else if (element.name === "a.b.c.standards") {
outputRef.supportedStandards = element.value;
} else if (element.name === "x.x.x.hversion") {
outputRef.hardwareVersion = element.value;
} else if (element.name === "x.x.x.sversion") {
outputRef.softwareVersion = element.value;
} else if (element.name === "x.x.x.uptime") {
outputRef.upTime = element.value;
} else if (element.name === "x.x.x.accessallowed") {
outputRef.modemNetworkAccessAllowed = element.value;
}
});
return output;
}
//Function Call
console.log(convertResponse(response.details));

Angular Getting Value from Object Object

I would like to extract the value from the JSON below (resReturn.result.gamingdata.original.success)
Just wonder why I can get the value only if I do several times of stringify and parse.
Can someone tell me how to simplify my code?
JSON:
{
"status":"Success",
"message":"100",
"resReturn":
{
"result":{
"gamingdata":
{
"headers":{},
"original":{"success":"Gaming Data Excel - upload success"},
"exception":null
}
}
}
}
My Code:
let resReturnJSON = JSON.stringify(this.UploadstatusGamingDataExcel.resReturn);
let resultobj = JSON.parse(resReturnJSON || '{}').result;
let resultJSON = JSON.stringify(resultobj);
let gamingdataobj = JSON.parse(resultJSON || '{}').gamingdata;
let gamingdataJSON = JSON.stringify(gamingdataobj);
let originalObj = JSON.parse(gamingdataJSON || '{}').original;
let originalJSON = JSON.stringify(originalObj);
let successObj = JSON.parse(originalJSON || '{}').success;
console.log(successObj);
const value = {
"status": "Success",
"message": "100",
"resReturn":
{
"result": {
"gamingdata":
{
"headers": {},
"original": { "success": "Gaming Data Excel - upload success" },
"exception": null
}
}
}
}
const jsonValue = JSON.stringify(value);
const valueFromJson = JSON.parse(jsonValue);
const success = (((((valueFromJson || {}).resReturn || {}).result || {}).gamingdata || {}).original || {}).success;
Check for truthiness for every property until you hit success property and return if found or return empty string.
const data = {
"status": "Success",
"message": "100",
"resReturn": {
"result": {
"gamingdata": {
"headers": {},
"original": {
"success": "Gaming Data Excel - upload success"
},
"exception": null
}
}
}
};
const success = (data.resReturn &&
data.resReturn.result &&
data.resReturn.result.gamingdata &&
data.resReturn.result.gamingdata.original.success) ?
data.resReturn.result.gamingdata.original.success : '';
console.log(success);
If you want a generalised function for json having array and objects, you can use this,
const data = {
"status": "Success",
"message": "100",
"resReturn": {
"result": {
"gamingdata": {
"headers": {},
"original": {
"success": "Gaming Data Excel - upload success"
},
"exception": null
}
}
}
};
const get = (p, o) =>
p.reduce((xs, x) =>
(xs && xs[x]) ? xs[x] : null, o)
console.log(get(['resReturn', 'result', 'gamingdata', 'original', 'success'], data));
I have one more simplest solution:
let obj: any;
try {
if (data.resReturn.result.gamingdata.original.success) {
obj = data.resReturn.result.gamingdata.original.success
}
} catch(e) {
obj = null
}
console.log(obj);
For other different ways, you can also refer this answer