Firebase function - push notification - javascript

Hi this is my first time working with Firebase push notifications and I'm trying to send a test push notification to a specific device and I'm getting an error when checking the functions. Here is my code and the errors, any help is greatly appreciated.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.testPushNotifications = functions.https.onRequest((req, res) => {
res.send("Trying to send a message")
console.log("LOGGER --- trying to send a push notification..");
const uid = 'O5cN65bAe3Mf7uMr7DCF8GlvsHA3'
const fcmToken = 'cUQ-IcReLTQ:APA91bGgDRIQfFi7k4AIo45b4sXr.........'
return admin.database().ref('/users/' + uid).once('value', snapshot => {
const user = snapshot.val();
console.log("Username is " + user.userName);
const payload = {
notification: {
title: 'Push Notification',
body: 'Test Notification message'
}
}
admin.messaging().sendToDevice(fcmToken, payload)
.then(function(response) => { // 27:30 error Parsing error: Unexpected token =>
console.log('Successfully sent message:', response);
})
.catch(function(error) => {
console.log('Error sending message:', error);
});
});
})
If I remove the => I get the following warnings and error:
admin.messaging().sendToDevice(fcmToken, payload)
.then(function(response) {
console.log('Successfully sent message:', response);
})
.catch(function(error) {
console.log('Error sending message:', error);
});
27:11 warning Unexpected function expression prefer-arrow-callback
27:11 error Each then() should return a value or throw promise/always-return
30:12 warning Unexpected function expression prefer-arrow-callback

Related

NodeJS: Firebase Admin SDK is not initializing

I am trying to run the below cod which initialises the Firebase Admin SDK, and send a notification message.
const admin = require('firebase-admin/app');
const errorCodes = require('source/error-codes');
const PropertiesReader = require('properties-reader');
const prop = PropertiesReader('properties.properties');
exports.sendSingleNotification = async (event, context) => {
const params = event.queryStringParameters;
var serviceAccount = require("xxx-xxx-firebase-adminsdk-xxx-xxx.json");
try {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
console.log("INITIALIZED");
// This registration token comes from the client FCM SDKs.
const registrationToken = params.fcmtoken;
console.log()
const message = {
notification: {
title: 'FooCorp up 1.43% on the day',
body: 'FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'
},
token: registrationToken
};
// Send a message to the device corresponding to the provided
// registration token.
admin.getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
return {"response":response}
})
.catch((error) => {
console.log('Error sending message:', error);
return {"error 1":error}
});
} catch (error) {
console.log(error);
return {"error 2":error}
}
};
Here the serviceAccount means the path of the Firebase private key file which is in the root of this project.
However when I run this code I always end up with the following error.
START RequestId: e66ffdd9-ab9c-4a68-ade2-7cfa97f42c31 Version: $LATEST
at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)] (/var/task/source/fcm/send-single-notification.js:14:42)rt' of undefined
END RequestId: e66ffdd9-ab9c-4a68-ade2-7cfa97f42c31
Something is undefined and I can't figure out what it is or what the error is.
How can I fix this?

408 timeout from Firebase Cloud Messaging after the message

