From 55f7e3496f2b124c3eeddfa66744530011943ce7 Mon Sep 17 00:00:00 2001 From: Emile Bex Date: Fri, 27 Oct 2023 16:07:17 +0200 Subject: [PATCH] [EN-6471] chore(mailjet): replace plezi with mailjet (#107) * [EN-6471] feat(newsletter): remove plezi to use mailjet for newsletter subscription * [EN-6471] chore(newsletter): connect newsletter route to mailjet --- README.md | 28 ++- package.json | 4 +- src/app.module.ts | 2 - src/contacts/contacts.controller.ts | 20 +- src/contacts/contacts.module.ts | 4 +- src/contacts/contacts.service.ts | 29 +-- .../mailjet/mailjet.service.ts | 218 ++++++++++-------- .../mailjet/mailjet.types.ts | 58 +++++ .../mailjet/mailjet.utils.ts | 89 +++++++ src/external-services/plezi/plezi.module.ts | 8 - src/external-services/plezi/plezi.service.ts | 72 ------ src/external-services/plezi/plezi.types.ts | 41 ---- .../dto/create-opportunity.dto.ts | 13 -- src/opportunities/opportunities.controller.ts | 14 +- src/opportunities/opportunities.module.ts | 4 +- src/opportunities/opportunities.service.ts | 25 +- tests/contacts/contacts.e2e-spec.ts | 17 +- tests/mocks.types.ts | 11 +- tests/opportunities/opportunities.e2e-spec.ts | 4 - yarn.lock | 182 ++++++++------- 20 files changed, 428 insertions(+), 415 deletions(-) create mode 100644 src/external-services/mailjet/mailjet.utils.ts delete mode 100644 src/external-services/plezi/plezi.module.ts delete mode 100644 src/external-services/plezi/plezi.service.ts delete mode 100644 src/external-services/plezi/plezi.types.ts diff --git a/README.md b/README.md index b67382e4..038c259b 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ yarn docker-compose up --build ``` -Dans le cas où vous travaillez sur mac, le module Sharp peut poser problème, vous devez donc le réinstaller au sein du container: +Dans le cas où vous travaillez sur mac, le module Sharp peut poser problème, vous devez donc le réinstaller au sein du +container: ``` docker exec -it api_new bash @@ -87,7 +88,8 @@ Pour lancer les migrations : yarn db:migrate ``` -APour remplir la base de données avec un utilisateur administrateur permettant la création par la suite d'autres utilisateurs : +APour remplir la base de données avec un utilisateur administrateur permettant la création par la suite d'autres +utilisateurs : ``` yarn db:seed @@ -95,7 +97,8 @@ yarn db:seed #### Avec Docker -La même chose que sans Docker, mais vous devez précéder les commandes par la suivante, et ne pas lancer la commande de création de DB: +La même chose que sans Docker, mais vous devez précéder les commandes par la suivante, et ne pas lancer la commande de +création de DB: ``` docker exec -it api bash @@ -152,9 +155,11 @@ yarn start #### Mode développement -Pour pouvoir utiliser le worker en local il faut lancer une instance de **_Redis_** en local : https://redis.io/docs/getting-started +Pour pouvoir utiliser le worker en local il faut lancer une instance de **_Redis_** en +local : https://redis.io/docs/getting-started -Il faut également enlever les variables d'environnement _REDIS_URL_ et _REDIS_TLS_URL_ afin que les modules **_Redis_** et **_Bull_** utilisent leur configuration par défaut pour se connecter à _**Redis**_ en local (`127.0.0.1:6379`) +Il faut également enlever les variables d'environnement _REDIS_URL_ et _REDIS_TLS_URL_ afin que les modules **_Redis_** +et **_Bull_** utilisent leur configuration par défaut pour se connecter à _**Redis**_ en local (`127.0.0.1:6379`) ``` yarn worker:start:dev @@ -222,10 +227,6 @@ MAILJET_SMS_TOKEN= MAILJET_SUPPORT_EMAIL= OFFER_NO_RESPONSE_DELAY= OFFER_REMINDER_DELAY= -PLEZI_API_KEY= -PLEZI_CONTENT_WEB_FORM_ID= -PLEZI_FORM_ID= -PLEZI_TENANT_NAME= PORT= PUSHER_API_KEY= PUSHER_API_SECRET= @@ -263,7 +264,8 @@ VONAGE_API_SECRET= docker run --name linkedout-db-test -e POSTGRES_PASSWORD=linkedout -e POSTGRES_USER=linkedout -e POSTGRES_DB=linkedout -d -p 54300:5432 postgres ``` -Vous avez besoin des données du fichier `.env.test` pour les tests en local, et de renseigner le champ _DATABASE_URL_ (_ex:_ `postgresql://linkedout:linkedout@localhost:54300/linkedout`) avec l'adresse de l'instance **_Docker_** +Vous avez besoin des données du fichier `.env.test` pour les tests en local, et de renseigner le champ _DATABASE_URL_ ( +_ex:_ `postgresql://linkedout:linkedout@localhost:54300/linkedout`) avec l'adresse de l'instance **_Docker_** ``` NODE_ENV=dev-test yarn db:migrate @@ -278,9 +280,11 @@ NODE_ENV=dev-test yarn db:migrate Le déploiement se fait automatiquement grâce à **_Github Actions_** et **_Heroku_**. -Si un commit est poussé sur `develop`, l'application sera déployé sur la pre-production : **[https://entourage-job-preprod.herokuapp.com](https://entourage-job-preprod.herokuapp.com)** +Si un commit est poussé sur `develop`, l'application sera déployé sur la pre-production : * +*[https://entourage-job-preprod.herokuapp.com](https://entourage-job-preprod.herokuapp.com)** -Si un commit est poussé sur `master`, l'application sera déployé sur la production : **[https://api.linkedout.fr](https://api.linkedout.fr)** +Si un commit est poussé sur `master`, l'application sera déployé sur la production : * +*[https://api.linkedout.fr](https://api.linkedout.fr)** Les tests sont effectués sur **_Github Actions_** avant de déployer le projet sur **_Heroku_**. diff --git a/package.json b/package.json index 867b43f8..67a31772 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "moment": "^2.29.4", "moment-timezone": "^0.5.43", "node-fetch": "^2.6.7", - "node-mailjet": "^3.3.7", + "node-mailjet": "^3.3.9", "passport": "^0.6.0", "passport-jwt": "^4.0.0", "passport-local": "^1.0.0", @@ -103,7 +103,7 @@ "@types/multer": "^1.4.7", "@types/node": "^16.0.0", "@types/node-fetch": "2.x", - "@types/node-mailjet": "^3.3.8", + "@types/node-mailjet": "^3.3.9", "@types/passport-jwt": "^3.0.6", "@types/passport-local": "^1.0.34", "@types/puppeteer": "^5.4.6", diff --git a/src/app.module.ts b/src/app.module.ts index e9a65520..2f17e8b3 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -29,7 +29,6 @@ import { ExternalDatabasesModule } from './external-databases/external-databases import { ExternalMessagesModule } from './external-messages/external-messages.module'; import { BitlyModule } from './external-services/bitly/bitly.module'; import { MailjetModule } from './external-services/mailjet/mailjet.module'; -import { PleziModule } from './external-services/plezi/plezi.module'; import { SalesforceModule } from './external-services/salesforce/salesforce.module'; import { MailsModule } from './mails/mails.module'; import { OpportunitiesModule } from './opportunities/opportunities.module'; @@ -120,7 +119,6 @@ export function getSequelizeOptions(): SequelizeModuleOptions { SMSModule, BitlyModule, ContactsModule, - PleziModule, OrganizationsModule, ExternalMessagesModule, ], diff --git a/src/contacts/contacts.controller.ts b/src/contacts/contacts.controller.ts index 77a5dc14..7dc73edd 100644 --- a/src/contacts/contacts.controller.ts +++ b/src/contacts/contacts.controller.ts @@ -13,10 +13,7 @@ import { ContactUsFormDto, ContactUsFormPipe, } from 'src/contacts/dto'; -import { - ContactStatus, - PleziTrackingData, -} from 'src/external-services/plezi/plezi.types'; +import { ContactStatus } from 'src/external-services/mailjet/mailjet.types'; import { isValidPhone } from 'src/utils/misc'; import { AdminZone } from 'src/utils/types'; import { ContactsService } from './contacts.service'; @@ -83,25 +80,18 @@ export class ContactsController { @Post('newsletter') async addContactForNewsletter( @Body('email') email: string, - @Body('zone') zone: AdminZone | AdminZone[], - @Body('status') status: ContactStatus | ContactStatus[], - @Body('visit') visit?: PleziTrackingData['visit'], - @Body('visitor') visitor?: PleziTrackingData['visitor'], - @Body('urlParams') - urlParams?: PleziTrackingData['urlParams'] + @Body('zone') zone?: AdminZone, + @Body('status') status?: ContactStatus ) { if (!email) { throw new BadRequestException(); } - return this.contactsService.sendContactToPlezi( + await this.contactsService.sendContactToMailjet({ email, zone, status, - visit, - visitor, - urlParams - ); + }); } @Public() diff --git a/src/contacts/contacts.module.ts b/src/contacts/contacts.module.ts index 0d1d25f1..c0033033 100644 --- a/src/contacts/contacts.module.ts +++ b/src/contacts/contacts.module.ts @@ -1,12 +1,12 @@ import { Module } from '@nestjs/common'; -import { PleziModule } from 'src/external-services/plezi/plezi.module'; +import { MailjetModule } from 'src/external-services/mailjet/mailjet.module'; import { SalesforceModule } from 'src/external-services/salesforce/salesforce.module'; import { MailsModule } from 'src/mails/mails.module'; import { ContactsController } from './contacts.controller'; import { ContactsService } from './contacts.service'; @Module({ - imports: [MailsModule, SalesforceModule, PleziModule], + imports: [MailsModule, SalesforceModule, MailjetModule], controllers: [ContactsController], providers: [ContactsService], exports: [ContactsService], diff --git a/src/contacts/contacts.service.ts b/src/contacts/contacts.service.ts index 5d76e34c..09e59aa1 100644 --- a/src/contacts/contacts.service.ts +++ b/src/contacts/contacts.service.ts @@ -1,14 +1,9 @@ import { Injectable } from '@nestjs/common'; - +import { MailjetService } from '../external-services/mailjet/mailjet.service'; +import { CustomContactParams } from '../external-services/mailjet/mailjet.types'; import { ContactCompanyFormDto, ContactUsFormDto } from 'src/contacts/dto'; -import { PleziService } from 'src/external-services/plezi/plezi.service'; -import { - ContactStatus, - PleziTrackingData, -} from 'src/external-services/plezi/plezi.types'; import { SalesforceService } from 'src/external-services/salesforce/salesforce.service'; import { MailsService } from 'src/mails/mails.service'; -import { AdminZone } from 'src/utils/types'; import { ContactCandidateFormDto } from './dto/contact-candidate-form.dto'; import { InscriptionCandidateFormDto } from './dto/inscription-candidate-form.dto'; @@ -17,25 +12,11 @@ export class ContactsService { constructor( private mailsService: MailsService, private salesforceService: SalesforceService, - private pleziService: PleziService + private mailjetService: MailjetService ) {} - async sendContactToPlezi( - email: string, - zone: AdminZone | AdminZone[], - status: ContactStatus | ContactStatus[], - visit?: PleziTrackingData['visit'], - visitor?: PleziTrackingData['visitor'], - urlParams?: PleziTrackingData['urlParams'] - ) { - return this.pleziService.sendContactToPlezi( - email, - zone, - status, - visit, - visitor, - urlParams - ); + async sendContactToMailjet(contact: CustomContactParams) { + return this.mailjetService.sendContact(contact); } async sendCompanyContactToSalesforce( diff --git a/src/external-services/mailjet/mailjet.service.ts b/src/external-services/mailjet/mailjet.service.ts index c4c3ec36..f24b0927 100644 --- a/src/external-services/mailjet/mailjet.service.ts +++ b/src/external-services/mailjet/mailjet.service.ts @@ -1,117 +1,149 @@ import { Injectable } from '@nestjs/common'; -import _ from 'lodash'; -import { Email, connect } from 'node-mailjet'; -import { CustomMailParams, MailjetTemplates } from './mailjet.types'; +import { connect, Email } from 'node-mailjet'; +import { + CustomContactParams, + CustomMailParams, + MailjetContactList, + MailjetContactTag, + MailjetContactTagNames, + MailjetCustomContact, + MailjetCustomResponse, + MailjetListActions, +} from './mailjet.types'; +import { createMail } from './mailjet.utils'; import SendParams = Email.SendParams; - -const useCampaigns = process.env.MAILJET_CAMPAIGNS_ACTIVATED === 'true'; +import SendParamsRecipient = Email.SendParamsRecipient; @Injectable() export class MailjetService { - private send: Email.PostResource; + private mailjetTransactional: Email.Client; + private mailjetNewsletter: Email.Client; constructor() { - const mailjet = connect( + this.mailjetTransactional = connect( `${process.env.MAILJET_PUB}`, `${process.env.MAILJET_SEC}` ); - this.send = mailjet.post('send', { version: 'v3.1' }); + this.mailjetNewsletter = connect( + `${process.env.MAILJET_NEWSLETTER_PUB}`, + `${process.env.MAILJET_NEWSLETTER_SEC}` + ); } - createMail({ - toEmail, - replyTo, - subject, - text, - html, - variables, - templateId, - }: CustomMailParams) { - const recipients: SendParams['Messages'][number] = { - To: [], - Cc: [], - From: { Email: '' }, - }; - if (typeof toEmail === 'string') { - recipients.To = [{ Email: toEmail }]; - } else if (Array.isArray(toEmail)) { - recipients.To = toEmail.map((email) => { - return { Email: email }; + async sendMail(params: CustomMailParams | CustomMailParams[]) { + const mailjetParams: SendParams = { Messages: [] }; + if (Array.isArray(params)) { + mailjetParams.Messages = params.map((p) => { + return createMail(p); }); - } else if (typeof toEmail === 'object') { - const { to, cc, bcc } = toEmail; - if (cc) { - recipients.Cc = Array.isArray(cc) - ? cc.map((email) => { - return { Email: email }; - }) - : [{ Email: cc }]; - } - if (to) { - recipients.To = Array.isArray(to) - ? to.map((email) => { - return { Email: email }; - }) - : [{ Email: to }]; - } - if (bcc) { - recipients.Bcc = Array.isArray(bcc) - ? bcc.map((email) => { - return { Email: email }; - }) - : [{ Email: bcc }]; - } + } else { + mailjetParams.Messages = [createMail(params)]; } - const content = templateId - ? { - Variables: { - siteLink: process.env.FRONT_URL, - ...variables, + return this.mailjetTransactional + .post('send', { + version: 'v3.1', + }) + .request(mailjetParams); + } + + async findContact( + email: CustomContactParams['email'] + ): Promise { + const contact: SendParamsRecipient = { + Email: email.toLowerCase(), + }; + + const { + body: { Data: data }, + } = (await this.mailjetNewsletter + .get('contact', { version: 'v3' }) + .request(contact)) as MailjetCustomResponse; + + return data[0]; + } + + async createContact( + email: CustomContactParams['email'] + ): Promise { + const contact: SendParamsRecipient = { + Email: email, + }; + const { + body: { Data: data }, + } = (await this.mailjetNewsletter + .post('contact', { version: 'v3' }) + .request(contact)) as MailjetCustomResponse; + + return data[0]; + } + + async updateContactTags( + id: string, + { zone, status }: Pick + ) { + let dataToUpdate: { Data: MailjetContactTag[] } = { + Data: [ + { + Name: MailjetContactTagNames.NEWSLETTER, + Value: true, + }, + ], + }; + + if (zone) { + dataToUpdate = { + Data: [ + ...dataToUpdate.Data, + { + Name: MailjetContactTagNames.ZONE, + Value: zone, }, - TemplateID: templateId, - CustomCampaign: useCampaigns - ? _.findKey(MailjetTemplates, (id) => { - return id === templateId; - }) - : undefined, - TemplateLanguage: true, - TemplateErrorReporting: { - Email: `${process.env.MAILJET_SUPPORT_EMAIL}`, - Name: `${process.env.MAILJET_FROM_NAME}`, + ], + }; + } + if (status) { + dataToUpdate = { + Data: [ + ...dataToUpdate.Data, + { + Name: MailjetContactTagNames.STATUS, + Value: status, }, - } - : { - 'Text-part': text, - 'HTML-part': html, - }; - return { - From: { - Email: `${process.env.MAILJET_FROM_EMAIL}`, - Name: `${process.env.MAILJET_FROM_NAME}`, - }, - Subject: subject, - Headers: replyTo - ? { - 'Reply-To': replyTo, - } - : undefined, - ...recipients, - ...content, + ], + }; + } + + return this.mailjetNewsletter + .put('contactdata', { version: 'v3' }) + .id(id) + .request(dataToUpdate); + } + + async subscribeToNewsletterList(id: string) { + const dataToUpdate: { ContactsLists: MailjetContactList[] } = { + ContactsLists: [ + { + Action: MailjetListActions.FORCE, + ListID: process.env.MAILJET_NEWSLETTER_LIST_ID, + }, + ], }; + + return this.mailjetNewsletter + .post('contact', { version: 'v3' }) + .id(id) + .action('managecontactslists') + .request(dataToUpdate); } - async sendMail(params: CustomMailParams | CustomMailParams[]) { - const mailjetParams: SendParams = { Messages: [] }; - if (Array.isArray(params)) { - mailjetParams.Messages = params.map((p) => { - return this.createMail(p); - }); - } else { - mailjetParams.Messages = [this.createMail(params)]; + async sendContact({ email, zone, status }: CustomContactParams) { + let contact = await this.findContact(email); + if (!contact) { + contact = await this.createContact(email); } - - return this.send.request(mailjetParams); + await this.subscribeToNewsletterList(contact.ID); + await this.updateContactTags(contact.ID, { zone, status }); } } diff --git a/src/external-services/mailjet/mailjet.types.ts b/src/external-services/mailjet/mailjet.types.ts index 1a57fc1c..5ca8050e 100644 --- a/src/external-services/mailjet/mailjet.types.ts +++ b/src/external-services/mailjet/mailjet.types.ts @@ -1,3 +1,5 @@ +import { AdminZone } from 'src/utils/types'; + export interface CustomMailParams { toEmail: | string @@ -15,6 +17,39 @@ export interface CustomMailParams { templateId: MailjetTemplate; } +export interface CustomContactParams { + email: string; + zone: AdminZone; + status: ContactStatus; +} + +export const MailjetContactTagNames = { + NEWSLETTER: 'newsletter_linkedout', + ZONE: 'antenne_linkedout', + STATUS: 'profil_linkedout', +} as const; + +export type MailjetContactTagName = + typeof MailjetContactTagNames[keyof typeof MailjetContactTagNames]; + +export interface MailjetContactTag { + Name: MailjetContactTagName; + Value: string | boolean; +} + +export const MailjetListActions = { + NO_FORCE: 'addnoforce', + FORCE: 'addforce', +} as const; + +export type MailjetListAction = + typeof MailjetListActions[keyof typeof MailjetListActions]; + +export interface MailjetContactList { + Action: MailjetListAction; + ListID: string; +} + export const MailjetTemplates = { ACCOUNT_CREATED: 3920498, CV_PREPARE: 3782475, @@ -52,3 +87,26 @@ export const MailjetTemplates = { export type MailjetTemplateKey = keyof typeof MailjetTemplates; export type MailjetTemplate = typeof MailjetTemplates[MailjetTemplateKey]; + +export const ContactStatuses = { + INDIVIDUAL: 'PARTICULIER', + COMPANY: 'ENTREPRISE', + ASSOCIATION: 'ASSOCIATION', + CANDIDATE: 'CANDIDAT', +} as const; + +export type ContactStatus = + typeof ContactStatuses[keyof typeof ContactStatuses]; + +export interface MailjetError { + statusCode: number; + ErrorMessage: string; +} + +export interface MailjetCustomContact { + ID: string; +} + +export interface MailjetCustomResponse { + body: { Data: ReadonlyArray }; +} diff --git a/src/external-services/mailjet/mailjet.utils.ts b/src/external-services/mailjet/mailjet.utils.ts new file mode 100644 index 00000000..c6ca6794 --- /dev/null +++ b/src/external-services/mailjet/mailjet.utils.ts @@ -0,0 +1,89 @@ +import _ from 'lodash'; +import { Email } from 'node-mailjet'; +import { CustomMailParams, MailjetTemplates } from './mailjet.types'; +import SendParams = Email.SendParams; + +const useCampaigns = process.env.MAILJET_CAMPAIGNS_ACTIVATED === 'true'; + +export function createMail({ + toEmail, + replyTo, + subject, + text, + html, + variables, + templateId, +}: CustomMailParams) { + const recipients: SendParams['Messages'][number] = { + To: [], + Cc: [], + From: { Email: '' }, + }; + if (typeof toEmail === 'string') { + recipients.To = [{ Email: toEmail }]; + } else if (Array.isArray(toEmail)) { + recipients.To = toEmail.map((email) => { + return { Email: email }; + }); + } else if (typeof toEmail === 'object') { + const { to, cc, bcc } = toEmail; + if (cc) { + recipients.Cc = Array.isArray(cc) + ? cc.map((email) => { + return { Email: email }; + }) + : [{ Email: cc }]; + } + if (to) { + recipients.To = Array.isArray(to) + ? to.map((email) => { + return { Email: email }; + }) + : [{ Email: to }]; + } + if (bcc) { + recipients.Bcc = Array.isArray(bcc) + ? bcc.map((email) => { + return { Email: email }; + }) + : [{ Email: bcc }]; + } + } + + const content = templateId + ? { + Variables: { + siteLink: process.env.FRONT_URL, + ...variables, + }, + TemplateID: templateId, + CustomCampaign: useCampaigns + ? _.findKey(MailjetTemplates, (id) => { + return id === templateId; + }) + : undefined, + TemplateLanguage: true, + TemplateErrorReporting: { + Email: `${process.env.MAILJET_SUPPORT_EMAIL}`, + Name: `${process.env.MAILJET_FROM_NAME}`, + }, + } + : { + 'Text-part': text, + 'HTML-part': html, + }; + return { + From: { + Email: `${process.env.MAILJET_FROM_EMAIL}`, + Name: `${process.env.MAILJET_FROM_NAME}`, + }, + Subject: subject, + Headers: replyTo + ? { + 'Reply-To': replyTo, + } + : undefined, + ...recipients, + ...content, + }; +} diff --git a/src/external-services/plezi/plezi.module.ts b/src/external-services/plezi/plezi.module.ts deleted file mode 100644 index ef4d615d..00000000 --- a/src/external-services/plezi/plezi.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PleziService } from './plezi.service'; - -@Module({ - providers: [PleziService], - exports: [PleziService], -}) -export class PleziModule {} diff --git a/src/external-services/plezi/plezi.service.ts b/src/external-services/plezi/plezi.service.ts deleted file mode 100644 index 049a721d..00000000 --- a/src/external-services/plezi/plezi.service.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import fetch from 'node-fetch'; -import qs from 'qs'; -import { - ContactStatus, - PleziContactRegions, - PleziContactStatuses, - PleziNewsletterId, - PleziTrackingData, -} from 'src/external-services/plezi/plezi.types'; -import { AdminZone } from 'src/utils/types'; - -@Injectable() -export class PleziService { - async sendContactToPlezi( - email: string, - zone: AdminZone | AdminZone[], - status: ContactStatus | ContactStatus[], - visit?: PleziTrackingData['visit'], - visitor?: PleziTrackingData['visitor'], - urlParams?: PleziTrackingData['urlParams'] - ) { - const queryParams = `${qs.stringify( - { - visit, - visitor, - form_id: process.env.PLEZI_FORM_ID, - content_web_form_id: process.env.PLEZI_CONTENT_WEB_FORM_ID, - email, - plz_ma_region: Array.isArray(zone) - ? zone.map((singleZone) => { - return PleziContactRegions[singleZone]; - }) - : PleziContactRegions[zone], - plz_je_suis: Array.isArray(status) - ? status.map((singleStatus) => { - return PleziContactStatuses[singleStatus]; - }) - : PleziContactStatuses[status], - keep_multiple_select_values: true, - subscriptions: PleziNewsletterId, - }, - { encode: false, arrayFormat: 'comma' } - )}${ - urlParams - ? `&${qs.stringify(urlParams, { - encode: false, - })}` - : '' - }`; - - const pleziApiRoute = `https://app.plezi.co/api/v1/create_contact_after_webform`; - - const response = await fetch(`${pleziApiRoute}?${queryParams}`, { - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'X-Tenant-Company': process.env.PLEZI_TENANT_NAME, - 'X-API-Key': process.env.PLEZI_API_KEY, - }, - method: 'GET', - }); - - const responseJSON = await response.json(); - - if (response.status !== 200 && response.status !== 201) { - throw new Error( - `${response.status}, ${responseJSON.errors[0].title}, ${responseJSON.errors[0].detail}` - ); - } - } -} diff --git a/src/external-services/plezi/plezi.types.ts b/src/external-services/plezi/plezi.types.ts deleted file mode 100644 index d4bfa534..00000000 --- a/src/external-services/plezi/plezi.types.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { AdminZone, AdminZones } from 'src/utils/types'; - -export const ContactStatuses = { - INDIVIDUAL: 'PARTICULIER', - COMPANY: 'ENTREPRISE', - STRUCTURE: 'STRUCTURE_INSERTION', - CANDIDATE: 'CANDIDAT_POTENTIEL', -} as const; - -export type ContactStatus = - typeof ContactStatuses[keyof typeof ContactStatuses]; - -export const PleziContactRegions: { [K in AdminZone]: string } = { - [AdminZones.LYON]: AdminZones.LYON.toLowerCase(), - [AdminZones.PARIS]: AdminZones.PARIS.toLowerCase(), - [AdminZones.LILLE]: AdminZones.LILLE.toLowerCase(), - [AdminZones.LORIENT]: AdminZones.LORIENT.toLowerCase(), - [AdminZones.RENNES]: AdminZones.RENNES.toLowerCase(), - [AdminZones.HZ]: AdminZones.HZ.toLowerCase(), -} as const; - -export const PleziContactStatuses: { [K in ContactStatus]: string } = { - PARTICULIER: 'un-particulier', - ENTREPRISE: 'une-entreprise', - STRUCTURE_INSERTION: 'une-structure-d-insertion', - CANDIDAT_POTENTIEL: 'un-candidat-potentiel', -} as const; - -export const PleziNewsletterId = '6311128ae317a70f550b73fc' as const; - -export interface PleziTrackingData { - visit?: string; - visitor?: string; - urlParams?: { - utm?: string; - utm_medium?: string; - utm_source?: string; - gclid?: string; - referer?: string; - }; -} diff --git a/src/opportunities/dto/create-opportunity.dto.ts b/src/opportunities/dto/create-opportunity.dto.ts index b6ac101e..1690a813 100644 --- a/src/opportunities/dto/create-opportunity.dto.ts +++ b/src/opportunities/dto/create-opportunity.dto.ts @@ -2,7 +2,6 @@ import { ApiProperty, PickType } from '@nestjs/swagger'; import { IsArray, IsBoolean, IsOptional } from 'class-validator'; import { Opportunity } from '../models'; import { Department } from 'src/common/locations/locations.types'; -import { PleziTrackingData } from 'src/external-services/plezi/plezi.types'; interface LocationDto { department: Department; @@ -65,16 +64,4 @@ export class CreateOpportunityDto extends PickType(Opportunity, [ @ApiProperty() @IsOptional() locations?: LocationDto | LocationDto[]; - - @ApiProperty() - @IsOptional() - visit?: PleziTrackingData['visit']; - - @ApiProperty() - @IsOptional() - visitor?: PleziTrackingData['visitor']; - - @ApiProperty() - @IsOptional() - urlParams?: PleziTrackingData['urlParams']; } diff --git a/src/opportunities/opportunities.controller.ts b/src/opportunities/opportunities.controller.ts index 3b081a98..abab1050 100644 --- a/src/opportunities/opportunities.controller.ts +++ b/src/opportunities/opportunities.controller.ts @@ -15,14 +15,13 @@ import { import { validate as uuidValidate } from 'uuid'; import { PayloadUser } from 'src/auth/auth.types'; import { Public, UserPayload } from 'src/auth/guards'; -import { PleziTrackingData } from 'src/external-services/plezi/plezi.types'; import { LinkedUser, LinkedUserGuard, UserPermissions, UserPermissionsGuard, } from 'src/users/guards'; -import { UserRole, UserRoles, Permissions } from 'src/users/users.types'; +import { Permissions, UserRole, UserRoles } from 'src/users/users.types'; import { getCandidateIdFromCoachOrCandidate } from 'src/users/users.utils'; import { isValidPhone } from 'src/utils/misc'; import { AdminZone, FilterParams } from 'src/utils/types'; @@ -83,14 +82,9 @@ export class OpportunitiesController { shouldSendNotifications, isCopy, candidatesIds, - visit, - visitor, - urlParams, ...restBody } = createOpportunityDto; - const pleziTrackingData: PleziTrackingData = { visit, visitor, urlParams }; - if (isAdmin && !isLoggedAsAdmin) { throw new ForbiddenException(); } @@ -127,8 +121,7 @@ export class OpportunitiesController { finalOpportunity.toJSON(), candidates, isAdmin, - shouldSendNotifications, - pleziTrackingData + shouldSendNotifications ); await this.opportunitiesService.createExternalDBOpportunity( @@ -177,8 +170,7 @@ export class OpportunitiesController { finalOpportunity.toJSON(), candidates, isAdmin, - shouldSendNotifications, - pleziTrackingData + shouldSendNotifications ); await this.opportunitiesService.createExternalDBOpportunity( diff --git a/src/opportunities/opportunities.module.ts b/src/opportunities/opportunities.module.ts index 269529d9..4539e827 100644 --- a/src/opportunities/opportunities.module.ts +++ b/src/opportunities/opportunities.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { SequelizeModule } from '@nestjs/sequelize'; import { ContractsModule } from '../common/contracts/contracts.module'; -import { PleziModule } from '../external-services/plezi/plezi.module'; import { BusinessLinesModule } from 'src/common/business-lines/business-lines.module'; import { CVsModule } from 'src/cvs/cvs.module'; import { ExternalDatabasesModule } from 'src/external-databases/external-databases.module'; +import { MailjetModule } from 'src/external-services/mailjet/mailjet.module'; import { MailsModule } from 'src/mails/mails.module'; import { QueuesModule } from 'src/queues/producers'; import { SMSModule } from 'src/sms/sms.module'; @@ -34,7 +34,7 @@ import { OpportunityUsersService } from './opportunity-users.service'; CVsModule, BusinessLinesModule, MailsModule, - PleziModule, + MailjetModule, SMSModule, ExternalDatabasesModule, QueuesModule, diff --git a/src/opportunities/opportunities.service.ts b/src/opportunities/opportunities.service.ts index c2e9f576..934ab20c 100644 --- a/src/opportunities/opportunities.service.ts +++ b/src/opportunities/opportunities.service.ts @@ -3,6 +3,7 @@ import { InjectModel } from '@nestjs/sequelize'; import * as _ from 'lodash'; import moment from 'moment'; import { Op } from 'sequelize'; +import { MailjetService } from '../external-services/mailjet/mailjet.service'; import { BusinessLineValue } from 'src/common/business-lines/business-lines.types'; import { BusinessLine } from 'src/common/business-lines/models'; import { @@ -12,11 +13,7 @@ import { import { Location } from 'src/common/locations/models'; import { CVsService } from 'src/cvs/cvs.service'; import { ExternalDatabasesService } from 'src/external-databases/external-databases.service'; -import { PleziService } from 'src/external-services/plezi/plezi.service'; -import { - ContactStatuses, - PleziTrackingData, -} from 'src/external-services/plezi/plezi.types'; +import { ContactStatuses } from 'src/external-services/mailjet/mailjet.types'; import { MailsService } from 'src/mails/mails.service'; import { QueuesService } from 'src/queues/producers/queues.service'; import { Jobs } from 'src/queues/queues.types'; @@ -84,7 +81,7 @@ export class OpportunitiesService { private cvsService: CVsService, private externalDatabasesService: ExternalDatabasesService, private mailsService: MailsService, - private pleziService: PleziService, + private mailjetService: MailjetService, private smsService: SMSService ) {} @@ -680,8 +677,7 @@ export class OpportunitiesService { opportunity: Opportunity, candidates: OpportunityUser[], isAdmin = false, - shouldSendNotifications = true, - pleziTrackingData?: PleziTrackingData + shouldSendNotifications = true ) { if (!isAdmin) { await this.mailsService.sendOnCreatedOfferMail(opportunity); @@ -694,14 +690,11 @@ export class OpportunitiesService { } try { - await this.pleziService.sendContactToPlezi( - opportunity.contactMail || opportunity.recruiterMail, - getZoneFromDepartment(opportunity.department), - ContactStatuses.COMPANY, - pleziTrackingData?.visit, - pleziTrackingData?.visitor, - pleziTrackingData?.urlParams - ); + await this.mailjetService.sendContact({ + email: opportunity.contactMail || opportunity.recruiterMail, + zone: getZoneFromDepartment(opportunity.department), + status: ContactStatuses.COMPANY, + }); } catch (err) { console.error(err); } diff --git a/tests/contacts/contacts.e2e-spec.ts b/tests/contacts/contacts.e2e-spec.ts index 3ce796cd..4b1e97f3 100644 --- a/tests/contacts/contacts.e2e-spec.ts +++ b/tests/contacts/contacts.e2e-spec.ts @@ -3,9 +3,8 @@ import { CACHE_MANAGER, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import request from 'supertest'; import { ContactsController } from 'src/contacts/contacts.controller'; -import { ContactsService } from 'src/contacts/contacts.service'; -import { PleziService } from 'src/external-services/plezi/plezi.service'; -import { ContactStatus } from 'src/external-services/plezi/plezi.types'; +import { MailjetService } from 'src/external-services/mailjet/mailjet.service'; +import { ContactStatus } from 'src/external-services/mailjet/mailjet.types'; import { SalesforceService } from 'src/external-services/salesforce/salesforce.service'; import { Queues } from 'src/queues/queues.types'; import { AdminZones, APIResponse } from 'src/utils/types'; @@ -13,7 +12,7 @@ import { CustomTestingModule } from 'tests/custom-testing.module'; import { DatabaseHelper } from 'tests/database.helper'; import { CacheMocks, - PleziMocks, + MailjetMock, QueueMocks, SalesforceMocks, } from 'tests/mocks.types'; @@ -43,8 +42,8 @@ describe('Contacts', () => { .useValue(CacheMocks) .overrideProvider(SalesforceService) .useValue(SalesforceMocks) - .overrideProvider(PleziService) - .useValue(PleziMocks) + .overrideProvider(MailjetService) + .useValue(MailjetMock) .compile(); app = moduleFixture.createNestApplication(); @@ -227,12 +226,6 @@ describe('Contacts', () => { }); describe('/newsletter - Subscribe contact email to newsletter list', () => { - beforeEach(() => { - jest - .spyOn(ContactsService.prototype, 'sendContactToPlezi') - .mockImplementationOnce(async () => null); - }); - it('Should return 201, if all content provided', async () => { const response: APIResponse< ContactsController['addContactForNewsletter'] diff --git a/tests/mocks.types.ts b/tests/mocks.types.ts index 568f610c..8bd337a3 100644 --- a/tests/mocks.types.ts +++ b/tests/mocks.types.ts @@ -3,7 +3,7 @@ import { Cache } from 'cache-manager'; import { CloudFrontService } from 'src/external-services/aws/cloud-front.service'; import { S3Service } from 'src/external-services/aws/s3.service'; import { BitlyService } from 'src/external-services/bitly/bitly.service'; -import { PleziService } from 'src/external-services/plezi/plezi.service'; +import { MailjetService } from 'src/external-services/mailjet/mailjet.service'; import { SalesforceService } from 'src/external-services/salesforce/salesforce.service'; type ProviderMock = { [K in keyof T]: jest.Mock }; @@ -100,6 +100,11 @@ export const SalesforceMocks: ProviderMock = { getCampaigns: jest.fn(), } as const; -export const PleziMocks: ProviderMock = { - sendContactToPlezi: jest.fn(), +export const MailjetMock: ProviderMock = { + sendMail: jest.fn(), + findContact: jest.fn(), + createContact: jest.fn(), + updateContactTags: jest.fn(), + subscribeToNewsletterList: jest.fn(), + sendContact: jest.fn(), } as const; diff --git a/tests/opportunities/opportunities.e2e-spec.ts b/tests/opportunities/opportunities.e2e-spec.ts index b2127769..267d062c 100644 --- a/tests/opportunities/opportunities.e2e-spec.ts +++ b/tests/opportunities/opportunities.e2e-spec.ts @@ -7,13 +7,11 @@ import { v4 as uuid } from 'uuid'; import { BitlyMocks, CacheMocks, - PleziMocks, QueueMocks, SalesforceMocks, } from '../mocks.types'; import { LoggedUser } from 'src/auth/auth.types'; import { BitlyService } from 'src/external-services/bitly/bitly.service'; -import { PleziService } from 'src/external-services/plezi/plezi.service'; import { SalesforceService } from 'src/external-services/salesforce/salesforce.service'; import { Opportunity, OpportunityUser } from 'src/opportunities/models'; import { OpportunityUserEvent } from 'src/opportunities/models/opportunity-user-event.model'; @@ -65,8 +63,6 @@ describe('Opportunities', () => { .useValue(CacheMocks) .overrideProvider(SalesforceService) .useValue(SalesforceMocks) - .overrideProvider(PleziService) - .useValue(PleziMocks) .compile(); app = moduleFixture.createNestApplication(); diff --git a/yarn.lock b/yarn.lock index f1e520ba..6d245701 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2322,10 +2322,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node-mailjet@^3.3.8": - version "3.3.8" - resolved "https://registry.npmjs.org/@types/node-mailjet/-/node-mailjet-3.3.8.tgz" - integrity sha512-bjrj7FOG5bVGpV1RovUF1JydGngW/TIrbGDTiId41v9ak3rwzGm4OVmPzjjKbqjECCD8toRXOdyTz28rkH5OlA== +"@types/node-mailjet@^3.3.9": + version "3.3.9" + resolved "https://registry.yarnpkg.com/@types/node-mailjet/-/node-mailjet-3.3.9.tgz#fec82b9142d9c94132e5b73b469c31b334333275" + integrity sha512-OSwpRjw0DPNCfRQTCcYOAT42uCcbOmAufZjHbqRW+MDsO3QOBTYajwT8J0JlRdgMdBrMEcEkIqLCbc0OSfSiRQ== "@types/node@*": version "20.1.7" @@ -3007,7 +3007,7 @@ assert-plus@1.0.0, assert-plus@^1.0.0: ast-types@^0.13.2: version "0.13.4" - resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== dependencies: tslib "^2.0.1" @@ -3159,7 +3159,7 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.5.0, bluebird@^3.7.2: +bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -3688,7 +3688,7 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookiejar@^2.1.0, cookiejar@^2.1.2, cookiejar@^2.1.3: +cookiejar@^2.1.0, cookiejar@^2.1.3: version "2.1.4" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== @@ -3824,7 +3824,7 @@ dashdash@^1.12.0: data-uri-to-buffer@3: version "3.0.1" - resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== data-urls@^2.0.0: @@ -3947,15 +3947,15 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" -degenerator@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz" - integrity sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ== +degenerator@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-3.0.4.tgz#07ccf95bc11044a37a6efc2f66029fb636e31f24" + integrity sha512-Z66uPeBfHZAHVmue3HPfyKu2Q0rC2cRxbTOsvmU/po5fvvcx27W4mIu9n0PUlQih4oUYvcG1BsbtVv8x7KDOSw== dependencies: ast-types "^0.13.2" escodegen "^1.8.1" esprima "^4.0.0" - vm2 "^3.9.8" + vm2 "^3.9.17" delay@^5.0.0: version "5.0.0" @@ -4281,7 +4281,7 @@ escape-string-regexp@^4.0.0: escodegen@^1.8.1: version "1.14.3" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== dependencies: esprima "^4.0.1" @@ -4690,7 +4690,7 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-safe-stringify@2.1.1, fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1: +fast-safe-stringify@2.1.1, fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== @@ -4756,7 +4756,7 @@ file-entry-cache@^6.0.1: file-uri-to-path@2: version "2.0.0" - resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== fill-range@^7.0.1: @@ -4881,7 +4881,7 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -formidable@^1.1.1, formidable@^1.2.2: +formidable@^1.1.1: version "1.2.6" resolved "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz" integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ== @@ -4922,7 +4922,7 @@ fs-extra@^10.0.0: fs-extra@^8.1.0: version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: graceful-fs "^4.2.0" @@ -4963,8 +4963,8 @@ fsevents@^2.3.2, fsevents@~2.3.2: ftp@^0.3.10: version "0.3.10" - resolved "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz" - integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ== dependencies: readable-stream "1.1.x" xregexp "2.0.0" @@ -5030,7 +5030,7 @@ get-symbol-description@^1.0.0: get-uri@3: version "3.0.2" - resolved "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== dependencies: "@tootallnate/once" "1" @@ -5236,18 +5236,18 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-proxy-agent@5, https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== +https-proxy-agent@5, https-proxy-agent@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" -https-proxy-agent@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== dependencies: agent-base "6" debug "4" @@ -5424,9 +5424,14 @@ ioredis@^4.28.5: standard-as-callback "^2.1.0" ip@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + version "1.1.8" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" + integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== + +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== ipaddr.js@1.9.1: version "1.9.1" @@ -6251,8 +6256,8 @@ jsonc-parser@3.2.0: jsonfile@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== optionalDependencies: graceful-fs "^4.1.6" @@ -6565,7 +6570,7 @@ lru-cache@^4.1.5: lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" @@ -6740,7 +6745,7 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.6, mime@^2.5.0: +mime@2.6.0, mime@^2.5.0: version "2.6.0" resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -6941,9 +6946,9 @@ neo-async@^2.6.2: resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -netmask@^2.0.1: +netmask@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== next-tick@1, next-tick@^1.1.0: @@ -7009,15 +7014,14 @@ node-int64@^0.4.0: resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-mailjet@^3.3.7: - version "3.3.7" - resolved "https://registry.npmjs.org/node-mailjet/-/node-mailjet-3.3.7.tgz" - integrity sha512-kwO0JlQi8MtYEc0DTgL8JeRS1ljjpNqmQ/+attDV3i4YiaCl8SoKs2gBdePWvoFtpCi8uBsfOgU5O9jE/lm2QQ== +node-mailjet@^3.3.9: + version "3.4.1" + resolved "https://registry.yarnpkg.com/node-mailjet/-/node-mailjet-3.4.1.tgz#d8b31a58dfc78e980d9b5de0f757eebbeab0a8b4" + integrity sha512-m+msgBJYgwFbIZBIPOnsGOtBt9xP03UqmkmuEcgTcLlr/U1GUJQrVI7cDFRgujybb9Cl1wl4thIGyM3wt6X+zQ== dependencies: - bluebird "^3.5.0" json-bigint "^1.0.0" qs "^6.5.0" - superagent "^6.1.0" + superagent "^7.1.1" superagent-proxy "^3.0.0" node-releases@^2.0.2: @@ -7288,7 +7292,7 @@ p-try@^2.0.0: pac-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz#b718f76475a6a5415c2efbe256c1c971c84f635e" integrity sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ== dependencies: "@tootallnate/once" "1" @@ -7302,13 +7306,13 @@ pac-proxy-agent@^5.0.0: socks-proxy-agent "5" pac-resolver@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz" - integrity sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-5.0.1.tgz#c91efa3a9af9f669104fa2f51102839d01cde8e7" + integrity sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q== dependencies: - degenerator "^3.0.1" + degenerator "^3.0.2" ip "^1.1.5" - netmask "^2.0.1" + netmask "^2.0.2" packet-reader@1.0.0: version "1.0.0" @@ -7713,7 +7717,7 @@ proxy-addr@~2.0.7: proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-5.0.0.tgz#d31405c10d6e8431fde96cba7a0c027ce01d633b" integrity sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g== dependencies: agent-base "^6.0.0" @@ -7799,13 +7803,20 @@ qs@6.9.3: resolved "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz" integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== -qs@^6.10.1, qs@^6.5.0, qs@^6.9.4: +qs@^6.10.1: version "6.10.3" resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== dependencies: side-channel "^1.0.4" +qs@^6.10.3, qs@^6.5.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + qs@~6.5.2: version "6.5.3" resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz" @@ -7843,7 +7854,7 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1, raw-body@^2.2.0: +raw-body@2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== @@ -7853,7 +7864,7 @@ raw-body@2.5.1, raw-body@^2.2.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@2.5.2: +raw-body@2.5.2, raw-body@^2.2.0: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== @@ -8431,12 +8442,12 @@ slice-ansi@^5.0.0: smart-buffer@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== socks-proxy-agent@5, socks-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== dependencies: agent-base "^6.0.2" @@ -8444,11 +8455,11 @@ socks-proxy-agent@5, socks-proxy-agent@^5.0.0: socks "^2.3.3" socks@^2.3.3: - version "2.6.2" - resolved "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz" - integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== dependencies: - ip "^1.1.5" + ip "^2.0.0" smart-buffer "^4.2.0" source-map-support@0.5.21, source-map-support@^0.5.20, source-map-support@^0.5.6, source-map-support@~0.5.20: @@ -8698,7 +8709,7 @@ strip-json-comments@~2.0.1: superagent-proxy@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/superagent-proxy/-/superagent-proxy-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/superagent-proxy/-/superagent-proxy-3.0.0.tgz#e1a17ccba25883599e18d2974020fe83ee7d95d1" integrity sha512-wAlRInOeDFyd9pyonrkJspdRAxdLrcsZ6aSnS+8+nu4x1aXbz6FWSTT9M6Ibze+eG60szlL7JA8wEIV7bPWuyQ== dependencies: debug "^4.3.2" @@ -8720,23 +8731,6 @@ superagent@3.8.1: qs "^6.5.1" readable-stream "^2.0.5" -superagent@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz" - integrity sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg== - dependencies: - component-emitter "^1.3.0" - cookiejar "^2.1.2" - debug "^4.1.1" - fast-safe-stringify "^2.0.7" - form-data "^3.0.0" - formidable "^1.2.2" - methods "^1.1.2" - mime "^2.4.6" - qs "^6.9.4" - readable-stream "^3.6.0" - semver "^7.3.2" - superagent@^7.1.0: version "7.1.1" resolved "https://registry.npmjs.org/superagent/-/superagent-7.1.1.tgz" @@ -8754,6 +8748,23 @@ superagent@^7.1.0: readable-stream "^3.6.0" semver "^7.3.5" +superagent@^7.1.1: + version "7.1.6" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-7.1.6.tgz#64f303ed4e4aba1e9da319f134107a54cacdc9c6" + integrity sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.3" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^2.0.1" + methods "^1.1.2" + mime "2.6.0" + qs "^6.10.3" + readable-stream "^3.6.0" + semver "^7.3.7" + supertest@^6.1.3: version "6.2.2" resolved "https://registry.npmjs.org/supertest/-/supertest-6.2.2.tgz" @@ -9084,7 +9095,12 @@ tslib@^1.11.1, tslib@^1.8.1: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1: +tslib@^2.0.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tslib@^2.1.0, tslib@^2.3.1: version "2.4.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== @@ -9335,7 +9351,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vm2@^3.9.8: +vm2@^3.9.17: version "3.9.19" resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.19.tgz#be1e1d7a106122c6c492b4d51c2e8b93d3ed6a4a" integrity sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg== @@ -9589,8 +9605,8 @@ xmlchars@^2.2.0: xregexp@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz" - integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA== xtend@^4.0.0: version "4.0.2" @@ -9609,7 +9625,7 @@ yallist@^2.1.2: yallist@^3.0.2: version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: