Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cloud Functions for Firebase onWrite trigger: snapshot.val is not a function

I created few functions in the same index.js file, which is sendEmail, sendEmailByDbStatusChange and sendEmailConfirmation.

sendEmail- To be call via HTTP/API

sendEmailByDbStatusChange - listening to DB while value change, but the action is hardcoded

sendEmailConfirmation- Listing to DB while value change, the action subject to the snapshot.

Below is my codes:

const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const gmailEmail = functions.config().gmail.email;
const gmailPassword = functions.config().gmail.password;
const mailTransport = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: gmailEmail,
    pass: gmailPassword,
  },
});

// Sends an email confirmation when a user changes his mailing list subscription.
exports.sendEmail = functions.https.onRequest((req, res) => {
  if (req.body.subject === undefined || req.body.recipient === undefined) {
    // This is an error case, as "message" is required.
    //res.status(400).send('subject/body/recipient is missing!');
    return false
  } else {
    const mailSubject = req.body.subject;
    const mailHtmlBody = req.body.htmlBody;
    const mailRecipient = req.body.recipient;



    const mailOptions = {
      from: '"Food Ninja." <[email protected]>',
      to: mailRecipient,
      subject: mailSubject,
      html: mailHtmlBody
    };

    //res.status(200).send('Success: ' + mailSubject + ' to ' + mailRecipient);

    return mailTransport.sendMail(mailOptions)
      .then(() => {
        console.log(`${mailSubject}subscription confirmation email sent to: `, mailRecipient)
        return res.status(200).send('Success: ' + mailSubject + ' to ' + mailRecipient)
      })
      .catch((error) => console.error('There was an error while sending the email:', error));
  }
});

exports.sendEmailByDbStatusChange = functions.database.ref('/users/{uid}').onWrite((event) => {
  //const snapshot = event.data;
  //const val = snapshot.val();

  //if (!snapshot.changed('subscribedToMailingList')) {
  //  return null;
  //}

  const mailSubject = 'Sending email with Cloud Function - by DB onWrite Trigger';
  const mailHtmlBody = '<h1>Hello Jerry</h1><p>If you receiving this means that you have successfully deployed a customized firebase function</p><p>Be Happy!<br><br>Food Ninja Team</p>';
  const mailRecipient = '[email protected]';

  const mailOptions = {
    from: '"Food Ninja." <[email protected]>',
    to: mailRecipient,
    subject: mailSubject,
    html: mailHtmlBody
  };

  //const subscribed = val.subscribedToMailingList;

  // Building Email message.
  //mailOptions.subject = subscribed ? 'Thanks and Welcome!' : 'Sad to see you go :`(';
  //mailOptions.text = subscribed ? 'Thanks you for subscribing to our newsletter. You will receive our next weekly newsletter.' : 'I hereby confirm that I will stop sending you the newsletter.';

  return mailTransport.sendMail(mailOptions)
    .then(() =>
      console.log(`${mailSubject}subscription confirmation email sent to: `, mailRecipient)
      //return res.status(200).send('Success: ' + mailSubject + ' to ' + mailRecipient)
    )
    .catch((error) => console.error('There was an error while sending the email:', error));
});

exports.sendEmailConfirmation = functions.database.ref('/users/{uid}').onWrite((event2) => {
  console.log(event2)
  console.log(event2.val())
  console.log(event2.val().data)
  console.log(event2.data)
  console.log(event2.data.val())
  const snapshot = event2.data;
  console.log(snapshot)
  const val = snapshot.val();
  console.log(val)

  if (!snapshot.changed('subscribedToMailingList')) {
    return null;
  }

  const mailOptions = {
    from: '"Spammy Corp." <[email protected]>',
    to: val.email,
  };

  const subscribed = val.subscribedToMailingList;

  // Building Email message.
  mailOptions.subject = subscribed ? 'Thanks and Welcome!' : 'Sad to see you go :`(';
  mailOptions.text = subscribed ? 'Thanks you for subscribing to our newsletter. You will receive our next weekly newsletter.' : 'I hereby confirm that I will stop sending you the newsletter.';

  return mailTransport.sendMail(mailOptions)
    .then(() => console.log(`New ${subscribed ? '' : 'un'}subscription confirmation email sent to:`, val.email))
    .catch((error) => console.error('There was an error while sending the email:', error));
});

