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!
Related
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());
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
});
I'm attempting to write some unit tests for API endpoints, and decided on JavaScript Express w/ Supertest. I've got the basic downs, but running into issues checking the response for a specific field. I want to parse the body and check if an expected field returns and has the correct value. Most everything I've seen online uses this method, but when I try it always passes, even when I enter values I know don't exist in the JSON. Any advice? Here is my code snippet:
describe('GET category', function () {
it('response w/ only injury returned', function () {
request('endpoint')
.get('path')
.set('header', 'token')
.expect(200)
.then(response => {
console.assert(response.body, "Baseball")
})
})
});
I have also tried changing the .then to .expect, with same results. If I do response.body.specificFieldinBody I get similar results. Any help?
You can do with your way - use .then syntax, but I think use Supertest assertion syntax will be good.
This mean, use response.body.should.have.property("Baseball"); instead of console.assert(response.body, "Baseball") (Baseball is your special field).
Or, My suggestion is creating a re-use code: Put a assertion callback function to a next expects section.
const isIncludeField = function (fieldName) {
return function (res) {
res.body.should.have.property(fieldName);
};
}
describe('GET category', function () {
it('response w/ only injury returned', function () {
request('endpoint')
.get('path')
.set('header', 'token')
.expect(200)
.expect(isIncludeField('Baseball')) //
.end(done); // call done callback
})
});
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.
Nothing happens to my firestore when I call the function below. I also can't see "inside helloWorld" on my GCP logs.
exports.helloWorld = functions.https.onCall((data, context) => {
console.log("inside helloWorld);
const users = admin.firestore().collection('users');
users.doc(data.userId).get().then( // --------------------Line A
(snapshot) => {
if (snapshot.exists) {
console.log("snapshot exists");
return null;
} else {
console.log("inside else");
users.doc(data.userId).set({
name: data.name
});
return null;
}
}
).catch(() => 'obligatory catch');
return; //-----------------------------------------------Line B
});
However, when I place the return on Line A, the function works as expected and a document is created in my firestore. "inside helloWorld" is shown on my GCP logs.
Why is that so?
I really appreciate any levels of clarification.
According to the documentation for callable functions (particularly the part about sending back a result):
To return data after an asynchronous operation, return a promise. The
data returned by the promise is sent back to the client. For example,
you could return sanitized text that the callable function wrote to
the Realtime Database.
Even if you don't want to send any content in the response, Callable functions still need to respond to the HTTP request that originated them, as all HTTP transactions do.
In either case, you still need to make use of the promises in all your async calls so that Cloud Functions knows when to respond to the client, after all the work is complete. Placing the return statement on "line A" is effectively returning the promise from the async work started by get(), fulfilling this requirement. Without the return statement, Cloud Functions is terminated your function because it thinks there is no more work to complete in order to send the final response.
If you're not familiar about how promises work in JavaScript, watch my video tutorials here: https://firebase.google.com/docs/functions/video-series/