Services
This commit is contained in:
@@ -6,6 +6,7 @@ import { CustomersModule } from './customers/customers.module';
|
|||||||
import configuration from './config/configuration';
|
import configuration from './config/configuration';
|
||||||
import { getTypeOrmOptions } from './database/typeorm-options';
|
import { getTypeOrmOptions } from './database/typeorm-options';
|
||||||
import { SalonsModule } from './salons/salons.module';
|
import { SalonsModule } from './salons/salons.module';
|
||||||
|
import { ServicesModule } from './services/services.module';
|
||||||
import { StylistsModule } from './stylists/stylists.module';
|
import { StylistsModule } from './stylists/stylists.module';
|
||||||
import { UsersModule } from './users/users.module';
|
import { UsersModule } from './users/users.module';
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ import { UsersModule } from './users/users.module';
|
|||||||
UsersModule,
|
UsersModule,
|
||||||
SalonsModule,
|
SalonsModule,
|
||||||
StylistsModule,
|
StylistsModule,
|
||||||
|
ServicesModule,
|
||||||
CustomersModule,
|
CustomersModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Permission } from './entities/permission.entity';
|
|||||||
import { UserPermission } from './entities/user-permission.entity';
|
import { UserPermission } from './entities/user-permission.entity';
|
||||||
import { CustomerPolicy } from './policies/customer.policy';
|
import { CustomerPolicy } from './policies/customer.policy';
|
||||||
import { SalonPolicy } from './policies/salon.policy';
|
import { SalonPolicy } from './policies/salon.policy';
|
||||||
|
import { ServicePolicy } from './policies/service.policy';
|
||||||
import { StylistPolicy } from './policies/stylist.policy';
|
import { StylistPolicy } from './policies/stylist.policy';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -12,12 +13,14 @@ import { StylistPolicy } from './policies/stylist.policy';
|
|||||||
providers: [
|
providers: [
|
||||||
AuthorizationService,
|
AuthorizationService,
|
||||||
SalonPolicy,
|
SalonPolicy,
|
||||||
|
ServicePolicy,
|
||||||
CustomerPolicy,
|
CustomerPolicy,
|
||||||
StylistPolicy,
|
StylistPolicy,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
AuthorizationService,
|
AuthorizationService,
|
||||||
SalonPolicy,
|
SalonPolicy,
|
||||||
|
ServicePolicy,
|
||||||
CustomerPolicy,
|
CustomerPolicy,
|
||||||
StylistPolicy,
|
StylistPolicy,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,4 +10,7 @@ export enum PermissionCode {
|
|||||||
STYLISTS_READ = 'stylists:read',
|
STYLISTS_READ = 'stylists:read',
|
||||||
STYLISTS_WRITE = 'stylists:write',
|
STYLISTS_WRITE = 'stylists:write',
|
||||||
STYLISTS_DELETE = 'stylists:delete',
|
STYLISTS_DELETE = 'stylists:delete',
|
||||||
|
SERVICES_READ = 'services:read',
|
||||||
|
SERVICES_WRITE = 'services:write',
|
||||||
|
SERVICES_DELETE = 'services:delete',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { AuthUser } from '../../auth/interfaces/auth-user.interface';
|
||||||
|
import { AuthorizationService } from '../authorization.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ServicePolicy {
|
||||||
|
constructor(private readonly authorizationService: AuthorizationService) {}
|
||||||
|
|
||||||
|
canManage(user: AuthUser): boolean {
|
||||||
|
return this.authorizationService.isAdmin(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddServices1719800000000 implements MigrationInterface {
|
||||||
|
name = 'AddServices1719800000000';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'services',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'title', type: 'varchar', length: '200' },
|
||||||
|
{ name: 'iconUrl', type: 'varchar', length: '500', isNullable: true },
|
||||||
|
{ name: 'coverUrl', type: 'varchar', length: '500', isNullable: true },
|
||||||
|
{ name: 'createdAt', type: 'timestamp', default: 'now()' },
|
||||||
|
{ name: 'updatedAt', type: 'timestamp', default: 'now()' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'service_skills',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'serviceId', type: 'uuid' },
|
||||||
|
{ name: 'title', type: 'varchar', length: '200' },
|
||||||
|
{ name: 'renewalDays', type: 'int' },
|
||||||
|
{ name: 'createdAt', type: 'timestamp', default: 'now()' },
|
||||||
|
{ name: 'updatedAt', type: 'timestamp', default: 'now()' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
'service_skills',
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ['serviceId'],
|
||||||
|
referencedTableName: 'services',
|
||||||
|
referencedColumnNames: ['id'],
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.dropTable('service_skills', true);
|
||||||
|
await queryRunner.dropTable('services', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { IsInt, IsNotEmpty, IsString, MaxLength, Min } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateServiceSkillDto {
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(200)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
renewalDays: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { IsNotEmpty, IsOptional, IsString, IsUrl, MaxLength } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateServiceDto {
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(200)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@IsUrl()
|
||||||
|
@MaxLength(500)
|
||||||
|
iconUrl?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@IsUrl()
|
||||||
|
@MaxLength(500)
|
||||||
|
coverUrl?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { IsInt, IsOptional, IsString, MaxLength, Min } from 'class-validator';
|
||||||
|
|
||||||
|
export class UpdateServiceSkillDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(200)
|
||||||
|
title?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
renewalDays?: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { IsOptional, IsString, IsUrl, MaxLength } from 'class-validator';
|
||||||
|
|
||||||
|
export class UpdateServiceDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(200)
|
||||||
|
title?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@IsUrl()
|
||||||
|
@MaxLength(500)
|
||||||
|
iconUrl?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@IsUrl()
|
||||||
|
@MaxLength(500)
|
||||||
|
coverUrl?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { Service } from './service.entity';
|
||||||
|
|
||||||
|
@Entity('service_skills')
|
||||||
|
export class ServiceSkill {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
serviceId: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Service, (service) => service.skills, { onDelete: 'CASCADE' })
|
||||||
|
@JoinColumn({ name: 'serviceId' })
|
||||||
|
service: Service;
|
||||||
|
|
||||||
|
@Column({ length: 200 })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@Column({ type: 'int' })
|
||||||
|
renewalDays: number;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
OneToMany,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { ServiceSkill } from './service-skill.entity';
|
||||||
|
|
||||||
|
@Entity('services')
|
||||||
|
export class Service {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ length: 200 })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 500, nullable: true })
|
||||||
|
iconUrl: string | null;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 500, nullable: true })
|
||||||
|
coverUrl: string | null;
|
||||||
|
|
||||||
|
@OneToMany(() => ServiceSkill, (skill) => skill.service)
|
||||||
|
skills: ServiceSkill[];
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
ParseUUIDPipe,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { CurrentUser } from '../auth/decorators/current-user.decorator';
|
||||||
|
import { Public } from '../auth/decorators/public.decorator';
|
||||||
|
import { Roles } from '../auth/decorators/roles.decorator';
|
||||||
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||||
|
import { AuthUser } from '../auth/interfaces/auth-user.interface';
|
||||||
|
import { UserRole } from '../users/enums/user-role.enum';
|
||||||
|
import { CreateServiceSkillDto } from './dto/create-service-skill.dto';
|
||||||
|
import { CreateServiceDto } from './dto/create-service.dto';
|
||||||
|
import { UpdateServiceSkillDto } from './dto/update-service-skill.dto';
|
||||||
|
import { UpdateServiceDto } from './dto/update-service.dto';
|
||||||
|
import { ServicesService } from './services.service';
|
||||||
|
|
||||||
|
@Controller('services')
|
||||||
|
@UseGuards(RolesGuard)
|
||||||
|
export class ServicesController {
|
||||||
|
constructor(private readonly servicesService: ServicesService) {}
|
||||||
|
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
@Post()
|
||||||
|
create(
|
||||||
|
@Body() createServiceDto: CreateServiceDto,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
) {
|
||||||
|
return this.servicesService.create(createServiceDto, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get()
|
||||||
|
findAll() {
|
||||||
|
return this.servicesService.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get(':id')
|
||||||
|
findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||||
|
return this.servicesService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
@Patch(':id')
|
||||||
|
update(
|
||||||
|
@Param('id', ParseUUIDPipe) id: string,
|
||||||
|
@Body() updateServiceDto: UpdateServiceDto,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
) {
|
||||||
|
return this.servicesService.update(id, updateServiceDto, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
@Delete(':id')
|
||||||
|
remove(
|
||||||
|
@Param('id', ParseUUIDPipe) id: string,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
) {
|
||||||
|
return this.servicesService.remove(id, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
@Post(':serviceId/skills')
|
||||||
|
createSkill(
|
||||||
|
@Param('serviceId', ParseUUIDPipe) serviceId: string,
|
||||||
|
@Body() createServiceSkillDto: CreateServiceSkillDto,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
) {
|
||||||
|
return this.servicesService.createSkill(
|
||||||
|
serviceId,
|
||||||
|
createServiceSkillDto,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get(':serviceId/skills')
|
||||||
|
findSkills(@Param('serviceId', ParseUUIDPipe) serviceId: string) {
|
||||||
|
return this.servicesService.findSkills(serviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get(':serviceId/skills/:skillId')
|
||||||
|
findSkill(
|
||||||
|
@Param('serviceId', ParseUUIDPipe) serviceId: string,
|
||||||
|
@Param('skillId', ParseUUIDPipe) skillId: string,
|
||||||
|
) {
|
||||||
|
return this.servicesService.findSkill(serviceId, skillId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
@Patch(':serviceId/skills/:skillId')
|
||||||
|
updateSkill(
|
||||||
|
@Param('serviceId', ParseUUIDPipe) serviceId: string,
|
||||||
|
@Param('skillId', ParseUUIDPipe) skillId: string,
|
||||||
|
@Body() updateServiceSkillDto: UpdateServiceSkillDto,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
) {
|
||||||
|
return this.servicesService.updateSkill(
|
||||||
|
serviceId,
|
||||||
|
skillId,
|
||||||
|
updateServiceSkillDto,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
@Delete(':serviceId/skills/:skillId')
|
||||||
|
removeSkill(
|
||||||
|
@Param('serviceId', ParseUUIDPipe) serviceId: string,
|
||||||
|
@Param('skillId', ParseUUIDPipe) skillId: string,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
) {
|
||||||
|
return this.servicesService.removeSkill(serviceId, skillId, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { AuthGuardsModule } from '../auth/auth-guards.module';
|
||||||
|
import { AuthorizationModule } from '../authorization/authorization.module';
|
||||||
|
import { ServiceSkill } from './entities/service-skill.entity';
|
||||||
|
import { Service } from './entities/service.entity';
|
||||||
|
import { ServicesController } from './services.controller';
|
||||||
|
import { ServicesService } from './services.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([Service, ServiceSkill]),
|
||||||
|
AuthorizationModule,
|
||||||
|
AuthGuardsModule,
|
||||||
|
],
|
||||||
|
controllers: [ServicesController],
|
||||||
|
providers: [ServicesService],
|
||||||
|
exports: [ServicesService],
|
||||||
|
})
|
||||||
|
export class ServicesModule {}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import {
|
||||||
|
ForbiddenException,
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { ServicePolicy } from '../authorization/policies/service.policy';
|
||||||
|
import { AuthUser } from '../auth/interfaces/auth-user.interface';
|
||||||
|
import { CreateServiceSkillDto } from './dto/create-service-skill.dto';
|
||||||
|
import { CreateServiceDto } from './dto/create-service.dto';
|
||||||
|
import { UpdateServiceSkillDto } from './dto/update-service-skill.dto';
|
||||||
|
import { UpdateServiceDto } from './dto/update-service.dto';
|
||||||
|
import { ServiceSkill } from './entities/service-skill.entity';
|
||||||
|
import { Service } from './entities/service.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ServicesService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Service)
|
||||||
|
private readonly servicesRepository: Repository<Service>,
|
||||||
|
@InjectRepository(ServiceSkill)
|
||||||
|
private readonly serviceSkillsRepository: Repository<ServiceSkill>,
|
||||||
|
private readonly servicePolicy: ServicePolicy,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
create(createServiceDto: CreateServiceDto, user: AuthUser): Promise<Service> {
|
||||||
|
this.ensureCanManage(user);
|
||||||
|
const service = this.servicesRepository.create(createServiceDto);
|
||||||
|
return this.servicesRepository.save(service).then((saved) => this.findOne(saved.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
findAll(): Promise<Service[]> {
|
||||||
|
return this.servicesRepository.find({
|
||||||
|
relations: { skills: true },
|
||||||
|
order: { createdAt: 'ASC', skills: { createdAt: 'ASC' } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string): Promise<Service> {
|
||||||
|
const service = await this.servicesRepository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: { skills: true },
|
||||||
|
order: { skills: { createdAt: 'ASC' } },
|
||||||
|
});
|
||||||
|
if (!service) {
|
||||||
|
throw new NotFoundException(`Service #${id} not found`);
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(
|
||||||
|
id: string,
|
||||||
|
updateServiceDto: UpdateServiceDto,
|
||||||
|
user: AuthUser,
|
||||||
|
): Promise<Service> {
|
||||||
|
this.ensureCanManage(user);
|
||||||
|
const service = await this.findOne(id);
|
||||||
|
Object.assign(service, updateServiceDto);
|
||||||
|
await this.servicesRepository.save(service);
|
||||||
|
return this.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string, user: AuthUser): Promise<void> {
|
||||||
|
this.ensureCanManage(user);
|
||||||
|
const service = await this.findOne(id);
|
||||||
|
await this.servicesRepository.remove(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createSkill(
|
||||||
|
serviceId: string,
|
||||||
|
createServiceSkillDto: CreateServiceSkillDto,
|
||||||
|
user: AuthUser,
|
||||||
|
): Promise<ServiceSkill> {
|
||||||
|
this.ensureCanManage(user);
|
||||||
|
await this.findOne(serviceId);
|
||||||
|
const skill = this.serviceSkillsRepository.create({
|
||||||
|
...createServiceSkillDto,
|
||||||
|
serviceId,
|
||||||
|
});
|
||||||
|
const saved = await this.serviceSkillsRepository.save(skill);
|
||||||
|
return this.findSkill(serviceId, saved.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findSkills(serviceId: string): Promise<ServiceSkill[]> {
|
||||||
|
await this.ensureServiceExists(serviceId);
|
||||||
|
return this.serviceSkillsRepository.find({
|
||||||
|
where: { serviceId },
|
||||||
|
order: { createdAt: 'ASC' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findSkill(serviceId: string, skillId: string): Promise<ServiceSkill> {
|
||||||
|
const skill = await this.serviceSkillsRepository.findOne({
|
||||||
|
where: { id: skillId, serviceId },
|
||||||
|
});
|
||||||
|
if (!skill) {
|
||||||
|
throw new NotFoundException(
|
||||||
|
`Service skill #${skillId} not found for service #${serviceId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return skill;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateSkill(
|
||||||
|
serviceId: string,
|
||||||
|
skillId: string,
|
||||||
|
updateServiceSkillDto: UpdateServiceSkillDto,
|
||||||
|
user: AuthUser,
|
||||||
|
): Promise<ServiceSkill> {
|
||||||
|
this.ensureCanManage(user);
|
||||||
|
const skill = await this.findSkill(serviceId, skillId);
|
||||||
|
Object.assign(skill, updateServiceSkillDto);
|
||||||
|
await this.serviceSkillsRepository.save(skill);
|
||||||
|
return this.findSkill(serviceId, skillId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeSkill(
|
||||||
|
serviceId: string,
|
||||||
|
skillId: string,
|
||||||
|
user: AuthUser,
|
||||||
|
): Promise<void> {
|
||||||
|
this.ensureCanManage(user);
|
||||||
|
const skill = await this.findSkill(serviceId, skillId);
|
||||||
|
await this.serviceSkillsRepository.remove(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ensureCanManage(user: AuthUser): void {
|
||||||
|
if (!this.servicePolicy.canManage(user)) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ensureServiceExists(serviceId: string): Promise<void> {
|
||||||
|
const exists = await this.servicesRepository.existsBy({ id: serviceId });
|
||||||
|
if (!exists) {
|
||||||
|
throw new NotFoundException(`Service #${serviceId} not found`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user