AWS SNS Multiple notifications received - ios

I have an API Gateway method calling a Lambda Node.js function. The Lambda function calls SNS and posts an APNS notification to my iPhone. When I invoke the API gateway or the Lambda function in the AWS console, I get one notification as expected. I also get one notification when running the Lambda code on the command line (Grunt and Node.js). I also get one notification when running the javascript from eclipse.
However, when I POST to the API gateway, I get 2-5 notifications. Every thing looks the same. I checked the Cloudwatch logs and it seems only one request is sent each time. Anybody have any idea how to debug this?

I've had similar. For me, it was that I wasn't calling the success callback properly.

I figured it out. I had my function outside the exports.handler function:
var AWS = require('aws-sdk');
var sns = new AWS.SNS();
var myAlerter = function(){
var numSent = 0;
var callback;
var arn = "arn:aws:sns:us-west-2:45435475457:endpoint/APNS/MyAlerter/5a11c61f-1122-3344-5566-656845463";
var sendNotification = function(messageText){
var apns = {
aps : {
alert : messageText,
sound : 'default'
}
};
var message = {
"APNS" : JSON.stringify(apns)
};
message = JSON.stringify(message);
var params = {
Message: message,
MessageStructure: 'json',
TargetArn: arn
};
numSent++;
sns.publish(params, function(err, data){
if (err){
callback(err, err.stack);
}else {
var result = {
error: false,
numSent : numSent,
data: data
};
callback(false,result);
}
});
};
return {
alert : function(message, cb){
callback = cb;
sendNotification(message);
}
}
}();
exports.handler = function(event, context){
var alertedCallback = function(error, data){
if (error){
context.done(error);
} else {
context.succeed(data);
}
};
myAlerter.alert(event.message, alertedCallback);
};
Everytime I called the API Gateway and invoked my Lambda function, the numSent variable would increment. I guess putting my function inside the exports.handler ensured that my function wasn't global or something.

Related

"ReferenceError: calendar is not defined" encountered in NodeJS but same code works in API Test Console in Google

Trying to follow this blog post Create a Smart Voicemail with Twilio, JavaScript and Google Calendar
When I run the code in Google Developer API Test Console, it works. However, the same parameters called within Twilio Function which runs NodeJS returns an error "ReferenceError: calendar is not defined"
I've made the Google Calendar events public and I've tried viewing it using the public URL and it works too. For someone reason calling it withing Twilio Functions is resulting in an error.
const moment = require('moment');
const { google } = require('googleapis');
exports.handler = function(context, event, callback) {
// Initialize Google Calendar API
const cal = google.calendar({
version: 'v3',
auth: context.GOOGLE_API_KEY
});
//Read Appointment Date
let apptDate = event.ValidateFieldAnswer;
var status = false;
const res = {
timeMin: moment().toISOString(),
timeMax: moment().add(10, 'minutes').toISOString(),
items: [{
id: context.GOOGLE_CALENDAR_ID
}]
};
console.log(res);
cal.freebusy.query({
resource: res
}).then((result) => {
const busy = result.data.calendars[calendar].busy;
console.log("Busy: " + busy);
if (busy.length !== 0) {
let respObj1 = {
"valid": false
};
console.log("Failed");
callback(null, respObj1);
} else {
let respObj1 = {
"valid": true
};
console.log("Success");
callback(null, respObj1);
}
}).catch(err => {
console.log('Error: checkBusy ' + err);
let respObj1 = {
"valid": false
};
callback(null, respObj1);
});
};
Have you encountered this before or is anyone able to identify the issue here?
Thanks
This line seems to be the issue:
const busy = result.data.calendars[calendar].busy;
As far as I can tell, calendar is never defined. This should work instead:
const busy = result.data.calendars[context.GOOGLE_CALENDAR_ID].busy;
It looks like this line of the code is different between the "Google Calendar FreeBusy Queries" and "Recording VoiceMails" sections of the tutorial and needs to be updated in the latter code sample.

Twilio statusCallback doesn't fire

