I have this scheduled function that :
read 1 document from collection
data: [ { 'field1' : 123, 'field2' : 456 }, { 'field1' : 123, 'field2' : 456 } ... ]loop on all data array
read new value from rest api call
update the value of data array in firestore
NOTE from firebase function console log I have the log 'field updated ...' after the log 'firebase document updated ...', I think because the request is not sync
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const db = admin.firestore();
const fetch = require('node-fetch');
var request = require("request");
const { Headers } = fetch;
exports.onStatisticUpdated = functions.region('europe-west1')
.runWith({
timeoutSeconds: 300,
})
.pubsub.schedule('every 60 minutes')
.onRun(async context => {
const doc = await admin
.firestore()
.collection('collection')
.doc('1234')
.get();
if (doc.exists) {
for (let i = 0; i < doc.data().data.length; i ) {
let myFirebaseData = doc.data().data[i];
var options = {
method: 'GET',
url: 'https://xyz....',
qs: { abcd: 'value' },
headers: {
'x-xxxx-host': 'v1....',
'x-xxxx-key': 'xxxxxxxxxxxx'
}
}
request(options, function (error, response, body) {
if (error) throw new Error(error);
var json = JSON.parse(body);
if (json['response'].length > 0) {
console.log('field updated ...');
myFirebaseData.field1 = json['response'][0].value1
myFirebaseData.field2 = json['response'][0].value2
};
});
}
// myFirebaseData is not updated at this time with new value filled by rest api !!!
console.log(' firebase document updated ...');
await admin
.firestore()
.collection('collection')
.doc('1234')
.update({
data: doc.data(),
});
}
});
question : how i can store the final document with new values filled by the rest api ?
CodePudding user response:
In Cloud Functions you need to manage asynchronous method calls via Promises (more details here in the doc). request supports callback interfaces natively but does not return a Promise.
You should use another library, like axios. In addition, since you want to execute a variable number of asynchronous Rest API calls in parallel, you need to use Promise.all().
Now, what is not clear to me in your code is how do you build the object used to update the 1234 document. I your current code, in the for (let i = 0; i < doc.data().data.length; i ) {}) loop you are actually overwriting the field1 and field2 properties of the myFirebaseData again and again...
Therefore you will find below the code structure/pattern that I think is correct and if it is not the case, just add a comment to this answer and we can fine tune the answer according to the extra details you will share.
exports.onStatisticUpdated = functions.region('europe-west1')
.runWith({
timeoutSeconds: 300,
})
.pubsub.schedule('every 60 minutes')
.onRun(async context => {
const doc = await admin
.firestore()
.collection('collection')
.doc('1234')
.get();
if (doc.exists) {
const promises = [];
for (let i = 0; i < doc.data().data.length; i ) {
let myFirebaseData = doc.data().data[i];
var options = {
method: 'get',
url: 'https://xyz....',
params: { abcd: 'value' },
headers: {
'x-xxxx-host': 'v1....',
'x-xxxx-key': 'xxxxxxxxxxxx'
}
}
promises.push(axios(options))
}
apiResponsesArray = await Promise.all(promises);
const updateObject = {};
apiResponsesArray.forEach((resp, index) => {
// THIS ENTIRE BLOCK NEEDS TO BE ADAPTED!!
// I'M JUST MAKING ASSUMPTIONS...
const responseData = resp.data;
updateObject["field1" index] = responseData.value1;
updateObject["field2" index] = responseData.value2;
})
console.log(updateObject)
console.log(' firebase document updated ...');
await admin
.firestore()
.collection('collection')
.doc('1234')
.update({
data: updateObject
});
return null; // IMPORTANT!!! see the link to the doc above
}
});
