Skip to content

Commit

Permalink
Refactor api response and error code
Browse files Browse the repository at this point in the history
  • Loading branch information
thongnguyen5 committed Apr 12, 2024
1 parent 501399b commit c1c4a13
Show file tree
Hide file tree
Showing 17 changed files with 218 additions and 175 deletions.
2 changes: 2 additions & 0 deletions app/apps/onebox/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AllExceptionsFilter } from '@app/global/global.exception';
import { ResponseInterceptor } from '@app/global/global.interceptor';
import { ValidationPipe } from '@nestjs/common';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
Expand Down Expand Up @@ -46,6 +47,7 @@ async function bootstrap() {
const httpAdapter = app.get(HttpAdapterHost);
app.useGlobalPipes(new ValidationPipe({ transform: true }));
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
app.useGlobalInterceptors(new ResponseInterceptor());

await app.listen(process.env.SERVER_PORT);
}
Expand Down
3 changes: 2 additions & 1 deletion app/apps/onebox/src/modules/address/address.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { MonitorModule as MonitorModelModule } from '@app/shared_modules/monitor/monitor.module';
import { ProjectModule as ProjectModelModule } from '@app/shared_modules/project/project.module';
import { Module } from '@nestjs/common';
import { MonitorModule } from '../monitor/monitor.module';
import { MonitorAddressController } from './address.controller';
import { MonitorAddressService } from './address.service';

