This is a followup to my question https://stackoverflow.com/questions/54100270/google-translate-api-and-firebase-firestore-are-killing-each-other
. Doug Stevenson said that I needed to return my functions. We watched his video Learn JavaScript Promises (Pt. 3) for sequential and parallel work in Cloud Functions
a few hundred times...
I'm working with two functions. The first, a call to Google Translate, now returns a translation. The second function is a Firestore set()
call, to write the translation to the database. The set()
works if I don't do the Google Translate call, but together they crash. Specifically, if I call Firebase set()
than the Google Translate function executes, we see Result1
, and nothing further. In other words, calling the database stops to code from pushing a translation into the translationArray
.
Here's my code now:
exports.Google_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request_NOT').onUpdate((change, context) => {
if (change.after.data().word != undefined) {
const { Translate } = require('@google-cloud/translate');
// Your Google Cloud Platform project ID
const projectId = 'myProject-cd99d';
// Instantiates a client
const translate = new Translate({
projectId: projectId,
});
// The text to translate
const text = change.after.data().word;
console.log(text);
// The target language
const target = 'en';
let translationArray = []; // clear translation array
const finalPromise = translate.translate(text, target)
.then(function (results) {
console.log("Result1: " + results);
console.log(Array.isArray(results));
console.log(results[0]);
let translation = results[0];
console.log(translation);
return translation
})
.then(function (results) {
console.log("Translation: " + results);
translationArray.push(results);
return translationArray
})
.then(function (results) {
console.log("TranslationArray: " + translationArray);
console.log("Result2: " + results);
console.log("Text: " + text)
return admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
translationArray: results,
language: 'en',
longLanguage: 'English'
});
})
.then(function (results) {
console.log("Result3: " + results)
console.log("Write succeeded!");
})
.catch(function (error) {
console.error(error);
});
} // close if
return 0;
}); // close oxfordPronunciationUS
The Firestore set()
call returns nothing, and it kills the Google Translation call. Specifically, without the Firestore call the code executes all the way through, logging everything. With the Firestore call, neither function executes (Google Translate is never called) and nothing logs after "text."
I don't understand what const finalPromise
does. It looks like an unused constant.
We read Why are the Firebase API asynchronous? and tried this code:
var promise = admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
translationArray: translationArray,
language: 'en',
longLanguage: 'English'
});
promise.then(...)
That didn't help.
I tried switching to IBM Watson Translate, and the same thing is happening. Without the database call the translation function works perfectly. With the Firestore call it gets the translation, runs the forEach
, and then stops when I try to push the word into the array. translationsArray
doesn't log and nothing writes to the database.
exports.IBM_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request_IBM').onUpdate((change, context) => {
if (change.after.data().word != undefined) {
let word = change.after.data().word;
let wordArray = [];
wordArray.push(word);
var LanguageTranslatorV3 = require('watson-developer-cloud/language-translator/v3');
var languageTranslator = new LanguageTranslatorV3({
version: '2018-05-01',
iam_apikey: 'swordfish',
url: 'https://gateway.watsonplatform.net/language-translator/api',
headers: {
'Content-Type': 'application/json'
}
});
var parameters = {
"text": wordArray,
"model_id": "es-en",
"source": "es",
"target": "en"
};
let translationsArray = [];
languageTranslator.translate(
parameters,
function (err, response) {
if (err) {
console.log('error:', err);
} else {
response.translations.forEach(function (translation) {
console.log(translation.translation);
translationsArray.push(translation.translation);
});
console.log(translationsArray);
admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(word).collection('Translations').doc('English').set({
translationsArray: translationsArray,
language: 'en',
longLanguage: 'English'
})
.then(function () {
console.log("Translations written to database.");
})
.catch(function (error) {
console.error(error);
});
}
}
);
}
return 0;
});
I also wrote the same cloud function calling the Oxford English Dictionary for the translation. This works perfectly, writing the translation to the database:
exports.Oxford_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request').onUpdate((change, context) => {
if (change.after.data().word != undefined) {
let word = change.after.data().word;
let options = {
uri: 'https://od-api.oxforddictionaries.com/api/v1/entries/es/' + change.after.data().word + '/translations%3Den', // translations=es
headers: {
"Accept": "application/json",
'app_id': 'groucho',
'app_key': 'swordfish'
},
json: true
};
let translationsArray = [];
return rp(options)
.then(function (wordObject) {
wordObject.results.forEach(function (result) {
result.lexicalEntries.forEach(function (lexicalEntry) {
lexicalEntry.entries.forEach(function (entry) {
entry.senses.forEach(function (sense) {
if (sense.translations) {
sense.translations.forEach(function (translation) {
translationsArray.push(translation.text);
});
} // close if
else {
if (sense.subsenses) {
sense.subsenses.forEach(function (subsense) {
if (subsense.translations) {
subsense.translations.forEach(function (translation) {
translationsArray.push(translation.text);
}); // close forEach
} // close if
else {
// console.log("No Translation");
} // close else
}); // close forEach
} // close if
} // close else
}); // close forEach
}); // close forEach
}); // close forEach
}); // close forEach
translationsArray = [...new Set(translationsArray)]; // removes duplicates
return admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(word).collection('Translations').doc('English').set({
translationsArray: translationsArray,
source: 'OED',
dateAdded: Date.now(), // timestamp
longLanguage: 'English',
shortLanguage: 'en',
word: word
})
.then(function () {
// console.log("Document written.");
})
.catch(function (error) {
console.log("Error writing document: ", error);
})
})
.then(function () {
// console.log("Document written for Oxford_EStranslateEN.");
})
.catch(function (error) {
console.log("error: " + error);
});
} // close if
// return 0;
});
One difference is that I call the OED via an HTTP request, using rp
(request-promise). I call return rp(options)
. This clearly returns a promise, and the promise is explicitly returned. The problem seems to be that in the Google version I'm not returning the promise when I call Google Translate, and IBM Watson returns a callback, not a promise, and I don't return that.