Skip to content

Commit

Permalink
Merge pull request #10 from Xitija/main
Browse files Browse the repository at this point in the history
PS - 1400 Time zone conversion and fetch from env
  • Loading branch information
vaivk369 authored Jul 18, 2024
2 parents 8e4f60d + a46d50d commit 26bcbab
Show file tree
Hide file tree
Showing 13 changed files with 429 additions and 233 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
"prettier"
],
root: true,
env: {
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ lerna-debug.log*

# environment
.env
package-lock.json
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "haqdarshak-customer-app",
"name": "event-management-app",
"version": "0.0.1",
"description": "",
"author": "",
Expand Down Expand Up @@ -30,7 +30,6 @@
"axios": "^1.6.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"pg": "^8.11.3",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
},
Expand All @@ -46,7 +45,7 @@
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ import { AttendeesModule } from './modules/attendees/attendees.module';
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
export class AppModule {}
8 changes: 5 additions & 3 deletions src/common/database-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';


@Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
type: 'postgres',
type: 'postgres',
host: configService.get('POSTGRES_HOST'),
port: configService.get('POSTGRES_PORT'),
database: configService.get('POSTGRES_DATABASE'),
username: configService.get('POSTGRES_USERNAME'),
password: configService.get('POSTGRES_PASSWORD'),
extra: {
timezone: 'Z', // Use "Z" for UTC or your preferred timezone
},
// entities: [
// User
// ],
Expand All @@ -23,4 +25,4 @@ import { TypeOrmModule } from '@nestjs/typeorm';
],
providers: [ConfigService],
})
export class DatabaseModule {}
export class DatabaseModule {}
53 changes: 31 additions & 22 deletions src/common/filters/exception.filter.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Response, Request } from 'express';
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import { Response } from 'express';
import APIResponse from '../utils/response';
import { ERROR_MESSAGES } from '../utils/constants.util';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
constructor(private readonly apiId?: string) { }
constructor(private readonly apiId?: string) {}

catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception instanceof HttpException ? exception.getStatus() : 500;
const exceptionResponse = exception instanceof HttpException ? exception.getResponse() : null;
const errorMessage =
exception instanceof HttpException
? (exceptionResponse as any).message || exception.message
: ERROR_MESSAGES.INTERNAL_SERVER_ERROR;
const detailedErrorMessage = `${errorMessage}`;
console.log('detailedErrorMessage', detailedErrorMessage);
const errorResponse = APIResponse.error(
this.apiId,
detailedErrorMessage,
exception instanceof HttpException ? exception.name : ERROR_MESSAGES.INTERNAL_SERVER_ERROR, // error
status.toString(),
);
return response.status(status).json(errorResponse);
}
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status =
exception instanceof HttpException ? exception.getStatus() : 500;
const exceptionResponse =
exception instanceof HttpException ? exception.getResponse() : null;
const errorMessage =
exception instanceof HttpException
? (exceptionResponse as any).message || exception.message
: ERROR_MESSAGES.INTERNAL_SERVER_ERROR;
const detailedErrorMessage = `${errorMessage}`;
console.log('detailedErrorMessage', detailedErrorMessage);
const errorResponse = APIResponse.error(
this.apiId,
detailedErrorMessage,
exception instanceof HttpException
? exception.name
: ERROR_MESSAGES.INTERNAL_SERVER_ERROR,
status.toString(),
);
return response.status(status).json(errorResponse);
}
}
64 changes: 17 additions & 47 deletions src/common/pipes/event-validation.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
import { ConfigService } from '@nestjs/config';
import {
PipeTransform,
Injectable,
BadRequestException,
forwardRef,
Inject,
} from '@nestjs/common';
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
import { CreateEventDto } from 'src/modules/event/dto/create-event.dto';
import { getTimezoneDate } from '../utils/pipe.util';
import { get } from 'http';
import { ERROR_MESSAGES } from '../utils/constants.util';
import {
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments,
} from 'class-validator';

