Firebase function is not realtime(?) - javascript

I have firebase function(javascript node.js) in flutter app. This function is creating the firebase collection of "timelineLocal" using the data based on "post" collection and "user" collection.
What I want is calling all the post from all the user and locate at the timelineLocal, but my function only calls the new post, not the old one. What am I missing? any help?
users -> userID -> data field user
timelineLocal-> userID -> "timelinePosts" -> data field
posts-> userID-> "userPosts" -> posted -> data field
Here are the code
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
exports.onCreateDistance = functions.firestore.document("/users/{userId}")
.onCreate(async (snapshot, context) => {
console.log("Follower Created", snapshot.id);
const userId = context.params.userId;
// 1) Create users posts ref
const userPostsRef = admin
.firestore()
.collection("posts")
.doc(userId)
.collection("userPosts");
// 2) Create user's timeline ref
const timelinePostsLocalRef = admin
.firestore()
.collection("timelineLocal")
.doc(userId)
.collection("timelinePosts");
// 3) Get users posts
const querySnapshotLocal = await userPostsRef.get();
// 4) Add each user post to user's timeline
querySnapshotLocal.forEach(doc => {
if (doc.exists) {
const postId = doc.id;
const postData = doc.data();
timelinePostsLocalRef.doc(postId).set(postData);
}
});
});
exports.onDeleteDistance = functions.firestore.document("/users/{userId}")
.onDelete(async (snapshot, context) => {
console.log("Follower Deleted", snapshot.id);
const userId = context.params.userId;
const timelinePostsLocalRef = admin
.firestore()
.collection("timelineLocal")
.doc(userId)
.collection("timelinePosts");
const querySnapshotLocal = await timelinePostsLocalRef.get();
querySnapshotLocal.forEach(doc => {
if (doc.exists) {
doc.ref.delete();
}
});
});
exports.onCreatePostLocal = functions.firestore.document('/posts/{userId}/userPosts/{postId}')
.onCreate(async (snapshot, context) => {
const postCreated = snapshot.data();
const userId = context.params.userId;
const postId = context.params.postId;
//1) get all the user who made the post
const usersRef = admin.firestore().collection('users');
const querySnapshotLocal = await usersRef.get();
//2) Add new post to each user's timeline
querySnapshotLocal.forEach(doc => {
const userId = doc.id;
admin.firestore().collection('timelineLocal').doc(userId).collection('timelinePosts').doc(postId).set(postCreated);
});
});
exports.onUpdatePostLocal = functions.firestore.document('/posts/{userId}/userPosts/{postId}').onUpdate(
async (change, context) => {
const postUpdated = change.after.data();
const userId = context.params.userId;
const postId = context.params.postId;
//1) get all the user who made the post
const usersRef = admin.firestore().collection('users');
const querySnapshotLocal = await usersRef.get();
//2) update new post to each user's timeline
querySnapshotLocal.forEach(doc => {
const userId = doc.id;
admin.firestore().collection('timeline').doc(userId).collection('timelinePosts').doc(postId).get().then(doc => {
if (doc.exists) {
doc.ref.update(postUpdated);
}
});
});
});
exports.onDeletePostLocal = functions.firestore.document('/posts/{userId}/userPosts/{postId}').onDelete(
async (change, context) => {
const userId = context.params.userId;
const postId = context.params.postId;
const usersRef = admin.firestore().collection('users');
const querySnapshotLocal = await usersRef.get();
//2) delete new post to each user's timeline
querySnapshotLocal.forEach(doc => {
const userId = doc.id;
admin.firestore().collection('timeline').doc(userId).collection('timelinePosts').doc(postId).get().then(doc => {
if (doc.exists) {
doc.ref.delete();
}
});
});
});

Related

firebase v9 I am storing the User UID from firebase auth into firestore, help me retrieve it for sign in comparison

this is my Register function
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const uid = userCredential.user.uid;
const data = {
id: uid,
email,
fullName,
};
async function storeUserUid() {
const newUser = doc(collection(db, "users", user.uid));
await setDoc(newUser, data);
console.log(uid) = aj3x5gAe2jUcngBoTY5cVpOTITu1
console.log(data) = Object {
"email": "lala#email.com",
"fullName": "lala",
"id": "aj3x5gAe2jUcngBoTY5cVpOTITu1",
}
My login function that needs helping. When authenticating it successfully I have console.logged the (uid) which is the first markup in the screenshow below. I am receiving the same uid from the second console.log(userList) under the "id" key. How can I access the object and compare both values so I can let the user then navigate to the "HomeScreen" if there is a match from the signInwithCredentials and the userList id. I need somehow to map through all the docs inside the "users" collection and compare their "id" keys and if there is a match with the signInwithCredentials uid => then, let the user in. MANY THANKS
const onLoginPress = () => {
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const uid = userCredential.user.uid;
console.log(uid);
async function getUser() {
const q = query(collection(db, "users"));
const userSnap = await getDocs(q);
const userList = userSnap.docs.map((doc) => doc.data());
console.log(userList);
}
getUser();
})
If you don't need to get all user docs, but just the one for the current user, that'd be:
async function getUser() {
const ref = doc(db, "users", FirebaseAuth.instance.currentUser.uid));
const userDoc = await getDoc(ref);
return userDoc.data();
}
getUser();

