Getting Internal Error when calling Firebase Cloud Function Directly - javascript

I'm getting the following Error when trying to call my Firebase Cloud Function directly:
Error: internal
at new HttpsErrorImpl (error.ts:65)
at _errorForResponse (error.ts:175)
at Service.<anonymous> (service.ts:276)
at step (tslib.es6.js:102)
at Object.next (tslib.es6.js:83)
at fulfilled (tslib.es6.js:73)
I've noticed that users have seen similar issues in the past where the Firebase version 7.22.0 was causing it, and it was resolved in 7.22.1 but I'm on 8.3.0, so that shouldn't be a problem.
My cloud function is never being triggered, I don't see any errors in the Firebase Functions log.
Here is my client-side function:
async function testCallFunction() {
const callTest = functions.httpsCallable('callTest');
return await callTest({ companyId: coRef })
.then((result) => {
console.log(result)
}).catch(err => {
console.log(err)
})
}
And here is my Cloud Function:
exports.callTest = functions.https.onCall((data, context) => {
console.log('Call Test Fired')
return admin.firestore().collection('users').doc(context.auth.uid).get()
.then((doc) => { return doc.data()})
.catch(err => {throw new functions.https.HttpsError(err.message)})
})
But it's never reaching the cloud function I get the internal error only and no logs in the cloud function logs.
I'm not sure what I'm doing wrong? I have made sure I'm using the latest firebase JS SDK (8.3.0) and I've tried to stick as close to the doc as I can. Is there anything obvious I'm doing wrong here?
Thanks

I'm seeing the exact same symptoms while using AngularFireFunctions httpsCallable method. Recently updated firebase sdk to 8.3.1 from 8.2.4 and I suspect this introduced the https callable internal error. Downgrading to 8.2.4 resolves the issue:
npm install firebase#8.2.4 --save

Related

Weird Promise behavior in Jest when tests rely on the Firestore emulator

