I'm using the Google Maps API with Node to get the formatted address of a particular location. The Maps API returns an empty array when no matching location can be found, so I'm trying to throw an error when the response array is empty using the following code:
function timeZoner(queryString) {
const mapsApiKey = 'API_KEY';
const googleMapsClient = require('#google/maps').createClient({
key: mapsApiKey
});
const VError = require('VError');
googleMapsClient.geocode({
address: queryString
}, function(err, response) {
if (!err) {
returnedResults = response.json.results;
if (returnedResults.length < 1) {
console.log('no match found');
var err1 = new Error;
}
var err2 = new VError(err1, 'no matched location');
console.log(err2.message);
} else {
console.log('Maps API error');
}
});
};
timeZoner('this location name will not match');
Based on the example above, console will log 'no match found' as expected, but err1 is never set, or at least isn't triggering err2 and logging its message in the console as I think it should based on the VError docs. Can someone offer insight on why the error isn't getting triggered?
I'm having trouble wrapping my head around errors in Node as I'm relatively new to JS and I'm brand new to Node. I am aware of error-first callbacks and see that the fact that googleMapsClient.geocode might be the reason the error isn't triggering, but I thought VError was supposed to assist with this. I appreciate any help.
Related
The Problem:
I have been unable to use Firebase (Google) Cloud Functions to collect and utilize device tokens for the cloud messaging feature.
Context:
I am a self-taught android-Java developer and have no JavaScript experience. Despite that, I believe I have code that should work and am not sure what the problem is. To my understanding, it could be one of three things:
Somehow my Firebase Realtime Database references are being called incorrectly and I am not retrieving data as expected.
I may need to use Promises to wait for all calls to be made before proceeding, however I don't really understand how I would incorporate that into the code I have.
I may be using multiple return statements incorrectly (which I am also fuzzy on).
My error message on the Firebase Realtime Database console is as follows:
#firebase/database: FIREBASE WARNING: Exception was thrown by user callback. Error: Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
at FirebaseMessagingError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseMessagingError (/srv/node_modules/firebase-admin/lib/utils/error.js:254:16)
at Messaging.validateRegistrationTokensType (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:729:19)
at Messaging.sendToDevice (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:328:14)
at admin.database.ref.once.snapshot (/srv/index.js:84:12)
at onceCallback (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:4933:51)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:4549:22
at exceptionGuard (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:698:9)
at EventList.raise (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:9684:17)
The above indicates I am not retrieving data either at all or by the time the return is called. My JavaScript function code is:
'use strict';
const admin = require('firebase-admin');
admin.initializeApp();
exports.pushNotification = functions.database.ref('/Chat Messages/{chatId}/{pushID}').onCreate((snapshot, context) => {
const valueObject = snapshot.after.val();
return admin.database().ref(`/Chat Basics/${valueObject.chatKey}/Chat Users`).once('value', statusSnapshot => {
var index = 0;
var totalkeys = statusSnapshot.numChildren();
var msgIDs = [];
statusSnapshot.forEach(msg=>{
msgIDs.push(msg.key.toString());
if(index === totalkeys - 1){
const payload = {
notification : {
title: valueObject.userName,
body: valueObject.message,
sound: "default"
}
}
sendNotificationPayload(valueObject.uid, payload);
}
index++;
});
});
});
function sendNotificationPayload(uid, payload){
admin.database()
.ref(`/User Token Data/${uid}`)
.once('value', snapshot=> {
var tokens = [];
//if(!snapshot.exists())return;
snapshot.forEach(item =>{
tokens.push(item.val())
});
admin.messaging()
.sendToDevice(tokens, payload)
.then(res => {
return console.log('Notification sent')
})
.catch(err => {
return console.log('Error in sending notification = '+err)
});
});
}
This code is mostly inspired by what was said to be a working example here from another Stack Overflow question here. I have successfully tested sending a notification to a single device by manually copying a device token into my function, so the function does run to completion. My Java code seems to be irrelevant to the problem, so I have not added it (please ask in the comments if you would like it added for further context).
What I Have Tried:
I have tried implementing promises into my code, but I don't think I was doing it properly. My main reference for this was here. I have also looked at the documentation for literally everything related to this topic, however my knowledge of JS is not sufficient to really apply barebones examples to my code.
My Firebase Realtime Database Nodes:
#1: Loop through chat members to collect user IDs:
"Chat Basics" : {
"1607801501690_TQY41wIfArhHDxEisyupZxwyHya2" : {
"Chat Users" : {
"JXrclZuu1aOwEpCe6KW8vSDea9h2" : true,
"TQY41wIfArhHDxEisyupZxwyHya2" : true
},
#2: Collect user tokens from collected IDs (ignore that tokens are matching):
"User Token Data" : {
"JXrclZuu1aOwEpCe6KW8vSDea9h2" : "duDR3KH3i3I:APA91bH_LCeslZlqL8akYw-LrM9Dv__nx4nU1TquCS0j6bGF1tlIARcheREuNdX1FheC92eelatBC8LO4t6gt8liRdFHV-NDuNLa13oHYxKgl3JBPPlrMo5rB5XhH7viTo4vfYOMftRi",
"TQY41wIfArhHDxEisyupZxwyHya2" : "duDR3KH3i3I:APA91bH_LCeslZlqL8akYw-LrM9Dv__nx4nU1TquCS0j6bGF1tlIARcheREuNdX1FheC92eelatBC8LO4t6gt8liRdFHV-NDuNLa13oHYxKgl3JBPPlrMo5rB5XhH7viTo4vfYOMftRi"
}
Conclusion:
Concrete examples would be much appreciated, especially since I am crunching right now. Thanks for your time and help!
Update:
After some more testing, it looks like the problem is definitely due to my lack of understanding of promises in two areas. Firstly, only one user is collected before the final return is called. Secondly, the final return is called before the 2nd forEach() loop can store snapshot data to an array.
For this code then, how may I modify (or rebuild) it so that it collects all keys before proceeding to retrieve token data from all keys - ultimately before returning the notification?
Just as with every question I post, I managed to figure out how to do it (tentatively) a few hours later. Below is a full example of how to send a notification to chat users based on a message sent (although it does not yet exclude the sender) to a given chat. The order of operations are as such:
User message is saved and triggers event. Relevant data the message contains are:
username, chat key, message
These are retrieved, with (username + message) as the (title + body) of the
notification respectively, and the chat key is used for user id reference.
Loop through chat user keys + collect.
Loop through array of chat user keys to collect array of device tokens.
Send notification when complete.
The code:
//Use firebase functions:log to see log
exports.pushNotification = functions.database.ref('/Chat Messages/{chatId}/{pushId}').onWrite((change, context) => {
const valueObject = change.after.val();
return admin.database().ref(`/Chat Basics/${valueObject.chatKey}/Chat Users`).once('value', statusSnapshot => {
var index = 0;
var totalkeys = statusSnapshot.numChildren();
var msgIDs = [];
statusSnapshot.forEach(msg=>{
msgIDs.push(msg.key.toString());
if(index === totalkeys - 1){
const payload = {
notification : {
title: valueObject.userName,
body: valueObject.message,
sound: "default"
}
}
let promises = [];
var tokens = [];
for(let i=0; i < msgIDs.length; i++){
let userId = msgIDs[i];
let promise = admin.database().ref(`/User Token Data/${userId}`).once('value', snapshot=> {
tokens.push(snapshot.val());
})
promises.push(promise);
}
return Promise.all(promises).then(() => {
return admin.messaging().sendToDevice(tokens, payload);
});
}
index++;
return false;
});
});
});
Im trying to sign a BlockCypher transaction on the bitcoin testnet using bitcoinjs as described here but I keep getting the error:
{"error": "Couldn't deserialize request: invalid character 'x' in literal true (expecting 'r')"}
I have searched around and can find no documentation on what the problem is. Below is the code im using to try and sign the transaction.
var bitcoin = require("bitcoinjs-lib");
var buffer = require('buffer');
var keys = new bitcoin.ECPair.fromWIF('cMvPQZiG5mLARSjxbBwMxKwzhTHaxgpTsXB6ymx7SGAeYUqF8HAT', bitcoin.networks.testnet);
const publicKey = keys.publicKey;
console.log(keys.publicKey.toString("hex"));
var newtx = {
inputs: [{addresses: ['ms9ySK54aEC2ykDviet9jo4GZE6GxEZMzf']}],
outputs: [{addresses: ['msWccFYm5PPCn6TNPbNEnprA4hydPGadBN'], value: 1000}]
};
// calling the new endpoint, same as above
$.post('https://api.blockcypher.com/v1/btc/test3/txs/new', JSON.stringify(newtx))
.then(function(tmptx) {
// signing each of the hex-encoded string required to finalize the transaction
tmptx.pubkeys = [];
tmptx.signatures = tmptx.tosign.map(function(tosign, n) {
tmptx.pubkeys.push(keys.publicKey.toString("hex"));
return keys.sign(new buffer.Buffer(tosign, "hex")).toString("hex");
});
// sending back the transaction with all the signatures to broadcast
$.post('https://api.blockcypher.com/v1/btc/test3/txs/send', tmptx).then(function(finaltx) {
console.log(finaltx);
}).catch(function (response) {
console.log(response.responseText);
});
}).catch(function (response) {
console.log(response.responseText);
});
It seems this line return keys.sign(new buffer.Buffer(tosign, "hex")).toString("hex"); is the problem but im not sure on what is wrong.
This question was discussed and answered here. This post and this one are to be looked into in particular.
As far as I understand, according to the issue respective one was opened at BlockCypher repo. Although its status is still opened till this date, current BlockCypher JS docs respective API description contains altered version of the line
return keys.sign(new buffer.Buffer(tosign, "hex")).toString("hex");
with toDER() conversion prior to toString(), consequently it looks like this now
return keys.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex");
Preface: I'm new to JavaScript. If this question is startlingly stupid, that's (part of) the reason.
Begin Update
I found the answer to my question prior to the flag as a dupe. I solved it with a try-catch block. This answer does not reference a try-catch block.
How do I return the response from an asynchronous call?
End Update
I'm attempting to create an Alexa project from scratch (well, at least without one of Amazon's templates). I've written the "guts" of the app and I've tested my functions with chai. Things were going swimmingly until I tried to wire up some intents.
I can see my intents are being sent based on console.log statements I've thrown in the helperClass, but the return values aren't making it back to my index.js file.
Two questions:
What am I mucking up?
How do I fix it?
Here's what I've done:
Based on that, I dug around to see what's going on in my index.js file's headers and I saw this:
var Alexa = require('alexa-app');
So I went to alexa-app and saw that it uses bluebird, which suggests to me that I'm dealing with a promise problem. Further, I saw this in the log when I send a request that works:
preRequest fired
postRequest fired
When a request doesn't work, I only see:
preRequest fired
I'm using Big Nerd Ranch's "Developing Alexa Skills Locally with Node.js".
Here's my problematic intent in my index.js file:
app.intent('getDaysFromNow', {
'slots': {
'INPUTDATE' : 'AMAZON.DATE'
},
'utterances': ['{|number of|how many} {days} {until|from|since} {|now|today} {|is|was} {-|INPUTDATE}'] // almost perfect
},
function(req, res) {
console.log('app.intent getDaysFromNow fired');
//get the slot
var inputDate = req.slot('INPUTDATE');
var reprompt = 'Ask me how many days until or from a specified date.';
if (_.isEmpty(inputDate)) {
console.console.log('app.intent daysFromNow blank request');
var prompt = 'I didn\'t hear the date you want.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
console.log('getDaysFromNow slot is not empty.');
var dateHelper = new DateHelper();
dateHelper.getDaysFromNow(inputDate).then(function(daysFromNow) {
console.log(daysFromNow);
res.say(dateHelper.formatDaysFromNowResponse(daysFromNow)).send(); // FIXME
}).catch(function(err) {
console.log(err.statusCode);
var prompt = 'Hmm...I don\'t have a date for that ' + inputDate;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
});
return false;
}
}
);
I know it's getting sent, but the value's not getting back to index.js. I think I've got a return problem. Here's the function in my helperClass.js whose return isn't getting back to index.js
// Takes AMAZON.DATE string as its argument
DateHelper.prototype.getDaysFromNow = function(inputDate) {
if (isValidDate(inputDate)) { // validate it
// if it's valid, run the function
inputDate = moment(inputDate, "YYYY-MM-DD").startOf('day'); // inputDate starts as a string, recast as a moment here
// create currentDate moment from current Date()
var currentDate = moment(new Date()).startOf('day');
// Calculate daysFromNow here
var daysFromNow = inputDate.diff(currentDate, 'days');
console.log("\t" + 'daysFromNow = ' + daysFromNow);
// ORIGINAL CODE
// return daysFromNow;
// EXPERIMENTAL CODE
return this.daysFromNow.then(
function(response) {
return response.body;
}
);
} else {
// throw an error
throw new Error("getDaysFromNow(): argument must be valid AMAZON.DATE string");
}
};
Thank you for reading. I welcome your suggestions.
It turns out it's not the way the return is sent from helperClass. The issue is how the call was made in the first place.
For the helperClass, I reverted to the original return.
// This is the return from `helperClass`
return daysFromNow;
In the index.js class, I put the call to helperClass in a try-catch block, like so:
app.intent('getDaysFromNow', {
'slots': {
'INPUTDATE': 'AMAZON.DATE'
},
'utterances': ['{|number of|how many} {days} {until|from|since} {|now|today} {|is|was} {-|INPUTDATE}'] // almost perfect
},
function(req, res) {
console.log('app.intent getDaysFromNow fired');
//get the slot
var inputDate = req.slot('INPUTDATE');
var reprompt = 'Ask me how many days until or from a specified date.';
if (_.isEmpty(inputDate)) {
console.log('app.intent daysFromNow blank request');
var prompt = 'I didn\'t hear the date you want.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
// ** CHANGED: This is the part that changed. **
console.log('getDaysFromNow slot is not empty.');
var dateHelper = new DateHelper();
try {
var daysFromNow = dateHelper.getDaysFromNow(inputDate);
res.say(dateHelper.formatDaysFromNowResponse(daysFromNow)).send();
} catch (error) {
console.log("error", error);
var prompt = 'Hmm...I don\'t have a date for that ' + inputDate;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
}
return false;
}
}
);
Hopefully, someone finds this helpful if they run into the same problem.
Another quickblox question - I am getting the following error when trying to run my application:
Uncaught TypeError: Cannot read property 'sigBytes' of undefinedquickblox.js:10707 c.HMAC.i.extend.initquickblox.js:10703 i.Hasher.a.extend._createHmacHelperquickblox.js:101 signMessagequickblox.js:35 createSessionquickblox.js:867 QuickBlox.createSessionchat.html:122 (anonymous function)
This error prevents successful authentication of the user. The code being executed is below(insecure as hell - proof of concept only!):
var chatToUser = window.localStorage.getItem("chatToUser");
var username = window.localStorage.getItem("user");
var password = window.localStorage.getItem("pwd");
var params = {login:username,password:password};
console.log("PARAMS: ",params);
$(document).ready(function(){
var chatService = new QBChat(params);
});
// JavaScript SDK initialization
QB.init(QBAPP.appID, QBAPP.authKey, QBAPP.authSecret);
// QuickBlox session creation
QB.createSession(params, function(err, result) {
if (err) {
console.log("ERROR:", err.detail);
} else {
chatUser = {
id: result.user_id,
pass: params.password
};
connectChat();
}
});
function connectChat() {
chatService = new QBChat({
onConnectFailed: onConnectFailed,
onConnectSuccess: onConnectSuccess,
onConnectClosed: onConnectClosed,
onChatMessage: onChatMessage
});
console.log("Chat Service: ", chatService);
console.log("Chat User: ", chatuser);
// connect to QB chat service
chatService.connect(chatUser);
}
/* Callbacks
------------------------------------------------------*/
// Connection is failed
function onConnectFailed() {
alert("We're having some difficulty talking to the server. Please try again later, or get in touch if th eproblem persists.")
}
// Connection is success
function onConnectSuccess() {
//green dot - live - status
alert("Connected.")
}
// Connection is closed
function onConnectClosed() {}
What do i need to do differently in order to successfully log the user in for chat?
If it helps, I have previously generated a session for them based on their 'user' credentials (username/password). Is it possible to simply reuse this session instead of creating a fresh one?
It just hit me! (adding a few console.logs helped...)
The app is looking for QBAPP.(etc) and the case/underscore formatting of those parameters did not line up after copying from an examples, hence the 'undefined'-s coming back and the sigbytes thingy falling over itself. So note to anyone else having this error - check your code carefully, for case and style before nagging the community with stupid questions!
I hope someone might learn from this.
I'm a little confused about where to place a response.success() when using serial Promises.
Here's the situation: I've got a cloud function that accepts an array of email addresses and the current user. The function does the following:
Finds the current user based upon it's user object id.
Iterates over the array of emails addresses
Find if there is an existing user for each given email address
If there is an existing user, we check to see if the existing user and the current user are friends
If they are not friends, it creates a friendship.
Now when I run this function without a response.success(), it does exactly what I expect it to and the friendships entries are created. But no matter where I place the response in the code, I get the resulting response.success message and none of the serialized promises execute.
Why the resulting success/failure matters: I'm executing this function from an iOS app and I'd like to properly handle the success or failure cases correctly on the iOS side.
Here is the cloud function:
Parse.Cloud.define("friendExistingUsers", function(request, response) {
// Get our parameters
var addresses = request.params.emailAddresses;
var userId = request.params.user;
// Query for our user
var userQuery = new Parse.Query("User");
userQuery.equalTo("objectId", userId)
userQuery.first().then(function(currentUser) {
// if we find the user, walk the addresses
var promise = Parse.Promise.as("success");
_.each(addresses, function(address) {
console.log(address);
// add a then to our promise to handle whether a relationship is
// being created.
promise = promise.then(function() {
// find if there is a user for that address
var emailQuery = new Parse.Query("User");
emailQuery.equalTo("email", address);
emailQuery.first().then(function(addressUser) {
if (typeof addressUser != 'undefined') {
// found one.
console.log(addressUser);
// figure out if our current user and this user are
// friends.
var friendQuery = new Parse.Query("FVFriendship");
friendQuery.equalTo("from", currentUser);
friendQuery.equalTo("to", addressUser);
friendQuery.first().then(function(relationship) {
if (typeof relationship != 'undefined') {
// if they are, we need to pass.
console.log("Found a relationship: " = relationship)
} else {
// They are not. Add the friendship
var Friendship = Parse.Object.extend("FVFriendship");
var friendship = new Friendship();
friendship.set("from", currentUser);
friendship.set("to", addressUser);
friendship.save().then(function(result) {
console.log("Created a friendship: " + result)
});
};
});
} else {
// we did not find a user for that address
console.log("No user for " + address);
};
});
});
});
console.log(promise);
return promise;
}).then(function() {
response.success("success");
});
});
Thanks in Advance. Let me know if there's anything else I can add.
Your .then callback function attached to promise should return a promise. Missing this is a common mistake when using promises.
Also Parse doesn't seem to show objects with console.log as browsers do, so I wrap them into JSON.stringify().