Skip to content

Commit

Permalink
Merge pull request #55 from Xitija/unit_test
Browse files Browse the repository at this point in the history
PS: 3353 Add generic typeorm service
  • Loading branch information
snehal0904 authored Jan 17, 2025
2 parents 955e1af + 9356f31 commit 8668bb5
Show file tree
Hide file tree
Showing 21 changed files with 693 additions and 321 deletions.
2 changes: 0 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import { AppController } from './app.controller';
import { AppService } from './app.service';
import { EventModule } from './modules/event/event.module';
import { DatabaseModule } from './common/database-modules';
import { AttendeesModule } from './modules/attendees/attendees.module';
import { AttendanceModule } from './modules/attendance/attendance.module';

@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
EventModule,
DatabaseModule,
AttendeesModule,
AttendanceModule,
],
controllers: [AppController],
Expand Down
37 changes: 37 additions & 0 deletions src/common/config/routeConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module.exports = {
routes: [
{
sourceRoute: '/event-service/event/v1/create',
type: 'POST',
inSequence: true,
orchestrated: true,
targetRoute: {
path: '/event-service/event/v1/create',
type: 'POST',
functionName: 'createEvent',
},
},
{
sourceRoute: '/event-service/event/v1/:id',
type: 'PATCH',
inSequence: true,
orchestrated: true,
targetRoute: {
path: '/event-service/event/v1/:id',
type: 'PATCH',
functionName: 'updateEvent',
},
},
{
sourceRoute: '/event-service/event/v1/list',
type: 'POST',
inSequence: true,
orchestrated: true,
targetRoute: {
path: '/event-service/event/v1/list',
type: 'POST',
functionName: 'listEvents',
},
},
],
};
4 changes: 2 additions & 2 deletions src/common/pipes/event-validation.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import {
BadRequestException,
ArgumentMetadata,
} from '@nestjs/common';
import { CreateEventDto } from 'src/modules/event/dto/create-event.dto';
import { CreateEventDto } from '../../modules/event/dto/create-event.dto';
import { ERROR_MESSAGES } from '../utils/constants.util';
import {
ValidationArguments,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import { EndConditionType } from '../utils/types';
import { UpdateEventDto } from 'src/modules/event/dto/update-event.dto';
import { UpdateEventDto } from '../../modules/event/dto/update-event.dto';

@Injectable()
export class DateValidationPipe implements PipeTransform {
Expand Down
101 changes: 101 additions & 0 deletions src/common/services/typeorm.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Injectable } from '@nestjs/common';
import {
DeepPartial,
DeleteResult,
EntityManager,
EntityTarget,
InsertResult,
Repository,
UpdateResult,
} from 'typeorm';
import { InjectEntityManager } from '@nestjs/typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';

@Injectable()
export class TypeormService {
constructor(
@InjectEntityManager() private readonly entityManager: EntityManager,
) {}

// Get repository for a specific entity
private getRepository<T>(entity: EntityTarget<T>): Repository<T> {
return this.entityManager.getRepository(entity);
}

// Find all records with optional conditions
async find<T>(entity: EntityTarget<T>, options?: object): Promise<T[]> {
return await this.getRepository(entity).find(options);
}

// Find one record by conditions
async findOne<T>(entity: EntityTarget<T>, conditions: object): Promise<T> {
const record = await this.getRepository(entity).findOne(conditions);
return record;
}

// Save a new entity or update existing
async save<T>(entity: EntityTarget<T>, data: Partial<T>): Promise<T> {
return await this.getRepository(entity).save(data as DeepPartial<T>);
}

// Update an existing entity by ID
async update<T>(
entity: EntityTarget<T>,
criteria: any,
partialEntity: QueryDeepPartialEntity<T>,
): Promise<UpdateResult> {
const repository = this.getRepository(entity);
return await repository.update(criteria, partialEntity);
}

// Delete an entity by ID
async delete<T>(
entity: EntityTarget<T>,
id: string | string[] | object,
): Promise<DeleteResult> {
const repository = this.getRepository(entity);
return repository.delete(id);
}

// Execute a raw query
async query<T>(query: string, parameters?: any[]): Promise<any> {
return await this.entityManager.query(query, parameters);
}

async queryWithBuilder<T>(
entity: EntityTarget<T>,
alias: string,
callback: (qb: any) => any,
): Promise<T[]> {
const repository = this.getRepository(entity);

// Create a query builder
const queryBuilder = repository.createQueryBuilder(alias);

// Apply custom query modifications using the callback
callback(queryBuilder);

// Execute and return results
return await queryBuilder.getMany();
}

async insert<T>(
entity: EntityTarget<T>,
values: QueryDeepPartialEntity<T>[],
returningFields: string[] = [],
): Promise<InsertResult> {
const repository = this.getRepository(entity);

const queryBuilder = repository
.createQueryBuilder()
.insert()
.into(entity)
.values(values);

if (returningFields.length) {
queryBuilder.returning(returningFields);
}

return await queryBuilder.execute();
}
}
10 changes: 9 additions & 1 deletion src/common/utils/constants.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ export const ERROR_MESSAGES = {
USERID_INVALID: 'Invalid UserId',
USERID_REQUIRED: 'UserId Required',
PROVIDE_ONE_USERID_IN_QUERY: 'Please provide userId in query params',
ENVIRONMENT_VARIABLES_MISSING: 'Environment variables missing!',
USERS_NOT_FOUND_IN_SERVICE: 'Users not found in user service',
SERVICE_NOT_FOUND: 'Service not found',
NO_PARTICIPANTS_FOUND: 'No participants found for the meeting',
MEETING_NOT_FOUND: 'Meeting not found',
NO_USERS_FOUND: 'No users found in system',
EVENT_DOES_NOT_EXIST: 'Event does not exist',
API_REQ_FAILURE: (url: string) => `Error occurred on API Request: ${url}`,
DB_QUERY_FAILURE: (url: string) => `Database Query Failed on API: ${url}`,
API_FAILURE: (url: string) => `API Failure: ${url}`,
Expand All @@ -111,6 +118,7 @@ export const SUCCESS_MESSAGES = {
EVENT_CREATED_LOG: (url: string) => `Event created with ID: ${url}`,
EVENTS_FETCHED_LOG: 'Successfully fetched events',
EVENT_UPDATED_LOG: 'Successfully updated events',
ATTENDANCE_MARKED_FOR_MEETING: 'Attendance marked for meeting',
};

export const API_ID = {
Expand All @@ -129,5 +137,5 @@ export const API_ID = {
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',
MARK_ZOOM_ATTENDANCE: 'mark.zoom.event.attendance',
MARK_EVENT_ATTENDANCE: 'api.event.mark.attendance',
};
2 changes: 1 addition & 1 deletion src/common/utils/transformer/date.transformer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { getTimezoneDateString } from 'src/common/utils/pipe.util';
import { getTimezoneDateString } from '../../../common/utils/pipe.util';
import { ValueTransformer } from 'typeorm';

export class TimeZoneTransformer implements ValueTransformer {
Expand Down
47 changes: 47 additions & 0 deletions src/common/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,50 @@ export type RecurrencePattern = {
value: string;
};
};

export type ZoomParticipant = {
id: string;
user_id: string;
name: string;
user_email: string;
join_time: string;
leave_time: string;
duration: number;
registrant_id: string;
failover: boolean;
status: string;
groupId: string;
internal_user: boolean;
};

export type UserDetails = {
userId: string;
username: string;
email: string;
name: string;
role: string;
mobile: string;
createdBy: string;
updatedBy: string;
createdAt: string;
updatedAt: string;
status: string;
total_count: string;
};

export interface AttendanceRecord {
userId: string;
attendance: 'present' | 'absent';
metaData: {
duration: number;
joinTime: string;
leaveTime: string;
};
}

export interface InZoomMeetingUserDetails {
user_email: string;
duration: number;
join_time: string;
leave_time: string;
}
28 changes: 19 additions & 9 deletions src/modules/attendance/attendance.controller.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,43 @@
import { Body, Controller, Post, Res, Req, UseFilters } from '@nestjs/common';
import {
Body,
Controller,
Post,
Res,
Req,
UseFilters,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { Response, Request } from 'express';
import { ApiBody, ApiQuery, ApiTags } from '@nestjs/swagger';
import { AttendanceService } from './attendance.service';
import { AllExceptionsFilter } from 'src/common/filters/exception.filter';
import { MarkZoomAttendanceDto } from './dto/MarkZoomAttendance.dto';
import { checkValidUserId } from 'src/common/utils/functions.util';
import { API_ID, ERROR_MESSAGES } from 'src/common/utils/constants.util';
import { AllExceptionsFilter } from '../../common/filters/exception.filter';
import { MarkMeetingAttendanceDto } from './dto/MarkAttendance.dto';
import { checkValidUserId } from '../../common/utils/functions.util';
import { API_ID, ERROR_MESSAGES } from '../../common/utils/constants.util';

@Controller('attendance/v1')
@ApiTags('Event-Attendance')
export class EventAttendance {
constructor(private readonly attendanceService: AttendanceService) {}

@UseFilters(new AllExceptionsFilter(API_ID.MARK_ZOOM_ATTENDANCE))
@UseFilters(new AllExceptionsFilter(API_ID.MARK_EVENT_ATTENDANCE))
@Post('/markeventattendance')
@ApiBody({ type: MarkZoomAttendanceDto })
@ApiBody({ type: MarkMeetingAttendanceDto })
@ApiQuery({
name: 'userId',
required: true,
description: ERROR_MESSAGES.USERID_REQUIRED,
example: '123e4567-e89b-12d3-a456-426614174000',
})
@UsePipes(new ValidationPipe({ transform: true }))
async markEventAttendance(
@Body() markZoomAttendanceDto: MarkZoomAttendanceDto,
@Body() markZoomAttendanceDto: MarkMeetingAttendanceDto,
@Res() response: Response,
@Req() request: Request,
): Promise<Response> {
const userId: string = checkValidUserId(request.query?.userId);
return this.attendanceService.markAttendanceForZoomMeetingParticipants(
return this.attendanceService.markAttendanceForMeetingParticipants(
markZoomAttendanceDto,
userId,
response,
Expand Down
15 changes: 13 additions & 2 deletions src/modules/attendance/attendance.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@ import { EventAttendance } from './attendance.controller';
import { AttendanceService } from './attendance.service';
import { HttpModule } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { OnlineMeetingAdapter } from '../../online-meeting-adapters/onlineMeeting.adapter';
import { ZoomService } from '../../online-meeting-adapters/zoom/zoom.adapter';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EventRepetition } from '../event/entities/eventRepetition.entity';
import { TypeormService } from 'src/common/services/typeorm.service';

@Module({
imports: [HttpModule],
imports: [HttpModule, TypeOrmModule.forFeature([EventRepetition])],
controllers: [EventAttendance],
providers: [AttendanceService, ConfigService],
providers: [
AttendanceService,
ConfigService,
OnlineMeetingAdapter,
ZoomService,
TypeormService,
],
})
export class AttendanceModule {}
Loading

0 comments on commit 8668bb5

Please sign in to comment.