How to read/update object instances converted into JSON from Firebase - javascript

I am currently stuck at the process of retrieving classes stored as JSON in firebase (I converted each instance of the object into JSON & stored it). My primary objective is to read a piece of data(at meeting which is also a JSON object) within a JSON object (meeting) stored as value in Firestore, using nodejs in Cloud Functions, as I am trying to create a push notification with FCM.
The code I have:
exports.meetingsUpdate = functions.firestore.document('/meetings/{meetingId}')
.onWrite(async (snap,context) => {
var data = snap.after.data()
data = await data.meeting;
const obj = await data.meeting;
const uids = await obj.groupUID;
const requestor = await obj.requesterName;
const payload = {
notification: {
title: 'Meeting request',
body: 'You have a meeting request from ' + requestor
}
}
for (uid of uids) {
// eslint-disable-next-line no-await-in-loop
const doc = await admin.firestore()
.collection('userNotificationTokens')
.doc(uid)
.get()
// eslint-disable-next-line no-await-in-loop
const token = await doc.data().token
// eslint-disable-next-line no-await-in-loop
const response = await admin.messaging().sendToDevice(token, payload)
.then((res) => {
console.log("Message sent successfully!", res.body)
});
}
return null;
})
Here is how my database mapping looks like
I'm relatively new to Firebase & its functionality and have been stuck with errors like
SyntaxError: Unexpected token u in JSON at position 0
TypeError: Cannot read property 'groupUID' of undefined
TypeError: Cannot read property 'token' of undefined
for quite sometime despite trying various approaches & tweaks.
The first meeting value is a JSON object, and the second meeting attribute is also a JSON object. Apologies for the somewhat convoluted storage of data.

Related

Firebase saying arrayUnion was called with invalid data

Im getting an error with firebase because im trying to update two values when I press handleSelect. Only issue is that the first updateDoc works fine as I'm trying to add an array into the "user" specific userChats database, but when I try to do the opposite and add the user array to the "chat" database, it fails.
const handleSelect = async(chat) =>{
const docRef = doc(db, 'users', user?.uid)
const docSnap = await getDoc(docRef)
const addRef = doc(db, 'userChats', user?.uid)
await updateDoc(addRef, {
userChats: arrayUnion(chat)
})
const addRecieverRef = doc(db, 'userChats', chat?.uid)
await updateDoc(addRecieverRef, {
userChats: arrayUnion(user)
})
console.log(chat.uid)
const concatUID = user.uid > chat.uid ? user.uid + chat.uid : chat.uid + user.uid;
if(!docSnap.exists() && user.uid!=chat.uid){
await setDoc(doc(db, 'messages', concatUID), {
messages: [],
})
}
else{
dispatch({type: 'CHANGE_USER', payload: chat})
console.log(chat)
}
}
Error
Chats.js:53 Uncaught (in promise) FirebaseError:
Function arrayUnion() called with invalid data.
Unsupported field value: a custom UserImpl object (found in document userChats/lJ4u4PqWynXAPthz3FVgYaQQ0Do1)
I already checked and all the reference values are correct, and both "user" and "chat" are plain objects
Firestore can only store values of the types indicated in its documentation on data types. The UserImpl object that you are trying to store is not of a supported type, which is what the error message indicates.
If the user object comes from Firebase Authentication, you can call toJSON() on it to get a JSON serialization of its data.

I'm having an error: Uncaught (in promise) TypeError: source is not async iterable

