From 4d86b20e8492320b0815a99735b43562490f9e5f Mon Sep 17 00:00:00 2001 From: ignavan39 Date: Thu, 23 Mar 2023 00:28:07 +0600 Subject: [PATCH] [feat]: refactored pipeline module --- src/auth/entities/refresh-token.entity.ts | 14 +++ src/dashboard/dashboard.controller.ts | 18 +-- src/pipeline/pipeline.controller.ts | 122 ++++--------------- src/pipeline/pipeline.exceptions.ts | 3 + src/pipeline/pipeline.service.ts | 135 ++++++++++++++++++++++ src/user/user.controller.ts | 10 +- 6 files changed, 190 insertions(+), 112 deletions(-) create mode 100644 src/auth/entities/refresh-token.entity.ts create mode 100644 src/pipeline/pipeline.exceptions.ts create mode 100644 src/pipeline/pipeline.service.ts diff --git a/src/auth/entities/refresh-token.entity.ts b/src/auth/entities/refresh-token.entity.ts new file mode 100644 index 0000000..035086a --- /dev/null +++ b/src/auth/entities/refresh-token.entity.ts @@ -0,0 +1,14 @@ +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { User } from '../../user/entities/user.entity'; + +@Entity('refresh_token') +export class RefreshToken extends BaseEntity { + @Column('text') + token: string; + + @Column('timestamp') + expiresIn: Date; + + @ManyToOne(() => User) + user: User; +} diff --git a/src/dashboard/dashboard.controller.ts b/src/dashboard/dashboard.controller.ts index f1a6dbf..d039d87 100644 --- a/src/dashboard/dashboard.controller.ts +++ b/src/dashboard/dashboard.controller.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common'; +import { BadRequestException, Body, Controller, Delete, Get, Inject, Param, Patch, Post, UseGuards } from '@nestjs/common'; import { IAM } from '../common/decorators'; import { JwtAuthGuard } from '../user/guards/jwt.guard'; import { @@ -16,12 +16,12 @@ import { DashboardPermissionGuard } from './guards/dashboard-permission.guard'; @UseGuards(JwtAuthGuard) @Controller('dashboard') export class DashboardController { - constructor(private readonly service: DashboardService) {} + constructor(@Inject(DashboardService) private readonly dashboardService: DashboardService) {} @Post('/create') async create(@Body() body: CreateDashboardDto, @IAM('id') userId: string) { try { - const dashboard = await this.service.create(body.name, userId); + const dashboard = await this.dashboardService.create(body.name, userId); return dashboard; } catch (e) { @@ -34,32 +34,32 @@ export class DashboardController { @Get('/') async getByUserId(@IAM('id') userId: string): Promise { - return this.service.getByUserId(userId); + return this.dashboardService.getByUserId(userId); } @UseGuards(DashboardPermissionGuard()) @Get('/:id') async getById(@Param('id') id: string, @IAM('id') userId: string) { - return this.service.getById(id); + return this.dashboardService.getById(id); } @UseGuards(DashboardPermissionGuard(PermissionType.Admin)) @Patch('/:id') async update(@Param('id') id: string, @Body() body: UpdateDashboardDto): Promise { - await this.service.update(id, body.name); + await this.dashboardService.update(id, body.name); } @UseGuards(DashboardPermissionGuard(PermissionType.Admin)) @Delete('/:id') async delete(@Param('id') id: string, @IAM('id') userId: string): Promise { - await this.service.delete(id, userId); + await this.dashboardService.delete(id, userId); } @UseGuards(DashboardPermissionGuard(PermissionType.Admin)) @Delete('/:id/permission/:email') async deletePermission(@Param('id') id: string, @Param('email') email: string): Promise { try { - await this.service.deletePermission(id, email); + await this.dashboardService.deletePermission(id, email); } catch (e) { if (e instanceof PermissionNotFoundException) { throw new BadRequestException(e.message); @@ -72,7 +72,7 @@ export class DashboardController { @Post('/:id/permission/add') async addAccess(@Param('id') id: string, @IAM('id') userId: string, @Body() body: AddPermissionDto): Promise { try { - await this.service.addAccess(id, userId, body); + await this.dashboardService.addAccess(id, userId, body); } catch (e) { if (e instanceof PermissionNotFoundException) { throw new BadRequestException(e.message); diff --git a/src/pipeline/pipeline.controller.ts b/src/pipeline/pipeline.controller.ts index 1ef4213..bb33093 100644 --- a/src/pipeline/pipeline.controller.ts +++ b/src/pipeline/pipeline.controller.ts @@ -10,9 +10,11 @@ import { Delete, BadRequestException, Get, + Inject, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; +import { isViolatedUniqueConstraintError } from 'src/common/utils/database-helpers'; import { Permission, PermissionType } from '../dashboard/entities/permission.entity'; import { DashboardPermissionGuard } from '../dashboard/guards/dashboard-permission.guard'; import { JwtAuthGuard } from '../user/guards/jwt.guard'; @@ -21,36 +23,22 @@ import { MovePipelineDto } from './dto/move-pipeline.dto'; import { UpdatePipelineDto } from './dto/update-pipeline.dto'; import { Pipeline } from './entities/pipeline.entity'; import { PipelinePermissionGuard } from './guards/pipeline-permission.guard'; +import { PipelineService } from './pipeline.service'; +import { PipelineAlreadyExist, PipelineNotFoundException, UnableToMovePipeline } from './pipeline.exceptions'; @UseGuards(JwtAuthGuard) @Controller('pipeline') export class PipelineController { - constructor( - @InjectRepository(Pipeline) private readonly repository: Repository, - @InjectRepository(Permission) private readonly permissionRepository: Repository, - ) {} + constructor(@Inject(PipelineService) private readonly pipelineService: PipelineService) {} @UseGuards(DashboardPermissionGuard(PermissionType.Admin)) @Post('/create') async create(@Body() args: CreatePipelineDto) { - const pipelines = await this.repository.find({ - where: { - dashboardId: args.dashboardId, - }, - order: { - order: 'DESC', - }, - }); - const lastOrder = pipelines.length === 0 ? 0 : pipelines[0].order; try { - return await this.repository.save({ - name: args.name, - dashboardId: args.dashboardId, - order: lastOrder + 1, - }); + return this.pipelineService.create(args); } catch (e) { - if ('detail' in e && e.detail.includes('already exists')) { - throw new ForbiddenException('pipeline with this name already exist'); + if (e instanceof PipelineAlreadyExist) { + throw new ForbiddenException(e.message); } } } @@ -58,101 +46,39 @@ export class PipelineController { @UseGuards(PipelinePermissionGuard(PermissionType.Admin)) @Patch('/:id') public async update(@Body() args: UpdatePipelineDto, @Param('id') id: string) { - await this.repository.update( - { - id, - }, - { - name: args.name, - }, - ); + await this.pipelineService.update(args, id); } @UseGuards(PipelinePermissionGuard(PermissionType.Admin)) @Delete('/:id') public async delete(@Param('id') id: string) { - const pipeline = await this.repository.findOne({ - where: { - id, - }, - }); - if (!pipeline) { - throw new ForbiddenException(); + try { + await this.pipelineService.delete(id); + } catch (e) { + if (e instanceof PipelineNotFoundException) { + throw new ForbiddenException(e.message); + } } - - await this.repository.query( - ` - UPDATE pipeline SET "order" = "order" - 1 - WHERE "order" > $1 - AND dashboard_id = $2 - `, - [pipeline.order, pipeline.dashboardId], - ); - - await this.repository.delete({ - id: pipeline.id, - }); } @UseGuards(PipelinePermissionGuard(PermissionType.Admin)) @Patch('/move/:id') public async move(@Body() args: MovePipelineDto, @Param('id') id: string) { - const pipeline = await this.repository.findOne({ - where: { - id, - }, - }); - if (!pipeline) { - throw new ForbiddenException(); - } - - let oldOrder = 0; - - if (args.leftId) { - const leftTemplate = await this.repository.findOne({ - where: { - id: args.leftId, - }, - }); - if (!leftTemplate) { - throw new BadRequestException('pipeline not found'); + try { + await this.pipelineService.move(args, id); + } catch (e) { + if (e instanceof PipelineNotFoundException) { + throw new ForbiddenException(e.message); + } + if (e instanceof UnableToMovePipeline) { + throw new BadRequestException(e.message); } - - oldOrder = leftTemplate.order; - } - - if (oldOrder > pipeline.order) { - await this.repository.query( - `UPDATE pipeline SET "order" = "order" - 1 - WHERE "order" > $1 - AND "order" <= $2 - AND dashboard_id = $2`, - [pipeline.order, oldOrder, pipeline.dashboardId], - ); - pipeline.order = oldOrder; - } else { - await this.repository.query( - `UPDATE pipeline SET "order" = "order" + 1 - WHERE "order" > $1 - AND "order" < $2 - AND dashboard_id = $2`, - [oldOrder, pipeline.order, pipeline.dashboardId], - ); - pipeline.order = oldOrder + 1; } - await this.repository.save(pipeline); } @UseGuards(DashboardPermissionGuard()) @Get('/:dashboardId') public async getAllByDashboardId(@Param('dashboardId') dashboardId: string) { - return this.repository.find({ - where: { - dashboardId, - }, - order: { - order: 'ASC', - }, - }); + return this.pipelineService.getAllByDashboardId(dashboardId); } } diff --git a/src/pipeline/pipeline.exceptions.ts b/src/pipeline/pipeline.exceptions.ts new file mode 100644 index 0000000..8ea7488 --- /dev/null +++ b/src/pipeline/pipeline.exceptions.ts @@ -0,0 +1,3 @@ +export class PipelineNotFoundException extends Error {} +export class UnableToMovePipeline extends Error {} +export class PipelineAlreadyExist extends Error {} diff --git a/src/pipeline/pipeline.service.ts b/src/pipeline/pipeline.service.ts new file mode 100644 index 0000000..e009e4e --- /dev/null +++ b/src/pipeline/pipeline.service.ts @@ -0,0 +1,135 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Permission } from '../dashboard/entities/permission.entity'; +import { isViolatedUniqueConstraintError } from '../common/utils/database-helpers'; +import { Pipeline } from './entities/pipeline.entity'; +import { CreatePipelineDto } from './dto/create-pipeline.dto'; +import { UpdatePipelineDto } from './dto/update-pipeline.dto'; +import { MovePipelineDto } from './dto/move-pipeline.dto'; +import { PipelineAlreadyExist, PipelineNotFoundException, UnableToMovePipeline } from './pipeline.exceptions'; + +@Injectable() +export class PipelineService { + constructor( + @InjectRepository(Pipeline) private readonly pipelineRepository: Repository, + @InjectRepository(Permission) private readonly permissionRepository: Repository, + ) {} + + async create(dto: CreatePipelineDto): Promise { + const pipelines = await this.pipelineRepository.find({ + where: { + dashboardId: dto.dashboardId, + }, + order: { + order: 'DESC', + }, + }); + const lastOrder = pipelines.length === 0 ? 0 : pipelines[0].order; + try { + return await this.pipelineRepository.save({ + name: dto.name, + dashboardId: dto.dashboardId, + order: lastOrder + 1, + }); + } catch (e) { + if (isViolatedUniqueConstraintError(e)) { + throw new PipelineAlreadyExist(); + } + throw e; + } + } + + async update(dto: UpdatePipelineDto, id: string): Promise { + await this.pipelineRepository.update( + { + id, + }, + { + name: dto.name, + }, + ); + } + + async delete(id: string): Promise { + const pipeline = await this.pipelineRepository.findOne({ + where: { + id, + }, + }); + if (!pipeline) { + throw new PipelineNotFoundException('pipeline not found'); + } + + await this.pipelineRepository.query( + ` + UPDATE pipeline SET "order" = "order" - 1 + WHERE "order" > $1 + AND dashboard_id = $2 + `, + [pipeline.order, pipeline.dashboardId], + ); + + await this.pipelineRepository.delete({ + id: pipeline.id, + }); + } + + async move(dto: MovePipelineDto, id: string): Promise { + const pipeline = await this.pipelineRepository.findOne({ + where: { + id, + }, + }); + if (!pipeline) { + throw new PipelineNotFoundException('pipeline not found'); + } + + let oldOrder = 0; + + if (dto.leftId) { + const leftPipeline = await this.pipelineRepository.findOne({ + where: { + id: dto.leftId, + }, + }); + if (!leftPipeline) { + throw new UnableToMovePipeline('unable to move pipeline'); + } + + oldOrder = leftPipeline.order; + } + + if (oldOrder > pipeline.order) { + await this.pipelineRepository.query( + `UPDATE pipeline SET "order" = "order" - 1 + WHERE "order" > $1 + AND "order" <= $2 + AND dashboard_id = $2`, + [pipeline.order, oldOrder, pipeline.dashboardId], + ); + pipeline.order = oldOrder; + } else { + await this.pipelineRepository.query( + `UPDATE pipeline SET "order" = "order" + 1 + WHERE "order" > $1 + AND "order" < $2 + AND dashboard_id = $2`, + [oldOrder, pipeline.order, pipeline.dashboardId], + ); + pipeline.order = oldOrder + 1; + } + await this.pipelineRepository.save(pipeline); + } + + async getAllByDashboardId(dashboardId: string): Promise { + return this.pipelineRepository.find({ + where: { + dashboardId, + }, + order: { + order: 'ASC', + }, + }); + } +} diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index ca60d2a..d358ef2 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, ForbiddenException, Get, NotFoundException, Post, UseGuards } from '@nestjs/common'; +import { Body, Controller, ForbiddenException, Get, Inject, NotFoundException, Post, UseGuards } from '@nestjs/common'; import * as _ from 'lodash'; import { IAM } from '../common/decorators'; import { SignInDto, AuthCommonResponse, SignUpDto } from './dto'; @@ -9,12 +9,12 @@ import { User } from './entities/user.entity'; @Controller('user') export class UsersController { - constructor(private readonly service: UserService) {} + constructor(@Inject(UserService) private readonly userService: UserService) {} @Post('/signIn') async signIn(@Body() body: SignInDto): Promise { try { - return await this.service.signIn(body); + return await this.userService.signIn(body); } catch (e) { if (e instanceof UserNotFoundException) { throw new NotFoundException(e.message); @@ -26,7 +26,7 @@ export class UsersController { @Post('/signUp') async signUp(@Body() body: SignUpDto): Promise { try { - return await this.service.signUp(body); + return await this.userService.signUp(body); } catch (e) { if (e instanceof UserAlreadyExistException) { throw new ForbiddenException('user with this email already exist'); @@ -38,6 +38,6 @@ export class UsersController { @UseGuards(JwtAuthGuard) @Get('/getCurrent') async getCurrent(@IAM('id') userId: string): Promise { - return this.service.getCurrent(userId); + return this.userService.getCurrent(userId); } }