【问题标题】:Cloud Functions for Firebase onWrite trigger: snapshot.val is not a functionCloud Functions for Firebase onWrite 触发器:snapshot.val 不是函数
【发布时间】:2018-10-11 04:19:18
【问题描述】:

我在同一个 index.js 文件中创建了几个函数,即 sendEmailsendEmailByDbStatusChangesendEmailConfirmation

sendEmail- 通过 HTTP/API 调用

sendEmailByDbStatusChange - 在值更改时监听 DB,但操作是硬编码的

sendEmailConfirmation- 值更改时列出到数据库,操作以快照为准。

以下是我的代码:

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." <foodninjaapp@gmail.com>',
      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 = 'admin@phd.com.my';

  const mailOptions = {
    from: '"Food Ninja." <foodninjaapp@gmail.com>',
    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." <noreply@firebase.com>',
    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));
});

我的问题是,在我将代码部署到firebase函数后,控制台显示sendEmailConfirmation由于event2.val不是函数而无法顺利执行。

我当前的代码结合了我的自定义代码和原始代码,sendEmailConfirmation 是原始代码。当独立运行原始代码时,它确实可以工作(原始代码是event,而不是快照的event2)。

请指教。

【问题讨论】:

    标签: javascript firebase firebase-realtime-database google-cloud-functions


    【解决方案1】:

    您似乎已更新到 Cloud Functions 的 Firebase SDK v1.0,但没有升级您的代码以匹配。

    整个过程在this documentation page中解释。现在你正被changes in database triggers 击中,这表明:

    事件数据现在是 DataSnapshot

    在早期版本中,event.dataDeltaSnapshot;现在在 v 1.0 中,它是 DataSnapshot

    对于onWriteonUpdate 事件,data 参数具有beforeafter 字段。其中每一个都是DataSnapshot,与admin.database.DataSnapshot 中可用的方法相同。例如:

    之前 (

    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
    });
    

    现在 (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
    });
    

    根据该示例,您将需要以下内容:

    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." <noreply@firebase.com>',
        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));
    });
    

    【讨论】:

    • 我不明白......如果它是一个更改对象......那么 if (!snapshot.changed('subscribedToMailingList')) 测试应该不再有效......跨度>
    • 如何获取请求 {uid} 的参数?在新版本中似乎有所改变。有了它,答案就完成了。谢!在我做之前:req.params.uid 但那不再起作用了。
    • 自从我写了这个答案以来,没有任何变化。但我的回答不使用参数。应该是context.params.uid。见firebase.google.com/docs/functions/…。如果这对您不起作用,请使用 minimal code that reproduces the problem 打开一个新问题。
    • @FrankvanPuffelen 您是否知道,您在回答中提到的用于发送电子邮件的官方云函数示例代码中似乎存在错误(即github.com/firebase/functions-samples/blob/Node-8/…)。以下行似乎使用了不存在的changed() 方法:if (!snapshot.changed('subscribedToMailingList')) {}。另一个 SO 成员遇到了问题:stackoverflow.com/questions/53568219/…
    • 在 1.0 中删除了 Snapshot.changed() 方法。我不知道该示例中的过时代码。请在 repo 上提交一个错误(甚至可能是一个 PR),因为这是应该修复的地方。
    【解决方案2】:

    从 firebase-functions 模块的 1.0.0 版开始,数据库 onWrite 事件现在传递 Change 对象而不是 DataSnapshot 对象作为第一个参数。您可以在documentation 中了解 1.0.0 中的所有重大更改。您应该使用此更改对象来选择是否要在调用它的更改之前或之后检查数据库的内容。

    【讨论】:

    • 如果它是一个更改对象......那么 if (!snapshot.changed('subscribedToMailingList')) 测试仍然无效......对吗?
    • 不,点击查看我为每个方法和对象链接的 API 文档,了解它们的工作原理。 Change 对象只有beforeafter 属性。
    • 谢谢..这就是我从你的回答中理解的......所以 snapshot.changed() 无效..我不明白 Jerry 的代码是如何工作的......它仍然是当前的官方示例中的代码更新到 1.0.0...正如您所写的,检查更改的唯一方法是比较两个对象(之前和之后)
    猜你喜欢
    • 2017-08-25
    • 2017-08-26
    • 2017-09-25
    • 2017-11-10
    • 2018-01-26
    • 2018-05-02
    • 2017-09-01
    • 2017-12-13
    相关资源
    最近更新 更多