From 44f23fdcea738be31eabf1ecaaaad0540ddd1e2e Mon Sep 17 00:00:00 2001 From: poojakarma Date: Wed, 21 Aug 2024 17:12:08 +0530 Subject: [PATCH 1/3] Refactor filter API for timeZone --- src/common/pipes/event-validation.pipe.ts | 73 ++++++++++++++++++++++- src/modules/event/dto/search-event.dto.ts | 45 +++++++++----- src/modules/event/event.controller.ts | 6 +- src/modules/event/event.service.ts | 34 +++++++---- 4 files changed, 129 insertions(+), 29 deletions(-) diff --git a/src/common/pipes/event-validation.pipe.ts b/src/common/pipes/event-validation.pipe.ts index bb75440..ab64126 100644 --- a/src/common/pipes/event-validation.pipe.ts +++ b/src/common/pipes/event-validation.pipe.ts @@ -1,4 +1,9 @@ -import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; +import { + PipeTransform, + Injectable, + BadRequestException, + ArgumentMetadata, +} from '@nestjs/common'; import { CreateEventDto } from 'src/modules/event/dto/create-event.dto'; import { ERROR_MESSAGES } from '../utils/constants.util'; import { @@ -180,6 +185,72 @@ export class AttendeesValidationPipe implements PipeTransform { } } +@Injectable() +export class SearchDateValidationPipe implements PipeTransform { + transform(value: any, metadata: ArgumentMetadata) { + const { date, startDate, endDate } = value.filters || {}; + + // Check if both startDate and endDate are passed with date + if (date && (startDate || endDate)) { + throw new BadRequestException( + 'Only one of date, startDate, or endDate should be provided.', + ); + } + + // Check if after and before are provided with date + if (date && (!date.after || !date.before)) { + throw new BadRequestException( + 'Both "after" and "before" fields are required when date is provided.', + ); + } + + if ( + startDate && + (!startDate.after || !startDate.before) && + endDate === undefined + ) { + throw new BadRequestException( + 'Both "after" and "before" fields are required when startDate is provided.', + ); + } + + if ( + endDate && + (!endDate.after || !endDate.before) && + startDate === undefined + ) { + throw new BadRequestException( + 'Both "after" and "before" fields are required when endDate is provided.', + ); + } + + if (startDate && endDate && (!startDate.after || !endDate.before)) { + throw new BadRequestException( + 'if StartDate and EndDate Provided then "after" fields is required in startDate and "before fields is required in endDate', + ); + } + + if ( + (date && new Date(date.after) > new Date(date.before)) || + (startDate && + !endDate && + new Date(startDate.after) > new Date(startDate.before)) || + (endDate && + !startDate && + new Date(endDate.after) > new Date(endDate.before)) || + (startDate && + endDate && + new Date(startDate.after) > new Date(endDate.before)) + ) { + throw new BadRequestException( + '"after" should be less than and equal to "before" fields ', + ); + } + + return value; + } +} + @ValidatorConstraint({ name: 'endsWithZ', async: false }) export class EndsWithZConstraint implements ValidatorConstraintInterface { validate(text: string, args: ValidationArguments) { diff --git a/src/modules/event/dto/search-event.dto.ts b/src/modules/event/dto/search-event.dto.ts index f37a421..ab21925 100644 --- a/src/modules/event/dto/search-event.dto.ts +++ b/src/modules/event/dto/search-event.dto.ts @@ -2,38 +2,50 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsOptional, - IsDateString, IsEnum, IsString, ValidateNested, IsUUID, + IsDateString, } from 'class-validator'; -export class FilterDto { +// DateRangeDto for specifying date range filters +class DateRangeDto { @ApiProperty({ - example: '2024-07-24', - description: 'specific date in YYYY-MM-DD format', + example: '2024-07-24T00:00:00Z', + description: + 'ISO 8601 format date-time representing the start of the range', }) @IsOptional() @IsDateString() - date: string; + after?: string; @ApiProperty({ - example: '2024-07-24', - description: 'Start date in YYYY-MM-DD format', + example: '2024-07-27T23:59:59Z', + description: 'ISO 8601 format date-time representing the end of the range', }) @IsOptional() @IsDateString() - startDate: string; + before?: string; +} +export class FilterDto { + @ApiProperty({ type: DateRangeDto, description: 'Start date range filter' }) + @IsOptional() + @ValidateNested() + @Type(() => DateRangeDto) + date?: DateRangeDto; - @ApiProperty({ - example: '2024-07-27', - description: 'End date in YYYY-MM-DD format', - }) + @ApiProperty({ type: DateRangeDto, description: 'Start date range filter' }) @IsOptional() - @IsDateString() - endDate: string; + @ValidateNested() + @Type(() => DateRangeDto) + startDate?: DateRangeDto; + @ApiProperty({ type: DateRangeDto, description: 'End date range filter' }) + @IsOptional() + @ValidateNested() + @Type(() => DateRangeDto) + endDate?: DateRangeDto; @ApiProperty({ example: ['live', 'draft', 'inActive', 'archived'], description: 'Array of status values: live, draft, inActive,archived', @@ -61,7 +73,10 @@ export class FilterDto { @IsString() title?: string; - @ApiProperty({ example: 'CohortId', description: 'Cohort' }) + @ApiProperty({ + example: '76a5e84a-4336-47c8-986f-98f7ad190e0b', + description: 'Cohort', + }) @IsOptional() @IsUUID('4') cohortId?: string; diff --git a/src/modules/event/event.controller.ts b/src/modules/event/event.controller.ts index f061378..0117a9d 100644 --- a/src/modules/event/event.controller.ts +++ b/src/modules/event/event.controller.ts @@ -32,6 +32,7 @@ import { RegistrationDateValidationPipe, AttendeesValidationPipe, RecurringEndDateValidationPipe, + SearchDateValidationPipe, } from 'src/common/pipes/event-validation.pipe'; import { ConfigService } from '@nestjs/config'; import { AllExceptionsFilter } from 'src/common/filters/exception.filter'; @@ -79,7 +80,10 @@ export class EventController { @ApiInternalServerErrorResponse({ description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR, }) - @UsePipes(new ValidationPipe({ transform: true })) + @UsePipes( + new ValidationPipe({ transform: true }), + new SearchDateValidationPipe(), + ) @ApiOkResponse({ description: 'Searched', status: 200, diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index 43bd6ae..f1177c1 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -170,9 +170,11 @@ export class EventService { // Handle specific date records if (filters?.date) { - const startDate = filters?.date; - const startDateTime = `${startDate} 00:00:00`; - const endDateTime = `${startDate} 23:59:59`; + // const startDate = filters?.date; + // const startDateTime = `${startDate} 00:00:00`; + // const endDateTime = `${startDate} 23:59:59`; + const startDateTime = filters?.date.after; // min date + const endDateTime = filters?.date.before; // max date ---> seraching on the basis of max date whereClauses.push( `(er."startDateTime" <= '${endDateTime}'::timestamp AT TIME ZONE 'UTC' AND er."endDateTime" >= '${startDateTime}'::timestamp AT TIME ZONE 'UTC')`, ); @@ -181,8 +183,11 @@ export class EventService { // Handle startDate if (filters?.startDate && filters.endDate === undefined) { const startDate = filters?.startDate; - const startDateTime = `${startDate} 00:00:00`; - const endDateTime = `${startDate} 23:59:59`; + // const startDateTime = `${startDate} 00:00:00`; + // const endDateTime = `${startDate} 23:59:59`; + const startDateTime = filters.startDate.after; + const endDateTime = filters.startDate.before; + whereClauses.push( `(er."startDateTime" <= '${endDateTime}' ::timestamp AT TIME ZONE 'UTC' AND er."startDateTime" >= '${startDateTime}' ::timestamp AT TIME ZONE 'UTC')`, ); @@ -190,17 +195,22 @@ export class EventService { if (filters?.startDate && filters.endDate) { const startDate = filters?.startDate; - const startDateTime = `${startDate} 00:00:00`; - const endDateTime = `${filters?.endDate} 23:59:59`; + // const startDateTime = `${startDate} 00:00:00`; + // const endDateTime = `${filters?.endDate} 23:59:59`; + const startDateTime = filters.startDate.after; // 21 -> startDate + const endDateTime = filters.endDate.before; + whereClauses.push( `(er."startDateTime" <= '${endDateTime}' ::timestamp AT TIME ZONE 'UTC' AND er."endDateTime" >= '${startDateTime}' ::timestamp AT TIME ZONE 'UTC')`, ); } if (filters.endDate && filters.startDate === undefined) { - const endDate = filters?.endDate; - const startDateTime = `${endDate} 00:00:00`; - const endDateTime = `${endDate} 23:59:59`; + // const endDate = filters?.endDate; + // const startDateTime = `${endDate} 00:00:00`; + // const endDateTime = `${endDate} 23:59:59`; + const startDateTime = filters.endDate.after; + const endDateTime = filters.endDate.before; whereClauses.push( `(er."endDateTime" <= '${endDateTime}' ::timestamp AT TIME ZONE 'UTC' AND er."endDateTime" >= '${startDateTime}' ::timestamp AT TIME ZONE 'UTC')`, ); @@ -222,12 +232,12 @@ export class EventService { // Handle status filter if (filters.status && filters.status.length > 0) { const statusConditions = filters.status - .map((status) => `"status" = '${status}'`) + .map((status) => `ed."status" = '${status}'`) .join(' OR '); whereClauses.push(`(${statusConditions})`); } else { // Add default status condition if no status is passed in the filter - whereClauses.push(`"status" = 'live'`); + whereClauses.push(`ed."status" = 'live'`); } // Handle cohortId filter From e004941025d882523de3cc35b9dd4111a5148814 Mon Sep 17 00:00:00 2001 From: poojakarma Date: Thu, 22 Aug 2024 17:34:04 +0530 Subject: [PATCH 2/3] Added createdBy filter in get list --- src/modules/event/event.service.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index f1177c1..82334ff 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -106,6 +106,7 @@ export class EventService { let finalquery = `SELECT er."eventDetailId" AS "eventRepetition_eventDetailId", + er."createdBy" AS "eventRepetition_createdBy", er.*, e."eventId" AS "event_eventId", e."eventDetailId" AS "event_eventDetailId", @@ -230,7 +231,7 @@ export class EventService { } // Handle status filter - if (filters.status && filters.status.length > 0) { + if (filters?.status && filters?.status.length > 0) { const statusConditions = filters.status .map((status) => `ed."status" = '${status}'`) .join(' OR '); @@ -241,10 +242,14 @@ export class EventService { } // Handle cohortId filter - if (filters.cohortId) { + if (filters?.cohortId) { whereClauses.push(`ed."metadata"->>'cohortId'='${filters.cohortId}'`); } + if (filters?.createdBy) { + whereClauses.push(`er."createdBy" = '${filters.createdBy}'`); + } + // Construct final query if (whereClauses.length > 0) { finalquery += ` WHERE ${whereClauses.join(' AND ')}`; From b145bc4dbb5fa14fcb8c539167838f20ea1cf12a Mon Sep 17 00:00:00 2001 From: poojakarma Date: Thu, 22 Aug 2024 17:40:50 +0530 Subject: [PATCH 3/3] created by added in body of filter --- src/modules/event/dto/search-event.dto.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/event/dto/search-event.dto.ts b/src/modules/event/dto/search-event.dto.ts index ab21925..b0ed642 100644 --- a/src/modules/event/dto/search-event.dto.ts +++ b/src/modules/event/dto/search-event.dto.ts @@ -80,6 +80,14 @@ export class FilterDto { @IsOptional() @IsUUID('4') cohortId?: string; + + @ApiProperty({ + example: 'eff008a8-2573-466d-b877-fddf6a4fc13e', + description: 'CreatedBy', + }) + @IsOptional() + @IsUUID('4') + createdBy?: string; } export class SearchFilterDto {