I'm using nft.storage for storing my data on ipfs using storeBlob as I want to store only data.
index.js:13
Uncaught (in promise) TypeError: source is not async iterable
at last (index.js:13:1)
at pack (index.js:14:1)
at packCar (lib.js:757:1)
at NFTStorage.encodeBlob (lib.js:472:1)
at NFTStorage.storeBlob (lib.js:151:1)
at NFTStorage.storeBlob (lib.js:542:1)
at storeAsset (Results.jsx:36:1)
at encryptingData (Results.jsx:63:1)
I used this function to get cid.
Here my metadata is encrypted string
const client = new NFTStorage({ token: NFT_STORAGE_KEY })
async function storeAsset(metadata) {
const cid = await client.storeBlob(metadata);
console.log("Metadata stored on Filecoin and IPFS with cid:", cid)
}
Can you try changing the below line?
Before:
const cid = await client.storeBlob(metadata);
After:
const cid = await client.storeBlob(new Blob([metadata]));
blob Reference
client.storeBlob is probably expecting a stream. But, if all you really need is an async iterable you may be able to get away with this:
async function* makeAsyncIterable(metadata) {
yield metadata;
}
const metadata = "???";
const metadataAsyncIterable = makeAsyncIterable(metadata);
console.log(Symbol.asyncIterator in metadataAsyncIterable); // Output: true
It is not clear what metadata variable is. this is how you should implement client.storeBlob
await client.storeBlob(
new Blob([
{
chain:"goerli",
contract_address: "0x....",
transaction_hash: "0x....",
description:"description",
address:"0x.....",
},
])
);

400 error on graphql mutation from gatsby to node backend

So the mutation works in graphiql so the issue is frontend, basically I have a function tied to an onClick that captures some input value and that value is sent to the backend server to update the database. Here is the request.
const updateTitle = async () => {
//api data
const data = await fetch(`http://localhost:8081/graphql`, {
method: 'POST',
body: JSON.stringify({
query: `
mutation {
updateMenu(menuInput: {_id: ${elementId},title: ${inputValue}}){
title
}
}
`
}),
headers: {
'Content-Type': 'application/json'
}
})
//convert api data to json
const json = await data.json();
console.log(json)
}
here is what is console logged: (Array is just where the error was)
{message: 'Syntax Error: Invalid number, expected digit but got: "c".', locations: Array(1)}
Because I am mapping an array of objects from a mongo database Im able to use the assigned Id for the mutation, console logged that and its fine, a previous button sets that Id into a state used in the query, and the input data is also set into a state which is used used in the mutation. The mutation takes place after setting these states so i cant see it being an async issue.
Here are the functions that set the input value and call the function with the fetch request. (the Id is set inside the array map function)
const [inputValue, setInputValue] = useState(" ");
const handleInput = event => {
setInputValue(event.target.value);
};
const logInputValue = () => {
console.log(inputValue);
updateTitle()
// window.location.reload()
};
Please let me know if any more information is needed to answer! I wont initially include the whole map function because its a working mess. But here is the code for the button inside the map function that starts the whole process.
<button onClick={() => { logInputValue(); setElementId(item._id); }}>save</button> </div>}
Thank you!!

Can anyone tell me why it is showing array is empty?

I want to send notification to multiple devices and for that am getting the token via querying the docs and saving the token to array but it shows that array is empty. Most probably error is because am not able to add elements in the array.
My code is:-
var registrationTokens=[];
const indexOfSender=usersList.indexOf(chatItem.senderUsername);
let removedUsername=usersList.splice(indexOfSender,1); //to remove the senders name from list
usersList.forEach(async(element)=>{
const query = admin.firestore().collection('users').where("username","==",element);
const querySnapshot = await query.get();
if (querySnapshot.docs.length > 0) {
const doc = querySnapshot.docs[0];
const data = doc.data();
registrationTokens.push(data.androidNotificationToken); //adding token over here
}
else {
console.log("Unable to get token for the username ", element);
}
});
const message =
{
notification: {
title:'Message',
body: body,
imageUrl: url,
},
tokens: registrationTokens,
data: { recipient: senderUserId },
};
admin.messaging().sendMulticast(message)
.then(response =>
{
if (response.failureCount > 0) {
const failedTokens = [];
response.responses.forEach((resp, idx) => {
if (!resp.success) {
failedTokens.push(registrationTokens[idx]);
}
});
console.log('List of tokens that caused failures: ' + failedTokens);
}
else
{
console.log('Successfully sent messages ', response);
}
});
Error
Error: tokens must be a non-empty array
at FirebaseMessagingError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseMessagingError (/workspace/node_modules/firebase-admin/lib/utils/error.js:254:16)
at Messaging.sendMulticast (/workspace/node_modules/firebase-admin/lib/messaging/messaging.js:294:19)
at sendNotificationForGroupChat (/workspace/index.js:238:35)
at exports.onCreateMessage.functions.region.firestore.document.onCreate (/workspace/index.js:116:9)
at process._tickCallback (internal/process/next_tick.js:68:7)
async inside forEach does not work the way you expect. If you add some logging, you will see that the loop ends before any of its async work is complete, leaving your tokens array empty before you pass it to FCM. Each iteration through the loop simply generates a promise that is not resolved. You will need to rewrite the code to actually wait for all those promises before calling FCM.
Read more about that:
Using async/await with a forEach loop
for-of loop will work just fine with asynchronous calls :)
Cheers

