I’m having an issue with Callable Functions
. Here’s my Firebase function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const request = require('request-promise');
admin.initializeApp(functions.config().firebase);
exports.phoneAuthRequest = functions.https.onCall((data, context) => {
// Message text passed from the client.
const text = data.text;
// Authentication / user information is automatically added to the request.
const uid = context.auth.uid;
const phone = context.auth.token.phone_number;
const url = "https://api.authy.com/protected/json/phones/verification/start?api_key=<key here>&via=sms&country_code=1&phone_number=" + phone;
const httpReq = {
uri: url,
method: 'POST',
json: true,
resolveWithFullResponse: true
};
return new Promise((resolve, reject) => {
request(httpReq).then((response) => {
// Twillio request returned with success
console.log("response: " + JSON.stringify(response));
return resolve(response);
}).catch(function(error) {
console.log("error: " + JSON.stringify(error));
return reject(error);
});
});
});
And here is my client example:
<html>
<head>
<script src="https://www.gstatic.com/firebasejs/5.4.2/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/5.4.2/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/5.4.2/firebase-database.js"></script>
<script src="https://www.gstatic.com/firebasejs/5.4.2/firebase-functions.js"></script>
<script>
var config = { <firebase configuration in here> };
firebase.initializeApp(config);
function myTest() {
var display = document.getElementById('display');
display.innerHTML = "Started";
var auth = firebase.auth();
auth.signInWithEmailAndPassword(<username>,<password>).then(function(authResult) {
display.innerHTML = "Signed in";
var phoneAuthRequest = firebase.functions().httpsCallable('phoneAuthRequest');
phoneAuthRequest({'text': 'Test'}).then(function(result) {
display.innerHTML = JSON.stringify(result);
}).catch(function(error) {
display.innerHTML = JSON.stringify(error);
});
}).catch(function(error) {
display.innerHTML = "Login failure. Your email or password could not be validated. " + JSON.stringify(error);
});
}
</script>
</head>
<body>
<input type="submit" value="Test It" data-wait="Loading..." id="loginBtn" class="login-window-login-btn w-button" onclick="myTest();">
<div id="display"></div>
</body>
</html>
A test of this code combination shows that the Firebase function ‘phoneAuthRequest’ gets called, which in turn makes the request to Twillio, and the response from Twillio is returned correctly in response, but the Console Logs show this error:
Unhandled error RangeError: Maximum call stack size exceeded
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13395:23)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13400:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:3010:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13399:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13400:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:3010:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13399:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13400:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:3010:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13399:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13400:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:3010:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13399:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
And of course, the response back at the client is:
{"code":"internal"}
As I read similar posts, the advice was to serialize the promises, which I’m not sure I totally understand how to do, or, to wrap the async call (request in this case) inside of a new promise, which is exactly what I have done here, and it still doesn’t work.
If you are going to answer this question, can you please be specific about the proposed solution by showing a code example?
Thanks in advance…
2
Answers
Ok, I resolved this issue with a ton of knocking my head against the wall. Here's the code:
Notice in the above that there are two distinct ways of returning from a callable function. If you haven't made any asynchonous calls, then you can simply return values, but if you have called an asynchonous function, you must return a promise. If you are looking for a Reject or Resolve in the calling routine, a return of values is assumed to be a Resolve.
I hope this helps others... Best of luck!
Try eliminating the client and call the Firebase function through curl.
Call functions via HTTP requests