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