refactor(module_access): changed the dto to use string instead of boolean for module_access and made a mapper to switch between boolean and string.
This commit is contained in:
parent
1afd74b0e5
commit
6b0763f277
43
src/common/mappers/module-access.mapper.ts
Normal file
43
src/common/mappers/module-access.mapper.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
type Modules =
|
||||||
|
| 'timesheets'
|
||||||
|
| 'timesheets_approval'
|
||||||
|
| 'employee_list'
|
||||||
|
| 'employee_management'
|
||||||
|
| 'personal_profile'
|
||||||
|
| 'dashboard';
|
||||||
|
|
||||||
|
export const module_list = [
|
||||||
|
'timesheets',
|
||||||
|
'timesheets_approval',
|
||||||
|
'employee_list',
|
||||||
|
'employee_management',
|
||||||
|
'personal_profile',
|
||||||
|
'dashboard',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const createDefaultModuleAccess = (): Record<Modules, boolean> =>
|
||||||
|
module_list.reduce((acc, mod) => {
|
||||||
|
acc[mod] = false;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<Modules, boolean>);
|
||||||
|
|
||||||
|
|
||||||
|
export const toBooleanFromString = (arr?: readonly string[] | null): Record<Modules, boolean> => {
|
||||||
|
const result = createDefaultModuleAccess();
|
||||||
|
if (!arr || !Array.isArray(arr)) return result;
|
||||||
|
for (const item of arr) {
|
||||||
|
if (typeof item !== 'string') continue;
|
||||||
|
const trimmed = item.trim();
|
||||||
|
if ((module_list as readonly string[]).includes(trimmed)) {
|
||||||
|
result[trimmed as Modules] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toStringFromBoolean = (map: Record<Modules, boolean>): Record<Modules, string | null> => {
|
||||||
|
return module_list.reduce((acc, mod) => {
|
||||||
|
acc[mod] = map[mod] ? mod : null;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<Modules, string | null>);
|
||||||
|
}
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
import { PipeTransform } from "@nestjs/common";
|
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
|
||||||
|
|
||||||
export class ModuleAccessValidationPipe implements PipeTransform {
|
|
||||||
constructor(private readonly prisma: PrismaService) { }
|
|
||||||
|
|
||||||
async transform(value: any) {
|
|
||||||
const { email, access } = value ?? {};
|
|
||||||
const user = await this.prisma.users.findUnique({
|
|
||||||
where: { email },
|
|
||||||
select: {
|
|
||||||
user_module_access: {
|
|
||||||
select: {
|
|
||||||
dashboard: true,
|
|
||||||
employee_list: true,
|
|
||||||
employee_management: true,
|
|
||||||
personal_profile: true,
|
|
||||||
timesheets: true,
|
|
||||||
timesheets_approval: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!Boolean(access)) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
import { Controller, Get, Query, Body, Post } from "@nestjs/common";
|
import { Controller, Get, Query, Body, Post } from "@nestjs/common";
|
||||||
import { Access } from "src/common/decorators/module-access.decorators";
|
import { Access } from "src/common/decorators/module-access.decorators";
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes";
|
|
||||||
import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto";
|
import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto";
|
||||||
import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto";
|
import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto";
|
||||||
import { EmployeesService } from "src/identity-and-account/employees/services/employees.service";
|
import { EmployeesService } from "src/identity-and-account/employees/services/employees.service";
|
||||||
import { AccessGetService } from "src/identity-and-account/user-module-access/services/module-access-get.service";
|
import { AccessGetService } from "src/identity-and-account/user-module-access/services/module-access-get.service";
|
||||||
|
|
||||||
//TODO: create a custom decorator to replace the findModuleAcces call function
|
//TODO: create a custom decorator to replace the findModuleAcces call function
|
||||||
|
|
||||||
@RolesAllowed(...GLOBAL_CONTROLLER_ROLES)
|
|
||||||
@Controller('employees')
|
@Controller('employees')
|
||||||
export class EmployeesController {
|
export class EmployeesController {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -18,7 +16,9 @@ export class EmployeesController {
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@Get('profile')
|
@Get('profile')
|
||||||
async findProfile(@Access('email') email: string, @Query('employee_email') employee_email?: string,
|
async findProfile(
|
||||||
|
@Access('email')email: string,
|
||||||
|
@Query('employee_email') employee_email?: string,
|
||||||
): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
|
): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
|
||||||
const granted_access = await this.accessGetService.findModuleAccess(email);
|
const granted_access = await this.accessGetService.findModuleAccess(email);
|
||||||
if (!granted_access.success) return { success: false, error: 'INVALID_USER' };
|
if (!granted_access.success) return { success: false, error: 'INVALID_USER' };
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { IsBoolean, IsDateString, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPositive, IsString } from 'class-validator';
|
import { IsArray, IsBoolean, IsDateString, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPositive, IsString } from 'class-validator';
|
||||||
import { ModuleAccess } from 'src/identity-and-account/user-module-access/dtos/module-acces.dto';
|
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
export class EmployeeDetailedDto {
|
export class EmployeeDetailedDto {
|
||||||
|
|
@ -16,5 +15,5 @@ export class EmployeeDetailedDto {
|
||||||
@IsDateString() @IsOptional() last_work_day?: string;
|
@IsDateString() @IsOptional() last_work_day?: string;
|
||||||
@IsString() @IsOptional() residence?: string;
|
@IsString() @IsOptional() residence?: string;
|
||||||
@IsInt() @IsPositive() @Type(() => Number) external_payroll_id: number;
|
@IsInt() @IsPositive() @Type(() => Number) external_payroll_id: number;
|
||||||
@Type(() => ModuleAccess) module_access: ModuleAccess;
|
@IsString() @IsArray() user_module_access: string[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { Injectable } from "@nestjs/common";
|
||||||
import { Users } from "@prisma/client";
|
import { Users } from "@prisma/client";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
|
import { module_list, toBooleanFromString, toStringFromBoolean } from "src/common/mappers/module-access.mapper";
|
||||||
import { toStringFromDate } from "src/common/utils/date-utils";
|
import { toStringFromDate } from "src/common/utils/date-utils";
|
||||||
import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto";
|
import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto";
|
||||||
import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto";
|
import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto";
|
||||||
|
|
@ -50,10 +51,7 @@ export class EmployeesService {
|
||||||
supervisor_full_name: `${r.supervisor?.user.first_name} ${r.supervisor?.user.last_name}`,
|
supervisor_full_name: `${r.supervisor?.user.first_name} ${r.supervisor?.user.last_name}`,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
return {
|
return { success: true, data: employee_list }
|
||||||
success: true,
|
|
||||||
data: employee_list,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOwnProfile(email: string): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
|
async findOwnProfile(email: string): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
|
||||||
|
|
@ -157,11 +155,16 @@ export class EmployeesService {
|
||||||
});
|
});
|
||||||
if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` };
|
if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` };
|
||||||
if (!employee.user) return { success: false, error: 'USER_NOT_FOUND' };
|
if (!employee.user) return { success: false, error: 'USER_NOT_FOUND' };
|
||||||
|
if (!employee.user.user_module_access) return { success: false, error: 'UNAUTHORIZED_ACCESS' };
|
||||||
|
|
||||||
let company_name = 'Solucom';
|
let company_name = 'Solucom';
|
||||||
if (employee.company_code === 271583) {
|
if (employee.company_code === 271583) {
|
||||||
company_name = 'Targo';
|
company_name = 'Targo';
|
||||||
}
|
}
|
||||||
|
const stringfy_module_access = toStringFromBoolean(employee.user.user_module_access);
|
||||||
|
const user_module_access_array = module_list
|
||||||
|
.map(mod => stringfy_module_access[mod])
|
||||||
|
.filter((value): value is string => value !== null && value !== undefined && value !== '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -178,15 +181,8 @@ export class EmployeesService {
|
||||||
employee_full_name: `${employee.user.first_name} ${employee.user.last_name}`,
|
employee_full_name: `${employee.user.first_name} ${employee.user.last_name}`,
|
||||||
first_work_day: toStringFromDate(employee.first_work_day),
|
first_work_day: toStringFromDate(employee.first_work_day),
|
||||||
last_work_day: employee.last_work_day ? toStringFromDate(employee.last_work_day) : undefined,
|
last_work_day: employee.last_work_day ? toStringFromDate(employee.last_work_day) : undefined,
|
||||||
supervisor_full_name: `${employee.supervisor?.user.first_name}, ${employee.supervisor?.user.last_name}`,
|
supervisor_full_name: employee.supervisor ? `${employee.supervisor?.user.first_name}, ${employee.supervisor?.user.last_name}` : '',
|
||||||
module_access: {
|
user_module_access: user_module_access_array
|
||||||
dashboard: employee.user.user_module_access?.dashboard ?? false,
|
|
||||||
employee_list: employee.user.user_module_access?.employee_list ?? false,
|
|
||||||
employee_management: employee.user.user_module_access?.employee_management ?? false,
|
|
||||||
personal_profile: employee.user.user_module_access?.personal_profile ?? false,
|
|
||||||
timesheets: employee.user.user_module_access?.timesheets ?? false,
|
|
||||||
timesheets_approval: employee.user.user_module_access?.timesheets_approval ?? false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -196,6 +192,7 @@ export class EmployeesService {
|
||||||
if (dto.company_name === 'Targo') {
|
if (dto.company_name === 'Targo') {
|
||||||
company_code = 271583;
|
company_code = 271583;
|
||||||
}
|
}
|
||||||
|
const normalized_access = toBooleanFromString(dto.user_module_access)
|
||||||
|
|
||||||
await this.prisma.$transaction(async (tx) => {
|
await this.prisma.$transaction(async (tx) => {
|
||||||
const user: Users = await tx.users.create({
|
const user: Users = await tx.users.create({
|
||||||
|
|
@ -207,12 +204,12 @@ export class EmployeesService {
|
||||||
residence: dto.residence,
|
residence: dto.residence,
|
||||||
user_module_access: {
|
user_module_access: {
|
||||||
create: {
|
create: {
|
||||||
dashboard: dto.module_access.dashboard,
|
dashboard: normalized_access.dashboard,
|
||||||
employee_list: dto.module_access.employee_list,
|
employee_list: normalized_access.employee_list,
|
||||||
employee_management: dto.module_access.employee_management,
|
employee_management: normalized_access.employee_management,
|
||||||
personal_profile: dto.module_access.personal_profile,
|
personal_profile: normalized_access.personal_profile,
|
||||||
timesheets: dto.module_access.timesheets,
|
timesheets: normalized_access.timesheets,
|
||||||
timesheets_approval: dto.module_access.timesheets_approval,
|
timesheets_approval: normalized_access.timesheets_approval,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -26,22 +26,12 @@ export abstract class AbstractUserService {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new NotFoundException(`No user with email #${email} exists`);
|
throw new NotFoundException(`No user with email #${email} exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user_module_access = user.user_module_access ?? {
|
|
||||||
dashboard: false,
|
|
||||||
employee_list: false,
|
|
||||||
employee_management: false,
|
|
||||||
personal_profile: false,
|
|
||||||
timesheets: false,
|
|
||||||
timesheets_approval: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const clean_user = {
|
const clean_user = {
|
||||||
first_name: user.first_name,
|
first_name: user.first_name,
|
||||||
last_name: user.last_name,
|
last_name: user.last_name,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
user_module_access,
|
user_module_access: user.user_module_access,
|
||||||
}
|
}
|
||||||
|
|
||||||
return clean_user;
|
return clean_user;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user