diff --git a/.talismanrc b/.talismanrc index b0c2af571e..bec158ad22 100644 --- a/.talismanrc +++ b/.talismanrc @@ -36,7 +36,7 @@ fileignoreconfig: - filename: server/src/http/controllers/v2/applications.controller.v2.test.ts checksum: 9147c705b5bdfe6eff6b1b75c467249ee1af783d57ff16d78722e48e60831c68 - filename: server/src/http/controllers/v2/appointment.controller.v2.test.ts - checksum: 52a0f730bc8feed13f4f1371c16a332872bec77ca31f0732422b7ca90b322df9 + checksum: bbbf100d65bae0d840c715f4b1d893986df7423f470f3ee300545959105f6ca4 - filename: server/src/http/controllers/v2/jobs.controller.v2.test.ts checksum: a99bfdcb5a8f4c66e9d055b44160adbc7ec2bcaf87209ff76fa9a9f674b9ca86 - filename: server/src/http/controllers/v2/jobs.controller.v2.ts @@ -160,7 +160,7 @@ fileignoreconfig: - filename: shared/helpers/generateUri.ts checksum: 6542db0d3eca959c6e81d574f8b71d4b18d2f1af21042ca5ed4dff319cd39555 - filename: shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap - checksum: aa01a8388e2accb53cc88b46750fea7487998584a107059e3149695c0d219ae6 + checksum: 903006c87a12fbb97070f96ee40d5acca427e46a00a3f4221cb0c7d351ede26b - filename: shared/helpers/openapi/generateOpenapi.test.ts checksum: d7b85c3dff488cec523d78f0926e15dbea41071a1864bda62b4d6caeb2541df3 - filename: shared/models/applications.model.ts diff --git a/server/src/http/controllers/application.controller.test.ts b/server/src/http/controllers/application.controller.test.ts index 8d2c999974..562f510893 100644 --- a/server/src/http/controllers/application.controller.test.ts +++ b/server/src/http/controllers/application.controller.test.ts @@ -1,3 +1,4 @@ +import { omit } from "lodash-es" import { ObjectId } from "mongodb" import { generateLbaCompanyFixture } from "shared/fixtures/recruteurLba.fixture" import { beforeEach, describe, expect, it, vi } from "vitest" @@ -86,16 +87,20 @@ describe("POST /v1/application", () => { const applications = await getDbCollection("applications").find({}).toArray() const applicant = await getDbCollection("applicants").findOne({ _id: applications[0]?.applicant_id }) + expect(omit(applicant, ["createdAt", "last_connection", "updatedAt"])).toEqual({ + _id: applicant?._id, + email: body.applicant_email, + firstname: body.applicant_first_name, + lastname: body.applicant_last_name, + phone: body.applicant_phone, + }) + expect(applications).toEqual([ { _id: expect.any(ObjectId), applicant_id: applicant?._id, applicant_attachment_name: body.applicant_file_name, - applicant_email: body.applicant_email, - applicant_first_name: body.applicant_first_name, - applicant_last_name: body.applicant_last_name, applicant_message_to_company: "", - applicant_phone: body.applicant_phone, caller: body.caller, company_address: "126 RUE DE L UNIVERSITE, 75007 Paris", company_email: recruteur.email, diff --git a/server/src/http/controllers/v2/applications.controller.v2.test.ts b/server/src/http/controllers/v2/applications.controller.v2.test.ts index 31d128ba23..43fb1d22be 100644 --- a/server/src/http/controllers/v2/applications.controller.v2.test.ts +++ b/server/src/http/controllers/v2/applications.controller.v2.test.ts @@ -1,3 +1,4 @@ +import { omit } from "lodash-es" import { ObjectId } from "mongodb" import { IApplicationApiPublic, JOB_STATUS } from "shared" import { NIVEAUX_POUR_LBA, RECRUITER_STATUS } from "shared/constants" @@ -141,15 +142,19 @@ describe("POST /v2/application", () => { expect.soft(response.statusCode).toEqual(202) expect.soft(response.json()).toEqual({ id: application!._id.toString() }) + expect.soft(omit(applicant, ["createdAt", "last_connection", "updatedAt"])).toEqual({ + _id: applicant?._id, + email: body.applicant_email, + firstname: body.applicant_first_name, + lastname: body.applicant_last_name, + phone: body.applicant_phone, + }) + expect(application).toEqual({ _id: expect.any(ObjectId), applicant_id: applicant?._id, applicant_attachment_name: body.applicant_attachment_name, - applicant_email: body.applicant_email, - applicant_first_name: body.applicant_first_name, - applicant_last_name: body.applicant_last_name, applicant_message_to_company: "", - applicant_phone: body.applicant_phone, company_email: recruteur.email, company_feedback: null, company_name: "ASSEMBLEE NATIONALE", @@ -200,15 +205,19 @@ describe("POST /v2/application", () => { expect.soft(response.statusCode).toEqual(202) expect.soft(response.json()).toEqual({ id: application!._id.toString() }) + expect.soft(omit(applicant, ["createdAt", "last_connection", "updatedAt"])).toEqual({ + _id: applicant?._id, + email: body.applicant_email, + firstname: body.applicant_first_name, + lastname: body.applicant_last_name, + phone: body.applicant_phone, + }) + expect(application).toEqual({ _id: expect.any(ObjectId), applicant_id: applicant?._id, applicant_attachment_name: body.applicant_attachment_name, - applicant_email: body.applicant_email, - applicant_first_name: body.applicant_first_name, - applicant_last_name: body.applicant_last_name, applicant_message_to_company: "", - applicant_phone: body.applicant_phone, company_address: "Paris", company_email: "test-application@mail.fr", company_feedback: null, diff --git a/server/src/jobs/anonymization/anonymizeApplications.ts b/server/src/jobs/anonymization/anonymizeApplications.ts index 93de8ad7b8..fe0fbff230 100644 --- a/server/src/jobs/anonymization/anonymizeApplications.ts +++ b/server/src/jobs/anonymization/anonymizeApplications.ts @@ -2,6 +2,18 @@ import { logger } from "../../common/logger" import { getDbCollection } from "../../common/utils/mongodbUtils" import { notifyToSlack } from "../../common/utils/slackUtils" +export const anonymizeApplicationProjection = { + company_recruitment_intention: 1, + company_feedback_date: 1, + company_siret: 1, + company_naf: 1, + job_origin: 1, + job_id: 1, + caller: 1, + created_at: 1, + applicant_id: 1, +} + const anonymize = async () => { logger.info(`Début anonymisation`) @@ -16,17 +28,7 @@ const anonymize = async () => { $match: matchCondition, }, { - $project: { - company_recruitment_intention: 1, - company_feedback_date: 1, - company_siret: 1, - company_naf: 1, - job_origin: 1, - job_id: 1, - caller: 1, - created_at: 1, - applicant_id: 1, - }, + $project: anonymizeApplicationProjection, }, { $merge: "anonymizedapplications", diff --git a/server/src/jobs/applications/fixApplications.ts b/server/src/jobs/applications/fixApplications.ts deleted file mode 100644 index 8b39da629f..0000000000 --- a/server/src/jobs/applications/fixApplications.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { cleanEmail } from "shared/helpers/common" - -import { logger } from "@/common/logger" - -import { getDbCollection } from "../../common/utils/mongodbUtils" - -const removeOrReplaceCharsInDB = async () => { - logger.info("Nettoyage des adresses emails mal formées dans applications.applicant_email") - - const charsRegex = /[^a-zA-Z0-9@_.+-]/ - const applicantsCursor = await getDbCollection("applications") - .find({ applicant_email: { $regex: charsRegex } }) - .toArray() - - for await (const application of applicantsCursor) { - const applicant_email = cleanEmail(application.applicant_email) - if (applicant_email !== application.applicant_email) { - await getDbCollection("applications").updateOne({ _id: application._id }, { $set: { applicant_email } }) - } - } -} - -export default async function fixApplications() { - await removeOrReplaceCharsInDB() - - await getDbCollection("applications").updateMany( - { company_naf: null }, - { $set: { company_naf: "" } }, - { - bypassDocumentValidation: true, - } - ) - - await getDbCollection("applications").updateMany( - { job_title: null }, - { $set: { job_title: "" } }, - { - bypassDocumentValidation: true, - } - ) -} diff --git a/server/src/jobs/simpleJobDefinitions.ts b/server/src/jobs/simpleJobDefinitions.ts index 5602764d02..05de3f6bba 100644 --- a/server/src/jobs/simpleJobDefinitions.ts +++ b/server/src/jobs/simpleJobDefinitions.ts @@ -3,7 +3,6 @@ import { generateSitemap } from "@/services/sitemap.service" import { anonymizeApplicantsAndApplications } from "./anonymization/anonymizeApplicantAndApplications" import { anonimizeUsersWithAccounts } from "./anonymization/anonymizeUserRecruteurs" import { anonymizeUsers } from "./anonymization/anonymizeUsers" -import fixApplications from "./applications/fixApplications" import { processApplications } from "./applications/processApplications" import { sendContactsToBrevo } from "./brevoContacts/sendContactsToBrevo" import { obfuscateCollections } from "./database/obfuscateCollections" @@ -152,10 +151,6 @@ export const simpleJobDefinitions: SimpleJobDefinition[] = [ fct: fixJobExpirationDate, description: "Répare les date d'expiration d'offre qui seraient trop dans le futur", }, - { - fct: fixApplications, - description: "Répare les adresses emails comportant des caractères erronés dans la collection applications", - }, { fct: fixRecruiterDataValidation, description: "Répare les data de la collection recruiters", diff --git a/server/src/jobs/updateBrevoBlockedEmails/updateBrevoBlockedEmails.ts b/server/src/jobs/updateBrevoBlockedEmails/updateBrevoBlockedEmails.ts index aad810ec25..8df260a277 100644 --- a/server/src/jobs/updateBrevoBlockedEmails/updateBrevoBlockedEmails.ts +++ b/server/src/jobs/updateBrevoBlockedEmails/updateBrevoBlockedEmails.ts @@ -54,7 +54,7 @@ export const saveBlacklistEmails = async (contacts) => { if (await getDbCollection("applications").findOne({ company_email: email })) { origin = BlackListOrigins.CANDIDATURE_SPONTANEE_RECRUTEUR - } else if (await getDbCollection("applications").findOne({ applicant_email: email })) { + } else if (await getDbCollection("applicants").findOne({ email })) { origin = BlackListOrigins.CANDIDATURE_SPONTANEE_CANDIDAT } else if (await getDbCollection("users").findOne({ email, role: EApplicantRole.CANDIDAT })) { origin = BlackListOrigins.PRDV_CANDIDAT diff --git a/server/src/migrations/20250107000000-clean-applications.ts b/server/src/migrations/20250107000000-clean-applications.ts new file mode 100644 index 0000000000..baf8e6119e --- /dev/null +++ b/server/src/migrations/20250107000000-clean-applications.ts @@ -0,0 +1,15 @@ +import { getDbCollection } from "@/common/utils/mongodbUtils" + +export const up = async () => { + await getDbCollection("applications").updateMany( + {}, + { + $unset: { + applicant_first_name: "", + applicant_last_name: "", + applicant_email: "", + applicant_phone: "", + }, + } + ) +} diff --git a/server/src/migrations/20250107000002-archive-applications.ts b/server/src/migrations/20250107000002-archive-applications.ts new file mode 100644 index 0000000000..f0a1d4b346 --- /dev/null +++ b/server/src/migrations/20250107000002-archive-applications.ts @@ -0,0 +1,42 @@ +import { getDbCollection } from "@/common/utils/mongodbUtils" +import { anonymizeApplicationProjection } from "@/jobs/anonymization/anonymizeApplications" + +export const up = async () => { + const filterStages = [ + { + $lookup: { + from: "applicants", + localField: "applicant_id", + foreignField: "_id", + as: "matches", + }, + }, + { + $match: { + "matches.0": { + $exists: false, + }, + }, + }, + ] + await getDbCollection("applications") + .aggregate([ + ...filterStages, + { + $project: anonymizeApplicationProjection, + }, + { + $merge: "anonymized_applications", + }, + ]) + .toArray() + const idsToDelete = await getDbCollection("applications") + .aggregate([ + ...filterStages, + { + $project: { _id: 1 }, + }, + ]) + .toArray() + await getDbCollection("applications").deleteMany({ _id: { $in: idsToDelete.map(({ _id }) => _id) } }) +} diff --git a/server/src/security/authorisationService.ts b/server/src/security/authorisationService.ts index 54a5dfb680..96651c3e14 100644 --- a/server/src/security/authorisationService.ts +++ b/server/src/security/authorisationService.ts @@ -14,6 +14,7 @@ import { assertUnreachable, parseEnum } from "shared/utils" import { Primitive } from "type-fest" import { getDbCollection } from "@/common/utils/mongodbUtils" +import { getApplicantFromDB } from "@/services/applicant.service" import { getComputedUserAccess, getGrantedRoles } from "@/services/roleManagement.service" import { getUserWithAccountByEmail, isUserDisabled, isUserEmailChecked } from "@/services/userWithAccount.service" @@ -241,8 +242,13 @@ async function getApplicationResource(schema: S, r return { application } } const jobResource = await jobToJobResource(job, recruiter) - const user = await getUserWithAccountByEmail(application.applicant_email) - return { application, jobResource, user } + const applicantOpt = await getApplicantFromDB({ _id: application.applicant_id }) + if (applicantOpt) { + const user = await getUserWithAccountByEmail(applicantOpt.email) + return { application, jobResource, user } + } else { + return { application, jobResource } + } } assertUnreachable(applicationDef) diff --git a/server/src/services/applicant.service.ts b/server/src/services/applicant.service.ts index ec582b6670..ec40168f81 100644 --- a/server/src/services/applicant.service.ts +++ b/server/src/services/applicant.service.ts @@ -25,15 +25,16 @@ const createApplicant = async (applicant: IApplicantNew) => { return payload } -export const getOrCreateApplicant = async (applicant: IApplicantNew | IApplicant) => { - let user = await getApplicantFromDB({ email: applicant?.email.toLowerCase() }) +export const getApplicantByEmail = (email: string) => getApplicantFromDB({ email: email.toLowerCase() }) - if (user) { +export const getOrCreateApplicant = async (applicant: IApplicantNew | IApplicant) => { + let dbApplicantOpt = await getApplicantByEmail(applicant.email.toLowerCase()) + if (dbApplicantOpt) { // update last_connection date on applicant collection (last application = last connection) - await updateApplicant(user._id, { last_connection: new Date() }) + await updateApplicant(dbApplicantOpt._id, { last_connection: new Date() }) } else { - user = await createApplicant(applicant) + dbApplicantOpt = await createApplicant(applicant) } - return user + return dbApplicantOpt } diff --git a/server/src/services/application.service.ts b/server/src/services/application.service.ts index 444bea19bd..3fe5c887e9 100644 --- a/server/src/services/application.service.ts +++ b/server/src/services/application.service.ts @@ -164,7 +164,7 @@ export const sendApplication = async ({ if (!validateCaller({ caller: newApplication.caller, referer })) { return { error: "missing_caller" } } else { - let validationResult = validatePermanentEmail(newApplication.applicant_email) + let validationResult = validatePermanentEmail(applicant_email) if (validationResult !== "ok") { return { error: validationResult } } @@ -505,14 +505,10 @@ const offreOrCompanyToCompanyFields = ( } } -const cleanApplicantFields = (newApplication: INewApplicationV1, applicant: IApplicant) => { +const cleanApplicantFields = (newApplication: INewApplicationV1) => { return { - applicant_first_name: applicant.firstname, - applicant_last_name: applicant.lastname, applicant_attachment_name: newApplication.applicant_file_name, - applicant_email: newApplication.applicant_email.toLowerCase(), applicant_message_to_company: prepareMessageForMail(newApplication.message), - applicant_phone: newApplication.applicant_phone, caller: newApplication.caller, } } @@ -525,7 +521,7 @@ const newApplicationToApplicationDocument = async (newApplication: INewApplicati const now = new Date() const application: IApplication = { ...offreOrCompanyToCompanyFields(offreOrCompany), - ...cleanApplicantFields(newApplication, applicant), + ...cleanApplicantFields(newApplication), applicant_id: applicant._id, company_email: recruteurEmail.toLowerCase(), company_recruitment_intention: null, @@ -555,12 +551,8 @@ const newApplicationToApplicationDocumentV2 = async ( const application: IApplication = { _id: new ObjectId(), applicant_id: applicant._id, - applicant_first_name: newApplication.applicant_first_name, - applicant_last_name: newApplication.applicant_last_name, applicant_attachment_name: newApplication.applicant_attachment_name, - applicant_email: newApplication.applicant_email.toLowerCase(), applicant_message_to_company: prepareMessageForMail(newApplication.applicant_message), - applicant_phone: newApplication.applicant_phone, job_searched_by_user: "job_searched_by_user" in newApplication ? newApplication.job_searched_by_user : null, company_recruitment_intention: null, company_feedback: null, @@ -788,11 +780,12 @@ export const sendMailToApplicant = async ({ }): Promise => { const partner = (application.caller && PARTNER_NAMES[application.caller]) ?? null const jobSourceType: string = await getJobSourceType(application) + const { email: applicantEmail } = applicant switch (company_recruitment_intention) { case ApplicantIntention.ENTRETIEN: { mailer.sendEmail({ - to: applicant.email, + to: applicantEmail, cc: email!, subject: `Réponse positive de ${application.company_name} à la candidature${partner ? ` ${partner}` : ""} de ${applicant.firstname} ${applicant.lastname}`, template: getEmailTemplate("mail-candidat-entretien"), @@ -811,7 +804,7 @@ export const sendMailToApplicant = async ({ } case ApplicantIntention.NESAISPAS: { mailer.sendEmail({ - to: application.applicant_email, + to: applicantEmail, cc: email!, subject: `Réponse de ${application.company_name} à la candidature de ${applicant.firstname} ${applicant.lastname}`, template: getEmailTemplate("mail-candidat-nsp"), @@ -829,7 +822,7 @@ export const sendMailToApplicant = async ({ } case ApplicantIntention.REFUS: { mailer.sendEmail({ - to: application.applicant_email, + to: applicantEmail, subject: `Réponse négative de ${application.company_name} à la candidature${partner ? ` ${partner}` : ""} de ${applicant.firstname} ${applicant.lastname}`, template: getEmailTemplate("mail-candidat-refus"), data: { @@ -1024,6 +1017,7 @@ const getApplicationAttachmentContent = async (application: IApplication): Promi export const processApplicationScanForVirus = async (application: IApplication, applicant: IApplicant) => { const fileContent = await getApplicationAttachmentContent(application) const hasVirus = await isInfected(fileContent) + const { email: applicantEmail } = applicant await getDbCollection("applications").findOneAndUpdate( { _id: application._id }, { $set: { scan_status: hasVirus ? ApplicationScanStatus.VIRUS_DETECTED : ApplicationScanStatus.NO_VIRUS_DETECTED } } @@ -1032,7 +1026,7 @@ export const processApplicationScanForVirus = async (application: IApplication, if (hasVirus) { const { url: urlOfDetail, urlWithoutUtm: urlOfDetailNoUtm } = buildUrlsOfDetail(publicUrl, application, { utm_campaign: "je-candidate-virus-pj" }) await mailer.sendEmail({ - to: application.applicant_email, + to: applicantEmail, subject: "Echec d'envoi de votre candidature", template: getEmailTemplate("mail-echec-envoi-candidature"), data: { @@ -1074,7 +1068,7 @@ export const processApplicationEmails = { to: application.company_email, subject: (type === LBA_ITEM_TYPE.RECRUTEURS_LBA ? `Candidature spontanée en alternance ${application.company_name}` : `Candidature en alternance - ${application.job_title}`) + - ` - ${application.applicant_first_name} ${application.applicant_last_name}`, + ` - ${applicant.firstname} ${applicant.lastname}`, template: getEmailTemplate(type === LBA_ITEM_TYPE.RECRUTEURS_LBA ? "mail-candidature-spontanee" : "mail-candidature"), data: { ...sanitizeApplicationForEmail(application), @@ -1094,7 +1088,7 @@ export const processApplicationEmails = { if (emailCompany?.accepted?.length) { await getDbCollection("applications").findOneAndUpdate({ _id: application._id }, { $set: { to_company_message_id: emailCompany.messageId } }) } else { - logger.error(`Application email rejected. applicant_email=${application.applicant_email} company_email=${application.company_email}`) + logger.error(`Application email rejected. applicant_email=${applicant.email} company_email=${application.company_email}`) throw internal("Email entreprise destinataire rejeté.") } }, diff --git a/server/src/services/emails.service.test.ts b/server/src/services/emails.service.test.ts index f7a9fdec14..7ea213592c 100644 --- a/server/src/services/emails.service.test.ts +++ b/server/src/services/emails.service.test.ts @@ -93,9 +93,7 @@ describe("email blaklist events", () => { it("Applicant blocked should register candidature_spontanee_candidat (blocked)", async () => { const applicant = generateApplicantFixture({ email: blacklistedEmail }) await getDbCollection("applicants").insertOne(applicant) - await getDbCollection("applications").insertOne( - generateApplicationFixture({ applicant_id: applicant._id, applicant_email: blacklistedEmail, to_applicant_message_id: fakeMessageId_1 }) - ) + await getDbCollection("applications").insertOne(generateApplicationFixture({ applicant_id: applicant._id, to_applicant_message_id: fakeMessageId_1 })) baseWebHookPayload.event = BrevoEventStatus.BLOCKED baseWebHookPayload["message-id"] = fakeMessageId_1 @@ -114,7 +112,7 @@ describe("email blaklist events", () => { baseBlockedAddress[0].reason.code = BrevoBlockedReasons.UNSUBSCRIBED_VIA_API const applicant = generateApplicantFixture({ email: blacklistedEmail }) await getDbCollection("applicants").insertOne(applicant) - await getDbCollection("applications").insertOne(generateApplicationFixture({ applicant_email: blacklistedEmail })) + await getDbCollection("applications").insertOne(generateApplicationFixture({ applicant_id: applicant._id })) await saveBlacklistEmails(baseBlockedAddress) @@ -142,6 +140,7 @@ describe("email blaklist events", () => { await getDbCollection("emailblacklists").deleteMany({}) await getDbCollection("applications").deleteMany({}) + await getDbCollection("applicants").deleteMany({}) await getDbCollection("users").insertOne(generateUserFixture({ email: blacklistedEmail, role: EApplicantRole.CANDIDAT })) baseBlockedAddress[0].reason.code = BrevoBlockedReasons.UNSUBSCRIBED_VIA_MA diff --git a/server/tests/utils/user.test.utils.ts b/server/tests/utils/user.test.utils.ts index fa5c957722..6a88a006aa 100644 --- a/server/tests/utils/user.test.utils.ts +++ b/server/tests/utils/user.test.utils.ts @@ -196,13 +196,13 @@ export async function saveRecruiter(data: Partial) { } export async function createApplicationTest(data: Partial) { - const u: IApplication = { + const application: IApplication = { ...getFixture().fromSchema(ZApplication), applicant_attachment_name: "my-cv.pdf", ...data, } - await getDbCollection("applications").insertOne(u) - return u + await getDbCollection("applications").insertOne(application) + return application } export async function createEmailBlacklistTest(data: Partial) { diff --git a/shared/fixtures/application.fixture.ts b/shared/fixtures/application.fixture.ts index 35f6f4ba03..f0dd99877a 100644 --- a/shared/fixtures/application.fixture.ts +++ b/shared/fixtures/application.fixture.ts @@ -8,10 +8,6 @@ export function generateApplicationFixture(data: Partial): IApplic return { _id: new ObjectId(), applicant_id: applicant._id, - applicant_email: "applicant@mail.fr", - applicant_first_name: "a", - applicant_last_name: "a", - applicant_phone: "0125252525", applicant_message_to_company: "some blahblahblah", applicant_attachment_name: "cv.pdf", company_recruitment_intention: null, diff --git a/shared/models/applications.model.ts b/shared/models/applications.model.ts index 3d117b85fe..87e6c03f7f 100644 --- a/shared/models/applications.model.ts +++ b/shared/models/applications.model.ts @@ -20,7 +20,7 @@ export enum ApplicationScanStatus { ERROR_APPLICANT_NOT_FOUND = "ERROR_APPLICANT_NOT_FOUND", } -export const ZApplication = z +const ZApplicationOld = z .object({ _id: zObjectId, applicant_id: zObjectId, @@ -77,12 +77,19 @@ export const ZApplication = z .strict() .openapi("Application") +export const ZApplication = ZApplicationOld.omit({ + applicant_email: true, + applicant_first_name: true, + applicant_last_name: true, + applicant_phone: true, +}) + export type IApplication = z.output // KBA 20241011 to remove once V2 is LIVE and V1 support has ended -export const ZNewApplication = ZApplication.extend({ - message: ZApplication.shape.applicant_message_to_company.optional(), - applicant_file_name: ZApplication.shape.applicant_attachment_name, +export const ZNewApplication = ZApplicationOld.extend({ + message: ZApplicationOld.shape.applicant_message_to_company.optional(), + applicant_file_name: ZApplicationOld.shape.applicant_attachment_name, applicant_file_content: z.string().max(4215276).openapi({ description: "Le contenu du fichier du CV du candidat. La taille maximale autorisée est de 3 Mo.", example: "data:application/pdf;base64,JVBERi0xLjQKJ...", @@ -102,7 +109,7 @@ export const ZNewApplication = ZApplication.extend({ }), crypted_company_email: z.string().nullish(), caller: zCallerParam.nullish(), - job_id: ZApplication.shape.job_id.optional(), + job_id: ZApplicationOld.shape.job_id.optional(), searched_for_job_label: z.string().nullish().openapi({ description: "Le métier recherché par le candidat envoyant une candidature spontanée.", example: "Vente de fleurs, végétaux", @@ -126,9 +133,9 @@ export const ZNewApplication = ZApplication.extend({ .openapi("ApplicationUi") // KBA 20241011 to remove once V2 is LIVE and V1 support has ended -const ZNewApplicationTransitionToV2 = ZApplication.extend({ - message: ZApplication.shape.applicant_message_to_company.optional(), - applicant_file_name: ZApplication.shape.applicant_attachment_name, +const ZNewApplicationTransitionToV2 = ZApplicationOld.extend({ + message: ZApplicationOld.shape.applicant_message_to_company.optional(), + applicant_file_name: ZApplicationOld.shape.applicant_attachment_name, applicant_file_content: z.string().max(4_215_276).openapi({ description: "Le contenu du fichier du CV du candidat. La taille maximale autorisée est de 3 Mo.", example: "data:application/pdf;base64,JVBERi0xLjQKJ...", @@ -148,7 +155,7 @@ const ZNewApplicationTransitionToV2 = ZApplication.extend({ }), crypted_company_email: z.string().nullish(), caller: zCallerParam.nullish(), - job_id: ZApplication.shape.job_id.optional(), + job_id: ZApplicationOld.shape.job_id.optional(), searched_for_job_label: z.string().nullish().openapi({ description: "Le métier recherché par le candidat envoyant une candidature spontanée.", example: "Vente de fleurs, végétaux", @@ -175,7 +182,7 @@ export type INewApplicationV1 = z.output type JobCollectionName = "recruteurslba" | "jobs_partners" | "recruiters" -export const ZApplicationApiPrivate = ZApplication.pick({ +export const ZApplicationApiPrivate = ZApplicationOld.pick({ applicant_first_name: true, applicant_last_name: true, applicant_email: true, @@ -184,7 +191,7 @@ export const ZApplicationApiPrivate = ZApplication.pick({ job_searched_by_user: true, caller: true, }).extend({ - applicant_message: ZApplication.shape.applicant_message_to_company.optional(), + applicant_message: ZApplicationOld.shape.applicant_message_to_company.optional(), applicant_attachment_content: z.string().max(4_215_276).describe("Le contenu du fichier du CV du candidat. La taille maximale autorisée est de 3 Mo."), recipient_id: z .string() @@ -216,6 +223,9 @@ export type IApplicationApiPublicJSON = Jsonify