Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Commit

Permalink
1.18.11 Refonte détail des fiches, plausible (#624)
Browse files Browse the repository at this point in the history
  • Loading branch information
alanlr authored May 13, 2022
1 parent 42170aa commit b35f233
Show file tree
Hide file tree
Showing 80 changed files with 2,167 additions and 1,378 deletions.
12 changes: 12 additions & 0 deletions server/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# CHANGELOG

## 1.18.11 [11/05/2022]
[LBA6-32] Contrôle des emails des candidats : pas d'email temporaire
[LBA6-110] Récupération du flag établissement mandataire
[LBA6-32] Proposition fautes de frappes emails
[LBA6-51] Formation : Refonte fiche détaillée
[LBA6-26] Formation : Refonte fiche détaillée entreprise
[LBA6-127] Refonte page stats
[LBA6-32] Limitation nombre de candidatures par jour
[LBA6-112] Events plausible
[LBA6-32] Proposition fautes de frappes emails
[LBA6-99] Infos CFA mandataire

## 1.18.10 [21/04/2022]
[LBA6-49] Api-doc opco
[LBA6-18] Essai Plausible
Expand Down
1 change: 1 addition & 0 deletions server/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"slackWebhookUrl": null,
"outputDir": ".local/output",
"formationsEndPoint": "/api/v1/entity/formations",
"maxApplicationPerDay": 100,
"private":
{
"apiKey": "12345",
Expand Down
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"bunyan-mongodb-stream": "0.0.3",
"bunyan-prettystream": "0.1.3",
"bunyan-slack": "0.0.10",
"burner-email-providers": "1.0.67",
"changelog-parser": "2.8.0",
"compose-middleware": "5.0.1",
"config": "3.3.1",
Expand Down
5 changes: 5 additions & 0 deletions server/src/api-docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,11 @@
"type": "string",
"description": "Lieu de formation seulement : l'uai du lieu de formation"
},
"mandataire":
{
"type": "boolean",
"description": "Indique si l'offre est déposée par un CFA mandataire (offres Matcha seulement)"
},
"headquarter":
{
"type": "object",
Expand Down
5 changes: 5 additions & 0 deletions server/src/common/model/schema/applications.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ const applicationSchema = {
default: null,
description: "Statut du mail envoyé au candidat suite à réponse de l'entreprise",
},
interet_offres_mandataire: {
type: Boolean,
default: false,
description: "Le candidat est il intéressé par les offres du CFA mandataire",
},
created_at: {
type: Date,
default: Date.now,
Expand Down
1 change: 1 addition & 0 deletions server/src/model/itemModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const itemModel = (type) => {
place: null /*{
city, // formation -> etablissement_formateur_localite
}*/,
mandataire: null, // matcha -> mandataire
creationDate: null, // matcha -> date_creation_etablissement
headquarter: null /*{ // uniquement pour formation
siret, // formation -> etablissement_gestionaire_siret
Expand Down
35 changes: 29 additions & 6 deletions server/src/service/applications.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const {
validateCompanyEmail,
validateFeedbackApplication,
validateFeedbackApplicationComment,
validatePermanentEmail,
checkUserApplicationCount,
} = require("./validateSendApplication");
const logger = require("../common/logger");
const publicUrl = config.publicUrl;
Expand Down Expand Up @@ -41,19 +43,20 @@ const images = {
const initApplication = (query, companyEmail) => {
return new Application({
applicant_file_name: query.applicant_file_name,
applicant_email: query.applicant_email,
applicant_email: query.applicant_email.toLowerCase(),
applicant_first_name: query.applicant_first_name,
applicant_last_name: query.applicant_last_name,
applicant_phone: query.applicant_phone,
message: prepareMessageForMail(query.message),
company_siret: query.company_siret,
company_email: companyEmail,
company_email: companyEmail.toLowerCase(),
company_name: query.company_name,
company_naf: query.company_naf,
company_address: query.company_address,
company_type: query.company_type,
job_title: query.job_title,
job_id: query.job_id,
interet_offres_mandataire: query.interet_offres_mandataire,
});
};

Expand Down Expand Up @@ -98,22 +101,42 @@ const sendApplication = async ({ mailer, query, shouldCheckSecret }) => {
} else if (shouldCheckSecret && query.secret !== config.private.secretUpdateRomesMetiers) {
return { error: "wrong_secret" };
} else {
await validateSendApplication({
let validationResult = await validateSendApplication({
fileName: query.applicant_file_name,
email: query.applicant_email,
firstName: query.applicant_first_name,
lastName: query.applicant_last_name,
phone: query.applicant_phone,
});

if (validationResult !== "ok") {
return { error: validationResult };
}

validationResult = await validatePermanentEmail({ email: query.applicant_email });

if (validationResult !== "ok") {
return { error: validationResult };
}

let companyEmail = shouldCheckSecret ? query.company_email : decryptWithIV(query.company_email, query.iv); // utilisation email de test ou decrypt vrai mail crypté
let cryptedEmail = shouldCheckSecret ? decryptWithIV(query.crypted_company_email, query.iv) : ""; // présent uniquement pour les tests utilisateurs

await validateCompanyEmail({
validationResult = await validateCompanyEmail({
companyEmail,
cryptedEmail,
});

if (validationResult !== "ok") {
return { error: validationResult };
}

validationResult = await checkUserApplicationCount(query.applicant_email.toLowerCase());

if (validationResult !== "ok") {
return { error: validationResult };
}

try {
let application = initApplication(query, companyEmail);

Expand Down Expand Up @@ -167,7 +190,7 @@ const sendApplication = async ({ mailer, query, shouldCheckSecret }) => {

return { result: "ok", message: "messages sent" };
} catch (err) {
console.log("err ", err);
logger.error(`Error sending application. Reason : ${err}`);
Sentry.captureException(err);
return { error: "error_sending_application" };
}
Expand Down Expand Up @@ -406,7 +429,7 @@ const addEmailToBlacklist = async (email, source) => {
}).save();
} catch (err) {
// catching unique address error
// do nothing
logger.error(`Failed to save email to blacklist (${email}). Reason : ${err}`);
}
};

Expand Down
3 changes: 3 additions & 0 deletions server/src/service/matcha.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ const transformMatchaJobForIdea = ({ job, distance, caller, contactAllowedOrigin
resultJob.company.siret = job.siret;
resultJob.company.name = job.raison_sociale;
resultJob.company.size = job.tranche_effectif;

resultJob.company.mandataire = job.mandataire;
resultJob.nafs = [{ label: job.libelle_naf }];
resultJob.company.mandataire = job.mandataire;
resultJob.company.creationDate = job.date_creation_etablissement;

resultJob.diplomaLevel = offre.niveau;
Expand Down
50 changes: 45 additions & 5 deletions server/src/service/validateSendApplication.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const Yup = require("yup");
const config = require("config");
const { isEmailBurner } = require("burner-email-providers");
const { Application } = require("../common/model");

const validateSendApplication = async (validable) => {
let schema = Yup.object().shape({
Expand All @@ -10,10 +13,15 @@ const validateSendApplication = async (validable) => {
.matches(/^[0-9]{10}$/, "⚠ Le numéro de téléphone doit avoir exactement 10 chiffres")
.required("⚠ Le téléphone est requis"),
});
await schema.validate(validable).catch(function () {
throw "error - validation of data failed";
let validation = await schema.validate(validable).catch(function () {
return "erreur";
});
return "ok";

if (validation === "erreur") {
return "données de candidature invalides";
} else {
return "ok";
}
};

const validateCompanyEmail = async (validable) => {
Expand All @@ -23,12 +31,42 @@ const validateCompanyEmail = async (validable) => {
.required("⚠ L'adresse e-mail société est requise."),
cryptedEmail: Yup.string().email("⚠ Adresse e-mail chiffrée invalide."),
});
await schema.validate(validable).catch(function () {
throw "error - validation of data failed";
let validation = await schema.validate(validable).catch(function () {
return "erreur";
});
if (validation === "erreur") {
return "email société invalide";
} else {
return "ok";
}
};

const validatePermanentEmail = async (validable) => {
if (isEmailBurner(validable.email)) {
return "email temporaire non autorisé";
}
return "ok";
};

const checkUserApplicationCount = async (applicantEmail) => {
let start = new Date();
start.setHours(0, 0, 0, 0);

let end = new Date();
end.setHours(23, 59, 59, 999);

let appCount = await Application.countDocuments({
applicant_email: applicantEmail.toLowerCase(),
created_at: { $gte: start, $lt: end },
});

if (appCount > config.maxApplicationPerDay) {
return "max candidatures atteint";
} else {
return "ok";
}
};

const validateFeedbackApplication = async (validable) => {
let schema = Yup.object().shape({
id: Yup.string().required("⚠ ID manquant."),
Expand Down Expand Up @@ -75,4 +113,6 @@ module.exports = {
validateFeedbackApplication,
validateFeedbackApplicationComment,
validateIntentionApplication,
validatePermanentEmail,
checkUserApplicationCount,
};
37 changes: 33 additions & 4 deletions server/tests/unit/validateSendApplication-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@ chai.use(require("chai-as-promised"));
const {
validateSendApplication,
validateFeedbackApplication,
validatePermanentEmail,
validateCompanyEmail,
} = require("../../src/service/validateSendApplication.js");
const { decryptWithIV } = require("../../src/common/utils/encryptString");

describe(__filename, () => {
it("validateSendApplication : Echoue si mauvais argument passé en param", async () => {
await expect(validateSendApplication()).to.be.rejectedWith("error - validation of data failed");
expect(await validateSendApplication()).to.equal("données de candidature invalides");
});
it("validateSendApplication : Echoue si un des champs ne passe pas la validation", async () => {
await expect(
validateSendApplication({ lastName: "too long name, more than 15 characters, will fail" })
).to.be.rejectedWith("error - validation of data failed");
expect(await validateSendApplication({ lastName: "too long name, more than 15 characters, will fail" })).to.equal(
"données de candidature invalides"
);
});
it("validateSendApplication : Echoue si un l'email est d'une boîte temporaire", async () => {
expect(await validatePermanentEmail({ email: "test@10minutemail.com" })).to.equal("email temporaire non autorisé");
});
it("validateSendApplication : Succès si l'email n'est pas d'une boîte temporaire", async () => {
expect(await validatePermanentEmail({ email: "test@gmail.com" })).to.equal("ok");
});
it("validateSendApplication : Passe si tous les champs sont valides", async () => {
expect(
Expand All @@ -29,6 +38,26 @@ describe(__filename, () => {
})
).to.equal("ok");
});
it("validateCompanyEmail : Passe si emails cryptés valides", async () => {
let companyEmail = decryptWithIV("28b99996da3c4ae72df064bec394754a3791", "1ac16072b289a73dc1c940b06d728933");

expect(
await validateCompanyEmail({
companyEmail,
cryptedEmail: companyEmail,
})
).to.equal("ok");
});
it("validateCompanyEmail : Passe si emails cryptés valides", async () => {
let companyEmail = decryptWithIV("fake_crypted_email", "1ac16072b289a73dc1c940b06d728933");

expect(
await validateCompanyEmail({
companyEmail,
cryptedEmail: companyEmail,
})
).to.equal("email société invalide");
});
it("validateFeedbackApplication : Echoue si mauvais argument passé en param", async () => {
await expect(validateFeedbackApplication()).to.be.rejectedWith("error - validation of data failed");
});
Expand Down
5 changes: 5 additions & 0 deletions server/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,11 @@ bunyan@1.8.12:
mv "~2"
safe-json-stringify "~1"

burner-email-providers@1.0.67:
version "1.0.67"
resolved "https://registry.yarnpkg.com/burner-email-providers/-/burner-email-providers-1.0.67.tgz#6759dbd1bef2eea6c093bd111d72687e4194c42a"
integrity sha512-w3aWPwragdi/fyGvPFl3Ll4jlbsVecdiC/vsApzyTMReylKBl8w+nN0Oh4fGJanu7Mcn13Xgnrl1OyQk7XEiug==

bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
Expand Down
Loading

0 comments on commit b35f233

Please sign in to comment.