refacto user handling

This commit is contained in:
OxSaitama 2023-10-18 18:55:29 +02:00
parent f08b258b8e
commit 40734f70fd
16 changed files with 165 additions and 138 deletions

View File

@ -4,12 +4,11 @@ import ApiController from "@Common/system/controller-pattern/ApiController";
import { Service } from "typedi";
import DocumentsService from "@Services/customer/DocumentsService/DocumentsService";
import { Documents, Prisma } from "@prisma/client";
import { Document } from "le-coffre-resources/dist/Customer";
import { Document, OfficeFolder } from "le-coffre-resources/dist/Customer";
import authHandler from "@App/middlewares/AuthHandler";
import documentHandler from "@App/middlewares/CustomerHandler/DocumentHandler";
import { validateOrReject } from "class-validator";
import OfficeFoldersService from "@Services/super-admin/OfficeFoldersService/OfficeFoldersService";
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
@Controller()
@Service()
@ -30,17 +29,20 @@ export default class DocumentsController extends ApiController {
if (req.query["q"]) {
query = JSON.parse(req.query["q"] as string);
}
const customerId: string = req.body.user.customerId;
if(query.where?.depositor) delete query.where.depositor;
if(query.where?.depositor_uid) delete query.where.depositor_uid;
const customerWhereInput: Prisma.DocumentsWhereInput = { ...query.where, depositor: { uid: customerId } };
const email: string = req.body.user.email;
if (!email) {
this.httpBadRequest(response, "Missing customer email");
return;
}
if (query.where?.depositor) delete query.where.depositor;
if (query.where?.depositor_uid) delete query.where.depositor_uid;
const customerWhereInput: Prisma.DocumentsWhereInput = { ...query.where, depositor: { contact: { email: email } } };
query.where = customerWhereInput;
if (query.include?.folder) delete query.include.folder;
//call service to get prisma entity
const documentEntities: Documents[] = await this.documentsService.get(query);
//Hydrate ressource with prisma entity
const documents = Document.hydrateArray<Document>(documentEntities, { strategy: "excludeAll" });
@ -55,7 +57,7 @@ export default class DocumentsController extends ApiController {
/**
* @description Get a specific document by uid
*/
@Get("/api/v1/customer/documents/:uid",[authHandler,documentHandler])
@Get("/api/v1/customer/documents/:uid", [authHandler, documentHandler])
protected async getOneByUid(req: Request, response: Response) {
try {
const uid = req.params["uid"];
@ -64,9 +66,10 @@ export default class DocumentsController extends ApiController {
return;
}
//get query
let query;
let query: Prisma.DocumentsInclude = {};
if (req.query["q"]) {
query = JSON.parse(req.query["q"] as string);
if (query.folder) delete query.folder;
}
const documentEntity = await this.documentsService.getByUid(uid, query);
@ -91,29 +94,43 @@ export default class DocumentsController extends ApiController {
* @description Create a new File
* @returns File created
*/
@Post("/api/v1/customer/documents", [authHandler])
@Post("/api/v1/customer/documents", [authHandler, documentHandler])
protected async post(req: Request, response: Response) {
try {
//init Document resource with request body values
const documentEntity = Document.hydrate<Document>(req.body);
if(!documentEntity.folder?.uid) {
const email = req.body.user.email;
if (!documentEntity.folder?.uid) {
this.httpBadRequest(response, "No folder uid provided");
return;
}
const folder = await this.officeFoldersService.getByUid(documentEntity.folder.uid, {folder_anchor: true});
if(!folder) {
const folder = await this.officeFoldersService.getByUid(documentEntity.folder.uid, {
folder_anchor: true,
customers: { include: { contact: true } },
});
if (!folder) {
this.httpBadRequest(response, "Folder not found");
return;
}
const folderEntity = OfficeFolder.hydrate<OfficeFolder>(folder);
if (folderEntity.folder_anchor?.status === "VERIFIED_ON_CHAIN") {
this.httpBadRequest(response, "Cannot update a verified folder");
const folderEntity = OfficeFolder.hydrate<OfficeFolder>(folder, { strategy: "excludeAll" });
if (!folderEntity.customers) {
this.httpBadRequest(response, "No customers found in folder");
return;
}
const depositor = folderEntity.customers.find((customer) => customer.contact?.email === email);
delete documentEntity.depositor;
documentEntity.depositor = depositor;
try {
//validate document
await validateOrReject(documentEntity, { groups: ["createDocument"], forbidUnknownValues: false });
} catch (error) {
this.httpValidationError(response, error);
return;
}
//validate document
await validateOrReject(documentEntity, { groups: ["createDocument"], forbidUnknownValues: false });
//call service to get prisma entity
const documentEntityCreated = await this.documentsService.create(documentEntity);

View File

@ -34,6 +34,8 @@ export default class FilesController extends ApiController {
const customerId: string = req.body.user.customerId;
const customerWhereInput: Prisma.FilesWhereInput = { document: { depositor: { uid: customerId } } };
query.where = customerWhereInput;
if(query.include?.document) delete query.include.document;
//call service to get prisma entity
const fileEntities = await this.filesService.get(query);
@ -210,6 +212,7 @@ export default class FilesController extends ApiController {
let query;
if (req.query["q"]) {
query = JSON.parse(req.query["q"] as string);
if(query.document) delete query.document;
}
const fileEntity = await this.filesService.getByUid(uid, query);

View File

@ -30,14 +30,19 @@ export default class OfficeFoldersController extends ApiController {
query = JSON.parse(req.query["q"] as string);
}
const customerId: string = req.body.user.customerId;
if(!customerId) {
this.httpBadRequest(response, "No customerId provided");
const email: string = req.body.user.email;
if (!email) {
this.httpBadRequest(response, "Missing customer email");
return;
}
if(query.where?.customers) delete query.where.customers;
const officeFolderWhereInput: Prisma.OfficeFoldersWhereInput = { ...query.where, customers: { some: { uid: customerId } }};
if (query.where?.customers) delete query.where.customers;
const officeFolderWhereInput: Prisma.OfficeFoldersWhereInput = {
...query.where,
customers: { some: { contact: { email: email } } },
};
query.where = officeFolderWhereInput;
if (query.include) delete query.include;
query.include = { customers: { include: { contact: true } } };
//call service to get prisma entity
const officeFolderEntities: OfficeFolders[] = await this.officeFoldersService.get(query);
@ -46,6 +51,11 @@ export default class OfficeFoldersController extends ApiController {
const officeFolders = OfficeFolder.hydrateArray<OfficeFolder>(officeFolderEntities, {
strategy: "excludeAll",
});
officeFolders.forEach((officeFolder) => {
officeFolder.customers = officeFolder.customers!.filter((customer) => customer.contact?.email === email);
});
//success
this.httpSuccess(response, officeFolders);
} catch (error) {
@ -67,12 +77,13 @@ export default class OfficeFoldersController extends ApiController {
return;
}
let query;
const email: string = req.body.user.email;
let query: Prisma.OfficeFoldersInclude = {};
if (req.query["q"]) {
query = JSON.parse(req.query["q"] as string);
if(query?.customers) {
query.customers = true;
}
if (query?.customers) delete query.customers;
query.customers = { include: { contact: true } };
}
const officeFolderEntity = await this.officeFoldersService.getByUid(uid, query);
@ -84,6 +95,8 @@ export default class OfficeFoldersController extends ApiController {
//Hydrate ressource with prisma entity
const officeFolder = OfficeFolder.hydrate<OfficeFolder>(officeFolderEntity, { strategy: "excludeAll" });
officeFolder.customers = officeFolder.customers!.filter((customer) => customer.contact?.email === email);
//success
this.httpSuccess(response, officeFolder);
} catch (error) {

View File

@ -72,7 +72,7 @@ export default class CustomerController extends ApiController {
}
const customersHydrated = Customer.hydrateArray<Customer>(customer);
const payload = await this.authService.getCustomerJwtPayload(customersHydrated[0]!);
const payload = await this.authService.getCustomerJwtPayload(customersHydrated);
const accessToken = this.authService.generateAccessToken(payload);
const refreshToken = this.authService.generateRefreshToken(payload);
this.httpSuccess(response, { accessToken, refreshToken });

View File

@ -3,36 +3,65 @@ import DocumentsService from "@Services/customer/DocumentsService/DocumentsServi
import Document from "le-coffre-resources/dist/SuperAdmin/Document";
import { NextFunction, Request, Response } from "express";
import Container from "typedi";
import ContactsService from "@Services/common/ContactService/ContactService";
import OfficeFoldersService from "@Services/super-admin/OfficeFoldersService/OfficeFoldersService";
import { OfficeFolder } from "le-coffre-resources/dist/SuperAdmin";
export default async function documentHandler(req: Request, response: Response, next: NextFunction) {
try {
const customerId = req.body.user.customerId;
const customerEmail = req.body.user.email;
const uid = req.path && req.path.split("/")[5];
if (!uid) {
response.status(HttpCodes.BAD_REQUEST).send("Missing document uid");
return;
}
if (uid) {
const documentService = Container.get(DocumentsService);
const document = await documentService.getByUid(uid, { folder: { include: { folder_anchor: true } } });
const documentService = Container.get(DocumentsService);
const document = await documentService.getByUid(uid, { folder: { include: { folder_anchor: true } } });
if (!document) {
response.status(HttpCodes.NOT_FOUND).send("Document not found");
return;
}
if (document?.depositor_uid != customerId) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
}
if (req.method === "POST" || req.method === "PUT") {
const documentEntity = Document.hydrate<Document>(document);
if (documentEntity.folder?.folder_anchor?.status === "VERIFIED_ON_CHAIN") {
response.status(HttpCodes.BAD_REQUEST).send("Cannot update a verified folder");
if (!document) {
response.status(HttpCodes.NOT_FOUND).send("Document not found");
return;
}
if (document?.depositor_uid != customerId) {
const contactService = Container.get(ContactsService);
const customers = await contactService.getByEmail(customerEmail);
if (customers && !customers.find((customer) => customer.uid === document?.depositor_uid)) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
}
}
if (req.method === "PUT" || req.method === "DELETE") {
const documentEntity = Document.hydrate<Document>(document);
if (documentEntity.folder!.folder_anchor?.status === "VERIFIED_ON_CHAIN") {
response.status(HttpCodes.BAD_REQUEST).send("Cannot update a verified folder");
return;
}
}
}
if (req.method === "POST") {
const documentEntity = Document.hydrate<Document>(req.body);
const officeFolderService = Container.get(OfficeFoldersService);
if (documentEntity.folder?.uid) {
const folder = await officeFolderService.getByUid(documentEntity.folder.uid, {
folder_anchor: true,
customers: { include: { contact: true } },
});
if (!folder) {
response.status(HttpCodes.NOT_FOUND).send("Folder not found");
return;
}
const folderEntity = OfficeFolder.hydrate<OfficeFolder>(folder);
if (folderEntity.folder_anchor?.status === "VERIFIED_ON_CHAIN") {
response.status(HttpCodes.BAD_REQUEST).send("Cannot update a verified folder");
return;
}
if (!folderEntity.customers?.find((customer) => customer.contact?.email === customerEmail)) {
response.status(HttpCodes.BAD_REQUEST).send("Cannot post a document in this folder");
return;
}
}
}
next();

View File

@ -5,9 +5,11 @@ import File from "le-coffre-resources/dist/SuperAdmin/File";
import { NextFunction, Request, Response } from "express";
import Container from "typedi";
import { EDocumentStatus } from "@prisma/client";
import CustomersService from "@Services/super-admin/CustomersService/CustomersService";
export default async function fileHandler(req: Request, response: Response, next: NextFunction) {
const customerId = req.body.user.customerId;
const customerEmail = req.body.user.email;
const uid = req.path && req.path.split("/")[5];
const file: string | undefined = req.body["q"];
@ -24,8 +26,12 @@ export default async function fileHandler(req: Request, response: Response, next
return;
}
if (file.document.depositor_uid != customerId) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
const customerService = Container.get(CustomersService);
const customers = await customerService.get({where: {contact: { email: customerEmail}}});
if (customers && !customers.find((customer) => customer.uid === file.document.depositor_uid)) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
}
}
if (req.method === "PUT") {
if (file.document.document_status === EDocumentStatus.VALIDATED) {
@ -43,8 +49,12 @@ export default async function fileHandler(req: Request, response: Response, next
return;
}
if (documentFound.depositor_uid != customerId) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
const customerService = Container.get(CustomersService);
const customers = await customerService.get({where: {contact: { email: customerEmail}}});
if (customers && !customers.find((customer) => customer.uid === documentFound.depositor_uid)) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
}
}
if (documentFound.document_status === EDocumentStatus.VALIDATED) {
response.status(HttpCodes.BAD_REQUEST).send("Cannot update a validated document");

View File

@ -1,10 +1,12 @@
import HttpCodes from "@Common/system/controller-pattern/HttpCodes";
import OfficeFoldersService from "@Services/customer/OfficeFoldersService/OfficeFoldersService";
import CustomersService from "@Services/super-admin/CustomersService/CustomersService";
import { NextFunction, Request, Response } from "express";
import Container from "typedi";
export default async function officeFolderHandler(req: Request, response: Response, next: NextFunction) {
const customerId = req.body.user.customerId;
const customerEmail = req.body.user.email;
const uid = req.path && req.path.split("/")[5];
if (uid) {
@ -15,8 +17,12 @@ export default async function officeFolderHandler(req: Request, response: Respon
return;
}
if (!officeFolder.customers.find((customer) => customer.uid == customerId)) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
const customerService = Container.get(CustomersService);
const customers = await customerService.get({where: {contact: { email: customerEmail}}});
if (customers && !customers.filter((customer) => officeFolder.customers.includes(customer))) {
response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor");
return;
}
}
}

View File

@ -0,0 +1,5 @@
-- DropIndex
DROP INDEX "contacts_cell_phone_number_key";
-- DropIndex
DROP INDEX "contacts_email_key";

View File

@ -35,9 +35,9 @@ model Contacts {
uid String @id @unique @default(uuid())
first_name String @db.VarChar(255)
last_name String @db.VarChar(255)
email String @unique @db.VarChar(255)
email String @db.VarChar(255)
phone_number String? @db.VarChar(50)
cell_phone_number String @unique @db.VarChar(50)
cell_phone_number String @db.VarChar(50)
civility ECivility @default(MALE)
address Addresses? @relation(fields: [address_uid], references: [uid], onDelete: Cascade)
address_uid String? @unique @db.VarChar(255)

View File

@ -18,8 +18,8 @@ export default class ContactRepository extends BaseRepository {
/**
* @description : Find unique customer by email
*/
public async findOneByEmail(email: string): Promise<(Contacts & {customers: Customers | null}) | null> {
return this.model.findUnique({
public async findSomeByEmail(email: string): Promise<(Contacts & {customers: Customers | null})[] | null> {
return this.model.findMany({
where: {
email: email,
},
@ -30,8 +30,8 @@ export default class ContactRepository extends BaseRepository {
/**
* @description : Find unique customer by email
*/
public async findOneByPhoneNumber(cell_phone_number: string): Promise<(Contacts & {customers: Customers | null}) | null> {
return this.model.findUnique({
public async findSomeByPhoneNumber(cell_phone_number: string): Promise<(Contacts & {customers: Customers | null})[] | null> {
return this.model.findMany({
where: {
cell_phone_number: cell_phone_number,
},

View File

@ -1,13 +1,12 @@
import { Customers, Prisma } from "@prisma/client";
import CustomersRepository from "@Repositories/CustomersRepository";
import BaseService from "@Services/BaseService";
import ContactsService from "@Services/common/ContactService/ContactService";
import { Customer } from "le-coffre-resources/dist/Admin";
import { Service } from "typedi";
@Service()
export default class CustomersService extends BaseService {
constructor(private customerRepository: CustomersRepository, private contactService: ContactsService) {
constructor(private customerRepository: CustomersRepository) {
super();
}
@ -24,14 +23,6 @@ export default class CustomersService extends BaseService {
* @throws {Error} If customer cannot be created
*/
public async create(customerEntity: Customer): Promise<Customers> {
const customers = await this.get({
where: {
contact: {
OR: [{ email: customerEntity.contact?.email }, { cell_phone_number: customerEntity.contact?.cell_phone_number }],
},
},
});
if(customers[0]) return customers[0];
return this.customerRepository.create(customerEntity);
}
@ -40,16 +31,6 @@ export default class CustomersService extends BaseService {
* @throws {Error} If customer cannot be modified
*/
public async update(uid: string, customerEntity: Customer): Promise<Customers> {
let errors = [];
if(customerEntity.contact?.email) {
const contactWithSameEmail = await this.contactService.getByEmail(customerEntity.contact.email);
if(contactWithSameEmail && contactWithSameEmail.uid != customerEntity.contact.uid) errors.push({property: "email", constraints: {email: "Email déjà utilisé"}});
}
if(customerEntity.contact?.cell_phone_number) {
const contactWithSamePhoneNumber = await this.contactService.getByPhone(customerEntity.contact.cell_phone_number);
if(contactWithSamePhoneNumber && contactWithSamePhoneNumber.uid != customerEntity.contact.uid) errors.push({property: "cell_phone_number", constraints: {phone: "numéro de téléphone déjà utilisé"}});
}
if(errors.length != 0) throw errors;
return this.customerRepository.update(uid, customerEntity);
}

View File

@ -43,14 +43,16 @@ export default class AuthService extends BaseService {
super();
}
public async getCustomerJwtPayload(customer: Customer): Promise<ICustomerJwtPayload | null> {
if(customer.status === ECustomerStatus["PENDING"]) {
customer.status = ECustomerStatus["VALIDATED"];
this.customerService.update(customer.uid!, customer);
public async getCustomerJwtPayload(customers: Customer[]): Promise<ICustomerJwtPayload | null> {
for (const customer of customers){
if (customer.status === ECustomerStatus["PENDING"]) {
customer.status = ECustomerStatus["VALIDATED"];
await this.customerService.update(customer.uid!, customer);
}
}
return {
customerId: customer.uid!,
email: customer.contact!.email,
customerId: customers[0]!.uid!,
email: customers[0]!.contact!.email,
};
}

View File

@ -13,15 +13,15 @@ export default class ContactsService extends BaseService {
* @description : Get all Contacts
* @throws {Error} If Contacts cannot be get
*/
public async getByEmail(email: string): Promise<(Contacts & {customers: Customers | null}) | null> {
return this.customerRepository.findOneByEmail(email);
public async getByEmail(email: string): Promise<(Contacts & {customers: Customers | null})[] | null> {
return this.customerRepository.findSomeByEmail(email);
}
/**
* @description : Create a new customer
* @throws {Error} If customer cannot be created
*/
public async getByPhone(cell_phone_number: string): Promise<(Contacts & {customers: Customers | null}) | null> {
return this.customerRepository.findOneByPhoneNumber(cell_phone_number);
public async getByPhone(cell_phone_number: string): Promise<(Contacts & {customers: Customers | null})[] | null> {
return this.customerRepository.findSomeByPhoneNumber(cell_phone_number);
}
}

View File

@ -1,13 +1,12 @@
import { Customers, Prisma } from "@prisma/client";
import CustomersRepository from "@Repositories/CustomersRepository";
import BaseService from "@Services/BaseService";
import ContactsService from "@Services/common/ContactService/ContactService";
import { Customer } from "le-coffre-resources/dist/Notary";
import { Service } from "typedi";
@Service()
export default class CustomersService extends BaseService {
constructor(private customerRepository: CustomersRepository, private contactService: ContactsService) {
constructor(private customerRepository: CustomersRepository) {
super();
}
@ -24,14 +23,6 @@ export default class CustomersService extends BaseService {
* @throws {Error} If customer cannot be created
*/
public async create(customerEntity: Customer): Promise<Customers> {
const customers = await this.get({
where: {
contact: {
OR: [{ email: customerEntity.contact?.email }, { cell_phone_number: customerEntity.contact?.cell_phone_number }],
},
},
});
if(customers[0]) return customers[0];
return this.customerRepository.create(customerEntity);
}
@ -40,16 +31,6 @@ export default class CustomersService extends BaseService {
* @throws {Error} If customer cannot be modified
*/
public async update(uid: string, customerEntity: Customer): Promise<Customers> {
let errors = [];
if(customerEntity.contact?.email) {
const contactWithSameEmail = await this.contactService.getByEmail(customerEntity.contact.email);
if(contactWithSameEmail && contactWithSameEmail.uid != customerEntity.contact.uid) errors.push({property: "email", constraints: {email: "mail déjà utilisé"}});
}
if(customerEntity.contact?.cell_phone_number) {
const contactWithSamePhoneNumber = await this.contactService.getByPhone(customerEntity.contact.cell_phone_number);
if(contactWithSamePhoneNumber && contactWithSamePhoneNumber.uid != customerEntity.contact.uid) errors.push({property: "cell_phone_number", constraints: {phone: "numéro de téléphone déjà utilisé"}});
}
if(errors.length != 0) throw errors;
return this.customerRepository.update(uid, customerEntity);
}

View File

@ -1,13 +1,12 @@
import { Customers, Prisma } from "@prisma/client";
import CustomersRepository from "@Repositories/CustomersRepository";
import BaseService from "@Services/BaseService";
import ContactsService from "@Services/common/ContactService/ContactService";
import { Customer } from "le-coffre-resources/dist/SuperAdmin";
import { Service } from "typedi";
@Service()
export default class CustomersService extends BaseService {
constructor(private customerRepository: CustomersRepository, private contactService: ContactsService) {
constructor(private customerRepository: CustomersRepository) {
super();
}
@ -24,14 +23,6 @@ export default class CustomersService extends BaseService {
* @throws {Error} If customer cannot be created
*/
public async create(customerEntity: Customer): Promise<Customers> {
const customers = await this.get({
where: {
contact: {
OR: [{ email: customerEntity.contact?.email }, { cell_phone_number: customerEntity.contact?.cell_phone_number }],
},
},
});
if(customers[0]) return customers[0];
return this.customerRepository.create(customerEntity);
}
@ -40,16 +31,6 @@ export default class CustomersService extends BaseService {
* @throws {Error} If customer cannot be modified
*/
public async update(uid: string, customerEntity: Customer): Promise<Customers> {
let errors = [];
if(customerEntity.contact?.email) {
const contactWithSameEmail = await this.contactService.getByEmail(customerEntity.contact.email);
if(contactWithSameEmail && contactWithSameEmail.uid != customerEntity.contact.uid) errors.push({property: "email", constraints: {email: "mail déjà utilisé"}});
}
if(customerEntity.contact?.cell_phone_number) {
const contactWithSamePhoneNumber = await this.contactService.getByPhone(customerEntity.contact.cell_phone_number);
if(contactWithSamePhoneNumber && contactWithSamePhoneNumber.uid != customerEntity.contact.uid) errors.push({property: "cell_phone_number", constraints: {phone: "numéro de téléphone déjà utilisé"}});
}
if(errors.length != 0) throw errors;
return this.customerRepository.update(uid, customerEntity);
}

View File

@ -6,11 +6,10 @@ import { PrismaClient } from "@prisma/client";
import { customer, customerContact, customerContact_, customer_ } from "@Test/config/MockedData";
import Container from "typedi";
import CustomersRepository from "@Repositories/CustomersRepository";
import ContactService from "@Services/common/ContactService/ContactService";
const prisma = new PrismaClient();
const CustomersServiceTest = new CustomersService(Container.get(CustomersRepository), Container.get(ContactService));
const CustomersServiceTest = new CustomersService(Container.get(CustomersRepository));
afterAll(async () => {
/*