Skip to content

Commit

Permalink
registered exception filters to policy-engine
Browse files Browse the repository at this point in the history
  • Loading branch information
Ptroger committed Mar 14, 2024
1 parent 7b870b6 commit c5217c8
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 10 deletions.
10 changes: 0 additions & 10 deletions apps/policy-engine/src/app/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { EvaluationRequest } from '@narval/policy-engine-shared'
import { Body, Controller, Get, Logger, Post } from '@nestjs/common'
import { generateInboundRequest } from '../app/persistence/repository/mock_data'
import { ApplicationException } from '../shared/exception/application.exception'
import { AppService } from './app.service'
import { EvaluationRequestDto } from './evaluation-request.dto'

Expand All @@ -21,15 +20,6 @@ export class AppController {
this.logger.log({
message: 'Received ping'
})

throw new ApplicationException({
message: 'Test error message',
context: {
foo: 'bar'
},
suggestedHttpStatusCode: 400
})
// throw new HttpException('THIS SHOULD SHOW SOMEHWERE', 500)
return 'pong'
}

Expand Down
19 changes: 19 additions & 0 deletions apps/policy-engine/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { NestFactory } from '@nestjs/core'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
import { lastValueFrom, map, of, switchMap } from 'rxjs'
import { AppModule } from './app/app.module'
import { Config } from './policy-engine.config'
import { ApplicationExceptionFilter } from './shared/filter/application-exception.filter'
import { HttpExceptionFilter } from './shared/filter/http-exception.filter'

/**
* Adds Swagger documentation to the application.
Expand Down Expand Up @@ -37,6 +40,21 @@ const withGlobalPipes = (app: INestApplication): INestApplication => {
return app
}

/**
* Adds a global exception filter to the application.
*
* @param app - The Nest application instance.
* @param configService - The configuration service instance.
* @returns The modified Nest application instance.
*/
const withGlobalFilters =
(configService: ConfigService<Config, true>) =>
(app: INestApplication): INestApplication => {
app.useGlobalFilters(new HttpExceptionFilter(configService), new ApplicationExceptionFilter(configService))

return app
}

async function bootstrap() {
const logger = new Logger('AuthorizationNodeBootstrap')
const application = await NestFactory.create(AppModule, { bodyParser: true })
Expand All @@ -51,6 +69,7 @@ async function bootstrap() {
of(application).pipe(
map(withSwagger),
map(withGlobalPipes),
map(withGlobalFilters(configService)),
switchMap((app) => app.listen(port))
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ArgumentsHost, Catch, ExceptionFilter, LogLevel, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { Response } from 'express'
import { Config, Env } from '../../policy-engine.config'
import { ApplicationException } from '../../shared/exception/application.exception'

@Catch(ApplicationException)
export class ApplicationExceptionFilter implements ExceptionFilter {
private logger = new Logger(ApplicationExceptionFilter.name)

constructor(private configService: ConfigService<Config, true>) {}

catch(exception: ApplicationException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
const status = exception.getStatus()
const isProduction = this.configService.get('env') === Env.PRODUCTION

this.log(exception)

response.status(status).json(
isProduction
? {
statusCode: status,
message: exception.message,
context: exception.context
}
: {
statusCode: status,
message: exception.message,
context: exception.context,
stack: exception.stack,
...(exception.origin && { origin: exception.origin })
}
)
}

// TODO (@wcalderipe, 16/01/24): Unit test the logging logic. For that, we
// must inject the logger in the constructor via dependency injection.
private log(exception: ApplicationException) {
const level: LogLevel = exception.getStatus() >= 500 ? 'error' : 'warn'

if (this.logger[level]) {
this.logger[level](exception.message, {
status: exception.getStatus(),
context: exception.context,
stacktrace: exception.stack,
origin: exception.origin
})
}
}
}
41 changes: 41 additions & 0 deletions apps/policy-engine/src/shared/filter/http-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { Response } from 'express'
import { Config, Env } from '../../policy-engine.config'

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
private logger = new Logger(HttpExceptionFilter.name)

constructor(private configService: ConfigService<Config, true>) {}

catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
const status = exception.getStatus()

const isProduction = this.configService.get('env') === Env.PRODUCTION

this.logger.error({
message: exception.message,
stack: exception.stack,
response: exception.getResponse(),
statusCode: status
})

response.status(status).json(
isProduction
? {
statusCode: status,
message: exception.message,
response: exception.getResponse()
}
: {
statusCode: status,
message: exception.message,
response: exception.getResponse(),
stack: exception.stack
}
)
}
}

0 comments on commit c5217c8

Please sign in to comment.