I'm sending push messages using FCM through Firebase Functions. The messages are being sent properly, but I'm getting the 408 time-out error after the message is sent. I'm suspecting it might have to do with the unregistered tokens not being cleaned up because:
if I were to send another message to the same device, the same timeout occurs and
the only error message I get from the Firebase log is Function execution took 60002 ms, finished with status: 'timeout'.
exports.sendMessage = functions.https.onRequest(async (request, response) => {
const {
sender,
recipient,
content,
docID
} = request.body
functions.logger.log(
"docID:",
docID,
);
// Get the list of device notification tokens.
let deviceTokens; let ref;
try {
ref = admin.firestore().collection("deviceToken").doc(recipient);
const doc = await ref.get();
if (!doc.exists) {
console.log("No such document!");
response.status(500).send(e)
} else {
console.log("doc.data():", doc.data());
deviceTokens = doc.data().token;
}
} catch (e) {
response.status(500).send(e)
}
let senderProfile;
try {
senderProfile = await admin.auth().getUser(sender);
console.log("senderProfile", senderProfile);
} catch (e) {
console.log(e);
response.status(500).send(e)
}
// Notification details.
let payload = {
notification: {
title: senderProfile.displayName,
body: content,
sound: "default",
},
data: {
uid: senderProfile.uid,
displayName: senderProfile.displayName,
docID,
messageType: "status"
}
};
functions.logger.log(
"deviceTokens", deviceTokens,
"payload", payload,
);
// Send notifications to all tokens.
const messageResponse = await admin.messaging().sendToDevice(deviceTokens, payload);
// For each message check if there was an error.
messageResponse.results.forEach((result, index) => {
const error = result.error;
if (error) {
functions.logger.error(
"Failure sending notification to",
deviceTokens[index],
error,
);
// Cleanup the tokens who are not registered anymore.
if (error.code === "messaging/invalid-registration-token" ||
error.code === "messaging/registration-token-not-registered") {
const updatedTokens = deviceTokens.filter((token) => token !== deviceTokens[index]);
console.log("updatedTokens", updatedTokens);
ref.update({
token: updatedTokens,
})
.catch(function(e) {
console.error("Error removing tokens", e);
response.status(500).send(e)
});
}
}
});
response.status(200)
});
I'm unsure why the following isn't cleaning up the unregistered tokens:
const updatedTokens = deviceTokens.filter((token) => token !== deviceTokens[index]);
ref.update({
token: updatedTokens,
})
You always have to end HTTP functions with response.status(200).send() or response.status(200).end(). In the above function, you have response.status(200) so you have to end it either with response.status(200).send() or response.status(200).end(). Please check the documentation if it helps.

How to get value inside transaction result Firebase via node js

