Firebase Cloud function for Push notification - firebase

I am able to send the push notification without delivery_receipt_requested. On adding the delivery_receipt_requested field, I am getting error.
I want to update a field in the database whenever the notification is delivered to the device from Firebase cloud function.
Error sending message: { Error: Invalid JSON payload received. Unknown
name "delivery_receipt_requested" at 'message': Cannot find field.
at FirebaseMessagingError.Error (native)
at FirebaseMessagingError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:39:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:85:28)
at new FirebaseMessagingError (/user_code/node_modules/firebase-admin/lib/utils/error.js:241:16)
at Function.FirebaseMessagingError.fromServerError (/user_code/node_modules/firebase-admin/lib/utils/error.js:271:16)
at /user_code/node_modules/firebase-admin/lib/messaging/messaging-api-request.js:149:50
at process._tickDomainCallback (internal/process/next_tick.js:135:7) errorInfo: { code:
'messaging/invalid-argument',
message: 'Invalid JSON payload received. Unknown name "delivery_receipt_requested" at \'message\': Cannot find field.' },
codePrefix: 'messaging' }
my payload structure
var payload = {
delivery_receipt_requested:true,
notification: {
title: user,
body: snapshot2.val()
},
android: {
notification: {
sound: "default"
}
},
token: userToken
};
admin.messaging().send(payload)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
return 1;
})
.catch((error) => {
console.log('Error sending message:', error);
return 0;
});
return snapshot2.val()
}).catch(error => {
console.log("Error" + userToken2)
});

Related

Sync user with Firebase functions to Hasura GraphQL

I want to use firebase to authenticate users and then firebase functions to insert users into Hasura but having problems with the firebase functions.
When I try to create a user from the app the "registerUser" function, which can be found below, it ends with an error:
Error detected in registerUser:
{"#type":"type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.Insight",
"errorGroup":"CLic1cmw6emOsAE",
"errorEvent":{"message":"Error: The uid must be a non-empty string with at most 128 characters.
at FirebaseAuthError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)\
at new FirebaseAuthError (/srv/node_modules/firebase-admin/lib/utils/error.js:147:16)
at AuthRequestHandler.AbstractAuthRequestHandler.setCustomUserClaims (/srv/node_modules/firebase-admin/lib/auth/auth-api-request.js:996:35)
at Auth.BaseAuth.setCustomUserClaims (/srv/node_modules/firebase-admin/lib/auth/auth.js:342:40)
at exports.registerUser.functions.https.onCall (/srv/index.js:32:18)
at func (/srv/node_modules/firebase-functions/lib/providers/https.js:272:32)
at corsHandler (/srv/node_modules/firebase-functions/lib/providers/https.js:292:44)\n at cors (/srv/node_modules/cors/lib/index.js:188:7)
at /srv/node_modules/cors/lib/index.js:224:17","eventTime":"2020-06-10T08:25:03.017Z","serviceContext":{"service":"registerUser","resourceType":"cloud_function"}}}
If I instead create a user directly via the firebase console my "processSignUp" runs
but ends with another error:
ReferenceError: fetch is not defined
at GraphQLClient.<anonymous> (/srv/node_modules/graphql-request/dist/src/index.js:108:25)
at step (/srv/node_modules/graphql-request/dist/src/index.js:44:23)
at Object.next (/srv/node_modules/graphql-request/dist/src/index.js:25:53)
at /srv/node_modules/graphql-request/dist/src/index.js:19:71
at new Promise (<anonymous>)
at __awaiter (/srv/node_modules/graphql-request/dist/src/index.js:15:12)
at GraphQLClient.request (/srv/node_modules/graphql-request/dist/src/index.js:98:16)
at exports.processSignUp.functions.auth.user.onCreate (/srv/index.js:60:25)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:132:23)
at /worker/worker.js:825:24
I've tried pretty much everything I could think of. I've used https://hasura.io/jwt-config/ to setup the JWT on Heroku. I've triple checked passwords and graphQL endpoint. I have no problems with the mutations or query variables when I play around in hasura console but I'm unable to connect the firebase functions to hasura. Thanks in advance.
functions/index.js
...
const client = new request.GraphQLClient(
"https://app-name.herokuapp.com/v1/graphql",
{
headers: {
"content-type": "application/json",
"x-hasura-admin-secret": "Password",
},
}
);
...
// On register.
exports.registerUser = functions.https.onCall((data) => {
const { email, password } = data;
try {
const userRecord = admin.auth().createUser({ email, password });
const customClaims = {
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": userRecord.uid,
},
};
admin.auth().setCustomUserClaims(userRecord.uid, customClaims);
return userRecord.toJSON();
} catch (e) {
let errorCode = "unknown";
let msg = "Something went wrong, please try again later";
if (e.code === "auth/email-already-exists") {
errorCode = "already-exists";
msg = e.message;
}
throw new functions.https.HttpsError(errorCode, msg, JSON.stringify(e));
}
});
...
// On sign up.
exports.processSignUp = functions.auth.user().onCreate(async (user) => {
const { uid: id, email } = user;
const mutation = `
mutation($id: String!, $email: String) {
insert_users(objects: [{
id: $id,
email: $email,
}]) {
affected_rows
}
}
`;
try {
const data = await client.request(mutation, { id, email });
return data;
} catch (e) {
throw new functions.https.HttpsError("invalid-argument", e.message);
}
});
In the package.json for your functions, try changing the node engine to 10 and your grapql-request package to 1.8.2.

