Ian W
Ian W

Reputation: 990

Properly chaining functions in Firebase function

I am building a function in Firebase Cloud Functions, which can utilize Node.js modules.

I am still new to the use of .then() and I'm struggling to figure out a way to chain my 3 functions webhookSend(), emailSendgrid(), and removeSubmissionProcessor() that happen right after the 'count' is incremented (the if statement that checks temp_shouldSendWebhook). The whole idea of returning promises still confuses me a little, especially when it it involves external libraries.

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp();

const request = require('request');

const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
const SENDGRID_API_KEY = firebaseConfig.sendgrid.key;
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);

exports.submissionProcess = functions.database.ref('/submissions/processor/{submissionId}').onWrite((change, context) => {
  var temp_metaSubmissionCount = 0; // omitted part of function correctly sets the count
  var temp_shouldSendWebhook = true; // omitted part of function correctly sets the boolean

  return admin.database().ref('/submissions/saved/'+'testuser'+'/'+'meta').child('count')
    .set(temp_metaSubmissionCount + 1)
    .then(() => {

      // here is where im stuck
      if (temp_shouldSendWebhook) {
        webhookSend();
        emailSendgrid();
        removeSubmissionProcessor();
      } else {
        emailSendgrid();
        removeSubmissionProcessor();
      }

    })
    .catch(() => {
      console.error("Error updating count")
    });

});

function emailSendgrid() {
  const user = '[email protected]'
  const name = 'Test name'

  const msg = {
      to: user,
      from: '[email protected]',
      subject:  'New Follower',
      // text: `Hey ${toName}. You have a new follower!!! `,
      // html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,

      // custom templates
      templateId: 'your-template-id-1234',
      substitutionWrappers: ['{{', '}}'],
      substitutions: {
        name: name
        // and other custom properties here
      }
  };
  return sgMail.send(msg)
}

function webhookSend() {
  request.post(
    {
      url: 'URLHERE',
      form: {test: "value"}
    },
    function (err, httpResponse, body) {
      console.log('REQUEST RESPONSE', err, body);
    }
  );
}

function removeSubmissionProcessor() {
  admin.database().ref('/submissions/processor').child('submissionkey').remove();
}

I want to be able to construct the 3 functions to be called one after another such that they will all execute.

Upvotes: 1

Views: 2198

Answers (2)

helloitsjoe
helloitsjoe

Reputation: 6529

In order to chain these functions, they each need to return a promise. When they do, you can call them sequentially like this:

return webhookSend()
  .then(() => {
    return emailSendgrid();
  })
  .then(() => {
    return removeSubmissionProcessor();
  });

Or in parallel like this:

return Promise.all([webhookSend, emailSendgrid, removeSubmissionProcessor]);

Now, to make your functions return promises:

emailSendgrid: It looks like this returns a promise (assuming sgMail.send(msg) returns a promise), so you shouldn't need to change this.

removeSubmissionProcessor: This calls a function that returns a promise, but doesn't return that promise. In other words it fires off an async call (admin.database....remove()) but doesn't wait for the response. If you add return before that call, this should work.

webhookSend calls a function that takes a callback, so you'll either need to use fetch (which is promise-based) instead of request, or you'll need to convert it to return a promise in order to chain it:

function webhookSend() {
  return new Promise((resolve, reject) => {
    request.post(
      {
        url: 'URLHERE',
        form: {test: "value"}
      },
      function (err, httpResponse, body) {
        console.log('REQUEST RESPONSE', err, body);
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      }
    );
  });
}

Upvotes: 5

Vinil Prabhu
Vinil Prabhu

Reputation: 1289

Use async functions and then you can use .then() or await before every function calls

for reference read this

Upvotes: -1

Related Questions