ReferenceError when trying to send notification with Firebase via Node.js - javascript

So I am trying to send a notification via Functions on Firebase.
I am doing the notification programming on JavaScript via Node.js.
Upon clicking Send Friend Request from one account, the other person is suppose to get a notification as specified on the payload of the JavaScript file I have attached below.
I keep getting the following error on my Firebase Functions
ReferenceError: event is not defined.
Here is an image of the exact error.
Here is my JavaScript file:
/*
* Functions SDK : is required to work with firebase functions.
* Admin SDK : is required to send Notification using functions.
*/
//This runs JavaScript in Strict Mode, which prevents the use of things such as undefined variables.
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
/*
* 'OnWrite' works as 'addValueEventListener' for android. It will fire the function
* everytime there is some item added, removed or changed from the provided 'database.ref'
* 'sendNotification' is the name of the function, which can be changed according to
* your requirement
*/
exports.sendNotification = functions.database.ref('/Notifications/{retrieveUserId}/{notificationId}').onWrite((data, context) => {
/*
* You can store values as variables from the 'database.ref'
* Just like here, I've done for 'user_id' and 'notification'
*/
const retrieveUserId = context.params.retrieveUserId;
const notificationId = context.params.notificationId;
console.log('User id is : ', retrieveUserId);
//Prevents notification being sent if there are no logs of notifications in the database.
if (!event.data.val()) {
return console.log('A notification has been deleted from the database : ', notificationId);
}
const deviceToken = admin.database().ref(`/Users/${retrieveUserId}/device_token`).once('value');
return deviceToken.then(result => {
const tokenId = result.val();
const payload = {
notification: {
title: "Friend Request",
body: "You have received a friend request from Slim Shady",
icon: "default"
}
};
return admin.messaging().sendToDevice(tokenId, payload).then(response => {
return console.log('Notification was sent to the user');
});
});
});
This is a picture of parents and children of my Firebase database referred to in the JavaScript file.
As the error states an event not being defined, I'm trying to figure out which event I have not defined.
What is the issue here?

