Firebase cloud functions: can't extract parameters when invoking a callable function - javascript

I'm using the Flutter SDK to call a cloud function. I add a parameter someField but on the cloud side, it cannot be retrieved and is always undefined. This is how I call the cloud function
static Future<void> doSomething() async {
await Firebase.initializeApp();
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'myFunction',
);
dynamic resp = await callable.call(
<String, dynamic>{
'someField': 'Hurray!',
},
);
}
The cloud function is written as follows
exports.myFunction = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const myParameter = req.body.someField; // is always Undefined
// also tried
// req.query.someField
// req.params.someField
doSomething(myParamter)
})
});

You're mixing up callable functions on the client app with HTTPS functions on the backend. Please review the documentation to understand the difference between them. If you want to use the Firebase SDK on the client to invoke a Cloud Functions, you should declare that using onCall instead of onRequest. When you write a callable function with onCall, you will have access to the input arguments via the first parameter delivered to the callback.
exports.myFunction = functions.https.onCall((data, context) => {
// data.someField should be populated from your client request
});

Related

Accessing Firestore Document data within JavaScript https on request function

I am working on a piece of my app where I need to make a call to a Firebase Function, which parses through Firestore data to return a dictionary that I will use to populate UI in Swift.
My function is declared as so:
exports.getUIBetData = functions.https.onRequest( (request, response) => {
This function takes in a userID as body parameter. Then, I need to hit firebase to get a specific document's data tied to this userId and perform some actions on it. I believe I am running into some issues with the async functionality behind getting data from a document, as I keep getting errors or simple promises that haven't been resolved. Here is my query:
const body = request.body;
const userId = body.data.userId;
const bettorIdDoc = admin.firestore()
.collection("user_dim").doc(userId).get().data();
I can confirm that "user_dim" is a valid collection, and the userId is a key to a document within it. However, I can't access the fields tied to this doc.
I was originally trying with just .data(), and realized from the official documentation that you need to do .get().data(), however this is async. How do I handle the async nature when I am attempting to do this within my main function (exports.getUIBetData = functions.https.onRequest( (request, response) => {)?
Error:
TypeError: admin.firestore(...).collection(...).doc(...).get(...).data is not a function
Loading data from Firestore (and pretty much any cloud API) is an asynchronous operation. You can see this by checking the return type of get(), which is Promise<DocumentSnapshot> and not just DocumentSnapshot.
This means you'll have to use then or await (if you're in an async context) to be able call data():
const bettorIdRef = admin.firestore()
.collection("user_dim").doc(userId)
ref.get().then((snapshot) => console.log(snapshot.data());

How should I get API token before executing Unit Test cases?

Within my unit test cases, I'm trying to do unit tests against some data in the API therefore an API token is required. I'm hoping to find a way to call token API and store it in Redux before firing any API.
I'm aware of setup.js in Jest, tried calling my API there and store in Redux didn't work well. I don't think the setup.js waited the method to finish completely before starting the unit test.
// Within the Setup.js, I was calling method directly
const getAPItoken = async() => {
await getToken();
}
getAPItoken();
Currently I'm getting the API token in 1 of the Unit Test files. Upon the method completion, rest of the Unit Tests will run fine since they are getting the API token from Redux.
Sample of what I'm doing now
describe('Get API token', () => {
test('it should return true after getting token', async () => {
// Within the method itself, it actually store the token to redux upon receiving from API, also it will return TRUE upon success
const retrievedToken = await getToken();
expect(retrievedToken).toBeTruthy();
});
Is there a better way to handle this?
You can use globalSetup. It accepts an async function that is triggered once before all test suites.
So you can optain the API key and set it on node global object so you can access if from anywhere.
// setup.js
module.exports = async () => {
global.__API_KEY__ = 'yoru API key';
};
// jest.config.js
module.exports = {
globalSetup: './setup.js',
};

Sending firebase database data to client through cloud function

I am trying to send data from my firebase database back to my client in a cloud function. I want to return the entire json child object. Here is the code for my cloud function:
exports.addNumbers = functions.https.onCall((data, context) => {
admin.database().ref('store/ingredients').once('value', function(snapshot) {
return snapshot.val();
});
});
Here is my client code that is invoking this cloud function and accessing its data:
const addNumbers = firebase.functions().httpsCallable('addNumbers');
addNumbers().then(result => {
console.log(result.data + "in client");
});
But the data returned in the client is null and the cloud function is returning only object [Object] in the firebase logs. Please someone help me.
You're almost there. You just need to return something from the top-level code in your Cloud Function.
The easiest way is to use a then clause:
exports.addNumbers = functions.https.onCall((data, context) => {
return admin.database().ref('store/ingredients').once('value').then((snapshot) => {
return snapshot.val();
});
});
Now the value from the database "bubbles up" to the calling code, and is then returned out of your function as a promise.
On modern JavaScript versions, you can make this code easier to read by using async/await:
exports.addNumbers = functions.https.onCall(async (data, context) => {
let snapshot = await admin.database().ref('store/ingredients').once('value')
return snapshot.val();
});
This works exactly the same under the hood (it's really just syntactic sugar), but most developers find it much easier to read.

How to connect to MongoDb Atlas from firebase cloud functions

I'm trying to follow this answer to connect to a mongodb atlas db from cloud functions.
I use this code from the answer above:
import { MongoClient } from 'mongodb'
const uri = 'mongodb://<USER>:<PASSWORD>#foo-shard-00-00-xxx.gcp.mongodb.net:27017,foo-shard-00-01-xxx.gcp.mongodb.net:27017,foo-shard-00-02-xxx.gcp.mongodb.net:27017/test?ssl=true&replicaSet=FOO-shard-0&authSource=admin&retryWrites=true'
let client
export default async () => {
if (client && client.isConnected()) {
console.log('DB CLIENT ALREADY CONNECTED')
} else try {
client = await MongoClient.connect(uri, { useNewUrlParser: true })
console.log('DB CLIENT RECONNECTED')
}
catch (e) {
throw e
}
return client
}
And then I have a function like this:
export const myFunction = functions.region('europe-west1').https.onRequest((request, response) => {
console.log(client.isConnected());
});
When I run firebase serve locally, I don't see 'DB CLIENT ALREADY CONNECTED' or 'DB CLIENT RECONNECTED' which means that anonymous function didn't get called. And when I try to access the client variable inside myFunction I get an error.
I'm learning Node at the moment so this might be a simple question. Thanks,
If you have some code to run in Cloud Functions, you need to invoke it. It's not possible to simply declare or export some function and expect it to run without calling it. If you want something to run at the global scope of your code, either don't wrap it in a function, or call the function at the global scope.

calling exports node.js function from front-end js using firebase

I am trying to send emails using js on firebase and am struggling to call back-end functions through firebase.
Here is the simple function I would like to call (inside index.js)
const functions = require('firebase-functions');
exports.test = functions.https.onCall((data, context) => {
return 1;
});
I would like to be able to call it through my script or external js like so:
function sendEmail()
{
var testFunction = firebase.functions().httpsCallable('test');
alert(testFunction());
}
^ This is inside the script tag of the index.html file
I've tried following https://firebase.google.com/docs/functions/callable
but have trouble understanding it
I've also tried seeing how this example of an email sender works and work with that but all the functions are triggered by the event of a user logging in or deleting their account: https://github.com/firebase/functions-samples/tree/master/quickstarts/email-users#functions-code
Follow the steps given in the link: Set up your local environment. Then add the following function.
function sendEmail() {
return new Promise((resolve,reject)=>{
firebase.functions().httpsCallable('test')().then(function(result) {
// Handle result of your cloud function.
resolve(data)
}).catch(function(err){
//Handle error
reject(err)
});
})
}
Now you can call this function anywhere. Your mistake was that you were calling sendEmail() as a synchronous function but it is an asynchronous function.
Hope it helps!

Categories