From 648f7d9fd1c1efe46a4f36f770e55cff98f41cf3 Mon Sep 17 00:00:00 2001 From: Thai Date: Wed, 12 Aug 2020 23:59:46 -0700 Subject: [PATCH 1/9] Adding event sign in endpoint (WIP). --- src/controllers/EventController.ts | 44 +++++++++++++++++++-- src/entities/AppUser.ts | 3 +- src/entities/index.ts | 2 +- src/mappers/AppUserMapper.ts | 43 ++++++++++++++++++++ src/mappers/index.ts | 1 + src/payloads/AppUser.ts | 63 +++++++++++++++++++++++++++++- src/payloads/index.ts | 2 +- src/services/AppUserService.ts | 8 ++++ 8 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 src/mappers/AppUserMapper.ts diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index c496f53d..a17361a1 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -2,21 +2,33 @@ import { JsonController, Param, Get, Post, Delete, Body } from 'routing-controll import { singleton, inject } from 'tsyringe'; import { ResponseSchema } from 'routing-controllers-openapi'; -import { Event } from '@Entities'; -import { EventRequest, EventResponse, MultipleEventResponse } from '@Payloads'; -import { EventService } from '@Services'; -import { EventMapper } from '@Mappers'; +import { Event, AppUser, AppUserRole } from '@Entities'; +import { + EventRequest, + EventResponse, + MultipleEventResponse, + EventSignInRequest, + EventSignInResponse, +} from '@Payloads'; +import { AppUserService, EventService } from '@Services'; +import { AppUserMapper, EventMapper } from '@Mappers'; @singleton() @JsonController('/api/events') export class EventController { + private appUserService: AppUserService; + private appUserMapper: AppUserMapper; private eventService: EventService; private eventMapper: EventMapper; constructor( + @inject(AppUserService) appUserService: AppUserService, + @inject(AppUserMapper) appUserMapper: AppUserMapper, @inject(EventService) eventService: EventService, @inject(EventMapper) eventMapper: EventMapper ) { + this.appUserService = appUserService; + this.appUserMapper = appUserMapper; this.eventService = eventService; this.eventMapper = eventMapper; } @@ -75,4 +87,28 @@ export class EventController { return this.eventMapper.entityToResponse(deletedEvent); } + + @Post('/:eventID/signin') + @ResponseSchema(EventSignInResponse) + async signInToEvent( + @Param('eventID') eventID: number, + @Body() appUserRequest: EventSignInRequest + ): Promise { + const { email } = appUserRequest; + const appUserFromEmail = await this.appUserService.getAppUserByEmail(email); + + if (appUserFromEmail == undefined) { + const newAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); + const savedAppUser = await this.appUserService.saveAppUser(newAppUser); + return this.appUserMapper.entityToResponse(savedAppUser); + } else { + const { role } = appUserFromEmail; + + if (role !== AppUserRole.GUEST) { + // return HTTP error + } else { + // TODO + } + } + } } diff --git a/src/entities/AppUser.ts b/src/entities/AppUser.ts index 010b40d1..6fa87d98 100644 --- a/src/entities/AppUser.ts +++ b/src/entities/AppUser.ts @@ -7,6 +7,7 @@ export enum AppUserRole { OFFICER = 'officer', MEMBER = 'member', INDUCTEE = 'inductee', + GUEST = 'Guest', } /** @@ -41,7 +42,7 @@ export class AppUser { @Column({ type: 'enum', enum: AppUserRole, - default: AppUserRole.INDUCTEE, + default: AppUserRole.GUEST, }) role: string; } diff --git a/src/entities/index.ts b/src/entities/index.ts index 3ebafc08..d58ce2ee 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1,4 +1,4 @@ -export { AppUser } from './AppUser'; +export { AppUser, AppUserRole } from './AppUser'; export { Attendance } from './Attendance'; export { Event, EventStatus, EventType } from './Event'; export { InducteeStat } from './InducteeStat'; diff --git a/src/mappers/AppUserMapper.ts b/src/mappers/AppUserMapper.ts new file mode 100644 index 00000000..4b1ffc4e --- /dev/null +++ b/src/mappers/AppUserMapper.ts @@ -0,0 +1,43 @@ +import { EventSignInRequest, EventSignInResponse } from '@Payloads'; +import { AppUser } from '@Entities'; +import { AppUserRepositoryToken } from '@Repositories'; + +import { Repository } from 'typeorm'; +import { classToPlain, plainToClass } from 'class-transformer'; +import { singleton, inject } from 'tsyringe'; + +@singleton() +export class AppUserMapper { + private appUserRepository: Repository; + + constructor(@inject(AppUserRepositoryToken) appUserRepository: Repository) { + this.appUserRepository = appUserRepository; + } + + requestToNewEntity(appUserRequest: EventSignInRequest): AppUser { + const plainAppUserRequest: Object = classToPlain(appUserRequest); + return this.appUserRepository.create(plainAppUserRequest); + } + + async requestToExistingEntity( + appUserRequest: EventSignInRequest, + appUserId: number + ): Promise { + const appUserObj: AppUser = appUserRequest as AppUser; + appUserObj.id = appUserId; + + const appUser: AppUser = await this.appUserRepository.preload(appUserObj); + if (appUser == undefined) { + return undefined; + } + + return appUser; + } + + entityToResponse(appUser: AppUser): EventSignInResponse { + const plainAppUser: Object = classToPlain(appUser); + const appUserResponse: EventSignInResponse = plainToClass(EventSignInResponse, plainAppUser); + + return appUserResponse; + } +} diff --git a/src/mappers/index.ts b/src/mappers/index.ts index a2cb8d47..063e97a1 100644 --- a/src/mappers/index.ts +++ b/src/mappers/index.ts @@ -1 +1,2 @@ export { EventMapper } from './EventMapper'; +export { AppUserMapper } from './AppUserMapper'; diff --git a/src/payloads/AppUser.ts b/src/payloads/AppUser.ts index ce030a74..3ef54f32 100644 --- a/src/payloads/AppUser.ts +++ b/src/payloads/AppUser.ts @@ -1,6 +1,67 @@ -import { IsInt } from 'class-validator'; +import { + IsInt, + IsString, + IsEmail, + IsBoolean, + IsEnum, + IsOptional, + ValidateNested, +} from 'class-validator'; +import { AppUserRole, InductionClass } from '@Entities'; +import { Type } from 'class-transformer'; export class AppUserPKPayload { @IsInt() readonly id: number; } + +abstract class BaseAppUserPayload { + @IsString() + readonly firstName: string; + + @IsString() + readonly lastName: string; + + @IsEmail() + readonly email: string; + + @IsString() + readonly major: string; + + @IsEnum(AppUserRole) + readonly role: string; + + @IsInt() + @IsOptional() + readonly graduationYear: number; + + @ValidateNested({ each: true }) + @Type(() => InductionClass) + @IsOptional() + readonly inductionClass: InductionClass; +} + +export class EventSignInRequest extends BaseAppUserPayload { + @IsBoolean() + readonly agreeToPhotoRelease: boolean; +} + +export class EventSignInResponse { + @IsInt() + id: number; + + @IsString() + firstName: string; + + @IsString() + lastName: string; + + @IsEmail() + email: string; + + @IsString() + major: string; + + @IsEnum(AppUserRole) + role: string; +} diff --git a/src/payloads/index.ts b/src/payloads/index.ts index 00981544..5f835fe6 100644 --- a/src/payloads/index.ts +++ b/src/payloads/index.ts @@ -1,2 +1,2 @@ export { EventRequest, EventResponse, MultipleEventResponse } from './Event'; -export { AppUserPKPayload } from './AppUser'; +export { AppUserPKPayload, EventSignInRequest, EventSignInResponse } from './AppUser'; diff --git a/src/services/AppUserService.ts b/src/services/AppUserService.ts index 905e7ac7..75e069f1 100644 --- a/src/services/AppUserService.ts +++ b/src/services/AppUserService.ts @@ -10,6 +10,10 @@ export class AppUserService { this.appUserRepository = getRepository(AppUser); } + saveAppUser(appUser: AppUser): Promise { + return this.appUserRepository.save(appUser); + } + /** * Get multiple app users. * @@ -18,4 +22,8 @@ export class AppUserService { getMultipleAppUsers(ids: number[]): Promise { return this.appUserRepository.find({ id: Any(ids) }); } + + getAppUserByEmail(email: string): Promise { + return this.appUserRepository.findOne({ email }); + } } From 0a41cb92b8af93cb483b15045291072d9b8424f8 Mon Sep 17 00:00:00 2001 From: Thai Date: Thu, 13 Aug 2020 09:26:00 -0700 Subject: [PATCH 2/9] Changed AppUserEventSignInRequest/Response to EventSignInRequest/Response. Also removed agreeToPhotoRelease in EventSignInRequest for now. --- src/entities/AppUser.ts | 2 +- src/payloads/AppUser.ts | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/entities/AppUser.ts b/src/entities/AppUser.ts index 6fa87d98..8ae0502a 100644 --- a/src/entities/AppUser.ts +++ b/src/entities/AppUser.ts @@ -7,7 +7,7 @@ export enum AppUserRole { OFFICER = 'officer', MEMBER = 'member', INDUCTEE = 'inductee', - GUEST = 'Guest', + GUEST = 'guest', } /** diff --git a/src/payloads/AppUser.ts b/src/payloads/AppUser.ts index 3ef54f32..7ef4c405 100644 --- a/src/payloads/AppUser.ts +++ b/src/payloads/AppUser.ts @@ -41,10 +41,7 @@ abstract class BaseAppUserPayload { readonly inductionClass: InductionClass; } -export class EventSignInRequest extends BaseAppUserPayload { - @IsBoolean() - readonly agreeToPhotoRelease: boolean; -} +export class EventSignInRequest extends BaseAppUserPayload {} export class EventSignInResponse { @IsInt() From 8fdbf46c0496579fb85cd34bb1a50358282e38ec Mon Sep 17 00:00:00 2001 From: Thai Date: Thu, 13 Aug 2020 14:49:25 -0700 Subject: [PATCH 3/9] Set up everything for registering attendance for signed in user (WIP). I still don't know what to do if the AppUser's role is an affiliate role (so inductee, member, officer, anything that is not guest). Might just treat those roles the same way as how I handle attendance register for non-affiliates. Also, how do I do testing for this???? --- src/controllers/EventController.ts | 20 +++++++++++++------- src/entities/Attendance.ts | 7 ++----- src/payloads/AppUser.ts | 25 ++----------------------- src/services/AppUserService.ts | 2 +- src/services/AttendanceService.ts | 24 ++++++++++++++++++++++++ src/services/EventService.ts | 20 +++++++++++++++++--- src/services/index.ts | 1 + 7 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 src/services/AttendanceService.ts diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index a17361a1..7eb60cef 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -2,7 +2,7 @@ import { JsonController, Param, Get, Post, Delete, Body } from 'routing-controll import { singleton, inject } from 'tsyringe'; import { ResponseSchema } from 'routing-controllers-openapi'; -import { Event, AppUser, AppUserRole } from '@Entities'; +import { Event, AppUser, AppUserRole, Attendance } from '@Entities'; import { EventRequest, EventResponse, @@ -10,7 +10,7 @@ import { EventSignInRequest, EventSignInResponse, } from '@Payloads'; -import { AppUserService, EventService } from '@Services'; +import { AppUserService, EventService, AttendanceService } from '@Services'; import { AppUserMapper, EventMapper } from '@Mappers'; @singleton() @@ -89,25 +89,31 @@ export class EventController { } @Post('/:eventID/signin') - @ResponseSchema(EventSignInResponse) + @ResponseSchema(Attendance) async signInToEvent( @Param('eventID') eventID: number, @Body() appUserRequest: EventSignInRequest - ): Promise { + ): Promise { const { email } = appUserRequest; const appUserFromEmail = await this.appUserService.getAppUserByEmail(email); if (appUserFromEmail == undefined) { const newAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); const savedAppUser = await this.appUserService.saveAppUser(newAppUser); - return this.appUserMapper.entityToResponse(savedAppUser); + await this.eventService.registerAttendance(eventID, savedAppUser); + + return null; } else { - const { role } = appUserFromEmail; + const { id, role } = appUserFromEmail; if (role !== AppUserRole.GUEST) { // return HTTP error } else { - // TODO + const updatedAppUser = await this.appUserMapper.requestToExistingEntity(appUserRequest, id); + const savedUpdatedUser = await this.appUserService.saveAppUser(updatedAppUser); + await this.eventService.registerAttendance(eventID, savedUpdatedUser); + + return null; } } } diff --git a/src/entities/Attendance.ts b/src/entities/Attendance.ts index 17787d57..62a8166e 100644 --- a/src/entities/Attendance.ts +++ b/src/entities/Attendance.ts @@ -10,17 +10,14 @@ import { Event } from './Event'; */ @Entity() export class Attendance { - @PrimaryGeneratedColumn() - id: number; - - @ManyToOne(() => AppUser) + @ManyToOne(() => AppUser, { primary: true }) attendee: AppUser; // Officer who checked off attendee @ManyToOne(() => AppUser) officer: AppUser; - @ManyToOne(() => Event) + @ManyToOne(() => Event, { primary: true }) event: Event; // num_hours diff --git a/src/payloads/AppUser.ts b/src/payloads/AppUser.ts index 7ef4c405..4e512148 100644 --- a/src/payloads/AppUser.ts +++ b/src/payloads/AppUser.ts @@ -1,14 +1,5 @@ -import { - IsInt, - IsString, - IsEmail, - IsBoolean, - IsEnum, - IsOptional, - ValidateNested, -} from 'class-validator'; -import { AppUserRole, InductionClass } from '@Entities'; -import { Type } from 'class-transformer'; +import { IsInt, IsString, IsEmail, IsEnum } from 'class-validator'; +import { AppUserRole } from '@Entities'; export class AppUserPKPayload { @IsInt() @@ -27,18 +18,6 @@ abstract class BaseAppUserPayload { @IsString() readonly major: string; - - @IsEnum(AppUserRole) - readonly role: string; - - @IsInt() - @IsOptional() - readonly graduationYear: number; - - @ValidateNested({ each: true }) - @Type(() => InductionClass) - @IsOptional() - readonly inductionClass: InductionClass; } export class EventSignInRequest extends BaseAppUserPayload {} diff --git a/src/services/AppUserService.ts b/src/services/AppUserService.ts index 75e069f1..589bdc26 100644 --- a/src/services/AppUserService.ts +++ b/src/services/AppUserService.ts @@ -10,7 +10,7 @@ export class AppUserService { this.appUserRepository = getRepository(AppUser); } - saveAppUser(appUser: AppUser): Promise { + async saveAppUser(appUser: AppUser): Promise { return this.appUserRepository.save(appUser); } diff --git a/src/services/AttendanceService.ts b/src/services/AttendanceService.ts new file mode 100644 index 00000000..eb1cf043 --- /dev/null +++ b/src/services/AttendanceService.ts @@ -0,0 +1,24 @@ +import { Attendance, AppUser, Event } from '@Entities'; +import { AttendanceRepositoryToken } from '@Repositories'; + +import { Repository } from 'typeorm'; +import { singleton, inject } from 'tsyringe'; + +@singleton() +export class AttendanceService { + private attendanceRepository: Repository; + + constructor(@inject(AttendanceRepositoryToken) attendanceRepository: Repository) { + this.attendanceRepository = attendanceRepository; + } + + createAttendance(event: Event, attendee: AppUser): Attendance { + const { role } = attendee; + const attendance = { event, attendee, role }; + return this.attendanceRepository.create(attendance); + } + + async saveAttendance(attendance: Attendance): Promise { + return this.attendanceRepository.save(attendance); + } +} diff --git a/src/services/EventService.ts b/src/services/EventService.ts index df07f7cf..e3ca0668 100644 --- a/src/services/EventService.ts +++ b/src/services/EventService.ts @@ -1,5 +1,6 @@ -import { Event } from '@Entities'; -import { EventRepositoryToken } from '@Repositories'; +import { Event, AppUser, AppUserRole, Attendance } from '@Entities'; +import { EventRepositoryToken, AttendanceRepositoryToken } from '@Repositories'; +import { AttendanceService } from '@Services'; import { Repository } from 'typeorm'; import { singleton, inject } from 'tsyringe'; @@ -7,9 +8,14 @@ import { singleton, inject } from 'tsyringe'; @singleton() export class EventService { private eventRepository: Repository; + private attendanceService: AttendanceService; - constructor(@inject(EventRepositoryToken) eventRepository: Repository) { + constructor( + @inject(EventRepositoryToken) eventRepository: Repository, + @inject(AttendanceService) attendanceService: AttendanceService + ) { this.eventRepository = eventRepository; + this.attendanceService = attendanceService; } /** @@ -50,4 +56,12 @@ export class EventService { const event = await this.eventRepository.findOne({ id }); return event ? this.eventRepository.remove(event) : undefined; } + + async registerAttendance(eventId: number, appUser: AppUser): Promise { + const event = await this.eventRepository.findOne({ id: eventId }); + const attendance = this.attendanceService.createAttendance(event, appUser); + const savedAttendance = await this.attendanceService.saveAttendance(attendance); + + return savedAttendance; + } } diff --git a/src/services/index.ts b/src/services/index.ts index b6985aca..65e2ab2e 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,2 +1,3 @@ export { EventService } from './EventService'; export { AppUserService } from './AppUserService'; +export { AttendanceService } from './AttendanceService'; From 56c902b6015b104b975d2682159a1269f2866975 Mon Sep 17 00:00:00 2001 From: Thai Date: Fri, 14 Aug 2020 12:32:34 -0700 Subject: [PATCH 4/9] Completed non-affiliate event sign in use case. Now just need to test. --- src/controllers/EventController.ts | 22 ++++++++++------------ src/loaders/RepositoryLoader.ts | 19 +++++++++++++++++-- src/mappers/AppUserMapper.ts | 22 ++++++++++++++++++++++ src/services/AppUserService.ts | 12 ++++++++++++ src/services/EventService.ts | 12 ++++++++++-- 5 files changed, 71 insertions(+), 16 deletions(-) diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index 7eb60cef..5d7c0795 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -2,15 +2,9 @@ import { JsonController, Param, Get, Post, Delete, Body } from 'routing-controll import { singleton, inject } from 'tsyringe'; import { ResponseSchema } from 'routing-controllers-openapi'; -import { Event, AppUser, AppUserRole, Attendance } from '@Entities'; -import { - EventRequest, - EventResponse, - MultipleEventResponse, - EventSignInRequest, - EventSignInResponse, -} from '@Payloads'; -import { AppUserService, EventService, AttendanceService } from '@Services'; +import { Event, AppUserRole, Attendance } from '@Entities'; +import { EventRequest, EventResponse, MultipleEventResponse, EventSignInRequest } from '@Payloads'; +import { AppUserService, EventService } from '@Services'; import { AppUserMapper, EventMapper } from '@Mappers'; @singleton() @@ -100,14 +94,18 @@ export class EventController { if (appUserFromEmail == undefined) { const newAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); const savedAppUser = await this.appUserService.saveAppUser(newAppUser); - await this.eventService.registerAttendance(eventID, savedAppUser); + const attendance = await this.eventService.registerAttendance(eventID, savedAppUser); - return null; + return attendance; } else { const { id, role } = appUserFromEmail; if (role !== AppUserRole.GUEST) { - // return HTTP error + /* + * TODO: Handle the case where user is an affiliate + * If affiliated user has a token, then verify it and proceed. If said + * user does not have a token, then send back an HTTP error. + */ } else { const updatedAppUser = await this.appUserMapper.requestToExistingEntity(appUserRequest, id); const savedUpdatedUser = await this.appUserService.saveAppUser(updatedAppUser); diff --git a/src/loaders/RepositoryLoader.ts b/src/loaders/RepositoryLoader.ts index fa52fb28..a688d2af 100644 --- a/src/loaders/RepositoryLoader.ts +++ b/src/loaders/RepositoryLoader.ts @@ -1,10 +1,25 @@ import { container } from 'tsyringe'; -import { EventRepositoryToken, EventRepositoryFactory } from '@Repositories'; -import { Event } from '@Entities'; +import { + EventRepositoryToken, + EventRepositoryFactory, + AppUserRepositoryToken, + AppUserRepositoryFactory, + AttendanceRepositoryToken, + AttendanceRepositoryFactory, +} from '@Repositories'; +import { Event, AppUser, Attendance } from '@Entities'; import { Repository } from 'typeorm'; export function loadRepositories(): void { container.register>(EventRepositoryToken, { useFactory: EventRepositoryFactory, }); + + container.register>(AppUserRepositoryToken, { + useFactory: AppUserRepositoryFactory, + }); + + container.register>(AttendanceRepositoryToken, { + useFactory: AttendanceRepositoryFactory, + }); } diff --git a/src/mappers/AppUserMapper.ts b/src/mappers/AppUserMapper.ts index 4b1ffc4e..c7784e84 100644 --- a/src/mappers/AppUserMapper.ts +++ b/src/mappers/AppUserMapper.ts @@ -14,11 +14,26 @@ export class AppUserMapper { this.appUserRepository = appUserRepository; } + /** + * Converts an EventSignInRequest payload to an AppUser entity and + * returns the newly created entity to the caller. + * + * @param appUserRequest The request payload from which the AppUser entity + * is created. + */ requestToNewEntity(appUserRequest: EventSignInRequest): AppUser { const plainAppUserRequest: Object = classToPlain(appUserRequest); return this.appUserRepository.create(plainAppUserRequest); } + /** + * Updates an existing AppUser entity with new data from the request payload. + * If there is no AppUser entity with the passed-in appUserId, then return undefined. + * + * @param appUserRequest The request payload from which the updated existing AppUser + * entity is created + * @param appUserId The supposed ID of an existing AppUser entity. + */ async requestToExistingEntity( appUserRequest: EventSignInRequest, appUserId: number @@ -34,6 +49,13 @@ export class AppUserMapper { return appUser; } + /** + * Converts an AppUser entity to an EventSignInResponse payload and returns the + * newly created response payload to the caller. + * + * @param appUser The AppUser entity to be converted to an EventSignInResponse + * payload. + */ entityToResponse(appUser: AppUser): EventSignInResponse { const plainAppUser: Object = classToPlain(appUser); const appUserResponse: EventSignInResponse = plainToClass(EventSignInResponse, plainAppUser); diff --git a/src/services/AppUserService.ts b/src/services/AppUserService.ts index 589bdc26..dd93c744 100644 --- a/src/services/AppUserService.ts +++ b/src/services/AppUserService.ts @@ -10,6 +10,12 @@ export class AppUserService { this.appUserRepository = getRepository(AppUser); } + /** + * Stores the AppUser passed in as a parameter to the + * AppUser table. + * + * @param appUser The AppUser to be stored to the db. + */ async saveAppUser(appUser: AppUser): Promise { return this.appUserRepository.save(appUser); } @@ -23,6 +29,12 @@ export class AppUserService { return this.appUserRepository.find({ id: Any(ids) }); } + /** + * Get an existing AppUser by their email address. + * + * @param email The email used to look for the corresponding AppUser. + * + */ getAppUserByEmail(email: string): Promise { return this.appUserRepository.findOne({ email }); } diff --git a/src/services/EventService.ts b/src/services/EventService.ts index e3ca0668..eef3903b 100644 --- a/src/services/EventService.ts +++ b/src/services/EventService.ts @@ -1,5 +1,5 @@ -import { Event, AppUser, AppUserRole, Attendance } from '@Entities'; -import { EventRepositoryToken, AttendanceRepositoryToken } from '@Repositories'; +import { Event, AppUser, Attendance } from '@Entities'; +import { EventRepositoryToken } from '@Repositories'; import { AttendanceService } from '@Services'; import { Repository } from 'typeorm'; @@ -57,6 +57,14 @@ export class EventService { return event ? this.eventRepository.remove(event) : undefined; } + /** + * Creates an Attendance entity using the event obtained from eventId and + * the passed-in AppUser entity, then stores that Attendance entity to the + * Attendance table. + * + * @param eventId + * @param appUser + */ async registerAttendance(eventId: number, appUser: AppUser): Promise { const event = await this.eventRepository.findOne({ id: eventId }); const attendance = this.attendanceService.createAttendance(event, appUser); From 385f5af6ce62baf1524e52be96f56514f041706d Mon Sep 17 00:00:00 2001 From: Thai Date: Fri, 14 Aug 2020 13:44:14 -0700 Subject: [PATCH 5/9] Tested POST to /api/events/:eventID/signin and it works fine. Worked properly for new non-affiliates and recurring non-affiliates in terms of signing up. Modified Attendance entity schema a little (made duration column nullable). --- src/controllers/EventController.ts | 4 ++-- src/entities/Attendance.ts | 4 ++-- src/services/AttendanceService.ts | 4 ++-- src/services/EventService.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index 5d7c0795..1582babf 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -94,9 +94,9 @@ export class EventController { if (appUserFromEmail == undefined) { const newAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); const savedAppUser = await this.appUserService.saveAppUser(newAppUser); - const attendance = await this.eventService.registerAttendance(eventID, savedAppUser); + await this.eventService.registerAttendance(eventID, savedAppUser); - return attendance; + return null; } else { const { id, role } = appUserFromEmail; diff --git a/src/entities/Attendance.ts b/src/entities/Attendance.ts index 62a8166e..07a42203 100644 --- a/src/entities/Attendance.ts +++ b/src/entities/Attendance.ts @@ -1,4 +1,4 @@ -import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm'; +import { Entity, Column, ManyToOne } from 'typeorm'; import { AppUser } from './AppUser'; import { Event } from './Event'; @@ -21,7 +21,7 @@ export class Attendance { event: Event; // num_hours - @Column('float') + @Column('float', { nullable: true }) duration: number; // indicates whether or not attendee was inductee at time of attendance diff --git a/src/services/AttendanceService.ts b/src/services/AttendanceService.ts index eb1cf043..56f4cf75 100644 --- a/src/services/AttendanceService.ts +++ b/src/services/AttendanceService.ts @@ -1,4 +1,4 @@ -import { Attendance, AppUser, Event } from '@Entities'; +import { Attendance, AppUser, AppUserRole, Event } from '@Entities'; import { AttendanceRepositoryToken } from '@Repositories'; import { Repository } from 'typeorm'; @@ -14,7 +14,7 @@ export class AttendanceService { createAttendance(event: Event, attendee: AppUser): Attendance { const { role } = attendee; - const attendance = { event, attendee, role }; + const attendance = { event, attendee, isInductee: role === AppUserRole.INDUCTEE }; return this.attendanceRepository.create(attendance); } diff --git a/src/services/EventService.ts b/src/services/EventService.ts index eef3903b..c9972fcb 100644 --- a/src/services/EventService.ts +++ b/src/services/EventService.ts @@ -3,7 +3,7 @@ import { EventRepositoryToken } from '@Repositories'; import { AttendanceService } from '@Services'; import { Repository } from 'typeorm'; -import { singleton, inject } from 'tsyringe'; +import { singleton, inject, delay } from 'tsyringe'; @singleton() export class EventService { @@ -12,7 +12,7 @@ export class EventService { constructor( @inject(EventRepositoryToken) eventRepository: Repository, - @inject(AttendanceService) attendanceService: AttendanceService + @inject(delay(() => AttendanceService)) attendanceService: AttendanceService ) { this.eventRepository = eventRepository; this.attendanceService = attendanceService; From 6d7fb25a69e8e4dbecd6ca016a0b3e8479bfa643 Mon Sep 17 00:00:00 2001 From: Thai Date: Fri, 14 Aug 2020 23:14:57 -0700 Subject: [PATCH 6/9] Made changes based on comments on PR #44 and fixed circular dependency! --- src/controllers/EventController.ts | 49 +++++++++++++++++++----------- src/mappers/AppUserMapper.ts | 3 -- src/payloads/AppUser.ts | 27 ++-------------- src/payloads/Event.ts | 14 +++++++-- src/payloads/index.ts | 10 ++++-- src/services/AttendanceService.ts | 11 ++++--- src/services/EventService.ts | 23 +++++++++----- 7 files changed, 76 insertions(+), 61 deletions(-) diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index 1582babf..f23c57b0 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -1,4 +1,4 @@ -import { JsonController, Param, Get, Post, Delete, Body } from 'routing-controllers'; +import { JsonController, Param, Get, Post, Delete, Body, OnUndefined } from 'routing-controllers'; import { singleton, inject } from 'tsyringe'; import { ResponseSchema } from 'routing-controllers-openapi'; @@ -83,36 +83,49 @@ export class EventController { } @Post('/:eventID/signin') - @ResponseSchema(Attendance) + @OnUndefined(409) async signInToEvent( @Param('eventID') eventID: number, @Body() appUserRequest: EventSignInRequest - ): Promise { + ): Promise { const { email } = appUserRequest; const appUserFromEmail = await this.appUserService.getAppUserByEmail(email); - if (appUserFromEmail == undefined) { + if (appUserFromEmail === undefined) { const newAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); const savedAppUser = await this.appUserService.saveAppUser(newAppUser); - await this.eventService.registerAttendance(eventID, savedAppUser); + const newAttendance = await this.eventService.registerForEventAttendance( + eventID, + savedAppUser + ); - return null; + return newAttendance; } else { const { id, role } = appUserFromEmail; - if (role !== AppUserRole.GUEST) { - /* - * TODO: Handle the case where user is an affiliate - * If affiliated user has a token, then verify it and proceed. If said - * user does not have a token, then send back an HTTP error. - */ - } else { - const updatedAppUser = await this.appUserMapper.requestToExistingEntity(appUserRequest, id); - const savedUpdatedUser = await this.appUserService.saveAppUser(updatedAppUser); - await this.eventService.registerAttendance(eventID, savedUpdatedUser); - - return null; + if (!(await this.eventService.hasDuplicateAttendance(eventID, appUserFromEmail))) { + if (role !== AppUserRole.GUEST) { + /* + * TODO: Handle the case where user is an affiliate + * If affiliated user has a token, then verify it and proceed. If said + * user does not have a token, then send back an HTTP error. + */ + } else { + const updatedAppUser = await this.appUserMapper.requestToExistingEntity( + appUserRequest, + id + ); + const savedUpdatedUser = await this.appUserService.saveAppUser(updatedAppUser); + const newAttendance = await this.eventService.registerForEventAttendance( + eventID, + savedUpdatedUser + ); + + return newAttendance; + } } + + return undefined; } } } diff --git a/src/mappers/AppUserMapper.ts b/src/mappers/AppUserMapper.ts index c7784e84..82214e54 100644 --- a/src/mappers/AppUserMapper.ts +++ b/src/mappers/AppUserMapper.ts @@ -42,9 +42,6 @@ export class AppUserMapper { appUserObj.id = appUserId; const appUser: AppUser = await this.appUserRepository.preload(appUserObj); - if (appUser == undefined) { - return undefined; - } return appUser; } diff --git a/src/payloads/AppUser.ts b/src/payloads/AppUser.ts index 4e512148..e62b8f49 100644 --- a/src/payloads/AppUser.ts +++ b/src/payloads/AppUser.ts @@ -1,12 +1,11 @@ -import { IsInt, IsString, IsEmail, IsEnum } from 'class-validator'; -import { AppUserRole } from '@Entities'; +import { IsInt, IsString, IsEmail } from 'class-validator'; export class AppUserPKPayload { @IsInt() readonly id: number; } -abstract class BaseAppUserPayload { +export class BaseAppUserPayload { @IsString() readonly firstName: string; @@ -19,25 +18,3 @@ abstract class BaseAppUserPayload { @IsString() readonly major: string; } - -export class EventSignInRequest extends BaseAppUserPayload {} - -export class EventSignInResponse { - @IsInt() - id: number; - - @IsString() - firstName: string; - - @IsString() - lastName: string; - - @IsEmail() - email: string; - - @IsString() - major: string; - - @IsEnum(AppUserRole) - role: string; -} diff --git a/src/payloads/Event.ts b/src/payloads/Event.ts index bc850e2d..19863ebc 100644 --- a/src/payloads/Event.ts +++ b/src/payloads/Event.ts @@ -10,8 +10,8 @@ import { } from 'class-validator'; import { Type } from 'class-transformer'; -import { AppUser, EventType, EventStatus } from '@Entities'; -import { AppUserPKPayload } from '@Payloads'; +import { AppUser, AppUserRole, EventType, EventStatus } from '@Entities'; +import { AppUserPKPayload, BaseAppUserPayload } from './AppUser'; abstract class BaseEventPayload { @IsString() @@ -73,3 +73,13 @@ export class MultipleEventResponse { @Type(() => EventResponse) events: EventResponse[]; } + +export class EventSignInRequest extends BaseAppUserPayload {} + +export class EventSignInResponse extends BaseAppUserPayload { + @IsInt() + id: number; + + @IsEnum(AppUserRole) + role: string; +} diff --git a/src/payloads/index.ts b/src/payloads/index.ts index 5f835fe6..af8c05bb 100644 --- a/src/payloads/index.ts +++ b/src/payloads/index.ts @@ -1,2 +1,8 @@ -export { EventRequest, EventResponse, MultipleEventResponse } from './Event'; -export { AppUserPKPayload, EventSignInRequest, EventSignInResponse } from './AppUser'; +export { + EventRequest, + EventResponse, + MultipleEventResponse, + EventSignInRequest, + EventSignInResponse, +} from './Event'; +export { AppUserPKPayload, BaseAppUserPayload } from './AppUser'; diff --git a/src/services/AttendanceService.ts b/src/services/AttendanceService.ts index 56f4cf75..ec4d3613 100644 --- a/src/services/AttendanceService.ts +++ b/src/services/AttendanceService.ts @@ -12,13 +12,16 @@ export class AttendanceService { this.attendanceRepository = attendanceRepository; } - createAttendance(event: Event, attendee: AppUser): Attendance { + async registerAttendance(event: Event, attendee: AppUser): Promise { const { role } = attendee; const attendance = { event, attendee, isInductee: role === AppUserRole.INDUCTEE }; - return this.attendanceRepository.create(attendance); + const newAttendance = this.attendanceRepository.create(attendance); + return this.attendanceRepository.save(newAttendance); } - async saveAttendance(attendance: Attendance): Promise { - return this.attendanceRepository.save(attendance); + async getAttendanceByEventUser(event: Event, attendee: AppUser): Promise { + const attendanceToCheck = await this.attendanceRepository.findOne({ event, attendee }); + + return attendanceToCheck; } } diff --git a/src/services/EventService.ts b/src/services/EventService.ts index c9972fcb..6e856bbb 100644 --- a/src/services/EventService.ts +++ b/src/services/EventService.ts @@ -1,9 +1,9 @@ import { Event, AppUser, Attendance } from '@Entities'; import { EventRepositoryToken } from '@Repositories'; -import { AttendanceService } from '@Services'; +import { AttendanceService } from './AttendanceService'; import { Repository } from 'typeorm'; -import { singleton, inject, delay } from 'tsyringe'; +import { singleton, inject } from 'tsyringe'; @singleton() export class EventService { @@ -12,7 +12,7 @@ export class EventService { constructor( @inject(EventRepositoryToken) eventRepository: Repository, - @inject(delay(() => AttendanceService)) attendanceService: AttendanceService + @inject(AttendanceService) attendanceService: AttendanceService ) { this.eventRepository = eventRepository; this.attendanceService = attendanceService; @@ -65,11 +65,20 @@ export class EventService { * @param eventId * @param appUser */ - async registerAttendance(eventId: number, appUser: AppUser): Promise { + async registerForEventAttendance(eventId: number, appUser: AppUser): Promise { const event = await this.eventRepository.findOne({ id: eventId }); - const attendance = this.attendanceService.createAttendance(event, appUser); - const savedAttendance = await this.attendanceService.saveAttendance(attendance); + const newAttendance = await this.attendanceService.registerAttendance(event, appUser); - return savedAttendance; + return newAttendance; + } + + async hasDuplicateAttendance(eventId: number, appUser: AppUser): Promise { + const event = await this.eventRepository.findOne({ id: eventId }); + const possibleDuplicateAttendance = await this.attendanceService.getAttendanceByEventUser( + event, + appUser + ); + + return possibleDuplicateAttendance !== undefined; } } From 20175ecf0a9fca7d87e91943987d5f29b07e8bb5 Mon Sep 17 00:00:00 2001 From: Thai Date: Sat, 15 Aug 2020 15:36:23 -0700 Subject: [PATCH 7/9] Refactored the code for /api/events/:eventID/signin. Moved the logic of saving non-affiliate AppUser entities to AppUserService. Changed RegisterAttendance so that it uses AttendanceRepository's Insert instead of Save to prevent inserts of duplicate data. --- src/controllers/EventController.ts | 59 ++++++++---------------------- src/services/AppUserService.ts | 32 ++++++++++++++-- src/services/AttendanceService.ts | 11 +++++- src/services/EventService.ts | 10 ----- 4 files changed, 52 insertions(+), 60 deletions(-) diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index f23c57b0..898f0d24 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -2,27 +2,24 @@ import { JsonController, Param, Get, Post, Delete, Body, OnUndefined } from 'rou import { singleton, inject } from 'tsyringe'; import { ResponseSchema } from 'routing-controllers-openapi'; -import { Event, AppUserRole, Attendance } from '@Entities'; +import { Event, Attendance } from '@Entities'; import { EventRequest, EventResponse, MultipleEventResponse, EventSignInRequest } from '@Payloads'; import { AppUserService, EventService } from '@Services'; -import { AppUserMapper, EventMapper } from '@Mappers'; +import { EventMapper } from '@Mappers'; @singleton() @JsonController('/api/events') export class EventController { private appUserService: AppUserService; - private appUserMapper: AppUserMapper; private eventService: EventService; private eventMapper: EventMapper; constructor( @inject(AppUserService) appUserService: AppUserService, - @inject(AppUserMapper) appUserMapper: AppUserMapper, @inject(EventService) eventService: EventService, @inject(EventMapper) eventMapper: EventMapper ) { this.appUserService = appUserService; - this.appUserMapper = appUserMapper; this.eventService = eventService; this.eventMapper = eventMapper; } @@ -84,48 +81,22 @@ export class EventController { @Post('/:eventID/signin') @OnUndefined(409) + @ResponseSchema(Attendance) async signInToEvent( @Param('eventID') eventID: number, @Body() appUserRequest: EventSignInRequest - ): Promise { - const { email } = appUserRequest; - const appUserFromEmail = await this.appUserService.getAppUserByEmail(email); - - if (appUserFromEmail === undefined) { - const newAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); - const savedAppUser = await this.appUserService.saveAppUser(newAppUser); - const newAttendance = await this.eventService.registerForEventAttendance( - eventID, - savedAppUser - ); - - return newAttendance; - } else { - const { id, role } = appUserFromEmail; - - if (!(await this.eventService.hasDuplicateAttendance(eventID, appUserFromEmail))) { - if (role !== AppUserRole.GUEST) { - /* - * TODO: Handle the case where user is an affiliate - * If affiliated user has a token, then verify it and proceed. If said - * user does not have a token, then send back an HTTP error. - */ - } else { - const updatedAppUser = await this.appUserMapper.requestToExistingEntity( - appUserRequest, - id - ); - const savedUpdatedUser = await this.appUserService.saveAppUser(updatedAppUser); - const newAttendance = await this.eventService.registerForEventAttendance( - eventID, - savedUpdatedUser - ); - - return newAttendance; - } - } - - return undefined; + ): Promise { + const savedAppUser = await this.appUserService.saveNonAffiliate(appUserRequest); + + // savedAppUser is undefined if AppUser from appUserRequest is an affiliate + if (savedAppUser === undefined) { + /* + * TODO: Handle the case where user is an affiliate + * If affiliated user has a token, then verify it and proceed. If said + * user does not have a token, then send back an HTTP error. + */ } + + return await this.eventService.registerForEventAttendance(eventID, savedAppUser); } } diff --git a/src/services/AppUserService.ts b/src/services/AppUserService.ts index dd93c744..01019a34 100644 --- a/src/services/AppUserService.ts +++ b/src/services/AppUserService.ts @@ -1,13 +1,17 @@ -import { AppUser } from '@Entities'; -import { singleton } from 'tsyringe'; +import { AppUser, AppUserRole } from '@Entities'; +import { AppUserMapper } from '@Mappers'; +import { EventSignInRequest } from '@Payloads'; +import { singleton, inject } from 'tsyringe'; import { Any, Repository, getRepository } from 'typeorm'; @singleton() export class AppUserService { private appUserRepository: Repository; + private appUserMapper: AppUserMapper; - constructor() { + constructor(@inject(AppUserMapper) appUserMapper: AppUserMapper) { this.appUserRepository = getRepository(AppUser); + this.appUserMapper = appUserMapper; } /** @@ -17,7 +21,7 @@ export class AppUserService { * @param appUser The AppUser to be stored to the db. */ async saveAppUser(appUser: AppUser): Promise { - return this.appUserRepository.save(appUser); + return await this.appUserRepository.save(appUser); } /** @@ -38,4 +42,24 @@ export class AppUserService { getAppUserByEmail(email: string): Promise { return this.appUserRepository.findOne({ email }); } + + async saveNonAffiliate(appUserRequest: EventSignInRequest): Promise { + const { email } = appUserRequest; + const appUserFromEmail = await this.getAppUserByEmail(email); + let currAppUser = null; + + if (appUserFromEmail === undefined) { + currAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); + } else { + const { id, role } = appUserFromEmail; + + if (role === AppUserRole.GUEST) { + currAppUser = await this.appUserMapper.requestToExistingEntity(appUserRequest, id); + } else { + return undefined; + } + } + + return await this.saveAppUser(currAppUser); + } } diff --git a/src/services/AttendanceService.ts b/src/services/AttendanceService.ts index ec4d3613..f3ae2aa3 100644 --- a/src/services/AttendanceService.ts +++ b/src/services/AttendanceService.ts @@ -12,11 +12,18 @@ export class AttendanceService { this.attendanceRepository = attendanceRepository; } - async registerAttendance(event: Event, attendee: AppUser): Promise { + async registerAttendance(event: Event, attendee: AppUser): Promise { const { role } = attendee; const attendance = { event, attendee, isInductee: role === AppUserRole.INDUCTEE }; const newAttendance = this.attendanceRepository.create(attendance); - return this.attendanceRepository.save(newAttendance); + + try { + await this.attendanceRepository.insert(newAttendance); + } catch { + return undefined; + } + + return newAttendance; } async getAttendanceByEventUser(event: Event, attendee: AppUser): Promise { diff --git a/src/services/EventService.ts b/src/services/EventService.ts index 6e856bbb..6c32a63b 100644 --- a/src/services/EventService.ts +++ b/src/services/EventService.ts @@ -71,14 +71,4 @@ export class EventService { return newAttendance; } - - async hasDuplicateAttendance(eventId: number, appUser: AppUser): Promise { - const event = await this.eventRepository.findOne({ id: eventId }); - const possibleDuplicateAttendance = await this.attendanceService.getAttendanceByEventUser( - event, - appUser - ); - - return possibleDuplicateAttendance !== undefined; - } } From 466f4db3945b61ae8a57084485864d6f8c0f83fc Mon Sep 17 00:00:00 2001 From: Thai Date: Sat, 15 Aug 2020 17:42:52 -0700 Subject: [PATCH 8/9] Refactor #2 of /api/events/:eventID/signin. Added RequestToEntityByEmail and AppUserService now doesn't depend on mappers and payloads anymore. --- src/controllers/EventController.ts | 21 +++++++++++++++++---- src/mappers/AppUserMapper.ts | 13 +++++++++++++ src/services/AppUserService.ts | 28 +++++++--------------------- src/services/AttendanceService.ts | 6 ------ 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index 898f0d24..a614ce3e 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -5,21 +5,24 @@ import { ResponseSchema } from 'routing-controllers-openapi'; import { Event, Attendance } from '@Entities'; import { EventRequest, EventResponse, MultipleEventResponse, EventSignInRequest } from '@Payloads'; import { AppUserService, EventService } from '@Services'; -import { EventMapper } from '@Mappers'; +import { AppUserMapper, EventMapper } from '@Mappers'; @singleton() @JsonController('/api/events') export class EventController { private appUserService: AppUserService; + private appUserMapper: AppUserMapper; private eventService: EventService; private eventMapper: EventMapper; constructor( @inject(AppUserService) appUserService: AppUserService, + @inject(AppUserMapper) appUserMapper: AppUserMapper, @inject(EventService) eventService: EventService, @inject(EventMapper) eventMapper: EventMapper ) { this.appUserService = appUserService; + this.appUserMapper = appUserMapper; this.eventService = eventService; this.eventMapper = eventMapper; } @@ -85,10 +88,20 @@ export class EventController { async signInToEvent( @Param('eventID') eventID: number, @Body() appUserRequest: EventSignInRequest - ): Promise { - const savedAppUser = await this.appUserService.saveNonAffiliate(appUserRequest); + ): Promise { + const { email } = appUserRequest; + const appUserFromEmail = await this.appUserService.getAppUserByEmail(email); + const appUserToSave = await this.appUserMapper.requestToEntityByEmail( + appUserFromEmail, + appUserRequest + ); + + if (appUserToSave === undefined) { + return undefined; + } + + const savedAppUser = await this.appUserService.saveNonAffiliate(appUserToSave); - // savedAppUser is undefined if AppUser from appUserRequest is an affiliate if (savedAppUser === undefined) { /* * TODO: Handle the case where user is an affiliate diff --git a/src/mappers/AppUserMapper.ts b/src/mappers/AppUserMapper.ts index 82214e54..c503d3b1 100644 --- a/src/mappers/AppUserMapper.ts +++ b/src/mappers/AppUserMapper.ts @@ -46,6 +46,19 @@ export class AppUserMapper { return appUser; } + async requestToEntityByEmail( + appUserFromEmail: AppUser, + appUserRequest: EventSignInRequest + ): Promise { + if (appUserFromEmail === undefined) { + return this.requestToNewEntity(appUserRequest); + } else { + const { id } = appUserFromEmail; + + return await this.requestToExistingEntity(appUserRequest, id); + } + } + /** * Converts an AppUser entity to an EventSignInResponse payload and returns the * newly created response payload to the caller. diff --git a/src/services/AppUserService.ts b/src/services/AppUserService.ts index 01019a34..a30a235f 100644 --- a/src/services/AppUserService.ts +++ b/src/services/AppUserService.ts @@ -1,17 +1,13 @@ import { AppUser, AppUserRole } from '@Entities'; -import { AppUserMapper } from '@Mappers'; -import { EventSignInRequest } from '@Payloads'; import { singleton, inject } from 'tsyringe'; import { Any, Repository, getRepository } from 'typeorm'; @singleton() export class AppUserService { private appUserRepository: Repository; - private appUserMapper: AppUserMapper; - constructor(@inject(AppUserMapper) appUserMapper: AppUserMapper) { + constructor() { this.appUserRepository = getRepository(AppUser); - this.appUserMapper = appUserMapper; } /** @@ -39,27 +35,17 @@ export class AppUserService { * @param email The email used to look for the corresponding AppUser. * */ - getAppUserByEmail(email: string): Promise { + getAppUserByEmail(email: string): Promise { return this.appUserRepository.findOne({ email }); } - async saveNonAffiliate(appUserRequest: EventSignInRequest): Promise { - const { email } = appUserRequest; - const appUserFromEmail = await this.getAppUserByEmail(email); - let currAppUser = null; + async saveNonAffiliate(appUser: AppUser): Promise { + const { role } = appUser; - if (appUserFromEmail === undefined) { - currAppUser = this.appUserMapper.requestToNewEntity(appUserRequest); - } else { - const { id, role } = appUserFromEmail; - - if (role === AppUserRole.GUEST) { - currAppUser = await this.appUserMapper.requestToExistingEntity(appUserRequest, id); - } else { - return undefined; - } + if (role !== undefined && role !== AppUserRole.GUEST) { + return undefined; } - return await this.saveAppUser(currAppUser); + return await this.saveAppUser(appUser); } } diff --git a/src/services/AttendanceService.ts b/src/services/AttendanceService.ts index f3ae2aa3..6c6a632b 100644 --- a/src/services/AttendanceService.ts +++ b/src/services/AttendanceService.ts @@ -25,10 +25,4 @@ export class AttendanceService { return newAttendance; } - - async getAttendanceByEventUser(event: Event, attendee: AppUser): Promise { - const attendanceToCheck = await this.attendanceRepository.findOne({ event, attendee }); - - return attendanceToCheck; - } } From 399bedcccd5d9a0f32b2479cc0c880ab0e2f90ac Mon Sep 17 00:00:00 2001 From: Thai Date: Sat, 15 Aug 2020 18:38:14 -0700 Subject: [PATCH 9/9] Refactored some more --- src/controllers/EventController.ts | 12 +----------- src/mappers/AppUserMapper.ts | 16 +++++++++++----- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/controllers/EventController.ts b/src/controllers/EventController.ts index a614ce3e..2995254e 100644 --- a/src/controllers/EventController.ts +++ b/src/controllers/EventController.ts @@ -89,17 +89,7 @@ export class EventController { @Param('eventID') eventID: number, @Body() appUserRequest: EventSignInRequest ): Promise { - const { email } = appUserRequest; - const appUserFromEmail = await this.appUserService.getAppUserByEmail(email); - const appUserToSave = await this.appUserMapper.requestToEntityByEmail( - appUserFromEmail, - appUserRequest - ); - - if (appUserToSave === undefined) { - return undefined; - } - + const appUserToSave = await this.appUserMapper.requestToEntityByEmail(appUserRequest); const savedAppUser = await this.appUserService.saveNonAffiliate(appUserToSave); if (savedAppUser === undefined) { diff --git a/src/mappers/AppUserMapper.ts b/src/mappers/AppUserMapper.ts index c503d3b1..9f05af6a 100644 --- a/src/mappers/AppUserMapper.ts +++ b/src/mappers/AppUserMapper.ts @@ -1,5 +1,6 @@ import { EventSignInRequest, EventSignInResponse } from '@Payloads'; import { AppUser } from '@Entities'; +import { AppUserService } from '@Services'; import { AppUserRepositoryToken } from '@Repositories'; import { Repository } from 'typeorm'; @@ -9,9 +10,14 @@ import { singleton, inject } from 'tsyringe'; @singleton() export class AppUserMapper { private appUserRepository: Repository; + private appUserService: AppUserService; - constructor(@inject(AppUserRepositoryToken) appUserRepository: Repository) { + constructor( + @inject(AppUserRepositoryToken) appUserRepository: Repository, + @inject(AppUserService) appUserService: AppUserService + ) { this.appUserRepository = appUserRepository; + this.appUserService = appUserService; } /** @@ -46,10 +52,10 @@ export class AppUserMapper { return appUser; } - async requestToEntityByEmail( - appUserFromEmail: AppUser, - appUserRequest: EventSignInRequest - ): Promise { + async requestToEntityByEmail(appUserRequest: EventSignInRequest): Promise { + const { email } = appUserRequest; + const appUserFromEmail = await this.appUserService.getAppUserByEmail(email); + if (appUserFromEmail === undefined) { return this.requestToNewEntity(appUserRequest); } else {