import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; import { InjectRepository } from '@mikro-orm/nestjs'; import { EntityRepository } from '@mikro-orm/core'; import { Role } from '../entities/role.entity'; import { Permission } from '../entities/permission.entity'; import { Restaurant } from '../../restaurants/entities/restaurant.entity'; import { EntityManager } from '@mikro-orm/postgresql'; import { CreateRoleDto } from '../dto/create-role.dto'; import { UpdateRoleDto } from '../dto/update-role.dto'; import { FindRolesDto } from '../dto/find-roles.dto'; import { RolePermission } from '../entities/rolePermission.entity'; @Injectable() export class RolesService { constructor( @InjectRepository(Role) private readonly roleRepository: EntityRepository, @InjectRepository(Permission) private readonly permissionRepository: EntityRepository, private readonly em: EntityManager, ) {} async create(restId: string, dto: CreateRoleDto) { const { name, title, permissionIds } = dto; // Check if role already exists const existing = await this.roleRepository.findOne({ name, restaurant: restId ? { id: restId } : null }); if (existing) { throw new BadRequestException('Role with this name already exists for the restaurant'); } let restaurant: Restaurant | null = null; if (restId) { restaurant = await this.em.findOne(Restaurant, { id: restId }); if (!restaurant) { throw new NotFoundException('Restaurant not found'); } } const role = this.roleRepository.create({ name, title, restaurant, }); // Add permissions if provided if (permissionIds && permissionIds.length > 0) { const permissions = await this.permissionRepository.find({ id: { $in: permissionIds } }); if (permissions.length !== permissionIds.length) { throw new BadRequestException('One or more permissions not found'); } permissions.forEach(p => role.permissions.add(p)); } await this.em.persistAndFlush(role); return role; } async findAll(restId: string, dto: FindRolesDto) { const { name, page = 1, limit = 20 } = dto; const offset = (page - 1) * limit; // Build query dynamically const query: Record = restId ? { restaurant: { id: restId } } : {}; // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const [roles, count] = await this.roleRepository.findAndCount(query as any, { offset, limit, populate: ['permissions', 'restaurant'], }); // Filter by name or title in memory if provided let filtered = roles; if (name) { filtered = roles.filter( r => r.name.toLowerCase().includes(name.toLowerCase()) || r.title.toLowerCase().includes(name.toLowerCase()), ); } return { data: filtered, total: count, page, limit, totalPages: Math.ceil(count / limit), }; } async findOne(restId: string, id: string) { const role = await this.roleRepository.findOne( { id, restaurant: { id: restId } }, { populate: ['permissions', 'restaurant'] }, ); if (!role) { throw new NotFoundException('Role not found'); } return role; } async update(restId: string, id: string, dto: UpdateRoleDto) { const role = await this.roleRepository.findOne( { id, restaurant: { id: restId } }, { populate: ['permissions', 'restaurant'] }, ); if (!role) { throw new NotFoundException('Role not found'); } if (dto.name) { role.name = dto.name; } if (dto.title) { role.title = dto.title; } if (dto.permissionIds && dto.permissionIds.length >= 0) { // Clear existing permissions and add new ones role.permissions.removeAll(); const permissions = await this.permissionRepository.find({ id: { $in: dto.permissionIds } }); if (permissions.length !== dto.permissionIds.length) { throw new BadRequestException('One or more permissions not found'); } permissions.forEach(p => role.permissions.add(p)); } await this.em.persistAndFlush(role); return role; } async remove(restId: string, id: string) { const role = await this.roleRepository.findOne( { id, restaurant: { id: restId } }, { populate: ['permissions', 'restaurant'] }, ); if (!role) { throw new NotFoundException('Role not found'); } if (!role.admins.isEmpty()) { throw new BadRequestException('Role has admins'); } // Hard delete pivot table entries (role_permissions) before soft deleting the role await this.em.nativeDelete(RolePermission, { role: { id: role.id } }); // Soft delete the role role.deletedAt = new Date(); return this.em.persistAndFlush(role); } }