From 7a25797ae02483ff04a62aed5e14e47bbe403ac8 Mon Sep 17 00:00:00 2001
From: Simon Hamery
Date: Mon, 28 Oct 2024 15:24:20 +0100
Subject: [PATCH] Fixbug adresse destination invalide (envoi de SMS) (#4648)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* refactor: améliore la gestion des erreurs des followups
* refactor: log pas utile
* refactor: inverse l'ordre d'envoi email / sms (followup)
* fix: remonte erreur addresse de destination invalide du back + interprete sur le front
* fix: message d'erreur dans le followup sauvegardé correctement
* refactor: formulation message erreur front - adresse invalide
---
backend/controllers/followups.ts | 58 +++++++++----------
backend/lib/messaging/email/email-service.ts | 3 +-
backend/lib/messaging/sms/sms-service.ts | 29 ++++++++--
lib/enums/error.ts | 20 +++++++
.../modals/errors-email-and-sms-modal.vue | 10 ++++
src/components/recap-email-and-sms-form.vue | 10 ++--
6 files changed, 87 insertions(+), 43 deletions(-)
create mode 100644 lib/enums/error.ts
diff --git a/backend/controllers/followups.ts b/backend/controllers/followups.ts
index 1fc869ce0c..b203fd4e75 100644
--- a/backend/controllers/followups.ts
+++ b/backend/controllers/followups.ts
@@ -10,10 +10,10 @@ import { SurveyType } from "../../lib/enums/survey.js"
import { FollowupFactory } from "../lib/followup-factory.js"
import { FetchSurvey } from "../../lib/types/survey.d.js"
import Request from "../types/express.d.js"
-import { phoneNumberValidation } from "../../lib/phone-number.js"
import config from "../config/index.js"
import { sendSimulationResultsEmail } from "../lib/messaging/email/email-service.js"
import { sendSimulationResultsSms } from "../lib/messaging/sms/sms-service.js"
+import { ErrorType, ErrorStatus, ErrorName } from "../../lib/enums/error.js"
export function followup(
req: Request,
@@ -39,23 +39,6 @@ export function followup(
})
}
-async function sendFollowupNotifications(followup: Followup, res: Response) {
- const { email, phone } = followup
- if (phone) {
- if (
- phoneNumberValidation(phone, config.smsService.internationalDiallingCodes)
- ) {
- await sendSimulationResultsSms(followup)
- } else {
- return res.status(422).send("Unsupported phone number format")
- }
- }
- if (email) {
- await sendSimulationResultsEmail(followup)
- }
- return res.send({ result: "OK" })
-}
-
async function createSimulationRecapUrl(req: Request, res: Response) {
const followup = await FollowupFactory.create(req.simulation)
await followup.addSurveyIfMissing(
@@ -69,6 +52,7 @@ async function createSimulationRecapUrl(req: Request, res: Response) {
export async function persist(req: Request, res: Response) {
const { surveyOptin, email, phone } = req.body
const simulation = req.simulation
+
try {
if (email || phone) {
const followup = await FollowupFactory.createWithResults(
@@ -77,17 +61,27 @@ export async function persist(req: Request, res: Response) {
email,
phone
)
- return sendFollowupNotifications(followup, res)
- } else {
- return createSimulationRecapUrl(req, res)
+ if (email) await sendSimulationResultsEmail(followup)
+ if (phone) await sendSimulationResultsSms(followup)
+ return res.send({ result: "OK" })
}
+
+ return createSimulationRecapUrl(req, res)
} catch (error: any) {
Sentry.captureException(error)
- if (error.name === "ValidationError") {
- return res.status(403).send(error.message)
- } else {
- return res.status(500).send(`Error while persisting followup`)
+
+ let status: number = ErrorStatus.InternalServerError
+
+ if (
+ error.name === ErrorName.ValidationError ||
+ error.message === ErrorType.UnsupportedPhoneNumberFormat
+ ) {
+ status = ErrorStatus.UnprocessableEntity
}
+
+ return res
+ .status(status)
+ .send(error.message || ErrorType.PersistingFollowup)
}
}
@@ -117,7 +111,7 @@ export function showFollowup(req: Request, res: Response) {
})
.catch((error: Error) => {
console.error("error", error)
- return res.sendStatus(400)
+ return res.sendStatus(ErrorStatus.BadRequest)
})
}
@@ -142,12 +136,13 @@ export function showSurveyResults(req: Request, res: Response) {
export function showSurveyResultByEmail(req: Request, res: Response) {
Followups.findByEmail(req.params.email)
.then((followups: Followup[]) => {
- if (!followups || !followups.length) return res.sendStatus(404)
+ if (!followups || !followups.length)
+ return res.sendStatus(ErrorStatus.NotFound)
res.send(followups)
})
.catch((error: Error) => {
console.error("error", error)
- return res.sendStatus(400)
+ return res.sendStatus(ErrorStatus.BadRequest)
})
}
@@ -160,7 +155,7 @@ export async function followupByAccessToken(
const followup: Followup | null = await Followups.findOne({
accessToken,
})
- if (!followup) return res.sendStatus(404)
+ if (!followup) return res.sendStatus(ErrorStatus.NotFound)
req.followup = followup
next()
}
@@ -216,7 +211,7 @@ async function getRedirectUrl(req: Request) {
case SurveyType.TousABordNotification:
return "https://www.tadao.fr/713-Demandeur-d-emploi.html"
default:
- throw new Error(`Unknown survey type: ${surveyType}`)
+ throw new Error(`${ErrorType.UnknownSurveyType} : ${surveyType}`)
}
}
@@ -227,7 +222,6 @@ export async function logSurveyLinkClick(req: Request, res: Response) {
res.redirect(redirectUrl)
} catch (error) {
Sentry.captureException(error)
- console.error("error", error)
- return res.sendStatus(404)
+ return res.sendStatus(ErrorStatus.NotFound)
}
}
diff --git a/backend/lib/messaging/email/email-service.ts b/backend/lib/messaging/email/email-service.ts
index cc8232c25c..d28767bd5f 100644
--- a/backend/lib/messaging/email/email-service.ts
+++ b/backend/lib/messaging/email/email-service.ts
@@ -7,13 +7,14 @@ import { EmailType } from "../../../../lib/enums/messaging.js"
import { SurveyType } from "../../../../lib/enums/survey.js"
import { Survey } from "../../../../lib/types/survey.js"
import { Followup } from "../../../../lib/types/followup.js"
+import { ErrorType } from "../../../../lib/enums/error.js"
import dayjs from "dayjs"
export async function sendSimulationResultsEmail(
followup: Followup
): Promise {
if (!followup.email) {
- throw new Error("Missing followup email")
+ throw new Error(ErrorType.MissingFollowupEmail)
}
const render: any = await emailRender(EmailType.SimulationResults, followup)
const sendEmailSmtpResponse = await sendEmailSmtp({
diff --git a/backend/lib/messaging/sms/sms-service.ts b/backend/lib/messaging/sms/sms-service.ts
index b9e10becf1..20f447755f 100644
--- a/backend/lib/messaging/sms/sms-service.ts
+++ b/backend/lib/messaging/sms/sms-service.ts
@@ -1,10 +1,14 @@
import axios from "axios"
import config from "../../../config/index.js"
-import { phoneNumberFormatting } from "./../../../../lib/phone-number.js"
+import {
+ phoneNumberFormatting,
+ phoneNumberValidation,
+} from "./../../../../lib/phone-number.js"
import { SmsType } from "../../../../lib/enums/messaging.js"
import { Followup } from "../../../../lib/types/followup.js"
import { Survey } from "../../../../lib/types/survey.d.js"
import { SurveyType } from "../../../../lib/enums/survey.js"
+import { ErrorType } from "../../../../lib/enums/error.js"
import dayjs from "dayjs"
import Sentry from "@sentry/node"
@@ -69,7 +73,16 @@ export async function sendSimulationResultsSms(
): Promise {
try {
if (!followup.phone) {
- throw new Error("Missing followup phone")
+ throw new Error(ErrorType.MissingFollowupPhone)
+ }
+
+ if (
+ !phoneNumberValidation(
+ followup.phone,
+ config.smsService.internationalDiallingCodes
+ )
+ ) {
+ throw new Error(ErrorType.UnsupportedPhoneNumberFormat)
}
const { username, password } = await getSMSConfig()
@@ -90,10 +103,14 @@ export async function sendSimulationResultsSms(
followup.smsSentAt = dayjs().toDate()
followup.smsMessageId = data.messageIds[0]
return await followup.save()
- } catch (err) {
- Sentry.captureException(err)
- followup.smsError = JSON.stringify(err, null, 2)
- throw err
+ } catch (error: any) {
+ // Avoid sending invalid destination address error to sentry
+ if (!error?.message?.includes("Invalid destination address")) {
+ Sentry.captureException(error)
+ }
+ followup.smsError = error?.message
+ await followup.save()
+ throw error
}
}
diff --git a/lib/enums/error.ts b/lib/enums/error.ts
new file mode 100644
index 0000000000..89ab511b6c
--- /dev/null
+++ b/lib/enums/error.ts
@@ -0,0 +1,20 @@
+export enum ErrorType {
+ UnsupportedPhoneNumberFormat = "Unsupported phone number format",
+ PersistingFollowup = "Persisting followup error",
+ MissingFollowupPhone = "Missing followup phone",
+ MissingFollowupEmail = "Missing followup email",
+ UnknownSurveyType = "Unknown survey type",
+}
+
+export enum ErrorStatus {
+ BadRequest = 400,
+ Unauthorized = 401,
+ Forbidden = 403,
+ UnprocessableEntity = 422,
+ NotFound = 404,
+ InternalServerError = 500,
+}
+
+export enum ErrorName {
+ ValidationError = "ValidationError",
+}
diff --git a/src/components/modals/errors-email-and-sms-modal.vue b/src/components/modals/errors-email-and-sms-modal.vue
index d46b10e460..c5217afefb 100644
--- a/src/components/modals/errors-email-and-sms-modal.vue
+++ b/src/components/modals/errors-email-and-sms-modal.vue
@@ -25,6 +25,16 @@ const recapEmailState = computed(() => store.recapEmailState)
}}
+
+
+ Une erreur s'est produite dans l'envoi du récapitulatif par SMS :
+ l'adresse de destination est invalide. Veuillez réessayer avec un
+ numéro valide ou utiliser l'envoi par email seulement.
+
+
{
ABTestingService.getValues().CTA_EmailRecontact
)
}
- } catch (error) {
- console.error(error)
- Sentry.captureException(error)
+ } catch (error: any) {
+ if (!error?.response?.data?.includes("Invalid destination address")) {
+ Sentry.captureException(error)
+ } else {
+ store.setFormRecapPhoneState("invalid-address")
+ }
}
}
@@ -181,7 +184,6 @@ const sendRecapByEmail = async (surveyOptin) => {
store.setModalState(undefined)
await postFollowup(surveyOptin, emailValue.value)
} catch (error) {
- Sentry.captureException(error)
store.setFormRecapEmailState("error")
throw error
}