Skip to content

Commit

Permalink
Merge pull request #39 from richard483/feature/run-week-1
Browse files Browse the repository at this point in the history
unify error response on auth controller
fixed ALL controller responses & exception, tested ALL api
tidy up error response & update docs
  • Loading branch information
richard483 authored Dec 3, 2023
2 parents 82278fd + ca5b653 commit f67e112
Show file tree
Hide file tree
Showing 18 changed files with 366 additions and 282 deletions.
67 changes: 38 additions & 29 deletions docs/auth-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,27 @@ HTTP Code: 200

```json
{
"id": "89edfeb7-0b26-4c88-950a-2216123ec367",
"email": "asdasdas@gmail.com",
"username": "string",
"firstName": "string",
"lastName": "string",
"password": "$2b$10$wxulL2C86NpVTzWMlA9Knu9CVUdasWCwSwaC3tqj/S9ziA/iT9JiW",
"createdAt": "2023-11-22T17:19:18.472Z",
"updatedAt": "2023-11-22T17:19:18.472Z",
"roles": ["USER"],
"description": null,
"previousWorkplaceId": [],
"previousWorkplaceCount": null,
"ratingsAvg": null,
"hasGoogleAccount": false
}
```

### Password not match

HTTP Code: 400

```json
{
"error": "PASSWORD_NOT_MATCH"
"status": true,
"statusCode": 200,
"data": {
"id": "57b3634c-3789-4a46-b6f0-afe22a195f27",
"email": "aassaad@gmail.com",
"username": "username",
"firstName": "firsl",
"lastName": "last",
"password": "$2b$10$yAmMDeIyOHRo/bKfWWQ6N.Z/LrnU1PEA4lMBO/4M/48c23zz54QYa",
"createdAt": "2023-12-03T17:59:31.655Z",
"updatedAt": "2023-12-03T17:59:31.655Z",
"roles": [
"USER"
],
"description": null,
"previousWorkplaceId": [],
"previousWorkplaceCount": null,
"ratingsAvg": null,
"companyId": null,
"hasGoogleAccount": false
}
}
```

Expand All @@ -141,7 +138,10 @@ HTTP Code: 400

```json
{
"error": "EMAIL_ALREADY_USED"
"status": false,
"statusCode": 400,
"message": "EMAIL_ALREADY_USED",
"error": "BAD_REQUEST"
}
```

Expand All @@ -151,9 +151,14 @@ HTTP Code: 400

```json
{
"status": false,
"statusCode": 400,
"message": ["email must be an email"],
"error": "Bad Request"
"message": [
{
"isEmail": "email must be an email"
}
],
"error": "BAD_REQUEST"
}
```

Expand All @@ -163,8 +168,12 @@ HTTP Code: 400

```json
{
"status": false,
"statusCode": 400,
"message": ["firstName must be a string", "firstName should not be empty"],
"error": "Bad Request"
"message": {
"firstName": "firstName must be a string",
"lastName": "lastName must be a string"
},
"error": "BAD_REQUEST"
}
```
10 changes: 9 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { ConfigModule } from '@nestjs/config';
import { JobModule } from './job/job.module';
import { ContractModule } from './contract/contract.module';
import { RatingModule } from './rating/rating.module';
import { ResponseInterceptor } from './interceptors/response.interceptor';
import { APP_INTERCEPTOR } from '@nestjs/core';