I'm trying to set up messages delivery status check with twilio. For some reasons twilio statusCallback doesn't fire. Could you please help me to find an error?
Here is a file where I do initialization and send messages:
const Twilio = require('twilio');
const {
TWILIO_ACCOUNT_SID,
TWILIO_AUTH_TOKEN,
TWILIO_PHONE_NUMBER
} = require('config');
const client = new Twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
module.exports = {
send: (body, phoneNumber) => client.messages.create({
body,
to: phoneNumber,
from: TWILIO_PHONE_NUMBER,
statusCallback: 'http://postb.in/b/XXXXXXXX'
})
.then((message) => {
console.log(message.sid);
return message.sid;
})
};
Here is a test where I call message sending after initialization:
const { expect } = require('chai');
const smsUtility = require('utils/sms');
describe('Sms utility', () => {
it('should send a message and return message sid', (done) => {
const body = 'Body';
const number = '+XXXXXXXXXXX';
smsUtility.send(body, number)
.then((messageSid) => {
expect(typeof messageSid).to.equal('string');
return done();
})
.catch((err) => {
console.log(err);
return done();
});
});
});
I use test credentials, but when I replace it with real twilio credentials, I successfully receive a message, so this part works fine. Also, when I try to ping postb.in url manually (with curl), it also works OK. Only statusCallback doesn't work.
Thanks.
Twilio developer evangelist here.
It looks to me like you have everything set up nicely aside from your Postbin URL.
I noticed you show your URL as http://postb.in/b/XXXXXXXX. But the /b/ version of the URL is the dashboard for your Postbin. Requests to the dashboard won't show up on the dashboard.
Instead, you should use the URL that looks like: http://postb.in/XXXXXXXX. Try that and let me know if it's working.

Send the parsed intent and slots back to the client from Amazon-Lex

Amazon Lex FAQ's mention that we can send the parsed intent and slots back to the client, so that we can place the business logic in the client. But am unable to find anything clear on this in the Lex documentation.
My use case:
Send text/voice data to Amazon lex, lex then parses the intent and slots and sends back the JSON with intent, slot and context data back the client which requested it, rather than sending it to Lambda or other backend API endpoint.
Can anyone please point out the right way/configuration for this?
Regards
If I'm understanding you correctly, you want your client to receive the LexResponse and handle it within the client rather than by Lambda or backend API. If this is correct, you can try the following implementation of Lex-Audio.
// This will handle the event when the mic button is clicked on your UI.
scope.audioClick = function () {
// Cognito Credentials for Lex Runtime Service
AWS.config.credentials = new AWS.CognitoIdentityCredentials(
{ IdentityPoolId: Settings.AWSIdentityPool },
{ region: Settings.AWSRegion }
);
AWS.config.region = Settings.AWSRegion;
config = {
lexConfig: { botName: Settings.BotName }
};
conversation = new LexAudio.conversation(config, function (state) {
scope.$apply(function () {
if (state === "Passive") {
scope.placeholder = Settings.PlaceholderWithMic;
}
else {
scope.placeholder = state + "...";
}
});
}, chatbotSuccess
, function (error) {
audTextContent = error;
}, function (timeDomain, bufferLength) {
});
conversation.advanceConversation();
};
The success function which is called after Lex has responded is as follows:
chatbotSuccess = function (data) {
var intent = data.intent;
var slots = data.slots;
// Do what you need with this data
};
Hopefully that gives you some idea of what you need to do. If you need the reference for Lex-Audio, there's a great post about it on the Amazon Blog which you should go check out:
https://aws.amazon.com/blogs/machine-learning/capturing-voice-input-in-a-browser/

Cannot read property of null in Firebase Functions

I'm sending out push notifications to users who subscribed to a certain topic in Firebase Messaging. Everything works but after the message gets sent out and I remove the value from event.data.adminRef I get this error message in my Firebase Functions logs:
TypeError: Cannot read property 'receiverId' of null
at exports.sendNotification.ref.onWrite.event (/user_code/index.js:24:38)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:35:20
at process._tickDomainCallback (internal/process/next_tick.js:129:7)
Notifcations function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var ref = functions.database.ref('/notificationRequests/{notificationId}')
exports.sendNotification = ref.onWrite(event => {
var notificationId = event.params.notificationId;
var notificationRequest = event.data.val();
console.log(notificationRequest);
var receiverId = notificationRequest.receiverId;
var message = notificationRequest.message
var data = notificationRequest.data
// The topic name can be optionally prefixed with "/topics/".
var topic = '/topics/user_' + receiverId;
// See the "Defining the message payload" section below for details
// on how to define a message payload.
var payload = {
notification: {
body: message,
sound: 'default'
},
data: { data }
};
var options = {
priority: "high",
contentAvailable: true
};
// Send a message to devices subscribed to the provided topic.
admin.messaging().sendToTopic(topic, payload, options)
.then(function(response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return event.data.adminRef.remove();
})
.catch(function(error) {
console.log("Error sending message:", error);
});
});
What does it mean? Thanks!
When you remove the message data after sending the message, the removal, which is equivalent to writing a null value, triggers your function to run again, this time with null data. You need to add a check at the top for null data, to short-circuit the second invocation:
if (!notificationRequest) {
return;
}
You also need to return the Promise returned by your sendToTopic().then() code. That ensures your cloud function will be kept alive until the asynchronous processing for sending the message and removing the data completes.
// return added
return admin.messaging().sendToTopic(topic, payload, options)
.then(function(response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return event.data.adminRef.remove();
})
.catch(function(error) {
console.log("Error sending message:", error);
});

