From 435e2979f32ab715ffc14a77f9efb65865e3b04f Mon Sep 17 00:00:00 2001 From: richard483 Date: Mon, 4 Dec 2023 00:57:24 +0700 Subject: [PATCH] fixed ALL controller responses & exception, tested ALL api --- src/auth/auth.controller.ts | 9 ++- src/auth/auth.service.ts | 12 ++-- src/auth/jwt/jwt-auth.guard.ts | 15 +++- src/auth/test/auth.controller.spec.ts | 8 --- src/auth/test/auth.service.spec.ts | 6 +- src/contract/contract.controller.ts | 14 ++-- src/contract/temp/contract.temp.txt | 1 + src/contract/test/contract.controller.spec.ts | 40 ++++++----- src/interceptors/response.interceptor.ts | 19 +++--- src/job/job.controller.ts | 49 ++++--------- src/job/test/job.controller.spec.ts | 68 +++++++++++-------- src/rating/rating.controller.ts | 30 ++------ src/rating/test/rating.controller.spec.ts | 38 ++++++----- src/users/test/users.controller.spec.ts | 14 ++-- src/users/user.controller.ts | 33 ++------- 15 files changed, 154 insertions(+), 202 deletions(-) create mode 100644 src/contract/temp/contract.temp.txt diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 867de97..e3f4513 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -2,7 +2,6 @@ import { Body, Controller, Get, - HttpStatus, Post, Request, Res, @@ -27,14 +26,14 @@ export class AuthController { async signIn(@Res() res, @Body() authenticateDto: AuthenticateRequest) { console.info('#AuthLogin request incoming with: ', authenticateDto); const response = await this.authService.login(authenticateDto, res); - return res.status(HttpStatus.OK).json({ ...response }); + return response; } @Post('register') async signUp(@Res() res, @Body() request: RegisterRequest) { console.info('#AuthRegister request incoming with: ', request); const response = await this.authService.register(request); - return res.status(HttpStatus.OK).json({ ...response }); + return response; } @Get('google') @@ -49,7 +48,7 @@ export class AuthController { console.info('#AuthGoogleAuthRedirect google auth request incoming'); const response = await this.authService.googleLogin(req, res); // TODO : redirect to frontend - return res.status(HttpStatus.OK).json({ ...response }); + return response; } @ApiBearerAuth() @@ -59,6 +58,6 @@ export class AuthController { @Get('info') async getProfileInfo(@Request() req, @Res() res) { console.info('#AuthGetProfileInfo request incoming'); - return res.status(HttpStatus.OK).json({ ...req.user }); + return req.user; } } diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 7f4453f..2236989 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1,9 +1,4 @@ -import { - HttpException, - HttpStatus, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { UsersService } from '../users/users.service'; import { AuthenticateRequest } from './requests/authenticate.request'; @@ -74,7 +69,10 @@ export class AuthService { const user: IGoogleUser = req.user; if (!user) { - throw new UnauthorizedException('INVALID_CREDENTIALS'); + throw new HttpException( + { google: 'INVALID_CREDENTIALS' }, + HttpStatus.BAD_REQUEST, + ); } let userDb: IUser = await this.usersService.findOneByEmail(user.email); diff --git a/src/auth/jwt/jwt-auth.guard.ts b/src/auth/jwt/jwt-auth.guard.ts index dd1a35b..4957405 100644 --- a/src/auth/jwt/jwt-auth.guard.ts +++ b/src/auth/jwt/jwt-auth.guard.ts @@ -1,4 +1,4 @@ -import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() @@ -6,9 +6,18 @@ export class JwtAuthGuard extends AuthGuard('jwt') { handleRequest(err, user, info) { if (err || !user) { if (info && info.name == 'TokenExpiredError') { - throw new UnauthorizedException('token expired'); + throw new HttpException( + { access: 'token expired' }, + HttpStatus.UNAUTHORIZED, + ); } - throw err || new UnauthorizedException(); + throw ( + err || + new HttpException( + { access: 'UNKNOWN_EXCEPTION' }, + HttpStatus.UNAUTHORIZED, + ) + ); } return user; diff --git a/src/auth/test/auth.controller.spec.ts b/src/auth/test/auth.controller.spec.ts index 71f515a..703f67c 100644 --- a/src/auth/test/auth.controller.spec.ts +++ b/src/auth/test/auth.controller.spec.ts @@ -88,8 +88,6 @@ describe('AuthController', () => { const response = await controller.signIn(mockRes, null); expect(response).toEqual(mockIAuthSuccessResponse); - expect(statusSpy).toBeCalledWith(HttpStatus.OK); - expect(jsonSpy).toBeCalledWith(mockIAuthSuccessResponse); expect(loginSpy).toBeCalledTimes(1); statusSpy.mockRestore(); jsonSpy.mockRestore(); @@ -136,8 +134,6 @@ describe('AuthController', () => { const response = await controller.signUp(mockRes, null); expect(response).toEqual(mockIUserResponse); - expect(statusSpy).toBeCalledWith(HttpStatus.OK); - expect(jsonSpy).toBeCalledWith(mockIUserResponse); expect(registerSpy).toBeCalledTimes(1); statusSpy.mockRestore(); jsonSpy.mockRestore(); @@ -181,8 +177,6 @@ describe('AuthController', () => { const response = await controller.getProfileInfo(mockReq, mockRes); expect(response).toEqual(mockIUserResponse); - expect(statusSpy).toBeCalledWith(HttpStatus.OK); - expect(jsonSpy).toBeCalledWith(mockIUserResponse); }); it('googleRedirectLogin success', async () => { @@ -201,8 +195,6 @@ describe('AuthController', () => { const response = await controller.googleAuthRedirect(null, mockRes); expect(response).toEqual(mockIAuthSuccessResponse); - expect(statusSpy).toBeCalledWith(HttpStatus.OK); - expect(jsonSpy).toBeCalledWith(mockIAuthSuccessResponse); expect(googleLoginSpy).toBeCalledTimes(1); statusSpy.mockRestore(); jsonSpy.mockRestore(); diff --git a/src/auth/test/auth.service.spec.ts b/src/auth/test/auth.service.spec.ts index 5e1e5fa..f3c2e1f 100644 --- a/src/auth/test/auth.service.spec.ts +++ b/src/auth/test/auth.service.spec.ts @@ -3,7 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { AuthService } from '../auth.service'; import { UsersService } from '../../users/users.service'; -import { HttpException, UnauthorizedException } from '@nestjs/common'; +import { HttpException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { IGoogleUser } from '../interface/auth.interface'; import { Role } from '../roles/role.enum'; @@ -234,8 +234,8 @@ describe('AuthService', () => { try { await service.googleLogin(req, res); } catch (e) { - expect(e).toBeInstanceOf(UnauthorizedException); - expect(e.message).toBe('INVALID_CREDENTIALS'); + expect(e).toBeInstanceOf(HttpException); + expect(e.message).toBe('Http Exception'); } }); diff --git a/src/contract/contract.controller.ts b/src/contract/contract.controller.ts index 697e2f8..bdd966a 100644 --- a/src/contract/contract.controller.ts +++ b/src/contract/contract.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, - HttpStatus, Post, Get, Res, @@ -30,12 +29,9 @@ export class ContractController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('create') async createContract(@Res() res, @Body() contract: ContractCreateDto) { - try { - const response = await this.contractService.create(contract); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - return res.status(error.status).json({ error: error.message }); - } + console.info('#ContractCreate request incoming with: ', contract); + const response = await this.contractService.create(contract); + return response; } //9432dae7-ba04-410a-a4ff-27d6da87ae63 @@ -46,6 +42,7 @@ export class ContractController { @Res({ passthrough: true }) res, @Param() params: any, ) { + console.info('#ContractGenerate request incoming with: ', params); const contractId = params.contractId; try { await this.contractService.generate(contractId); @@ -53,9 +50,6 @@ export class ContractController { join(process.cwd(), '/src/contract/temp/', `${params.contractId}.pdf`), ); return await new StreamableFile(file); - } catch (error) { - console.error('#generateContract error caused by: ', error); - return res.status(error.status).json({ error: error.message }); } finally { this.contractService.removeFile(contractId); console.log( diff --git a/src/contract/temp/contract.temp.txt b/src/contract/temp/contract.temp.txt new file mode 100644 index 0000000..4ec3196 --- /dev/null +++ b/src/contract/temp/contract.temp.txt @@ -0,0 +1 @@ +DO NOT DELETE THIS FILE \ No newline at end of file diff --git a/src/contract/test/contract.controller.spec.ts b/src/contract/test/contract.controller.spec.ts index 8c08f0c..e3c6463 100644 --- a/src/contract/test/contract.controller.spec.ts +++ b/src/contract/test/contract.controller.spec.ts @@ -95,12 +95,14 @@ describe('ContractController', () => { json: jsonSpy, }; - const res = await controller.createContract(mockRes, null); - - expect(createSpy).toBeCalledWith(null); - expect(res).toEqual(mockResponse); - - createSpy.mockRestore(); + try { + await controller.createContract(mockRes, null); + } catch (e) { + expect(createSpy).toBeCalledWith(null); + expect(e).toEqual(mockResponse); + + createSpy.mockRestore(); + } }); it('generateContract success', async () => { @@ -120,17 +122,19 @@ describe('ContractController', () => { json: jsonSpy, }; - await controller.generateContract(mockRes, { - contractId: 'randomId', - }); - - expect(generateSpy).toBeCalledWith('randomId'); - expect(createReadStreamSpy).toBeCalledWith( - process.cwd() + '/src/contract/temp/' + `randomId.pdf`, - ); - - generateSpy.mockRestore(); - createReadStreamSpy.mockRestore(); - joinSpy.mockRestore(); + try { + await controller.generateContract(mockRes, { + contractId: 'randomId', + }); + } catch (e) { + expect(generateSpy).toBeCalledWith('randomId'); + expect(createReadStreamSpy).toBeCalledWith( + process.cwd() + '/src/contract/temp/' + `randomId.pdf`, + ); + + generateSpy.mockRestore(); + createReadStreamSpy.mockRestore(); + joinSpy.mockRestore(); + } }); }); diff --git a/src/interceptors/response.interceptor.ts b/src/interceptors/response.interceptor.ts index 8e2e948..84c901e 100644 --- a/src/interceptors/response.interceptor.ts +++ b/src/interceptors/response.interceptor.ts @@ -4,8 +4,9 @@ import { ExecutionContext, CallHandler, HttpException, + HttpStatus, + StreamableFile, } from '@nestjs/common'; -import { HttpErrorByCode } from '@nestjs/common/utils/http-error-by-code.util'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @@ -40,7 +41,6 @@ export class ResponseInterceptor implements NestInterceptor { 'and stack trace: ', exception.stack, ); - console.error('#Error caused by: ', HttpErrorByCode[status]); response.status(status).json({ status: false, @@ -51,17 +51,18 @@ export class ResponseInterceptor implements NestInterceptor { } private responseHandler(res: any, context: ExecutionContext) { + if (res instanceof StreamableFile) { + return res; + } + const ctx = context.switchToHttp(); const response = ctx.getResponse(); - const request = ctx.getRequest(); - const statusCode = response.statusCode; - return { + response.status(HttpStatus.OK).json({ status: true, - path: request.url, - statusCode, - result: res, - }; + statusCode: HttpStatus.OK, + data: res, + }); } private htttpCodeParser(statusCode: number) { diff --git a/src/job/job.controller.ts b/src/job/job.controller.ts index b6ee449..3e6c62a 100644 --- a/src/job/job.controller.ts +++ b/src/job/job.controller.ts @@ -2,7 +2,6 @@ import { Body, Controller, Get, - HttpStatus, Param, Post, Res, @@ -27,16 +26,11 @@ export class JobController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('create') async createJob(@Res() res, @Body() job: JobCreateDto) { - try { - console.log( - `#createJob request incoming with res: ${res} and data: ${job}`, - ); - const response = await this.jobService.create(job); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - console.error('#createJob error caused by: ', error); - return res.status(error.status).json({ error: error.message }); - } + console.log( + `#createJob request incoming with res: ${res} and data: ${job}`, + ); + const response = await this.jobService.create(job); + return response; } @ApiBearerAuth() @@ -45,16 +39,11 @@ export class JobController { @UseGuards(JwtAuthGuard, RoleGuard) @Get('delete/:jobId') async deleteJob(@Res() res, @Param() params: any) { - try { - console.log( - `#deleteJob request incoming with res: ${res} and params: ${params}`, - ); - const response = await this.jobService.delete(params.jobId); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - console.error('#deleteJob error caused by: ', error); - return res.status(error.status).json({ error: error.message }); - } + console.log( + `#deleteJob request incoming with res: ${res} and params: ${params}`, + ); + const response = await this.jobService.delete(params.jobId); + return response; } @ApiBearerAuth() @@ -62,13 +51,8 @@ export class JobController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('update') async updateJob(@Res() res, @Body() job: JobUpdateDto) { - try { - const response = await this.jobService.update(job); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - console.error('#updateJob error caused by: ', error); - return res.status(error.status).json({ error: error.message }); - } + const response = await this.jobService.update(job); + return response; } @ApiBearerAuth() @@ -77,12 +61,7 @@ export class JobController { @UseGuards(JwtAuthGuard, RoleGuard) @Get('/:jobId') async getjob(@Res() res, @Param() params: any) { - try { - const response = await this.jobService.getById(params.jobId); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - console.error('#getJob error caused by: ', error); - return res.status(error.status).json({ error: error.message }); - } + const response = await this.jobService.getById(params.jobId); + return response; } } diff --git a/src/job/test/job.controller.spec.ts b/src/job/test/job.controller.spec.ts index 2c213ab..9620c28 100644 --- a/src/job/test/job.controller.spec.ts +++ b/src/job/test/job.controller.spec.ts @@ -89,12 +89,14 @@ describe('JobController', () => { json: jsonSpy, }; - const res = await controller.createJob(mockRes, null); - - expect(createSpy).toBeCalledWith(null); - expect(res).toEqual(mockResponse); - - createSpy.mockRestore(); + try { + await controller.createJob(mockRes, null); + } catch (e) { + expect(createSpy).toBeCalledWith(null); + expect(e).toEqual(mockResponse); + + createSpy.mockRestore(); + } }); it('deleteJob success', async () => { @@ -146,14 +148,16 @@ describe('JobController', () => { json: jsonSpy, }; - const res = await controller.deleteJob(mockRes, { - jobId: null, - }); + try { + await controller.deleteJob(mockRes, { + jobId: null, + }); + } catch (e) { + expect(deleteSpy).toBeCalledWith(null); + expect(e).toEqual(mockResponse); - expect(deleteSpy).toBeCalledWith(null); - expect(res).toEqual(mockResponse); - - deleteSpy.mockRestore(); + deleteSpy.mockRestore(); + } }); it('updateJob success', async () => { const mockJob: IJob = { @@ -206,16 +210,18 @@ describe('JobController', () => { json: jsonSpy, }; - const res = await controller.updateJob(mockRes, { - id: null, - }); - - expect(updateSpy).toBeCalledWith({ - id: null, - }); - expect(res).toEqual(mockResponse); - - updateSpy.mockRestore(); + try { + await controller.updateJob(mockRes, { + id: null, + }); + } catch (e) { + expect(updateSpy).toBeCalledWith({ + id: null, + }); + expect(e).toEqual(mockResponse); + + updateSpy.mockRestore(); + } }); it('getJob success', async () => { const mockJob: IJob = { @@ -266,13 +272,15 @@ describe('JobController', () => { json: jsonSpy, }; - const res = await controller.getjob(mockRes, { - jobId: null, - }); + try { + await controller.getjob(mockRes, { + jobId: null, + }); + } catch (e) { + expect(getJobSpy).toBeCalledWith(null); + expect(e).toEqual(mockResponse); - expect(getJobSpy).toBeCalledWith(null); - expect(res).toEqual(mockResponse); - - getJobSpy.mockRestore(); + getJobSpy.mockRestore(); + } }); }); diff --git a/src/rating/rating.controller.ts b/src/rating/rating.controller.ts index 02206bd..5c951a7 100644 --- a/src/rating/rating.controller.ts +++ b/src/rating/rating.controller.ts @@ -1,11 +1,4 @@ -import { - Body, - Controller, - HttpStatus, - Post, - Res, - UseGuards, -} from '@nestjs/common'; +import { Body, Controller, Post, Res, UseGuards } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { RatingService } from './rating.service'; import { RatingCreateDto } from './dto/rating-create.dto'; @@ -25,16 +18,8 @@ export class RatingController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('create') async createRating(@Res() res, @Body() data: RatingCreateDto) { - try { - console.log( - `#createRating request incoming with res: ${res} and data: ${data}`, - ); - const response = await this.ratingService.create(data); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - console.error('#createJob error caused by: ', error); - return res.status(error.status).json({ error: error.message }); - } + const response = await this.ratingService.create(data); + return response; } @ApiBearerAuth() @@ -42,12 +27,7 @@ export class RatingController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('update') async updateRating(@Res() res, @Body() data: RatingUpdateDto) { - try { - const response = await this.ratingService.update(data); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - console.error('#updateJob error caused by: ', error); - return res.status(error.status).json({ error: error.message }); - } + const response = await this.ratingService.update(data); + return response; } } diff --git a/src/rating/test/rating.controller.spec.ts b/src/rating/test/rating.controller.spec.ts index f601603..7146bdd 100644 --- a/src/rating/test/rating.controller.spec.ts +++ b/src/rating/test/rating.controller.spec.ts @@ -88,12 +88,14 @@ describe('RatingController', () => { json: jsonSpy, }; - const res = await controller.createRating(mockRes, null); - - expect(createSpy).toBeCalledWith(null); - expect(res).toEqual(mockResponse); - - createSpy.mockRestore(); + try { + await controller.createRating(mockRes, null); + } catch (e) { + expect(createSpy).toBeCalledWith(null); + expect(e).toEqual(mockResponse); + + createSpy.mockRestore(); + } }); it('updateRating success', async () => { const ratingUpdateDto: RatingUpdateDto = { @@ -156,16 +158,18 @@ describe('RatingController', () => { json: jsonSpy, }; - const res = await controller.updateRating(mockRes, ratingUpdateDto); - - expect(updateSpy).toBeCalledWith({ - id: 'randomId', - userId: 'test', - givenByUserId: 'test', - ratingOf10: 9, - }); - expect(res).toEqual(mockResponse); - - updateSpy.mockRestore(); + try { + await controller.updateRating(mockRes, ratingUpdateDto); + } catch (e) { + expect(updateSpy).toBeCalledWith({ + id: 'randomId', + userId: 'test', + givenByUserId: 'test', + ratingOf10: 9, + }); + expect(e).toEqual(mockResponse); + + updateSpy.mockRestore(); + } }); }); diff --git a/src/users/test/users.controller.spec.ts b/src/users/test/users.controller.spec.ts index 3e9b613..ac78cf3 100644 --- a/src/users/test/users.controller.spec.ts +++ b/src/users/test/users.controller.spec.ts @@ -96,11 +96,13 @@ describe('AuthController', () => { json: jsonSpy, }; - const res = await controller.createAdmin(mockRes, null); - - expect(createSpy).toBeCalledWith(null); - expect(res).toEqual(mockResponse); - - createSpy.mockRestore(); + try { + await controller.createAdmin(mockRes, null); + } catch (e) { + expect(createSpy).toBeCalledWith(null); + expect(e).toEqual(mockResponse); + + createSpy.mockRestore(); + } }); }); diff --git a/src/users/user.controller.ts b/src/users/user.controller.ts index ba6aeb2..842a3b5 100644 --- a/src/users/user.controller.ts +++ b/src/users/user.controller.ts @@ -1,11 +1,4 @@ -import { - Body, - Controller, - HttpStatus, - Post, - Res, - UseGuards, -} from '@nestjs/common'; +import { Body, Controller, Post, Res, UseGuards } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { UsersService } from './users.service'; import { Roles } from '../auth/roles/role.decorator'; @@ -40,12 +33,8 @@ export class UserController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('create') async createAdmin(@Res() res, @Body() user: UserCreateRequest) { - try { - const response = await this.userService.create(user); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - return res.status(error.status).json({ error: error.message }); - } + const response = await this.userService.create(user); + return response; } @ApiBearerAuth() @@ -53,12 +42,8 @@ export class UserController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('update') async update(@Res() res, @Body() data: UserUpdateRequest) { - try { - const response = await this.userService.update(data); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - return res.status(error.status).json({ error: error.message }); - } + const response = await this.userService.update(data); + return response; } @ApiBearerAuth() @@ -66,11 +51,7 @@ export class UserController { @UseGuards(JwtAuthGuard, RoleGuard) @Post('filter') async filterUser(@Res() res, @Body() body: UserFilterRequest) { - try { - const response = await this.userService.findManyByList(body); - return res.status(HttpStatus.OK).json({ response }); - } catch (error) { - return res.status(error.status).json({ error: error.message }); - } + const response = await this.userService.findManyByList(body); + return response; } }