Skip to content

Commit

Permalink
Merge pull request #11 from Xitija/main
Browse files Browse the repository at this point in the history
PS-1410 Attendees for Private events only
  • Loading branch information
vaivk369 authored Jul 18, 2024
2 parents 26bcbab + 1c2d35a commit dcbd6b0
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 163 deletions.
5 changes: 4 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"singleQuote": true,
"trailingComma": "all"
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"semi": true
}
16 changes: 16 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"prettier.singleQuote": true,
"prettier.printWidth": 80,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"eslint.codeAction.showDocumentation": {
"enable": true
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": ["typescript", "javascript"]
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.42.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"prettier": "^3.3.3",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "^29.1.0",
Expand Down
1 change: 0 additions & 1 deletion src/.env.development
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

41 changes: 7 additions & 34 deletions src/common/pipes/event-validation.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,44 +135,17 @@ export class RecurringEndDateValidationPipe implements PipeTransform {
}

@Injectable()
export class ParamsValidationPipe implements PipeTransform {
export class AttendeesValidationPipe implements PipeTransform {
transform(createEventDto: CreateEventDto) {
if (createEventDto.isRestricted) {
const params = createEventDto.params;
if (!params || typeof params !== 'object') {
throw new BadRequestException('Invalid params object');
}
const attendees = createEventDto?.attendees;
console.log('attendees', attendees);

// if (!params.cohortIds && !params.userIds) {
// throw new BadRequestException(
// 'Either cohortIds or userIds must be provided in params',
// );
// }

// if (params.cohortIds && params.userIds) {
// throw new BadRequestException(
// 'Only one of cohortIds or userIds should be provided in params',
// );
// }

// if (params.cohortIds) {
// this.validateUUIDs(params.cohortIds);
// } else if (params.userIds) {
// this.validateUUIDs(params.userIds);
// }
} else if (!createEventDto.isRestricted) {
createEventDto.params = {};
if (!createEventDto.isRestricted) {
if (attendees && attendees.length) {
throw new BadRequestException(ERROR_MESSAGES.ATTENDEES_NOT_REQUIRED);
}
}

return createEventDto;
}

private validateUUIDs(ids: string[]) {
const uuidRegex = /^[a-f\d]{8}(-[a-f\d]{4}){3}-[a-f\d]{12}$/i; // UUID regex pattern
for (const id of ids) {
if (!uuidRegex.test(id)) {
throw new BadRequestException(`Invalid UUID format: ${id}`);
}
}
}
}
4 changes: 2 additions & 2 deletions src/common/utils/constants.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const ERROR_MESSAGES = {
'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',
ATTENDEES_REQUIRED: 'Attendees required for private event',
ATTENDEES_NOT_REQUIRED: 'Attendees 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',
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 @@ -22,4 +22,3 @@ export class TimeZoneTransformer implements ValueTransformer {
);
}
}

25 changes: 18 additions & 7 deletions src/modules/event/dto/create-event.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ import {
IsObject,
ValidateIf,
ValidateNested,
Validate,
IsArray,
IsDefined,
ArrayMinSize,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { EventTypes } from 'src/common/utils/types';
import { ERROR_MESSAGES } from 'src/common/utils/constants.util';

export class MeetingDetailsDto {
@ApiProperty({ description: 'Meeting ID', example: 94292617 })
Expand Down Expand Up @@ -188,13 +191,21 @@ export class CreateEventDto {

@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: { invitees: ['e9fec05a-d6ab-44be-bfa4-eaeef2ef8fe9'] },
description: 'Attendees',
example: {
attendees: [
'eff008a8-2573-466d-b877-fddf6a4fc13e',
'e9fec05a-d6ab-44be-bfa4-eaeef2ef8fe9',
],
},
})
@IsObject()
params: any; // direct userIds
@ValidateIf((o) => o.isRestricted === true)
@IsDefined({ message: ERROR_MESSAGES.ATTENDEES_REQUIRED })
@IsArray()
@Type(() => String)
@ArrayMinSize(1)
@IsUUID('4', { each: true })
attendees: string[];

@ApiProperty({
type: Object,
Expand Down
5 changes: 2 additions & 3 deletions src/modules/event/entities/event.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,13 @@ export class Events {
@Column({ type: 'uuid' })
eventDetailId: string;

// OR onetomany
@OneToOne(() => EventDetail, (eventDetail) => eventDetail.events, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'eventDetailId' })
eventDetail: EventDetail;

// @OneToMany(() => EventRepetition, (eventRepetition) => eventRepetition.event)
// eventRepetitions: EventRepetition[];
@OneToMany(() => EventRepetition, (eventRepetition) => eventRepetition.event)
eventRepetitions: EventRepetition[];
}
17 changes: 9 additions & 8 deletions src/modules/event/entities/eventDetail.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CreateDateColumn,
UpdateDateColumn,
OneToMany,
OneToOne,
} from 'typeorm';
import { Events } from './event.entity';
import { EventRepetition } from './eventRepetition.entity';
Expand Down Expand Up @@ -52,8 +53,8 @@ export class EventDetail {
@Column({ type: 'text' })
description: string;

@Column({ type: 'jsonb' })
params: object;
@Column('text', { array: true })
attendees: string[];

@CreateDateColumn({
type: 'timestamptz',
Expand Down Expand Up @@ -84,12 +85,12 @@ export class EventDetail {
@Column({ type: 'jsonb', nullable: true })
metadata: object;

@OneToMany(() => Events, (event) => event.eventDetail)
@OneToOne(() => Events, (event) => event.eventDetail)
events: Event[];

// @OneToMany(
// () => EventRepetition,
// (eventRepetition) => eventRepetition.eventDetail,
// )
// eventRepetitions: EventRepetition[];
@OneToMany(
() => EventRepetition,
(eventRepetition) => eventRepetition.eventDetail,
)
eventRepetitions: EventRepetition[];
}
26 changes: 13 additions & 13 deletions src/modules/event/entities/eventRepetition.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ export class EventRepetition {
})
endDateTime: Date;

// @ManyToOne(() => Events, (event) => event.eventRepetitions, {
// onDelete: 'CASCADE',
// onUpdate: 'CASCADE',
// })
// @JoinColumn({ name: 'eventId' })
// event: Event;

// @ManyToOne(() => EventDetail, (eventDetail) => eventDetail.eventRepetitions, {
// onDelete: 'CASCADE',
// onUpdate: 'CASCADE',
// })
// @JoinColumn({ name: 'eventDetailId' })
// eventDetail: EventDetail;
@ManyToOne(() => Events, (event) => event.eventRepetitions, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'eventId' })
event: Event;

@ManyToOne(() => EventDetail, (eventDetail) => eventDetail.eventRepetitions, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'eventDetailId' })
eventDetail: EventDetail;
}
84 changes: 67 additions & 17 deletions src/modules/event/event.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, UsePipes, Res, ValidationPipe, BadRequestException, ParseUUIDPipe, UseFilters } from '@nestjs/common';
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
UsePipes,
Res,
ValidationPipe,
BadRequestException,
ParseUUIDPipe,
UseFilters,
} from '@nestjs/common';
import { EventService } from './event.service';
import { CreateEventDto } from './dto/create-event.dto';
import { UpdateEventDto } from './dto/update-event.dto';
Expand All @@ -13,53 +27,80 @@ import {
} from '@nestjs/swagger';
import { Response } from 'express';
import { SearchFilterDto } from './dto/search-event.dto';
import { DateValidationPipe, RegistrationDateValidationPipe, ParamsValidationPipe } from 'src/common/pipes/event-validation.pipe';
import {
DateValidationPipe,
RegistrationDateValidationPipe,
AttendeesValidationPipe,
} from 'src/common/pipes/event-validation.pipe';
import { ConfigService } from '@nestjs/config';
import { AllExceptionsFilter } from 'src/common/filters/exception.filter';
import { API_ID, ERROR_MESSAGES, SUCCESS_MESSAGES } from 'src/common/utils/constants.util';
import {
API_ID,
ERROR_MESSAGES,
SUCCESS_MESSAGES,
} from 'src/common/utils/constants.util';