@Module({
imports: [
Expand All @@ -18,6 +20,12 @@ import { RatingModule } from './rating/rating.module';
RatingModule,
],
controllers: [AppController],
providers: [AppService],
providers: [
AppService,
{
provide: APP_INTERCEPTOR,
useClass: ResponseInterceptor,
},
],
})
export class AppModule {}
33 changes: 9 additions & 24 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
Body,
Controller,
Get,
HttpStatus,
Post,
Request,
Res,
Expand All @@ -26,25 +25,15 @@ export class AuthController {
@Post('login')
async signIn(@Res() res, @Body() authenticateDto: AuthenticateRequest) {
console.info('#AuthLogin request incoming with: ', authenticateDto);
try {
const response = await this.authService.login(authenticateDto, res);
return res.status(HttpStatus.OK).json({ ...response });
} catch (error) {
console.error('#AuthLogin error caused by: ', error);
return res.status(error.status).json({ error: error.message });
}
const response = await this.authService.login(authenticateDto, res);
return response;
}

@Post('register')
async signUp(@Res() res, @Body() request: RegisterRequest) {
console.info('#AuthRegister request incoming with: ', request);
try {
const response = await this.authService.register(request);
return res.status(HttpStatus.OK).json({ ...response });
} catch (error) {
console.error('#AuthRegister error caused by: ', error);
return res.status(error.status).json({ error: error.message });
}
const response = await this.authService.register(request);
return response;
}

@Get('google')
Expand All @@ -57,14 +46,9 @@ export class AuthController {
@UseGuards(GoogleGuard)
async googleAuthRedirect(@Request() req, @Res() res) {
console.info('#AuthGoogleAuthRedirect google auth request incoming');
try {
const response = await this.authService.googleLogin(req, res);
// TODO : redirect to frontend
return res.status(HttpStatus.OK).json({ ...response });
} catch (error) {
console.error('#AutGoogleAuthRedirect error caused by: ', error);
return res.status(error.status).json({ error: error.message });
}
const response = await this.authService.googleLogin(req, res);
// TODO : redirect to frontend
return response;
}

@ApiBearerAuth()
Expand All @@ -73,6 +57,7 @@ export class AuthController {
@UseGuards(JwtAuthGuard, RoleGuard)
@Get('info')
async getProfileInfo(@Request() req, @Res() res) {
return res.status(HttpStatus.OK).json({ ...req.user });
console.info('#AuthGetProfileInfo request incoming');
return req.user;
}
}
23 changes: 14 additions & 9 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
BadRequestException,
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';
Expand All @@ -24,7 +20,7 @@ export class AuthService {
): Promise<IAuthenticate> {
const user = await this.usersService.findOne(authenticateRequest.email);
if (!user) {
throw new UnauthorizedException('INVALID_CREDENTIALS');
throw new HttpException({ user: 'NOT_FOUND' }, HttpStatus.NOT_FOUND);
}

const isPasswordMatch = await compare(
Expand All @@ -33,7 +29,10 @@ export class AuthService {
);

if (!isPasswordMatch) {
throw new UnauthorizedException('INVALID_CREDENTIALS');
throw new HttpException(
{ password: 'NOT_MATCH' },
HttpStatus.BAD_REQUEST,
);
}

// remove password from user object
Expand All @@ -54,7 +53,10 @@ export class AuthService {

async register(userCreate: RegisterRequest): Promise<IUser> {
if (!this.passwordValidation(userCreate.password)) {
throw new BadRequestException('INVALID_PASSWORD');
throw new HttpException(
{ password: 'INVALID_PASSWORD' },
HttpStatus.BAD_REQUEST,
);
}
const user = await this.usersService.create({
...userCreate,
Expand All @@ -67,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);
Expand Down
15 changes: 12 additions & 3 deletions src/auth/jwt/jwt-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
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;
Expand Down
64 changes: 27 additions & 37 deletions src/auth/test/auth.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -109,15 +107,15 @@ describe('AuthController', () => {
.spyOn(authService, 'login')
.mockRejectedValue(mockBadRequestResponse);

const response = await controller.signIn(mockRes, null);

expect(response).toEqual(mockBadRequestResponse);
expect(statusSpy).toBeCalledWith(HttpStatus.BAD_REQUEST);
expect(jsonSpy).toBeCalledWith({ error: mockBadRequestResponse.message });
expect(loginSpy).toBeCalledTimes(1);
statusSpy.mockRestore();
jsonSpy.mockRestore();
loginSpy.mockRestore();
try {
await controller.signIn(mockRes, null);
} catch (e) {
expect(e).toEqual(mockBadRequestResponse);
expect(loginSpy).toBeCalledTimes(1);
statusSpy.mockRestore();
jsonSpy.mockRestore();
loginSpy.mockRestore();
}
});

it('register success', async () => {
Expand All @@ -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();
Expand All @@ -157,15 +153,15 @@ describe('AuthController', () => {
.spyOn(authService, 'register')
.mockRejectedValue(mockBadRequestResponse);

const response = await controller.signUp(mockRes, null);

expect(response).toEqual(mockBadRequestResponse);
expect(statusSpy).toBeCalledWith(HttpStatus.BAD_REQUEST);
expect(jsonSpy).toBeCalledWith({ error: mockBadRequestResponse.message });
expect(registerSpy).toBeCalledTimes(1);
statusSpy.mockRestore();
jsonSpy.mockRestore();
registerSpy.mockRestore();
try {
await controller.signUp(mockRes, null);
} catch (e) {
expect(e).toEqual(mockBadRequestResponse);
expect(registerSpy).toBeCalledTimes(1);
statusSpy.mockRestore();
jsonSpy.mockRestore();
registerSpy.mockRestore();
}
});

it('getProfileInfo', async () => {
Expand All @@ -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 () => {
Expand All @@ -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();
Expand All @@ -222,17 +214,15 @@ describe('AuthController', () => {
.spyOn(authService, 'googleLogin')
.mockRejectedValue(mockBadRequestResponse);

const response = await controller.googleAuthRedirect(null, mockRes);

expect(response).toEqual(mockBadRequestResponse);
expect(statusSpy).toBeCalledWith(HttpStatus.BAD_REQUEST);
expect(jsonSpy).toBeCalledWith({
error: mockBadRequestResponse.message,
});
expect(loginSpy).toBeCalledTimes(1);
statusSpy.mockRestore();
jsonSpy.mockRestore();
loginSpy.mockRestore();
try {
await controller.googleAuthRedirect(null, mockRes);
} catch (e) {
expect(e).toEqual(mockBadRequestResponse);
expect(loginSpy).toBeCalledTimes(1);
statusSpy.mockRestore();
jsonSpy.mockRestore();
loginSpy.mockRestore();
}
});

it('google placeholder', async () => {
Expand Down
Loading

0 comments on commit f67e112

Please sign in to comment.