I have created an AWS Lambda function with this code:
console.log('Loading function');
var AWS = require('aws-sdk');
AWS.config.region = 'us-west-2';
exports.handler = function(event, context) {
console.log("\n\nLoading handler\n\n");
var sns = new AWS.SNS();
sns.publish({
Message: 'Test publish to SNS from Lambda',
TopicArn: 'TOPIC_ARN'
}, function(err, data) {
if (err) {
console.log(err.stack);
return;
}
console.log('push sent');
console.log(data);
context.done(null, 'Function Finished!');
});
};
When it runs, this code suppose to publish a message to an AWS SNS topic. I've subscribed an email address to this topic to check it.
When I clicked on the "Save & Test" button, I've started being blown up with emails. I got over 1,000 emails from it, and it doesn't stop.
I've tried to delete the topic, the subscription, the function itself, but nothing works. I keep getting those emails.
Is there anything I can do to stop this email rain?
Related
I am testing with a payment processing system and every time a transaction is completed, the payment processor should hit my endpoint with a POST request with payment details so I can save it to my database (Firestore).
Only thing is the function fails on the first try. What I mean is, say a customer pays, the payment processor hits my cloud function, it fails to save to my database. When a second customer makes the transaction a minute, 5 minutes or even 18 minutes later according to my observation, everything works as expected.
Am I facing a cold start problem or what is happening. And how do I solve this.
Here is my function
exports.stkCallback = functions.https.onRequest(async (request, response) => {
if (request.method === 'POST') {
if (request.body.Body.stkCallback.ResultCode === 0) {
const jsonData = request.body.Body.CallbackMetadata;
console.log("USER HAS COMPLETED THE TRANSACTION");
var transactionID;
///This below line logs successfully everytime meaning my payment processor has sent the POST
/// request
console.log("checkoutid:", request.body.Body.CheckoutRequestID)
///I have saved the CheckoutRequestID previously to Firestore so I first query the document
//// with that ID (CheckoutRequestID) and get its data so I can update the transaction as
//// complete
var docRef=db.collection("Transactions").doc(request.body.Body.CheckoutRequestID);
await docRef.get().then((doc) =>{
// eslint-disable-next-line promise/always-return
if (doc.exists) {
//console.log("Document data:", doc.data());
transactionID=doc.id;
transactionData.push(doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch((error)=> {
console.log("Error getting document:", error);
});
///Once I get the data I can then go ahead and do other operations.
///Only the above query fails the first time which I don't know why
///By failing Saying No such Document. Which the document does exist
***carrying out other operations using the fetched transactionID and transactionData***
response.sendStatus(200);
} else {
console.log("USER HAS CANCELLED THE TRANSACTION");
response.sendStatus(200);
}
I have refactored my code and reproduced it to the below
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.stkCallback = functions.https.onRequest(async (request, response) => {
const accountSid = "#";
const authToken = "#";
const client = require("twilio")(accountSid, authToken);
if (request.method === 'POST') {
if (request.body.Body.ResultCode === 0) {
const jsonData = request.body.Body.CallbackMetadata;
console.log("USER HAS COMPLETED THE TRANSACTION");
var transactionID;
///This below line logs successfully everytime meaning my
/////payment processor has sent the POST request
console.log("checkoutid:",
request.body.Body.CheckoutRequestID)
////The below function is critical to all the other below functions below it as
///it supplies the necessary data all the way down
///It is also the function that fails on the first run
var docRef= await db.collection("Transactions").doc(request.body.Body.CheckoutRequestID).get()
.catch((error)=> {
console.log("Error getting document:", error);
});
//// a log of some data from the above function
//// when it fails, the below log is undefined,
console.log("tyyy",docRef.data().Home)
transactionID=docRef.id;
////the data returned from above function is used to perform other operations.
////Below is just one of them
////consequently, it will fail as some values like doc.data().Uid will be ////undefined
await db.collection("Users").doc(doc.data().Uid).collection("Transactions").doc(transactionID).update({
TransactionComplete: true,
transactionCompletedTimeDb: admin.firestore.FieldValue.serverTimestamp(),
Amount: jsonData.Item[0].Value,
ReceiptNO: jsonData.Item[1].Value,
TransactionDate: jsonData.Item[3].Value,
PhoneNumber: jsonData.Item[4].Value,
UserId: doc.data().Uid
})
// eslint-disable-next-line promise/always-return
.catch((error)=> {
// The document probably doesn't exist.
console.error("Error updating document: ", error);
});
response.sendStatus(200);
} else {
console.log("USER HAS CANCELLED THE TRANSACTION");
response.sendStatus(200);
}
});
Attaching an image of a failed function, do note the time
An image of logs of the same triggered function right after (3 minutes later). As you can see the function completes successfully
This seems like a Cold Start Issue
The mitigation of this issue will depend on many information that you are not sharing with us like the complete function, dependencies that you are using, and instance size.
Spreading a loaded function into multiple small functions will help with the cold start time, also using smaller, updated, and cloud oriented libraries will also help.
Also, the size of the payload could be an important factor here, how big is the size of the payload sent to the function and how big is the size of the info that you are writing into the logs? All these small pieces have an important influence on the performance of a cold start.
As a quick solution for your Issue, I can safely say that creating a Scheduled task that triggers your functions every 30 minutes, for example, would be enough to mitigate your issue in the short term.
I'm trying to set up push notifications from one user to another using Back4App which is a parse server. I have followed their guide here
the Javascript cloud code they use is below:
Parse.Cloud.define("pushsample", function (request, response) {
Parse.Push.send({
channels: ["News"],
data: {
title: "Hello from the Cloud Code",
alert: "Back4App rocks!",
}
}, {
success: function () {
// Push was successful
response.success("push sent");
console.log("Success: push sent");
},
error: function (error) {
// Push was unsucessful
response.error("error with push: " + error);
console.log("Error: " + error);
},
useMasterKey: true
});
});
I am updating a custom parse class called notifications within the app which I would also like to send to the user the notification is directed at. When saving this class I am grabbing the UserID which is also stored in the installation class used to send pushes. I am completely new to Javascript so am wondering if someone could tell me how to edit the above code to receive the userID from the method on the device and then run a query to send to just this user.
The push notification feature allows to configure options and customizing the push.
You can send a query to update one specific user. Please, take a look at the example below:
Parse.Cloud.define("sendPushToUser", async (request) => {
var query = new Parse.Query(Parse.Installation);
let userId = request.params.userId;
query.equalTo('userId', userId);
Parse.Push.send({
where: query,
data: {
alert: "Ricky Vaughn was injured in last night's game!",
name: "Vaughn"
}
})
.then(function() {
// Push was successful
}, function(error) {
// Handle error
});
});
At the moment, you can read more about these options here.
After creating a classroom invitation the method classroom.create returns with the return status 200 but the user never receives email with the invitation message.
I have given the the client id associated with the service account that I am using, the scopes classroom.roster and mail.google.com, but nothing seems to work.
Am I missing something?
Thank you very much,
Andres
This is the fragment of code that creates the invitation:
//get authorization client
const auth = await google.auth.getClient({
scopes
});
//impersonate teacher account
if (auth instanceof google.auth.JWT) {
auth.subject = 'teacher_email#dom.edu';
}
const options = { auth,
requestBody: {
courseId: '19220887720',
role: 'STUDENT',
userId: 'student_email#dom.edu'
}
};
//send invitation
try {
const invitation = await classroom.invitations.create(options);
console.log('invitation was sent:', invitation);
} catch (err) {
console.error('error: ' , err);
}
Response: status 200.
The invitation is really created. Response includes an invitation id and If you check on the ClassRoom home page->people you can see the correct student was invited.
Based from this forum, students need to have the same settings on in their account, which they may not by default. They should also choose to be a student first.
Also, notification e-mails of Google Classroom are sent from classroom.google.com domain. See Email notifications - Classroom Help for further details.
I'm writing a trigger on a Cloudant database that should convert each new document into a Slack notification.
I've created a sequence of two actions: one to prepare the Slack message, one to send it. To send the Slack message I'm using the package action provided by IBM Bluemix OpenWhisk.
Cloudant changes feed --> Prepare Text --> Slack Post --> response
As the triggers send me all Cloudant events (new/modified/deleted documents), how can I only forward to Slack the NEW document event and ignore things like deleted documents.
For synchronous processing, simply return an error in our action
function main(doc) {
if (doc._deleted) {
// ignore deleted documents
return { error: "ignoring deleted doc" };
} else {
// prepare the text for the Slack post action
const slackMessage = ...
return { text: slackMessage };
}
}
return new Error("ignoring deleted doc") works too.
Or using the Promise object, one can call reject(reason) to interrupt the flow of a sequence.
function main(doc) {
return new Promise((resolve, reject) => {
if (doc._deleted) {
// ignore deleted documents
reject({ error: "ignoring deleted doc" });
} else {
// prepare the text for the Slack post action
const slackMessage = ...
resolve({ text: slackMessage });
}
};
}
The call to reject will stop the sequence flow. Any of reject('interrupted!'), reject(new Error('interrupted!'), reject({ error: 'interrupted!' }).
Warning: reject() will not work. You must provide a reason to reject.
I've got an Azure Mobile Service with a custom API. I have tested this API in the past from iOS and it seems to work fine. I am now testing this API on Android. This is the API method in question:
exports.post = function(request, response) {
var body = request.body;
var email = body.email;
var tables = request.service.tables;
var users = tables.getTable('User');
users.where({ email: email }).read({
success: function (userList) {
if (userList.length === 0) {
response.send(200, { Status: 'Error', Error: 'Email not found.' });
} else {
var user = userList[0];
var providerId = user.ObjectId;
var accounts = tables.getTable('Account');
accounts.where({ User: providerId }).read({
success: function (accountList) {
if (accountList.length === 0) {
response.send(200, { Status: 'Error', Error: 'Internal server error.' });
} else {
var account = accountList[0];
var mssql = request.service.mssql;
var sql = "EXEC [db].[usp_RequestPasswordReset] ?;";
mssql.query(sql, [account.id], {
success: function (results) {
console.log(results);
var codeRow = results[0];
if (codeRow == undefined) {
console.log("codeRow is undefined");
} else {
console.log(codeRow);
}
var code = codeRow.Code;
response.send(200, { Status: 'Success', Message: 'Please check your email for further instructions.', Code: code });
sendEmail(email, user.Name, code);
}
});
}
}
});
}
}
});
};
Now, sendEmail is a separate function that sends an email using Azure's SendGrid feature.
What is really perplexing me is that all of the code appears to be working fine.
The stored procedure executes just fine.
The database is updated exactly as I would expect.
The email comes through the SendGrid service exactly as expected.
The console.log messages that I have in the code display the expected values.
The only thing that is funky is that the call is returning a "500: Internal Server Error" error.
This is true both in my Android client and also in the API log on the Azure Management Portal.
The error message I am getting is telling me that var code = codeRow.Code; is trying to access 'Code' of 'undefined'. But it's not undefined.
Going back and checking my iOS client against this produces the same results.
Everything works fine except for the message returned to the user.
To be clear, the error code is 500, not 200, since it's possible for my code to return an "Internal Server Error" message.
Also, I am very sure that my mssql.query success block is firing, based on the console log messages and the outcome.
So, what gives?
mssql.query can call your callback more than once depending on what's in your stored procedure. You can define a variable outside your callback, e.g.
var callbackReceived = false;
and then in your callback, only send a response for the call that actually receives the updated record:
if (callbackReceived === false && results && results.length > 0) {
callbackReceived = true;
// continue as before
}
See also this question answered by one of the Azure developers:
Azure mobile service custom API calling SQL SP multiple times