Is it possible to assert the number of reads/writes in a Firebase Cloud Function test? - firebase

I have a test suite that runs integration tests for my Firebase Cloud Functions against a locally running Firebase emulator, is it possible to assert the number of reads/writes made to the emulated Firestore instance?
E.g. for the following function I would want to assert that 1 write occurs & 0 reads
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.addMessage = functions.https.onCall(async (data, context) => {
const original = data.query.text;
const snapshot = await admin.firestore().collection("/messages").add({ original: original });
return { code: 200, message: "Success" };
});

Currently, Its not possible to assert the number of reads and writes in a Firebase Cloud Function test.
I would suggest that the best way would be is to write the test data to the Firebase Emulator, then read it back and also count the number of collections/documents as well using Client SDK.
If you want to assert data coming from the Firebase Emulator, then you can use Requests Monitor. We used Client SDK for this purpose as Admin SDK requests and access calls will not be listed because it bypass Security Rules. It's a current limitation according to this Blog.
The Firebase emulator has a log that shows requests as shown in example below.
Note: I don't think that you should have a dependency on it, since Firebase Emulator Request Monitor is a new feature that its implementation may change and Admin SDK can be included over time.

Related

gcp cloud function queue

I have written a GCP Cloud Function with a http trigger ("REST API") that invokes a Cloud Function with a Firebase Realtime Database trigger (onCreate).
The Cloud Function with a Firebase Realtime Database trigger performs REST calls to other services based on received data from the REST API.
I have noticed that the called services sometimes returns http 429 (too many calls) since my REST API does not have a limit to how many calls can be received.
The REST API has security measures in place to prevent unauthorised calls to invoke the Cloud Function with a Firebase Realtime Database trigger (onCreate). I do not wish to limit the amount of calls to my API, but rather place all requests in a queue and process them in sequence.
It is important that all calls are processed as promptly as possible. I do not wish to process transactions in 60 second intervals.
In my current solution all calls to the GCP HTTP http REST API immediately triggers the GCP Cloud Function via Firebase Realtime Database insert (onCreate event).
What I would like is to maybe have a queue in between my REST API and the Firebase Realtime Database insert (onCreate event) to ensure that only one GCP Cloud Function can execute simultaneously.
What is the best way to achieve this functionality?
Kind regards /K
EDIT:
Might Maximum instances be a solution here?
https://cloud.google.com/functions/docs/configuring/max-instances
You can Trigger Cloud Functions using Cloud Tasks. Here's an example I use whereby emails are placed inside of the Cloud Task queue I created and they are then processed by the task runner one-after-another:
import { CloudTasksClient } from '#google-cloud/tasks';
// define and use the following for your task:
// CLOUD_FUNCTION_URL
// TASK_HTTP_METHOD
// TASK_CONTENT_TYPE
// SERVICE_ACCOUNT_EMAIL
// PROJECT_ID
// REGION
// QUEUE_NAME
const client = new CloudTasksClient({ projectId: PROJECT_ID });
/**
* Build the Cloud Task
* In the below example we take a POST body and
* stringify it before converting to base64.
*/
const convertedPayload = JSON.stringify(payload);
const body = Buffer.from(convertedPayload).toString('base64');
const task: TaskT = {
httpRequest: {
url: CLOUD_FUNCTION_URL,
httpMethod: TASK_HTTP_METHOD,
body,
headers: {
'Content-Type': TASK_CONTENT_TYPE,
},
oidcToken: {
serviceAccountEmail: SERVICE_ACCOUNT_EMAIL,
},
},
scheduleTime: {
seconds: Date.now() / 1000, // <--- start the task now (in unix time)
},
};
return client.createTask({
parent: client.queuePath(PROJECT_ID, REGION, QUEUE_NAME),
task,
});
You'll also need to configure some IAM permissions for your development and app IAM users like Cloud Functions Invoker and Cloud Functions Viewer.

How to trigger Pub/Sub Topic for Firebase Emulator

Using Firebase Functions, I have code that runs every hour via a Google Cloud Scheduler Job.
It looks like this:
exports.hourly_tick =
functions.pubsub.topic("hourly-tick").onPublish((message, context) => {
return getData()
.then((data) => {
sendEmail(data["message"]);
})
.catch((error) => {
return console.log("🚩 Caught error: ", error);
});
});
I need to be able to test this locally, and am able to start my Firebase Emulator via firebase emulators:start from my terminal. However I do not know how to trigger this function in my local test environment to see logs in the local emulator.
How can I test this scheduled job / firebase function with the local emulator?
This is an ongoing feature request in Firebase tools (see GitHub issue).
As mentioned in the thread:
I think we maybe misled with how we represented #2011. It lets those functions be loaded into the emulator but doesn't actually trigger them on a schedule. Instead you'd have to manually trigger them using a Pub/Sub message.
You can check a workaround on this answer where you'd have to manually trigger a scheduled function using a Pub/Sub message.