How to fix Cloud Function error admin.database.ref is not a function at exports

I'm currently trying to modify my Cloud Functions and move in under https.onRequest so that i can call use it to schedule a cron job. How it i'm getting the following error in the logs.
TypeError: admin.database.ref is not a function
at exports.scheduleSendNotificationMessageJob.functions.https.onRequest (/user_code/index.js:30:20)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:57:9)
exports.scheduleSendNotificationMessageJob = functions.https.onRequest((req, res) => {
admin.database.ref('/notifications/{studentId}/notifications/{notificationCode}')
.onCreate((dataSnapshot, context) => {
const dbPath = '/notifications/' + context.params.pHumanId + '/fcmCode';
const promise = admin.database().ref(dbPath).once('value').then(function(tokenSnapshot) {
const theToken = tokenSnapshot.val();
res.status(200).send(theToken);
const notificationCode = context.params.pNotificationCode;
const messageData = {notificationCode: notificationCode};
const theMessage = { data: messageData,
notification: { title: 'You have a new job reminder' }
};
const options = { contentAvailable: true,
collapseKey: notificationCode };
const notificationPath = '/notifications/' + context.params.pHumanId + '/notifications/' + notificationCode;
admin.database().ref(notificationPath).remove();
return admin.messaging().sendToDevice(theToken, theMessage, options);
});
return null;
});
});
You cannot use the definition of an onCreate() Realtime Database trigger within the definition of an HTTP Cloud Function.
If you switch to an HTTP Cloud Function "so that (you) can call use it to schedule a cron job" it means the trigger will be the call to the HTTP Cloud Function. In other words you will not be anymore able to trigger an action (or the Cloud Function) when new data is created in the Realtime Database.
What you can very well do is to read the data of the Realtime Database, as follows, for example (simplified scenario of sending a notification):
exports.scheduleSendNotificationMessageJob = functions.https.onRequest((req, res) => {
//get the desired values from the request
const studentId = req.body.studentId;
const notificationCode = req.body.notificationCode;
//Read data with the once() method
admin.database.ref('/notifications/' + studentId + '/notifications/' + notificationCode)
.once('value')
.then(snapshot => {
//Here just an example on how you would get the desired values
//for your notification
const theToken = snapshot.val();
const theMessage = ....
//......
// return the promise returned by the sendToDevice() asynchronous task
return admin.messaging().sendToDevice(theToken, theMessage, options)
})
.then(() => {
//And then send back the result (see video referred to below)
res.send("{ result : 'message sent'}") ;
})
.catch(err => {
//........
});
});
You may watch the following official Firebase video about HTTP Cloud Functions: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3. It shows how to read data from Firestore but the concept of reading and sending back the response (or an error) is the same for the Realtime Database. Together with the 2 other videos of the series (https://firebase.google.com/docs/functions/video-series/?authuser=0), it also explains how it is important to correctly chain promises and to indicate to the platform that the work of the Cloud Function is finished.
For me, this error happened when writing admin.database instead of admin.database().

Categories

Resources