feat(validationPipe): Global Exception Filter basic setup using APP_FILTER and APP_PIPE
This commit is contained in:
parent
ef4f6340d2
commit
f9931f99c8
|
|
@ -876,6 +876,52 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/shifts/upsert/{email}/{date}": {
|
||||
"put": {
|
||||
"operationId": "ShiftsController_upsert_by_date",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "email",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "date",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpsertShiftDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Shifts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/shifts": {
|
||||
"post": {
|
||||
"operationId": "ShiftsController_create",
|
||||
|
|
@ -2513,6 +2559,10 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"UpsertShiftDto": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"CreateShiftDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { BadRequestException, Module, ValidationPipe } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { ArchivalModule } from './modules/archival/archival.module';
|
||||
|
|
@ -22,6 +22,9 @@ import { ShiftsModule } from './modules/shifts/shifts.module';
|
|||
import { TimesheetsModule } from './modules/timesheets/timesheets.module';
|
||||
import { UsersModule } from './modules/users-management/users.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { APP_FILTER, APP_PIPE } from '@nestjs/core';
|
||||
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
|
||||
import { ValidationError } from 'class-validator';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -46,6 +49,29 @@ import { ConfigModule } from '@nestjs/config';
|
|||
UsersModule,
|
||||
],
|
||||
controllers: [AppController, HealthController],
|
||||
providers: [AppService, OvertimeService],
|
||||
providers: [
|
||||
AppService,
|
||||
OvertimeService,
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: HttpExceptionFilter
|
||||
},
|
||||
{
|
||||
provide: APP_PIPE,
|
||||
useValue: new ValidationPipe({
|
||||
whitelist: true,
|
||||
forbidNonWhitelisted: true,
|
||||
transform: true,
|
||||
exceptionFactory: (errors: ValidationError[] = [])=> {
|
||||
const messages = errors.flatMap((e)=> Object.values(e.constraints ?? {}));
|
||||
return new BadRequestException({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: messages.length ? messages : errors,
|
||||
});
|
||||
},
|
||||
}),
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
|||
24
src/common/filters/http-exception.filter.ts
Normal file
24
src/common/filters/http-exception.filter.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from "@nestjs/common";
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
@Catch(HttpException)
|
||||
export class HttpExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: HttpException, host: ArgumentsHost) {
|
||||
const http_context = host.switchToHttp();
|
||||
const response = http_context.getResponse<Response>();
|
||||
const request = http_context.getRequest<Request>();
|
||||
const http_status = exception.getStatus();
|
||||
|
||||
const exception_response = exception.getResponse();
|
||||
const normalized = typeof exception_response === 'string'
|
||||
? { message: exception_response }
|
||||
: (exception_response as Record<string, unknown>);
|
||||
const response_body = {
|
||||
statusCode: http_status,
|
||||
timestamp: new Date().toISOString(),
|
||||
path: request.url,
|
||||
...normalized,
|
||||
};
|
||||
response.status(http_status).json(response_body);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ import { ATT_TMP_DIR } from './config/attachment.config'; // log to be removed p
|
|||
|
||||
import { ModuleRef, NestFactory, Reflector } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
// import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard';
|
||||
import { RolesGuard } from './common/guards/roles.guard';
|
||||
import { OwnershipGuard } from './common/guards/ownership.guard';
|
||||
|
|
@ -25,13 +24,11 @@ async function bootstrap() {
|
|||
|
||||
const reflector = app.get(Reflector); //setup Reflector for Roles()
|
||||
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({ whitelist: true, transform: true}));
|
||||
app.useGlobalGuards(
|
||||
// new JwtAuthGuard(reflector), //Authentification JWT
|
||||
new RolesGuard(reflector), //deny-by-default and Role-based Access Control
|
||||
new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet
|
||||
);
|
||||
);
|
||||
|
||||
// Authentication and session
|
||||
app.use(session({
|
||||
|
|
|
|||
|
|
@ -16,4 +16,3 @@ export function toDateOnlyUTC(input: string | Date): Date {
|
|||
const date = new Date(input);
|
||||
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
|||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { ShiftPayloadDto, UpsertShiftDto } from "../dtos/upsert-shift.dto";
|
||||
import { timeFromHHMMUTC, toDateOnlyUTC, weekStartMondayUTC } from "../helpers/shifts-date-time-helpers";
|
||||
import { error, time } from "console";
|
||||
|
||||
type DayShiftResponse = {
|
||||
start_time: string;
|
||||
|
|
@ -16,14 +15,10 @@ type DayShiftResponse = {
|
|||
|
||||
type UpsertAction = 'created' | 'updated' | 'deleted';
|
||||
|
||||
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ShiftsCommandService extends BaseApprovalService<Shifts> {
|
||||
constructor(prisma: PrismaService) { super(prisma); }
|
||||
|
||||
|
||||
//create/update/delete master method
|
||||
async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto):
|
||||
Promise<{ action: UpsertAction; day: DayShiftResponse[] }> {
|
||||
|
|
@ -230,7 +225,6 @@ async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto)
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
private normalize_shift_payload(payload: ShiftPayloadDto) {
|
||||
//normalize shift's infos
|
||||
const start_time = timeFromHHMMUTC(payload.start_time);
|
||||
|
|
@ -271,8 +265,6 @@ async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto)
|
|||
return `${hh}:${mm}`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//approval methods
|
||||
|
||||
protected get delegate() {
|
||||
|
|
@ -288,17 +280,4 @@ async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto)
|
|||
this.updateApprovalWithTransaction(transaction, id, is_approved),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
old without new = delete
|
||||
new without old = post
|
||||
old with new = patch old with new
|
||||
*/
|
||||
async upsertShift(old_shift?: UpsertShiftDto, new_shift?: UpsertShiftDto) {
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user