Skip to content

Commit

Permalink
Merge pull request #14 from Xitija/main
Browse files Browse the repository at this point in the history
PS-1582 Modify timezone to UTC while accepting and in api response
  • Loading branch information
vaivk369 authored Jul 30, 2024
2 parents 9de0f3a + a7288b8 commit c16d159
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 207 deletions.
134 changes: 87 additions & 47 deletions src/common/pipes/event-validation.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,50 @@
import { ConfigService } from '@nestjs/config';
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
import { CreateEventDto } from 'src/modules/event/dto/create-event.dto';
import { getTimezoneDate } from '../utils/pipe.util';
import { ERROR_MESSAGES } from '../utils/constants.util';
import {
ValidationArguments,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';

@Injectable()
export class DateValidationPipe implements PipeTransform {
constructor(private configService: ConfigService) {}

transform(createEventDto: CreateEventDto) {
const timeZone = this.configService.get<string>('TIMEZONE');
const startDate = getTimezoneDate(
timeZone,
new Date(createEventDto.startDatetime),
);
const endDate = getTimezoneDate(
timeZone,
new Date(createEventDto.endDatetime),
);
const currentDate = getTimezoneDate(timeZone); // Current date in the specified timezone
const eventStartDate = createEventDto.startDatetime.split('T')[0];
const eventEndDate = createEventDto.endDatetime.split('T')[0];

if (startDate <= currentDate) {
const startDate = new Date(createEventDto.startDatetime);
const endDate = new Date(createEventDto.endDatetime);
const currentDate = new Date();

if (eventStartDate !== eventEndDate && createEventDto.isRecurring) {
throw new BadRequestException(
'Start date must be today or a future date',
ERROR_MESSAGES.MULTIDAY_EVENT_NOT_RECURRING,
);
}
if (startDate <= currentDate) {
throw new BadRequestException(ERROR_MESSAGES.START_DATE_INVALID);
}
if (endDate < startDate) {
throw new BadRequestException(
'End date should be greater than or equal to start date',
);
throw new BadRequestException(ERROR_MESSAGES.END_DATE_INVALID);
}

return createEventDto;
}
}

@Injectable()
export class RegistrationDateValidationPipe implements PipeTransform {
constructor(private configService: ConfigService) {}

transform(createEventDto: CreateEventDto) {
const timeZone = this.configService.get<string>('TIMEZONE');
const currentDate = getTimezoneDate(timeZone);
const startDate = getTimezoneDate(
timeZone,
new Date(createEventDto.startDatetime),
);
const endDate = getTimezoneDate(
timeZone,
new Date(createEventDto.endDatetime),
);
const currentDate = new Date();
const startDate = new Date(createEventDto.startDatetime);

const registrationStartDate = createEventDto.registrationEndDate
? getTimezoneDate(
timeZone,
new Date(createEventDto.registrationStartDate),
)
? new Date(createEventDto.registrationStartDate)
: null;
const isRestricted = createEventDto.isRestricted;
const registrationEndDate = createEventDto.registrationEndDate
? getTimezoneDate(timeZone, new Date(createEventDto.registrationEndDate))
? new Date(createEventDto.registrationEndDate)
: null;

// Ensure registration dates are not provided for restricted events
Expand Down Expand Up @@ -109,25 +96,68 @@ export class RegistrationDateValidationPipe implements PipeTransform {
}

export class RecurringEndDateValidationPipe implements PipeTransform {
constructor(private configService: ConfigService) {}

transform(createEventDto: CreateEventDto) {
const timeZone = this.configService.get<string>('TIMEZONE');
if (createEventDto.isRecurring) {
const recurrenceEndDate = new Date(createEventDto.recurrenceEndDate);
const startDate = new Date(createEventDto.startDatetime);
const currentDate = getTimezoneDate(timeZone);
if (recurrenceEndDate < currentDate) {
const endConditionValue =
createEventDto.recurrencePattern?.endCondition?.value;
const endConditionType =
createEventDto.recurrencePattern?.endCondition?.type;

if (!endConditionType || !endConditionValue) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRENCE_END_DATE_INVALID,
ERROR_MESSAGES.RECURRING_PATTERN_REQUIRED,
);
}

if (recurrenceEndDate < startDate) {
if (endConditionType === 'endDate') {
const recurrenceEndDate = new Date(endConditionValue);

const dateValid =
recurrenceEndDate && !isNaN(recurrenceEndDate.getTime());

if (!dateValid) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRENCE_END_DATE_INVALID,
);
}

const startDate = new Date(createEventDto.startDatetime);
const currentDate = new Date();
if (recurrenceEndDate < currentDate) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRENCE_END_DATE_SHOULD_BE_GREATER_THAN_CURRENT_DATE,
);
}

if (recurrenceEndDate <= startDate) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRENCE_END_DATE_AFTER_EVENT_DATE,
);
}
// createEventDto.recurrencePattern.endCondition.value = endDate;
} else if (endConditionType === 'occurrences') {
const occurrences = Number(endConditionValue);

if (!occurrences || occurrences < 1) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRENCE_OCCURRENCES_INVALID,
);
}
} else if (
endConditionType !== 'occurrences' &&
endConditionType !== 'endDate'
) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRENCE_END_DATE_BEFORE_EVENT_DATE,
ERROR_MESSAGES.RECURRENCE_PATTERN_INVALID,
);
}
} else if (
!createEventDto.isRecurring &&
Object.keys(createEventDto?.recurrencePattern ?? {})?.length
) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRING_PATTERN_NOT_REQUIRED,
);
}