console.log not showing in Parse Logs page

In the below cloud code i would like to get a feedback of the saveAll function but after calling the code from my client in the parse Logs page i can only see:
I2014-10-08T15:28:32.930Z] v249: Ran cloud function acceptMeetingBis for user dyGu143Xho with:
Input: {"meetingId":"bUSTGNhOer"}
Result: Meeting accepted
Here is my cloud code:
Parse.Cloud.define("acceptMeetingBis", function(request, response) {
var userAcceptingTheMeeting = request.user;
var meetingId = request.params.meetingId;
var changedObjects = [];
var queryForMeeting = new Parse.Query("MeetingObject");
queryForMeeting.equalTo("objectId", meetingId);
queryForMeeting.first({
success: function(meeting) {
var userCreatorOfMeeting = meeting.get("user");
userAcceptingTheMeeting.increment("acceptedMeetings", +1);
changedObjects.push(userAcceptingTheMeeting);
meeting.add("participantsObjectId", userAcceptingTheMeeting.id);
if (meeting.get("participantsObjectId").length === meeting.get("meetingNumberOfPersons")) {
meeting.set("isAvailable", false);
}
changedObjects.push(meeting);
Parse.Object.saveAll(changedObjects, {
success: function(objects) {
console.log("Successfully saved objects"); //this line doesn't show up
response.success("objects saved");
},
error: function(error) {
// An error occurred while saving one of the objects.
response.error(error);
}
});
//future query and push notifications will go here
response.success("Meeting accepted");
},
error: function() {
response.error("Failed to accept the meeting");
}
});
});
I will also need to add some push and another nested query after the saveAll() but before doing/trying that i would like to know if this is the right method to use or if i have to build the code in a different way. I'm new to javascript and honestly i'm struggling to understand some concepts, like promises. Any help would be much appreciated.
Your call to
Parse.Object.saveAll
is asynchronous, and you call
response.success("Meeting accepted")
immediately after making the asynchronous call, which ends the cloud code running of the method. If you simply replace the
response.success("objects saved")
with
response.success("Meeting accepted")
you should get what you want.
I didn't see the rest of your question about promises. You should check out Parse's documentation on chaining promises, which is what you want here.
Essentially, here's what you'll want to do:
Parse.Cloud.define("acceptMeetingBis", function(request, response) {
var userAcceptingTheMeeting = request.user;
var meetingId = request.params.meetingId;
var changedObjects = [];
var meetingToAccept;
var queryForMeeting = new Parse.Query("MeetingObject");
queryForMeeting.get(meetingId).then(function(meeting) {
meetingToAccept = meeting;
var userCreatorOfMeeting = meeting.get("user");
userAcceptingTheMeeting.increment("acceptedMeetings", +1);
return userAcceptingTheMeeting.save();
}).then(function(userWhoAcceptedMeetingNowSaved) {
meetingToAccept.add("participantsObjectId", userWhoAcceptedMeetingNowSaved.id);
if (meetingToAccept.get("participantsObjectId").length === meetingToAccept.get("meetingNumberOfPersons")) {
meetingToAccept.set("isAvailable", false);
}
return meetingToAccept.save();
}).then(function(savedMeeting) {
response.success("Meeting accepted");
}, function(error) {
response.error("Failed to accept the meeting");
});
});
For each asynchronous action you want to do, perform it at the end of one of the .then functions and return the result (it returns a promise). Keep adding .then functions until you're done all the work you want to do, at which point call response.success.

Resources