My function is triggered by a database event and uses Firebase Cloud Messaging to send a notification to a topic. My first function works fine, but the second one keeps throwing this error:
2018-02-20T21:16:49.878Z E receiveMessage: TypeError: admin.messaging(...).send is not a function
at exports.receiveMessage.functions.database.ref.onCreate (/user_code/index.js:55: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:695:26
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
here is the index.js file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
exports.recceiveInvitation = /* the function that works */;
exports.receiveMessage = functions.database.ref('/messages/{chatId}/{time}').onCreate((event) => {
const chatId = event.params.chatId;
console.log('messages', chatId);
var sender = event.data.val().name;
var messageContent = event.data.val().message;
if(messageContent.length >= 100){
messageContent = messageContent.substring(0,97)+"...";
}
const payload = {
data: {
title: `New Message from ${sender}`,
body: messageContent
},
topic: chatId
};
return admin.messaging().send(payload);
});
I ran npm install firebase-admin, but I did not help.
change this:
return admin.messaging().send(payload);
to this:
return admin.messaging().sendToTopic(topicname,payload);
to be able to send notifications to topics.
You can do the above, or check the note below
Note:
You need to update the firebase-admin npm package, to be able to use send():
npm install firebase-admin#latest
more info here:-
https://firebase.google.com/support/release-notes/admin/node
https://firebase.google.com/docs/cloud-messaging/admin/send-messages
let message = {notification: {title: "your title",body: "your message",},token:"token of user device"
};
admin.messaging().send(message)
Related
I am trying to log each time an account is created and deleted.
I created a trigger functions.auth.user().onCreate() and as I understand it returns an user object as in the docs: here, and here.
The functions deploy without trouble but when the trigger is called it throws an error:
Error: Process exited with code 16
at process.<anonymous> (/layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/invoker.js:92:22)
at process.emit (events.js:314:20)
at process.EventEmitter.emit (domain.js:483:12)
at process.exit (internal/process/per_thread.js:168:15)
at sendCrashResponse (/layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/logger.js:44:9)
at process.<anonymous> (/layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/invoker.js:88:44)
at process.emit (events.js:314:20)
at process.EventEmitter.emit (domain.js:483:12)
at processPromiseRejections (internal/process/promises.js:209:33)
at processTicksAndRejections (internal/process/task_queues.js:98:32)
Error which I cannot understand.
Here is my code
// functions/index.js
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const { google } = require('googleapis')
const { firestore } = require("firebase-admin");
exports.logging = require('./logging');
admin.initializeApp()
// And other working functions
The actual functions
// functions/logging.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const { firestore } = require('firebase-admin');
const authUserTrigger = functions.auth.user()
exports.userSignup = authUserTrigger.onCreate((user) => {
storeUser(user)
})
exports.userDelete = authUserTrigger.onDelete((user) => {
storeUser(user)
})
async function storeUser(user) {
// functions.logger.log(user.email) -> this works
// Destructured original object
let updatedUser = (({ displayName, email }) => ({ displayName, email }))(user);
functions.logger.log('updatedUser', updatedUser )
await admin
.firestore()
.collection('logs')
.doc('users')
.collection('signup')
.set({
user: {updatedUser}, // I think this is the culprint
// user, This doesn't work either
createTimestamp: firestore.FieldValue.serverTimestamp()
}, { merge: true })
};
Thank you in advance
EDIT ==========
#Tanay was right. Needed to change set to add.
As #Tanay stated, you cannot use set() in a collection in Firebase, it must be a document. If you want to add a document to the collection with an auto ID then you can use add() on the collection with the data.
When i checking my "img" entry on firebase with google cloud function and the request module i get an error but only when i deployed it on local emulator it works.
Error i get on my cloud logs:
removeExpiredDocuments
Exception from a finished function: TypeError: Cannot read properties of undefined (reading 'statusCode')
Example URL for 404:
https://ch.cat-ret.assets.lidl/catalog5media/ch/article/5104560/third/lg/5104560_01.jpg
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const requesth = require('request');
admin.initializeApp();
exports.removeExpiredDocuments = functions.region('europe-west1').runWith({ memory: "256MB" }).pubsub.schedule("every 1 hours").onRun(async (context) => {
const db = admin.firestore();
const snaps = await db.collection("products").get();
let promises = [];
snaps.forEach((snap) => {
requesth(snap.data().img, function (error, response) {
functions.logger.info('[img] error: ' + error, { structuredData: true });
if ((response) && (response.statusCode) == 404) {
promises.push(snap.ref.delete());
functions.logger.info('[img] not found ' + snap.data().name, { structuredData: true });
}
});
});
return Promise.all(promises);
});
I wanna use google cloud function for checking if the "img" entry is statusCode 404 and if its statusCode 404 delete the document.
#edit new random error without changed anything
#2 Image of random change errors
I'm trying to get an emphemoral key from stripe using firebase functions. Here is the error I see in my firebase logs:
getStripeEphemeralKeysUnhandled error TypeError: Cannot read property 'create' of undefined at exports.getStripeEphemeralKeys.functions.https.onCall (/workspace/index.js:40:42) at func (/workspace/node_modules/firebase-functions/lib/providers/https.js:273:32) at process._tickCallback (internal/process/next_tick.js:68:7)
Here is the error that shows in the xcode console:
Error creating ephenmeral key: Error Domain=com.firebase.functions Code=13 "INTERNAL" UserInfo={NSLocalizedDescription=INTERNAL} INTERNAL
firebase function written in Javascript:
const functions = require('firebase-functions');
const stripe_key = "sk_test_LT2Wc4v9kUD6XxG8Nq2LNh4P00thTqtiSa"
const admin = require('firebase-admin');
var stripe = require('stripe')(stripe_key);
admin.initializeApp(functions.config().firebase);
exports.createStripeCustomer = functions.https.onCall(async (data, context) => {
const full_name = data.full_name;
const email = data.email;
const customer = await stripe.customers.create({
email: email,
name: full_name,
description: full_name
});
console.log('new customer created: ', customer.id)
return {
customer_id: customer.id
}
});
exports.getStripeEphemeralKeys = functions.https.onCall(async (data, context) => {
const api_version = data.api_version;
const customer_id = data.customer_id;
const key = await stripe.ephemeralKeys.create (
{customer: customer_id},
{apiVersion: api_version}
);
return key;
});
iOS call using swift
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let stripe_customer_id = MyDefaults.getDefaultsForCustID()
functions.httpsCallable("getStripeEphemeralKeys").call(["api_version" : apiVersion, "customer_id" : stripe_customer_id]) { (response, error) in
if let error = error {
completion(nil, error)
}
if let response = (response?.data as? [String: Any]) {
completion(response, nil)
}
}
}
I'm not versed enough in Javascript to know what the error means except there seems to be a problem with the word create. Any thoughts are much appreciated!
OK Thanks #floatingLomas #Rafael Lemos. You guys were on the right track. It was the stripe version. I was having some pod errors that I was pushing to fix later so cleaned up and updated cocoa pods ruby and npm... now when I firebase deploy - I finally got the right version of stripe up to firebase...
I"M GOOD NOW -- THANKS!!!
I keep getting this error when I run this Firebase function:
TypeError: Cannot read property 'add' of undefined
at visaCountry (/srv/index.js:51:11)
at exports.dialogflowFirebaseFulfillment.functions.https.onRequest (/srv/index.js:105:40)
at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:57:9)
at /worker/worker.js:783:7
at /worker/worker.js:766:11
at _combinedTickCallback (internal/process/next_tick.js:132:7)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
The agent variable works in the scope that it is set, but returns undefined when inside the visaCountry function. Does anyone know why this is happening?
Function
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
var serviceAccount = require("./config/sdkjfhdsjkhfjdsf.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://sdfdsfjshdfjds.firebaseio.com"
});
var db = admin.firestore();
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Agent = '+ agent);
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
// An action is a string used to identify what needs to be done in fulfillment
let action = agent.action;
console.log('Actions = '+ JSON.stringify(action));
// Parameters are any entites that Dialogflow has extracted from the request.
const parameters = agent.parameters; // https://dialogflow.com/docs/actions-and-parameters
// Contexts are objects used to track and store conversation state
const inputContexts = agent.contexts;
function visaCountry(agent) {
let visasRead = db.collection('requirements').doc('India').get();
console.log('Agent = '+ agent);
agent.add(`Here are the visas for` + visasRead.name);
}
let intentMap = new Map();
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('Visa Search Request', visaCountry());
// intentMap.set('your intent name here', googleAssistantHandler);
agent.handleRequest(intentMap);
});
The reason behind that is because you defined your function as function visaCountry(agent) { }. Technically trying to access the agent variable from the function scope which you didn't pass and that's why it is undefined.
Just simply pass to visaCountry() to the variable agent.
Like the following:
intentMap.set('Visa Search Request', visaCountry(agent));
Imagine the following:
const agent = 'data';
run();
run(agent);
function run(agent) {
console.log(agent);
}
I hope that helps!
I have updated the question as found the root cause of the issue.
As I have hosted my React SSR app which uses firebase database in the client serving by one of the cloud function named app throwing an error of Error: FIREBASE FATAL ERROR: Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.. When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo?
ORIGINAL Question: Why firebase cloud function throwing an error of 'The default Firebase app does not exist.'?
So I am trying out firebase function for the first time. admin.messaging() throwing me the following error. Help me figure out why?
If I look at the console I get results till console.log('deviceToken', deviceToken);
so whats wrong in const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.updateUnreadCount = functions.database.ref('/chats/{chatId}/{messageId}')
.onCreate(async(snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const adminApp = admin.initializeApp(appOptions, 'app');
const { message, senderId, receiverUid } = snap.val();
console.log(message, senderId, receiverUid);
console.log('------------------------');
const deleteApp = () => adminApp.delete().catch(() => null);
try {
const db = adminApp.database();
const reciverUserRef = await db.ref(`users/${receiverUid}/contacts/${senderId}/`);
console.log('reciverUserRef', reciverUserRef);
const deviceTokenSnapshot = await reciverUserRef.child('deviceToken').once('value');
const deviceToken = await deviceTokenSnapshot.val();
console.log('deviceToken', deviceToken);
const payload = {
notification: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
}
};
const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);
console.log('Successfully sent message: ', JSON.stringify(messageDone));
return deleteApp().then(() => res);
} catch (err) {
console.log('error', err);
return deleteApp().then(() => Promise.reject(err));
}
});
Update1: According to this https://firebase.google.com/docs/cloud-messaging/send-message#send_to_a_topic, admin.messaging().sendToDevice(deviceToken, payload) APIs are only available in the Admin Node.js SDK?
So switched to
const payload = {
data: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
},
token: deviceToken
};
const messageDone = await admin.messaging().send(payload);
Which is not working either. Getting an error Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services. Any lead will be helpful.
EDIT: Finally got the function working.
My index.js is exporting to functions, follwoing
exports.app = functions.https.onRequest(app); //React SSR
exports.updateChat = functions.database.ref('/chats/{chatId}/{messageId}').onCreate(updateChat);
exports.app is a react ssr function, which I am using to host my site. This uses database too. and throwing error of multiple database instance.
When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo? Any suggestions, please?
You can initialise db outside export function.
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(appOptions, 'app')
//continue code
Update:
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(options);
async function initialize(options, apps = 'app') {
try {
const defaultApp = adminApp.name
if(defaultApp) {
const adminApp1 = admin.initializeApp(apps);
}else {
const adminApp1 = admin.initializeApp(options, apps);
}
}catch(err) {
console.error(err);
}
}
Modify this snippet as per your need and try it out
It abstracts initialize of app in another function. Just call this function at appropriate place in your code.