Error upon Cloud Function for Firebase deployment - node.js

I've been trying to deploy a Cloud Function to my Firebase project.
It's my first time doing so, also my first time programming with JavaScript.
Here's my code in Node.JS:
'use strict'
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
const firebaseTriggers = functions.region('europe-west1').firestore;
const db = admin.firestore();
exports.postNotification = firebaseTriggers
.document('/post notifications/{notificatioId}').onWrite((snap, context) => {
const notifcationRecieverId = snap.data().mReciever;
const payload = {
data: {
notification_type: 'POST',
title: snap.data().mTitle,
body: snap.data().mDescription,
sender_id: snap.data().mSender,
reciever_id: snap.data().mReciever,
notification_id: context.params.notificatioId
}
};
return db.collection('dog owners')
.document(notifcationRecieverId)
.get()
.then(recieverDoc => {
console.log('Retrieving FCM tokens');
const tokens = recieverDoc.data().mTokens;
console.log('Sending notification payload');
return admin.message().sendToDevice(tokens, payload);
});
});
Upong deployment, I'm getting the following error:
Can someone help me understand why?

Firstly you have got space in your colleciton name. This is bad convetion.
post notifications => postNotifications

Related

How to use twilio with Firebase Cloud Function?

I want to send a WhatsApp message to a particular number when a Firebase Realtime Database is triggered. But unable to implement the code properly -
const accountSid = 'AC565656563389214ace8531';
const authToken = '[AuthToken]';
const client = require('twilio')(accountSid, authToken);
client.messages
.create({
body: 'Your appointment is coming up on July 21 at 3PM,
from: 'whatsapp:+1415454545386',
to: 'whatsapp:+9196456454566'
})
.then(message => console.log(message.sid))
.done();
I want to use this code in Firebase function for the trigger in the Realtime database.
The following should do the trick (untested):
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Use one of the options detailed in the doc (https://firebase.google.com/docs/functions/config-env)
// for storing the two following secrets
const accountSid = ...
const authToken = ...
const client = require('twilio')(accountSid, authToken);
exports.sendMessage = functions.database.ref('/.../{pushId}') // Adapt the path
.onCreate((snapshot, context) => { // Adapt the trigger? E.g. OnWrite?
return client.messages
.create({
body: 'Your appointment is coming up on July 21 at 3PM',
from: 'whatsapp:+1415454545386',
to: 'whatsapp:+9196456454566'
})
.then(message => {
console.log(message.sid);
return null;
});
});

Why this callable cloud function is failing with "app":"MISSING"?

I am calling a cloud function which runs a transaction, however it is returning an error to console which says:
Callable request verification passed {"verifications":{"auth":"VALID","app":"MISSING"}}
Googling it led me to App Check which is a new thing in Firebase. I am using React-Native firebase npm packages and following its documentation about App Check is extremely difficult due to lack of proper explanation and examples.
Below I have the code which I am trying to execute in the function:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const firestore_ = admin.firestore();
// const { CustomProvider } = require("#react-native-firebase/app-check");
const appCheckForDefaultApp = admin.appCheck();
const GeoPoint = admin.firestore.GeoPoint;
const FieldValue = admin.firestore.FieldValue;
const _geofirestore_ = require("geofirestore");
const GeoFirestore = _geofirestore_.initializeApp(firestore_);
exports.createNew = functions.runWith({
allowInvalidAppCheckToken: true // Opt-out: Requests with invalid App
// Check tokens continue to your code.
}).region('europe-west6').https.onCall(async (data, context) => {
try {
//Checking that the user calling the Cloud Function is authenticated
if (!context.auth) {
return "The user is not authenticated: " + context.auth;
// throw new UnauthenticatedError('The user is not authenticated. Only authenticated Admin users can create new users.');
}
const longitude = data.longitude;
const latitude = data.latitude;
const thirty_mins_old = data.thirty_mins_old;
const currenttime = data.currenttime;
const GeoFirestore_ = new _geofirestore_.GeoFirestore(firestore_);
const sfDocRef = GeoFirestore_.collection('mycollection')
.limit(1)
.near({ center: new GeoPoint(latitude, longitude), radius: 0.05 });
GeoFirestore.runTransaction((transaction) => {
const geotransaction = new _geofirestore_.GeoTransaction(transaction, new GeoPoint(latitude, longitude));
return geotransaction.get(sfDocRef._collectionPath).then((sfDoc) => {
...
});
});
} catch (error) {
if (error.type === 'UnauthenticatedError') {
throw new functions.https.HttpsError('unauthenticated', error.message);
} else if (error.type === 'NotAnAdminError' || error.type === 'InvalidRoleError') {
throw new functions.https.HttpsError('failed-precondition', error.message);
} else {
throw new functions.https.HttpsError('internal', error.message);
}
}
});
EDIT:
I am debugging the app so I am not working on production. Does debugging still requires this to be configured?
The log message you are seeing is not an error - it's informational.
On each request, your callable functions will verify any auth or appcheck token included in the request. When these tokens are not present, the execution is passed to your handler - it's your responsibility to handle requests with missing tokens if necessary. It looks like you are already handling the case for missing auth token.
When executing functions in your auth emulator, auth/appcheck tokens are minimally verified - i.e. they should be valid JWT token but we don't actually verify the signature to ensure that it's signed by Firebase Auth/AppCheck backend.
If your function is erroring in your development environment, I suspect that the error is elsewhere.

Express JS: Send Databse as Variable / Parameter while requiring a module

Say I have the following code
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const serviceAccount = require("./permissions.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://blah-blah-blah.firebaseio.com"
});
const db = admin.firestore();
app.use("/auth", require("./auth"));
Now the problem is that I have to use the cloud firestore databse functionality, which is curently stored in the variable db, in my auth.js file as well, without using admin.initializeApp a second time. Is there any way to acomplish that, like importing/exporting the db variable etc.
This is where I am using it in auth.js:
const express = require("express");
const auth = express.Router();
db.collection("data")
.doc("sample")
.get()
.then(
document => {
if (document.exists) {
return done(null, {
id: user.id,
name: user.name,
email: user.email
});
} else {
//create document
}
},
err => {
//handle error
}
);
Of course, right now db will be undefined, which is the problem I need to tackle
Since version 1.0.0 of the Firebase SDK for Cloud Functions you have to initialize with admin.initializeApp();, see the doc here.
Then, to interact with Firestore, you just need to use the Admin SDK, for example as follows:
admin.firestore().collection('....').get();
So, if I understand correctly your question, the following changes should do the trick:
index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
//const db = admin.firestore(); not sure you need that here, it depends if you interact (or not) with Firestore in index.js
app.use("/auth", require("./auth"));
auth.js
const admin = require("firebase-admin");
const db = admin.firestore();
//....
db.collection("data")
.doc("sample")
.get()
.then(
document => {
if (document.exists) {
return done(null, {
id: user.id,
name: user.name,
email: user.email
});
} else {
//create document
}
},
err => {
//handle error
}
);
//....

