From 2a4a36ffec2f5680b59f07a1ab144ad10c81f226 Mon Sep 17 00:00:00 2001 From: Alan Le Ruyet Date: Wed, 6 Dec 2023 17:09:59 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=C3=A0=20tester?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/http/routes/emails.controller.ts | 249 ++++++++++---------- server/src/services/application.service.ts | 21 +- 2 files changed, 128 insertions(+), 142 deletions(-) diff --git a/server/src/http/routes/emails.controller.ts b/server/src/http/routes/emails.controller.ts index 146e1299d1..9615d01f2e 100644 --- a/server/src/http/routes/emails.controller.ts +++ b/server/src/http/routes/emails.controller.ts @@ -5,13 +5,136 @@ import config from "@/config" import { logger } from "../../common/logger" import { Etablissement } from "../../common/model" -import { addEmailToBlacklist, removeEmailFromLbaCompanies } from "../../services/application.service" +import { addEmailToBlacklist, findApplicationByMessageId, removeEmailFromLbaCompanies, updateApplicationStatusFromHardbounce } from "../../services/application.service" import * as appointmentService from "../../services/appointment.service" import { BrevoEventStatus } from "../../services/brevo.service" import dayjs from "../../services/dayjs.service" import * as eligibleTrainingsForAppointmentService from "../../services/eligibleTrainingsForAppointment.service" import { Server } from "../server" +const processWebhookEvent = async (payload) => { + const { date, event, email } = payload + const messageId = payload["message-id"] + + // application + if (event === BrevoEventStatus.HARD_BOUNCE) { + const application = await findApplicationByMessageId({ + messageId, + email, + }) + + if (application) { + await updateApplicationStatusFromHardbounce({ payload, application }) + return + } + } + + const eventDate = dayjs.utc(date).tz("Europe/Paris").toDate() + + // If mail sent from appointment model + const appointment = await appointmentService.findOne({ "to_cfa_mails.message_id": { $regex: messageId } }) + if (appointment) { + const previousEmail = appointment.to_cfa_mails.find((mail) => mail.message_id.includes(messageId)) + + if (!previousEmail) return + + await appointment.update({ + $push: { + to_etablissement_emails: { + campaign: previousEmail.campaign, + status: event, + message_id: previousEmail.message_id, + webhook_status_at: eventDate, + }, + }, + }) + + // Disable eligibleTrainingsForAppointments in case of hard_bounce + if (event === BrevoEventStatus.HARD_BOUNCE) { + const eligibleTrainingsForAppointmentsWithEmail = await eligibleTrainingsForAppointmentService.find({ cfa_recipient_email: appointment.cfa_recipient_email }) + + await Promise.all( + eligibleTrainingsForAppointmentsWithEmail.map(async (eligibleTrainingsForAppointment) => { + await eligibleTrainingsForAppointment.update({ referrers: [] }) + + logger.info('Widget parameters disabled for "hard_bounce" reason', { + eligibleTrainingsForAppointmentId: eligibleTrainingsForAppointment._id, + cfa_recipient_email: appointment.cfa_recipient_email, + }) + }) + ) + await addEmailToBlacklist(appointment.cfa_recipient_email as string, "rdv-transactional") + } + + return + } + + // If mail sent from etablissement model + const [etablissementFound] = await Etablissement.find({ "mailing.message_id": { $regex: messageId } }) + if (etablissementFound) { + const previousEmail = etablissementFound?.to_etablissement_emails?.find((mail) => mail?.message_id?.includes(messageId)) + + await etablissementFound.update({ + $push: { + to_etablissement_emails: { + campaign: previousEmail?.campaign, + status: event, + message_id: previousEmail?.message_id, + webhook_status_at: eventDate, + }, + }, + }) + return + } + + // If mail sent from appointment (to the candidat) + const [appointmentCandidatFound] = await appointmentService.find({ + "to_applicant_mails.message_id": { $regex: messageId }, + }) + if (appointmentCandidatFound) { + const previousEmail = appointmentCandidatFound?.to_applicant_mails?.find((mail) => mail.message_id.includes(messageId)) + + if (!previousEmail) return + + await appointmentCandidatFound.update({ + $push: { + to_applicant_mails: { + campaign: previousEmail.campaign, + status: event, + message_id: previousEmail.message_id, + webhook_status_at: eventDate, + }, + }, + }) + return + } + + // If mail sent from appointment (to the CFA) + const [appointmentCfaFound] = await appointmentService.find({ "to_cfa_mails.message_id": { $regex: messageId } }) + if (appointmentCfaFound && appointmentCfaFound?.to_cfa_mails) { + const previousEmail = appointmentCfaFound.to_cfa_mails.find((mail) => mail.message_id.includes(messageId)) + + if (!previousEmail) { + return + } + + await appointmentCfaFound.update({ + $push: { + to_cfa_mails: { + campaign: previousEmail.campaign, + status: event, + message_id: previousEmail.message_id, + webhook_status_at: eventDate, + }, + }, + }) + return + } + + if (event === BrevoEventStatus.HARD_BOUNCE) { + await Promise.all([addEmailToBlacklist(email, "campaign"), removeEmailFromLbaCompanies(email)]) + } +} /** * Email controllers. */ @@ -31,129 +154,7 @@ export default (server: Server) => { throw Boom.forbidden() } - const { date, event, email } = req.body - - if (event === BrevoEventStatus.HARD_BOUNCE) { - // ???? comment identifier la source du blacklisting - - await Promise.all([addEmailToBlacklist(email, "campaign"), removeEmailFromLbaCompanies(email)]) - } - - // server.post( - // "/application/webhook", - // { - // schema: zRoutes.post["/application/webhook"], - // }, - // async (req, res) => { - // const { apikey } = req.query - // if (apikey !== config.smtp.brevoWebhookApiKey) { - // throw Boom.unauthorized() - // } - - // await updateApplicationStatus({ payload: req.body }) - // return res.status(200).send({ result: "ok" }) - // } - // ) - - const messageId = req.body["message-id"] - const eventDate = dayjs.utc(date).tz("Europe/Paris").toDate() - - const appointment = await appointmentService.findOne({ "to_cfa_mails.message_id": { $regex: messageId } }) - - // If mail sent from appointment model - if (appointment) { - const previousEmail = appointment.to_cfa_mails.find((mail) => mail.message_id.includes(messageId)) - - if (!previousEmail) return - - await appointment.update({ - $push: { - to_etablissement_emails: { - campaign: previousEmail.campaign, - status: event, - message_id: previousEmail.message_id, - webhook_status_at: eventDate, - }, - }, - }) - - // Disable eligibleTrainingsForAppointments in case of hard_bounce - if (event === BrevoEventStatus.HARD_BOUNCE) { - const eligibleTrainingsForAppointmentsWithEmail = await eligibleTrainingsForAppointmentService.find({ cfa_recipient_email: appointment.cfa_recipient_email }) - - await Promise.all( - eligibleTrainingsForAppointmentsWithEmail.map(async (eligibleTrainingsForAppointment) => { - await eligibleTrainingsForAppointment.update({ referrers: [] }) - - logger.info('Widget parameters disabled for "hard_bounce" reason', { - eligibleTrainingsForAppointmentId: eligibleTrainingsForAppointment._id, - cfa_recipient_email: appointment.cfa_recipient_email, - }) - }) - ) - await addEmailToBlacklist(appointment.cfa_recipient_email as string, "rdv-transactional") - } - } - - const [etablissementFound] = await Etablissement.find({ "mailing.message_id": { $regex: messageId } }) - - // If mail sent from etablissement model - if (etablissementFound) { - const previousEmail = etablissementFound?.to_etablissement_emails?.find((mail) => mail?.message_id?.includes(messageId)) - - await etablissementFound.update({ - $push: { - to_etablissement_emails: { - campaign: previousEmail?.campaign, - status: event, - message_id: previousEmail?.message_id, - webhook_status_at: eventDate, - }, - }, - }) - } - - const [appointmentCandidatFound] = await appointmentService.find({ - "to_applicant_mails.message_id": { $regex: messageId }, - }) - - // If mail sent from appointment (to the candidat) - if (appointmentCandidatFound) { - const previousEmail = appointmentCandidatFound?.to_applicant_mails?.find((mail) => mail.message_id.includes(messageId)) - - if (!previousEmail) return - - await appointmentCandidatFound.update({ - $push: { - to_applicant_mails: { - campaign: previousEmail.campaign, - status: event, - message_id: previousEmail.message_id, - webhook_status_at: eventDate, - }, - }, - }) - } - - const [appointmentCfaFound] = await appointmentService.find({ "to_cfa_mails.message_id": { $regex: messageId } }) - - // If mail sent from appointment (to the CFA) - if (appointmentCfaFound && appointmentCfaFound?.to_cfa_mails) { - const previousEmail = appointmentCfaFound.to_cfa_mails.find((mail) => mail.message_id.includes(messageId)) - - if (!previousEmail) return - - await appointmentCfaFound.update({ - $push: { - to_cfa_mails: { - campaign: previousEmail.campaign, - status: event, - message_id: previousEmail.message_id, - webhook_status_at: eventDate, - }, - }, - }) - } + await processWebhookEvent(req.body) return res.status(200).send({}) } diff --git a/server/src/services/application.service.ts b/server/src/services/application.service.ts index a9ded2a57c..4708d77d75 100644 --- a/server/src/services/application.service.ts +++ b/server/src/services/application.service.ts @@ -15,7 +15,6 @@ import { prepareMessageForMail } from "../common/utils/fileUtils.js" import { sentryCaptureException } from "../common/utils/sentryUtils.js" import config from "../config.js" -import { BrevoEventStatus } from "./brevo.service.js" import { scan } from "./clamav.service" import { getOffreAvecInfoMandataire } from "./formulaire.service" import mailer from "./mailer.service.js" @@ -86,7 +85,7 @@ export const addEmailToBlacklist = async (email: string, blacklistingOrigin: str * @param {string} email * @returns {Promise} */ -const findApplicationByMessageId = async ({ messageId, email }: { messageId: string; email: string }) => +export const findApplicationByMessageId = async ({ messageId, email }: { messageId: string; email: string }) => Application.findOne({ company_email: email, to_company_message_id: messageId }) /** @@ -552,7 +551,7 @@ export const sendNotificationToApplicant = async ({ /** * @description updates application and triggers action from email webhook */ -export const updateApplicationStatus = async ({ payload }: { payload: any }): Promise => { +export const updateApplicationStatusFromHardbounce = async ({ payload, application }: { payload: any; application: IApplication }): Promise => { /* Format payload cf. https://developers.brevo.com/docs/how-to-use-webhooks { "event": "delivered", @@ -572,27 +571,13 @@ export const updateApplicationStatus = async ({ payload }: { payload: any }): Pr } */ - const { event, subject, email } = payload - - if (event !== BrevoEventStatus.HARD_BOUNCE) { - return - } + const { subject, email } = payload if (!subject.startsWith("Candidature en alternance") && !subject.startsWith("Candidature spontanée")) { // les messages qui ne sont pas de candidature vers une entreprise sont ignorés return } - const application = await findApplicationByMessageId({ - messageId: payload["message-id"], - email, - }) - - if (!application) { - logger.error(`Application webhook : application not found. message_id=${payload["message-id"]} email=${email} subject=${subject}`) - return - } - await addEmailToBlacklist(email, application.job_origin ?? "unknown") if (application.job_origin === "lba") {