@Injectable()
export class DateValidationPipe implements PipeTransform {
// constructor(@Inject(forwardRef(() => ConfigService)) private configService: ConfigService) {

// }
constructor(private configService: ConfigService) {}

transform(createEventDto: CreateEventDto) {
const timeZone = 'Asia/Kolkata';
const timeZone = this.configService.get<string>('TIMEZONE');
const startDate = getTimezoneDate(
timeZone,
new Date(createEventDto.startDatetime),
Expand All @@ -31,14 +18,7 @@ export class DateValidationPipe implements PipeTransform {
timeZone,
new Date(createEventDto.endDatetime),
);
const currentDate = getTimezoneDate(timeZone); // Current date
// this.configService.get<string>('TIMEZONE'); // Get the timezone from the config service

console.log('currentDate', currentDate);
console.log('startDate', startDate);
console.log('endDate', endDate);

console.log(startDate <= currentDate, 'startDate <= currentDate');
const currentDate = getTimezoneDate(timeZone); // Current date in the specified timezone

if (startDate <= currentDate) {
throw new BadRequestException(
Expand All @@ -56,8 +36,10 @@ export class DateValidationPipe implements PipeTransform {

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

transform(createEventDto: CreateEventDto) {
const timeZone = 'Asia/Kolkata';
const timeZone = this.configService.get<string>('TIMEZONE');
const currentDate = getTimezoneDate(timeZone);
const startDate = getTimezoneDate(
timeZone,
Expand All @@ -69,35 +51,21 @@ export class RegistrationDateValidationPipe implements PipeTransform {
);
const registrationStartDate = createEventDto.registrationEndDate
? getTimezoneDate(
timeZone,
new Date(createEventDto.registrationStartDate),
)
timeZone,
new Date(createEventDto.registrationStartDate),
)
: null;
const isRestricted = createEventDto.isRestricted;
const registrationEndDate = createEventDto.registrationEndDate
? getTimezoneDate(timeZone, new Date(createEventDto.registrationEndDate))
: null;

console.log(
registrationStartDate,
'rrrrr',
startDate,
registrationStartDate > startDate,
registrationStartDate < startDate,
);
console.log(
createEventDto.isRestricted && registrationStartDate,
'createEventDto.isRestricted && registrationStartDate ',
);
console.log(
createEventDto.isRestricted && registrationEndDate,
'createEventDto.isRestricted && registrationEndDate',
);
// Ensure registration dates are not provided for restricted events

if (
(createEventDto.isRestricted && registrationStartDate) ||
(createEventDto.isRestricted && registrationEndDate)
) {
console.log('');
throw new BadRequestException(
ERROR_MESSAGES.RESTRICTED_EVENT_NO_REGISTRATION_DATE,
);
Expand Down Expand Up @@ -141,12 +109,14 @@ export class RegistrationDateValidationPipe implements PipeTransform {
}

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

transform(createEventDto: CreateEventDto) {
const currentDate = getTimezoneDate('Asia/Kolkata');
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) {
throw new BadRequestException(
ERROR_MESSAGES.RECURRENCE_END_DATE_INVALID,
Expand Down
114 changes: 63 additions & 51 deletions src/common/utils/constants.util.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,68 @@
export const ERROR_MESSAGES = {
INVALID_REQUEST: 'Invalid request',
NOT_FOUND: 'Not found',
UNAUTHORIZED: 'Unauthorized',
FORBIDDEN: 'Forbidden',
BAD_REQUEST: 'Bad request',
INVALID_REQUEST_BODY: 'Invalid request body',
INTERNAL_SERVER_ERROR: 'Internal Server Error',
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',
REGISTRATION_END_DATE_BEFORE_EVENT_DATE: 'Registration end date must be on or before the event start date',
REGISTRATION_START_DATE_INVALID: 'Registration start date must be in the future',
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 must be after the event start date',
RECURRING_PATTERN_REQUIRED: 'Recurrence Pattern required for event',
REGISTRATION_START_DATE_REQUIRED: 'Registration Start Date required for event',
INVITEES_REQUIRED: 'Invitees required for private event',
INVITEES_NOT_REQUIRED: 'Invitees not required for public event',
EVENT_NOT_FOUND: 'Event not found',
EVENT_ATTENDEE_NOT_FOUND: 'Event attendee not found',
EVENT_ATTENDEE_HISTORY_NOT_FOUND: 'Event attendee history not found',
EVENT_ATTENDEE_HISTORY_ITEM_NOT_FOUND: 'Event attendee history item not found',
}
INVALID_REQUEST: 'Invalid request',
NOT_FOUND: 'Not found',
UNAUTHORIZED: 'Unauthorized',
FORBIDDEN: 'Forbidden',
BAD_REQUEST: 'Bad request',
INVALID_REQUEST_BODY: 'Invalid request body',
INTERNAL_SERVER_ERROR: 'Internal Server Error',
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',
REGISTRATION_END_DATE_BEFORE_EVENT_DATE:
'Registration end date must be on or before the event start date',
REGISTRATION_START_DATE_INVALID:
'Registration start date must be in the future',
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 must be after the event start date',
RECURRING_PATTERN_REQUIRED: 'Recurrence Pattern required for event',
REGISTRATION_START_DATE_REQUIRED:
'Registration Start Date required for event',
RESTRICTED_EVENT_NO_REGISTRATION_DATE:
'Cannot have registration date for restricted event',
INVITEES_REQUIRED: 'Invitees required for private event',
INVITEES_NOT_REQUIRED: 'Invitees not required for public event',
EVENT_NOT_FOUND: 'Event not found',
EVENT_ATTENDEE_NOT_FOUND: 'Event attendee not found',
EVENT_ATTENDEE_HISTORY_NOT_FOUND: 'Event attendee history not found',
EVENT_ATTENDEE_HISTORY_ITEM_NOT_FOUND:
'Event attendee history item not found',
};

export const SUCCESS_MESSAGES = {
EVENT_CREATED: 'Event created successfully',
EVENT_UPDATED: 'Event updated successfully',
EVENT_DELETED: 'Event deleted successfully',
EVENT_NOT_FOUND: 'Event not found',
EVENT_ATTENDEE_CREATED: 'Event attendee created successfully',
EVENT_ATTENDEE_UPDATED: 'Event attendee updated successfully',
EVENT_ATTENDEE_DELETED: 'Event attendee deleted successfully',
EVENT_ATTENDEE_HISTORY_ITEM_CREATED: 'Event attendee history item created successfully',
EVENT_ATTENDEE_HISTORY_ITEM_UPDATED: 'Event attendee history item updated successfully',
EVENT_ATTENDEE_HISTORY_ITEM_DELETED: 'Event attendee history item deleted successfully',
}
EVENT_CREATED: 'Event created successfully',
EVENT_UPDATED: 'Event updated successfully',
EVENT_DELETED: 'Event deleted successfully',
EVENT_NOT_FOUND: 'Event not found',
EVENT_ATTENDEE_CREATED: 'Event attendee created successfully',
EVENT_ATTENDEE_UPDATED: 'Event attendee updated successfully',
EVENT_ATTENDEE_DELETED: 'Event attendee deleted successfully',
EVENT_ATTENDEE_HISTORY_ITEM_CREATED:
'Event attendee history item created successfully',
EVENT_ATTENDEE_HISTORY_ITEM_UPDATED:
'Event attendee history item updated successfully',
EVENT_ATTENDEE_HISTORY_ITEM_DELETED:
'Event attendee history item deleted successfully',
};

export const API_ID = {
CREATE_EVENT: 'api.event.create',
GET_EVENT_BY_ID: 'api.event.getbyid',
GET_EVENTS: 'api.events.get',
UPDATE_EVENT: 'api.event.update',
DELETE_EVENT: 'api.event.delete',
GET_EVENT_ATTENDEES: 'api.event.attendees.get',
GET_EVENT_ATTENDEE: 'api.event.attendee.get',
CREATE_EVENT_ATTENDEE: 'api.event.attendee.create',
UPDATE_EVENT_ATTENDEE: 'api.event.attendee.update',
DELETE_EVENT_ATTENDEE: 'api.event.attendee.delete',
GET_EVENT_ATTENDEE_HISTORY: 'api.event.attendee.history.get',
GET_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.get',
CREATE_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.create',
UPDATE_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.update',
DELETE_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.delete',
}
CREATE_EVENT: 'api.event.create',
GET_EVENT_BY_ID: 'api.event.getbyid',
GET_EVENTS: 'api.events.get',
UPDATE_EVENT: 'api.event.update',
DELETE_EVENT: 'api.event.delete',
GET_EVENT_ATTENDEES: 'api.event.attendees.get',
GET_EVENT_ATTENDEE: 'api.event.attendee.get',
CREATE_EVENT_ATTENDEE: 'api.event.attendee.create',
UPDATE_EVENT_ATTENDEE: 'api.event.attendee.update',
DELETE_EVENT_ATTENDEE: 'api.event.attendee.delete',
GET_EVENT_ATTENDEE_HISTORY: 'api.event.attendee.history.get',
GET_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.get',
CREATE_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.create',
UPDATE_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.update',
DELETE_EVENT_ATTENDEE_HISTORY_ITEM: 'api.event.attendee.history.item.delete',
};
25 changes: 25 additions & 0 deletions src/common/utils/transformer/date.transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ConfigService } from '@nestjs/config';
import { getTimezoneDate } from 'src/common/utils/pipe.util';
import { ValueTransformer } from 'typeorm';

export class TimeZoneTransformer implements ValueTransformer {
// private timeZone: string;

constructor(private configService: ConfigService) {}

// To DB: Convert the date to UTC before saving
to(entityValue: Date): Date {
// if (!entityValue) return entityValue;
return entityValue;
}

// From DB: Convert the date from UTC to the desired time zone after fetching
from(databaseValue: Date): Date {
if (!databaseValue) return databaseValue;
return getTimezoneDate(
this.configService.get<string>('TIMEZONE'),
new Date(databaseValue),
);
}
}

Loading

0 comments on commit 26bcbab

Please sign in to comment.