You have not defined event in this code block:
if (!event.data.val()) {

Related

Terminate asynchronous firebase-function properly [duplicate]

This question already has an answer here:
Why is my PDF not saving intermittently in my Node function?
(1 answer)
Closed last year.
As described in the firebase docs, it is required to
"resolve functions that perform asynchronous processing (also known as
"background functions") by returning a JavaScript promise."
(https://firebase.google.com/docs/functions/terminate-functions?hl=en).
otherwise it might happen, that
"the Cloud Functions instance running your function does not shut down
before your function successfully reaches its terminating condition or
state. (https://firebase.google.com/docs/functions/terminate-functions?hl=en)
In this case I am trying to adapt a demo-code for pdf-generation written by Volodymyr Golosay on https://medium.com/firebase-developers/how-to-generate-and-store-a-pdf-with-firebase-7faebb74ccbf.
The demo uses 'https.onRequest' as trigger and fulfillis the termination requirement with 'response.send(result)'. In the adaption I need to use a 'document.onCreate' trigger and therefor need to find a different termination.
In other functions I can fulfill this requirement by using async/await, but here I am struggling to get a stable function with good performance. The shown function logs after 675 ms "finished with status: 'ok' ", but around 2 minutes later it logs again that the pdf-file is saved now (see screenshot of the logger).
What should I do to terminate the function properly?
// adapting the demo code by Volodymyr Golosay published on https://medium.com/firebase-developers/how-to-generate-and-store-a-pdf-with-firebase-7faebb74ccbf
// library installed -> npm install pdfmake
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
const Printer = require('pdfmake');
const fonts = require('pdfmake/build/vfs_fonts.js');
const fontDescriptors = {
Roboto: {
normal: Buffer.from(fonts.pdfMake.vfs['Roboto-Regular.ttf'], 'base64'),
bold: Buffer.from(fonts.pdfMake.vfs['Roboto-Medium.ttf'], 'base64'),
italics: Buffer.from(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'),
bolditalics: Buffer.from(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'),
}
};
exports.generateDemoPdf = functions
// trigger by 'document.onCreate', while demo uses 'https.onRequest'
.firestore
.document('collection/{docId}')
.onCreate(async (snap, context) => {
const printer = new Printer(fontDescriptors);
const chunks = [];
// define the content of the pdf-file
const docDefinition = {
content: [{
text: 'PDF text is here.',
fontSize: 19 }
]
};
const pdfDoc = printer.createPdfKitDocument(docDefinition);
pdfDoc.on('data', (chunk) => {
chunks.push(chunk);
});
pdfDoc.on('end', async () => {
const result = Buffer.concat(chunks);
// Upload generated file to the Cloud Storage
const docId = "123456789"
const bucket = admin.storage().bucket();
const fileRef = bucket.file(`${docId}.pdf`, {
metadata: {
contentType: 'application/pdf'
}
});
await fileRef.save(result);
console.log('result is saved');
// NEEDS PROPER TERMINATION HERE?? NEEDS TO RETURN A PROMISE?? FIREBASE DOCS: https://firebase.google.com/docs/functions/terminate-functions?hl=en
// the demo with 'https.onRequest' uses the following line to terminate the function properly:
// response.send(result);
});
pdfDoc.on('error', (err) => {
return functions.logger.log('An error occured!');
});
pdfDoc.end();
});
I think everything is fine in your code. It seems it takes 1m 34s to render the file and save it to storage.
Cloud function will be terminated automatically when all micro and macro tasks are done. Right after you last await.
To check how long does it takes and does it terminate right after saving, you can run the firebase emulator on your local machine.
You will see logs in the terminal and simultaneously watch on storage.
I suspect you did terminate properly - that's the nature of promises. Your function "terminated" with a 200 status, returning a PROMISE for the results of the PDF save. When the PDF save actually terminates later, the result is logged and the promise resolved. This behavior is WHY you return the promise.

Best to way to Run a Node JS script on Google Cloud

First of all, "I'm noob"
My first time running some script on a cloud server.
So, I have a code that counts the number of views from my youtube vídeo and writes it in the title.
But I want that code running 24/7.
I make it works running on a VM instance on google cloud using cron-node running the code every 5 minutes and make it run forever with "forever" on the node.
And my question is: Have a better way to run it? Like using cloud functions or something? Or I should still run it on VM instance?
If yes, what should I do? Because I don't know how to use external dependencies on Cloud function like google API, if I don't have a console to install googleapi dependencies. (Maybe it's easy to make it, but I just don't know yet).
var readline = require('readline');
var { google } = require('googleapis');
var OAuth2 = google.auth.OAuth2;
const cron = require("node-cron");
cron.schedule("*/5 * * * *", () => {
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/youtube-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'update-youtube-title.json';
const youtube = google.youtube('v3');
const video_id = 'f0ARwVVxoBc';
// Load client secrets from a local file.
fs.readFile('credentials.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the YouTube API.
authorize(JSON.parse(content), makeAuthCall);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function (err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function (code) {
rl.close();
oauth2Client.getToken(code, function (err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) throw err;
console.log('Token stored to ' + TOKEN_PATH);
});
}
/**
* Lists the names and IDs of up to 10 files.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
/*
function getChannel(auth) {
var service = google.youtube('v3');
service.channels.list({
auth: auth,
part: 'snippet,contentDetails,statistics',
forUsername: 'GoogleDevelopers'
}, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var channels = response.data.items;
if (channels.length == 0) {
console.log('No channel found.');
} else {
console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
'it has %s views.',
channels[0].id,
channels[0].snippet.title,
channels[0].statistics.viewCount);
}
});
}
*/
//código novo
const makeAuthCall = (auth) => {
//get views
youtube.videos.list(
{
auth: auth,
id: video_id,
part: 'snippet,statistics'
},
(err, response) => {
if (err) {
console.log(`A contagem de views falhou ${err}`)
return;
}
if (response.data.items[0]) {
//atualização encontrada
console.log('Encontramos uma atualização...');
updateVideoTitle(response.data.items[0], auth);
}
}
);
};
const updateVideoTitle = (video, auth) => {
let viewsText = video.statistics.viewCount.toLocaleString();
const newTitle = `Esse Vídeo tem ${viewsText} Visualizações`;
const oldTitle = video.snippet.title;
if (newTitle != oldTitle){
video.snippet.title = newTitle;
console.log(`Atualizando contagem de views para ${viewsText}`);
youtube.videos.update(
{
auth: auth,
part: 'snippet,statistics',
resource: video,
},
(err, response) => {
console.log(response)
if (err) {
console.log(`A contagem de views falhou ${err}`);
return;
}
if (response.data.items) {
console.log("Done");
}
}
);
}else{
console.log("Atualização não necessária");
};
}
})
This is a very broad question with too much to unpack. Keep in mind the differences below are very simplistically explained and there could be more steps.
Compute Engine/VM:
Step 1. Install javascript runtime (Nodejs) on the OS.
Step 2. Put your code & dependencies
Cloud Functions:
Step 1. Put your code & dependencies
Summary:
Cloud Functions removes the need to manage the operating system and
runtime. It might be easier for you. One way or another, you won't
escape the need for your dependency setup.
For Practice:
These "quick start" articles will give an understanding of the setup process. You learn by doing with a "hello world" example.
For Dependencies:
There is an article here that shows how to declare them in Cloud Functions. The sections of this article explains how you can load dependencies in different ways. The first example says: "The dependency is then imported in the function" and you see the part that says "require('escape-html')", it's the same as the "escape-html": "^1.0.3". This means that you can work backwards and see the requirements in your code to add them to your dependency manifest. But you may have to learn a bit more about the syntax by doing a Google search about package.json/npm and how it works in general.
For Authentication:
Assuming your code is properly written, the "require('googleapis')" at the top of your code means that you should only need to add dependencies like mentioned previously and your code may be good to go for authentication.
For Cron jobs:
In your code you are doing that by using "const cron = require("node-cron");" at the top of you code. This is like everything else I mentioned about dependencies and it's a software that triggers your code. I'm not sure if this will work like all other ones because Google has its own Cron jobs that work like this (outside the Cloud Functions environment, but triggers the Cloud Functions on the network). Keep in mind that this method could alter your code significantly. So if you can test with const cron = require("node-cron"); dependencies, then do that first.
Disclaimer:
Please open more specific questions if you can next time. Stackoverflow is not for consultation or broad questions with many follow-ups. It's more for coding answers to specific questions. Some may decide to close this post due to that.
Hope this helps.

Cloud Function is not running - React Native

I'm new to React Native and I have an issue with Cloud Functions.
getAuthor(uid){
var getUser = firebase.functions().httpsCallable('getUser');
console.log('success');
getUser({uid: uid}).then(function(result) {
console.log('getUser called')
var user = result.data.uid;
return(
result
)
})
.catch(function(error) {
var code = error.code;
var message = error.message;
var details = error.details;
});
}
When I run this code, 'success' is printed but 'getUser called' is never printed. I take this to mean getUser is never called. But I have followed the Firebase guide 'Call functions from your app' and it seems to be the same. Is it a problem with Cloud Functions never being initialised or something? In the Firebase guide it says to initialise an instance of Cloud Functions by adding
var functions = firebase.functions();
but when I added it to config.js I got an error so I skipped this step. Sorry if this seems obvious, I have never used React Native or Firebase before. Any help would be greatly appreciated!
Edit: the function has been deployed, as can be seen in this screenshot
This is the function, by the way:
exports.getUser = functions.https.onCall((data, context) => {
const uid = data.uid;
auth.getUser(uid)
.then(function(UserRecord) {
console.log('USER RECEIVED:' + UserRecord.email.toJSON());
return {email : UserRecord.email.toJSON()};
})
.catch(function(error){
console.log(error);
});
Turns out I can't access user information with their UID! The solution is to add it into Firebase Database instead.

|SOLVED| Get User Auth data from Firebase & add it to Firebase DB

Hello!
EDIT~
I've eventually managed to do this and wanted to share it just in case anyone else needs it. Most tutorials I've found were outdated and none of them seemed to work for me. But I've finally got everything to work so Here it is!
Sign Up -
(I've created a sign up form with input fields for the username, extra info, password and email)
Make sure you import all firebase scripts you want to use and ABOVE all of them, the firebase app main script. In my case I only needed
Auth & Database - And BELOW all of this you put your Firebase App config and import either an
external .js file where you'll be using firebase functions or write it
all down there. This was a very silly mistake I did myself and I kept getting errors on the console. This is because I've been
trying to call my external .js file BEFORE importing the firebase main
scripts, which makes no sense right?
So here's my .js file for the
sign up function:
//On a different .js file where I make use of most of my functions I've added this part
//(just because I've defined more const for all my functions and I wanted to have them all
//in one place):
//getting all elements -- I've only put in this example the ones that I've used for Sign Up
const signupBtn = document.getElementById("btnsignUp");
const txtEmail = document.getElementById('txtEmail');
const txtPassword = document.getElementById('txtPassword');
const userId = document.getElementById('txtName');
const discord = document.getElementById('txtDiscord');
const bday = document.getElementById('txtBday');
const gender = document.getElementById('txtGender');
const imgURL = document.getElementById('txtimgURL');
//getting references to the apps
const auth = firebase.auth();
const database = firebase.database();
const rootRef = database.ref('users');
//------------------------------------------------//
//firebase SIGN UP.js
signupBtn.addEventListener("click", function(){
var email = txtEmail.value;
var pass = txtPassword.value;
//Signing up
auth.createUserWithEmailAndPassword(email, pass)
.then(() => {
//send verification email
sendVerificationEmail();
})
.catch(function(error) {
// Handle Errors here.
//var errorCode = error.code;
var errorMessage = error.message;
alert("Error :" + errorMessage);
});
});
//verification email function
var sendVerificationEmail = () => {
auth.currentUser.sendEmailVerification()
.then(() => {
alert("Verification Email Sent! Check your mailbox.")
})
.catch(error => {
alert("Error :" + errorMessage);
})
}
//DATABASE
//'set' adds new data to he db
signupBtn.addEventListener('click', (e) => {
e.preventDefault();
rootRef.child(userId.value).set({
Email: txtEmail.value,
Discord: discord.value,
Gender: gender.value,
Birthday: bday.value,
ImgURL: imgURL.value,
CC: 0,//Here I've added some more info that's be stored too along with
RS: 0,//the data that the user has provided
Rupreets: 0,
Bag: 1,//1: small, 2: medium, 3: big
})
});
//And that's all!
In my case, what I did with the database part is something like this:
-App name-
|
+--Users:
|
+--username1
|
+-info1
|
+-info2
|
+-info2
|
+--username2
|
+-info1
|
+-info2
|
+-info2
Well, I hope this will help somebody else too n.n
welcome to Stack Overflow!
A couple of things:
You never actually call writeUserData in your code snippet; you just define it. If you don't call it, nothing will be written.
userId is never defined, so even if you called writeUserData, your database path would be be undefined. You'd need to get the userId from firebase.auth().currentUser.uid. For more on that, see this Firebase doc: https://firebase.google.com/docs/auth/unity/manage-users#get_a_users_profile .
-- Edit --
Here's a code sample. I haven't put in absolutely everything, just the relevant omissions:
//Signing up
firebase.auth().createUserWithEmailAndPassword(email, pass)
.then((data) => {
// createUserWithEmailAndPassWord returns a promise, which, when resolved will contain various user-related properties, so
let id = data.User.uid;
function writeUserData(userId, name, email, imageURL) {
firebase.database().ref('Users/' + userId).set({
Username: name,
Email: email,
Profile_pic: imageURL,
});
}
// call your function, referencing the id above
writeUserData(id, name, email, imageURL)
If the idea of promises and calling functions isn't comfortable, you might look at a Javascript learning resource like javascript.info
Good luck!

How can I get a 'get' request to run on a schedule in NodeJS?

The function I would like this function to run by itself at time intervals. As it is now I have to visit the '/getCompanyInfo' path to trigger it. I would like it to run every minute as if I was visiting the '/getCompanyInfo' path each minute. The app is on Heroku and I would like the function to execute without any pages open.
The original function that is triggered by visiting the path.
const express = require('express');
const app = express();
/**
* getCompanyInfo ()
*/
app.get('/getCompanyInfo', function(req,res){
const companyID = oauthClient.getToken().realmId;
console.log(companyID)
const url = OAuthClient.environment.production ;
oauthClient.makeApiCall({url: url + 'v3/company/0000000000/salesreceipt/8?minorversion=41'})
.then(function(authResponse){
console.log("The response for API call is :"+JSON.parse(JSON.stringify(authResponse)));
res.send(authResponse);
})
.catch(function(e) {
console.error(e);
});
});
One of my attempts here was to put it in a function that executes each minute using node-schedule.
This one doesn't do anything other than print 'This will run once a minute.' to the console.
I tried removing
app.get(function(req,res){
and the
})
below it but that made the app (hosted on Heroku) fail to build.
const express = require('express');
const app = express();
var schedule = require('node-schedule');
var j = schedule.scheduleJob('* * * * *', function(){
console.log('This will run once a minute.');
app.get(function(req,res){
const companyID = oauthClient.getToken().realmId;
console.log(companyID)
const url = OAuthClient.environment.production ;
oauthClient.makeApiCall({url: url + 'v3/company/0000000000/salesreceipt/8?minorversion=41'})
.then(function(authResponse){
console.log("The response for API call is :"+JSON.parse(JSON.stringify(authResponse)));
res.send(authResponse);
})
.catch(function(e) {
console.error(e);
});
});
});
More Context:
It is inside an app I have on Heroku. I would like to set the app to make a requests for JSON data from the API every x time without me having to touch it.
app.get initializes api handler - e.g. this is your api route definition - the thing that will respond when you call GET /getCompanyInfo via web browser or some other client. You should not redefine it regularly with your scheduled action.
The failed build after you've removed the route handler is probably because of the res.send(authResponse); left behind.
You could have something like:
// function that will be used to get the data
const getCompanyInfo = (done) => {
const companyID = oauthClient.getToken().realmId
console.log(companyID)
const url = OAuthClient.environment.production
oauthClient.makeApiCall({url: url + 'v3/company/0000000000/salesreceipt/8?minorversion=41'})
.then((authResponse) => {
console.log("The response for API call is :"+JSON.parse(JSON.stringify(authResponse)))
done(authResponse)
})
.catch((e) => {
console.error(e)
})
}
// this will trigger the function regularly on the specified interval
const j = schedule.scheduleJob('* * * * *', () => {
getCompanyInfo((companyInfo) => {
// ...do whatever you want with the info
})
})
// this will return you the data by demand, when you call GET /getCompanyInfo via browser
app.get('/getCompanyInfo', function(req,res) {
getCompanyInfo((companyInfo) => {
res.send(companyInfo)
})
})
Heroku has an add on called Heroku Scheduler that does what you want. The node-schedule npm package might do the job, but as you mentioned, you probably aren't going to be able to see the execution/results/logs of your jobs that run every 24 hours without making some interface for it on your own.
For your issue, calling app.get doesn't make a lot of sense. That's just telling node about the route. Assuming you have your /getCompanyInfo route up and running, you just need to call it in your scheduled job, not re-register it every time.
You could also just do this (http being the http client you're using):
var j = schedule.scheduleJob('* * * * *', async function(){
console.log('This will run once a minute.');
const result = await http.get('/getCompanyInfo');
console.log(result);
});

Categories