代码之家  ›  专栏  ›  技术社区  ›  BBacon

在发送电子邮件之前在Firestore函数中组合Firebase Firestore查询结果

  •  0
  • BBacon  · 技术社区  · 6 年前

    我有几个问题困扰着我的承诺。这次我尝试将4个Firestore查询合并为一个,并验证其中是否有任何查询返回结果,如果返回结果,我想向用户抛出一个错误,如果没有,我想继续发送电子邮件并存储它的数据。

    如何等待/组合查询并验证结果?

    export const sendMail = functions.https.onRequest((req: functions.Request, res: functions.Response) => {
      const { name, email, doc, state, city, phone, msg, } = req.body
    
      var dbPromises = [];
    
      const ip1 = req.headers["fastly-client-ip"]
      dbPromises.push(firestore.collection('messages').where('ip1', '==', ip1).get())
    
      const ip2 = req.headers["x-forwarded-for"]
      dbPromises.push(firestore.collection('messages').where('ip2', '==', ip2).get())
    
      dbPromises.push(firestore.collection('messages').where('email', '==', email).get())
    
      dbPromises.push(firestore.collection('blocked-emails').where('email', '==', email).get())
    
      Promise.all(dbPromises)
        .then(() => {
          // TODO validate if there is any result > 0, if any, throw error to the user, else continue into sending the email
        });
    
      const mailOptions = {
        from: `"${name}" <${email}>`,
        to: 'a_email@gmail.com',
        replyTo: `"${name}" <${email}>`,
        subject: `Contact - ${name}`,
        html: `<div>${name}</div>
               <div>${email}</div>
               <div>${doc}</div>
               <div>${state}</div>
               <div>${city}</div>
               <div>${phone}</div>
               <div>${msg}</div>`,
      }
    
      cors()(req, res, () => {
        transport
          .sendMail(mailOptions)
          .then(() => {
            firestore
              .collection('messages')
              .add({
                name: name,
                email: email,
                doc: doc,
                state: state,
                city: city,
                phone: phone,
                msg: msg,
                ip1: ip1,
                ip2: ip2,
              })
              .then(() => {
                return res.status(201).send()
              })
          })
          .catch(error => {
            console.log('Internal error.', error)
            return res.status(500).send()
          })
      })
    
    })
    

    如何真正组合并检查是否有任何结果是>0,是否向用户返回错误?

    1 回复  |  直到 6 年前
        1
  •  1
  •   James Poag    6 年前

    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    const cors = require('cors');
    
    // Needed this to connect to Firestore, my code not yours
    admin.initializeApp();
    admin.firestore().settings({ timestampsInSnapshots: true });
    
    // Emulate the transport.sendMail() for debug purposes
    let transport = {
        sendMail: (options) => {
            return new Promise((resolve, reject) => {
                console.log(`Sending Mail: ${options}`);
                resolve(options);
            });
        }
    }
    
    module.exports.sendMail = functions.https.onRequest((req, res) => {
    
        if (req.method !== 'POST') // won't have a body
            return res.status(400).send(`Error: ${req.method} is not Accepted.`);
    
        // extract params from body
        const { name, email, doc, state, city, phone, msg, } = req.body
    
        let dbPromises = [];
        let firestore = admin.firestore(); // alias to lineup with OP's code
    
        // extract headers
        const ip1 = req.headers["fastly-client-ip"];
        const ip2 = req.headers["x-forwarded-for"];
    
        // validate input, if bad: emit Client error
        if (!ip1 || !ip2 || !email)
            return res.status(400).send("Error: Invalid Request.");
    
        // query previous message existence
        dbPromises.push(firestore.collection('messages').where('ip1', '==', ip1).get());
        dbPromises.push(firestore.collection('messages').where('ip2', '==', ip2).get())
        dbPromises.push(firestore.collection('messages').where('email', '==', email).get())
        dbPromises.push(firestore.collection('blocked-emails').where('email', '==', email).get())
    
        // Need to return a promise so your function doesn't timeout
        return Promise.all(dbPromises)
            .then(resultDocs => {
                if (resultDocs.length !== 4)
                    throw new Error("Programmer Error");
    
                // validate if there is any result > 0, if any, throw error to the user
                if (resultDocs[0] !== null && resultDocs[0].docs.length !== 0)
                    throw new Error(`${ip1} already exists`);
                if (resultDocs[1] !== null && resultDocs[1].docs.length !== 0)
                    throw new Error(`${ip2} already exists`);
                if (resultDocs[2] !== null && resultDocs[2].docs.length !== 0)
                    throw new Error(`${email} already exists`);
                if (resultDocs[3] !== null && resultDocs[3].docs.length !== 0)
                    throw new Error(`${email} is blocked`);
    
                return null;
            })
            .then(() => {
                // Build message for mailer
                const mailOptions = {
                    from: `"${name}" <${email}>`,
                    to: 'a_email@gmail.com',
                    replyTo: `"${name}" <${email}>`,
                    subject: `Contact - ${name}`,
                    html: `<div>${name}</div>
                         <div>${email}</div>
                         <div>${doc}</div>
                         <div>${state}</div>
                         <div>${city}</div>
                         <div>${phone}</div>
                         <div>${msg}</div>`,
                }
    
                let innerPromise = null;
    
                // Fix headers for cross-origin
                cors()(req, res, () => {
                    // send mail returns a promise
                    innerPromise = transport.sendMail(mailOptions);
                });
    
                return innerPromise; // return promise or null
            })
            .then(sendMailResult => {
    
                if (!sendMailResult) {
                    // not sure if transport.sendMail even returns result
                    // do validation here if yes
                }
    
                // write message to store
                return firestore
                    .collection('messages')
                    .add({
                        name: name,
                        email: email,
                        doc: doc,
                        state: state,
                        city: city,
                        phone: phone,
                        msg: msg,
                        ip1: ip1,
                        ip2: ip2,
                    });
            })
            .then(() => {
                return res.status(201).send("Success")
            })
            .catch(err => {
                console.log(err);
                res.status(500).send(String(err));
            })
    })
    

    主要的收获是承诺的结构:总是从内部返回完成的数据或另一个承诺,并使用 .then . main函数还应该返回一个承诺,这样云函数就不会在一切完成之前超时。