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": {
|
"/shifts": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "ShiftsController_create",
|
"operationId": "ShiftsController_create",
|
||||||
|
|
@ -2513,6 +2559,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UpsertShiftDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
},
|
||||||
"CreateShiftDto": {
|
"CreateShiftDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { BadRequestException, Module, ValidationPipe } from '@nestjs/common';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { ArchivalModule } from './modules/archival/archival.module';
|
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 { TimesheetsModule } from './modules/timesheets/timesheets.module';
|
||||||
import { UsersModule } from './modules/users-management/users.module';
|
import { UsersModule } from './modules/users-management/users.module';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
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({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -46,6 +49,29 @@ import { ConfigModule } from '@nestjs/config';
|
||||||
UsersModule,
|
UsersModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController, HealthController],
|
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 {}
|
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 { ModuleRef, NestFactory, Reflector } from '@nestjs/core';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { ValidationPipe } from '@nestjs/common';
|
|
||||||
// import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard';
|
// import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from './common/guards/roles.guard';
|
import { RolesGuard } from './common/guards/roles.guard';
|
||||||
import { OwnershipGuard } from './common/guards/ownership.guard';
|
import { OwnershipGuard } from './common/guards/ownership.guard';
|
||||||
|
|
@ -25,13 +24,11 @@ async function bootstrap() {
|
||||||
|
|
||||||
const reflector = app.get(Reflector); //setup Reflector for Roles()
|
const reflector = app.get(Reflector); //setup Reflector for Roles()
|
||||||
|
|
||||||
app.useGlobalPipes(
|
|
||||||
new ValidationPipe({ whitelist: true, transform: true}));
|
|
||||||
app.useGlobalGuards(
|
app.useGlobalGuards(
|
||||||
// new JwtAuthGuard(reflector), //Authentification JWT
|
// new JwtAuthGuard(reflector), //Authentification JWT
|
||||||
new RolesGuard(reflector), //deny-by-default and Role-based Access Control
|
new RolesGuard(reflector), //deny-by-default and Role-based Access Control
|
||||||
new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet
|
new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet
|
||||||
);
|
);
|
||||||
|
|
||||||
// Authentication and session
|
// Authentication and session
|
||||||
app.use(session({
|
app.use(session({
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,3 @@ export function toDateOnlyUTC(input: string | Date): Date {
|
||||||
const date = new Date(input);
|
const date = new Date(input);
|
||||||
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
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 { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { ShiftPayloadDto, UpsertShiftDto } from "../dtos/upsert-shift.dto";
|
import { ShiftPayloadDto, UpsertShiftDto } from "../dtos/upsert-shift.dto";
|
||||||
import { timeFromHHMMUTC, toDateOnlyUTC, weekStartMondayUTC } from "../helpers/shifts-date-time-helpers";
|
import { timeFromHHMMUTC, toDateOnlyUTC, weekStartMondayUTC } from "../helpers/shifts-date-time-helpers";
|
||||||
import { error, time } from "console";
|
|
||||||
|
|
||||||
type DayShiftResponse = {
|
type DayShiftResponse = {
|
||||||
start_time: string;
|
start_time: string;
|
||||||
|
|
@ -16,14 +15,10 @@ type DayShiftResponse = {
|
||||||
|
|
||||||
type UpsertAction = 'created' | 'updated' | 'deleted';
|
type UpsertAction = 'created' | 'updated' | 'deleted';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsCommandService extends BaseApprovalService<Shifts> {
|
export class ShiftsCommandService extends BaseApprovalService<Shifts> {
|
||||||
constructor(prisma: PrismaService) { super(prisma); }
|
constructor(prisma: PrismaService) { super(prisma); }
|
||||||
|
|
||||||
|
|
||||||
//create/update/delete master method
|
//create/update/delete master method
|
||||||
async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto):
|
async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto):
|
||||||
Promise<{ action: UpsertAction; day: DayShiftResponse[] }> {
|
Promise<{ action: UpsertAction; day: DayShiftResponse[] }> {
|
||||||
|
|
@ -230,7 +225,6 @@ async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private normalize_shift_payload(payload: ShiftPayloadDto) {
|
private normalize_shift_payload(payload: ShiftPayloadDto) {
|
||||||
//normalize shift's infos
|
//normalize shift's infos
|
||||||
const start_time = timeFromHHMMUTC(payload.start_time);
|
const start_time = timeFromHHMMUTC(payload.start_time);
|
||||||
|
|
@ -271,8 +265,6 @@ async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto)
|
||||||
return `${hh}:${mm}`;
|
return `${hh}:${mm}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//approval methods
|
//approval methods
|
||||||
|
|
||||||
protected get delegate() {
|
protected get delegate() {
|
||||||
|
|
@ -288,17 +280,4 @@ async upsertShfitsByDate(email:string, date_string: string, dto: UpsertShiftDto)
|
||||||
this.updateApprovalWithTransaction(transaction, id, is_approved),
|
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