Skip to content

Commit 2c82705

Browse files
authored
Merge pull request #91 from monstar-lab-oss/sivan/user-from-ctx
user from ctx
2 parents 72df1d5 + 48e896f commit 2c82705

File tree

11 files changed

+83
-80
lines changed

11 files changed

+83
-80
lines changed

src/auth/controllers/auth.controller.spec.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ import { AuthService } from '../services/auth.service';
55
import { RegisterInput } from '../dtos/auth-register-input.dto';
66
import { LoginInput } from '../dtos/auth-login-input.dto';
77
import { RefreshTokenInput } from '../dtos/auth-refresh-token-input.dto';
8-
import {
9-
AuthTokenOutput,
10-
UserRefreshTokenClaims,
11-
} from '../dtos/auth-token-output.dto';
8+
import { AuthTokenOutput } from '../dtos/auth-token-output.dto';
129
import { RequestContext } from '../../shared/request-context/request-context.dto';
1310
import { AppLogger } from '../../shared/logger/logger.service';
1411

@@ -68,37 +65,27 @@ describe('AuthController', () => {
6865
loginInputDto.username = '[email protected]';
6966
loginInputDto.password = '123123';
7067

71-
const reqObject: any = {};
72-
7368
jest.spyOn(mockedAuthService, 'login').mockImplementation(() => null);
7469

75-
expect(await authController.login(ctx, reqObject, loginInputDto)).toEqual(
76-
{
77-
data: null,
78-
meta: {},
79-
},
80-
);
70+
expect(await authController.login(ctx, loginInputDto)).toEqual({
71+
data: null,
72+
meta: {},
73+
});
8174
});
8275
});
8376

