Cloud function works locally, but not after deploying it - javascript

I am writing my very first Cloud Function which replaces the word "happy" with a smile.
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
exports.emojify = functions.database.ref("/messages/{pushId}/text").onWrite((change, context) => {
const original = change.before.val();
const emojified = emojifyText(original);
return admin.database().ref().set(emojified);
});
function emojifyText(text) {
let t = text;
t = t.replace(/\b(H|h)appy\b/ig, "😀");
console.log("Result:", t);
return t;
};
I found out I can test before deploying by running firebase functions:shell and doing like this:
firebase > emojify({ before: "Happy!" })
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
info: Result: 😀!
info: Execution took 2949 ms, user function completed successfully
It works. However, when testing with my Android app, the logs of my function will show:
TypeError: Cannot read property 'replace' of null
at emojifyText (/user_code/index.js:15:13)
at exports.emojify.functions.database.ref.onWrite (/user_code/index.js:8:23)
at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:112: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:82:36)
at /var/tmp/worker/worker.js:716:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
I don't understand.
After some new attempts, my code is the following:
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
exports.emojify = functions.database.ref("/messages/{pushId}/text").onWrite((change, context) => {
// if (!change.before.val()) { return null; }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// I tried with this and without it, but neither helped.
const original = change.after.val();
const emojified = original.replace(/\b(H|h)appy\b/ig, "😀");
return admin.database().ref("/messages/{pushId}/text").update(emojified);
});
The closest I got was to actually make it erase everything in the base, including the path messages and replace it with the written text, with the text replaced by the emoji. Something like:
But it was using set() instead of update(), which didn't show any sign of modifying anything.

const original = change.before.val(); is the data before the write. So if there was no data at the "/messages/{pushId}/text" node before you write there, the variable original will be null.
You shoud change to:
const original = change.after.val(); which is the data after the write, i.e. your new data, which you want to "emojify".
Update following your comments below
You should use the update() method (doc here), as follows:
return admin.database().ref("/messages/" + context.params.pushId + "/").update({ text: emojified });

Related

Firebase function error: Cannot convert undefined or null to object at Function.keys (<anonymous>)

Description of the problem:
My App aim is to store family spending in Firebase Realtime Database. I want that, when a new spending is stored, a notification is sent to all other devices.
I try to send a notification to a single device and it works fine, but when I try to get all the tokens in an array, I have an error:
TypeError: Cannot convert undefined or null to object at Function.keys ().
code of index.js :
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.androidPushNotification = functions.database
.ref("{nodo}/spese/{spesaPush}")
.onCreate(async (snapshot, context) => {
const original = snapshot.val();
const getDeviceTokensPromise = admin.database()
.ref(`/utenti/{tutti}/token`).once('value');
let tokensSnapshot;
let tokens;
tokensSnapshot = await getDeviceTokensPromise;
// This is the line generating the errors.
// If I get only a specific token and
// use tokens = tokensSnapshot.val(); anything works fine
tokens = Object.keys(tokensSnapshot.val());
const result = original.chi + " ha speso " +
original.costo + " € per acquistare " +
original.desc;
console.log(result);
const payload = {
notification: {
title: 'New spending inserted!',
body: result,
}
};
const response = await admin.messaging().sendToDevice(tokens, payload);
return result;
});
It seems that values are not reached yet, but I thought that the keyword await lets system to wait until all data are available. From the log I noticed that the value I need is null and I don't understand why.
If I use this line of code:
const getDeviceTokensPromise = admin.database()
.ref(`/utenti/SpecificUser/token`).once('value');
....
//then
tokens = tokensSnapshot.val();
The notification is sent to the device that has the token under the name of "SpecificUser"
EDIT:
I provide a pic of my db. I notice that none of the field is null, so I don't know why I see this error
Thank you to anyone that helps me
i had same error and it is solve by database...
when i saw my database values unfortunately i stored undefined value so my whole result got error like you...
see your whole values and fields that store values properly.

Flutter Firebase Functions Error: Database not defined

I've created a delete oldFiles function for my Database that deletes nodes from my chat messages. I've used the example function provided by Firebase and updated it to fit my use. My database structure is databaseName/messages/{pushId} and I've added const functions = require('firebase-functions') and const admin = require('firebase-admin') and admin.initializeApp(). Here is what I have...
exports.deleteOldItems = functions.database.ref('messages/{pushId}').onWrite(async (change) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const cutoff = (DateTime.now().millisecondsSinceEpoch - CUT_OFF_TIME);
const oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
When I review my Function logs, I'm getting the following errors...
ReferenceError: DateTime is not defined
at exports.deleteOldItems.functions.database.ref.onWrite (/srv/index.js:17:18)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:131:23)
at /worker/worker.js:825:24
at
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
And my Functions are finishing with status: error. Any ideas to what may be going on?
DateTime isn't a valid JavaScript object or identifier. If you want to work with dates and times, you will need to work with Date, as you are in the line just above where you have DateTime. You should probably review the JavaScript documentation for Date to learn how it works.

Cloud Function error "Cannot read property 'data' of undefined"