How to get a child key name randomly from the Firebase Realtime database with a Javascript cloud function

I want to randomly get a child key from the pickers section and then add data to it from another node. I want to do all of this with a JavaScript Cloud Function. Here is my code.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.onDataAdded = functions.database.ref('/Pickup-Requests/{uid}').onCreate((snapshot, context) => {
const getRandomPickerid =
database.ref('/Pickers').once('value').then(event => {
const pickerUid = Object.keys()[random];
return pickerUid;
})
.catch(error => {
console.error("Error", error);
});
const pickerUid = getRandomPickerid;
const data = snapshot.val();
const newData = data;
return snapshot.ref.parent.child(pickerUid).set(newData);
});
How can I do this?
The following should do the trick:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
function randomKey(obj) {
var keys = Object.keys(obj);
return keys[(keys.length * Math.random()) << 0];
}
exports.onDataAdded = functions.database.ref('/Pickup-Requests/{uid}').onCreate((snapshot, context) => {
const db = admin.database();
const data = snapshot.val();
return db.ref('/Pickers').once('value')
.then(snapshot => {
const pickerUid = randomKey(snapshot.val());
return snapshot.ref.parent.child(pickerUid).set(data);
})
});
I would suggest you watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/

Requesting different endpoints with firestore Cloud Function

I am trying to have a flexible Cloud Function that executes on different end points.
My original Cloud Function looks like this:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const _ = require('lodash')
const { getObjectValues } = require('./helper-functions.js')
admin.initializeApp()
const json2csv = require('json2csv').parse
exports.csvJsonReport = functions.https.onRequest((request, response) => {
const db = admin.firestore()
const userAnswers = db.collection('/surveys/CNA/submissions')
return (
userAnswers
.get()
// eslint-disable-next-line promise/always-return
.then(querySnapshot => {
let surveySubmissions = []
querySnapshot.forEach(doc => {
const userSubmission = doc.data()
surveySubmissions.push({
..._.mapValues(userSubmission.answers, getObjectValues), // format answers
...userSubmission.anonUser,
})
})
const csv = json2csv(surveySubmissions)
response.setHeader('Content-disposition', 'attachment; filename=cna.csv')
response.set('Content-Type', 'text/csv')
response.status(200).send(csv)
})
.catch(error => {
console.log(error)
})
)
})
I am trying to extend this function to work on multiple collections. In the above function I am targeting the CNA collection. so instead of db.collection('/surveys/CNA/submissions/') I would like it to be db.collection('/surveys/:surveyId/submissions/')
Below is my attempt at trying to extend my original Cloud Function:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const express = require('express')
const bodyParser = require('body-parser')
const _ = require('lodash')
const { getObjectValues } = require('./helper-functions.js')
admin.initializeApp(functions.config().firebase)
const db = admin.firestore()
const app = express()
const main = express()
main.use('/api/v1', app)
main.use(bodyParser.json())
exports.webApi = functions.https.onRequest(main)
app.get('surveys/:id', (request, response) => {
const surveyId = request.query
const userAnswers = db.collection(`/survey/${surveyId}/submissions`)
return (
userAnswers
.get()
// eslint-disable-next-line promise/always-return
.then(querySnapshot => {
let surveySubmissions = []
querySnapshot.forEach(doc => {
const userSubmission = doc.data()
surveySubmissions.push({
..._.mapValues(userSubmission.answers, getObjectValues), // format answers
...userSubmission.anonUser,
})
})
const csv = json2csv(surveySubmissions)
response.setHeader('Content-disposition', 'attachment; filename=cna.csv')
response.set('Content-Type', 'text/csv')
response.status(200).send(csv)
})
.catch(error => {
console.log(error)
})
)
})
When I request my endpoint: myapp.firebaseapp.com/api/v1/surveys/CNA
Cannot GET /api/v1/surveys/CNA is shown in my browser.
Could someone please point me in the right direction?
To crate a GET /survey/:id endpoint in order to fetch a submission by id, use the following code in your new Cloud Function:
app.get('surveys/:id', (request, response) => {
const surveyId = request.params.id
const userAnswers = db.collection(`/survey/${surveyId}/submissions`)
Let me know if it works for you.

How to Save Payload after sending a notifications in cloud functions?

How I can save a payload after sending them to specific Token in the specific node " Notifications/" to retrieve it in single screen later,
and it saves very well,
but when I got a notification I see providerName as a undefined when I declare a variable "providerName"
const functions = require("firebase-functions");
const admin = require("firebase-admin");
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://khadamatiapp-42657.firebaseio.com"
});
exports.acceptedOrder = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onCreate(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
console.log("#pid", pid);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(snapshot => {
providerName = snapshot.val().username;
console.log("pName", providerName); // here i got ProviderOne
});
console.log("#providerName", providerName); //here i got undefined
const payload = {
notification: {
from: pid,
to: userUid,
title: "New Order",
body: `Hi ${username}, You Order is Accepted from ${providerName}, check it now! `
//Hi userOne, You Order is Accepted from ***Undefined***, check it now!
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
return null;
});
Update
I have three functions and it's a trigger in the same root,
now acceptedOrderFromProvider that's invoked when I create new Element in the "AcceptedOrders" Root and send a push notification
and another function is CompletedOrderFromProvider that's trigger if the status changed, send a notification I use an onUpdate rigger but doesn't work well,
it's invoked when every element created or updated,
so how to force it to invoke just when some field "status" changed?
check here image
exports.acceptedOrderFromProvider = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onCreate(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
// console.log("#pid", pid);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(async snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "Accepted Order",
body: `Hi ${username}, You Order is Accepted from ${providerName}, check it now! `
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});
exports.cancelledOrderFromProvider = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onDelete(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
// console.log("#pid", pid);
const afterData = snapshot.val();
// console.log(afterData);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
const nameOfProblem = snapshot.val().nameOfProblem;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(async snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "Order Cancelled",
body: `Hi ${username}, ${providerName} Cancelled your Order "${nameOfProblem}"!`
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});
exports.CompletedOrderFromProvider = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onUpdate(async (snapshot, context) => {
console.log(snapshot.after.val());
const registrationTokens = snapshot.after.val().userToken;
const pid = context.params.pid;
const username = snapshot.after.val().username;
const userUid = snapshot.after.val().userUid;
const nameOfProblem = snapshot.after.val().nameOfProblem;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(async snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "Order Completed",
body: `Hi ${username}, ${providerName} Completed your Order "${nameOfProblem}"! Check it Now`
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});
Code inside a .then() is run asynchronously, so even though it appears above the rest of the code in the function, it may not be called until much later. Put all code that works with providerName inside the .then() callback to ensure it is called only after providerName has been retrieved:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://khadamatiapp-42657.firebaseio.com"
});
exports.acceptedOrder = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onCreate(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
console.log("#pid", pid);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "New Order",
body: `Hi ${username}, You Order is Accepted from ${providerName}, check it now! `
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});