8477
describe('refreshToken', () => {
85-
let tokenUser: UserRefreshTokenClaims;
8678
let refreshTokenInputDto: RefreshTokenInput;
8779
let authToken: AuthTokenOutput;
88-
let request: any;
8980

9081
beforeEach(() => {
91-
tokenUser = { id: 1 };
9282
refreshTokenInputDto = {
9383
refreshToken: 'refresh_token',
9484
};
9585
authToken = {
9686
accessToken: 'new_access_token',
9787
refreshToken: 'new_refresh_token',
9888
};
99-
request = {
100-
user: tokenUser,
101-
};
10289

10390
jest
10491
.spyOn(mockedAuthService, 'refreshToken')
@@ -108,11 +95,10 @@ describe('AuthController', () => {
10895
it('should generate refresh token', async () => {
10996
const response = await authController.refreshToken(
11097
ctx,
111-
request,
11298
refreshTokenInputDto,
11399
);
114100

115-
expect(mockedAuthService.refreshToken).toBeCalledWith(ctx, tokenUser);
101+
expect(mockedAuthService.refreshToken).toBeCalledWith(ctx);
116102
expect(response.data).toEqual(authToken);
117103
});
118104

src/auth/controllers/auth.controller.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
Req,
32
Post,
43
Body,
54
UseGuards,
@@ -9,19 +8,14 @@ import {
98
ClassSerializerInterceptor,
109
HttpCode,
1110
} from '@nestjs/common';
12-
import { Request } from 'express';
1311
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
1412

1513
import { AuthService } from '../services/auth.service';
1614
import { LoginInput } from '../dtos/auth-login-input.dto';
1715
import { RegisterInput } from '../dtos/auth-register-input.dto';
1816
import { RegisterOutput } from '../dtos/auth-register-output.dto';
1917
import { LocalAuthGuard } from '../guards/local-auth.guard';
20-
import {
21-
AuthTokenOutput,
22-
UserAccessTokenClaims,
23-
UserRefreshTokenClaims,
24-
} from '../dtos/auth-token-output.dto';
18+
import { AuthTokenOutput } from '../dtos/auth-token-output.dto';
2519
import { RefreshTokenInput } from '../dtos/auth-refresh-token-input.dto';
2620
import { JwtRefreshGuard } from '../guards/jwt-refresh.guard';
2721
import {
@@ -59,16 +53,12 @@ export class AuthController {
5953
@UseInterceptors(ClassSerializerInterceptor)
6054
login(
6155
@ReqContext() ctx: RequestContext,
62-
@Req() req: Request,
6356
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6457
@Body() credential: LoginInput,
6558
): BaseApiResponse<AuthTokenOutput> {
6659
this.logger.logWithContext(ctx, `${this.login.name} was called`);
6760

68-
const authToken = this.authService.login(
69-
ctx,
70-
req.user as UserAccessTokenClaims,
71-
);
61+
const authToken = this.authService.login(ctx);
7262
return { data: authToken, meta: {} };
7363
}
7464

@@ -105,16 +95,12 @@ export class AuthController {
10595
@UseInterceptors(ClassSerializerInterceptor)
10696
async refreshToken(
10797
@ReqContext() ctx: RequestContext,
108-
@Req() req: Request,
10998
// eslint-disable-next-line @typescript-eslint/no-unused-vars
11099
@Body() credential: RefreshTokenInput,
111100
): Promise<BaseApiResponse<AuthTokenOutput>> {
112101
this.logger.logWithContext(ctx, `${this.refreshToken.name} was called`);
113102

114-
const authToken = await this.authService.refreshToken(
115-
ctx,
116-
req.user as UserRefreshTokenClaims,
117-
);
103+
const authToken = await this.authService.refreshToken(ctx);
118104
return { data: authToken, meta: {} };
119105
}
120106
}

src/auth/dtos/auth-token-output.dto.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ export class AuthTokenOutput {
1414
}
1515

1616
export class UserAccessTokenClaims {
17+
@Expose()
1718
id: number;
19+
@Expose()
1820
username: string;
21+
@Expose()
1922
roles: ROLE[];
2023
}
2124

src/auth/services/auth.service.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { UserOutput } from '../../user/dtos/user-output.dto';
1010
import {
1111
AuthTokenOutput,
1212
UserAccessTokenClaims,
13-
UserRefreshTokenClaims,
1413
} from '../dtos/auth-token-output.dto';
1514
import { ROLE } from '../constants/role.constant';
1615
import { AppLogger } from '../../shared/logger/logger.service';
@@ -19,7 +18,6 @@ import { RequestContext } from '../../shared/request-context/request-context.dto
1918
describe('AuthService', () => {
2019
let service: AuthService;
2120

22-
const refreshTokenClaims: UserRefreshTokenClaims = { id: 6 };
2321
const accessTokenClaims: UserAccessTokenClaims = {
2422
id: 6,
2523
username: 'jhon',
@@ -130,7 +128,7 @@ describe('AuthService', () => {
130128
it('should return auth token for valid user', async () => {
131129
jest.spyOn(service, 'getAuthToken').mockImplementation(() => authToken);
132130

133-
const result = service.login(ctx, accessTokenClaims);
131+
const result = service.login(ctx);
134132

135133
expect(service.getAuthToken).toBeCalledWith(ctx, accessTokenClaims);
136134
expect(result).toEqual(authToken);
@@ -151,14 +149,16 @@ describe('AuthService', () => {
151149
});
152150

153151
describe('refreshToken', () => {
152+
ctx.user = accessTokenClaims;
153+
154154
it('should generate auth token', async () => {
155155
jest
156156
.spyOn(mockedUserService, 'findById')
157157
.mockImplementation(async () => userOutput);
158158

159159
jest.spyOn(service, 'getAuthToken').mockImplementation(() => authToken);
160160

161-
const result = await service.refreshToken(ctx, refreshTokenClaims);
161+
const result = await service.refreshToken(ctx);
162162

163163
expect(service.getAuthToken).toBeCalledWith(ctx, userOutput);
164164
expect(result).toMatchObject(authToken);
@@ -169,9 +169,9 @@ describe('AuthService', () => {
169169
.spyOn(mockedUserService, 'findById')
170170
.mockImplementation(async () => null);
171171

172-
await expect(
173-
service.refreshToken(ctx, refreshTokenClaims),
174-
).rejects.toThrowError('Invalid user id');
172+
await expect(service.refreshToken(ctx)).rejects.toThrowError(
173+
'Invalid user id',
174+
);
175175
});
176176

177177
afterEach(() => {

src/auth/services/auth.service.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { RegisterOutput } from '../dtos/auth-register-output.dto';
1010
import {
1111
AuthTokenOutput,
1212
UserAccessTokenClaims,
13-
UserRefreshTokenClaims,
1413
} from '../dtos/auth-token-output.dto';
1514
import { UserOutput } from '../../user/dtos/user-output.dto';
1615
import { AppLogger } from '../../shared/logger/logger.service';
@@ -49,13 +48,10 @@ export class AuthService {
4948
return user;
5049
}
5150

52-
login(
53-
ctx: RequestContext,
54-
accessTokenClaims: UserAccessTokenClaims,
55-
): AuthTokenOutput {
51+
login(ctx: RequestContext): AuthTokenOutput {
5652
this.logger.logWithContext(ctx, `${this.login.name} was called`);
5753

58-
return this.getAuthToken(ctx, accessTokenClaims);
54+
return this.getAuthToken(ctx, ctx.user);
5955
}
6056

6157
async register(
@@ -74,13 +70,10 @@ export class AuthService {
7470
});
7571
}
7672

77-
async refreshToken(
78-
ctx: RequestContext,
79-
refreshTokenClaims: UserRefreshTokenClaims,
80-
): Promise<AuthTokenOutput> {
73+
async refreshToken(ctx: RequestContext): Promise<AuthTokenOutput> {
8174
this.logger.logWithContext(ctx, `${this.refreshToken.name} was called`);
8275

83-
const user = await this.userService.findById(ctx, refreshTokenClaims.id);
76+
const user = await this.userService.findById(ctx, ctx.user.id);
8477
if (!user) {
8578
throw new UnauthorizedException('Invalid user id');
8679
}

src/auth/strategies/local.strategy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export class LocalStrategy extends PassportStrategy(Strategy, STRATEGY_LOCAL) {
1515
private authService: AuthService,
1616
private readonly logger: AppLogger,
1717
) {
18+
// Add option passReqToCallback: true to configure strategy to be request-scoped.
1819
super({
1920
usernameField: 'username',
2021
passwordField: 'password',

src/shared/filters/all-exceptions.filter.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ describe('AllExceptionsFilter', () => {
2525
const mockedLogger = {
2626
warn: jest.fn().mockReturnThis(),
2727
setContext: jest.fn().mockReturnThis(),
28+
warnWithContext: jest.fn().mockReturnThis(),
2829
};
2930
let filter: AllExceptionsFilter<any>;
3031

src/shared/filters/all-exceptions.filter.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ConfigService } from '@nestjs/config';
1111
import { REQUEST_ID_TOKEN_HEADER } from '../constants';
1212
import { AppLogger } from '../logger/logger.service';
1313
import { BaseApiError } from '../errors/base-api-error';
14+
import { createRequestContext } from '../request-context/util';
1415

1516
@Catch()
1617
export class AllExceptionsFilter<T> implements ExceptionFilter {
@@ -30,6 +31,7 @@ export class AllExceptionsFilter<T> implements ExceptionFilter {
3031
const path = req.url;
3132
const timestamp = new Date().toISOString();
3233
const requestId = req.headers[REQUEST_ID_TOKEN_HEADER];
34+
const requestContext = createRequestContext(req);
3335

3436
let stack: any;
3537
let statusCode: HttpStatus;
@@ -75,7 +77,10 @@ export class AllExceptionsFilter<T> implements ExceptionFilter {
7577
requestId,
7678
timestamp,
7779
};
78-
this.logger.warn({ error, stack });
80+
this.logger.warnWithContext(requestContext, error.message, {
81+
error,
82+
stack,
83+
});
7984

8085
// Suppress original internal server error details in prod mode
8186
const isProMood = this.config.get<string>('env') !== 'development';

src/shared/interceptors/logging.interceptor.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
import { Observable } from 'rxjs';
88
import { tap } from 'rxjs/operators';
99
import { AppLogger } from '../logger/logger.service';
10-
import { REQUEST_ID_TOKEN_HEADER } from '../constants';
10+
import { createRequestContext } from '../request-context/util';
1111

1212
@Injectable()
1313
export class LoggingInterceptor implements NestInterceptor {
@@ -18,10 +18,7 @@ export class LoggingInterceptor implements NestInterceptor {
1818
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
1919
const request = context.switchToHttp().getRequest();
2020
const method = request.method;
21-
const url = request.originalUrl;
22-
const requestID = request.headers[REQUEST_ID_TOKEN_HEADER];
23-
24-
this.appLogger.log({ method, requestID, url });
21+
const ctx = createRequestContext(request);
2522

2623
const now = Date.now();
2724
return next.handle().pipe(
@@ -30,13 +27,10 @@ export class LoggingInterceptor implements NestInterceptor {
3027
const statusCode = response.statusCode;
3128

3229
const responseTime = Date.now() - now;
33-
this.appLogger.log({
34-
method,
35-
requestID,
36-
url,
37-
statusCode,
38-
responseTime,
39-
});
30+
31+
const resData = { method, statusCode, responseTime };
32+
33+
this.appLogger.logWithContext(ctx, 'Request completed', { resData });
4034
}),
4135
);
4236
}

src/shared/logger/logger.service.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,54 @@ export class AppLogger implements LoggerService {
5555

5656
// TODO : This is a temporary function, it will be renamed to `log` once we update it across
5757
// all controllers, services, etc.
58-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
59-
logWithContext(ctx: RequestContext, message: any): Logger {
58+
logWithContext(
59+
ctx: RequestContext,
60+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
61+
message: any,
62+
meta?: Record<string, any>,
63+
): Logger {
64+
const timestamp = new Date().toISOString();
65+
6066
return this.logger.info({
6167
message,
68+
contextName: this.context,
69+
ctx,
70+
timestamp,
71+
...meta,
72+
});
73+
}
74+
75+
warnWithContext(
76+
ctx: RequestContext,
77+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
78+
message: any,
79+
meta?: Record<string, any>,
80+
): Logger {
81+
const timestamp = new Date().toISOString();
82+
83+
return this.logger.warn({
84+
message,
85+
contextName: this.context,
6286
ctx,
87+
timestamp,
88+
...meta,
89+
});
90+
}
91+
92+
errorWithContext(
93+
ctx: RequestContext,
94+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
95+
message: any,
96+
meta?: Record<string, any>,
97+
): Logger {
98+
const timestamp = new Date().toISOString();
99+
100+
return this.logger.error({
101+
message,
63102
contextName: this.context,
103+
ctx,
104+
timestamp,
105+
...meta,
64106
});
65107
}
66108
}

0 commit comments

Comments
 (0)