Flutter: How can I create repeating push notifications?

how can I create repeating push notifications? I want to be able to schedule those at least a little bit flexible (e. g. every year, every 3 months, every month, ... nothing too fancy).
I already tried the package flutter_local_notifications but for me it looks like I just can create repeating notifications for every week or every day.
I am already using firebase so I would be open to any solutions which are based on that.
My goal is the following: The users of my app should be able to create a (repeating) reminder in form of a push notification.
A Firebase-based solution: Assuming you are on the Blaze plan, you could create a scheduled cloud function using PubSub, like here. Such methods are billed based on the number of total functions, not invocations, so you have some flexibility, though if you need this to be a per-user feature, it might not be a viable solution. If you can provide a minimal granularity (for example, 5 minutes), you could deploy a single scheduled function to notify all pending "tasks" to users, while the users can schedule a reminder by saving an entity in Cloud Firestore, which describes the exact time and date when they would receive a reminder. The scheduled function then maps all currently pending tasks, and deleted handled tasks.
For example, in TypeScript you might do something like:
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
const serviceAccount = require("path/to/serviceAccountKey.json")
// Initialize the app with a service account, granting admin privileges
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://databaseName.firebaseio.com"
})
// Declare the exported PubSub function
export const checkPendingNotifications = = functions.pubsub.schedule('every 5 minutes').onRun(async (context) => {
console.log('This will be run every 5 minutes!')
const db = admin.firestore()
const scheduledTasks = await db.collection('s').get();
scheduledTasks.forEach(taskSnapshot => {
const data = taskSnapshot.data()
// ... TODO Check for user-submitted dates in data
})
return null;
});

How do you return data from a back end node js to front end html?

Im working with Firebase functions. I want to return data from a Node JS cloud function to to be able to use it with html /front end code.
const functions = require("firebase-functions");
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
Keep in mind that not all the Javscript code you have will be in a Cloud Function, code that directly interacts with your front end will still need to be in there, however, any code that directly operates with firebase can be transfered to a Cloud Functions.
You could use either Callable Functions, which can be called directly from your Javascript code but are hosted on the cloud, or HTTP Functions which are basically a HTTP request to an URL that sends you a response when it's done.
Cloud Functions also provides an emulator so you can test everything locally.
Before starting anything thought, I would recommend you to watch this playlist provided by Firebase that gives a really good intruction to Cloud Functions, so exactly what you are looking for.

Deploying Cloud Firestore Trigger function from GCP Console

I have a function that is fired on a onUpdate trigger from my cloud firestore database.
The function not being called at all when I change my database.
I did not deploy the function using firestore CLI, instead I deployed it using the GCP Console.
Here is the function:
exports.NotificationListener = functions
.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
const userId = context.params.userId.toString();
const eventId = context.eventId;
console.log('EventId:' + eventId);
console.log('Change in:' + userId);
return 200;
});
Here is the deployment information from the GCP console (showing the trigger):
Finally, here is the Cloud Firestore schema:
I want to monitor any changes to any "USER" in the collection: "/user", hence I am using "user/{userId}".
Why is this function not being called when I change the database ?
EDIT 1
A little information about my environment:
I have my entire project core in a TypeScript file. I have over 40 HTTPS triggered functions that are currently online.
I add a new function in my TS file, then I do a npm run build to compile and get the JS file.
Finally, I go to Google Cloud Console and create a function and choose "Zip Upload" and upload the compiled JS file (obviously, along with the required JSON files for getting Database URL, Authentication etc.)
This approach works perfectly fine, at least for HTTP triggered firestore functions.
Now I repeated the same steps as above for the onUpdate trigger and just instead of choosing HTTP trigger, I chose Cloud Firestore trigger. The trigger information can be found above in the screenshot.
onUpdate is not being fired on DB changes.
EDIT 2
My event trigger function NotificationListener is showing up in the firebase console functions list along with my other 40 HTTPS functions. But it is not being called.
#doug-stevenson, your answer seems to have disappeared, I am not sure why.
Anyway, I found the reason why it wasn't working.
My firebase database was in project "Placeholder 1" and my GCP functions were in project "Placeholder 2".
Now, I was able to update the "Placeholder 1" DB from GCP functions (in "Placeholder 2") using firabse-functions API because I set the DatabaseURL to "Placeholder 1".
But, just setting the DatabaseURL to the desired database doesn't work if you want to LISTEN to the database for changes. You actually need to have the function in the same project otherwise it is not able to subscribe and listen for events.
I think it's a little inconsistent that you can read/write to a DB from different projects, but to listen for events, function needs to be in same project.
Or maybe I am missing something fundamental that caused this confusion for me.

Resources