Firestore Cloud Function - Send E-Mail onCreate with SendGrid

I have a contact form which submits data to the Firestore Database. My intention is, that as soon as there's another entry in the collection requests, Firestore shall fire a function via Cloud Function, which contains the configuration for SendGrid, which again is supposed to send the data of this specific entry to an e-mail.
I've also tried to deploy this function, which was successful - but the console shows the following errors, which I reckon won't be the only one:
Cannot read property 'requestId' of undefined
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
exports.firestoreEmail = functions.firestore
.document('requests/{requestId}')
.onCreate(event => {
const requestId = event.params.requestId;
const db = admin.firestore();
return db.collection('requests').doc(requestId)
.get()
.then(doc => {
const requestId = event.params.requestId;
const request = doc.data();
const msg = {
to: 'fuh#gmx.net',
from: 'hello#angularfirebase.com',
templateId: 'd-',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: request.name,
lastname: request.lastname,
email: request.email,
package: request.package,
date: request.date,
text: request.text
// and other custom properties here
}
};
return sgMail.send(msg)
})
.then(() => console.log('email sent!') )
.catch(err => console.log(err) )
});
Edit:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
exports.request = functions.firestore
.document('requests/{requestId}')
.onCreate((snap, context) => {
const db = admin.firestore();
return db.collection('requests').doc(requestId)
.get()
.then(doc => {
const requestId = snap.id;
const request = doc.data();
const msg = {
to: 'fuhr#gmx.net',
from: 'hello#angularfirebase.com',
templateId: 'd-3cd6b40ad6674f33702107d2',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: request.name,
lastname: request.lastname,
email: request.email,
package: request.package,
date: request.date,
text: request.text
// and other custom properties here
}
};
return sgMail.send(msg)
})
.then(() => console.log('email sent!') )
.catch(err => console.log(err) )
});
The .onCreate() method doesn't return event, it returns the snapshot of the object and from it you get the id of the new object.
So in your case, it has to be:
exports.firestoreEmail = functions.firestore.document('requests/{requestId}')
.onCreate((snap, context) => {
const requestId = snap.id; // get the id
const db = admin.firestore();
return db.collection('requests').doc(requestId)
.get()
.then(doc => {
const request = doc.data();
const msg = {
to: 'fuhr#gmx.net',
from: 'hello#angularfirebase.com',
templateId: 'd-3cd6b40ad6674f33702107d2',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: request.name,
lastname: request.lastname,
email: request.email,
package: request.package,
date: request.date,
text: request.text
// and other custom properties here
}
};
return sgMail.send(msg)
})
.then(() => console.log('email sent!') )
.catch(err => console.log(err) )
});

Categories

Resources