From 96f626738f3728d0a0631105c1184d3dc0161fb7 Mon Sep 17 00:00:00 2001 From: poojakarma Date: Wed, 31 Jul 2024 16:38:08 +0530 Subject: [PATCH 1/6] update status title shortdiscription --- src/modules/event/dto/update-event.dto.ts | 320 ++++++++++++---------- src/modules/event/event.controller.ts | 4 +- src/modules/event/event.service.ts | 95 ++++++- 3 files changed, 262 insertions(+), 157 deletions(-) diff --git a/src/modules/event/dto/update-event.dto.ts b/src/modules/event/dto/update-event.dto.ts index 4d533f6..092935a 100644 --- a/src/modules/event/dto/update-event.dto.ts +++ b/src/modules/event/dto/update-event.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsNotEmpty, IsOptional, IsString, IsUUID, IsEnum, IsLongitude, IsLatitude, IsBoolean, IsInt, Min, IsDateString, IsObject } from 'class-validator'; +import { IsNotEmpty, IsOptional, IsString, IsUUID, IsEnum, IsLongitude, IsLatitude, IsBoolean, IsInt, Min, IsDateString, IsObject, ValidateIf } from 'class-validator'; export class UpdateEventDto { @@ -13,169 +13,189 @@ export class UpdateEventDto { @IsOptional() title?: string; - @ApiProperty({ - type: String, - description: 'Short Description', - example: 'This is a sample event', - required: false, - }) - @IsString() - @IsNotEmpty() - @IsOptional() - shortDescription?: string; @ApiProperty({ type: String, - description: 'Description', - example: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' + description: 'Status', + example: 'live' }) - @IsString() - @IsNotEmpty() - @IsOptional() - description: string; - - - @ApiProperty({ - type: String, - description: 'image', - example: 'https://example.com/sample-image.jpg' + @IsEnum(['live', 'draft', 'archived'], { + message: 'Status must be one of: live, draft, archived', }) @IsString() - @IsNotEmpty() @IsOptional() - image: string; - - @ApiProperty({ - type: String, - description: 'Event Type', - example: 'online' - }) - @IsEnum(['online', 'offline', 'onlineandoffline'], { - message: 'Event Type must be one of: online, offline, onlineandoffline' - } - ) - @IsString() @IsNotEmpty() - @IsOptional() - eventType: string; + status: string; @ApiProperty({ type: String, - description: 'isRestricted', + description: 'isRecurring', example: true }) @IsBoolean() - @IsOptional() - isRestricted: boolean; - - @ApiProperty({ - type: String, - description: 'Start Datetime', - example: '2024-03-18T10:00:00Z' - }) - @IsDateString() - @IsOptional() - startDatetime: Date; - - @ApiProperty({ - type: String, - description: 'End Datetime', - example: '2024-03-18T10:00:00Z' - }) - @IsDateString() - @IsOptional() - endDatetime: Date; - - @ApiProperty({ - type: String, - description: 'Location', - example: 'Event Location' - }) - @IsString() - @IsNotEmpty() - @IsOptional() - location: string; - - - @ApiProperty({ - type: Number, - description: 'Latitude', - example: 18.508345134886994 - }) - @IsLongitude() - @IsOptional() - longitude: number; - - @ApiProperty({ - type: Number, - description: 'Latitude', - example: 18.508345134886994 - }) - @IsLatitude() - @IsOptional() - latitude: number; - - @ApiProperty({ - type: String, - description: 'Online Provider', - example: 'Zoom' - }) - @IsString() - @IsNotEmpty() - @IsOptional() - onlineProvider: string; - - @ApiProperty({ - type: String, - description: 'Registration Deadline', - example: '2024-03-18T10:00:00Z' - }) - @IsDateString() - @IsOptional() - registrationDeadline: Date; - - @ApiProperty({ - type: Number, - description: 'Max Attendees', - example: 100 - }) - @IsInt() - @IsOptional() - @Min(0) - maxAttendees: number; - - @ApiProperty({ - type: Object, - description: 'Params', - // example: { cohortIds: ['eff008a8-2573-466d-b877-fddf6a4fc13e', 'e9fec05a-d6ab-44be-bfa4-eaeef2ef8fe9'] }, - // example: { userIds: ['eff008a8-2573-466d-b877-fddf6a4fc13e', 'e9fec05a-d6ab-44be-bfa4-eaeef2ef8fe9'] }, - example: { cohortIds: ['eff008a8-2573-466d-b877-fddf6a4fc13e'] }, - }) - @IsObject() - @IsOptional() - params: any; - - @ApiProperty({ - type: Object, - description: 'Recordings', - example: { url: 'https://example.com/recording' } - }) - @IsObject() - @IsOptional() - recordings: any; - - @ApiProperty({ - type: String, - description: 'Status', - example: 'live' - }) - @IsEnum(['live', 'draft', 'inActive'], { - message: 'Status must be one of: live, draft, inActive', - }) - @IsString() - @IsOptional() - @IsNotEmpty() - status: string; + target: boolean; + + // Validation to ensure if target is true, title or status must be provided + @ValidateIf(o => !o.title && !o.status) // Ensure that if neither title nor status is provided, validation fails + @IsNotEmpty({ message: 'If target is provided, at least one of title or status must be provided.' }) + dummyField?: any; + + + // @ApiProperty({ + // type: String, + // description: 'Event Type', + // example: 'online' + // }) + // @IsEnum(['online', 'offline', 'onlineandoffline'], { + // message: 'Event Type must be one of: online, offline, onlineandoffline' + // } + // ) + // @IsString() + // @IsNotEmpty() + // @IsOptional() + // eventType: string; + + + + // @ApiProperty({ + // type: String, + // description: 'Start Datetime', + // example: '2024-03-18T10:00:00Z' + // }) + // @IsDateString() + // @IsOptional() + // startDatetime: Date; + + // @ApiProperty({ + // type: String, + // description: 'End Datetime', + // example: '2024-03-18T10:00:00Z' + // }) + // @IsDateString() + // @IsOptional() + // endDatetime: Date; + + // @ApiProperty({ + // type: String, + // description: 'Location', + // example: 'Event Location' + // }) + // @IsString() + // @IsNotEmpty() + // @IsOptional() + // location: string; + + + // @ApiProperty({ + // type: Number, + // description: 'Latitude', + // example: 18.508345134886994 + // }) + // @IsLongitude() + // @IsOptional() + // longitude: number; + + // @ApiProperty({ + // type: Number, + // description: 'Latitude', + // example: 18.508345134886994 + // }) + // @IsLatitude() + // @IsOptional() + // latitude: number; + + + + + // @ApiProperty({ + // type: String, + // description: 'Short Description', + // example: 'This is a sample event', + // required: false, + // }) + // @IsString() + // @IsNotEmpty() + // @IsOptional() + // shortDescription?: string; + + // @ApiProperty({ + // type: String, + // description: 'Description', + // example: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' + // }) + // @IsString() + // @IsNotEmpty() + // @IsOptional() + // description: string; + + + // @ApiProperty({ + // type: String, + // description: 'image', + // example: 'https://example.com/sample-image.jpg' + // }) + // @IsString() + // @IsNotEmpty() + // @IsOptional() + // image: string; + + // @ApiProperty({ + // type: String, + // description: 'Online Provider', + // example: 'Zoom' + // }) + // @IsString() + // @IsNotEmpty() + // @IsOptional() + // onlineProvider: string; + + // @ApiProperty({ + // type: String, + // description: 'Registration Deadline', + // example: '2024-03-18T10:00:00Z' + // }) + // @IsDateString() + // @IsOptional() + // registrationDeadline: Date; + + // @ApiProperty({ + // type: Number, + // description: 'Max Attendees', + // example: 100 + // }) + // @IsInt() + // @IsOptional() + // @Min(0) + // maxAttendees: number; + + // @ApiProperty({ + // type: Object, + // description: 'Params', + // // example: { cohortIds: ['eff008a8-2573-466d-b877-fddf6a4fc13e', 'e9fec05a-d6ab-44be-bfa4-eaeef2ef8fe9'] }, + // // example: { userIds: ['eff008a8-2573-466d-b877-fddf6a4fc13e', 'e9fec05a-d6ab-44be-bfa4-eaeef2ef8fe9'] }, + // example: { cohortIds: ['eff008a8-2573-466d-b877-fddf6a4fc13e'] }, + // }) + // @IsObject() + // @IsOptional() + // params: any; + + // @ApiProperty({ + // type: Object, + // description: 'Recordings', + // example: { url: 'https://example.com/recording' } + // }) + // @IsObject() + // @IsOptional() + // recordings: any; + + // @ApiProperty({ + // type: String, + // description: 'isRestricted', + // example: true + // }) + // @IsBoolean() + // @IsOptional() + // isRestricted: boolean; @IsString() diff --git a/src/modules/event/event.controller.ts b/src/modules/event/event.controller.ts index a558cb6..d49945f 100644 --- a/src/modules/event/event.controller.ts +++ b/src/modules/event/event.controller.ts @@ -47,7 +47,7 @@ export class EventController { constructor( private readonly eventService: EventService, private readonly configService: ConfigService, - ) {} + ) { } @UseFilters(new AllExceptionsFilter(API_ID.CREATE_EVENT)) @Post('/create') @@ -124,7 +124,7 @@ export class EventController { throw new BadRequestException(ERROR_MESSAGES.INVALID_REQUEST_BODY); } const userId = '01455719-e84f-4bc8-8efa-7024874ade08'; // later come from JWT-token - // return this.eventService.updateEvent(id, updateEventDto, userId, response); + return this.eventService.updateEvent(id, updateEventDto, response); } @UseFilters(new AllExceptionsFilter(API_ID.DELETE_EVENT)) diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index c0855d2..d5a1222 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -7,7 +7,7 @@ import { import { CreateEventDto } from './dto/create-event.dto'; import { UpdateEventDto } from './dto/update-event.dto'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, In } from 'typeorm'; +import { Repository, In, Not } from 'typeorm'; import { Events } from './entities/event.entity'; import { Response } from 'express'; import APIResponse from 'src/common/utils/response'; @@ -93,6 +93,7 @@ export class EventService { try { const { filters } = requestBody; const today = new Date(); + let finalquery = `SELECT er."eventDetailId" AS "eventRepetition_eventDetailId", er.*, @@ -109,7 +110,7 @@ export class EventService { //User not pass any things then it show today and upcoming event if (!filters || Object.keys(filters).length === 0) { finalquery += ` WHERE (er."startDateTime" >= CURRENT_TIMESTAMP - OR er."endDateTime" > CURRENT_TIMESTAMP) AND status='live'`; + OR er."endDateTime" > CURRENT_TIMESTAMP) AND ed.status='live'`; } // if user pass somthing in filter then make query @@ -240,7 +241,7 @@ export class EventService { return finalquery; } - async createEvents(createEventDto, response) {} + async createEvents(createEventDto, response) { } async createEventDetailDB( createEventDto: CreateEventDto, @@ -274,6 +275,90 @@ export class EventService { return this.eventDetailRepository.save(eventDetail); } + async updateEvent(eventRepetitionId, updateBody, response) { + const apiId = 'api.update.event'; + try { + const eventRepetationresult = await this.eventRepetitionRepository.findOne({ where: { eventRepetitionId } }); + if (!eventRepetationresult) { + return response + .status(HttpStatus.NOT_FOUND) + .json( + APIResponse.error( + apiId, + 'Event Not Found', + 'No records found.', + 'NOT_FOUND', + ), + ); + } + const event = await this.eventRepository.findOne({ where: { eventId: eventRepetationresult.eventId } }) + // To delete or update all recurrentce record + if (updateBody?.target) { + const eventId = eventRepetationresult.eventId; // come frome event Repetation table + const eventDetailId = event.eventDetailId; // come from event table + // fecth all record more than one present in event detail table or not on behalf of single event id + const getAllRecord = await this.eventRepetitionRepository.find({ + where: { eventId: eventId, eventDetailId: Not(eventDetailId) } + }) + const existingEventDetail = await this.eventDetailRepository.findOne({ where: { eventDetailId: event.eventDetailId } }) + const eventRepetitionIdsToUpdate = getAllRecord.map(record => record.eventRepetitionId); + const eventDetailIdsToDelete = getAllRecord.map(record => record.eventDetailId); + // if present then need to update eventDetailId in eventReepetation table and delete record from eventDetail table + if (getAllRecord.length != 0) { + const updateResult = await this.eventRepetitionRepository.update( + { eventRepetitionId: In(eventRepetitionIdsToUpdate) }, + { eventDetailId: eventDetailId } + ) + // Delete records from the EventDetail table + const deleteResult = await this.eventDetailRepository.delete( + { eventDetailId: In(eventDetailIdsToDelete) } + ) + } + + Object.assign(existingEventDetail, updateBody) + existingEventDetail.updatedAt = new Date(); + const updateResults = await this.eventDetailRepository.save(existingEventDetail); + } + // To delete or update specific recurrentce record + else { + const existingEventDetails = await this.eventDetailRepository.findOne({ where: { eventDetailId: eventRepetationresult.eventDetailId } }) + //create new record + if (event.eventDetailId === existingEventDetails.eventDetailId) { + Object.assign(existingEventDetails, updateBody) + delete existingEventDetails.eventDetailId + const newEntry = await this.eventDetailRepository.save(existingEventDetails); + const updateEventDetails = await this.eventRepetitionRepository.update( + { eventRepetitionId: eventRepetationresult.eventRepetitionId }, + { eventDetailId: newEntry.eventDetailId } + ) + } + //update in existing record + else { + console.log("yes this is event which will not create new one "); + Object.assign(existingEventDetails, updateBody) + const newEntry = await this.eventDetailRepository.save(existingEventDetails); + } + } + return response + .status(HttpStatus.OK) + .json(APIResponse.success(apiId, "result", 'OK')) + + } + catch (error) { + console.log(error, "error"); + return response + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json( + APIResponse.error( + apiId, + ERROR_MESSAGES.INTERNAL_SERVER_ERROR, + error, + '500', + ), + ); + } + } + async createEventDB( createEventDto: CreateEventDto, eventDetail: EventDetail, @@ -419,7 +504,7 @@ export class EventService { } } - createNonRecurringEvent(createEventDto: CreateEventDto) {} + createNonRecurringEvent(createEventDto: CreateEventDto) { } async getEventOccurrences(eventId: string): Promise { return this.eventRepetitionRepository.find({ where: { eventId: eventId } }); @@ -514,7 +599,7 @@ export class EventService { if ( config.endCondition.type === 'endDate' && occurrences[occurrences.length - 1]?.endDateTime > - new Date(config.endCondition.value) + new Date(config.endCondition.value) ) { occurrences.pop(); } From fb1181b36facd92dd91560e3743b60edfc79c0c9 Mon Sep 17 00:00:00 2001 From: poojakarma Date: Fri, 2 Aug 2024 11:41:00 +0530 Subject: [PATCH 2/6] added cohortId filter --- src/modules/event/dto/search-event.dto.ts | 6 ++ src/modules/event/event.service.ts | 119 +++------------------- 2 files changed, 19 insertions(+), 106 deletions(-) diff --git a/src/modules/event/dto/search-event.dto.ts b/src/modules/event/dto/search-event.dto.ts index d9f8d0e..f37a421 100644 --- a/src/modules/event/dto/search-event.dto.ts +++ b/src/modules/event/dto/search-event.dto.ts @@ -6,6 +6,7 @@ import { IsEnum, IsString, ValidateNested, + IsUUID, } from 'class-validator'; export class FilterDto { @@ -59,6 +60,11 @@ export class FilterDto { @IsOptional() @IsString() title?: string; + + @ApiProperty({ example: 'CohortId', description: 'Cohort' }) + @IsOptional() + @IsUUID('4') + cohortId?: string; } export class SearchFilterDto { diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index 8d7d8e8..9650452 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -2,12 +2,13 @@ import { BadRequestException, HttpStatus, Injectable, + NotFoundException, NotImplementedException, } from '@nestjs/common'; import { CreateEventDto } from './dto/create-event.dto'; import { UpdateEventDto } from './dto/update-event.dto'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, In, Not } from 'typeorm'; +import { Repository, In, Not, MoreThan } from 'typeorm'; import { Events } from './entities/event.entity'; import { Response } from 'express'; import APIResponse from 'src/common/utils/response'; @@ -117,10 +118,13 @@ export class EventService { // Append LIMIT and OFFSET to the query finalquery += ` LIMIT ${limit} OFFSET ${offset}`; + const result = await this.eventRepetitionRepository.query(finalquery); + const totalCount = result[0]?.total_count // Add isEnded key based on endDateTime const finalResult = result.map((event) => { + delete event.total_count; const endDateTime = new Date(event.endDateTime); return { ...event, @@ -128,37 +132,19 @@ export class EventService { }; }); if (finalResult.length === 0) { - return response - .status(HttpStatus.NOT_FOUND) - .json( - APIResponse.error( - apiId, - 'Event Not Found', - 'No records found.', - 'NOT_FOUND', - ), - ); + throw new NotFoundException('Event Not Found') } return response .status(HttpStatus.OK) .json( APIResponse.success( apiId, - { totalCount: finalResult[0].total_count, events: finalResult }, + { totalCount, events: finalResult }, 'OK`', ), ); } catch (error) { - return response - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json( - APIResponse.error( - apiId, - ERROR_MESSAGES.INTERNAL_SERVER_ERROR, - error, - '500', - ), - ); + throw error; } } @@ -227,6 +213,11 @@ export class EventService { whereClauses.push(`"status" = 'live'`); } + // Handle cohortId filter + if (filters.cohortId) { + whereClauses.push(`ed."metadata"->>'cohortId'='${filters.cohortId}'`) + } + // Construct final query if (whereClauses.length > 0) { finalquery += ` WHERE ${whereClauses.join(' AND ')}`; @@ -266,90 +257,6 @@ export class EventService { return this.eventDetailRepository.save(eventDetail); } - async updateEvent(eventRepetitionId, updateBody, response) { - const apiId = 'api.update.event'; - try { - const eventRepetationresult = await this.eventRepetitionRepository.findOne({ where: { eventRepetitionId } }); - if (!eventRepetationresult) { - return response - .status(HttpStatus.NOT_FOUND) - .json( - APIResponse.error( - apiId, - 'Event Not Found', - 'No records found.', - 'NOT_FOUND', - ), - ); - } - const event = await this.eventRepository.findOne({ where: { eventId: eventRepetationresult.eventId } }) - // To delete or update all recurrentce record - if (updateBody?.target) { - const eventId = eventRepetationresult.eventId; // come frome event Repetation table - const eventDetailId = event.eventDetailId; // come from event table - // fecth all record more than one present in event detail table or not on behalf of single event id - const getAllRecord = await this.eventRepetitionRepository.find({ - where: { eventId: eventId, eventDetailId: Not(eventDetailId) } - }) - const existingEventDetail = await this.eventDetailRepository.findOne({ where: { eventDetailId: event.eventDetailId } }) - const eventRepetitionIdsToUpdate = getAllRecord.map(record => record.eventRepetitionId); - const eventDetailIdsToDelete = getAllRecord.map(record => record.eventDetailId); - // if present then need to update eventDetailId in eventReepetation table and delete record from eventDetail table - if (getAllRecord.length != 0) { - const updateResult = await this.eventRepetitionRepository.update( - { eventRepetitionId: In(eventRepetitionIdsToUpdate) }, - { eventDetailId: eventDetailId } - ) - // Delete records from the EventDetail table - const deleteResult = await this.eventDetailRepository.delete( - { eventDetailId: In(eventDetailIdsToDelete) } - ) - } - - Object.assign(existingEventDetail, updateBody) - existingEventDetail.updatedAt = new Date(); - const updateResults = await this.eventDetailRepository.save(existingEventDetail); - } - // To delete or update specific recurrentce record - else { - const existingEventDetails = await this.eventDetailRepository.findOne({ where: { eventDetailId: eventRepetationresult.eventDetailId } }) - //create new record - if (event.eventDetailId === existingEventDetails.eventDetailId) { - Object.assign(existingEventDetails, updateBody) - delete existingEventDetails.eventDetailId - const newEntry = await this.eventDetailRepository.save(existingEventDetails); - const updateEventDetails = await this.eventRepetitionRepository.update( - { eventRepetitionId: eventRepetationresult.eventRepetitionId }, - { eventDetailId: newEntry.eventDetailId } - ) - } - //update in existing record - else { - console.log("yes this is event which will not create new one "); - Object.assign(existingEventDetails, updateBody) - const newEntry = await this.eventDetailRepository.save(existingEventDetails); - } - } - return response - .status(HttpStatus.OK) - .json(APIResponse.success(apiId, "result", 'OK')) - - } - catch (error) { - console.log(error, "error"); - return response - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json( - APIResponse.error( - apiId, - ERROR_MESSAGES.INTERNAL_SERVER_ERROR, - error, - '500', - ), - ); - } - } - async createEventDB( createEventDto: CreateEventDto, eventDetail: EventDetail, From 0225166477be12c3345742a008d95327e11afcc2 Mon Sep 17 00:00:00 2001 From: poojakarma Date: Fri, 2 Aug 2024 15:38:28 +0530 Subject: [PATCH 3/6] Added conditon for remove or update this and following --- src/modules/event/dto/search-event.dto.ts | 6 -- src/modules/event/event.service.ts | 117 +++++++++++++++++++++- 2 files changed, 112 insertions(+), 11 deletions(-) diff --git a/src/modules/event/dto/search-event.dto.ts b/src/modules/event/dto/search-event.dto.ts index 249e598..f37a421 100644 --- a/src/modules/event/dto/search-event.dto.ts +++ b/src/modules/event/dto/search-event.dto.ts @@ -7,7 +7,6 @@ import { IsString, ValidateNested, IsUUID, - IsUUID, } from 'class-validator'; export class FilterDto { @@ -66,11 +65,6 @@ export class FilterDto { @IsOptional() @IsUUID('4') cohortId?: string; - - @ApiProperty({ example: 'CohortId', description: 'CohortId' }) - @IsOptional() - @IsUUID('4') - cohortId?: string } export class SearchFilterDto { diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index 4159a43..9595499 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -3,13 +3,12 @@ import { HttpStatus, Injectable, NotFoundException, - NotFoundException, NotImplementedException, } from '@nestjs/common'; import { CreateEventDto } from './dto/create-event.dto'; import { UpdateEventDto } from './dto/update-event.dto'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, In, Not, MoreThan } from 'typeorm'; +import { Repository, In, Not, MoreThan, MoreThanOrEqual } from 'typeorm'; import { Events } from './entities/event.entity'; import { Response } from 'express'; import APIResponse from 'src/common/utils/response'; @@ -129,8 +128,6 @@ export class EventService { const result = await this.eventRepetitionRepository.query(finalquery); const totalCount = result[0]?.total_count - const totalCount = result[0]?.total_count - // Add isEnded key based on endDateTime const finalResult = result.map((event) => { delete event.total_count; @@ -241,6 +238,117 @@ export class EventService { return finalquery; } + async updateEvent(eventRepetitionId, updateBody, response) { + const apiId = 'api.update.event'; + try { + const currentTimestamp = new Date(); + // need to check startdate of this particulr event for edit permission + const eventRepetition = await this.eventRepetitionRepository.findOne({ where: { eventRepetitionId, startDateTime: MoreThan(currentTimestamp), } }); + if (!eventRepetition) { + return response + .status(HttpStatus.NOT_FOUND) + .json( + APIResponse.error(apiId, 'Event Not Found', 'No records found.', 'NOT_FOUND') + ); + } + + const event = await this.eventRepository.findOne({ where: { eventId: eventRepetition.eventId } }); + const eventDetail = await this.eventDetailRepository.findOne({ where: { eventDetailId: event.eventDetailId } }); + + if (this.isInvalidUpdate(updateBody, eventDetail)) { + throw new BadRequestException('Not editable field'); + } + + if (updateBody?.target) { + // Handle updates or deletions for all recurrence records + await this.handleAllEventUpdate(updateBody, event, eventRepetition); + } else { + // Handle updates or deletions for a specific recurrence record + await this.handleSpecificRecurrenceUpdate(updateBody, event, eventRepetition); + } + + return response + .status(HttpStatus.OK) + .json(APIResponse.success(apiId, "result", 'OK')); + + } catch (error) { + console.error(error); + throw error; + } + } + + async handleAllEventUpdate(updateBody, event, eventRepetition) { + console.log(eventRepetition, "eventRepetation"); + + const eventId = eventRepetition.eventId; + const eventDetailId = event.eventDetailId; + + // Convert eventRepetition.startDate to a string with time zone if necessary + const startDateTime = eventRepetition.startDateTime; + const existingEventDetails = await this.eventDetailRepository.findOne({ where: { eventDetailId: eventDetailId } }); + + const recurrenceRecords = await this.eventRepetitionRepository.find({ + where: { eventId: eventId, eventDetailId: Not(eventDetailId), startDateTime: MoreThanOrEqual(startDateTime) } + }); + + if (recurrenceRecords.length > 0) { + await this.eventRepetitionRepository.update( + { eventRepetitionId: In(recurrenceRecords.map(record => record.eventRepetitionId)) }, + { eventDetailId: eventDetailId } + ); + + await this.eventDetailRepository.delete( + { eventDetailId: In(recurrenceRecords.map(record => record.eventDetailId)) } + ); + } + + if (updateBody.onlineDetails) { + await this.eventRepetitionRepository.update( + { eventDetailId: eventDetailId }, + { onlineDetails: updateBody.onlineDetails } + ); + } + + Object.assign(existingEventDetails, updateBody); + existingEventDetails.updatedAt = new Date(); + await this.eventDetailRepository.save(existingEventDetails); + } + + async handleSpecificRecurrenceUpdate(updateBody, event, eventRepetition) { + const eventDetailId = eventRepetition.eventDetailId; + + const existingEventDetails = await this.eventDetailRepository.findOne({ where: { eventDetailId: eventDetailId } }); + + if (event.eventDetailId === existingEventDetails.eventDetailId) { + Object.assign(existingEventDetails, updateBody); + delete existingEventDetails.eventDetailId; + const newEntry = await this.eventDetailRepository.save(existingEventDetails); + await this.eventRepetitionRepository.update( + { eventRepetitionId: eventRepetition.eventRepetitionId }, + { eventDetailId: newEntry.eventDetailId } + ); + } else { + Object.assign(existingEventDetails, updateBody); + await this.eventDetailRepository.save(existingEventDetails); + } + } + + isInvalidUpdate(updateBody, eventDetail) { + if (updateBody.location || (updateBody.latitude && updateBody.longitude)) { + if (eventDetail.eventType === 'online') { + return true; + } + } + + if (updateBody.onlineDetails) { + if (eventDetail.eventType === 'offline') { + return true; + } + } + + return false; + } + async createEventDetailDB( createEventDto: CreateEventDto, ): Promise { @@ -551,7 +659,6 @@ export class EventService { config.endCondition.type === 'endDate' && occurrences[occurrences.length - 1]?.endDateTime > new Date(config.endCondition.value) - new Date(config.endCondition.value) ) { occurrences.pop(); } From 8a96632ab34f7a7fe7fac318510be0dd4119ff6c Mon Sep 17 00:00:00 2001 From: poojakarma Date: Fri, 2 Aug 2024 18:48:32 +0530 Subject: [PATCH 4/6] add validation for non recurring --- src/modules/event/event.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index 9595499..aedde7e 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -253,6 +253,10 @@ export class EventService { } const event = await this.eventRepository.findOne({ where: { eventId: eventRepetition.eventId } }); + // condition for prevent non recuring event + if (!event.isRecurring && !updateBody.target) { + throw new BadRequestException('You can not pass target false beacuse event is non recurring') + } const eventDetail = await this.eventDetailRepository.findOne({ where: { eventDetailId: event.eventDetailId } }); if (this.isInvalidUpdate(updateBody, eventDetail)) { From 8324f8e802ba57ca4b2f607771880763474c39d4 Mon Sep 17 00:00:00 2001 From: poojakarma Date: Tue, 6 Aug 2024 10:19:27 +0530 Subject: [PATCH 5/6] changes for status update --- src/modules/event/dto/update-event.dto.ts | 48 ++++++++++------ src/modules/event/event.service.ts | 69 ++++++++++++----------- 2 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/modules/event/dto/update-event.dto.ts b/src/modules/event/dto/update-event.dto.ts index 092935a..9327d3a 100644 --- a/src/modules/event/dto/update-event.dto.ts +++ b/src/modules/event/dto/update-event.dto.ts @@ -1,17 +1,20 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsNotEmpty, IsOptional, IsString, IsUUID, IsEnum, IsLongitude, IsLatitude, IsBoolean, IsInt, Min, IsDateString, IsObject, ValidateIf } from 'class-validator'; +import { IsNotEmpty, IsOptional, IsString, IsUUID, IsEnum, IsLongitude, IsLatitude, IsBoolean, IsInt, Min, IsDateString, IsObject, ValidateIf, ValidateNested } from 'class-validator'; +import { MeetingDetails } from "src/common/utils/types"; +import { MeetingDetailsDto } from "./create-event.dto"; +import { Type } from "class-transformer"; export class UpdateEventDto { - @ApiProperty({ - type: String, - description: 'title', - example: 'Sample Event' - }) - @IsString() - @IsNotEmpty() - @IsOptional() - title?: string; + // @ApiProperty({ + // type: String, + // description: 'title', + // example: 'Sample Event' + // }) + // @IsString() + // @IsNotEmpty() + // @IsOptional() + // title?: string; @ApiProperty({ @@ -33,11 +36,26 @@ export class UpdateEventDto { example: true }) @IsBoolean() - target: boolean; + isMainEvent: boolean; - // Validation to ensure if target is true, title or status must be provided - @ValidateIf(o => !o.title && !o.status) // Ensure that if neither title nor status is provided, validation fails - @IsNotEmpty({ message: 'If target is provided, at least one of title or status must be provided.' }) + // @ApiProperty({ + // type: MeetingDetailsDto, + // description: 'Online Meeting Details', + // example: { + // url: 'https://example.com/meeting', + // id: '123-456-789', + // password: 'xxxxxxx', + // }, + // }) + // @IsObject() + // @ValidateNested({ each: true }) + // @IsOptional() + // @Type(() => MeetingDetailsDto) + // meetingDetails: MeetingDetails; + + // Validation to ensure if isMainEvent is true, title or status must be provided + @ValidateIf(o => !o.title && !o.status && !o.onlineDetails && !o.location && !o.latitude) // Ensure that if neither title nor status is provided, validation fails + @IsNotEmpty({ message: 'If isMainEvent is provided, at least one of title or status must be provided.' }) dummyField?: any; @@ -105,8 +123,6 @@ export class UpdateEventDto { // latitude: number; - - // @ApiProperty({ // type: String, // description: 'Short Description', diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index aedde7e..70331a5 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -242,55 +242,45 @@ export class EventService { const apiId = 'api.update.event'; try { const currentTimestamp = new Date(); - // need to check startdate of this particulr event for edit permission - const eventRepetition = await this.eventRepetitionRepository.findOne({ where: { eventRepetitionId, startDateTime: MoreThan(currentTimestamp), } }); + // need to check startdate of this particuler event for edit permission + const eventRepetition = await this.eventRepetitionRepository.findOne({ where: { eventRepetitionId, startDateTime: MoreThan(currentTimestamp) } }); if (!eventRepetition) { - return response - .status(HttpStatus.NOT_FOUND) - .json( - APIResponse.error(apiId, 'Event Not Found', 'No records found.', 'NOT_FOUND') - ); + throw new NotFoundException('Event Not found') } const event = await this.eventRepository.findOne({ where: { eventId: eventRepetition.eventId } }); // condition for prevent non recuring event - if (!event.isRecurring && !updateBody.target) { - throw new BadRequestException('You can not pass target false beacuse event is non recurring') + if (!event.isRecurring && !updateBody.isMainEvent) { + throw new BadRequestException('You can not pass isMainEvent false beacuse event is non recurring') } const eventDetail = await this.eventDetailRepository.findOne({ where: { eventDetailId: event.eventDetailId } }); if (this.isInvalidUpdate(updateBody, eventDetail)) { throw new BadRequestException('Not editable field'); } - - if (updateBody?.target) { + let result; + if (updateBody?.isMainEvent) { // Handle updates or deletions for all recurrence records - await this.handleAllEventUpdate(updateBody, event, eventRepetition); + result = await this.handleAllEventUpdate(updateBody, event, eventRepetition); } else { // Handle updates or deletions for a specific recurrence record - await this.handleSpecificRecurrenceUpdate(updateBody, event, eventRepetition); + result = await this.handleSpecificRecurrenceUpdate(updateBody, event, eventRepetition); } return response .status(HttpStatus.OK) - .json(APIResponse.success(apiId, "result", 'OK')); + .json(APIResponse.success(apiId, result, 'OK')); } catch (error) { - console.error(error); throw error; } } async handleAllEventUpdate(updateBody, event, eventRepetition) { - console.log(eventRepetition, "eventRepetation"); - const eventId = eventRepetition.eventId; const eventDetailId = event.eventDetailId; - - // Convert eventRepetition.startDate to a string with time zone if necessary - const startDateTime = eventRepetition.startDateTime; const existingEventDetails = await this.eventDetailRepository.findOne({ where: { eventDetailId: eventDetailId } }); - + const startDateTime = eventRepetition.startDateTime; const recurrenceRecords = await this.eventRepetitionRepository.find({ where: { eventId: eventId, eventDetailId: Not(eventDetailId), startDateTime: MoreThanOrEqual(startDateTime) } }); @@ -313,28 +303,41 @@ export class EventService { ); } - Object.assign(existingEventDetails, updateBody); + Object.assign(existingEventDetails, updateBody, { eventRepetitionId: eventRepetition.eventRepetitionId }); existingEventDetails.updatedAt = new Date(); - await this.eventDetailRepository.save(existingEventDetails); + const result = await this.eventDetailRepository.save(existingEventDetails); + return result; } async handleSpecificRecurrenceUpdate(updateBody, event, eventRepetition) { const eventDetailId = eventRepetition.eventDetailId; - const existingEventDetails = await this.eventDetailRepository.findOne({ where: { eventDetailId: eventDetailId } }); - + existingEventDetails.updatedAt = new Date() + let result; if (event.eventDetailId === existingEventDetails.eventDetailId) { - Object.assign(existingEventDetails, updateBody); + if (existingEventDetails.status === 'archived') { + throw new BadRequestException('Event is already archived') + } + Object.assign(existingEventDetails, updateBody, { eventRepetitionId: eventRepetition.eventRepetitionId }); delete existingEventDetails.eventDetailId; - const newEntry = await this.eventDetailRepository.save(existingEventDetails); - await this.eventRepetitionRepository.update( - { eventRepetitionId: eventRepetition.eventRepetitionId }, - { eventDetailId: newEntry.eventDetailId } - ); + result = await this.eventDetailRepository.save(existingEventDetails); + // const updateResult = await this.eventRepetitionRepository.update( + // { eventRepetitionId: eventRepetition.eventRepetitionId }, + // { eventDetailId: newEntry.eventDetailId } + // ); + eventRepetition.eventDetailId = result.eventDetailId; + const UpdateResult = await this.eventRepetitionRepository.save(eventRepetition); } else { - Object.assign(existingEventDetails, updateBody); - await this.eventDetailRepository.save(existingEventDetails); + Object.assign(existingEventDetails, updateBody, { eventRepetitionId: eventRepetition.eventRepetitionId }); + result = await this.eventDetailRepository.save(existingEventDetails); + } + if (updateBody.onlineDetails) { + result = await this.eventRepetitionRepository.update( + { eventDetailId: eventDetailId }, + { onlineDetails: updateBody.onlineDetails } + ); } + return result; } isInvalidUpdate(updateBody, eventDetail) { From 0cb885b52445f8a6eaaebb9882d6ec8c4a51f837 Mon Sep 17 00:00:00 2001 From: poojakarma Date: Tue, 6 Aug 2024 14:54:42 +0530 Subject: [PATCH 6/6] reolve conflicts --- src/modules/event/event.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index 70331a5..58786bd 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -126,7 +126,7 @@ export class EventService { finalquery += ` LIMIT ${limit} OFFSET ${offset}`; const result = await this.eventRepetitionRepository.query(finalquery); - const totalCount = result[0]?.total_count + const totalCount = result[0]?.total_count; // Add isEnded key based on endDateTime const finalResult = result.map((event) => { @@ -151,7 +151,6 @@ export class EventService { ), ); } catch (error) { - throw error throw error; } }