Hi I'm stuck at the part where I get error when getting documents. I want to get the fields for users name.
This is the result i get from the console log: From user id: PGIfPp8wmnR1PB1XeiBfUgSnGO62From user: [object Promise] To user id [object Promise].
What am i doing wrong ?
index.js
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.sendNotification = functions.firestore.document('users/{userID}/notifications/{notificationID}')
.onWrite((change, context) => {
const user_id = context.params.userID;
const notification_id = context.params.notificationID;
console.log('We have notification from: ' + user_id + ' The notification id is: ' + notification_id);
return db.collection('users').doc(user_id)
.collection('notifications').doc(notification_id).get().then(queryResult => {
if (!queryResult.exists) {
console.log('No documents');
}else{
console.log("Document data: " ,queryResult.data());
const from_user_id = queryResult.data().from;
const from_data = admin.firestore().collection('users').doc(from_user_id).get();
const to_data = admin.firestore().collection('users').doc(user_id).get();
console.log("From user id: " + from_user_id + "From user: " + from_data + " To user id " + to_data);
}
return Promise.all([from_data, to_data]).then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
return console.log("FROM: " + from_name + " TO: " + to_name);
});
}).catch(err =>{
console.error('Error getting document', err);
})
});
Error i get
Error getting document ReferenceError: from_data is not defined
at db.collection.doc.collection.doc.get.then.queryResult (/user_code/index.js:31:37)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
In these two lines:
const from_data = admin.firestore().collection('users').doc(from_user_id).get();
const to_data = admin.firestore().collection('users').doc(user_id).get();
You're assinging values to promises that will resolve with document data. You haven't got actual document data yet. You need to use the promises returned by get() to find the actual data, just like you're doing earlier in your function.
Related
I am trying to select data from mysql database, and it has some issues.
It seems, that select query is working fine, in debug I can see, that there are results, but when I return them, there's nothing.
I have that folder structure:
includes\database\createConnection.js
includes\database\databaseOperations.js
includes\routings.js
app.js
app.js
const express = require('express');
const app = express();
const fs = require('fs');
const path = require('path');
const appConfig = JSON.parse(fs.readFileSync(path.resolve("./configs/appconfig.json")))
const routings = require(path.resolve("./includes/routings.js"));
app.use(express.static("public"))
app.listen(3000, () => {
console.log(`Server is running on http://${appConfig.host}:${appConfig.port}`);
});
app.get('/testdb',(req,res)=>{
routings.testdb(req,res);
res.status(200);
});
createConnection.js
function connectToDatabase(){
const fs = require('fs')
const mysql = require('mysql')
const path = require('path');
const dbConfig = JSON.parse(fs.readFileSync(path.resolve("./configs/dbconfig.json")))
let connection = mysql.createConnection({
host: dbConfig.host,
port: dbConfig.port,
user: dbConfig.username,
password: dbConfig.password,
database: dbConfig.database
})
connection.connect(function(error){
if(error) throw error;
});
return connection;
}
module.exports = {connectToDatabase}
databaseOperations.js
function dbExec(connection,query){
connection.query(query, function(err,result,fields){
if(err) console.log("There was an error, while doing select query: " + query);
console.log("Result from dbexec: " + result);
return result;
});
}
function selectUser(connection,username){
let query = `SELECT * FROM users WHERE username = '${username}'`;
let userdata = dbExec(connection,query);
console.log("From select user: " + userdata);
return userdata;
}
module.exports = {selectUser};
routings.js
function testdb(req,res){
const fs = require('fs');
const path = require('path');
const testing = require(path.resolve("./includes/database/createConnection.js"));
const dbo = require(path.resolve("./includes/database/databaseOperations.js"));
let connection = testing.connectToDatabase();
let results = dbo.selectUser(connection,"raitis");
console.log(results);
return (req,res) =>{
res.send(results);
}
}
module.exports = {testdb}
Expected result:
User data returned as an JSON object in a response to client
Actual result:
No data in response, undefined object
In debug console logs I get:
Server is running on http://127.0.0.1:3000
app.js:11
From select user: undefined
includes/database/databaseOperations.js:12
undefined
includes/routings.js:9
Result from dbexec: [object Object]
Which seems really weird, because by that log, it first executes selectUser() without executing dbExec() afterwards, gets back to routings.js does console log there, and only after that, it shows console log from dbExec()
What I am doing wrong here? :)
My data in MySQL -
id, username, password, useralias, sessionid
'0', 'raitis', '123', 'admin', '333'
Your selectUser function does not receive retrun from your dbExec function.
You have two possibilities :
use async / await :
async function dbExec(connection,query){
connection.query(query, function(err,result,fields){
if(err) console.log("There was an error, while doing select query: " + query);
console.log("Result from dbexec: " + result);
return result;
});
}
async function selectUser(connection,username){
let query = `SELECT * FROM users WHERE username = '${username}'`;
let userdata = await dbExec(connection,query);
console.log("From select user: " + userdata);
return userdata;
}
use callback function
function dbExec(connection,query,callback){
connection.query(query, function(err,result,fields){
if(err) console.log("There was an error, while doing select query: " + query);
console.log("Result from dbexec: " + result);
return result;
});
}
function selectUser(connection,username){
let query = `SELECT * FROM users WHERE username = '${username}'`;
dbExec(connection,query,function(result) {
console.log("From select user: " + result);
return result;
});
}
I'm using firestore onUpdate trigger and trying to get the data of the
updated document (field name, new value).
const newValue = change.after.data();
const name = newValue.name;
I expect to notify the user with the new mark added to his profile for example:
the new attend mark is 50
But when i display them in the body of the notification it shows:
Here is the full cloud function snippet:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//functions.config().firebase
exports.updateUser = functions.firestore
.document('stuThird/{userId}/stuMarks/{markId}')
.onUpdate((change, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = change.after.data();
// ...or the previous value before this update
const previousValue = change.before.data();
// access a particular field as you would any JS property
const name = newValue.name;
var body = ' the new mark of' + name + 'is'+ newValue;
if(newValue){
var message = {
notification: {
title: 'new mark changed',
body: body,
},
topic: 'bebo'
};
}
// Send the message.
return admin.messaging().send(message)
.then((message) => {
return console.log('Successfully sent message:', message);
})
.catch((error) => {
console.error('Error sending message:', error);
});
// perform desired operations ...
});
**
here is the database:
**
I am writing a Cloud Function in Firebase that sends a notification to devices with tokens that are stored as documents in a collection:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendDMNotification = functions.firestore.document('/dm_threads/{thread_id}/messages/{message_id}')
.onCreate((snapshot, context) => {
const newMessage = snapshot.data();
const senderName = newMessage.authorName;
const senderID = newMessage.authorUID;
const messageText = newMessage.message;
const recipientName = newMessage.recipientName;
const recipientID = newMessage.recipientUID;
const timestamp = newMessage.timestamp;
console.log(senderName + " " + senderID + " " + messageText + " " + recipientName + " " + recipientID + " " + timestamp.toString());
let deviceTokenQuery = admin.firestore().collection(`/Users/${recipientID}/device_tokens/`);
return deviceTokenQuery.get().then(querySnapshot => {
console.log('deviceTokenQuery returned');
let tokens = querySnapshot.docs;
console.log('1');
const notificationPromises = tokens.map(token => {
console.log('2');
let token_id = token.tokenID.toString();
console.log('3');
const payload = {
notification: {
title: senderName,
body: messageText,
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload)
});
console.log('4');
return Promise.all(notificationPromises);
});
});
Only "deviceTokenQuery returned", "1", and "4" log statements appear. Do I have a syntax problem or am I not using these these methods properly?
I want to test the jobs I'm running with agenda. I have written the following test but then I realized that all the expect()... functions are not getting called before it determines that the test had passed or failed. I put some console.log() by all the expect() functions and they do get called just not before it determines if the tests result. I tried adding expect.assertions(6); at the top of the test but it didn't make the test wait longer, it just failed saying it didn't find all 6.
How do I make jest wait for the whole test to finish?
This is the code I want to test:
rules.js
const Account = require("./../models/user.account");
const Listing = require("./../models/user.account.listing");
const Message = require("./../models/user.account.listing.message");
const MessageRule = require("./../models/user.account.listing.messageRule");
const Reservation = require("./../models/user.account.listing.reservation");
const Core = require("./../core");
const moment = require("moment-timezone");
require("colors");
module.exports = function(agenda) {
agenda.define("send message", {priority: "highest"}, async (job, done) => {
try {
console.time("send message " + job.attrs._id);
const userID = job.attrs.data.userID;
const listingID = job.attrs.data.listingID;
const reservationID = job.attrs.data.reservationID;
const messageRuleID = job.attrs.data.messageRuleID;
const sendDate = job.attrs.data.sendDate;
//console.log("Rule Job:".cyan + " send message - listingID: " + listingID);
let messageQuery = {
userID,
listingID,
reservationID,
messageRuleID
};
let message = await Message.findOne(messageQuery);
if (message) {
throw new Error(
"Message has already been sent. userID: " +
userID +
" listingID: " +
listingID +
" reservationID: " +
reservationID +
" messageRuleID: " +
messageRuleID +
" message._id: " +
message._id +
" message.sendDate: " +
message.sendDate +
" message.sentDate: " +
message.sentDate
);
} else {
const isLastMinuteMessage = false;
let listing = await Listing.findById(listingID);
let account = await Account.findById(listing.accountID);
let messageRule = await MessageRule.findById(messageRuleID);
let reservation = await Reservation.findById(reservationID);
// Check that it found all of the required documents
if (!listing || !listing._id) {
throw new Error("Missing the listing document. userID: " + userID + " listingID: " + listingID);
}
if (!account || !account._id) {
throw new Error(
"Missing the account document. userID: " + userID + " accountID: " + listing.accountID
);
}
if (!messageRule || !messageRule._id) {
throw new Error(
"Missing the messageRule document. userID: " + userID + " messageRuleID: " + messageRuleID
);
}
if (!reservation || !reservation._id) {
throw new Error(
"Missing the reservation document. userID: " + userID + " reservationID: " + reservationID
);
}
// Double check the send date by recalculating it then checking comparing them
if (messageRule.event == "checkin" || messageRule.event == "checkout") {
let sendDateCheck = moment.tz(
reservation.startDate + " " + messageRule.time,
"YYYY-MM-DD H",
listing.timeZone
);
if (messageRule.event == "checkout") {
sendDateCheck = sendDateCheck.add(reservation.nights, "day");
}
sendDateCheck = sendDateCheck.add(messageRule.days, "day");
if (!sendDateCheck.isSame(sendDate)) {
throw new Error(
"Message send date and calculated send date don't match. userID: " +
userID +
" listingID: " +
listingID +
" reservationID: " +
reservationID +
" messageRuleID: " +
messageRuleID +
" sendDate: " +
moment(sendDate).format() +
" sendDateCheck: " +
moment(sendDateCheck).format()
);
}
}
await Core.buildMessage(account, listing, messageRule, reservation, isLastMinuteMessage);
}
console.timeEnd("send message " + job.attrs._id);
done();
} catch (error) {
console.error("Rule Job: send message", error);
return done(error);
}
});
};
And here is my test:
rules.test.js
const Account = require("./../models/user.account");
const Listing = require("./../models/user.account.listing");
const Message = require("./../models/user.account.listing.message");
const MessageRule = require("./../models/user.account.listing.messageRule");
const Reservation = require("./../models/user.account.listing.reservation");
const Core = require("./../core");
const Rules = require("./rules");
const Docs = require("./../tests/docs");
describe("Tests for rules.js", () => {
// Mock moment()
Date.now = jest.fn(() => 1538794832371); //October 5th 2018 8pm PST
beforeEach(() => {
jest.clearAllMocks();
});
describe("rules()", () => {
it("should send a message, message exists but is enabled", async () => {
// Mock Message
// Message.findOne()
const MessageFindOneMock = jest.spyOn(Message, "findOne");
const MessageFindOneResult = Docs.messageReviewReminderDisabledThenEnabled;
const MessageFindOne = jest.fn(() => MessageFindOneResult);
MessageFindOneMock.mockImplementation(MessageFindOne);
const userID = Docs.userID;
const reservation = Docs.reservationInTheFuture;
const messageRule = Docs.messageRuleCheckUp;
const accountID = Docs.listing.accountID;
const listingID = Docs.listing._id;
const reservationID = reservation._id;
const messageRuleID = messageRule._id;
// Mock Listing
// Listing.findById()
const ListingFindByIdMock = jest.spyOn(Listing, "findById");
const ListingFindByIdResult = Docs.listing;
const ListingFindById = jest.fn(() => ListingFindByIdResult);
ListingFindByIdMock.mockImplementation(ListingFindById);
// Mock Account
// Account.findById()
const AccountFindByIdMock = jest.spyOn(Account, "findById");
const AccountFindByIdResult = {_id: accountID};
const AccountFindById = jest.fn(() => AccountFindByIdResult);
AccountFindByIdMock.mockImplementation(AccountFindById);
// Mock MessageRule
// MessageRule.findById()
const MessageRuleFindByIdMock = jest.spyOn(MessageRule, "findById");
const MessageRuleFindByIdResult = messageRule;
const MessageRuleFindById = jest.fn(() => MessageRuleFindByIdResult);
MessageRuleFindByIdMock.mockImplementation(MessageRuleFindById);
// Mock Reservation
// Reservation.findById()
const ReservationFindByIdMock = jest.spyOn(Reservation, "findById");
const ReservationFindByIdResult = reservation;
const ReservationFindById = jest.fn(() => ReservationFindByIdResult);
ReservationFindByIdMock.mockImplementation(ReservationFindById);
// Mock Core
// Core.buildMessage()
const CoreBuildMessageMock = jest.spyOn(Core, "buildMessage");
const CoreBuildMessage = jest.fn((account, listing, messageRule, reservation, isLastMinuteMessage) => {
expect(account._id).toBe(accountID);
expect(listing._id).toBe(listingID);
expect(messageRule._id).toBe(messageRuleID);
expect(reservation._id).toBe(reservationID);
});
CoreBuildMessageMock.mockImplementation(CoreBuildMessage);
// Run test
const sendDate = "2018-11-08T10:00:00-06:00";
const done = jest.fn(error => {
expect(error).toBeFalsy();
expect(CoreBuildMessage).toHaveBeenCalledTimes(10);
});
const job = {
attrs: {
data: {
userID,
listingID,
reservationID,
messageRuleID,
sendDate
}
}
};
let agenda = {
define: jest.fn((name, options, callback) => {
if (name == "send message") {
callback(job, done);
}
})
};
Rules(agenda);
});
});
});
In your rules.js you have defined your callback as an async function:
agenda.define("send message", {priority: "highest"}, async (job, done) // <--- this
However you are calling it with:
define: jest.fn((name, options, callback) => {
if (name == "send message") {
callback(job, done);
}
})
So there is no await there ...
Which explains why you get stuff running but not awaiting for it to finish.
Should be something like:
define: await jest.fn((name, options, callback) => {
if (name == "send message") {
await callback(job, done);
}
})
I am retrieving data from the firestore to get the data. I store the data into a variable. I need to use that variable in another part of the code. when I use that it shows me an undefined code. I retrieved the user details and sending the notification . In datapayload I need to pass the retrieved field . But it shows an error undefined
cloud functions
const functions = require('firebase-functions');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.splitting = functions.firestore
.document('deyaPayUsers/{authid}/Split/{authid1}/SentInvitations/{autoid}')// Here the path is is triggereing
.onWrite(event =>{
var id = event.params.authid;//here we get the authid of the user who triggeres.
var dbref = db.collection('deyaPayUsers').doc(id);
var sendername ;
var getDoc = dbref.get()
.then(doc =>{
if(!doc.exists){
console.log("No Such Document");
}else{
console.log('Document data of the firstname', doc.data().FirstName);
sendername = doc.data().FirstName;
}
console.log("sent"+sendername);
})
.catch(err => {
console.log("error getting document",err);
});
console.log(id);
var id1 = event.params.authid1;
var splitid = event.params.autoid;
console.log("laha"+sendername);
var document = event.data.data();
var phoneNumber = [];
//In loop
for(var k in document){ // this line says about looping the document to get the phonenumber field in all the invites
phoneNumber.push(document[k].PhoneNumber);// All the phonenumbers are stored in the phoneNumber array.
}
console.log("The json data is " +phoneNumber);
var ph1 = document.Invite1.PhoneNumber;//It gets the single user phoneumber in Invite1
var amount = document.Invite1.Amount;//It gets the amount of the single user in invite1 in integer
var a = amount.toString();// It is used to convert the Integer amount into String
console.log(a);
console.log(document);
console.log(ph1);
var deyaPay = db.collection("deyaPayUsers");// This path is user profile path to query with phonenumber
for(var k in document){ // here It is the loop in a document to get the phonenumber
var p = document[k].PhoneNumber;// here we get the phonenumber for every loop in invite and stored in p variable
var am = document[k].Amount;// we get the amount of that invite
var ams = am.toString();// converting integer amount into string
console.log("AMount of the user"+ams);
let phQuery = deyaPay.where('PhoneNumber','==',p)// This line says that checking the user proile PhoneNumber field and the document phonenumber which is stored in p variable.
.get()
.then(snapshot => {
snapshot.forEach(doc=>{ // It loops all the documnets whether the PhoneNumbers are matching with user profiles phonenumber
console.log(doc.id, " => ", doc.data());// If matches it prints the doc id and the user data
var userData = doc.data();//Here we get the doc data of that matched phonenumber
var userId = doc.id;// here it we get the id of the Matched profile
var FirstName = userData.FirstName;
console.log(FirstName);
var LastName = userData.LastName;
console.log(FirstName);
var FullName = FirstName + LastName;
console.log(FullName);
var Token = userData.FCMToken; // we need to take that fcm token to send the notification
console.log(userId);
console.log("FCM Token for that phoneNumber" + Token);
console.log(userData.PhoneNumber);
console.log(ph1 + "Exist In DB");
var msg = FirstName + " "+ "requested you to pay $" +ams;
console.log("total notification is" +msg);
let payload = { //This is for sending notification message
notification: {
title: "Message",
body: msg,
sound: "default",
},
'data':{// these is the data it calls in the messagereceived method
'Name':sendername,
'Amount':ams
}
};
console.log(payload);
return admin.messaging().sendToDevice(Token, payload).then((response)=> { // This method is used for returning the notification to a specific device
console.log(Token);
console.info("Successfully sent notification")
}).catch(function(error) {
console.warn("Error sending notification " , error)
});
});
}) .catch(err => {
console.log('Error getting documents', err);
});
}
});
try this, Problem is whenever you perform any async operation in node.js, it does not wait for it to complete execute next line.
const functions = require('firebase-functions');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.splitting = functions.firestore
.document('deyaPayUsers/{authid}/Split/{authid1}/SentInvitations/{autoid}') // Here the path is is triggereing
.onWrite(async (event) => {
try {
const responses = [];
var id = event.params.authid; //here we get the authid of the user who triggeres.
var dbref = db.collection('deyaPayUsers').doc(id);
var sendername;
var doc = await dbref.get();
if (!doc.exists) {
console.log("No Such Document");
} else {
console.log('Document data of the firstname', doc.data().FirstName);
sendername = doc.data().FirstName;
}
console.log(id);
var id1 = event.params.authid1;
var splitid = event.params.autoid;
console.log("laha" + sendername);
var document = event.data.data();
var phoneNumber = [];
//In loop
for (var k in document) { // this line says about looping the document to get the phonenumber field in all the invites
phoneNumber.push(document[k].PhoneNumber); // All the phonenumbers are stored in the phoneNumber array.
}
console.log("The json data is " + phoneNumber);
var ph1 = document.Invite1.PhoneNumber; //It gets the single user phoneumber in Invite1
var amount = document.Invite1.Amount; //It gets the amount of the single user in invite1 in integer
var a = amount.toString(); // It is used to convert the Integer amount into String
console.log(a);
console.log(document);
console.log(ph1);
var deyaPay = db.collection("deyaPayUsers"); // This path is user profile path to query with phonenumber
for (var k in document) { // here It is the loop in a document to get the phonenumber
var p = document[k].PhoneNumber; // here we get the phonenumber for every loop in invite and stored in p variable
var am = document[k].Amount; // we get the amount of that invite
var ams = am.toString(); // converting integer amount into string
console.log("AMount of the user" + ams);
let snapshot = await deyaPay.where('PhoneNumber', '==', p) // This line says that checking the user proile PhoneNumber field and the document phonenumber which is stored in p variable.
.get();
snapshot.forEach(doc => { // It loops all the documnets whether the PhoneNumbers are matching with user profiles phonenumber
console.log(doc.id, " => ", doc.data()); // If matches it prints the doc id and the user data
var userData = doc.data(); //Here we get the doc data of that matched phonenumber
var userId = doc.id; // here it we get the id of the Matched profile
var FirstName = userData.FirstName;
console.log(FirstName);
var LastName = userData.LastName;
console.log(FirstName);
var FullName = FirstName + LastName;
console.log(FullName);
var Token = userData.FCMToken; // we need to take that fcm token to send the notification
console.log(userId);
console.log("FCM Token for that phoneNumber" + Token);
console.log(userData.PhoneNumber);
console.log(ph1 + "Exist In DB");
var msg = FirstName + " " + "requested you to pay $" + ams;
console.log("total notification is" + msg);
let payload = { //This is for sending notification message
notification: {
title: "Message",
body: msg,
sound: "default",
},
'data': { // these is the data it calls in the messagereceived method
'Name': sendername,
'Amount': ams
}
};
console.log(payload);
const response = await admin.messaging().sendToDevice(Token, payload)
console.info("Successfully sent notification")
responses.push(response);
});
};
return responses; //here
} catch (error) {
console.info("error", error)
}
});