Codementor Events

How to use Cloud functions to rescue the app from giving a bad UX?

Published Aug 02, 2020Last updated Aug 03, 2020
How to use Cloud functions to rescue the app from giving a bad UX?

Hey Guys!👋🏽
In this article, I 'll explain one of the techniques that you can use to prevent users from having a bad UX when you want to enable or disable the app services or its functions.
Every business has its own set of variables that could lead to an uncertain situation and this will be very helpful during those times.

For eg. There might be a climate condition like rain/fog where the services of the app have to be paused or there could be a serious bug that has been spotted and it needs some time to fix or the government might have issued regulations to businesses to operate only at certain times of the day.

Your app should not get into any uncertain situation(I pray 🙏) but if it happens the app should be able to pause and resume gracefully.

What is required?

During unplanned situations to the app, a switch like capability which when turned off should make the app unavailable for use and when turned on should notify the affected users (through push notification) that the app is available for use again. All of this should happen with just turn on/off of the switch and without requiring turning any other wheels. Because its 2019 and simple things should be extremely simple.
an example situation would be: When there is an issue with payment processing, you might want to turn off the switch and prevent the user from making payments. When the payment issue is resolved, the switch can be turned on and a push notification will be sent to all the users who tried making payments.

P.S. 2019 was not a typo, I just want to exclude 2020 from my calendar 😒

Implementation Constraints

Relying on the backend API services might not help solve the problem because they are prone to failure and sometimes be the cause of uncertain situation itself. a DDOS attack or bad deployment or loss of a docker image can delay the backend API to come for the rescue. Tools outside the tech stack had to be chosen to make sure we avoid a single point of failure.

Here is what is needed

  • a switch like configuration which could be turned on/off
  • a disposable database of users who got affected during the app downtime
  • a cross-platform push notifications provider

giphy.gif

Yeah! I googled it! but there is no off the shelf tool that can help us implement these functionalities. Except, firebase offers a suite of tools that when put together in a combination will solve the problem.

The required firebase tools to accompolish this combination: Firebase Cloud functions, Firebase Remote Config, Firestore, and Firebase cloud messaging.

Before we start, let me get you familiarized with these firebase products that we are going to use. if you are already familiar skip to the section where I describe how to wire all these products together.

Firebase Remote Config
Remote Config is a cloud service that lets you read a set of values you have defined in the remote config console. It is a JSON set of values that can be read inside the app. If the values are changed in the remote config console, it will start reflecting in the app. No deployments or updates required whatsoever. Learn more about remote config here.

Firebase Cloud functions
Cloud functions offer serverless capabilities exactly as google cloud functions. These cloud functions can be invoked through a set of events. These events include HTTPS events as well. Learn more about firebase from here

Firestore
Cloud-based NOSQL database where documents can be stored and retrieved using client SDKs. You can learn about the firestore from here

Firebase cloud messaging
FCM offers an abstract layer over push notifications for ios and android. Managing tokens and sending of push messages through their SDK.

How to wire all of these services ?

Before we start The following SDKs had to be integrated inside the app Remote Config, Firestore, and FCM.
The remote config values should be read inside the app to determine whether to enable or disable the app services. When the app gets to know the app service is disabled, the app should send its FCM token to a firestore database. The cloud function should be attached to a remote config to listen to the edit events of the config.

How will it work ?

Once all the firebase products are wired, its time for control flow.

For this example, we are looking at an e-commerce app that sells balloons online and delivers it 😁 Because Why not? 💁🏼 we get everything online these days.

Default

By default, the app will be enabled and the cloud function will not be invoked because no edits are made to the config.

Screenshot 2020-07-24 at 3.33.24 AM.png

Disable the app

When the services of the app need to stop, the remote config will be edited.
The cloud function should ignore the update and do nothing. The app learns that it is disabled, so it will send and preserve its FCM token to the firestore database to be later notified when the app is resumed.

Screenshot 2020-07-24 at 3.34.32 AM.png

Re-enable the app by editing the config

Once when the app needs to be resumed again, the remote config had to be edited. The cloud function that listens to the edit event of remote config will be triggered this time. The cloud function will see whether the previous state of the config was disabled and the current state of the remote config is enabled. if the conditions are met, then the cloud function will send a push notification to all the users affected through FCM tokens stored in the firestore.

Screenshot 2020-07-24 at 3.36.40 AM.png

Screenshot 2020-07-24 at 3.36.33 AM.png

Cloud function triggerred by the Remote Config

const functions = require("firebase-functions");
const rp = require("request-promise");
const admin = require("firebase-admin");
const lodash = require("lodash");

admin.initializeApp();

exports.sendPushNotification = functions.remoteConfig.onUpdate(
  (versionMetadata) => {
    const config = admin.remoteConfig();
    return config
      .getTemplate()
      .then((template) => {
        var data = lodash._.get(
          template,
          "parameters.app_enabled.defaultValue.value",
          {}
        );
        var app = JSON.parse(data);
        // if the enabled flag is set to true
        if (app.enabled) {
          var resumeMessage = lodash._.get(app, "resume_message", null);
          if (resumeMessage !== null) {
            sendPushToDevice(resumeMessage.title, resumeMessage.body);
          }
        }
        return;
      })
      .catch((err) => {
        console.error("Unable to get template");
        console.error(err);
      });
  }
);

sendPushToDevice = async (title, body) => {
  var db = admin.firestore();
  let usersRef = db.collection("affected-users");
  let pushTokens = [];
  usersRef
    .get()
    .then(async (snapshot) => {
      snapshot.forEach((doc) => {
        const data = doc.data();
        if (data.token) {
          pushTokens.push(data.token);
        }
        
      });
      const payload = {
        notification: { title, body, image: "https://replaceme-with-a-cute-image.jpg" },
        tokens: pushTokens,
        topic: "app-enabled",
      };
      const response = await admin.messaging().sendMulticast(payload);
      console.log("response = ", response);
      return;
    })
    .catch((err) => {
      console.log("Error getting documents", err);
    });

  //Delete the documents
  usersRef
    .get()
    .then(async (snapshot) => {
      snapshot.forEach((doc) => {
        doc.ref.delete();
      });
      return;
    })
    .catch((err) => {
      console.log("Error getting documents", err);
    });
};

Ta Ta.. See you Later
I hope you find this article useful for your app. Feel free to suggest improvements. Be home and Be safe. Bye ✌🏽
Contact me on Codementor

Discover and read more posts from Dinesh
get started