Firebase Admin SDK NodeJS -- "There is no user record corresponding to the provided identifier." Error

Following the Firebase SDK docs on https://firebase.google.com/docs/auth/admin/email-action-links#generate_email_verification_link and getting the following error, which makes little sense as the function is triggered from the server environment using the authenticated admin.auth().
Might anyone know what is causing the issue?
Error from admin.auth().generateEmailVerificationLink : { Error: There is no user record corresponding to the provided identifier.
at FirebaseAuthError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseAuthError (/srv/node_modules/firebase-admin/lib/utils/error.js:147:16)
at Function.FirebaseAuthError.fromServerError (/srv/node_modules/firebase-admin/lib/utils/error.js:186:16)
at /srv/node_modules/firebase-admin/lib/auth/auth-api-request.js:1201:49
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
errorInfo:
{ code: 'auth/user-not-found',
message: 'There is no user record corresponding to the provided identifier.' },
codePrefix: 'auth' }
The code is just this:
exports = module.exports = functions.firestore
.document("xxx/{docId}")
.onCreate(async (snap, context) => {
let yyy = snap.data();
let { uid, userData } = yyy;
console.log("from sendEmailVerification, uid, userdata: ", userData);
const actionCodeSettings = {
url: `${emailConfirmationUrl}`,
handleCodeInApp: false
};
return admin.auth().generateEmailVerificationLink(userData.email, actionCodeSettings)
.then(async (link) => {
console.log("uid from generateEmailVerificationLink and email: ", uid, userData.email)
await admin.firestore().collection('users').doc(uid).set({
verificationLink: link,
emailVerified: false
}, { merge: true });
return emailfunc.sendCustomVerificationEmail(userData.email, link);
})
.catch((err) => {
console.error("Error from admin.auth().generateEmailVerificationLink :", err);
return Promise.reject(err);
});
});
You read the user's email address from the database (Firestore). That user account, however, doesn't exist in Firebase Auth. It must also exist in Firebase Auth if you wish to use APIs like getUser() and generateEmailVerificationLink(). Having it only in Firestore is not enough.

Timeout occurred when trying to send messages from firebase admin

