From e94d2deadef44fa71f3592e6a8cbbe278b379d8e Mon Sep 17 00:00:00 2001 From: Vins Date: Wed, 4 Oct 2023 08:54:48 +0200 Subject: [PATCH 1/5] Customers emails limited --- src/common/emails/EmailBuilder.ts | 5 +++++ src/services/common/MailchimpService/MailchimpService.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common/emails/EmailBuilder.ts b/src/common/emails/EmailBuilder.ts index bb41d029..9aec7bc5 100644 --- a/src/common/emails/EmailBuilder.ts +++ b/src/common/emails/EmailBuilder.ts @@ -17,6 +17,11 @@ export default class EmailBuilder { const documentPrisma = await this.documentsService.getByUid(documentEntity.uid, { depositor: {include: {contact: true}}, folder:{include:{ office: true}} }); if(!documentPrisma) throw new Error("Document not found"); const document = Document.hydrate(documentPrisma); + + //Use mailchimpService.get get if an email was sent to the user in the lst hour + const lastEmail = await this.mailchimpService.get({ where: { to: document.depositor!.contact!.email, sentAt: { gte: new Date(Date.now() - 3600000) } } }); + if(lastEmail.length > 0) return; + const to = document.depositor!.contact!.email; const civility = this.getCivility(document.depositor!.contact!.civility); const templateVariables = { diff --git a/src/services/common/MailchimpService/MailchimpService.ts b/src/services/common/MailchimpService/MailchimpService.ts index 8f9f5f5e..8eb603bf 100644 --- a/src/services/common/MailchimpService/MailchimpService.ts +++ b/src/services/common/MailchimpService/MailchimpService.ts @@ -1,6 +1,6 @@ import EmailRepository from "@Repositories/EmailRepository"; import BaseService from "@Services/BaseService"; -import { Emails } from "@prisma/client"; +import { Emails, Prisma } from "@prisma/client"; import { Service } from "typedi"; import MailchimpClient from "@mailchimp/mailchimp_transactional"; import { BackendVariables } from "@Common/config/variables/Variables"; @@ -24,7 +24,7 @@ export default class MailchimpService extends BaseService { * @description : Get all emails * @throws {Error} If emails cannot be get */ - public async get(query: any): Promise { + public async get(query: Prisma.EmailsFindManyArgs): Promise { return this.emailRepository.findMany(query); } From 95bcb05340d44c03a8704cc987234073c23dc614 Mon Sep 17 00:00:00 2001 From: Maxime Lalo Date: Wed, 4 Oct 2023 17:23:32 +0200 Subject: [PATCH 2/5] :sparkles: Refacto archive/restore --- src/app/api/notary/OfficeFoldersController.ts | 120 ++++++++++++++++-- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/src/app/api/notary/OfficeFoldersController.ts b/src/app/api/notary/OfficeFoldersController.ts index f0b19ead..5df72822 100644 --- a/src/app/api/notary/OfficeFoldersController.ts +++ b/src/app/api/notary/OfficeFoldersController.ts @@ -44,9 +44,9 @@ export default class OfficeFoldersController extends ApiController { customers: { some: { OR: [ - {contact: { first_name: { contains: filter, mode: "insensitive" } }}, - {contact: { last_name: { contains: filter, mode: "insensitive" } }}, - ] + { contact: { first_name: { contains: filter, mode: "insensitive" } } }, + { contact: { last_name: { contains: filter, mode: "insensitive" } } }, + ], }, }, }, @@ -56,8 +56,8 @@ export default class OfficeFoldersController extends ApiController { } const userId: string = req.body.user.userId; - if(query.where?.stakeholders) delete query.where.stakeholders; - const officeFoldersWhereInput: Prisma.OfficeFoldersWhereInput = { ...query.where, stakeholders: {some: {uid: userId }}}; + if (query.where?.stakeholders) delete query.where.stakeholders; + const officeFoldersWhereInput: Prisma.OfficeFoldersWhereInput = { ...query.where, stakeholders: { some: { uid: userId } } }; query.where = officeFoldersWhereInput; //call service to get prisma entity @@ -111,7 +111,9 @@ export default class OfficeFoldersController extends ApiController { return; } - const officeFolderFound = await this.officeFoldersService.getByUid(uid); + const officeFolderFound = await this.officeFoldersService.getByUid(uid, { + folder_anchor: true, + }); if (!officeFolderFound) { this.httpNotFoundRequest(response, "office folder not found"); @@ -119,13 +121,17 @@ export default class OfficeFoldersController extends ApiController { } //init OfficeFolder resource with request body values - const officeFolderEntity = OfficeFolder.hydrate(req.body); + const officefolderToUpdate = OfficeFolder.hydrate(req.body); //validate folder - await validateOrReject(officeFolderEntity, { groups: ["updateFolder"], forbidUnknownValues: false }); + await validateOrReject(officefolderToUpdate, { groups: ["updateFolder"], forbidUnknownValues: false }); + if ((officeFolderFound as any).folder_anchor?.status === "VERIFIED_ON_CHAIN") { + this.httpBadRequest(response, "Cannot update a verified folder"); + return; + } //call service to get prisma entity - const officeFolderEntityUpdated = await this.officeFoldersService.update(uid, officeFolderEntity); + const officeFolderEntityUpdated = await this.officeFoldersService.update(uid, officefolderToUpdate); //Hydrate ressource with prisma entity const officeFolders = OfficeFolder.hydrate(officeFolderEntityUpdated, { @@ -140,6 +146,102 @@ export default class OfficeFoldersController extends ApiController { } } + /** + * @description Modify a specific folder by uid + */ + @Put("/api/v1/notary/folders/:uid/archive", [authHandler, ruleHandler, folderHandler]) + protected async archive(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const officeFolderFound = await this.officeFoldersService.getByUid(uid, { + folder_anchor: true, + }); + + if (!officeFolderFound) { + this.httpNotFoundRequest(response, "office folder not found"); + return; + } + + //init OfficeFolder resource with request body values + const officefolderToUpdate = OfficeFolder.hydrate(officeFolderFound); + + officefolderToUpdate.status = "ARCHIVED"; + officefolderToUpdate.archived_description = req.body.archived_description ?? ""; + + //validate folder + await validateOrReject(officefolderToUpdate, { groups: ["updateFolder"], forbidUnknownValues: false }); + + if ((officeFolderFound as any).folder_anchor?.status !== "VERIFIED_ON_CHAIN") { + this.httpBadRequest(response, "Cannot archive a not anchored folder"); + return; + } + + //call service to get prisma entity + const officeFolderEntityUpdated = await this.officeFoldersService.update(uid, officefolderToUpdate); + + //Hydrate ressource with prisma entity + const officeFolders = OfficeFolder.hydrate(officeFolderEntityUpdated, { + strategy: "excludeAll", + }); + + //success + this.httpSuccess(response, officeFolders); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Modify a specific folder by uid + */ + @Put("/api/v1/notary/folders/:uid/restore", [authHandler, ruleHandler, folderHandler]) + protected async restore(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const officeFolderFound = await this.officeFoldersService.getByUid(uid, { + folder_anchor: true, + }); + + if (!officeFolderFound) { + this.httpNotFoundRequest(response, "office folder not found"); + return; + } + + //init OfficeFolder resource with request body values + const officefolderToUpdate = OfficeFolder.hydrate(officeFolderFound); + + officefolderToUpdate.status = "LIVE"; + officefolderToUpdate.archived_description = ""; + + //validate folder + await validateOrReject(officefolderToUpdate, { groups: ["updateFolder"], forbidUnknownValues: false }); + + //call service to get prisma entity + const officeFolderEntityUpdated = await this.officeFoldersService.update(uid, officefolderToUpdate); + + //Hydrate ressource with prisma entity + const officeFolders = OfficeFolder.hydrate(officeFolderEntityUpdated, { + strategy: "excludeAll", + }); + + //success + this.httpSuccess(response, officeFolders); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } /** * @description Get a specific folder by uid * @returns IFolder From 2e47e7a8209785c37a21d65940fb4ce2a777e5f0 Mon Sep 17 00:00:00 2001 From: Maxime Lalo Date: Wed, 4 Oct 2023 17:45:03 +0200 Subject: [PATCH 3/5] :sparkles: Cannot ask documents on an anchored folder --- src/app/api/notary/DocumentsController.ts | 29 ++++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/app/api/notary/DocumentsController.ts b/src/app/api/notary/DocumentsController.ts index 0be80d12..9c75455d 100644 --- a/src/app/api/notary/DocumentsController.ts +++ b/src/app/api/notary/DocumentsController.ts @@ -4,18 +4,23 @@ import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; import DocumentsService from "@Services/notary/DocumentsService/DocumentsService"; import { Documents, Prisma } from "@prisma/client"; -import { Document } from "le-coffre-resources/dist/Notary"; +import { Document, OfficeFolder } from "le-coffre-resources/dist/Notary"; import { validateOrReject } from "class-validator"; import authHandler from "@App/middlewares/AuthHandler"; import ruleHandler from "@App/middlewares/RulesHandler"; import documentHandler from "@App/middlewares/OfficeMembershipHandlers/DocumentHandler"; import EmailBuilder from "@Common/emails/EmailBuilder"; +import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; // import NotificationBuilder from "@Common/notifications/NotificationBuilder"; @Controller() @Service() export default class DocumentsController extends ApiController { - constructor(private documentsService: DocumentsService, private emailBuilder: EmailBuilder) { + constructor( + private documentsService: DocumentsService, + private emailBuilder: EmailBuilder, + private officeFoldersService: OfficeFoldersService, + ) { super(); } @@ -32,8 +37,8 @@ export default class DocumentsController extends ApiController { query = JSON.parse(req.query["q"] as string); } const officeId: string = req.body.user.office_Id; - const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId } ; - if(!query.where) query.where = { document_type : {office: officeWhereInput}}; + const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId }; + if (!query.where) query.where = { document_type: { office: officeWhereInput } }; query.where.document_type!.office = officeWhereInput; //call service to get prisma entity @@ -60,6 +65,20 @@ export default class DocumentsController extends ApiController { //init Document resource with request body values const documentEntity = Document.hydrate(req.body); + const folder = await this.officeFoldersService.getByUid(documentEntity.folder?.uid!, { + folder_anchor: true, + }); + if (!folder) { + this.httpBadRequest(response, "Folder not found"); + return; + } + + const folderRessource = OfficeFolder.hydrate(folder); + if (folderRessource.folder_anchor) { + this.httpBadRequest(response, "Cannot ask document on an anchored or anchoring folder"); + return; + } + //validate document await validateOrReject(documentEntity, { groups: ["createDocument"], forbidUnknownValues: false }); @@ -104,8 +123,6 @@ export default class DocumentsController extends ApiController { //init Document resource with request body values const documentEntity = Document.hydrate(req.body); - - //validate document await validateOrReject(documentEntity, { groups: ["updateDocument"] }); From 46cc08eecae69ff101b4b58a06e0b8cc5801e918 Mon Sep 17 00:00:00 2001 From: Maxime Lalo Date: Wed, 4 Oct 2023 17:47:05 +0200 Subject: [PATCH 4/5] :bug: Removing any type --- src/app/api/notary/OfficeFoldersController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/api/notary/OfficeFoldersController.ts b/src/app/api/notary/OfficeFoldersController.ts index 5df72822..e6f113a0 100644 --- a/src/app/api/notary/OfficeFoldersController.ts +++ b/src/app/api/notary/OfficeFoldersController.ts @@ -122,11 +122,11 @@ export default class OfficeFoldersController extends ApiController { //init OfficeFolder resource with request body values const officefolderToUpdate = OfficeFolder.hydrate(req.body); - + const officeFolderFoundRessource = OfficeFolder.hydrate(officeFolderFound); //validate folder await validateOrReject(officefolderToUpdate, { groups: ["updateFolder"], forbidUnknownValues: false }); - if ((officeFolderFound as any).folder_anchor?.status === "VERIFIED_ON_CHAIN") { + if (officeFolderFoundRessource.folder_anchor?.status === "VERIFIED_ON_CHAIN") { this.httpBadRequest(response, "Cannot update a verified folder"); return; } From 1f339299bbf7f9e366f1c0009314f7fd9e733211 Mon Sep 17 00:00:00 2001 From: OxSaitama Date: Wed, 4 Oct 2023 19:25:20 +0200 Subject: [PATCH 5/5] refacto votes and notifications --- package.json | 2 +- src/app/api/id360/CustomerController.ts | 8 ++++-- src/app/api/id360/DocumentController.ts | 2 -- src/app/api/idnot/UserController.ts | 4 +++ src/app/api/super-admin/LiveVoteController.ts | 8 ++---- .../notifications/NotificationBuilder.ts | 28 +++++++++---------- .../repositories/AppointmentsRepository.ts | 18 ++++++++++-- src/common/repositories/VotesRepository.ts | 8 +++--- .../common/Id360Service/Id360Service.ts | 5 ++-- .../LiveVoteService/LiveVoteService.ts | 28 +++++++++++++------ 10 files changed, 67 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index d64f06cf..c0ec46b8 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "express": "^4.18.2", "fp-ts": "^2.16.1", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.89", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.90", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/app/api/id360/CustomerController.ts b/src/app/api/id360/CustomerController.ts index fc234983..475b5bb6 100644 --- a/src/app/api/id360/CustomerController.ts +++ b/src/app/api/id360/CustomerController.ts @@ -2,7 +2,7 @@ import { Response, Request } from "express"; import { Controller, Post } from "@ControllerPattern/index"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; -import Id360Service from "@Services/common/Id360Service/Id360Service"; +import Id360Service, { EnrollmentResponse } from "@Services/common/Id360Service/Id360Service"; import CustomersService from "@Services/customer/CustomersService/CustomersService"; import AuthService from "@Services/common/AuthService/AuthService"; import { Customer } from "le-coffre-resources/dist/SuperAdmin"; @@ -34,7 +34,11 @@ export default class CustomerController extends ApiController { return; } try { - const enrollment = await this.id360Service.getEnrollment(callbackToken); + const res = await this.id360Service.getEnrollment(callbackToken); + const enrollment = await res.json() as EnrollmentResponse; + if(enrollment.status === "STARTED") { + this.loginCallback(req, response); + } if (enrollment.status !== "OK") { this.httpUnauthorized(response, "Enrollment status is not OK"); return; diff --git a/src/app/api/id360/DocumentController.ts b/src/app/api/id360/DocumentController.ts index 1b8d43ca..b0285212 100644 --- a/src/app/api/id360/DocumentController.ts +++ b/src/app/api/id360/DocumentController.ts @@ -20,7 +20,6 @@ export default class DocumentController extends ApiController { protected async getDocumentVerificationFromId360(req: Request, response: Response) { try { - console.log("document callback", req, response) this.httpSuccess(response); } catch (error) { console.log(error); @@ -33,7 +32,6 @@ export default class DocumentController extends ApiController { protected async getCustomerVerificationFromId360(req: Request, response: Response) { try { - console.log("customer callback", req, response) this.httpSuccess(response); } catch (error) { console.log(error); diff --git a/src/app/api/idnot/UserController.ts b/src/app/api/idnot/UserController.ts index 102af93a..9952e029 100644 --- a/src/app/api/idnot/UserController.ts +++ b/src/app/api/idnot/UserController.ts @@ -25,6 +25,10 @@ export default class UserController extends ApiController { if (!code) throw new Error("code is required"); const idNotToken = await this.idNotService.getIdNotToken(code); + if(!idNotToken) { + this.httpValidationError(response, "IdNot token undefined"); + return; + } const user = await this.idNotService.getOrCreateUser(idNotToken); if(!user) { diff --git a/src/app/api/super-admin/LiveVoteController.ts b/src/app/api/super-admin/LiveVoteController.ts index 65956b50..295ab1a7 100644 --- a/src/app/api/super-admin/LiveVoteController.ts +++ b/src/app/api/super-admin/LiveVoteController.ts @@ -30,7 +30,7 @@ export default class LiveVoteController extends ApiController { try { const userId = req.body.user.userId; //init IUser resource with request body values - const voteEntity = Vote.hydrate(req.body); + const voteEntity = Vote.hydrate(req.body, { strategy: "excludeAll" }); //validate user await validateOrReject(voteEntity, { groups: ["createVote"] }); @@ -54,7 +54,7 @@ export default class LiveVoteController extends ApiController { AND: [ { appointment: { - AND: [{ user_uid: voteEntity.appointment.targeted_user.uid }, { status: EAppointmentStatus.OPEN }], + AND: [{ user_uid: voteEntity.appointment.user.uid }, { status: EAppointmentStatus.OPEN }], }, }, { voter: { uid: userId } }, @@ -79,9 +79,7 @@ export default class LiveVoteController extends ApiController { return; } //Hydrate ressource with prisma entity - const vote = Vote.hydrate(voteEntityCreated, { - strategy: "excludeAll", - }); + const vote = Vote.hydrate(voteEntityCreated, { strategy: "excludeAll" }); await this.notificationBuilder.sendVoteNotification(vote); diff --git a/src/common/notifications/NotificationBuilder.ts b/src/common/notifications/NotificationBuilder.ts index 3db177a2..30e88fc1 100644 --- a/src/common/notifications/NotificationBuilder.ts +++ b/src/common/notifications/NotificationBuilder.ts @@ -14,7 +14,7 @@ export default class NotificationBuilder { private documentsService: DocumentsService, private usersService: UsersService, private foldersService: OfficeFoldersService, - private backendVariables: BackendVariables + private backendVariables: BackendVariables, ) {} public async sendDocumentDepositedNotification(documentEntity: Documents) { @@ -42,10 +42,12 @@ export default class NotificationBuilder { } public async sendFolderAnchoredNotification(folderEntity: OfficeFolders) { - if(!folderEntity.uid) return; - const officeFolderPrisma = await this.foldersService.getByUid(folderEntity.uid, - { folder_anchor: true, office: true, stakeholders: true } - ); + if (!folderEntity.uid) return; + const officeFolderPrisma = await this.foldersService.getByUid(folderEntity.uid, { + folder_anchor: true, + office: true, + stakeholders: true, + }); if (!officeFolderPrisma) throw new Error("Folder not found"); const folder = OfficeFolder.hydrate(officeFolderPrisma); if (folder.folder_anchor?.status !== "VERIFIED_ON_CHAIN") return; @@ -66,23 +68,19 @@ export default class NotificationBuilder { public async sendVoteNotification(vote: Vote) { if (vote.appointment.status !== "OPEN") return; - const superAdminList = await this.usersService.get({ where: { role: { label: "super-admin" } } }); + const superAdminList = await this.usersService.get({ where: { role: { name: "super-admin" } } }); + const userTargeted = await this.usersService.getByUid(vote.appointment.user.uid!, { contact: true }); + const userTargetedEntity = User.hydrate(userTargeted!, { strategy: "excludeAll" }); let message = ""; if (vote.appointment.choice === "NOMINATE") { - message = - "Un collaborateur souhaite attribuer le titre de Super Administrateur à " + - vote.appointment.targeted_user + - ". Cliquez ici pour voter."; + message = `Un collaborateur souhaite attribuer le titre de Super Administrateur à ${userTargetedEntity.contact?.first_name} ${userTargetedEntity.contact?.last_name}. Cliquez ici pour voter.`; } else if (vote.appointment.choice === "DISMISS") { - message = - "Un collaborateur souhaite retirer le titre de Super Administrateur à " + - vote.appointment.targeted_user + - ". Cliquez ici pour voter."; + message = `Un collaborateur souhaite retirer le titre de Super Administrateur à ${userTargetedEntity.contact?.first_name} ${userTargetedEntity.contact?.last_name}. Cliquez ici pour voter.`; } this.notificationsService.create({ message: message, - redirection_url: `${this.backendVariables.APP_HOST}/users/${vote.appointment.targeted_user.uid}`, + redirection_url: `${this.backendVariables.APP_HOST}/users/${vote.appointment.user.uid}`, created_at: new Date(), updated_at: new Date(), user: superAdminList || [], diff --git a/src/common/repositories/AppointmentsRepository.ts b/src/common/repositories/AppointmentsRepository.ts index 62b74d34..dcb1e660 100644 --- a/src/common/repositories/AppointmentsRepository.ts +++ b/src/common/repositories/AppointmentsRepository.ts @@ -1,7 +1,7 @@ import Database from "@Common/databases/database"; import BaseRepository from "@Repositories/BaseRepository"; import { Service } from "typedi"; -import { Appointments, EAppointmentStatus, Prisma } from "@prisma/client"; +import { Appointments, EAppointmentStatus, EVote, Prisma, Users, Votes } from "@prisma/client"; @Service() export default class AppointmentsRepository extends BaseRepository { @@ -39,6 +39,18 @@ export default class AppointmentsRepository extends BaseRepository { return this.model.update(updateArgs); } + public async findOneByStatusUserAndChoice(userUid: string, choice: EVote, status: EAppointmentStatus): Promise { + return this.model.findUnique({ + where: { + user_uid_choice_status: { + user_uid: userUid, + choice: choice, + status: status, + }, + }, + }); + } + /** * @description : Find one appointment */ @@ -54,12 +66,12 @@ export default class AppointmentsRepository extends BaseRepository { /** * @description : Find one appointment with votes */ - public async findOneByUidWithVotes(uid: string) { + public async findOneByUidWithVotes(uid: string): Promise<(Appointments & {votes: Votes[], user: Users}) | null> { return this.model.findUnique({ where: { uid: uid, }, - include: {votes: true}, + include: {votes: true, user: true}, }); } diff --git a/src/common/repositories/VotesRepository.ts b/src/common/repositories/VotesRepository.ts index a9bf7746..d5f2bf55 100644 --- a/src/common/repositories/VotesRepository.ts +++ b/src/common/repositories/VotesRepository.ts @@ -29,10 +29,10 @@ export default class VotesRepository extends BaseRepository { */ public async create(vote: Vote): Promise { let whereArg: Prisma.AppointmentsWhereUniqueInput; - if(vote.appointment.targeted_user.uid) { + if(vote.appointment.user.uid) { whereArg = { user_uid_choice_status: { - user_uid: vote.appointment.targeted_user.uid, + user_uid: vote.appointment.user.uid, choice: EVote[vote.appointment.choice as keyof typeof EVote], status: EAppointmentStatus.OPEN, } @@ -49,7 +49,7 @@ export default class VotesRepository extends BaseRepository { where: whereArg, create: { choice: EVote[vote.appointment.choice as keyof typeof EVote], - user_uid: vote.appointment.targeted_user.uid!, + user_uid: vote.appointment.user.uid!, } }, }, @@ -61,7 +61,7 @@ export default class VotesRepository extends BaseRepository { } }; - return this.model.create({...createArgs, include: {appointment: {include: {votes: true}}}}); + return this.model.create({...createArgs, include: {appointment: {include: {votes: true, user: true}}}}); } /** diff --git a/src/services/common/Id360Service/Id360Service.ts b/src/services/common/Id360Service/Id360Service.ts index 0271396d..9a98fe39 100644 --- a/src/services/common/Id360Service/Id360Service.ts +++ b/src/services/common/Id360Service/Id360Service.ts @@ -4,7 +4,7 @@ import { BackendVariables } from "@Common/config/variables/Variables"; import DocumentsService from "@Services/super-admin/DocumentsService/DocumentsService"; import FilesService from "../FilesService/FilesService"; -type EnrollmentResponse = { +export type EnrollmentResponse = { url: string; id: number; api_key: string; @@ -196,13 +196,12 @@ export default class Id360Service extends BaseService { } public async getEnrollment(token: string) { - const res = await fetch( + return await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/status/${token}`, { method: "GET" }, ); - return (await res.json()) as EnrollmentResponse; } public async finalizeEnrollment(apiKey: string) { diff --git a/src/services/super-admin/LiveVoteService/LiveVoteService.ts b/src/services/super-admin/LiveVoteService/LiveVoteService.ts index a034f976..65aaa726 100644 --- a/src/services/super-admin/LiveVoteService/LiveVoteService.ts +++ b/src/services/super-admin/LiveVoteService/LiveVoteService.ts @@ -2,10 +2,11 @@ import BaseService from "@Services/BaseService"; import { Service } from "typedi"; import User, { Appointment, Role, Vote } from "le-coffre-resources/dist/SuperAdmin"; import VotesRepository from "@Repositories/VotesRepository"; -import { Appointments, EAppointmentStatus, EVote, Prisma, Votes } from "@prisma/client"; +import { Appointments, EAppointmentStatus, EVote, Prisma, Users, Votes } from "@prisma/client"; import UsersService from "../UsersService/UsersService"; import RolesService from "../RolesService/RolesService"; import AppointmentsRepository from "@Repositories/AppointmentsRepository"; +import NotificationBuilder from "@Common/notifications/NotificationBuilder"; @Service() export default class LiveVoteService extends BaseService { @@ -14,12 +15,13 @@ export default class LiveVoteService extends BaseService { private appointmentRepository: AppointmentsRepository, private userService: UsersService, private roleService: RolesService, + private notificationBuilder: NotificationBuilder, ) { super(); } public async verifyVoterChoice(vote: Vote): Promise { - const userWithRole = await this.userService.getByUidWithRole(vote.appointment.targeted_user.uid!); + const userWithRole = await this.userService.getByUidWithRole(vote.appointment.user.uid!); if (userWithRole!.role.name === "super-admin" && vote.appointment.choice === EVote.DISMISS) { return true; } @@ -65,27 +67,31 @@ export default class LiveVoteService extends BaseService { return this.appointmentRepository.findOneByUid(uid, query); } - public async getAppointmentWithVotes(vote: Vote): Promise { + public async getAppointmentWithVotes(vote: Vote): Promise<(Appointments & {votes: Votes[], user: Users}) | null> { if (vote.appointment.uid) { - return this.appointmentRepository.findOneByUid(vote.appointment.uid); + return this.appointmentRepository.findOneByUidWithVotes(vote.appointment.uid); } const appointmentByUser = await this.appointmentRepository.findMany({ where: { AND: [ - { user_uid: vote.appointment.targeted_user.uid }, + { user_uid: vote.appointment.user.uid }, { status: EAppointmentStatus.OPEN }, { choice: EVote[vote.appointment.choice as keyof typeof EVote] }, ], }, - include: { votes: true }, + include: { votes: true, user: true }, }); - if (appointmentByUser.length >= 1) { + if (appointmentByUser.length != 0) { return this.appointmentRepository.findOneByUidWithVotes(appointmentByUser[0]!.uid); } return null; } private async closeVote(appointment: Appointments, vote: Votes) { + const apointmentFound = await this.appointmentRepository.findOneByStatusUserAndChoice(appointment.user_uid, EVote[appointment.choice as keyof typeof EVote], EAppointmentStatus.CLOSED); + if(apointmentFound) { + await this.appointmentRepository.delete(apointmentFound.uid); + } await this.appointmentRepository.update(vote.appointment_uid, EAppointmentStatus.CLOSED); const user = await this.userService.getByUid(appointment.user_uid, { role: true }); const userEntity = User.hydrate(user!, { strategy: "excludeAll" }); @@ -98,12 +104,14 @@ export default class LiveVoteService extends BaseService { const roles = await this.roleService.get({ where: { name: "default" } }); const roleEntity = Role.hydrate(roles[0]!, { strategy: "excludeAll" }); userEntity.role = roleEntity; + await this.notificationBuilder.sendDismissNotification(userEntity); await this.userService.update(appointment!.user_uid, userEntity); return vote; } else if (appointment.choice === EVote.NOMINATE) { const roles = await this.roleService.get({ where: { name: "super-admin" } }); const roleEntity = Role.hydrate(roles[0]!, { strategy: "excludeAll" }); userEntity!.role = roleEntity; + await this.notificationBuilder.sendNominateNotification(userEntity); await this.userService.update(appointment!.user_uid, userEntity); return vote; } @@ -118,8 +126,10 @@ export default class LiveVoteService extends BaseService { const appointment = await this.getAppointmentWithVotes(vote); if (appointment) { - const appointmentEntity = Appointment.hydrate(appointment, { strategy: "excludeAll" }); - if (appointmentEntity?.votes && appointmentEntity.votes.length >= 2) { + const voteEntity = Vote.hydrateArray(appointment.votes, { strategy: "excludeAll" }); + const appointementWithVotesHydrated = {...appointment, votes: voteEntity}; + const appointmentEntity = Appointment.hydrate(appointementWithVotesHydrated, { strategy: "excludeAll" }); + if (appointmentEntity.votes && appointmentEntity.votes.length >= 2) { const voteCreated = await this.voteRepository.create(vote); return this.closeVote(appointment, voteCreated); }