Cannot read property 'From' of undefined at /user_code/index.js

I am working on sending notification between two applications. I have tried to do this through node.js but now I am stuck at this error for 1 month but found no solution to it.please help me out in it. this is very important for me.I will be very thankful for any kind of help.
"use-strict"
const functions = require('firebase-functions');
const admin= require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification= functions.firestore.document('Users/{user_id}/Notifications/{notification_id}').onWrite((change, context) => {
const user_id= context.params.user_id;
const notification_id= context.params.notification_id;
return admin.firestore().collection("ServiceProviders").doc(user_id).collection("Notifications").doc("notification_id").get().then(querySnapshot => {
const from_user_id= querySnapshot.data().From;
const from_message= querySnapshot.data().message;
const from_data= admin.firestore.collection("Users").doc(from_user_id).get();
const to_data= admin.firestore.collection("Users").doc(user_id).get();
return Promise.all([from_data, to_data]).then(result => {
const from_name=result[0].data().name;
const to_name=result[1].data().name;
const token_id= result[1].data().token_id;
const payload= {
notifications:{
title: "Notification from : " + from_name,
body: from_message,
icon:"default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(result => {
return console.log("Notification Sent");
});
});
});
here is the monstrous error`
Your firestore query isn't returning anything; probably because either the document/collection doesn't exist.
Right off the bat, it stands out that your "notification_id" is queried as string, rather than the const you set earlier. It's kind of a gamble, but changing line 10 to this may help:
return admin.firestore().collection("ServiceProviders").doc(user_id).collection("Notifications").doc(notification_id).get().then(querySnapshot => {

Promise not returning value on request

I have been trying to get this to work, but am new to NodeJS. I suspect the issue is due to async, but am not familiar with how it works.
The idea behind this code is that it monitors a firebase database change and sends an email to the users. I am getting everything from the change snapshot, and using the values to check another table for user data. The request is not returning before the email gets sent and I am unsure why.
Edit I should specify that the email function sgMail is firing off before I get the results from the requests. I've tried putting a delay, but I am still not getting the result to return in time.
Here's my index.js
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
var requestify = require('requestify');
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database.ref('Order/{orderID}')
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = 'https://shlep-me-f516e.firebaseio.com/User/'+shipperInfo+'.json';
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
});
function getTravelerData() {
return new Promise(resolve => {
requestify.get('https://shlep-me-f516e.firebaseio.com/User/' + travelerInfo + '.json')
.then(function (response) {
resolve(response.getBody())
});
});
}
var TravelD = getTravelerData();
//Send an email
const msg = {
to: 'andrew#shlepme.com',
from: 'support#shlepme.com',
subject: 'New Follower',
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: 'd1ccfeb9-2e2d-4979-a3ca-c53975fe486e',
substitutionWrappers: ['%', '%'],
substitutions: {
'%shipper_name%': "Test",
'traveler_name': TravelD.name
// and other custom properties here
}
};
console.log('Sending email');
console.log(TravelD);
return sgMail.send(msg)
});
Any ideas? I have been trying to figure this out.
It seems that you need to understand about Promises first.
When you start using promises you will need to ALWAYS use them and chain one with the other.
So I would rewrite your code like this: (not tested)
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require("firebase-functions");
var requestify = require("requestify");
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require("#sendgrid/mail");
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database
.ref("Order/{orderID}")
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = "https://shlep-me-f516e.firebaseio.com/User/" + shipperInfo + ".json";
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
var shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
})
.then(function (shipperResult) {
//Send an email
const msg = {
to: "andrew#shlepme.com",
from: "support#shlepme.com",
subject: "New Follower",
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: "d1ccfeb9-2e2d-4979-a3ca-c53975fe486e",
substitutionWrappers: ["%", "%"],
substitutions: {
"%shipper_name%": "Test",
traveler_name: shipperResult.name
// and other custom properties here
}
};
console.log("Sending email");
console.log(shipperResult);
return sgMail.send(msg);
});
});

Resources