I'm building an iOS messenger app using Swift, Firebase and Nodejs.
My Goal:
Whenever a user sends a message and writes message data (such as senderId, receiverId, messageText) into a Firebase database inside node (/messages/{pushId}/), I want to make a message count increment by 1 using a transaction method that Firebase provides and display a notification to a receiver user.
Progress I've made so far and Problem I'm facing:
I've successfully increment message count (totalCount) using transaction method but I can't get value inside transaction result (Here's image of functions log )
I want to get "value_: 1"( this is the incremented message count) inside snapshot and put it to a badge parameter.
exports.observeMessages = functions.database.ref('/messages/{pushId}/')
.onCreate((snapshot, context) => {
const fromId = snapshot.val().fromId;
const toId = snapshot.val().toId;
const messageText = snapshot.val().messageText;
console.log('User: ', fromId, 'is sending to', toId);
return admin.database().ref('/users/' + toId).once('value').then((snap) => {
return snap.val();
}).then((recipientId) => {
return admin.database().ref('/users/' + fromId).once('value').then((snaps) => {
return snaps.val();
}).then((senderId) => {
return admin.database().ref('/user-messages/' + toId + '/totalCount').transaction((current) => {
return (current || 0) + 1
}).then((readCount) => {
console.log('check readCount:', readCount);
var message = {
data: {
fromId: fromId,
badge: //I want to display the message count here
},
apns: {
payload: {
aps: {
alert: {
title: 'You got a message from ' + senderId.username,
body: messageText
},
"content-available": 1
}
}
},
token: recipientId.fcmToken
};
return admin.messaging().send(message)
}).then((response) => {
console.log('Successfully sent message:', response);
return response;
})
.catch((error) => {
console.log('Error sending message:', error);
//throw new error('Error sending message:', error);
})
})
})
})
Does anyone know how to do this?
Thanks in advance.
The API documentation for transaction() suggests that the promise from the transaction will receive an object with a property snapshot with the snapshot of the data that was written at the location of the transaction. So:
admin.database.ref("path/to/count")
.transaction(current => {
// do what you want with the value
})
.then(result => {
const count = result.snapshot.val(); // the value of the count written
})

Firebase Firestore FCM message sending issue

My code :
exports.fcmSend = functions.firestore.document('messages/{userId}').onCreate(event => {
console.log("fcm send method");
const message = event.data.data();
const userId = event.params.userId;
const token_id = 'asdfsadfdsafds';
let token = "";
const payload = {
notification: {
title: "Test",
body: "Test",
icon: "https://placeimg.com/250/250/people"
}
};
db.collection('fcmTokens').doc('token_id').get().then((doc) => {
console.log(doc.id, '=>', doc.data());
const data = doc.data();
token = data.token;
console.log("token", token);
})
.then(() => {
return event.data.ref.set({"title": "hello"}).sendToDevice(token, payload);
})
.catch((err) => {
console.log('Error getting documents', err);
return err;
});
});
Error :
Error getting documents TypeError:
event.data.ref.set(...).sendToDevice is not a function
at db.collection.doc.get.then.then (/user_code/index.js:117:50)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
There are two separate Firebase products involved here:
Cloud Firestore, where you stroke the FCM tokens for a user.
the Cloud Messaging Admin SDK, which you use to send notifications to a user.
The sendToDevice method exists on the Admin SDK for Cloud Messaging, not on a Firestore database reference where you're trying to invoke it.
To fix the problem you'll first need to import the Admin SDK into your index.js:
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
Then you modify your functions for step 1 and 2. It should look something like this:
// Load the tokens from Firestore
db.collection('fcmTokens').doc('token_id').get().then((doc) => {
console.log(doc.id, '=>', doc.data());
const data = doc.data();
token = data.token;
console.log("token", token);
const payload = {
notification: {
title: 'hello',
}
};
return admin.messaging().sendToDevice(token, payload)
})
.catch((err) => {
console.log('Error getting documents', err);
return err;
});

Intermittent authentication errors in Cloud Functions

I have a simple Cloud Function that receives a webhook and then does a streaming insert into BigQuery. The code is based on this sample (except that I am using streaming inserts)
exports.webHook = function webHook (req, res) {
return Promise.resolve()
.then(() => {
if (req.method !== 'POST') {
const error = new Error('Only POST requests are accepted');
error.code = 405;
throw error;
}
const events = req.body || {};
if (events) {
const opts = { ignoreUnknownValues: true };
bigquery
.dataset('config.DATASET')
.table('config.TABLE')
.insert(events, opts)
.then((data) => {
console.log(`Success: ${JSON.stringify(data[0])}`);
})
.catch((error) => {
if (error.name === 'PartialFailureError') {
console.error(`PARTIAL ERROR: ${JSON.stringify(error)}`);
} else {
console.error(`OTHER ERROR: ${JSON.stringify(error)}`);
}
});
};
})
.then(() => res.status(200).end())
.catch((err) => {
console.error(err);
res.status(err.code || 500).send(err);
return Promise.reject(err);
});
};
This function works well most of the time, but I do get the occasional authentication error, which then goes away.
textPayload: "OTHER ERROR: {"code":401,"errors":[{"message":"Request
had invalid authentication credentials. Expected OAuth 2 access token,
login cookie or other valid authentication credential. See
https://developers.google.com/identity/sign-in/web/devconsole-project.","domain":"global","reason":"unauthorized"}],"message":"Request
had invalid authentication credentials. Expected OAuth 2 access token,
login cookie or other valid authentication credential. See
https://developers.google.com/identity/sign-in/web/devconsole-project."}"
I am not sure how auth could be an issue since the Cloud Function and BigQuery are all in the same project.
The folks on the Cloud Functions team think this may be due to an issue with the access token time-to-live (TTL) and suggested a workaround that has worked for me. Instead of initializing BigQuery at the top of your code (as all their examples have it), put the initializing code right inside the function that makes the call.
Do this:
exports.webHook = function webHook (req, res) {
const bigquery = require('#google-cloud/bigquery')();
return Promise.resolve()
.then(() => {
if (req.method !== 'POST') {
const error = new Error('Only POST requests are accepted');
error.code = 405;
throw error;
}
.
.
instead of:
const bigquery = require('#google-cloud/bigquery')();
.
.
exports.webHook = function webHook (req, res) {
return Promise.resolve()
.then(() => {
if (req.method !== 'POST') {
const error = new Error('Only POST requests are accepted');
error.code = 405;
throw error;
}
.
.

Categories