I'm trying to create a new document under the collection "accounts" (this is working), and then add the ID of the newly created account to an array of accounts the current user has access to (array is called accountAccess). Here's the function:
async function addAccount(accountName, id) {
await db.collection('accounts').add({
name: accountName,
owner: id
}).then(async (account) => {
const accountIdToAdd = db.FieldValue.arrayUnion(account.id);
await db.collection('users').doc(userId).update({
accountAccess: accountIdToAdd,
});
});
}
But I receive the error `[Unhandled promise rejection: TypeError: undefined is not an object (evaluating '_firebase.db.FieldValue.arrayUnion')]
Any ideas what I'm doing wrong in the then block?
Related
I am using Firestore realtime listeners in my Vue 3 / TypeScript app.
I created a composable that returns a ref to a user doc.
export const getUserListener = (userId: string) => {
const user = ref<User>();
const userDocRef = doc(db, 'users', userId);
const unsubscribe = onSnapshot(
userDocRef,
(snapshot) =>
(user.value = {
...snapshot.data(),
id: snapshot.id,
})
);
onUnmounted(unsubscribe);
return user;
};
It works great when populating the front end with user doc values.
But now I want to use those values in another composable, like so:
export const doSomethingWithUserListener = () => {
const user = useGetUserListener('user-id-abc-123');
if (user.value) {
console.log('User found');
} else {
console.log('User not found');
}
}
And this always returns User not found.
I already know why this is happening...
When getUserListener() first runs the user ref is undefined. So the ref returns as undefined immediately.
The onSnapshot part is asynchronous, so it executes some milliseconds later, and only then populates the ref with user data.
This is not an issue when populating fields on the front end, because users do not perceive the delay. But it causes an issue when using the listener in other composables, because the ref value is always undefined.
Since I known why this is happening I'm not looking for an explanation, but rather help with how to overcome this problem.
Summary: I want to use the listener ref value in other composables. But I can't because it always returns undefined.
Any ideas?
I can't figure out the issue why my tokens list remains empty!
I have a collection of users with a field of role to each document. I am iterating over those documents, to check which document field role is equal to shipper in order to retrieve token. So that I could sendMulticast notifications to each shipper device.
My order snapshot also contains the shipper tokens as well I am iterating over users collections to grab token for pushing into the list but both of them aren't pushing data into list.
Here's my cloud function code:
exports.notifyShipper= functions.firestore.document('/orders/{documentId}')
.onCreate(async (snap,context) => {
functions.logger.log('Sending Notifications to the shippers');
var shipperTokens=[];
await admin.firestore().collection("users").get().then(snapshot => {
snapshot.forEach(doc => {
if(doc.role =='shipper'){
shipperTokens.push(doc.token);
functions.logger.log(doc.token);
}
});
});
Array.prototype.push.apply(shipperTokens, snap.shipperTokens);
functions.logger.log(snap.shipperTokens.toString());
// const tokensOfShippers = [
// 'd7M5eixYQYCmXgDx1rOmKc:APA91bHUELrCtEIHHcoQnQ9p5SqqpeZbKpUe60OyGPlZ4GZ9cm5wosfTZFoHV7NcDm2-5Sowpck2YCIkpzZ7ku3u6LSk9tODHA54OrawxNvOQqlK-x7W6VE4wsAS5KCtK4JPtg2nRfCg',
// 'cBPUl8FzTJaQx8Mg_CkPbv:APA91bG0bZu7lD8EAxExTqcnJfZjpl71YdtfjdugwREGo-sO8URbjJILRc_DHY61wKJlnYGWCjbKdieKpvPJd5rtaPYa6rBHo1JQqiToVGs4WGd-2D3Y-bMVIzzU8TIzR772oTAUiyR7',
// 'cuJAUqqCRKq77UsbBS6kZD:APA91bEDWjSR4z9yUSfFg-FpOPMA3VXsyjJ613Etv5MhpV31nsSP5hhgumWmsoLKGh1_TbP3yd9KDU7vO4DYqW5mJpSjPKzllBuw7I-lEAY6UYasJKQXv9DZt82agPyr7oac-JBC_XMr',
// 'erYeroRkRM-s9y70l8cFqd:APA91bGzT_nT-kWUBXTBX-aDtN8Ew5ypDpbDFWcQw6O5zo4jFQFnMy2_E4Lt5GYVJXcaudK8u7LhkoQadjlfmxtG9jhIhCCch2o850UJcCqrCiNz7muBwKcp0E-y3MrHz9iAAJZ_4uJ3'
// ];
await admin.messaging().sendMulticast({
tokens : shipperTokens,
notification: {
title: "Hi TELLOO Shipper",
body: "We recieved an order, please have a look on the dashboard!",
imageUrl: "https://en.pimg.jp/040/171/400/1/40171400.jpg",
}
});
});
This is the complete error that I'm facing in logs:
Error: tokens must be a non-empty array
at FirebaseMessagingError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:44:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:90:28)
at new FirebaseMessagingError (/workspace/node_modules/firebase-admin/lib/utils/error.js:279:16)
at Messaging.sendMulticast (/workspace/node_modules/firebase-admin/lib/messaging/messaging.js:301:19)
at /workspace/index.js:391:53
at processTicksAndRejections (internal/process/task_queues.js:95:5)
If you look at the Firebase documentation on reading multiple documents, you'll see that it uses the data() function on each DocumentSnapshot to get at the fields of that document.
So your doc.role and doc.token should instead of doc.data().role and doc.data().token.
I know that there are similar posts about this, but I tried all of them and non worked.
I wrote this function for getting the user's name from the UID. I have a collection called users, with each user's uid as the name of the document, and a value called displayName in it.
exports.getDisplayName = functions.https.onCall((data, context) => {
if(context.auth){
const uid = data.uid
return admin.database().ref(`/users/${uid}`).get().then(doc=>{return{name: doc.data().displayName}})
}else{
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
})
When it deploy it everything works fine but when I call it I get these 2 errors and I can't figure out how to solve it.
Failed to load resource: the server responded with a status of 500 ()
Unhandled Promise Rejection: Error: INTERNAL
I have another function that works fine:
exports.getUidFromEmail = functions.https.onCall((data, context) => {
if(context.auth){
const email = data.email
return admin.auth().getUserByEmail(email).then((userRecord)=>{ return {user: userRecord.uid}})
}else{
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
})
edit: the error it says is this:
Unhandled error FirebaseError: Can't determine Firebase Database URL.
at FirebaseDatabaseError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:44:28)
at new FirebaseDatabaseError (/workspace/node_modules/firebase-admin/lib/utils/error.js:205:23)
at DatabaseService.ensureUrl (/workspace/node_modules/firebase-admin/lib/database/database-internal.js:97:15)
at DatabaseService.getDatabase (/workspace/node_modules/firebase-admin/lib/database/database-internal.js:65:26)
at FirebaseApp.database (/workspace/node_modules/firebase-admin/lib/firebase-app.js:228:24)
at FirebaseNamespace.fn (/workspace/node_modules/firebase-admin/lib/firebase-namespace.js:191:45)
at /workspace/index.js:17:22
at func (/workspace/node_modules/firebase-functions/lib/providers/https.js:273:32)
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
errorInfo: {
code: 'database/invalid-argument',
message: "Can't determine Firebase Database URL."
}
}
Solved my issue. It was because I was using Cloud Firestore instead of Realtime database, because a lot of the examples and docs are using it, I thought it just had a different syntax 🤦🏻
here's the fixed code:
exports.getDisplayName = functions.https.onCall((data, context) => {
if(context.auth){
const uid = data.uid
return admin.firestore().collection("users").doc(uid).get().then(doc=>{
console.log(doc)
return{name: doc.data().displayName}
})
}else{
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
})
it's admin.firestore() not admin.database()
I have created a firebase function that listen on onCreate event, however the DocumentSnapshot.data() is returning empty.
The function code is:
exports.createClientAccount = functions.firestore
.document('/userProfile/{userId}/clientList/{clientId}')
.onCreate(async (snap, context) => {
console.log('****snap.data(): ', snap.data()); //Showing Empty from the console.
return admin
.auth()
.createUser({
uid: context.params.clientId,
email: snap.data().email,
password: '123456789',
displayName: snap.data().fullName,
})
.then(userRecord => {
return admin
.database()
.ref(`/userProfile/${userRecord.uid}`)
.set({
fullName: userRecord.displayName, //ERROR here: Reference.set failed: First argument contains undefined
email: userRecord.email,
coachId: context.params.userId,
admin: false,
startingWeight: snap.data().startingWeight,
});
})
.catch(error => {
console.error('****Error creating new user',error);
});
});
The document IS created on the firebase database under
/userProfile/{userId}/clientList/{clientId}
clientId document created on the database
As per the documentation, onCreate listens when a new document is created and returns the snapshot of the data created through the DocumentSnapshot interface. However, I have checked from the console that snap.data() is empty. I don't understand why it is empty if the document is created successfully on the database.
image showing error returned by the functions when creating the userProfile
From the function code, return admin.auth.createUser({}) is creating the user as anonymous because snap.data().email is undefined, but it should create a non anonymous user.
First, please try to change document('/userProfile/{userId}/clientList/{clientId}') to document('userProfile/{userId}/clientList/{clientId}').
path should not start with /.
exports.createClientAccount = functions.firestore
.document('userProfile/{userId}/clientList/{clientId}')
At the end problem was that when I created the document with add({}) I was not including the fields in the instruction. This is the function that creates the client document and now the function gets triggered correctly.
async clientCreate(
fullName: string,
email: string,
startingWeight: number
): Promise<any> {
const newClientRef = await this.firestore
.collection(`userProfile/${this.userId}/clientList/`)
.add({
fullName,
email,
startingWeight: startingWeight * 1,
});
return newClientRef.update({
id: newClientRef.id
});
}
I was calling this.firestore.collection(...).add({}) with no fields, so when it happened, the cloud function got triggered and the DocumentSnapshot.data() was empty thus returning the Reference.set error. The cloud function createClientAccount is correct. Thanks.
More explanation:
A document gets created on iOS. It contains a city and a type.
In CloudFunctions, I detect the document creation/update via the onWrite trigger.
I want to query other FireStore documents for Users meeting the City + type criteria. Then I want to send them notifications.
Here is my code:
exports.NewRequestCreated = functions.firestore.document('/Requests/{documentId}').onWrite(event => {
// Get an object with the current document value
var myRequest = event.data.data();
// Get an object with the previous document value (for update or delete)
// Note that previous will be null if no old value exists.
if (event.data.previous) {
var oldDocument = event.data.previous.data();
}
//Get list of donors that can match this request
var compatibleTypesArray = getCompatibeUsers(myRequest.Type);
var matchingUsersArray = [];
var usersRef = functions.firestore.document('/Users');//functions.firestore.document('/Users');
var matchingUsersQuery = usersRef.where('city', '==', myRequest.city).where('type', '==', myRequest.type)
var user = matchingUsersQuery.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
});
But this is failing with the following error in Functions Console:
TypeError: usersRef.where is not a function
at exports. exports.NewRequestCreated Created.functions.firestore.document.onWrite.event (/user_code/index.js:45:40)
at Object. (/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)
usersRef is a DocumentReference. As you'll see in the API docs, there's no where() method on that class.
Perhaps you meant to instead get a CollectionReference object using collection(), which you can then query for documents using where() method.
functions.firestore.collection('/Users');