@Module({
controllers: [MonitorAddressController],
providers: [MonitorAddressService],
exports: [MonitorAddressService],
imports: [MonitorModelModule, ProjectModelModule],
imports: [MonitorModelModule, ProjectModelModule, MonitorModule],
})
export class MonitorAddressModule {}
42 changes: 18 additions & 24 deletions app/apps/onebox/src/modules/address/address.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { ServiceException } from '@app/global/global.exception';
import { MonitorAddressRepository } from '@app/shared_modules/monitor/repositories/monitor.address.repository';
import { MonitorRepository } from '@app/shared_modules/monitor/repositories/monitor.repository';
import { MonitorAddress } from '@app/shared_modules/monitor/schemas/monitor.address.schema';
import { Monitor } from '@app/shared_modules/monitor/schemas/monitor.schema';
import { ProjectMemberRepository } from '@app/shared_modules/project/repositories/project.member.repository';
import { Injectable } from '@nestjs/common';
import { Builder } from 'builder-pattern';
import { MonitorService } from '../monitor/monitor.service';
import { User } from '../users/schemas/user.schema';
import {
CreateMonitorAddressDto,
Expand All @@ -22,31 +21,17 @@ export class MonitorAddressService {
constructor(
private readonly projectMemberRepository: ProjectMemberRepository,
private readonly monitorRepository: MonitorRepository,
private readonly monitorService: MonitorService,
) {}

private async findAndAuthMonitor(
user: User,
monitorId: string,
): Promise<Monitor> {
const monitor = await this.monitorRepository.findById(monitorId);
if (!monitor) {
throw new ServiceException('monitor not found', 404);
}
const member = this.projectMemberRepository.findByUserAndProject(
user.userId,
monitor.projectId,
);
if (!member) {
throw new ServiceException('unauthorized', 401);
}
return monitor;
}

async createMonitorAddress(
user: User,
request: CreateMonitorAddressDto,
): Promise<MonitorAddressResponseDto[]> {
const monitor = await this.findAndAuthMonitor(user, request.monitorId);
const monitor = await this.monitorService.findAndAuthMonitor(
user,
request.monitorId,
);
const addresses = request.addresses.map((address) =>
Builder<MonitorAddress>()
.projectId(monitor.projectId)
Expand All @@ -68,7 +53,10 @@ export class MonitorAddressService {
user: User,
request: GetMonitorAddressRequestDto,
): Promise<GetMonitorAddressResponseDto> {
const monitor = await this.findAndAuthMonitor(user, request.monitorId);
const monitor = await this.monitorService.findAndAuthMonitor(
user,
request.monitorId,
);
return MonitorAddressRepository.getRepository(monitor.network)
.getMonitorAddress(monitor.monitorId, request.limit, request.offset)
.then((addresses) =>
Expand All @@ -84,7 +72,10 @@ export class MonitorAddressService {
user: User,
request: DeleteMonitorAddressDto,
): Promise<DeleteMonitorAddressResponseDto> {
const monitor = await this.findAndAuthMonitor(user, request.monitorId);
const monitor = await this.monitorService.findAndAuthMonitor(
user,
request.monitorId,
);
// lower case request.addresses
request.addresses = request.addresses.map((address) =>
address.toLowerCase(),
Expand All @@ -100,7 +91,10 @@ export class MonitorAddressService {
user: User,
request: SearchMonitorAddressRequestDto,
): Promise<GetMonitorAddressResponseDto> {
const monitor = await this.findAndAuthMonitor(user, request.monitorId);
const monitor = await this.monitorService.findAndAuthMonitor(
user,
request.monitorId,
);
return MonitorAddressRepository.getRepository(monitor.network)
.findAddressByMonitorAndAddress(
monitor.monitorId,
Expand Down
3 changes: 2 additions & 1 deletion app/apps/onebox/src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ErrorCode } from '@app/global/global.error';
import { comparePassword } from '@app/utils/bcrypt.util';
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
Expand All @@ -17,7 +18,7 @@ export class AuthService {
if (user && (await comparePassword(pass, user.password))) {
return user;
}
return null;
throw ErrorCode.WRONG_EMAIL_OR_PASSWORD.asException();
}

async login(user: User): Promise<LoginResponseDto> {
Expand Down
7 changes: 4 additions & 3 deletions app/apps/onebox/src/modules/auth/strategies/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { ErrorCode } from '@app/global/global.error';
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UsersService } from '../../users/users.service';
Expand All @@ -16,10 +17,10 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
async validate(payload: any) {
const user = await this.usersService.findOneByUserId(payload.userId);
if (!user) {
throw new UnauthorizedException();
throw ErrorCode.UNAUTHORIZED.asException();
}
if (user.passwordHash !== payload.token) {
throw new UnauthorizedException();
throw ErrorCode.UNAUTHORIZED.asException();
}
return user;
}
Expand Down
7 changes: 4 additions & 3 deletions app/apps/onebox/src/modules/auth/strategies/local.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Strategy } from 'passport-local';
import { ErrorCode } from '@app/global/global.error';
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { Strategy } from 'passport-local';
import { AuthService } from '../auth.service';

@Injectable()
Expand All @@ -14,7 +15,7 @@ export class LocalStrategy extends PassportStrategy(Strategy) {
async validate(email: string, password: string): Promise<any> {
const user = await this.authService.validateUser(email, password);
if (!user) {
throw new UnauthorizedException();
throw ErrorCode.UNAUTHORIZED.asException();
}
return user;
}
Expand Down
10 changes: 8 additions & 2 deletions app/apps/onebox/src/modules/monitor/monitor.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { MonitorModule as MonitorModelModule } from '@app/shared_modules/monitor/monitor.module';
import { ProjectModule as ProjectModelModule } from '@app/shared_modules/project/project.module';
import { WebhookModule } from '@app/shared_modules/webhook/webhook.module';
import { Module } from '@nestjs/common';
import { ProjectModule } from '../project/project.module';
import { MonitorController } from './monitor.controller';
import { MonitorService } from './monitor.service';
import { WebhookModule } from '@app/shared_modules/webhook/webhook.module';
@Module({
controllers: [MonitorController],
providers: [MonitorService],
exports: [MonitorService],
imports: [MonitorModelModule, ProjectModelModule, WebhookModule],
imports: [
MonitorModelModule,
ProjectModelModule,
WebhookModule,
ProjectModule,
],
})
export class MonitorModule {}
66 changes: 18 additions & 48 deletions app/apps/onebox/src/modules/monitor/monitor.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ServiceException } from '@app/global/global.exception';
import { ErrorCode } from '@app/global/global.error';
import { MonitorAddressRepository } from '@app/shared_modules/monitor/repositories/monitor.address.repository';
import { MonitorRepository } from '@app/shared_modules/monitor/repositories/monitor.repository';
import {
Monitor,
MonitorNotificationMethod,
WebhookNotification,
} from '@app/shared_modules/monitor/schemas/monitor.schema';
Expand All @@ -10,6 +11,7 @@ import { ProjectRepository } from '@app/shared_modules/project/repositories/proj
import { WebhookService } from '@app/shared_modules/webhook/webhook.service';
import { Injectable } from '@nestjs/common';
import { Builder } from 'builder-pattern';
import { ProjectService } from '../project/project.service';
import { User } from '../users/schemas/user.schema';
import {
CreateMonitorDto,
Expand All @@ -26,51 +28,39 @@ export class MonitorService {
private readonly projectRepository: ProjectRepository,
private readonly monitorRepository: MonitorRepository,
private readonly webhookService: WebhookService,
private readonly projectService: ProjectService,
) {}

async findAndAuthMonitor(user: User, monitorId: string): Promise<Monitor> {
const monitor = await this.monitorRepository.findById(monitorId);
if (!monitor) {
throw ErrorCode.MONITOR_NOT_FOUND.asException();
}
await this.projectService.checkProjectPermission(user, monitor.projectId);
return monitor;
}

async listMonitors(
user: User,
projectId: string,
): Promise<MonitorResponseDto[]> {
const member = await this.projectMemberRepository.findByUserAndProject(
user.userId,
projectId,
);
if (!member) {
throw new Error('not authorized');
}
this.projectService.checkProjectPermission(user, projectId);
const monitors = await this.monitorRepository.listMonitorsByProject(
projectId,
);
return monitors.map((monitor) => MonitorResponseDto.from(monitor));
}

async getMonitor(user: User, monitorId: string): Promise<MonitorResponseDto> {
const monitor = await this.monitorRepository.findById(monitorId);
if (!monitor) {
throw new ServiceException('monitor not found', 404);
}
const member = await this.projectMemberRepository.findByUserAndProject(
user.userId,
monitor.projectId,
);
if (!member) {
throw new Error('not authorized');
}
const monitor = await this.findAndAuthMonitor(user, monitorId);
return MonitorResponseDto.from(monitor);
}

async createMonitor(
user: User,
request: CreateMonitorDto,
): Promise<MonitorResponseDto> {
const member = await this.projectMemberRepository.findByUserAndProject(
user.userId,
request.projectId,
);
if (!member) {
throw new Error('not authorized');
}
this.projectService.checkProjectPermission(user, request.projectId);

const monitor = request.toMonitor(user.userId);
// request create webhook in webhook microservice
Expand All @@ -94,17 +84,7 @@ export class MonitorService {
user: User,
request: DeleteMonitorDto,
): Promise<DeleteMonitorResponseDto> {
const monitor = await this.monitorRepository.findById(request.monitorId);
if (!monitor) {
throw new ServiceException('monitor not found', 404);
}
const member = await this.projectMemberRepository.findByUserAndProject(
user.userId,
monitor.projectId,
);
if (!member) {
throw new ServiceException('not authorized', 401);
}
const monitor = await this.findAndAuthMonitor(user, request.monitorId);
await this.webhookService.deleteWebhook(monitor.webhookId);
await this.monitorRepository.deleteMonitor(monitor.monitorId);
await this.projectRepository.increaseMonitorCount(monitor.projectId, -1);
Expand All @@ -118,17 +98,7 @@ export class MonitorService {
user: User,
request: UpdateMonitorDto,
): Promise<MonitorResponseDto> {
const monitor = await this.monitorRepository.findById(request.monitorId);
if (!monitor) {
throw new ServiceException('monitor not found', 404);
}
const member = await this.projectMemberRepository.findByUserAndProject(
user.userId,
monitor.projectId,
);
if (!member) {
throw new ServiceException('not authorized', 401);
}
const monitor = await this.findAndAuthMonitor(user, request.monitorId);
const updateMonitor = new Map<string, any>();
if (request.name) {
updateMonitor['name'] = request.name;
Expand Down
22 changes: 17 additions & 5 deletions app/apps/onebox/src/modules/project/project.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ServiceException } from '@app/global/global.exception';
import { ErrorCode } from '@app/global/global.error';
import { ProjectMemberRepository } from '@app/shared_modules/project/repositories/project.member.repository';
import { ProjectRepository } from '@app/shared_modules/project/repositories/project.repository';
import {
Expand All @@ -20,15 +20,27 @@ export class ProjectService {
private readonly projectMemberRepository: ProjectMemberRepository,
) {}

async checkProjectPermission(
user: User,
projectId: string,
): Promise<ProjectMember> {
const member = await this.projectMemberRepository.findByUserAndProject(
user.userId,
projectId,
);
if (!member) {
throw ErrorCode.PROJECT_FORBIDDEN.asException();
}
return member;
}

async getProject(user: User, id: string): Promise<ProjectResponseDto> {
const project = await this.projectRepository.findById(id);
if (!project) {
throw new ServiceException('Project not found', 404);
throw ErrorCode.PROJECT_NOT_FOUND.asException();
}

if (project.ownerId !== user.userId) {
throw new ServiceException('Project not found', 404);
}
this.checkProjectPermission(user, project.projectId);

return ProjectResponseDto.from(project);
}
Expand Down
12 changes: 4 additions & 8 deletions app/apps/onebox/src/modules/users/users.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {
ArgumentMetadata,
BadRequestException,
Injectable,
PipeTransform,
} from '@nestjs/common';
import { validate } from 'class-validator';
import { ErrorCode } from '@app/global/global.error';
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';

@Injectable()
export class CreateUserValidationPipe implements PipeTransform {
Expand All @@ -17,7 +13,7 @@ export class CreateUserValidationPipe implements PipeTransform {
const errors = await validate(object);
if (errors.length > 0) {
const err = this.buildError(errors);
throw new BadRequestException(err, 'Validation failed');
throw ErrorCode.BAD_REQUEST.asException('Invalid data', err);
}
return value;
}
Expand Down
Loading

0 comments on commit c1c4a13

Please sign in to comment.