Node JS ignores undefined check - javascript

Im working on a NodeJs app that takes an event from FB and puts it into a local database. For every event in the first page of the api query this goes well, except for the last one.
I am getting the following error:
[December 1st 2016, 1:48:39 pm] TypeError: Cannot read property 'name'
of undefined
at IncomingMessage. (/home/node/virgo/app.js:217:32)
at IncomingMessage.emit (events.js:129:20)
at _stream_readable.js:907:16
at process._tickCallback (node.js:372:11)
Just after
console.log(resultBody);
Code:
function addFBEvent(facebookId){
console.log("getting event: " + facebookId);
var options = {
hostname: 'graph.facebook.com',
port: 443,
path: '/v2.8/'+facebookId+'?fields=name,description,start_time,end_time,place,photos{images}&access_token={INSERT API ACCESS CODE HERE}',
method: 'GET'
};
https.request(options, function(res2) {
var resultBody = "";
res2.setEncoding('utf8');
res2.on('data', function (chunk) {
resultBody = resultBody + chunk;
});
res2.on('end', function () {
dbConnection = sql.createConnection({
host : settings.dbHost,
user : settings.dbUser,
password : settings.dbPassword
});
dbConnection.connect(function(err){
if(!err) {
console.log("Database is connected ... nn");
} else {
console.log("Error connecting database ... nn");
}
});
var json = JSON.parse(resultBody);
console.log(resultBody);
if (json != undefined){
var eventName = json.name;
var eventStart = json.start_time;
var eventEnd = json.end_time;
var eventDescription = json.description;
var eventPlace = json.place.name;
var eventPoster = json.photos.data[json.photos.data.length-1].images[0].source;
var eventId = json.id;
console.log("name: " + eventName + ", start: " + eventStart + ", end: " + eventEnd + ", place: " + eventPlace + ", Poster: " + eventPoster);
//console.log("Description: " + eventDescription);
dbConnection.query('INSERT INTO SVVirgo.activities(title, description, image, start, end, price, location, facebook) VALUES ("'+eventName+'","'+eventDescription+'","'+eventPoster+'","'+eventStart+'","'+eventEnd+'",0,"'+eventPlace+'","'+eventId+'")', function (err, result){
});
}
dbConnection.end();
})
}).end();
}
See graph api explorer for the breaking event: Graph API
Event code: 1682486658666506
I've tried catching the undefined json object with no luck, i've also tried to validate the json using this solution: Stackoverflow validate JSON javascript