return createEventDto;
Expand All @@ -138,7 +168,6 @@ export class RecurringEndDateValidationPipe implements PipeTransform {
export class AttendeesValidationPipe implements PipeTransform {
transform(createEventDto: CreateEventDto) {
const attendees = createEventDto?.attendees;
console.log('attendees', attendees);

if (!createEventDto.isRestricted) {
if (attendees && attendees.length) {
Expand All @@ -149,3 +178,14 @@ export class AttendeesValidationPipe implements PipeTransform {
return createEventDto;
}
}

@ValidatorConstraint({ name: 'endsWithZ', async: false })
export class EndsWithZConstraint implements ValidatorConstraintInterface {
validate(text: string, args: ValidationArguments) {
return typeof text === 'string' && text.endsWith('Z');
}

defaultMessage(args: ValidationArguments) {
return '($value) must end with "Z"';
}
}
15 changes: 13 additions & 2 deletions src/common/utils/constants.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const ERROR_MESSAGES = {
BAD_REQUEST: 'Bad request',
INVALID_REQUEST_BODY: 'Invalid request body',
INTERNAL_SERVER_ERROR: 'Internal Server Error',
START_DATE_INVALID: 'Start date must be today or a future date',
END_DATE_INVALID: 'End date time should be greater than start date time',
REGISTRATION_DATE_INVALID: 'Registration date must be in the future',
REGISTRATION_START_DATE_BEFORE_EVENT_DATE:
'Registration start date must be before the event start date',
Expand All @@ -16,10 +18,19 @@ export const ERROR_MESSAGES = {
REGISTRATION_END_DATE_INVALID: 'Registration end date must be in the future',
REGISTRATION_START_DATE_BEFORE_END_DATE:
'Registration start date must be before registration end date',
RECURRENCE_END_DATE_INVALID: 'Recurrence end date must be in the future',
RECURRENCE_END_DATE_BEFORE_EVENT_DATE:
RECURRENCE_END_DATE_INVALID:
'Recurrence end date must be a valid ISO 8601 date string ',
RECURRENCE_END_DATE_SHOULD_BE_GREATER_THAN_CURRENT_DATE:
'Recurrence end date should be greater than current date',
RECURRENCE_END_DATE_AFTER_EVENT_DATE:
'Recurrence end date must be after the event start date',
RECURRING_PATTERN_REQUIRED: 'Recurrence Pattern required for event',
RECURRING_PATTERN_NOT_REQUIRED:
'Recurrence Pattern not required for non recurring event',
RECURRENCE_OCCURRENCES_INVALID:
'Recurrence occurrences must be greater than 0',
RECURRENCE_PATTERN_INVALID: 'Recurrence pattern invalid',
MULTIDAY_EVENT_NOT_RECURRING: 'Multiday events cannot be recurring',
REGISTRATION_START_DATE_REQUIRED:
'Registration Start Date required for event',
RESTRICTED_EVENT_NO_REGISTRATION_DATE:
Expand Down
4 changes: 0 additions & 4 deletions src/common/utils/pipe.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ export function getTimezoneDateString(
timezoneOffsetString: string,
utcDate: Date,
): string {
const offsetInMilliseconds = timezoneOffset * 60 * 60 * 1000;
const targetDate = new Date(utcDate.getTime() + offsetInMilliseconds);

// Format the date to the desired format
const formattedDate = utcDate.toLocaleString('en-GB', {
timeZone: timeZone,
Expand All @@ -38,7 +35,6 @@ export function getTimezoneDateString(
// Replace the default locale date format separators with the desired format
const [date, time] = formattedDate.split(', ');
const [day, month, year] = date.split('/');
// const formattedDateStr = `${year}-${month}-${day} ${time.replace('.', ':')} +${String(timezoneOffset).padStart(2, '0')}30`;
const formattedDateStr = `${year}-${month}-${day} ${time.replace('.', ':')} ${timezoneOffsetString}`;

return formattedDateStr;
Expand Down
1 change: 0 additions & 1 deletion src/common/utils/transformer/date.transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export class TimeZoneTransformer implements ValueTransformer {

// From DB: Convert the date from UTC to the desired time zone after fetching
from(databaseValue: Date): string {
console.log(databaseValue, typeof databaseValue, 'databsbval');
if (!databaseValue) return databaseValue.toISOString();
return getTimezoneDateString(
this.configService.get<number>('TIMEZONE_OFFSET'),
Expand Down
1 change: 1 addition & 0 deletions src/common/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type MeetingDetails = {
id: string;
url: string;
password: string;
providerGenerated: boolean;
};

export enum DaysOfWeek {
Expand Down
40 changes: 16 additions & 24 deletions src/modules/attendees/entity/attendees.entity.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { ConfigService } from '@nestjs/config';
import { TimeZoneTransformer } from 'src/common/utils/transformer/date.transformer';
import { Events } from 'src/modules/event/entities/event.entity';
import {
Entity,
Expand All @@ -17,47 +15,41 @@ export class EventAttendees {
@PrimaryGeneratedColumn('uuid')
eventAttendeesId: string;

@Column({ type: 'uuid' })
userId: string;

@Column({ type: 'uuid', nullable: true })
eventRepetitionId: string;

@Column({ nullable: true, default: null })
isAttended: boolean | null;

@Column({ type: 'jsonb', nullable: true })
joinedLeftHistory: any;

@Column({ type: 'int', nullable: false, default: 0 })
duration: number;

@Column({ type: 'varchar', nullable: true, collation: 'pg_catalog.default' })
status: string;

@Column({ type: 'timestamptz', nullable: true,
transformer: new TimeZoneTransformer(new ConfigService())
})
@Column({
type: 'timestamptz',
nullable: true,
})
enrolledAt: Date;

@Column({ type: 'uuid', nullable: true })
enrolledBy: string;

@UpdateDateColumn({ type: 'timestamptz', transformer: new TimeZoneTransformer(new ConfigService()) })
@UpdateDateColumn({
type: 'timestamptz',
})
updatedAt: Date;

@Column({ type: 'uuid', nullable: true })
updatedBy: string;

@CreateDateColumn({ type: 'timestamptz', transformer: new TimeZoneTransformer(new ConfigService()) })
createdAt: Date;

@Column({ type: 'uuid', nullable: true })
createdBy: string;

@Column({ type: 'uuid' })
userId: string;

@Column({ type: 'uuid' })
eventId: string;

@Column({ nullable: true, default: null })
isAttended: boolean | null;

@Column({ type: 'jsonb', nullable: true })
joinedLeftHistory: any;

// @ManyToOne(() => Events, event => event.eventAttendees)
// @JoinColumn({ name: 'eventId' })
// event: Events;
Expand Down
Loading

0 comments on commit c16d159

Please sign in to comment.