While using the Google Cloud's Firestore Emulator, I'm calling the following method:
global.db.runTransaction(async () => 100)
This dummy call works when executed using node, but fails when executed using jest inside a test function. When running it from Jest, the runTransaction method throws:
Error: You must return a Promise in your transaction()-callback.
referencing node_modules/#google-cloud/firestore/build/src/transaction.js:362:27
That file has the following snippet of code, which is causing the failure:
async runTransaction(updateFunction, options) {
// ...
const promise = updateFunction(this);
if (!(promise instanceof Promise)) {
throw new Error('You must return a Promise in your transaction()-callback.');
}
// ...
}
In other words, when running in Jest, the library considers async () => 100 function not to return a promise.
I changed the library code to add some debug messages:
async runTransaction(updateFunction, options) {
// ...
const promise = updateFunction(this);
if (!(promise instanceof Promise)) {
// *** Added these debug messages ***
console.log(updateFunction);
console.log(promise);
console.log(promise instanceof Promise);
throw new Error('You must return a Promise in your transaction()-callback.');
}
// ...
}
And I am getting the following:
[AsyncFunction (anonymous)]
Promise { 100 }
false
To complicate matters, this test passes as expected within jest:
test('promise', () => {
const fn = async () => 100;
const res = fn();
expect(res instanceof Promise).toEqual(true);
}
I'm a bit at a loss here as to why executing this library code with Jest fails compared to Node, while executing a similar statement with Jest in the code snippet just above succeeds.
Any suggestions on how to approach this head-scratcher?
EDIT
Initializing a local db variable instead of global.db solves the issue.
Originally, I had the global.db set in jest.config.js like so:
module.exports = async () => {
const Firestore = require("#google-cloud/firestore");
global.db = new Firestore({ projectId: process.env.GOOGLE_CLOUD_PROJECT });
return {}
}
The application code works by relying on global.db that is set through an initialization file, and I wanted to mimic this for the tests.
I guess the question is, then, how should one go about initializing a global db variable that can be used in all tests?
ANSWER
Ended up creating a setup.js file and loaded it with setupFilesAfterEnv per the docs. Content:
const Firestore = require("#google-cloud/firestore/build/src");
global.db = new Firestore({ projectId: process.env.GOOGLE_CLOUD_PROJECT });
Now things are working properly. Other solutions, such as using the globals entry in the object exported in jest.config.js (not shown in code snippets above) didn't work as per the documentation the values must be JSON-serializable, which is not the case for Firestore.

React Native, Axios and Unhandled Promise Rejections

I am using Axios in my React-Native app to communicate with a Nodejs backend, and am using react-redux dispatch to call the actions that will utilize Axios. However, no matter what I try I land up getting "Unhandled Promise Rejection" anytime there is an error of any sort. This is frustrating me so much I'm about to give up on Axios and just go back to using fetch. I've googled this problem (which seems to be very common indeed), but have not found any solid solution yet.
Here is an example of how I'm using Axios in my actions to send requests to my Nodejs backend server:
export const getAppointments = (userId) => {
return async (dispatch) => {
const request = axios
.get(`${SERVER_BOOKINGS}/upcoming_appointments/${userId}`)
.then((response) => {
let ourPayload = {};
ourPayload.success = response.data.success;
if (ourPayload.success) {
ourPayload.bookings = response.data.bookings;
dispatch({
type: GET_UPCOMING_APPOINTMENTS,
payload: ourPayload,
});
}
})
.catch((err) => {
console.log("caught an error, HERE:", err);
//throw err
});
};
};
And here is the code I'm using to call this action:
const getAppointments = async () => {
try {
await dispatch(
bookingActions.getAppointments(userObject.userData.userId)
);
} catch (error) {
console.log("we actually caught an axios error client side!");
}
}
If I leave the code exactly as above and I deliberately cause an error response from my Nodejs code , I get a console.log message saying "caught an error, HERE", but get nothing from the catch block where I actually dispatch the action (2nd block of code).
If I uncomment out the throw err line in the first catch block, I still get the console.log, still get nothing from the second catch block.... but now I get an Unhandled Promise Rejection warning.
Right now, this code works very well as long as there isn't an error, and is completely worthless anytime there is an one. In all honestly, I don't know whether the issue is one with axios, react-redux dispatch or just a failure on my part to understand the way Promises are meant to work, but I've wasted untold hours trying to figure out what should be a really simple matter of catching errors... and I'm falling behind schedule on this project because of it. Any advice/help would be greatly appreciated!
I think that the problem is caused since "bookingActions.getAppointments" is not a Promise function so there is no need to "try-catch" it.
try to change it like this:
const getAppointments = () => dispatch(bookingActions.getAppointments(userObject.userData.userId));

Catching exceptions when calling set in Firestore

I haven't touch JS or Firebase for a while so forgive style issues. I ran into an issue calling set on a DocumentReference. You can't have a value of undefined. That's fine, but i expected my catch block to catch the exception, but instead the whole function crashed (this is within a cloud function). I've tried to simplify the code:
app.get('/test', (req, res) => {
return db.collection('users').doc('someid').set({
first: undefined
})
.then(()=> {
res.send('it worked?');
})
.catch((error: any) => {
console.error(error);
res.status(500).send('it didn\'t work');
})
})
A couple of notes, I'm using ExpressJS and testing this in the Firebase Functions emulator, writing to the real Firestore.
So my question is why isn't the error being caught? Instead I receive a response containing the exception details. Is the solution to check values before calling set? And if so, given it isn't written in the documentation linked above, how are you meant to know that?
The issue is that the set() function is throwing the exception before returning a promise because the parameter is invalid so you should validate that your object has no undefined fields.
From experimentation I have found that it accepts nulls so here is a way to work around the issue:
let firstValue;
app.get('/test', (req, res) => {
return db.collection('users').doc('someid').set({
first: firstValue || null
})
.then(()=> {
res.send('it worked?');
})
.catch((error: any) => {
console.error(error);
res.status(500).send('it didn\'t work');
})
})
I haven't found any indication in the documentation that the set() function does not accept data with undefined fields but I don't know why you would get them there in the first place as I don't think it would be useful to store it in the database. Also it is a best practice to always initialize variables to avoid having to deal with such issues, use blank values such as "" or {} if you haven't calculated the value yet.

Getting "TypeError: Cannot read property 'catch' of undefined" when executing a mailgun cloud function in firebase

I am trying to send an email using mailgun and firebase cloud functions. The mail IS sent, but still returns an error that crashes my app. I believe this is caused by the error:
TypeError: Cannot read property 'catch' of undefined
at exports.sendReportRequest.functions.https.onRequest (/srv/lib/index.js:57:9)
In my code, I am using mailgun's mailgun-js module to send the email, and I have copied code straight from the documentation. However, firebase requires that I "handle promises correctly", ie uses .catch to catch any errors. However, once deployed, it seems that firebase doesn't recognize .send() as a promise, and throws an error.
This is for a flutter application, and whenever I run the cloud function, my app crashes, and the functions throws an error in the firebase logs.
I am already using the blaze plan, so external API calls work. In fact, the email IS sent, but still throws an error that crashes my app. There are no problems with domain and apikey, else email wouldnt be sent.
I have tried using onRequest, onCall and trigger functions, but they all throw the same error.
I've tried returning the promise and not returning, but to no avail. Right now I am not returning anything (or returning void).
// MailGun cloud function
export const sendReportRequest = functions.https.onCall((data, context) => {
console.log("REPORT SENT");
const sendData = {
from: 'Excited User <me#samples.mailgun.org>',
to: 'xxxx#gmail.com',
subject: 'Hello',
text: 'Testing some Mailgun awesomeness!'
};
mg.messages().send(sendData, (error, body) => {
if (error) { console.log("error!", error); }
console.log("message sent:", body);
})
.catch((err) => { console.log("error", err); });
});
Error
TypeError: Cannot read property 'catch' of undefined
at Object.<anonymous> (/srv/lib/index.js:55:9)
at Generator.next (<anonymous>)
at /srv/lib/index.js:7:71
at new Promise (<anonymous>)
at __awaiter (/srv/lib/index.js:3:12)
at exports.sendReportRequest.functions.https.onRequest (/srv/lib/index.js:43:69)
at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:57:9)
at /worker/worker.js:783:7
at /worker/worker.js:766:11
at _combinedTickCallback (internal/process/next_tick.js:132:
That is because send is probably not returning a promise, it uses the callback method instead. you can notice that because you pass it a function which recieves the returning data in the line mg.messages().send(sendData, *(error, body)* => {.
This means there is no catch phrase that can come after it. You get the error inside the callback function , so just handle it there, or if u really want you can wrap it with try and throw out the error, than catch it outside, like this:
try {
mg.messages().send(sendData, (error, body) => {
if (error) { throw new Error(error); }
console.log("message sent:", body);
})
} catch(e) {
// handle error
}
As a side note, I went to mailgun-js github repo to try and get you a documentation example, and i saw it is archived and no longer supported... you might wanna consider using another library :)
Also, here is a nice article I found which explains pretty good the whole callbacks / promises / async-await mess, what are the differences and how and when to use each, if you want to read more.

Firebase Firestore: when do promises from offline write operations resolve?

I activated offline as stated in the docs like:
firebase
.firestore()
.enablePersistence()
.then(() => {
console.log('offlinemode acctivated')
})
The log appears as I would expect.
When adding data like so:
db
.collection('foo')
.add({foo: 'bar'})
.then(docRef => {
console.log('Added Foo: ', docRef.id)
// do some stuff here with the newly created foo and it's id.
})
.catch(console.error)
Neither .then() nor .catch() are getting called while offline. This is even though the object is added to the foo collection in my offline DB as this callback is executed:
db
.collection('foo')
.onSnapshot(callback)
Am I missing something? I would expect the promise to either fail or resolve, so I can react accordingly.
Promises from write operations in Firestore will only resolve when there is confirmation from the server that the write completed, even though they may successfully be written to local cache.
Here's my solution:
I wrap the call in a function that should eventually return a resolving promise no matter the offline/online status
I then get the saved doc from onSnapshot which returns the doc written to local cache (works both online and offline).
Here's my code (with a little typescript):
export function dbWritePromise(functionPromise: Promise<any>): Promise<any>{
if(window.navigator.onLine){
return functionPromise
}
else{
return Promise.resolve()
}
}
// I grabbed this function from a Github issue one upon a time
export function docSnapshotPromise(ref: firebase.firestore.DocumentReference): Promise<any>{
return new Promise((resolve, reject) => {
const unsubscribe = ref.onSnapshot(doc => {
resolve(doc)
unsubscribe()
}, err => {
reject(err)
unsubscribe()
})
})
}
In use (I'm using the update function here, but add would work the same way) This code is working with documents from a collection called organizations:
try{
//update org doc
await dbWritePromise(orgRef.update({
name: 'New and improved name here'
}))
// wait for this updated doc to be written to local cache, then we can get the updated org
const updatedOrgRef = await docSnapshotPromise(orgRef)
const updatedOrg = updatedOrgRef.data()
console.log(updatedOrg.name) // outputs the new and improved name
}
catch (err) { handleError(err) }
The error thrown might be some error with local cache, or it might be a server error such as a permissions error returned by the Firestore rules (when online). Obviously any error from the server during offline mode would silently fail, even when the app is back online.
I'd love to see other peoples' solutions here!

Categories

Resources