Instead of https.request try using request.
It will give you parsed JSON and you won't have to do it manually.
If you want to do it manually like you do, then remember to wrap var json = JSON.parse(resultBody); in a try/catch block (or use tryjson) because the JSON.parse cannot be used on unknown data outside of a try/catch - it can throw exceptions.
Another thing, don't open you database connection in your route handlers. You should open the connection once and just use it in your handlers. See this answer for more info about it.
Right now you are connecting to the database but you continue outside of the connection callback, so you run all the lines beginning from var json = JSON.parse(resultBody); before the DB connection is established.
Additionally, the error may be not because json is undefined but because json.place is undefined.
You can change this:
var eventPlace = json.place.name;
to this:
var eventPlace = json.place && json.place.name;
You must also check json.photos before you access json.photos.data and test if json.photos.data is an array before you treat it as such (you can do it with:
if (json.photos && Array.isArray(json.photos.data)) {
// ...
}
Basically, you need to make sure the values are what you want them to be before you access them. For example accessing this:
json.photos.data[json.photos.data.length-1].images[0].source
can fail when json is undefined, when json.photos is undefined, when json.photos.data is undefined, when json.photos.data is not an array, when json.photos.data.length is zero, when json.photos.data[json.photos.data.length-1] is undefined, when json.photos.data[json.photos.data.length-1].images is undefined, when json.photos.data[json.photos.data.length-1].images is not an array, when json.photos.data[json.photos.data.length-1].images is an array but it's empty, or when json.photos.data[json.photos.data.length-1].images is a non-empty array but json.photos.data[json.photos.data.length-1].images[0].source is undefined.
As you can see there are a lot of assumptions that you are doing here. When those assumptions are not met, the code will fail.

Related

Why do I get an error when putting a JSONObject into an indexedDB database?

I am currently trying to get an indexedDB database running. However, I am struggling with some issues regarding indexedDB's put method. Although the keypath is defined and the JSONObject that is handed over contains a value which is named in the same way as the defined keypath, the put method causes the following error:
Uncaught DOMException: Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value.
In order to make sure that the JSONObject really contains the value that shall be used as the key, I am logging the object. Thats what it looks like:
{"key":102019,"month":10,"year":2019,"spendings":[{"type":{"name":"Technology","importance":70,"iconURL":"./Resources/Technology.png"},"cost":"1500","name":"Macbook pro","timestamp":1571696285911}],"budget":0}
The code that is being used to store the data is the following:
function callbackSaveSpendingMonth(database, spendingMonth) {
let userName = defaultUserName;
let transaction = database.transaction(userName, "readwrite");
let objectStore = transaction.objectStore(userName, { keyPath: 'key' });
let JSONspendingMonth = JSON.stringify(spendingMonth);
console.log(JSONspendingMonth);
let request = objectStore.put(JSONspendingMonth);
request.onsuccess = function (event) {
console.log("The month " + spendingMonth.getMonth() + "/" + spendingMonth.getYear() + " has been saved successfully!");
}
transaction.oncomplete = function (event) {
console.log("A connection to indexedDB has successfully been established!");
}
}

JSON variable not retrieving updated value

I am trying to post a quote retrieved from a JSON file. My code posts a tweet every 20 seconds (for testing purposes it is 20 seconds). I can find my quote and put it in a JSON file by using the server(quoteIndex) function. The server(quoteIndex) adds a new quote to my output.json file. I know that output.json updates the {"quote": ""} part each time it finds a quote on an even index. However, in my tweetIt() function, when I assign var quoteFile = require("./output.json"), my quoteFile.quote value does not update. This is an issue because my javascript bot is a twitter bot that tweets quotes. And twitter does not allow duplicate quotes, handing me a "duplicate status" error.
This is the main code
// I wanted to start at the 8th index and continue with every even index.
var quoteIndex = 8;
// post a tweet every 20 seconds
setInterval(tweetIt, 1000*20);
tweetIt()
function tweetIt() {
//
// This will start looking for quotes to post
// It will put the quote in a JSON file
//
quoteIndex = quoteIndex + 2;
console.log('value of index: ' + quoteIndex)
server(quoteIndex);
var js = require('json-update');
js.load('./output.json', function(err, obj) {
console.log("Loaded from json:");
console.log(obj); // loads most recent quote
}); // this is what I want to tweet
var quoteFile = require('./output.json');
// Read from JSON file for the quote
var params = {
status: quoteFile.quote
}
//
// prints same quote each time, does not update
//
console.log("quote to tweet: " + quoteFile.quote)
// tweet a quote
//
// The first quote will tweet, however when the JSON file
// updates, I will get a "duplicate status" error.
// This is because my quoteFile still has not updated.
//
T.post('statuses/update', params, getData);
function getData(err, data, response) {
if (err) {
console.log("Something went wrong!: " + err);
} else {
console.log("Tweeted something!");
}
}
}
server(quoteNumber)
function server(quoteNumber) {
var fs = require('fs');
var request = require("request"),
cheerio = require("cheerio"),
url = "https://en.wikiquote.org/wiki/A_Song_of_Ice_and_Fire";
request(url, function (error, response, body) {
if (!error) {
var $ = cheerio.load(body);
var quote;
var json = {quote : ""};
$( "div.mw-parser-output ul" ).filter(function( INDEX ) {
// even indexed quotes are the ones I want
if (INDEX % 2 == 0 && (INDEX == quoteNumber)) {
quote = $( this ).text();
json.quote = quote; // this also has the most recent quote
}
});
} else {
console.log("We’ve encountered an error: " + error);
}
//
// Write our quotes out to a JSON file.
//
fs.writeFile('output.json', JSON.stringify(json, null, 4).replace(/\\n/g, " "), function(err){
console.log('File successfully written! - Check your project directory for the output.json file');
})
});
Basically, when I run the code using node.js, the first quote will tweet because the JSON file has a quote for me. Then when it is time to find the next quote using quoteIndex = quoteIndex + 2, my JSON file updates as expected in my project folder. My main issues is that in my tweetIt() function, the quoteFile.quote is not showing the updated quote, even though the JSON file has an updated quote for me. How can I have the updated quote?
Any tips would be appreciated, thanks.
Whenever you require() a file, be it a module or JSON file or native addon, it is cached once it is successfully loaded. If you need to be able to reload the JSON file, you should instead just call readFile() and JSON.parse() the resulting file data manually each time.

How Can I Update Another Object within User afterSave in Parse Cloud Code?

I have two classes - User, and GameScore. Each User has fields called [R,G,B,W,U], and when the user gets updated, I would like to update these fields for the matching user location in the GameScore class. I intend to do this by comparing city, country, and state fields, but would rather do it by matching LatLng (an array). I have tried accomplishing this with cloud code using afterSave:
Parse.Cloud.afterSave("User", function (request) {
var city = request.object.get("city");
var country = request.object.get("country");
var state = request.object.get("state");
var latLng = request.object.get("LatLng");
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("city", city);
query.find({
success: function (results) {
// check if state and country match here, else create new record
var fromParse = JSON.parse(results);
var objectId = fromParse.objectId;
console.log(objectId);
// need to add the change in User's Score to globalScore for their city here
},
error: function (error) {
console.log("Error: " + error.code + " " + error.message);
var newScore = Parse.Object.extend("GameScore");
newScore.set("R",request.object.get("R"));
newScore.set("G",request.object.get("G"));
newScore.set("B",request.object.get("B"));
newScore.set("U",request.object.get("U"));
newScore.set("W",request.object.get("W"));
newScore.set("city",city);
newScore.set("country",country);
newScore.set("state",state);
newScore.set("LatLng",latLng);
newScore.save();
}
});
});
After deploying, and triggering the afterSave hook, I get this error:
E2015-11-26T17:13:57.896Z]v11 after_save triggered for User:
Input: {"object":{"B":0,"G":4,"LatLng":[47.6062095,-122.3320708],"R":6,"U":0,"W":3,"city":"Seattle","country":"US","createdAt":"2015-11-23T03:07:24.043Z","currentMonth":"Nov","number":"00000","objectId":"oC7RBcwztX","smsIds":[null],"state":"CA","textsLifetime":20,"textsThisMonth":20,"updatedAt":"2015-11-26T17:13:57.892Z"}}
Result: Uncaught SyntaxError: Unexpected end of input in :0
Parse.com! Help! Full disclosure: This is my first time using parse.com cloud-code. I'm enjoying it, but is there a way to clear the console log in parse's dashboard?
As per https://parse.com/docs/cloudcode/guide#cloud-code-aftersave-triggers:
You should be passing the object for User like:
Parse.Cloud.afterSave(Parse.User, function(request) {...}
Additionally, I don't believe you need to JSON.Parse the return data and id can be accessed with the 'id' property on parse objects.
gameScore.save(null, {
success: function(gameScore) {
// Execute any logic that should take place after the object is saved.
alert('New object created with objectId: ' + gameScore.id);
},
https://parse.com/docs/js/guide#objects-saving-objects

Parse CloudCode order of execution

Im trying to send a push message to everyone with read access every time a new note is saved.
In pseudocode it should get the ACL. Evaluate each member in the ACL and return an array of all users with read access. Then send a push notification to each member.
I've tried running separate task one by one and it works properly. However when I put everything together in the following code I get strange results. Looking at the log I can see it not executing in order as I expect. I first though the getACL call was an asynchronous call so I tried to implement promises with no luck. Then after help from stackoverflow I find out that getACL is not asynchronous therefore the code should just work, right?
This is what I'm trying:
Parse.Cloud.afterSave("Notes", function(request) {
var idsToSend = [];
var i = 0;
console.log("1 start");
var objACL = request.object.getACL();
var ACLinJSON = objACL.toJSON();
console.log("2 ACL = " + ACLinJSON);
for (var key in ACLinJSON) {
if (ACLinJSON[key].read == "true") {
idsToSend[i] = key.id;
console.log("3 i = " + i + " = " + idsToSend[i]);
i++;
}
}
console.log("4 idsToSend = " + idsToSend);
//lookup installations
var query = new Parse.Query(Parse.Installation);
query.containedIn('user', idsToSend);
Parse.Push.send({
where: query,
data: {
alert: "note updated"
}
}, {
success: function() {
console.log("Success sent push");
},
error: function(error) {
console.error("can’t find user"); //error
}
});
});
And this is the response I see from parse log
I2014-08-04T08:08:06.708Z]4 idsToSend =
I2014-08-04T08:08:06.712Z]2 ACL = [object Object]
I2014-08-04T08:08:06.714Z]1 start
I2014-08-04T08:08:06.764Z]Success sent push
Everything is completely out of order??
How can I execute the above function in the way it's written?
I've found the logs are not in order when I run things too, could be a timing issue or something, ignore the order when they're in the same second, I have done other tests to confirm things really do run in order on my own Cloud Code... had me completely confused for a while there.
The issue you're having is that log #3 is never being hit... try tracing ACLinJSON on it's own to see the actual structure. When you append it to a string it outputs [object Object] as you have seen, so do console.log(ACLinJSON); instead.
Here's the structure I've seen:
{
"*":{"read":true},
"Administrator":{"write":true}
}
Based on that I would expect your loop to work, but it may have a different level of wrapping.
UPDATE:
Turns out the issue was looking for the string "true" instead of a boolean true, thus the fix is to replace the following line:
// replace this: if (ACLinJSON[key].read == "true") {
if (ACLinJSON[key].read == true) {

How to save 3 objects from node.js to parse.com with relations

I'm trying to use node.js (v0.10.28, mac os x) to save 3 objects to parse.com. The objects have relations to each other.
Here are the relations:
I configured the Classes using the data browser:
Car (not being updated by the script)
Drives
Res
StaSto
My goal is to understand Relations (yes, I read the documentation on relations on parse.com, but I can't apply it to this simple example). My script will
query for the car I want (only 1 in parse.com),
then it will create the objects for Car, Drives, Res and `StaSto
then it will relate the objects and put some fake values in them.
and finally, it will try to save them. Here is where the script fails with the error: Failed to create new myRes, with error code: undefined. myDrive gets created, even if it should be the last element to be created and the creation of myRes fails.
can anyone helps? Or is there other better way to do it?
Thanks.
Here is my script:
console.log("Starting");
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// PARSE Classes!
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
var Parse = require('parse').Parse;
var parse_JSKey = "xxx";
var parse_AppKey = "xxx";
Parse.initialize(parse_AppKey, parse_JSKey);
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// PARSE Classes!
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
var Drives = Parse.Object.extend("Drives");
var StaSto = Parse.Object.extend("StaSto");
var Res = Parse.Object.extend("Res");
var Car = Parse.Object.extend("Car");
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// fake data
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
var myDriveData = {'distance':21, 'speed': 99};
var carID = "mrNQcPRNl4"; //already in parse.com!
var myStaSto_lat = 48.1333;
var myStaSto_lng = 11.5667;
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// insert into parse
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// --- query for the car
var carQuery = new Parse.Query(Car);
carQuery.get(carID, {
success: function(result) {
var thisCar = result;
// --- create a new drive
var myDrive = new Drives(); // created a new object, but empty
myDrive.set('distance', myDriveData.distance);
myDrive.set('speed', myDriveData.speed);
// --- create a new StaSto
var myStaSto = new StaSto();
myStaSto.set('coord',new Parse.GeoPoint({latitude: myStaSto_lat, longitude: myStaSto_lng}));
myStaSto.set('drive',myDrive);
// --- create a new Res
var myRes = new Res();
myRes.set('drive',myDrive);
myRes.set('car',thisCar);
myRes.set('res',717);
console.log(myRes);
// --- ----> save them all
myRes.save(null, {
success: function(response) {
// Execute any logic that should take place after the object is saved.
console.log('saved myRes succesfully');
myStaSto.save(null,{
success: function(response){
console.log('saved myStaSto succesfully');
myDrive.save(null,{
success: function(response){
console.log('saved myDrive succesfully');
},
error: function(response,error){
console.log('Failed to create new myDrive, with error code: ' + error.description);
}
}); // end of myDrive.save
},
error: function(response,error){
console.log('Failed to create new myStaSto, with error code: ' + error.description);
}
}); // end of myStaSto save
},
error: function(response, error) {
// Execute any logic that should take place if the save fails.
// error is a Parse.Error with an error code and description.
console.log('Failed to create new myRes, with error code: ' + error.description);
}
}); // end of myRes.save
},
error: function(object, error) {
console.log('error in car query: ' + error.code + " " + error.message);
}
}); // end of car query.
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
dont see anything wrong with your code around the 'save.res. You might want to read up on parse promises tho...
try playing with it in a less complex form.
try to save 'res' with just one FK type relation and , say a straight value in 'res.drive'..
In the lucky chance that you get a good Save instead of 'undef' error then...
you can try switching one of your current relation types to a pointer and see if that works.
It turned out that my code was correct. The error comes because I set up the column as a relation in parse.com (see pictures).
I changed that to Pointer, and voila, it works just fine. I must admit that the JavaScript developer Guide of parse.com is a bit short on mentioning this.

Categories