I have a firestore collection containing post documents, each document contains a reference to an author (user) and a case document.
How do I get the user and the case in the same onSnapshot?
Here's what I'd like to do with await, but that doesn't seem to be an option with react-native-firebase.
export const firebasePostLooper = (snapshot) => {
let data = [];
snapshot.forEach(async (doc) => {
let newItem = {id: doc.id, ...doc.data()};
if (newItem.author) {
let authorData = await getDoc(newItem.author); // doesn't work with rnfirebase
if (authorData.exists()) {
newItem.userData = {userID: authorData.id, ...authorData.data()};
}
}
if (newItem.case) {
let caseData = await getDoc(newItem.case);
if (caseData.exists()) {
newItem.userData = {userID: caseData.id, ...caseData.data()};
}
}
data.push(newItem);
});
return data;
};
This doesn't work because getDoc() doesn't exist.
So I'm left with using .then()
export const firebasePostLooper = (snapshot) => {
let data = [];
snapshot.forEach((doc) => {
let newItem = {id: doc.id, ...doc.data()};
if (newItem.author) {
newItem.author
.get()
.then((res) => {
newItem.authorData = res.data();
if (newItem.case) {
newItem.case
.get()
.then((caseRes) => {
newItem.caseData = caseRes.data();
data.push(newItem);
})
.catch((err) => console.error(err));
}
})
.catch((err) => console.error(err));
} else {
data.push(newItem);
}
});
return data;
};
This second method doesn't seem to be working, data is empty at the return statement but data.push(newItem) contains the correct document with the 2 referenced documents.
You're returning data before it gets filled inside the promise. You should handle the returning of the data inside a .then() in order to return it after the promise has resolved and not before.
Take a look at this example where if we handle the emptyData object outside the promise chain, we just return the initial value before it has been filled.
let promise = new Promise((resolve, reject)=>{
setTimeout(resolve, 1000, 'foo');
})
let emptyData= [];
let notEmptyData = [];
promise
.then(res=>{
emptyData.push(res);
notEmptyData.push(res);
console.log("Full data: " + notEmptyData) // "Full data: foo"
});
console.log("Empty data: " + emptyData); // "Empty data: "
I am trying to implement verification using Cloud Function and Twilio. Twilio works correctly and SMS with the code is sent, but in the Flutter application I get the error [firebase_functions / deadline-exceeded] DEADLINE_EXCEEDE. What am I doing wrong?
Cloud Function:
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);
exports.sendSms = (req, res) => {
console.log('start sendSms');
var func = client.verify.services('xxxxx')
.verifications
.create({to: req.body.data.phone, channel: 'sms'})
.then(verification => {return verification})
.then(result => {
if (result.status === 'pending')
return {status: 'success', result: "pending"};
else
return {status: 'error', error_code: -1, error_msg: result.status};
})
.catch(e => {
return {status: 'error', error_code: e.code, error_msg: e.message};
});
return Promise.all([func]).then(result => {
console.log(result); // console output [ { status: 'success', result: 'pending' } ]
return result[0];
});
};
Flutter:
HttpsCallable callable = FirebaseFunctions.instanceFor(region: 'europe-west3').httpsCallable(
'send-sms',
options: HttpsCallableOptions(timeout: const Duration(seconds: 10))
);
dynamic results = await callable.call(<String, dynamic>{'phone': phoneNumber});
The create method returns promise too so you must add the return keyword there as well.
Instead of:
var func = client.verify.services('xxxxx')
Try this:
exports.sendSms = functions.https.onCall((data, context) => {
//^^^^^^^^^^^^^^^^^^^^^^
console.log('start sendSms');
return client.verify.services('xxxxx')
.verifications
.create({to: data.phone, channel: 'sms'})
.then(verification => {return verification})
.then(result => {
if (result.status === 'pending')
return {status: 'success', result: "pending"};
else
return {status: 'error', error_code: -1, error_msg: result.status};
})
.catch(e => {
return {status: 'error', error_code: e.code, error_msg: e.message};
});
});
That being said you don't need this part:
return Promise.all([func]).then(result => {
console.log(result); // console output [ { status: 'success', result: 'pending' } ]
return result[0];
});
Why did I add Promise to the function it still gives an error ??
I cannot call this function again with the parameter I give ??
Get User by Uid in Fire.js file( I custom this file )
getUserbyUid = async ({ Uid }) => {
return new Promise((res, rej) => {
firebase.firestore()
.collection('user')
.doc(Uid)
.get()
.then(documentSnapshot => {
res(documentSnapshot.data().name);
})
.catch(error => { rej(error) });
})
}
in Feed.js i call it
async componentDidMount() {
await firebase.firestore().collection("Post").get()
.then(querySnapshot => {
querySnapshot.forEach(documentSnapshot => {
this.setState({ dataPost: this.state.dataPost.concat({ ...documentSnapshot.data(),
key: documentSnapshot.id, }) })
console.log(Fire.shared.getUserbyUid(documentSnapshot.data().uid)); <= error this if i console.log(documentSnapshot.data().uid)) it will return a Uid
but If i call in above it will return Promise { "_U": 0,"_V": 0, "_W": null,"_X": null,}
});
});
}
Below this code downloadcontroller.js
// Retrieve all downloads from the database.
exports.findAll = async (req, res) => {
downloadObj
.findAll(
{
include: [
{
model: composerObj,
required: false,
},
{
model: raagaObj,
required: false,
},
{
model: taalaObj,
required: false,
},
],
where: req.query,
raw:true,
nest: true,
})
.then((data) => {
data.forEach(async (element) => {
const artistsArray = [];
let whereConstraint = {};
JSON.parse(element.artists).forEach( async (ele) => {
artistsArray.push(ele.artistId);
});;
whereConstraint = {
id : {
[Op.in]: artistsArray
}
}
element.Active = "true11";
const artistData = artistService.customfindAll(whereConstraint);
element.artistData = artistData;
console.log("artistData",artistData);
});
console.log("data",data);
console.log("3");
res.status(200).send({
status:200,
message: "ok",
data:data
});
console.log("4");
})
.catch((err) => {
res.status(500).send({
status:500,
message:
err.message || "Some error occurred while retrieving Download.",
});
});
};
Below this code, artistServer.js file
exports.customfindAll = async (whereConstraint) => {
return new Promise(function(resolve, reject) {
artistObj
.findAll({
attributes:{
include: [
[fn('IF',literal('imagePath!=""'),(fn('CONCAT', artistImagePath, 'artist/', col('imagePath'))),artistImageDefaultPath), 'imageFullPath'],
],
},
where: whereConstraint,
order: [
['artistName', 'ASC'],
],
raw:true,
nest: true,
})
.then((data) => {
resolve(data);
});
});
}
Here my problem is
const artistData = artistService.customfindAll(whereConstraint);
not waiting for the data. .So that I got the result, below have mentioned. Actually the artistData attribute column, need result data.
{
"status": 200,
"message": "ok",
"data": [
{
"id": 1,
"fileType": "1",
"customFileName": "test filename",
"artists": "[{\"artistId\":1},{\"artistId\":4},{\"artistId\":2}]",
"accompanyingArtists": "[{\"instrumentId\":1,\"accompanyingArtistId\":1},{\"instrumentId\":2,\"accompanyingArtistId\":6},{\"instrumentId\":3,\"accompanyingArtistId\":4}]",
"Active": "true11",
"artistData": {}
},
{
"id": 2,
"fileType": "1",
"customFileName": "new file name",
"artists": "[{\"artistId\":1},{\"artistId\":4},{\"artistId\":2},{\"artistId\":6},{\"artistId\":3}]",
"accompanyingArtists": "[{\"instrumentId\":1,\"accompanyingArtistId\":1},{\"instrumentId\":2,\"accompanyingArtistId\":6},{\"instrumentId\":3,\"accompanyingArtistId\":4},{\"instrumentId\":3,\"accompanyingArtistId\":3},{\"instrumentId\":4,\"accompanyingArtistId\":2}]",
"Active": "true11",
"artistData": {}
}
]
}
In console page, I artistdata attribute pending...
enter image description here
The customFindAll function returns a Promise but the forEach does not wait for it. Add an await:
const artistData = await artistService.customfindAll(whereConstraint);
Also, forEach does not wait, it just fires off the async functions and move on. To wait for the forEach to finish, you need a map and a Promise.all, like this:
await Promise.all(data.map(async (element) => {
const artistsArray = [];
let whereConstraint = {};
JSON.parse(element.artists).forEach( async (ele) => {
artistsArray.push(ele.artistId);
});;
whereConstraint = {
id : {
[Op.in]: artistsArray
}
}
element.Active = "true11";
const artistData = await artistService.customfindAll(whereConstraint);
element.artistData = artistData;
console.log("artistData",artistData);
}));
The way it works this way is that data.forEach(async (e) => ... runs the async function and discards the results (the Promises). If you use a map, like data.map(async (e) => ..., you'll get an array of Promises, then use await Promise.all(...) to wait for them.
I have this action in my vuex store:
loadExercises ({commit}) {
commit('setLoading', true)
const db = firebase.firestore()
db.collection('exercises').get()
.then(querySnapshot => {
const exercises = []
querySnapshot.forEach((doc) => {
exercises.push({
title: doc.data().title,
language: doc.data().language,
translated: doc.data().translated,
lastOpen: doc.data().lastOpen,
dueDate: doc.data().dueDate,
uid: doc.data().uid,
userId: doc.data().userId,
words: [{ word: '', uid: '', translation: '' }]
})
db.collection('exercises').doc(doc.data().uid).collection('words').get()
.then(words => {
const wordsArray = []
words.forEach(word => {
wordsArray.push(word.data())
})
let exercise = this.getters.loadedExercise(doc.data().uid)
exercise.words = wordsArray
})
.catch(error => {
commit('setLoading', false)
console.log(error)
})
})
commit('setLoading', false)
commit('setLoadedExercises', exercises)
})
.catch(error => {
commit('setLoading', false)
console.log(error)
})
}
It is supposed to fetch exercises from a firebase cloudstore db. It works on some routes but not all.
When using these two getters it works:
loadedExercises (state) {
return state.loadedExercises
},
loadedExercise (state) {
return (exerciseId) => {
return state.loadedExercises.find(exercise => {
return exercise.uid === exerciseId
})
}
}
But when I use these getters:
upcomingExercises (state, getters) {
return getters.loadedExercises.filter(exercise => {
return exercise.dueDate > 0
})
},
latestExercises (state, getters) {
return getters.loadedExercises.splice(0, 5)
},
it does not work I just get "TypeError: Cannot set property 'words' of undefined". What is it that I do wrong?
It looks to me like you aren't returning the values back to the function.
Try replacing
db.collection('exercises').get()
with
return db.collection('exercises').get()
and
db.collection('exercises').doc(doc.data().uid).collection('words').get()
with
return db.collection('exercises').doc(doc.data().uid).collection('words').get()