@Controller('event/v1')
@ApiTags('Create Event')
export class EventController {
constructor(private readonly eventService: EventService,
constructor(
private readonly eventService: EventService,
private readonly configService: ConfigService,
) { }
) {}

@UseFilters(new AllExceptionsFilter(API_ID.CREATE_EVENT))
@Post('/create')
@ApiBody({ type: CreateEventDto })
@UsePipes(new ValidationPipe({ transform: true }), new DateValidationPipe, new RegistrationDateValidationPipe, new ParamsValidationPipe)
@UsePipes(
new ValidationPipe({ transform: true }),
new DateValidationPipe(new ConfigService()),
new RegistrationDateValidationPipe(new ConfigService()),
new AttendeesValidationPipe(),
)
@ApiCreatedResponse({
description: SUCCESS_MESSAGES.EVENT_CREATED,
})
@ApiBadRequestResponse({ description: ERROR_MESSAGES.INVALID_REQUEST_BODY })
@ApiInternalServerErrorResponse({ description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR })
async create(@Body() createEventDto: CreateEventDto, @Res() response: Response,) {
@ApiInternalServerErrorResponse({
description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR,
})
async create(
@Body() createEventDto: CreateEventDto,
@Res() response: Response,
) {
const userId = '016badad-22b0-4566-88e9-aab1b35b1dfc'; // later come from JWT-token
this.configService;
console.log('createEventDtocontr', createEventDto);
return this.eventService.createEvent(createEventDto, userId, response);
}

@UseFilters(new AllExceptionsFilter(API_ID.GET_EVENTS))
@Post('/list')
@ApiBody({ type: SearchFilterDto })
@ApiInternalServerErrorResponse({ description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR })
@ApiInternalServerErrorResponse({
description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR,
})
@UsePipes(new ValidationPipe({ transform: true }))
@ApiOkResponse({
description: 'Searched',
status: 200
status: 200,
})
async findAll(@Res() response: Response, @Body() requestBody: SearchFilterDto) {
async findAll(
@Res() response: Response,
@Body() requestBody: SearchFilterDto,
) {
// return this.eventService.getEvents(response, requestBody);
}

@UseFilters(new AllExceptionsFilter(API_ID.GET_EVENT_BY_ID))
@Get('/:id')
@ApiOkResponse({
description: 'Get event details by id',
status: 200
status: 200,
})
@ApiInternalServerErrorResponse({
description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR,
})
@ApiInternalServerErrorResponse({ description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR })
findOne(@Param('id', ParseUUIDPipe) id: string, @Res() response: Response) {
// return this.eventService.getEventByID(id, response);
}
Expand All @@ -68,11 +109,17 @@ export class EventController {
@Patch('/:id')
@ApiBody({ type: UpdateEventDto })
@ApiResponse({ status: 200, description: SUCCESS_MESSAGES.EVENT_UPDATED })
@ApiInternalServerErrorResponse({ description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR })
@ApiInternalServerErrorResponse({
description: ERROR_MESSAGES.INTERNAL_SERVER_ERROR,
})
@UsePipes(new ValidationPipe({ transform: true }))
updateEvent(@Param('id', ParseUUIDPipe) id: string, @Body() updateEventDto: UpdateEventDto, @Res() response: Response) {
updateEvent(
@Param('id', ParseUUIDPipe) id: string,
@Body() updateEventDto: UpdateEventDto,
@Res() response: Response,
) {
if (!updateEventDto || Object.keys(updateEventDto).length === 0) {
throw new BadRequestException(ERROR_MESSAGES.INVALID_REQUEST_BODY)
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);
Expand All @@ -82,7 +129,10 @@ export class EventController {
@Delete('/:id')
@ApiResponse({ status: 200, description: SUCCESS_MESSAGES.EVENT_DELETED })
@ApiResponse({ status: 404, description: SUCCESS_MESSAGES.EVENT_NOT_FOUND })
deleteEvent(@Param('id', ParseUUIDPipe) id: string, @Res() response: Response) {
deleteEvent(
@Param('id', ParseUUIDPipe) id: string,
@Res() response: Response,
) {
// return this.eventService.deleteEvent(id, response);
}
}
Loading

0 comments on commit dcbd6b0

Please sign in to comment.