tokens must be a non-empty array at FirebaseMessagingError.FirebaseError - firebase

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.

Related

Unhandled Promise issue when adding id of newly created document to array

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?

Why is Firestore rejecting the operation?

I have a firestore action in a react-native project:
export const getMessages = (convoId) => {
return (dispatch) => {
firebase
.firestore()
.collection('messages')
.where('userConvos', 'array-contains', convoId)
.orderBy('time')
.onSnapshot((querySnapshot) => {
const messages = [];
querySnapshot.forEach((doc) => {
messages.push(doc.data());
});
dispatch({ type: types.LOAD_MSGS, payload: messages });
});
};
};
the two composite indices I've tried in firestore:
and my database structure is like so:
Messages (collection)
content (field)
time (field)
userConvos (field)
The problem is when I make this firestore call, I get the
Error: Firestore: Operation was rejected because the system is not in a state required for the opereation's execution
But if the error is dismissed, the data is ordered as expected. What's going on and how can this error be eliminated?
You have a missing index. To create it, do the following.
View the android device logs by typing on the terminal:
adb logcat
If you reproduce the error, then something like the following will appear in the logs:
05-16 04:23:20.887 3746 3895 I ReactNativeJS: nativeErrorMessage:
'FAILED_PRECONDITION: The query requires an index. You can create it
here:
https://console.firebase.google.com/v1/r/project/XXXXXXXXX/firestore/indexes?create_composite=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
Open a browser and paste the link.
Press the Create Index button.

How to update / push data to an empty array in Firestore - Firebase

I am working with React and Firestore. When a user is registered I save an empty array {chats : []} on their profile. When a user compose a message to a another user, during that time I want to push some data to this chats array. One thing should be kept in mind, that this specific array shouldn't get overwritten. Simply, a new value should be pushed to it.
All I can do is
db.collection('Users').doc(userid).update({
chats: ['somevalue']
})
Note : My method overwrites the data of the whole array.
The Firestore User Data Sample
db.collection('Users').doc(this.state.user.uid).set({
name: `${this.state.fname} ${this.state.lname}`,
email: this.state.email,
time: new Date().getTime(),
id: this.state.user.uid,
address: this.state.place,
username: this.state.username,
bio: this.state.bio,
img: this.state.imgURL,
chats: [] //To add the chats list to the user profile
}).then(() => {
console.log("User Info Added...");
}).catch(error => {
console.log(error);
})
I have figured it out, and I think its the easiest way to achieve it. You can find out how it works through the comments.
All I did is :
Get the chats[] arrays from the Users object
Push New Values to the retrieved arrays
Update the chats[] array with new updated values pushed to it.
//Get the Following Users through the specific document ID
Promise.all([
db.collection('Users').doc(new Cookies().getCookie('uid')).get(),
db.collection('Users').doc(this.props.data['0'].userId).get()
]).then(e => {
//Get the Chats array from the object
let c = e.map(lists => lists.data().chats)
//push new values which is 'loc' in my case to the Chats arrays
c.forEach(e => {
e.push(loc)
})
//Make two Other promises to update the chat arrays with the upated values.
Promise.all([
db.collection('Users').doc(new Cookies().getCookie('uid')).update({
chats: c[0]
}),
db.collection('Users').doc(this.props.data['0'].userId).update({
chats: c[1]
}),
]).then(e => {
console.log("Docments Updated")
})
})

Reference.set failed: First argument contains undefined

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.

Upon document creation in Firestore cloud functions, I want to make a query to get compatible documents, then send them notifications

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');

Resources