Skip to content

Commit

Permalink
Fixbug adresse destination invalide (envoi de SMS) (#4648)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Shamzic authored Oct 28, 2024
1 parent 45c8974 commit 7a25797
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 43 deletions.
58 changes: 26 additions & 32 deletions backend/controllers/followups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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)
})
}

Expand All @@ -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)
})
}

Expand All @@ -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()
}
Expand Down Expand Up @@ -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}`)
}
}

Expand All @@ -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)
}
}
3 changes: 2 additions & 1 deletion backend/lib/messaging/email/email-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Followup> {
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({
Expand Down
29 changes: 23 additions & 6 deletions backend/lib/messaging/sms/sms-service.ts
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -69,7 +73,16 @@ export async function sendSimulationResultsSms(
): Promise<Followup> {
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()
Expand All @@ -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
}
}

Expand Down
20 changes: 20 additions & 0 deletions lib/enums/error.ts
Original file line number Diff line number Diff line change
@@ -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",
}
10 changes: 10 additions & 0 deletions src/components/modals/errors-email-and-sms-modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ const recapEmailState = computed(() => store.recapEmailState)
}}
</p>
</div>
<div
v-if="recapPhoneState === 'invalid-address'"
class="fr-alert fr-alert--error"
>
<p>
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.
</p>
</div>
<div
v-if="recapPhoneState === 'ok' && recapEmailState === 'ok'"
class="fr-alert fr-alert--success"
Expand Down
10 changes: 6 additions & 4 deletions src/components/recap-email-and-sms-form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,12 @@ const sendRecap = async (surveyOptin) => {
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")
}
}
}
Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit 7a25797

Please sign in to comment.