I recently started playing with cloud functions and I am getting this error:
> TypeError: Cannot read property 'data' of undefined
> at exports.createPost.functions.firestore.document.onCreate (/srv/index.js:15:37)
> at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:131:23)
> at /worker/worker.js:825:24
> at <anonymous>
> at process._tickDomainCallback (internal/process/next_tick.js:229:7)
and this is my code
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const algoliasearch = require('algoliasearch');
const ALGOLIA_APP_ID = "";
const ALGOLIA_ADMIN_KEY = "";
const ALGOLIA_INDEX_NAME = "Posts";
admin.initializeApp(functions.config().firebase);
//const firestore = admin.firestore;
exports.createPost = functions.firestore
.document('User/{UserId}/Posts/{PostsID}')
.onCreate( async (snap, context) => {
const newValue = snap.after.data();
newValue.objectID = snap.after.id;
var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY);
var index = client.initIndex(ALGOLIA_INDEX_NAME);
index.saveObject(newValue);
});
The onCreate function triggers at the right time and the problem is just the error. I have done my research and couldn't figure it out. I hope I can get some help.
Thanks in advance :).
onCreate functions receive a DocumentSnapshot type argument as its first parameter. It looks like your function is not actually expecting that. Since you're trying to use a property called after, it looks like your code is expecting a Change type argument, which will never be the case for onCreate events. Change type objects are delivered to onUpdate or onWrite events only so you can detect the before and after states of a document.
If you want the data from a newly created document in an onCreate type trigger, you should code like this:
exports.createPost = functions.firestore
.document('User/{UserId}/Posts/{PostsID}')
.onCreate( async (snap, context) => {
const newValue = snap.data();
// use the document properties of newValue here

Increment Realtime Database count on new user with Firebase Cloud Function

* UPDATED: THIS WORKS. SEE ANSWER BELOW *
I'm trying to write a Firebase Cloud Function that increments a Realtime Database /userCount value whenever a new user is created.
I've tried the following, but am getting "TypeError: userCountRef.transaction is not a function" in incrementCountOnNewUser.
Transactions are working for my other function incrementCountOnOpen when the value of garage is set to true, but the ref is derived from the after event object.
Any suggestions on how to do this?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// const userCountRef = functions.database.ref("/userCount"); // does NOT work
const userCountRef = admin.database().ref('/userCount'); // THIS WORKS!
exports.incrementCountOnNewUser = functions.auth.user().onCreate((user) => {
return userCountRef.transaction(count => count + 1);
});
exports.incrementCountOnOpen = functions.database.ref("/garage").onUpdate(({after}) => {
const countRef = after.ref.parent.child('count');
const newValue = after.val();
return newValue
? countRef.transaction(count => count + 1)
: null;
});
It turns out that the code above works! I had switched from the commented out code (which does NOT work). I guess it didn't wait long enough for it propagate after I published, because I see it working now!
Sorry for the confusion.

Cloud Function for Firebase Type Error - Cannot Read Property

I'm trying to write a Cloud Function that creates a record whenever someone uses our legacy app to create a record (we have changed the Firebase backend architecture and want to slowly migrate users). However, I'm getting the following error in my logs:
TypeError: Cannot read property 'update' of undefined
at exports.makeNewComment.functions.database.ref.onWrite.event (/user_code/index.js:14:92)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:35:20
at process._tickDomainCallback (internal/process/next_tick.js:129:7)
Here is the script in question:
//required modules
var functions = require('firebase-functions');
const admin = require('firebase-admin');
// Listens for new comments added to /comments/ and adds it to /post-comments/
exports.makeNewComment = functions.database.ref('comments/{commentId}').onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
const commentId = event.params.commentId;
const comment = event.data.val();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
//return event.data.ref.parent.child('post-comments').set(comment);
return functions.database.ref('post-comments/' + comment['postID'] + '/' + commentId).update(comment).then(url => {
return functions.database.ref('user-comments/' + comment['postedBy'] + '/' + commentId).update(comment);
});
});
//initialize
admin.initializeApp(functions.config().firebase);
Thanks!
You can't use functions.database.ref() in the middle of a function to get a ref to somewhere in your database. That's only for defining a new Cloud Function.
If you want a ref to somewhere in your database, you can use event.data.ref or event.data.adminRef to get a ref to the location where the event triggered. You could then use the root property of that to rebuild a new ref to somewhere else in the database. Or you can use your admin object to build a new ref.
It might be helpful to look at some sample code to get a sense of how things work.
Based on Doug's answer, you can replace functions.database.ref with event.data.ref.root.
var functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.makeNewComment = functions.database.ref('comments/{commentId}').onWrite(event => {
const commentId = event.params.commentId;
const comment = event.data.val();
return event.data.ref.root.child('post-comments/' + comment['postID'] + '/' + commentId).update(comment).then(url => {
return event.data.ref.root.child('user-comments/' + comment['postedBy'] + '/' + commentId).update(comment);
});
});
admin.initializeApp(functions.config().firebase);

Categories

Resources