Am trying to send notifications to all registered users from firebase admin every 2 hours with pubsub schedule function
This is code am deploying in firebase functions
exports.scheduledFunction =functions.pubsub.schedule('every 2 hours').onRun((context) => {
console.log('This will be run every 2 hours!');
listAllUsers(undefined)});
function listAllUsers(nextPageToken) {
// List batch of users, 1000 at a time.
admin.auth().listUsers(1000, nextPageToken)
.then(function(listUsersResult) {
listUsersResult.users.forEach(function(userRecord) {
pushCheckMessageNotification(userRecord.customClaims['latest']);
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
})
.catch(function(error) {
console.log('Error listing users:', error);
});
}
function pushCheckMessageNotification(token){
const promise = [];
promise.push(admin.messaging().send(
{
data: {
body: "Test",
title: "Test",
chat: "CHECK_MESSAGES"
},
android:{
priority: "high"
},
token: '' + token,
}).then(() => {
console.log("Pushed notification.");
console.log("Notify Token", token);
}).catch(error => {
console.error("Notification push crash", error);
}));
}
The below error occurred sometimes and notification not pushed
{ Error: Error while making request: timeout of 10000ms exceeded.
at FirebaseAppError.Error (native)
at FirebaseAppError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseAppError.PrefixedFirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseAppError (/user_code/node_modules/firebase-admin/lib/utils/error.js:122:28)
at /user_code/node_modules/firebase-admin/lib/utils/api-request.js:152:23
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
errorInfo:
{ code: 'app/network-timeout',
message: 'Error while making request: timeout of 10000ms exceeded.' },
codePrefix: 'app' }

admin.messaging(...).send is not a function

I am using firebase-admin to send a push notification via Cloud Functions, but I keep getting this error:
TypeError: admin.messaging(...).send is not a function
at exports.sendNotification.functions.database.ref.onWrite (/user_code/index.js:43:27)
at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:59:27)
at next (native)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:28:71
at __awaiter (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:24:12)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:53:36)
at /var/tmp/worker/worker.js:700:26
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
Any idea why it's not working? I am using firebase-admin version 5.9.1, so it is the latest version. It should include the .send function but it's still not working. I have uninstalled the plugin and re-installed it, but still, it did not work.
This is the code I am using:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.cert({
projectId: "xxx",
clientEmail: 'xxx#xxx.iam.gserviceaccount.com',
privateKey: '-----BEGIN PRIVATE KEY-----<KEY>-----END PRIVATE KEY-----\n'
}),
databaseURL: "https://xxx.firebaseio.com"
});
exports.sendNotification = functions.database.ref("xxx").onWrite((event) => {
console.log("notify");
const registrationToken = "xxx";
// See documentation on defining a message payload.
var message = {
notification: {
title: "You have a new service request",
body: "this is the main body"
},
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
return admin.messaging().send(message)
.then(function (response) {
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
What am I doing wrong?
It should be sendToDevice not send, maybe something like this :
var message = {
notification: {
title: "You have a new service request",
body: "this is the main body"
},
data: {
score: '850',
time: '2:45'
}
};
return admin.messaging().sendToDevice(registrationToken, message)
.then(function (response) {
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});

Cloud Functions for Firebase send push to multiple users

I try to get multiple push notifications to run on Cloud Functions for Firebase without success.
I store my message receipts in a node
message_receipts
-KtvyTN3nbVKoFjHdpJg
hHhs5Aco38X1W8EhaaxrwsQDXwy1:"receipt"
nI25FjUnBfQiCWzdCUIAe8CWTPQ2:"receipt"
To send push notifications I try following on cloud functions:
//*********************************************************************************************************** */
//handle lsit item added by shared user
if (String(msgData.messageType) == 'ListItemAddedBySharedUser') {
return admin.database().ref("message_receipts").child(event.params.messageID).once('value').then(receipts => {
receipts.forEach(function (receipt) {
//Send push to receipt
return admin.database().ref('/users/' + receipt.key).once('value').then(usnap => {
//Send push to users fcmToken
const userSnap = usnap.val()
console.log('sending Push to ' + userSnap.fcmToken)
//create Notification Payload
var payload = {
notification: {
title: msgData.title,
body: msgData.message,
badge: '1',
sound: 'default',
sbID: String(event.data.key),
senderID: msgData.senderID,
listID: msgData.listID,
receiptID: receipt.key,
notificationType: String(msgData.messageType),
}
};
return admin.messaging().sendToDevice(userSnap.fcmToken, payload).then(response => {
console.log("Successfully sent invite message:", response)
console.log(response.results[0].error)
}).catch((err) => { console.log("Error sending Push", err) })
})
})
})
} //*********************************************************************************************************** */
All I get is one notification sent.
I'am very new to java script and cloud functions.
What do I need to do to get all of my users notified?
You need to aggregate all of the asynchronous actions you're taking. Here you're doing a forEach on the message receipts, but then you're returning a single promise. Try something like:
var promises = [];
receipts.forEach(function (receipt) {
//Send push to receipt
promises.push(admin.database().ref('/users/' + receipt.key).once('value').then(usnap => {
/* ... */
}))
})
return Promise.all(promises);
This will aggregate all of your outstanding notifications into a single Promise.all call, which will wait until they all complete.
You can send a batch of messages as described in the documentation:
https://firebase.google.com/docs/cloud-messaging/send-message#send-a-batch-of-messages
// Create a list containing up to 500 messages.
const messages = [];
messages.push({
notification: {title: 'Price drop', body: '5% off all electronics'},
token: registrationToken,
});
messages.push({
notification: {title: 'Price drop', body: '2% off all books'},
topic: 'readers-club',
});
return admin.messaging().sendAll(messages)
.then((response) => {
console.log(response.successCount + ' messages were sent successfully');
});

Resources