My problem is, after i deploy the code to firebase function, the console shows that the sendEmailConfirmation unable to execute smoothly due to event2.val is not a function.

My current code combined with my customize code and the original code, which sendEmailConfirmation is the original code. When run the original code independently it did work (original was event instead of event2 for the snapshot).

Please advise.

like image 460
Jerry Avatar asked May 01 '18 04:05

Jerry


People also ask

How do I trigger a Firebase cloud function?

Cloud Firestore function triggersTriggered when a document is written to for the first time. Triggered when a document already exists and has any value changed. Triggered when a document with data is deleted. Triggered when onCreate , onUpdate or onDelete is triggered.

What are cloud functions in Firebase?

Cloud Functions for Firebase is a serverless framework that lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your JavaScript or TypeScript code is stored in Google's cloud and runs in a managed environment.

Which type of trigger is bound while creating cloud function in the lab?

With Cloud Functions you write simple, single-purpose functions that are attached to events emitted from your cloud infrastructure and services. Your Cloud Function is triggered when an event being watched is fired.


2 Answers

It looks like you updated to v1.0 of the Firebase SDK for Cloud Functions, but didn't upgrade your code to match.

The entire process is explained in this documentation page. Right now you're being hit by the changes in database triggers, which shows that:

Event data now a DataSnapshot

In earlier releases, event.data was a DeltaSnapshot; now in v 1.0 it is a DataSnapshot.

For onWrite and onUpdate events, the data parameter has before and after fields. Each of these is a DataSnapshot with the same methods available in admin.database.DataSnapshot. For example:

Before (<= v0.9.1)

exports.dbWrite = functions.database.ref('/path').onWrite((event) => {
  const beforeData = event.data.previous.val(); // data before the write
  const afterData = event.data.val(); // data after the write
});

Now (v1.0.0)

exports.dbWrite = functions.database.ref('/path').onWrite((change, context) => {
  const beforeData = change.before.val(); // data before the write
  const afterData = change.after.val(); // data after the write
});

According to that example, you'll need something along these lines:

exports.sendEmailConfirmation = functions.database.ref('/users/{uid}').onWrite((change, context) => {
  const snapshot = change.after;
  const val = snapshot.val();
  console.log(val)

  if (!snapshot.changed('subscribedToMailingList')) {
    return null;
  }

  const mailOptions = {
    from: '"Spammy Corp." <[email protected]>',
    to: val.email,
  };

  const subscribed = val.subscribedToMailingList;

  // Building Email message.
  mailOptions.subject = subscribed ? 'Thanks and Welcome!' : 'Sad to see you go :`(';
  mailOptions.text = subscribed ? 'Thanks you for subscribing to our newsletter. You will receive our next weekly newsletter.' : 'I hereby confirm that I will stop sending you the newsletter.';

  return mailTransport.sendMail(mailOptions)
    .then(() => console.log(`New ${subscribed ? '' : 'un'}subscription confirmation email sent to:`, val.email))
    .catch((error) => console.error('There was an error while sending the email:', error));
});
like image 145
Frank van Puffelen Avatar answered Oct 03 '22 15:10

Frank van Puffelen


Since version 1.0.0 of the firebase-functions module, database onWrite events now deliver a Change object rather than a DataSnapshot object as the first parameter. You can read about all the breaking changes in 1.0.0 in the documentation. You should use this change object instead to choose if you want to examine the contents of the database before or after the change that invoked it.

like image 43
Doug Stevenson Avatar answered Oct 03 '22 13:10

Doug Stevenson