Объединение результатов запроса Firebase Firestore в функции Firestore перед отправкой электронной почты - PullRequest
0 голосов
/ 20 сентября 2018

У меня есть несколько проблем, оборачивающих мои обещания.На этот раз я пытаюсь объединить 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 Ответ

0 голосов
/ 21 сентября 2018

Хорошо, вот что я смог придумать.Я запустил это в отладчике, чтобы пройти и убедиться, что все работает.

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.Основная функция также должна возвращать обещание, чтобы облачная функция не превышала время ожидания, пока все не завершится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...