From d1b5ad7cc639a45c6fe816d1dd938b5baa2c9c87 Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Sun, 14 Apr 2024 22:54:15 -0400 Subject: [PATCH 01/17] Implement achievements backend --- server/src/achievement/achievement.dto.ts | 24 +- .../src/achievement/achievement.e2e-spec.ts | 128 ++++++++++ server/src/achievement/achievement.gateway.ts | 59 ++++- server/src/achievement/achievement.service.ts | 219 +++++++++++++++++- server/src/challenge/challenge.service.ts | 3 + server/src/client/client.service.ts | 7 +- .../src/organization/organization.service.ts | 13 +- 7 files changed, 436 insertions(+), 17 deletions(-) create mode 100644 server/src/achievement/achievement.e2e-spec.ts diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index 175bfe1c..41a53e5c 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -18,14 +18,13 @@ export enum AchievementType { export interface AchievementDto { id: string; - eventId: string; - requiredPoints: number; - name: string; - description: string; - imageUrl: string; - locationType: LocationType; - achievementType: AchievementType; - organizations: string[]; + eventId?: string; + requiredPoints?: number; + name?: string; + description?: string; + imageUrl?: string; + locationType?: LocationType; + achievementType?: AchievementType; } export interface AchievementTrackerDto { @@ -34,3 +33,12 @@ export interface AchievementTrackerDto { achievementId: string; dateComplete?: string; } + +export interface UpdateAchievementDataDto { + achievement: AchievementDto; + deleted: boolean; +} + +export interface RequestAchievementDataDto { + achievements: string[]; +} \ No newline at end of file diff --git a/server/src/achievement/achievement.e2e-spec.ts b/server/src/achievement/achievement.e2e-spec.ts new file mode 100644 index 00000000..4f097778 --- /dev/null +++ b/server/src/achievement/achievement.e2e-spec.ts @@ -0,0 +1,128 @@ +import * as request from 'supertest'; +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { AchievementService } from './achievement.service'; +import { AppModule } from '../app.module'; +import { PrismaService } from '../prisma/prisma.service'; +import { UserService } from '../user/user.service'; +import { + AuthType, + User, + EventBase, + EventTracker, + AchievementTracker, + Achievement, +} from '@prisma/client'; +import { SessionLogService } from '../session-log/session-log.service'; +import { EventService } from '../event/event.service'; +import { ClientService } from '../client/client.service'; +import { GroupService } from '../group/group.service'; +import { OrganizationService } from '../organization/organization.service'; +import { ClientModule } from '../client/client.module'; +import { AchievementDto, AchievementTrackerDto } from './achievement.dto'; +import { AppAbility, CaslAbilityFactory } from '../casl/casl-ability.factory'; + + + +describe('AchievementModule E2E', () => { + let app: INestApplication; + let moduleRef: TestingModule; + let achievementService: AchievementService; + let prisma: PrismaService; + let userService: UserService; + let eventService: EventService; + let user: User; + let tracker: AchievementTracker; + let event: EventBase; + let organizationService: OrganizationService; + let abilityFactory: CaslAbilityFactory; + let fullAbility: AppAbility; + + beforeAll(async () => { + moduleRef = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleRef.createNestApplication(); + await app.init(); + const module: TestingModule = await Test.createTestingModule({ + imports: [ClientModule], + providers: [ + AchievementService, + PrismaService, + UserService, + SessionLogService, + EventService, + ClientService, + GroupService, + OrganizationService, + CaslAbilityFactory, + ], + }).compile(); + + const log = console.log; + console.log = function () {}; + + achievementService = module.get(AchievementService); + prisma = module.get(PrismaService); + userService = module.get(UserService); + eventService = module.get(EventService); + organizationService = module.get(OrganizationService); + abilityFactory = module.get(CaslAbilityFactory); + + user = await userService.register( + 'test@gmail.com', + 'test', + '2025', + 0, + 0, + AuthType.DEVICE, + 'asdf', + 'GRADUATE', + ); + + user = await prisma.user.findFirstOrThrow({ + where: { id: user.id }, + include: { memberOf: true }, + }); + + console.log = log; + }); + + it('should successfully find AchievementService', async () => { + const achService = moduleRef.get(AchievementService); + expect(achService).toBeDefined(); + }); + + describe('Update functions', () => { + it('should update an achievement: upsertAchievementFromDto', async () => { + const achID = (await prisma.achievement.findFirstOrThrow()).id; + const achDTO: AchievementDto = { + id: achID, + name: 'test achievement', + description: 'chal dto', + requiredPoints: 50, + imageUrl: 'update test', + }; + await achievementService.upsertAchievementFromDto(fullAbility, achDTO); + const ach = await prisma.achievement.findFirstOrThrow({ + where: { id: achID }, + }); + expect(ach.imageUrl).toEqual('update test'); + }); + + describe('Delete functions', () => { + it('should remove achievement from eventbase: removeAchievement', async () => { + const ach = await prisma.achievement.findFirstOrThrow(); + + await achievementService.removeAchievement(fullAbility, ach.id); + const achres = await prisma.challenge.findFirst({ + where: { id: ach.id }, + }); + expect(achres).toEqual(null); + }); + }); + + + +}; \ No newline at end of file diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index a1c3ed8a..3a24cbb4 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -9,12 +9,67 @@ import { UserGuard } from '../auth/jwt-auth.guard'; import { EventService } from '../event/event.service'; import { CallingUser } from '../auth/calling-user.decorator'; import { ClientService } from '../client/client.service'; -import { AchievementDto, AchievementTrackerDto } from './achievement.dto'; +import { AchievementDto, AchievementTrackerDto, RequestAchievementDataDto, UpdateAchievementDataDto } from './achievement.dto'; import { AchievementService } from './achievement.service'; import { PoliciesGuard } from '../casl/policy.guard'; +import { UserAbility } from '../casl/user-ability.decorator'; +import { AppAbility } from '../casl/casl-ability.factory'; +import { Action } from '../casl/action.enum'; +import { subject } from '@casl/ability'; @WebSocketGateway({ cors: true }) @UseGuards(UserGuard, PoliciesGuard) export class AchievementGateway { - constructor(private achievementService: AchievementService) {} + constructor( + private achievementService: AchievementService, + private clientService: ClientService + ) {} + + /** + * request achievements by list of ids + * update achievement with a dto + * @param user + * @param data + */ + + @SubscribeMessage('requestAchievementData') + async requestAchievementData( + @UserAbility() ability: AppAbility, + @CallingUser() user: User, + @MessageBody() data: RequestAchievementDataDto, + ) { + const achievement = + await this.achievementService.getAchievementsByIdsForAbility(ability, data.achievements); + } + + + @SubscribeMessage('updateAchievementData') + async updateAchievementData( + @UserAbility() ability: AppAbility, + @CallingUser() user: User, + @MessageBody() data: UpdateAchievementDataDto, + ) { + const achievement = await this.achievementService.getAchievementFromId(data.achievement.id); + + if (!achievement && ability.cannot(Action.Create, 'Achievement')) { + await this.clientService.emitErrorData( + user, + 'Permission denied for achievement update!', + ); + return; + } + + if (data.deleted && achievement) { + await this.achievementService.removeAchievement(ability, achievement.id); + await this.achievementService.emitUpdateAchievementData(achievement, true); + } else { + const achievement = await this.achievementService.upsertAchievementFromDto(ability, data.achievement); + } + + if (!achievement) { + await this.clientService.emitErrorData(user, 'Failed to update achievement!'); + return; + } + } } + diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index 2c777242..bdbd5e8d 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -1,17 +1,228 @@ +import { SessionLogService } from './../session-log/session-log.service'; import { Injectable } from '@nestjs/common'; -import { PrismaClient, User } from '@prisma/client'; +import { + $Enums, + Achievement, + AchievementTracker, + LocationType, + PrismaClient, + User, +} from '@prisma/client'; import { ClientService } from '../client/client.service'; import { PrismaService } from '../prisma/prisma.service'; -import { AchievementDto, AchievementTrackerDto } from './achievement.dto'; +import { accessibleBy } from '@casl/prisma'; +import { AppAbility, CaslAbilityFactory } from '../casl/casl-ability.factory'; +import { Action } from '../casl/action.enum'; +import { subject } from '@casl/ability'; +import { defaultAchievementData } from '../organization/organization.service'; +import { + AchievementType, + AchievementDto, + LocationType as LocType, + AchievementTrackerDto, + UpdateAchievementDataDto, +} from './achievement.dto'; @Injectable() export class AchievementService { constructor( - private prisma: PrismaService, + private readonly prisma: PrismaService, private clientService: ClientService, + private abilityFactory: CaslAbilityFactory, ) {} - async getAwardFromId(id: string) { + /** get an achievement by its id */ + async getAchievementFromId(id: string) { return await this.prisma.achievement.findFirstOrThrow({ where: { id } }); } + + /** get list of achievements by IDs */ + async getAchievementsByIdsForAbility( + ability: AppAbility, + ids: string[], + ): Promise { + return await this.prisma.achievement.findMany({ + where: { + AND: [ + { id: { in: ids } }, + accessibleBy(ability, Action.Read).Achievement, + ], + }, + }); + } + + /** upsert achievement */ + async upsertAchievementFromDto( + ability: AppAbility, + achievement: AchievementDto, + ) { + let ach = await this.prisma.achievement.findFirst({ + where: { id: achievement.id }, + }); + + if ( + ach && + (await this.prisma.achievement.findFirst({ + select: { id: true }, + where: { + AND: [ + accessibleBy(ability, Action.Update).Achievement, + { id: ach.id }, + ], + }, + })) + ) { + const assignData = { + requiredPoints: achievement.requiredPoints, + name: achievement.name?.substring(0, 2048), + description: achievement.description?.substring(0, 2048), + imageUrl: achievement.imageUrl?.substring(0, 2048), + locationType: achievement.locationType as LocationType, + achievementType: achievement.achievementType as AchievementType, + }; + + const data = await this.abilityFactory.filterInaccessible( + assignData, + subject('Achievement', ach), + ability, + Action.Update, + ); + + ach = await this.prisma.achievement.update({ + where: { id: ach.id }, + data, + }); + } else if (!ach && ability.can(Action.Create, 'Achievement')) { + const data = { + name: + achievement.name?.substring(0, 2048) ?? defaultAchievementData.name, + description: + achievement.description?.substring(0, 2048) ?? + defaultAchievementData.description, + imageUrl: + achievement.imageUrl?.substring(0, 2048) ?? + defaultAchievementData.imageUrl, + locationType: achievement.locationType as LocationType, + achievementType: achievement.achievementType as AchievementType, + eventIndex: achievement.eventId ?? 0, // check + requiredPoints: achievement.requiredPoints ?? 0, + }; + + ach = await this.prisma.achievement.create({ + data, + }); + + console.log(`Created achievement ${ach.id}`); + } else { + return null; + } + return ach; + } + + /** removes achievement by ID */ + async removeAchievement(ability: AppAbility, achievementId: string) { + const achievement = await this.prisma.achievement.findFirst({ + where: { + AND: [ + { id: achievementId }, + accessibleBy(ability, Action.Delete).Achievement, + ], + }, + }); + + if (!achievement) return; + + await this.prisma.achievementTracker.delete({ + where: { + id: achievementId, + }, + }); + + await this.prisma.achievement.delete({ + where: { id: achievementId }, + }); + console.log(`Deleted achievement ${achievementId}`); + } + + async emitUpdateAchievementData( + achievement: Achievement, + deleted: boolean, + target?: User, + ) { + const dto: UpdateAchievementDataDto = { + achievement: deleted + ? { id: achievement.id } + : await this.dtoForAchievement(achievement), + deleted, + }; + + await this.clientService.sendProtected( + 'updateAchievementData', + target?.id ?? achievement.id, + dto, + { + id: achievement.id, + dtoField: 'achievement', + subject: subject('Achievement', achievement), + }, + ); + } + + async dtoForAchievement(ach: Achievement): Promise { + return { + id: ach.id, + eventId: ach.linkedEventId as string, + requiredPoints: ach.requiredPoints, + name: ach.name, + description: ach.description, + imageUrl: ach.imageUrl, + locationType: ach.locationType as LocType, + achievementType: ach.achievementType as AchievementType, + }; + } + + // /** Creates an achievement tracker */ + // async createAchievementTracker(user: User, achievementId: string) { + // const existing = await this.prisma.achievementTracker.findFirst({ + // where: { userId: user.id, achievementId }, + // }); + + // if (existing) return; + + // const progress = await this.prisma.achievementTracker.create({ + // data: { + // userId: user.id, + // progress: 0, + // achievementId, + // }, + // }); + + // return progress; + // } + + // // async getAchievementTrackerById + + // async dtoForAchievementTracker(tracker: AchievementTracker): Promise { + // return { + // userId: tracker.userId, + // progress: tracker.progress, + // achievementId: tracker.achievementId, + // dateComplete: tracker.dateComplete?.toISOString(), + // }; + // } + + // /** Emits & updates an achievement tracker */ + // async emitUpdateAchievementTracker(tracker: AchievementTracker, target?: User) { + // const dto = await this.dtoForAchievementTracker(tracker); + + // await this.clientService.sendProtected( + // 'updateAchievementTrackerData', + // target?.id ?? tracker.userId, + // dto, + // { + // id: dto.achievementId, + // subject: 'AchievementTracker', + // }, + // ); + // } } diff --git a/server/src/challenge/challenge.service.ts b/server/src/challenge/challenge.service.ts index 5f62a31c..3cc8d348 100644 --- a/server/src/challenge/challenge.service.ts +++ b/server/src/challenge/challenge.service.ts @@ -325,17 +325,20 @@ export class ChallengeService { if (!challenge) return; + // checks for any eventTracker entries that reference the challenge const usedTrackers = await this.prisma.eventTracker.findMany({ where: { curChallengeId: challengeId, }, }); + // finds replacement challenge within the same event as the one being deleted const replacementChal = await this.prisma.challenge.findFirstOrThrow({ where: { linkedEventId: challenge.linkedEventId }, select: { id: true }, }); + // updates all affected trackers to reference the replacement challenge for (const tracker of usedTrackers) { await this.prisma.eventTracker.update({ where: { id: tracker.id }, diff --git a/server/src/client/client.service.ts b/server/src/client/client.service.ts index 3551a903..f866b914 100644 --- a/server/src/client/client.service.ts +++ b/server/src/client/client.service.ts @@ -19,11 +19,14 @@ import { import { EventTrackerDto, UpdateEventDataDto } from '../event/event.dto'; import { GroupInviteDto, UpdateGroupDataDto } from '../group/group.dto'; import { UpdateOrganizationDataDto } from '../organization/organization.dto'; +import { AchievementTrackerDto, UpdateAchievementDataDto } from '../achievement/achievement.dto'; export type ClientApiDef = { updateUserData: UpdateUserDataDto; updateErrorData: UpdateErrorDto; updateChallengeData: UpdateChallengeDataDto; + updateAchievementData: UpdateAchievementDataDto; + updateAchievementTrackerData: AchievementTrackerDto; updateEventTrackerData: EventTrackerDto; updateEventData: UpdateEventDataDto; updateLeaderData: UpdateLeaderDataDto; @@ -96,7 +99,7 @@ export class ClientService { this.gateway.server.in(target).socketsJoin(resource.id); const fieldList = Object.keys( - resource.dtoField ? (dto[resource.dtoField] as {}) : dto, + resource.dtoField ? (resource.dtoField as {}) : dto, ); const options: PermittedFieldsOptions = { @@ -133,7 +136,7 @@ export class ClientService { if (resource.dtoField) { (dto as any)[resource.dtoField] = Object.fromEntries( - Object.entries(dto[resource.dtoField] as any).filter(([k, v]) => + Object.entries(resource.dtoField as any).filter(([k, v]) => fields.includes(k), ), ) as any; diff --git a/server/src/organization/organization.service.ts b/server/src/organization/organization.service.ts index 39f91286..248d2f52 100644 --- a/server/src/organization/organization.service.ts +++ b/server/src/organization/organization.service.ts @@ -6,7 +6,8 @@ import { TimeLimitationType, OrganizationSpecialUsage, User, - LocationType, + LocationType,s + AchievementType } from '@prisma/client'; import { ClientService } from '../client/client.service'; import { v4 } from 'uuid'; @@ -43,6 +44,16 @@ export const defaultChallengeData = { closeRadius: 100, }; +export const defaultAchievementData = { + eventIndex: 0, + requiredPoints: 50, + name: 'Default Achievement', + description: 'Statue', + imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/5/5f/CentralAvenueCornell2.jpg', + locationType: LocationType.ARTS_QUAD, + achievementType: AchievementType.TOTAL_CHALLENGES, +}; + @Injectable() export class OrganizationService { constructor( From b6ab1837738aa55d0d24be36393fb40cb2e4bb73 Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Sun, 14 Apr 2024 23:18:50 -0400 Subject: [PATCH 02/17] Linting --- server/src/achievement/achievement.dto.ts | 4 +- server/src/achievement/achievement.gateway.ts | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index 41a53e5c..d3eba160 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -35,10 +35,10 @@ export interface AchievementTrackerDto { } export interface UpdateAchievementDataDto { - achievement: AchievementDto; + achievement: AchievementDto; deleted: boolean; } export interface RequestAchievementDataDto { achievements: string[]; -} \ No newline at end of file +} diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index 3a24cbb4..ef935179 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -9,7 +9,12 @@ import { UserGuard } from '../auth/jwt-auth.guard'; import { EventService } from '../event/event.service'; import { CallingUser } from '../auth/calling-user.decorator'; import { ClientService } from '../client/client.service'; -import { AchievementDto, AchievementTrackerDto, RequestAchievementDataDto, UpdateAchievementDataDto } from './achievement.dto'; +import { + AchievementDto, + AchievementTrackerDto, + RequestAchievementDataDto, + UpdateAchievementDataDto, +} from './achievement.dto'; import { AchievementService } from './achievement.service'; import { PoliciesGuard } from '../casl/policy.guard'; import { UserAbility } from '../casl/user-ability.decorator'; @@ -22,10 +27,10 @@ import { subject } from '@casl/ability'; export class AchievementGateway { constructor( private achievementService: AchievementService, - private clientService: ClientService + private clientService: ClientService, ) {} - /** + /** * request achievements by list of ids * update achievement with a dto * @param user @@ -38,18 +43,22 @@ export class AchievementGateway { @CallingUser() user: User, @MessageBody() data: RequestAchievementDataDto, ) { - const achievement = - await this.achievementService.getAchievementsByIdsForAbility(ability, data.achievements); + const achievement = + await this.achievementService.getAchievementsByIdsForAbility( + ability, + data.achievements, + ); } - @SubscribeMessage('updateAchievementData') async updateAchievementData( @UserAbility() ability: AppAbility, @CallingUser() user: User, @MessageBody() data: UpdateAchievementDataDto, ) { - const achievement = await this.achievementService.getAchievementFromId(data.achievement.id); + const achievement = await this.achievementService.getAchievementFromId( + data.achievement.id, + ); if (!achievement && ability.cannot(Action.Create, 'Achievement')) { await this.clientService.emitErrorData( @@ -61,15 +70,24 @@ export class AchievementGateway { if (data.deleted && achievement) { await this.achievementService.removeAchievement(ability, achievement.id); - await this.achievementService.emitUpdateAchievementData(achievement, true); + await this.achievementService.emitUpdateAchievementData( + achievement, + true, + ); } else { - const achievement = await this.achievementService.upsertAchievementFromDto(ability, data.achievement); + const achievement = + await this.achievementService.upsertAchievementFromDto( + ability, + data.achievement, + ); } if (!achievement) { - await this.clientService.emitErrorData(user, 'Failed to update achievement!'); + await this.clientService.emitErrorData( + user, + 'Failed to update achievement!', + ); return; } } } - From c5d06c36e6647fbd5c726c7e5fe436eb33fe204d Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Sun, 14 Apr 2024 22:54:15 -0400 Subject: [PATCH 03/17] Implement achievements backend --- server/src/achievement/achievement.dto.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index d3eba160..056b35a1 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -25,6 +25,13 @@ export interface AchievementDto { imageUrl?: string; locationType?: LocationType; achievementType?: AchievementType; + eventId?: string; + requiredPoints?: number; + name?: string; + description?: string; + imageUrl?: string; + locationType?: LocationType; + achievementType?: AchievementType; } export interface AchievementTrackerDto { @@ -33,12 +40,3 @@ export interface AchievementTrackerDto { achievementId: string; dateComplete?: string; } - -export interface UpdateAchievementDataDto { - achievement: AchievementDto; - deleted: boolean; -} - -export interface RequestAchievementDataDto { - achievements: string[]; -} From cba5e90a000ff8818b69a945ae362222e85bf115 Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Sun, 14 Apr 2024 23:18:50 -0400 Subject: [PATCH 04/17] Linting --- server/src/achievement/achievement.dto.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index 056b35a1..113b51c7 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -40,3 +40,12 @@ export interface AchievementTrackerDto { achievementId: string; dateComplete?: string; } + +export interface UpdateAchievementDataDto { + achievement: AchievementDto; + deleted: boolean; +} + +export interface RequestAchievementDataDto { + achievements: string[]; +} From 880237529be72472915ecaec66379ad421bdbeaf Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Mon, 15 Apr 2024 00:14:11 -0400 Subject: [PATCH 05/17] Enhance documentation for DTO, gateway, service files; remove duplicate code error --- server/src/achievement/achievement.dto.ts | 9 +-------- server/src/achievement/achievement.gateway.ts | 8 ++++++++ server/src/achievement/achievement.service.ts | 9 ++++++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index 113b51c7..cf25c443 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -15,7 +15,7 @@ export enum AchievementType { TotalJourneys = 'TOTAL_JOURNEYS', TotalChallengesOrJourneys = 'TOTAL_CHALLENGES_OR_JOURNEYS', } - +) export interface AchievementDto { id: string; eventId?: string; @@ -25,13 +25,6 @@ export interface AchievementDto { imageUrl?: string; locationType?: LocationType; achievementType?: AchievementType; - eventId?: string; - requiredPoints?: number; - name?: string; - description?: string; - imageUrl?: string; - locationType?: LocationType; - achievementType?: AchievementType; } export interface AchievementTrackerDto { diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index ef935179..1575b1a0 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -50,6 +50,14 @@ export class AchievementGateway { ); } + /** + * Updates achievement data based on a provided DTO. + * Triggered by the 'updateAchievementData' message. + * @param ability + * @param user + * @param data + * @returns + */ @SubscribeMessage('updateAchievementData') async updateAchievementData( @UserAbility() ability: AppAbility, diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index bdbd5e8d..45829c95 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -31,12 +31,12 @@ export class AchievementService { private abilityFactory: CaslAbilityFactory, ) {} - /** get an achievement by its id */ + /** get an achievement by its ID */ async getAchievementFromId(id: string) { return await this.prisma.achievement.findFirstOrThrow({ where: { id } }); } - /** get list of achievements by IDs */ + /** get list of achievements by IDs, based on ability */ async getAchievementsByIdsForAbility( ability: AppAbility, ids: string[], @@ -119,7 +119,7 @@ export class AchievementService { return ach; } - /** removes achievement by ID */ + /** remove achievement by ID */ async removeAchievement(ability: AppAbility, achievementId: string) { const achievement = await this.prisma.achievement.findFirst({ where: { @@ -144,6 +144,7 @@ export class AchievementService { console.log(`Deleted achievement ${achievementId}`); } + /** emit & update data for an achievement */ async emitUpdateAchievementData( achievement: Achievement, deleted: boolean, @@ -181,6 +182,8 @@ export class AchievementService { }; } + /** tracking functions; not yet completed */ + // /** Creates an achievement tracker */ // async createAchievementTracker(user: User, achievementId: string) { // const existing = await this.prisma.achievementTracker.findFirst({ From fca604adec28f223190bee30744da617f9f9594f Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Thu, 18 Apr 2024 11:25:20 -0400 Subject: [PATCH 06/17] Fixed bugs -Modified prisma schema to link achievements to organizations -Fixed bugs in achievement DTO, service, gateway -Fixed logic in updateEventData, updateChallengeData, updateAchievementData -Removed old code from faulty merge --- .vscode/settings.json | 3 +- admin/src/all.dto.ts | 47 ++--- admin/src/components/ServerApi.tsx | 22 +++ game/lib/api/game_client_api.dart | 20 ++ game/lib/api/game_client_dto.dart | 183 ++++++++++++------ game/lib/api/game_server_api.dart | 6 + server/src/achievement/achievement.dto.ts | 20 +- .../src/achievement/achievement.e2e-spec.ts | 24 +-- server/src/achievement/achievement.gateway.ts | 52 +++-- server/src/achievement/achievement.service.ts | 129 +++++++----- server/src/challenge/challenge.gateway.ts | 5 +- server/src/client/client.service.ts | 22 +-- server/src/event/event.gateway.ts | 4 +- .../src/organization/organization.service.ts | 2 +- 14 files changed, 329 insertions(+), 210 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index cf9784df..7498308f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,6 @@ "typescript.preferences.importModuleSpecifier": "relative", "githubPullRequests.ignoredPullRequestBranches": [ "master" - ] + ], + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file diff --git a/admin/src/all.dto.ts b/admin/src/all.dto.ts index 65836e9b..1671e481 100644 --- a/admin/src/all.dto.ts +++ b/admin/src/all.dto.ts @@ -1,32 +1,12 @@ // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! -type LocationType = - | "EngQuad" - | "ArtsQuad" - | "AgQuad" - | "NorthCampus" - | "WestCampus" - | "Collegetown" - | "IthacaCommons" - | "Any"; - -type AchievementType = +type AchievementTypeDto = | "TotalPoints" | "TotalChallenges" | "TotalJourneys" | "TotalChallengesOrJourneys"; -type AchievementLocationTypeDto = - | "ENG_QUAD" - | "ARTS_QUAD" - | "AG_QUAD" - | "NORTH_CAMPUS" - | "WEST_CAMPUS" - | "COLLEGETOWN" - | "IthacaCommons" - | "Any"; - type AchievementAchievementTypeDto = | "TOTAL_POINTS" | "TOTAL_CHALLENGES" @@ -57,14 +37,14 @@ type UserAuthTypeDto = "apple" | "google" | "device"; export interface AchievementDto { id: string; - eventId: string; - requiredPoints: number; - name: string; - description: string; - imageUrl: string; - locationType: AchievementLocationTypeDto; - achievementType: AchievementAchievementTypeDto; - organizations: string[]; + eventId?: string; + requiredPoints?: number; + name?: string; + description?: string; + imageUrl?: string; + locationType?: string; + achievementType?: AchievementAchievementTypeDto; + initialOrganizationId?: string; } export interface AchievementTrackerDto { @@ -74,6 +54,15 @@ export interface AchievementTrackerDto { dateComplete?: string; } +export interface UpdateAchievementDataDto { + achievement: AchievementDto; + deleted: boolean; +} + +export interface RequestAchievementDataDto { + achievements: string[]; +} + export interface LoginDto { idToken: string; lat: number; diff --git a/admin/src/components/ServerApi.tsx b/admin/src/components/ServerApi.tsx index d1226e59..6da8aedb 100644 --- a/admin/src/components/ServerApi.tsx +++ b/admin/src/components/ServerApi.tsx @@ -13,6 +13,14 @@ export class ServerApi { this.socket.emit(ev, data); } + requestAchievementData(data: dto.RequestAchievementDataDto) { + this.send("requestAchievementData", data); + } + + updateAchievementData(data: dto.UpdateAchievementDataDto) { + this.send("updateAchievementData", data); + } + requestChallengeData(data: dto.RequestChallengeDataDto) { this.send("requestChallengeData", data); } @@ -132,6 +140,20 @@ export class ServerApi { this.socket.on("updateChallengeData", (data) => callback(data)); } + onUpdateAchievementData( + callback: (data: dto.UpdateAchievementDataDto) => void + ) { + this.socket.removeAllListeners("updateAchievementData"); + this.socket.on("updateAchievementData", (data) => callback(data)); + } + + onUpdateAchievementTrackerData( + callback: (data: dto.AchievementTrackerDto) => void + ) { + this.socket.removeAllListeners("updateAchievementTrackerData"); + this.socket.on("updateAchievementTrackerData", (data) => callback(data)); + } + onUpdateEventTrackerData(callback: (data: dto.EventTrackerDto) => void) { this.socket.removeAllListeners("updateEventTrackerData"); this.socket.on("updateEventTrackerData", (data) => callback(data)); diff --git a/game/lib/api/game_client_api.dart b/game/lib/api/game_client_api.dart index c1da5988..15b4a686 100644 --- a/game/lib/api/game_client_api.dart +++ b/game/lib/api/game_client_api.dart @@ -23,6 +23,16 @@ class GameClientApi { Stream get updateChallengeDataStream => _updateChallengeDataController.stream; + final _updateAchievementDataController = + StreamController.broadcast(sync: true); + Stream get updateAchievementDataStream => + _updateAchievementDataController.stream; + + final _updateAchievementTrackerDataController = + StreamController.broadcast(sync: true); + Stream get updateAchievementTrackerDataStream => + _updateAchievementTrackerDataController.stream; + final _updateEventTrackerDataController = StreamController.broadcast(sync: true); Stream get updateEventTrackerDataStream => @@ -85,6 +95,16 @@ class GameClientApi { (data) => _updateChallengeDataController .add(UpdateChallengeDataDto.fromJson(data))); + sock.on( + "updateAchievementData", + (data) => _updateAchievementDataController + .add(UpdateAchievementDataDto.fromJson(data))); + + sock.on( + "updateAchievementTrackerData", + (data) => _updateAchievementTrackerDataController + .add(AchievementTrackerDto.fromJson(data))); + sock.on( "updateEventTrackerData", (data) => _updateEventTrackerDataController diff --git a/game/lib/api/game_client_dto.dart b/game/lib/api/game_client_dto.dart index 6fa0d6aa..e98f53e1 100644 --- a/game/lib/api/game_client_dto.dart +++ b/game/lib/api/game_client_dto.dart @@ -1,35 +1,13 @@ // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! -enum LocationType { - EngQuad, - ArtsQuad, - AgQuad, - NorthCampus, - WestCampus, - Collegetown, - IthacaCommons, - Any, -} - -enum AchievementType { +enum AchievementTypeDto { TotalPoints, TotalChallenges, TotalJourneys, TotalChallengesOrJourneys, } -enum AchievementLocationTypeDto { - ENG_QUAD, - ARTS_QUAD, - AG_QUAD, - NORTH_CAMPUS, - WEST_CAMPUS, - COLLEGETOWN, - IthacaCommons, - Any, -} - enum AchievementAchievementTypeDto { TOTAL_POINTS, TOTAL_CHALLENGES, @@ -83,64 +61,92 @@ class AchievementDto { Map toJson() { Map fields = {}; fields['id'] = id; - fields['eventId'] = eventId; - fields['requiredPoints'] = requiredPoints; - fields['name'] = name; - fields['description'] = description; - fields['imageUrl'] = imageUrl; - fields['locationType'] = locationType!.name; - fields['achievementType'] = achievementType!.name; - fields['organizations'] = organizations; + if (eventId != null) { + fields['eventId'] = eventId; + } + if (requiredPoints != null) { + fields['requiredPoints'] = requiredPoints; + } + if (name != null) { + fields['name'] = name; + } + if (description != null) { + fields['description'] = description; + } + if (imageUrl != null) { + fields['imageUrl'] = imageUrl; + } + if (locationType != null) { + fields['locationType'] = locationType; + } + if (achievementType != null) { + fields['achievementType'] = achievementType!.name; + } + if (initialOrganizationId != null) { + fields['initialOrganizationId'] = initialOrganizationId; + } return fields; } AchievementDto.fromJson(Map fields) { id = fields["id"]; - eventId = fields["eventId"]; - requiredPoints = fields["requiredPoints"]; - name = fields["name"]; - description = fields["description"]; - imageUrl = fields["imageUrl"]; + eventId = fields.containsKey('eventId') ? (fields["eventId"]) : null; + requiredPoints = fields.containsKey('requiredPoints') + ? (fields["requiredPoints"]) + : null; + name = fields.containsKey('name') ? (fields["name"]) : null; + description = + fields.containsKey('description') ? (fields["description"]) : null; + imageUrl = fields.containsKey('imageUrl') ? (fields["imageUrl"]) : null; locationType = - AchievementLocationTypeDto.values.byName(fields['locationType']); - achievementType = - AchievementAchievementTypeDto.values.byName(fields['achievementType']); - organizations = List.from(fields['organizations']); + fields.containsKey('locationType') ? (fields["locationType"]) : null; + achievementType = fields.containsKey('achievementType') + ? (AchievementAchievementTypeDto.values + .byName(fields['achievementType'])) + : null; + initialOrganizationId = fields.containsKey('initialOrganizationId') + ? (fields["initialOrganizationId"]) + : null; } void partialUpdate(AchievementDto other) { id = other.id; - eventId = other.eventId; - requiredPoints = other.requiredPoints; - name = other.name; - description = other.description; - imageUrl = other.imageUrl; - locationType = other.locationType; - achievementType = other.achievementType; - organizations = other.organizations; + eventId = other.eventId == null ? eventId : other.eventId; + requiredPoints = + other.requiredPoints == null ? requiredPoints : other.requiredPoints; + name = other.name == null ? name : other.name; + description = other.description == null ? description : other.description; + imageUrl = other.imageUrl == null ? imageUrl : other.imageUrl; + locationType = + other.locationType == null ? locationType : other.locationType; + achievementType = + other.achievementType == null ? achievementType : other.achievementType; + initialOrganizationId = other.initialOrganizationId == null + ? initialOrganizationId + : other.initialOrganizationId; } AchievementDto({ required this.id, - required this.eventId, - required this.requiredPoints, - required this.name, - required this.description, - required this.imageUrl, - required this.locationType, - required this.achievementType, - required this.organizations, + this.eventId, + this.requiredPoints, + this.name, + this.description, + this.imageUrl, + this.locationType, + this.achievementType, + this.initialOrganizationId, }); late String id; - late String eventId; - late int requiredPoints; - late String name; - late String description; - late String imageUrl; - late AchievementLocationTypeDto locationType; - late AchievementAchievementTypeDto achievementType; - late List organizations; + late String? eventId; + late int? requiredPoints; + late String? name; + late String? description; + late String? imageUrl; + late String? locationType; + late AchievementAchievementTypeDto? achievementType; + late String? initialOrganizationId; } class AchievementTrackerDto { @@ -184,6 +190,55 @@ class AchievementTrackerDto { late String? dateComplete; } +class UpdateAchievementDataDto { + Map toJson() { + Map fields = {}; + fields['achievement'] = achievement!.toJson(); + fields['deleted'] = deleted; + return fields; + } + + UpdateAchievementDataDto.fromJson(Map fields) { + achievement = AchievementDto.fromJson(fields['achievement']); + deleted = fields["deleted"]; + } + + void partialUpdate(UpdateAchievementDataDto other) { + achievement = other.achievement; + deleted = other.deleted; + } + + UpdateAchievementDataDto({ + required this.achievement, + required this.deleted, + }); + + late AchievementDto achievement; + late bool deleted; +} + +class RequestAchievementDataDto { + Map toJson() { + Map fields = {}; + fields['achievements'] = achievements; + return fields; + } + + RequestAchievementDataDto.fromJson(Map fields) { + achievements = List.from(fields['achievements']); + } + + void partialUpdate(RequestAchievementDataDto other) { + achievements = other.achievements; + } + + RequestAchievementDataDto({ + required this.achievements, + }); + + late List achievements; +} + class LoginDto { Map toJson() { Map fields = {}; diff --git a/game/lib/api/game_server_api.dart b/game/lib/api/game_server_api.dart index db515744..173077e3 100644 --- a/game/lib/api/game_server_api.dart +++ b/game/lib/api/game_server_api.dart @@ -40,6 +40,12 @@ class GameServerApi { _socket.emit(ev, data); } + void requestAchievementData(RequestAchievementDataDto dto) => + _invokeWithRefresh("requestAchievementData", dto.toJson()); + + void updateAchievementData(UpdateAchievementDataDto dto) => + _invokeWithRefresh("updateAchievementData", dto.toJson()); + void requestChallengeData(RequestChallengeDataDto dto) => _invokeWithRefresh("requestChallengeData", dto.toJson()); diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index cf25c443..883457cc 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -1,21 +1,9 @@ -export enum LocationType { - EngQuad = 'ENG_QUAD', - ArtsQuad = 'ARTS_QUAD', - AgQuad = 'AG_QUAD', - NorthCampus = 'NORTH_CAMPUS', - WestCampus = 'WEST_CAMPUS', - Collegetown = 'COLLEGETOWN', - IthacaCommons = 'IthacaCommons', - Any = 'Any', -} - -export enum AchievementType { +export enum AchievementTypeDto { TotalPoints = 'TOTAL_POINTS', TotalChallenges = 'TOTAL_CHALLENGES', TotalJourneys = 'TOTAL_JOURNEYS', TotalChallengesOrJourneys = 'TOTAL_CHALLENGES_OR_JOURNEYS', } -) export interface AchievementDto { id: string; eventId?: string; @@ -23,8 +11,10 @@ export interface AchievementDto { name?: string; description?: string; imageUrl?: string; - locationType?: LocationType; - achievementType?: AchievementType; + locationType?: string; + achievementType?: AchievementTypeDto; + // achievementType?: string; + initialOrganizationId?: string; } export interface AchievementTrackerDto { diff --git a/server/src/achievement/achievement.e2e-spec.ts b/server/src/achievement/achievement.e2e-spec.ts index 4f097778..f7298bb9 100644 --- a/server/src/achievement/achievement.e2e-spec.ts +++ b/server/src/achievement/achievement.e2e-spec.ts @@ -22,8 +22,6 @@ import { ClientModule } from '../client/client.module'; import { AchievementDto, AchievementTrackerDto } from './achievement.dto'; import { AppAbility, CaslAbilityFactory } from '../casl/casl-ability.factory'; - - describe('AchievementModule E2E', () => { let app: INestApplication; let moduleRef: TestingModule; @@ -69,7 +67,7 @@ describe('AchievementModule E2E', () => { eventService = module.get(EventService); organizationService = module.get(OrganizationService); abilityFactory = module.get(CaslAbilityFactory); - + user = await userService.register( 'test@gmail.com', 'test', @@ -96,19 +94,20 @@ describe('AchievementModule E2E', () => { describe('Update functions', () => { it('should update an achievement: upsertAchievementFromDto', async () => { - const achID = (await prisma.achievement.findFirstOrThrow()).id; - const achDTO: AchievementDto = { - id: achID, + const achId = (await prisma.achievement.findFirstOrThrow()).id; + const achDto: AchievementDto = { + id: achId, name: 'test achievement', - description: 'chal dto', + description: 'achievements dto', requiredPoints: 50, imageUrl: 'update test', }; - await achievementService.upsertAchievementFromDto(fullAbility, achDTO); + await achievementService.upsertAchievementFromDto(fullAbility, achDto); const ach = await prisma.achievement.findFirstOrThrow({ - where: { id: achID }, + where: { id: achId }, }); - expect(ach.imageUrl).toEqual('update test'); + expect(ach.imageUrl).toEqual('update test'); + }); }); describe('Delete functions', () => { @@ -122,7 +121,4 @@ describe('AchievementModule E2E', () => { expect(achres).toEqual(null); }); }); - - - -}; \ No newline at end of file +}); diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index 1575b1a0..e6e2a91c 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -43,11 +43,14 @@ export class AchievementGateway { @CallingUser() user: User, @MessageBody() data: RequestAchievementDataDto, ) { - const achievement = - await this.achievementService.getAchievementsByIdsForAbility( - ability, - data.achievements, - ); + const achs = await this.achievementService.getAchievementsByIdsForAbility( + ability, + data.achievements, + ); + console.log(achs.length); + for (const ach of achs) { + await this.achievementService.emitUpdateAchievementData(ach, false, user); + } } /** @@ -64,20 +67,21 @@ export class AchievementGateway { @CallingUser() user: User, @MessageBody() data: UpdateAchievementDataDto, ) { + // TODO: need to change organizations when removing achievement const achievement = await this.achievementService.getAchievementFromId( data.achievement.id, ); - if (!achievement && ability.cannot(Action.Create, 'Achievement')) { - await this.clientService.emitErrorData( - user, - 'Permission denied for achievement update!', - ); - return; - } - - if (data.deleted && achievement) { - await this.achievementService.removeAchievement(ability, achievement.id); + if (data.deleted) { + if ( + !achievement || !(await this.achievementService.removeAchievement( + ability, + achievement.id, + )) + ) { + await this.clientService.emitErrorData(user, 'Failed to delete event!'); + return; + } await this.achievementService.emitUpdateAchievementData( achievement, true, @@ -88,14 +92,20 @@ export class AchievementGateway { ability, data.achievement, ); - } - if (!achievement) { - await this.clientService.emitErrorData( - user, - 'Failed to update achievement!', + if (!achievement) { + await this.clientService.emitErrorData( + user, + 'Failed to upsert achievement!', + ); + return; + } + + this.clientService.subscribe(user, achievement.id); + await this.achievementService.emitUpdateAchievementData( + achievement, + false, ); - return; } } } diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index 45829c95..363291d4 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common'; import { $Enums, Achievement, + AchievementType, AchievementTracker, LocationType, PrismaClient, @@ -16,9 +17,8 @@ import { Action } from '../casl/action.enum'; import { subject } from '@casl/ability'; import { defaultAchievementData } from '../organization/organization.service'; import { - AchievementType, + AchievementTypeDto, AchievementDto, - LocationType as LocType, AchievementTrackerDto, UpdateAchievementDataDto, } from './achievement.dto'; @@ -54,58 +54,68 @@ export class AchievementService { /** upsert achievement */ async upsertAchievementFromDto( ability: AppAbility, - achievement: AchievementDto, + achievement: AchievementDto ) { let ach = await this.prisma.achievement.findFirst({ where: { id: achievement.id }, }); - if ( - ach && - (await this.prisma.achievement.findFirst({ - select: { id: true }, + const canUpdateOrg = + (await this.prisma.organization.count({ + where: { + AND: [ + { id: achievement.initialOrganizationId ?? '' }, + accessibleBy(ability, Action.Update).Organization, + ], + }, + })) > 0; + const canUpdateAch = + (await this.prisma.achievement.count({ where: { AND: [ accessibleBy(ability, Action.Update).Achievement, - { id: ach.id }, + { id: ach?.id ?? '' }, ], }, - })) - ) { - const assignData = { - requiredPoints: achievement.requiredPoints, - name: achievement.name?.substring(0, 2048), - description: achievement.description?.substring(0, 2048), - imageUrl: achievement.imageUrl?.substring(0, 2048), - locationType: achievement.locationType as LocationType, - achievementType: achievement.achievementType as AchievementType, - }; + })) > 0; - const data = await this.abilityFactory.filterInaccessible( + const assignData = { + requiredPoints: achievement.requiredPoints, + name: achievement.name?.substring(0, 2048), + description: achievement.description?.substring(0, 2048), + imageUrl: achievement.imageUrl?.substring(0, 2048), + locationType: achievement.locationType as LocationType, + achievementType: achievement.achievementType as AchievementTypeDto, + }; + + if (ach && canUpdateAch) { + const updateData = await this.abilityFactory.filterInaccessible( + ach.id, assignData, - subject('Achievement', ach), + 'Achievement', ability, Action.Update, + this.prisma.achievement, ); ach = await this.prisma.achievement.update({ where: { id: ach.id }, - data, + data: updateData, }); - } else if (!ach && ability.can(Action.Create, 'Achievement')) { + } else if (!ach && canUpdateOrg) { const data = { name: - achievement.name?.substring(0, 2048) ?? defaultAchievementData.name, + assignData.name?.substring(0, 2048) ?? defaultAchievementData.name, description: - achievement.description?.substring(0, 2048) ?? + assignData.description?.substring(0, 2048) ?? defaultAchievementData.description, imageUrl: - achievement.imageUrl?.substring(0, 2048) ?? + assignData.imageUrl?.substring(0, 2048) ?? defaultAchievementData.imageUrl, - locationType: achievement.locationType as LocationType, - achievementType: achievement.achievementType as AchievementType, - eventIndex: achievement.eventId ?? 0, // check - requiredPoints: achievement.requiredPoints ?? 0, + locationType: assignData.locationType as LocationType, + achievementType: assignData.achievementType as AchievementTypeDto, + // eventIndex: assignData.eventId ?? 0, // check + requiredPoints: assignData.requiredPoints ?? 0, }; ach = await this.prisma.achievement.create({ @@ -119,7 +129,7 @@ export class AchievementService { return ach; } - /** remove achievement by ID */ + /** remove achievement by ID; return removal status */ async removeAchievement(ability: AppAbility, achievementId: string) { const achievement = await this.prisma.achievement.findFirst({ where: { @@ -130,18 +140,19 @@ export class AchievementService { }, }); - if (!achievement) return; - - await this.prisma.achievementTracker.delete({ - where: { - id: achievementId, - }, - }); - - await this.prisma.achievement.delete({ - where: { id: achievementId }, - }); - console.log(`Deleted achievement ${achievementId}`); + if (achievement) { + await this.prisma.achievement.delete({ + where: { id: achievementId }, + }); + await this.prisma.achievementTracker.delete({ + where: { + id: achievementId, + }, + }); //should this automatically delete? + console.log(`Deleted achievement ${achievementId}`); + return true; + } + return false; } /** emit & update data for an achievement */ @@ -163,12 +174,18 @@ export class AchievementService { dto, { id: achievement.id, + subject: 'Achievement', dtoField: 'achievement', - subject: subject('Achievement', achievement), + prismaStore: this.prisma.achievement, }, ); } + /** + * Converts an achievement from the database to a DTO + * @param ach event to get DTO for + * @returns an AchievementDTO for the event + */ async dtoForAchievement(ach: Achievement): Promise { return { id: ach.id, @@ -177,8 +194,30 @@ export class AchievementService { name: ach.name, description: ach.description, imageUrl: ach.imageUrl, - locationType: ach.locationType as LocType, - achievementType: ach.achievementType as AchievementType, + locationType: + ach.locationType == + LocationType.AG_QUAD ? 'AG_QUAD' + : ach.locationType === LocationType.ENG_QUAD + ? "ENG_QUAD" + : ach.locationType === LocationType.ARTS_QUAD + ? "ARTS_QUAD" + : ach.locationType === LocationType.COLLEGETOWN + ? "COLLEGETOWN" + : ach.locationType === LocationType.ITHACA_COMMONS + ? "ITHACA_COMMONS" + : ach.locationType === LocationType.NORTH_CAMPUS + ? "NORTH_CAMPUS" + : ach.locationType === LocationType.WEST_CAMPUS + ? "WEST_CAMPUS" + : ach.locationType === LocationType.ANY + ? "ANY" + : "OTHER", + // achievementType: ach.achievementType as AchievementTypeDto, + achievementType: + ach.achievementType === AchievementType.TOTAL_POINTS ? AchievementTypeDto.TotalPoints + : ach.achievementType === AchievementType.TOTAL_CHALLENGES ? AchievementTypeDto.TotalChallenges + : ach.achievementType === AchievementType.TOTAL_JOURNEYS ? AchievementTypeDto.TotalJourneys + : ach.achievementType === AchievementType.TOTAL_CHALLENGES_OR_JOURNEYS ? AchievementTypeDto.TotalChallengesOrJourneys }; } diff --git a/server/src/challenge/challenge.gateway.ts b/server/src/challenge/challenge.gateway.ts index d1a4d862..f73a1953 100644 --- a/server/src/challenge/challenge.gateway.ts +++ b/server/src/challenge/challenge.gateway.ts @@ -128,12 +128,13 @@ export class ChallengeGateway { data.challenge.id, ); - if (data.deleted && challenge) { + if (data.deleted) { const ev = (await this.eventService.getEventById( - challenge.linkedEventId ?? '', + challenge?.linkedEventId ?? '', ))!; if ( + !challenge || !(await this.challengeService.removeChallenge(ability, challenge.id)) ) { return; diff --git a/server/src/client/client.service.ts b/server/src/client/client.service.ts index 1d173fce..5712bb7f 100644 --- a/server/src/client/client.service.ts +++ b/server/src/client/client.service.ts @@ -102,14 +102,6 @@ export class ClientService { } else { this.gateway.server.in(target).socketsJoin(resource.id); - const fieldList = Object.keys( - resource.dtoField ? (resource.dtoField as {}) : dto, - ); - - const options: PermittedFieldsOptions = { - fieldsFrom: rule => rule.fields || fieldList, - }; - // Find all targeted users const users = await this.getAffectedUsers(target); @@ -127,17 +119,15 @@ export class ClientService { if (Object.keys(accessibleObj).length === 0) continue; if (resource.dtoField) { - (dto as any)[resource.dtoField] = Object.fromEntries( - Object.entries(resource.dtoField as any).filter(([k, v]) => - fields.includes(k), - ), - ) as any; - - await this.sendEvent(users, event, dto); + const newDto = { + ...dto, + [resource.dtoField]: accessibleObj, + }; + await this.sendEvent([user.id], event, newDto); } else { await this.sendEvent([user.id], event, accessibleObj); } } } } -} +} \ No newline at end of file diff --git a/server/src/event/event.gateway.ts b/server/src/event/event.gateway.ts index 42c13692..ef99b768 100644 --- a/server/src/event/event.gateway.ts +++ b/server/src/event/event.gateway.ts @@ -115,8 +115,8 @@ export class EventGateway { ) { const ev = await this.eventService.getEventById(data.event.id); - if (data.deleted && ev) { - if (!(await this.eventService.removeEvent(ability, ev.id))) { + if (data.deleted) { + if (!ev || !(await this.eventService.removeEvent(ability, ev.id))) { await this.clientService.emitErrorData(user, 'Failed to delete event!'); return; } diff --git a/server/src/organization/organization.service.ts b/server/src/organization/organization.service.ts index 73a132d4..e6ea0363 100644 --- a/server/src/organization/organization.service.ts +++ b/server/src/organization/organization.service.ts @@ -6,7 +6,7 @@ import { TimeLimitationType, OrganizationSpecialUsage, User, - LocationType,s + LocationType, AchievementType } from '@prisma/client'; import { ClientService } from '../client/client.service'; From b7fda2f56d8aa7d670a0ac2a892f67e7c805cd0b Mon Sep 17 00:00:00 2001 From: neketka Date: Sat, 20 Apr 2024 19:41:39 -0400 Subject: [PATCH 07/17] Fixed build error --- admin/src/all.dto.ts | 25 +++++++--------- admin/src/components/Events.tsx | 14 ++++----- game/lib/api/game_client_dto.dart | 20 ++++++------- server/src/achievement/achievement.dto.ts | 5 ++-- server/src/achievement/achievement.service.ts | 29 +++---------------- server/src/client/client.service.ts | 9 ++++-- 6 files changed, 38 insertions(+), 64 deletions(-) diff --git a/admin/src/all.dto.ts b/admin/src/all.dto.ts index 26cbec73..0d6801da 100644 --- a/admin/src/all.dto.ts +++ b/admin/src/all.dto.ts @@ -1,23 +1,12 @@ // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! - -type AchievementType = +type AchievementTypeDto = | "TotalPoints" | "TotalChallenges" | "TotalJourneys" | "TotalChallengesOrJourneys"; -type AchievementLocationTypeDto = - | "ENG_QUAD" - | "ARTS_QUAD" - | "AG_QUAD" - | "NORTH_CAMPUS" - | "WEST_CAMPUS" - | "COLLEGETOWN" - | "ITHACA_COMMONS" - | "ANY"; - type AchievementAchievementTypeDto = | "TOTAL_POINTS" | "TOTAL_CHALLENGES" @@ -33,7 +22,7 @@ type LoginEnrollmentTypeDto = | "ALUMNI" | "GUEST"; -export type ChallengeLocationDto = +type ChallengeLocationDto = | "ENG_QUAD" | "ARTS_QUAD" | "AG_QUAD" @@ -43,7 +32,15 @@ export type ChallengeLocationDto = | "ITHACA_COMMONS" | "ANY"; -export type EventCategoryDto = +type EventCategory = + | "FOOD" + | "NATURE" + | "HISTORICAL" + | "CAFE" + | "DININGHALL" + | "DORM"; + +type EventCategoryDto = | "FOOD" | "NATURE" | "HISTORICAL" diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index cb098ad6..1bde844f 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -55,8 +55,8 @@ function EventCard(props: { props.event.difficulty === "Easy" ? "Easy" : props.event.difficulty === "Normal" - ? "Normal" - : "Hard"; + ? "Normal" + : "Hard"; let categoryInput = props.event.category as string; const categoryType = @@ -150,8 +150,8 @@ function fromForm(form: EntryForm[], id: string): EventDto { (form[5] as OptionEntryForm).value === 0 ? "Easy" : (form[5] as OptionEntryForm).value === 1 - ? "Normal" - : "Hard", + ? "Normal" + : "Hard", latitudeF: 0, longitudeF: 0, @@ -186,11 +186,7 @@ function toForm(event: EventDto) { name: "Difficulty", options: ["Easy", "Normal", "Hard"], value: - event.difficulty === "Easy" - ? 0 - : event.difficulty === "Normal" - ? 1 - : 2, + event.difficulty === "Easy" ? 0 : event.difficulty === "Normal" ? 1 : 2, }, { name: "Publicly Visible", diff --git a/game/lib/api/game_client_dto.dart b/game/lib/api/game_client_dto.dart index b36853a9..172e40c7 100644 --- a/game/lib/api/game_client_dto.dart +++ b/game/lib/api/game_client_dto.dart @@ -8,17 +8,6 @@ enum AchievementTypeDto { TotalChallengesOrJourneys, } -enum AchievementLocationTypeDto { - ENG_QUAD, - ARTS_QUAD, - AG_QUAD, - NORTH_CAMPUS, - WEST_CAMPUS, - COLLEGETOWN, - ITHACA_COMMONS, - ANY, -} - enum AchievementAchievementTypeDto { TOTAL_POINTS, TOTAL_CHALLENGES, @@ -51,6 +40,15 @@ enum ChallengeLocationDto { ANY, } +enum EventCategory { + FOOD, + NATURE, + HISTORICAL, + CAFE, + DININGHALL, + DORM, +} + enum EventCategoryDto { FOOD, NATURE, diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index 8e0dc4e3..3936cfe2 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -1,3 +1,5 @@ +import { ChallengeLocationDto } from '../challenge/challenge.dto'; + export enum AchievementTypeDto { TotalPoints = 'TOTAL_POINTS', TotalChallenges = 'TOTAL_CHALLENGES', @@ -12,9 +14,8 @@ export interface AchievementDto { name?: string; description?: string; imageUrl?: string; - locationType?: string; + locationType?: ChallengeLocationDto; achievementType?: AchievementTypeDto; - // achievementType?: string; initialOrganizationId?: string; } diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index 363291d4..30818fc0 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -22,6 +22,7 @@ import { AchievementTrackerDto, UpdateAchievementDataDto, } from './achievement.dto'; +import { ChallengeLocationDto } from '../challenge/challenge.dto'; @Injectable() export class AchievementService { @@ -54,7 +55,7 @@ export class AchievementService { /** upsert achievement */ async upsertAchievementFromDto( ability: AppAbility, - achievement: AchievementDto + achievement: AchievementDto, ) { let ach = await this.prisma.achievement.findFirst({ where: { id: achievement.id }, @@ -194,30 +195,8 @@ export class AchievementService { name: ach.name, description: ach.description, imageUrl: ach.imageUrl, - locationType: - ach.locationType == - LocationType.AG_QUAD ? 'AG_QUAD' - : ach.locationType === LocationType.ENG_QUAD - ? "ENG_QUAD" - : ach.locationType === LocationType.ARTS_QUAD - ? "ARTS_QUAD" - : ach.locationType === LocationType.COLLEGETOWN - ? "COLLEGETOWN" - : ach.locationType === LocationType.ITHACA_COMMONS - ? "ITHACA_COMMONS" - : ach.locationType === LocationType.NORTH_CAMPUS - ? "NORTH_CAMPUS" - : ach.locationType === LocationType.WEST_CAMPUS - ? "WEST_CAMPUS" - : ach.locationType === LocationType.ANY - ? "ANY" - : "OTHER", - // achievementType: ach.achievementType as AchievementTypeDto, - achievementType: - ach.achievementType === AchievementType.TOTAL_POINTS ? AchievementTypeDto.TotalPoints - : ach.achievementType === AchievementType.TOTAL_CHALLENGES ? AchievementTypeDto.TotalChallenges - : ach.achievementType === AchievementType.TOTAL_JOURNEYS ? AchievementTypeDto.TotalJourneys - : ach.achievementType === AchievementType.TOTAL_CHALLENGES_OR_JOURNEYS ? AchievementTypeDto.TotalChallengesOrJourneys + locationType: ach.locationType as ChallengeLocationDto, + achievementType: ach.achievementType as AchievementTypeDto, }; } diff --git a/server/src/client/client.service.ts b/server/src/client/client.service.ts index 5712bb7f..539bd642 100644 --- a/server/src/client/client.service.ts +++ b/server/src/client/client.service.ts @@ -19,7 +19,10 @@ import { import { EventTrackerDto, UpdateEventDataDto } from '../event/event.dto'; import { GroupInviteDto, UpdateGroupDataDto } from '../group/group.dto'; import { UpdateOrganizationDataDto } from '../organization/organization.dto'; -import { AchievementTrackerDto, UpdateAchievementDataDto } from '../achievement/achievement.dto'; +import { + AchievementTrackerDto, + UpdateAchievementDataDto, +} from '../achievement/achievement.dto'; import { ExtractSubjectType } from '@casl/ability'; export type ClientApiDef = { @@ -109,7 +112,7 @@ export class ClientService { const ability = this.abilityFactory.createForUser(user); const accessibleObj = await this.abilityFactory.filterInaccessible( resource.id, - resource.dtoField ? (dto[resource.dtoField] as any) : dto, + resource.dtoField ? (dto as any)[resource.dtoField] : dto, resource.subject, ability, Action.Read, @@ -130,4 +133,4 @@ export class ClientService { } } } -} \ No newline at end of file +} From f397b0ddd3d7dfb7e3b871fe61d1d224f33e9099 Mon Sep 17 00:00:00 2001 From: neketka Date: Sat, 20 Apr 2024 19:57:43 -0400 Subject: [PATCH 08/17] Fix failing build --- admin/src/all.dto.ts | 160 +++++++++++++++++----------- admin/src/components/Challenges.tsx | 27 +++-- admin/src/components/Events.tsx | 16 ++- game/lib/api/game_client_dto.dart | 20 +++- scripts/updateapi-lib/tsgen.js | 6 +- scripts/updateapi-lib/tsgen.ts | 6 +- 6 files changed, 143 insertions(+), 92 deletions(-) diff --git a/admin/src/all.dto.ts b/admin/src/all.dto.ts index 0d6801da..e18172c6 100644 --- a/admin/src/all.dto.ts +++ b/admin/src/all.dto.ts @@ -1,67 +1,103 @@ // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! -type AchievementTypeDto = - | "TotalPoints" - | "TotalChallenges" - | "TotalJourneys" - | "TotalChallengesOrJourneys"; - -type AchievementAchievementTypeDto = - | "TOTAL_POINTS" - | "TOTAL_CHALLENGES" - | "TOTAL_JOURNEYS" - | "TOTAL_CHALLENGES_OR_JOURNEYS"; - -type LoginAudDto = "android" | "ios" | "web"; - -type LoginEnrollmentTypeDto = - | "UNDERGRADUATE" - | "GRADUATE" - | "FACULTY" - | "ALUMNI" - | "GUEST"; - -type ChallengeLocationDto = - | "ENG_QUAD" - | "ARTS_QUAD" - | "AG_QUAD" - | "NORTH_CAMPUS" - | "WEST_CAMPUS" - | "COLLEGETOWN" - | "ITHACA_COMMONS" - | "ANY"; - -type EventCategory = - | "FOOD" - | "NATURE" - | "HISTORICAL" - | "CAFE" - | "DININGHALL" - | "DORM"; - -type EventCategoryDto = - | "FOOD" - | "NATURE" - | "HISTORICAL" - | "CAFE" - | "DININGHALL" - | "DORM"; - -type EventTimeLimitationDto = "LIMITED_TIME" | "PERPETUAL"; - -type EventDifficultyDto = "Easy" | "Normal" | "Hard"; - -type SetAuthToOAuthProviderDto = "apple" | "google"; - -type UserEnrollmentTypeDto = - | "UNDERGRADUATE" - | "GRADUATE" - | "FACULTY" - | "ALUMNI" - | "GUEST"; - -type UserAuthTypeDto = "apple" | "google" | "device"; +export enum AchievementTypeDto { + TotalPoints = "TotalPoints", + TotalChallenges = "TotalChallenges", + TotalJourneys = "TotalJourneys", + TotalChallengesOrJourneys = "TotalChallengesOrJourneys", +} + +export enum AchievementLocationTypeDto { + ENG_QUAD = "ENG_QUAD", + ARTS_QUAD = "ARTS_QUAD", + AG_QUAD = "AG_QUAD", + NORTH_CAMPUS = "NORTH_CAMPUS", + WEST_CAMPUS = "WEST_CAMPUS", + COLLEGETOWN = "COLLEGETOWN", + ITHACA_COMMONS = "ITHACA_COMMONS", + ANY = "ANY", +} + +export enum AchievementAchievementTypeDto { + TOTAL_POINTS = "TOTAL_POINTS", + TOTAL_CHALLENGES = "TOTAL_CHALLENGES", + TOTAL_JOURNEYS = "TOTAL_JOURNEYS", + TOTAL_CHALLENGES_OR_JOURNEYS = "TOTAL_CHALLENGES_OR_JOURNEYS", +} + +export enum LoginAudDto { + android = "android", + ios = "ios", + web = "web", +} + +export enum LoginEnrollmentTypeDto { + UNDERGRADUATE = "UNDERGRADUATE", + GRADUATE = "GRADUATE", + FACULTY = "FACULTY", + ALUMNI = "ALUMNI", + GUEST = "GUEST", +} + +export enum ChallengeLocationDto { + ENG_QUAD = "ENG_QUAD", + ARTS_QUAD = "ARTS_QUAD", + AG_QUAD = "AG_QUAD", + NORTH_CAMPUS = "NORTH_CAMPUS", + WEST_CAMPUS = "WEST_CAMPUS", + COLLEGETOWN = "COLLEGETOWN", + ITHACA_COMMONS = "ITHACA_COMMONS", + ANY = "ANY", +} + +export enum EventCategory { + FOOD = "FOOD", + NATURE = "NATURE", + HISTORICAL = "HISTORICAL", + CAFE = "CAFE", + DININGHALL = "DININGHALL", + DORM = "DORM", +} + +export enum EventCategoryDto { + FOOD = "FOOD", + NATURE = "NATURE", + HISTORICAL = "HISTORICAL", + CAFE = "CAFE", + DININGHALL = "DININGHALL", + DORM = "DORM", +} + +export enum EventTimeLimitationDto { + LIMITED_TIME = "LIMITED_TIME", + PERPETUAL = "PERPETUAL", +} + +export enum EventDifficultyDto { + Easy = "Easy", + Normal = "Normal", + Hard = "Hard", +} + +export enum SetAuthToOAuthProviderDto { + apple = "apple", + google = "google", +} + +export enum UserEnrollmentTypeDto { + UNDERGRADUATE = "UNDERGRADUATE", + GRADUATE = "GRADUATE", + FACULTY = "FACULTY", + ALUMNI = "ALUMNI", + GUEST = "GUEST", +} + +export enum UserAuthTypeDto { + apple = "apple", + google = "google", + device = "device", +} export interface AchievementDto { id: string; @@ -70,7 +106,7 @@ export interface AchievementDto { name?: string; description?: string; imageUrl?: string; - locationType?: string; + locationType?: AchievementLocationTypeDto; achievementType?: AchievementAchievementTypeDto; initialOrganizationId?: string; } diff --git a/admin/src/components/Challenges.tsx b/admin/src/components/Challenges.tsx index f2bc09b7..ab1c179a 100644 --- a/admin/src/components/Challenges.tsx +++ b/admin/src/components/Challenges.tsx @@ -1,7 +1,7 @@ import { useContext, useState } from "react"; import { compareTwoStrings } from "string-similarity"; import styled, { css } from "styled-components"; -import { ChallengeDto } from "../all.dto"; +import { ChallengeDto, ChallengeLocationDto } from "../all.dto"; import { moveDown, moveUp } from "../ordering"; import { AlertModal } from "./AlertModal"; import { DeleteModal } from "./DeleteModal"; @@ -24,7 +24,6 @@ import { } from "./ListCard"; import { SearchBar } from "./SearchBar"; import { ServerDataContext } from "./ServerData"; -import { ChallengeLocationDto } from "../all.dto"; const ChallengeImage = styled.div<{ url: string }>` width: calc(100% + 23px); @@ -38,15 +37,15 @@ const ChallengeImage = styled.div<{ url: string }>` `} `; -const locationOptions = [ - "ENG_QUAD", - "ARTS_QUAD", - "AG_QUAD", - "NORTH_CAMPUS", - "WEST_CAMPUS", - "COLLEGETOWN", - "ITHACA_COMMONS", - "ANY", +const locationOptions: ChallengeLocationDto[] = [ + ChallengeLocationDto.ENG_QUAD, + ChallengeLocationDto.ARTS_QUAD, + ChallengeLocationDto.AG_QUAD, + ChallengeLocationDto.NORTH_CAMPUS, + ChallengeLocationDto.WEST_CAMPUS, + ChallengeLocationDto.COLLEGETOWN, + ChallengeLocationDto.ITHACA_COMMONS, + ChallengeLocationDto.ANY, ]; function ChallengeCard(props: { @@ -90,7 +89,7 @@ function makeForm(): EntryForm[] { { name: "Location", latitude: 42.447546, longitude: -76.484593 }, { name: "Location Description", - options: locationOptions, + options: locationOptions as string[], value: 0, }, { name: "Name", characterLimit: 256, value: "" }, @@ -158,9 +157,7 @@ function fromForm( return { id, name: (form[2] as FreeEntryForm).value, - location: locationOptions[ - (form[1] as OptionEntryForm).value - ] as ChallengeLocationDto, + location: locationOptions[(form[1] as OptionEntryForm).value], description: (form[3] as FreeEntryForm).value, points: (form[4] as NumberEntryForm).value, imageUrl: (form[5] as FreeEntryForm).value, diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index 1bde844f..1da9d807 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -22,7 +22,11 @@ import { SearchBar } from "./SearchBar"; import { ServerDataContext } from "./ServerData"; import { compareTwoStrings } from "string-similarity"; -import { EventDto } from "../all.dto"; +import { + EventDifficultyDto, + EventDto, + EventTimeLimitationDto, +} from "../all.dto"; import { AlertModal } from "./AlertModal"; import { EventCategoryDto } from "../all.dto"; @@ -135,7 +139,9 @@ function fromForm(form: EntryForm[], id: string): EventDto { id, requiredMembers: (form[3] as NumberEntryForm).value, timeLimitation: - (form[4] as OptionEntryForm).value === 0 ? "PERPETUAL" : "LIMITED_TIME", + (form[4] as OptionEntryForm).value === 0 + ? EventTimeLimitationDto.PERPETUAL + : EventTimeLimitationDto.LIMITED_TIME, name: (form[0] as FreeEntryForm).value, description: (form[1] as FreeEntryForm).value, @@ -148,10 +154,10 @@ function fromForm(form: EntryForm[], id: string): EventDto { challenges: [], difficulty: (form[5] as OptionEntryForm).value === 0 - ? "Easy" + ? EventDifficultyDto.Easy : (form[5] as OptionEntryForm).value === 1 - ? "Normal" - : "Hard", + ? EventDifficultyDto.Normal + : EventDifficultyDto.Hard, latitudeF: 0, longitudeF: 0, diff --git a/game/lib/api/game_client_dto.dart b/game/lib/api/game_client_dto.dart index 172e40c7..bc23534a 100644 --- a/game/lib/api/game_client_dto.dart +++ b/game/lib/api/game_client_dto.dart @@ -8,6 +8,17 @@ enum AchievementTypeDto { TotalChallengesOrJourneys, } +enum AchievementLocationTypeDto { + ENG_QUAD, + ARTS_QUAD, + AG_QUAD, + NORTH_CAMPUS, + WEST_CAMPUS, + COLLEGETOWN, + ITHACA_COMMONS, + ANY, +} + enum AchievementAchievementTypeDto { TOTAL_POINTS, TOTAL_CHALLENGES, @@ -108,7 +119,7 @@ class AchievementDto { fields['imageUrl'] = imageUrl; } if (locationType != null) { - fields['locationType'] = locationType; + fields['locationType'] = locationType!.name; } if (achievementType != null) { fields['achievementType'] = achievementType!.name; @@ -129,8 +140,9 @@ class AchievementDto { description = fields.containsKey('description') ? (fields["description"]) : null; imageUrl = fields.containsKey('imageUrl') ? (fields["imageUrl"]) : null; - locationType = - fields.containsKey('locationType') ? (fields["locationType"]) : null; + locationType = fields.containsKey('locationType') + ? (AchievementLocationTypeDto.values.byName(fields['locationType'])) + : null; achievementType = fields.containsKey('achievementType') ? (AchievementAchievementTypeDto.values .byName(fields['achievementType'])) @@ -175,7 +187,7 @@ class AchievementDto { late String? name; late String? description; late String? imageUrl; - late String? locationType; + late AchievementLocationTypeDto? locationType; late AchievementAchievementTypeDto? achievementType; late String? initialOrganizationId; } diff --git a/scripts/updateapi-lib/tsgen.js b/scripts/updateapi-lib/tsgen.js index 2c6fd5fc..6aebdd42 100644 --- a/scripts/updateapi-lib/tsgen.js +++ b/scripts/updateapi-lib/tsgen.js @@ -8,11 +8,11 @@ function genTsDtoFile(dtoDefs) { // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! `; for (const [name, fields] of dtoDefs.enumDtos.entries()) { - tsCode += `type ${name} =`; + tsCode += `export enum ${name} {`; for (const field of fields) { - tsCode += `\n | "${field}"`; + tsCode += `\n ${field} = "${field}",`; } - tsCode += ";\n\n"; + tsCode += "}\n\n"; } for (const [name, propMap] of dtoDefs.baseDtos.entries()) { tsCode += `export interface ${name} {\n`; diff --git a/scripts/updateapi-lib/tsgen.ts b/scripts/updateapi-lib/tsgen.ts index 6b7850a8..2aa1b9e1 100644 --- a/scripts/updateapi-lib/tsgen.ts +++ b/scripts/updateapi-lib/tsgen.ts @@ -8,11 +8,11 @@ export function genTsDtoFile(dtoDefs: DtoDefs) { `; for (const [name, fields] of dtoDefs.enumDtos.entries()) { - tsCode += `type ${name} =`; + tsCode += `export enum ${name} {`; for (const field of fields) { - tsCode += `\n | "${field}"`; + tsCode += `\n ${field} = "${field}",`; } - tsCode += ";\n\n"; + tsCode += "}\n\n"; } for (const [name, propMap] of dtoDefs.baseDtos.entries()) { From ce55317e3907e31c7d8b75e45479189c8800d8ee Mon Sep 17 00:00:00 2001 From: neketka Date: Sat, 20 Apr 2024 20:09:04 -0400 Subject: [PATCH 09/17] Fix code-style --- server/src/achievement/achievement.gateway.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index e6e2a91c..2efc274e 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -74,7 +74,8 @@ export class AchievementGateway { if (data.deleted) { if ( - !achievement || !(await this.achievementService.removeAchievement( + !achievement || + !(await this.achievementService.removeAchievement( ability, achievement.id, )) From 4da2771a00eba1133f7b941e09609166d4bb4ed8 Mon Sep 17 00:00:00 2001 From: neketka Date: Sun, 21 Apr 2024 12:07:40 -0400 Subject: [PATCH 10/17] Fix dtos --- admin/src/all.dto.ts | 24 ++++++------------------ game/lib/api/game_client_dto.dart | 22 +--------------------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/admin/src/all.dto.ts b/admin/src/all.dto.ts index c6943a39..adc38bd3 100644 --- a/admin/src/all.dto.ts +++ b/admin/src/all.dto.ts @@ -1,15 +1,12 @@ // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! -type AchievementLocationType = - | "EngQuad" - | "ArtsQuad" - | "AgQuad" - | "NorthCampus" - | "WestCampus" - | "Collegetown" - | "IthacaCommons" - | "Any"; +export enum AchievementTypeDto { + TotalPoints = "TotalPoints", + TotalChallenges = "TotalChallenges", + TotalJourneys = "TotalJourneys", + TotalChallengesOrJourneys = "TotalChallengesOrJourneys", +} export enum AchievementLocationTypeDto { ENG_QUAD = "ENG_QUAD", @@ -54,15 +51,6 @@ export enum ChallengeLocationDto { ANY = "ANY", } -export enum EventCategory { - FOOD = "FOOD", - NATURE = "NATURE", - HISTORICAL = "HISTORICAL", - CAFE = "CAFE", - DININGHALL = "DININGHALL", - DORM = "DORM", -} - export enum EventCategoryDto { FOOD = "FOOD", NATURE = "NATURE", diff --git a/game/lib/api/game_client_dto.dart b/game/lib/api/game_client_dto.dart index 39a569ff..04621812 100644 --- a/game/lib/api/game_client_dto.dart +++ b/game/lib/api/game_client_dto.dart @@ -1,18 +1,7 @@ // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! -enum AchievementLocationType { - EngQuad, - ArtsQuad, - AgQuad, - NorthCampus, - WestCampus, - Collegetown, - IthacaCommons, - Any, -} - -enum AchievementType { +enum AchievementTypeDto { TotalPoints, TotalChallenges, TotalJourneys, @@ -62,15 +51,6 @@ enum ChallengeLocationDto { ANY, } -enum EventCategory { - FOOD, - NATURE, - HISTORICAL, - CAFE, - DININGHALL, - DORM, -} - enum EventCategoryDto { FOOD, NATURE, From a72d3b5b058898f0e1634d253470d082f500ad60 Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Sun, 21 Apr 2024 20:14:47 -0400 Subject: [PATCH 11/17] Fixed bugs in achievement --- .../src/achievement/achievement.e2e-spec.ts | 83 +++++++++++++++-- server/src/achievement/achievement.service.ts | 88 ++++--------------- server/src/challenge/challenge.e2e-spec.ts | 2 +- 3 files changed, 98 insertions(+), 75 deletions(-) diff --git a/server/src/achievement/achievement.e2e-spec.ts b/server/src/achievement/achievement.e2e-spec.ts index f7298bb9..dd8d180c 100644 --- a/server/src/achievement/achievement.e2e-spec.ts +++ b/server/src/achievement/achievement.e2e-spec.ts @@ -12,6 +12,7 @@ import { EventTracker, AchievementTracker, Achievement, + OrganizationSpecialUsage, } from '@prisma/client'; import { SessionLogService } from '../session-log/session-log.service'; import { EventService } from '../event/event.service'; @@ -21,6 +22,8 @@ import { OrganizationService } from '../organization/organization.service'; import { ClientModule } from '../client/client.module'; import { AchievementDto, AchievementTrackerDto } from './achievement.dto'; import { AppAbility, CaslAbilityFactory } from '../casl/casl-ability.factory'; +import { ChallengeDto, ChallengeLocationDto } from '../challenge/challenge.dto'; +import { AchievementTypeDto } from './achievement.dto'; describe('AchievementModule E2E', () => { let app: INestApplication; @@ -35,6 +38,7 @@ describe('AchievementModule E2E', () => { let organizationService: OrganizationService; let abilityFactory: CaslAbilityFactory; let fullAbility: AppAbility; + let orgUsage: OrganizationSpecialUsage; beforeAll(async () => { moduleRef = await Test.createTestingModule({ @@ -67,6 +71,7 @@ describe('AchievementModule E2E', () => { eventService = module.get(EventService); organizationService = module.get(OrganizationService); abilityFactory = module.get(CaslAbilityFactory); + fullAbility = abilityFactory.createFull(); user = await userService.register( 'test@gmail.com', @@ -92,27 +97,95 @@ describe('AchievementModule E2E', () => { expect(achService).toBeDefined(); }); + describe('Create and read functions', () => { + it('should add an achievement: upsertAchievementFromDto', async () => { + const orgUsage = OrganizationSpecialUsage; + const orgId = ( + await organizationService.getDefaultOrganization(orgUsage.DEVICE_LOGIN) + ).id; + + const achDto: AchievementDto = { + id: '12345', + eventId: 'event123', + name: 'test', + description: 'ach dto', + requiredPoints: 1, + imageUrl: 'url', + locationType: ChallengeLocationDto.EngQuad, + achievementType: AchievementTypeDto.TotalChallengesOrJourneys, + initialOrganizationId: orgId, + }; + + const ach = await achievementService.upsertAchievementFromDto( + fullAbility, + achDto, + ); + console.log(ach); + + const findAch = await prisma.achievement.findFirstOrThrow({ + where: { id: ach!.id }, + }); + expect(findAch.description).toEqual('ach dto'); + }); + + it('should read achievements: getAchievementFromId, getAchievementsByIdsForAbility', async () => { + const ach = await prisma.achievement.findFirstOrThrow({ + where: { + description: 'ach dto', + }, + }); + const first = await achievementService.getAchievementFromId(ach.id); + expect(first).toEqual(ach); + + const achsByUser = + await achievementService.getAchievementsByIdsForAbility(fullAbility, [ + ach.id, + ]); + expect(achsByUser[0]).toEqual(ach); + }); + }); + describe('Update functions', () => { it('should update an achievement: upsertAchievementFromDto', async () => { const achId = (await prisma.achievement.findFirstOrThrow()).id; + const test = (await achievementService.getAchievementFromId(achId)) + .imageUrl; + console.log('before: ' + test); + const orgUsage = OrganizationSpecialUsage; + const orgId = ( + await organizationService.getDefaultOrganization(orgUsage.DEVICE_LOGIN) + ).id; + const achDto: AchievementDto = { id: achId, - name: 'test achievement', - description: 'achievements dto', - requiredPoints: 50, + eventId: 'event123', + name: 'test', + description: 'ach dto', + requiredPoints: 1, imageUrl: 'update test', + locationType: ChallengeLocationDto.EngQuad, + achievementType: AchievementTypeDto.TotalChallengesOrJourneys, + initialOrganizationId: orgId, }; + await achievementService.upsertAchievementFromDto(fullAbility, achDto); const ach = await prisma.achievement.findFirstOrThrow({ where: { id: achId }, }); + const testAfterUpdate = ( + await achievementService.getAchievementFromId(achId) + ).imageUrl; + console.log('after: ' + testAfterUpdate); + expect(ach.imageUrl).toEqual('update test'); }); }); describe('Delete functions', () => { - it('should remove achievement from eventbase: removeAchievement', async () => { - const ach = await prisma.achievement.findFirstOrThrow(); + it('should remove achievement: removeAchievement', async () => { + const ach = await prisma.achievement.findFirstOrThrow({ + where: { description: 'ach dto' }, + }); await achievementService.removeAchievement(fullAbility, ach.id); const achres = await prisma.challenge.findFirst({ diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index 30818fc0..ddb5c456 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -65,11 +65,13 @@ export class AchievementService { (await this.prisma.organization.count({ where: { AND: [ - { id: achievement.initialOrganizationId ?? '' }, accessibleBy(ability, Action.Update).Organization, + { id: achievement.initialOrganizationId ?? '' }, ], }, })) > 0; + console.log(canUpdateOrg); + const canUpdateAch = (await this.prisma.achievement.count({ where: { @@ -80,17 +82,16 @@ export class AchievementService { }, })) > 0; - const assignData = { - requiredPoints: achievement.requiredPoints, - name: achievement.name?.substring(0, 2048), - description: achievement.description?.substring(0, 2048), - imageUrl: achievement.imageUrl?.substring(0, 2048), - locationType: achievement.locationType as LocationType, - achievementType: achievement.achievementType as AchievementTypeDto, - }; - if (ach && canUpdateAch) { - const updateData = await this.abilityFactory.filterInaccessible( + const assignData = { + requiredPoints: achievement.requiredPoints, + name: achievement.name?.substring(0, 2048), + description: achievement.description?.substring(0, 2048), + imageUrl: achievement.imageUrl?.substring(0, 2048), + locationType: achievement.locationType as LocationType, + achievementType: achievement.achievementType as AchievementTypeDto, + }; + const data = await this.abilityFactory.filterInaccessible( ach.id, assignData, 'Achievement', @@ -101,22 +102,22 @@ export class AchievementService { ach = await this.prisma.achievement.update({ where: { id: ach.id }, - data: updateData, + data, }); } else if (!ach && canUpdateOrg) { const data = { name: - assignData.name?.substring(0, 2048) ?? defaultAchievementData.name, + achievement.name?.substring(0, 2048) ?? defaultAchievementData.name, description: - assignData.description?.substring(0, 2048) ?? + achievement.description?.substring(0, 2048) ?? defaultAchievementData.description, imageUrl: - assignData.imageUrl?.substring(0, 2048) ?? + achievement.imageUrl?.substring(0, 2048) ?? defaultAchievementData.imageUrl, - locationType: assignData.locationType as LocationType, - achievementType: assignData.achievementType as AchievementTypeDto, + locationType: achievement.locationType as LocationType, + achievementType: achievement.achievementType as AchievementTypeDto, // eventIndex: assignData.eventId ?? 0, // check - requiredPoints: assignData.requiredPoints ?? 0, + requiredPoints: achievement.requiredPoints ?? 0, }; ach = await this.prisma.achievement.create({ @@ -145,11 +146,6 @@ export class AchievementService { await this.prisma.achievement.delete({ where: { id: achievementId }, }); - await this.prisma.achievementTracker.delete({ - where: { - id: achievementId, - }, - }); //should this automatically delete? console.log(`Deleted achievement ${achievementId}`); return true; } @@ -200,50 +196,4 @@ export class AchievementService { }; } - /** tracking functions; not yet completed */ - - // /** Creates an achievement tracker */ - // async createAchievementTracker(user: User, achievementId: string) { - // const existing = await this.prisma.achievementTracker.findFirst({ - // where: { userId: user.id, achievementId }, - // }); - - // if (existing) return; - - // const progress = await this.prisma.achievementTracker.create({ - // data: { - // userId: user.id, - // progress: 0, - // achievementId, - // }, - // }); - - // return progress; - // } - - // // async getAchievementTrackerById - - // async dtoForAchievementTracker(tracker: AchievementTracker): Promise { - // return { - // userId: tracker.userId, - // progress: tracker.progress, - // achievementId: tracker.achievementId, - // dateComplete: tracker.dateComplete?.toISOString(), - // }; - // } - - // /** Emits & updates an achievement tracker */ - // async emitUpdateAchievementTracker(tracker: AchievementTracker, target?: User) { - // const dto = await this.dtoForAchievementTracker(tracker); - - // await this.clientService.sendProtected( - // 'updateAchievementTrackerData', - // target?.id ?? tracker.userId, - // dto, - // { - // id: dto.achievementId, - // subject: 'AchievementTracker', - // }, - // ); - // } } diff --git a/server/src/challenge/challenge.e2e-spec.ts b/server/src/challenge/challenge.e2e-spec.ts index 52e8fda3..2226e4d6 100644 --- a/server/src/challenge/challenge.e2e-spec.ts +++ b/server/src/challenge/challenge.e2e-spec.ts @@ -35,7 +35,7 @@ describe('ChallengeModule E2E', () => { let abilityFactory: CaslAbilityFactory; let fullAbility: AppAbility; - /** beforeAll runs before anything else, add new users and prerequesites to tests + /** beforeAll runs before anything else, add new users and prerequisites to tests * afterAll runs after all the tests, use it to remove lingering values in the database */ beforeAll(async () => { moduleRef = await Test.createTestingModule({ From bc318b84c8bf668618fd3fa284cc1938a2ce8312 Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Mon, 22 Apr 2024 09:07:46 -0400 Subject: [PATCH 12/17] Fixed code style across entire codebase --- .github/PULL_REQUEST_TEMPLATE.md | 3 +- .vs/ProjectSettings.json | 2 +- .vs/VSWorkspaceState.json | 9 +- .vscode/launch.json | 2 +- .vscode/settings.json | 6 +- admin/public/index.html | 2 +- admin/src/components/Challenges.tsx | 16 +- admin/src/components/EntryModal.tsx | 6 +- admin/src/components/Events.tsx | 12 +- admin/src/components/Groups.tsx | 6 +- admin/src/components/Modal.tsx | 9 +- admin/src/components/Organizations.tsx | 6 +- admin/src/components/ServerApi.tsx | 6 +- admin/src/components/ServerData.tsx | 18 +- admin/src/components/Users.tsx | 4 +- admin/src/index.css | 8 +- admin/src/index.tsx | 2 +- admin/src/post.ts | 2 +- admin/tsconfig.json | 12 +- game/analysis_options.yaml | 1 - game/android/app/google-services.json | 2 +- .../AppIcon.appiconset/Contents.json | 160 +++++----- .../LaunchImage.imageset/Contents.json | 26 +- .../LaunchImage.imageset/README.md | 2 +- game/web/index.html | 39 ++- game/web/manifest.json | 42 +-- scripts/dbreset.js | 2 +- scripts/tests.js | 24 +- scripts/updateapi-lib/apiscanner.js | 92 +++--- scripts/updateapi-lib/dartgen.js | 291 +++++++++--------- scripts/updateapi-lib/dtoscanner.js | 187 +++++------ scripts/updateapi-lib/dtoscanner.ts | 8 +- scripts/updateapi-lib/tsgen.js | 68 ++-- scripts/updateapi.js | 23 +- server/src/achievement/achievement.service.ts | 1 - server/src/challenge/challenge.gateway.ts | 5 +- server/src/event/event.e2e-spec.ts | 45 ++- server/src/event/event.service.ts | 12 +- server/src/group/group.service.ts | 5 +- .../src/organization/organization.e2e-spec.ts | 9 +- 40 files changed, 587 insertions(+), 588 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index db6101c9..7a2d4b5d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,7 +21,6 @@ Remaining TODOs: Depends on #{number of PR} - ### Test Plan @@ -36,7 +35,7 @@ Depends on #{number of PR} - A newly discovered dependency that hasn’t been addressed -### Breaking Changes +### Breaking Changes diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json index 0cf5ea50..ba2c8694 100644 --- a/.vs/ProjectSettings.json +++ b/.vs/ProjectSettings.json @@ -1,3 +1,3 @@ { "CurrentProjectSetting": "No Configurations" -} \ No newline at end of file +} diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json index 246622a9..1bdf5932 100644 --- a/.vs/VSWorkspaceState.json +++ b/.vs/VSWorkspaceState.json @@ -1,10 +1,5 @@ { - "ExpandedNodes": [ - "", - "\\game", - "\\game\\lib", - "\\game\\test" - ], + "ExpandedNodes": ["", "\\game", "\\game\\lib", "\\game\\test"], "SelectedNode": "\\game\\lib\\main.dart", "PreviewInSolutionExplorer": false -} \ No newline at end of file +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 12bff38c..16907b59 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -25,4 +25,4 @@ "flutterMode": "release" } ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 7498308f..59186479 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,5 @@ { "typescript.preferences.importModuleSpecifier": "relative", - "githubPullRequests.ignoredPullRequestBranches": [ - "master" - ], + "githubPullRequests.ignoredPullRequestBranches": ["master"], "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file +} diff --git a/admin/public/index.html b/admin/public/index.html index 81fb533f..75feb39a 100644 --- a/admin/public/index.html +++ b/admin/public/index.html @@ -1,4 +1,4 @@ - + diff --git a/admin/src/components/Challenges.tsx b/admin/src/components/Challenges.tsx index 0b2d9728..490a236e 100644 --- a/admin/src/components/Challenges.tsx +++ b/admin/src/components/Challenges.tsx @@ -151,7 +151,7 @@ function toForm(challenge: ChallengeDto) { function fromForm( form: EntryForm[], eventId: string, - id: string + id: string, ): ChallengeDto { return { id, @@ -194,7 +194,7 @@ export function Challenges() { entryButtonText="CREATE" onEntry={() => { serverData.updateChallenge( - fromForm(form, serverData.selectedEvent, "") + fromForm(form, serverData.selectedEvent, ""), ); setCreateModalOpen(false); }} @@ -209,7 +209,7 @@ export function Challenges() { entryButtonText="EDIT" onEntry={() => { serverData.updateChallenge( - fromForm(form, serverData.selectedEvent, currentId) + fromForm(form, serverData.selectedEvent, currentId), ); setEditModalOpen(false); }} @@ -255,7 +255,7 @@ export function Challenges() { : compareTwoStrings(b.name ?? "", query) - compareTwoStrings(a.name ?? "", query) + compareTwoStrings(b.description ?? "", query) - - compareTwoStrings(a.description ?? "", query) + compareTwoStrings(a.description ?? "", query), ) .map((chal: ChallengeDto) => ( id === chal.id - ) ?? 0 + (id: string) => id === chal.id, + ) ?? 0, ); serverData.updateEvent(selectedEvent); }} @@ -276,8 +276,8 @@ export function Challenges() { selectedEvent.challenges = moveDown( selectedEvent.challenges, selectedEvent.challenges.findIndex( - (id: string) => id === chal.id - ) + (id: string) => id === chal.id, + ), ); serverData.updateEvent(selectedEvent); }} diff --git a/admin/src/components/EntryModal.tsx b/admin/src/components/EntryModal.tsx index c0510bc2..9aecb949 100644 --- a/admin/src/components/EntryModal.tsx +++ b/admin/src/components/EntryModal.tsx @@ -75,7 +75,7 @@ function OptionEntryFormBox(props: { form: OptionEntryForm }) { value={val} onChange={(e) => setVal( - props.form.options[(props.form.value = e.target.selectedIndex)] + props.form.options[(props.form.value = e.target.selectedIndex)], ) } > @@ -111,7 +111,7 @@ function DateEntryFormBox(props: { form: DateEntryForm }) { useEffect( () => setVal(props.form.date.toISOString().slice(0, -1)), - [props.form] + [props.form], ); return ( @@ -175,7 +175,7 @@ function DraggableMarker(props: { } }, }), - [] + [], ); return ( diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index 91c41d97..dc856527 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -57,8 +57,8 @@ function EventCard(props: { props.event.difficulty === "Easy" ? "Easy" : props.event.difficulty === "Normal" - ? "Normal" - : "Hard"; + ? "Normal" + : "Hard"; let categoryInput = props.event.category as string; const categoryType = @@ -154,8 +154,8 @@ function fromForm(form: EntryForm[], id: string): EventDto { (form[5] as OptionEntryForm).value === 0 ? EventDifficultyDto.Easy : (form[5] as OptionEntryForm).value === 1 - ? EventDifficultyDto.Normal - : EventDifficultyDto.Hard, + ? EventDifficultyDto.Normal + : EventDifficultyDto.Hard, latitudeF: 0, longitudeF: 0, @@ -284,14 +284,14 @@ export function Events() { serverData.organizations .get(serverData.selectedOrg) ?.events?.map((evId: string) => serverData.events.get(evId)!) - .filter((ev?: EventDto) => !!ev) ?? [] + .filter((ev?: EventDto) => !!ev) ?? [], ) .sort( (a: EventDto, b: EventDto) => compareTwoStrings(b.name ?? "", query) - compareTwoStrings(a.name ?? "", query) + compareTwoStrings(b.description ?? "", query) - - compareTwoStrings(a.description ?? "", query) + compareTwoStrings(a.description ?? "", query), ) .map((ev) => ( r.id === data.id + (r) => r.id === data.id, ); rowsClone.splice(deletedRowIndex, 1); const newData = Array.from(serverData.groups.values()).map((gr) => - toForm(gr) + toForm(gr), ); newData.forEach((gr) => { let index = rowsClone.findIndex((r) => r.id === gr.id); @@ -185,7 +185,7 @@ function getColumns(setRowsData: any, serverData: any) { export function Groups() { const serverData = useContext(ServerDataContext); const [rowsData, setRowsData] = useState( - Array.from(serverData.groups.values()).map((gr) => toForm(gr)) + Array.from(serverData.groups.values()).map((gr) => toForm(gr)), ); return ( diff --git a/admin/src/components/Modal.tsx b/admin/src/components/Modal.tsx index 8a281070..745b28bc 100644 --- a/admin/src/components/Modal.tsx +++ b/admin/src/components/Modal.tsx @@ -14,10 +14,9 @@ const ModalBackground = styled.div<{ opacity: number }>` transition: opacity 0.15s; overflow-y: auto; - ${(props) => - css` - opacity: ${props.opacity}; - `} + ${(props) => css` + opacity: ${props.opacity}; + `} `; const ModalLayout = styled.div` @@ -97,7 +96,7 @@ export function Modal(props: { , - document.getElementById("modal-root")! + document.getElementById("modal-root")!, ); } return <>; diff --git a/admin/src/components/Organizations.tsx b/admin/src/components/Organizations.tsx index d0477b27..e25cef62 100644 --- a/admin/src/components/Organizations.tsx +++ b/admin/src/components/Organizations.tsx @@ -76,7 +76,7 @@ function makeForm() { function fromForm( form: EntryForm[], id: string, - oldDto: OrganizationDto + oldDto: OrganizationDto, ): OrganizationDto { return { ...oldDto, @@ -176,7 +176,7 @@ export function Organizations() { .sort( (a, b) => compareTwoStrings(b.name ?? "", query) - - compareTwoStrings(a.name ?? "", query) + compareTwoStrings(a.name ?? "", query), ) .map((org) => ( void + callback: (data: dto.UpdateAchievementDataDto) => void, ) { this.socket.removeAllListeners("updateAchievementData"); this.socket.on("updateAchievementData", (data) => callback(data)); } onUpdateAchievementTrackerData( - callback: (data: dto.AchievementTrackerDto) => void + callback: (data: dto.AchievementTrackerDto) => void, ) { this.socket.removeAllListeners("updateAchievementTrackerData"); this.socket.on("updateAchievementTrackerData", (data) => callback(data)); @@ -184,7 +184,7 @@ export class ServerApi { } onUpdateOrganizationData( - callback: (data: dto.UpdateOrganizationDataDto) => void + callback: (data: dto.UpdateOrganizationDataDto) => void, ) { this.socket.removeAllListeners("updateOrganizationData"); this.socket.on("updateOrganizationData", (data) => callback(data)); diff --git a/admin/src/components/ServerData.tsx b/admin/src/components/ServerData.tsx index e17a123c..53fee1c5 100644 --- a/admin/src/components/ServerData.tsx +++ b/admin/src/components/ServerData.tsx @@ -52,7 +52,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { const sock = useMemo( () => new ServerApi(connection.connection!), - [connection] + [connection], ); const [serverData, setServerData] = useState(() => ({ ...defaultData })); @@ -118,7 +118,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { sock.updateOrganizationData({ organization: { id }, deleted: true }); }, }), - [serverData, setServerData, sock] + [serverData, setServerData, sock], ); useEffect(() => { @@ -142,13 +142,13 @@ export function ServerDataProvider(props: { children: ReactNode }) { sock.requestChallengeData({ challenges: (data.event as EventDto).challenges?.filter( - (chal: string) => !(chal in oldChallenges) + (chal: string) => !(chal in oldChallenges), ) ?? [], }); serverData.events.set( (data.event as EventDto).id, - data.event as EventDto + data.event as EventDto, ); } @@ -160,7 +160,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { } else { serverData.challenges.set( (data.challenge as ChallengeDto).id, - data.challenge as ChallengeDto + data.challenge as ChallengeDto, ); } @@ -181,7 +181,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { } else { serverData.groups.set( (data.group as GroupDto).id, - data.group as GroupDto + data.group as GroupDto, ); } @@ -193,18 +193,18 @@ export function ServerDataProvider(props: { children: ReactNode }) { } else { const oldEvents = serverData.organizations.get( - (data.organization as OrganizationDto).id + (data.organization as OrganizationDto).id, )?.events ?? []; sock.requestEventData({ events: (data.organization as OrganizationDto).events?.filter( - (ev: string) => !(ev in oldEvents) + (ev: string) => !(ev in oldEvents), ), }); serverData.organizations.set( (data.organization as OrganizationDto).id, - data.organization as OrganizationDto + data.organization as OrganizationDto, ); } diff --git a/admin/src/components/Users.tsx b/admin/src/components/Users.tsx index 13590d64..91566f1e 100644 --- a/admin/src/components/Users.tsx +++ b/admin/src/components/Users.tsx @@ -191,7 +191,7 @@ function getColumns(setRowsData: any, serverData: any) { let rowsClone = [...tableManager.rowsApi.rows]; let updatedRowIndex = rowsClone.findIndex( - (r) => r.id === data.id + (r) => r.id === data.id, ); rowsClone[updatedRowIndex] = data; @@ -213,7 +213,7 @@ function getColumns(setRowsData: any, serverData: any) { export function Users() { const serverData = useContext(ServerDataContext); const [rowsData, setRowsData] = useState( - Array.from(serverData.users.values()).map((us) => toForm(us)) + Array.from(serverData.users.values()).map((us) => toForm(us)), ); return ( diff --git a/admin/src/index.css b/admin/src/index.css index 3d5577e1..69e48f23 100644 --- a/admin/src/index.css +++ b/admin/src/index.css @@ -20,8 +20,8 @@ dd { } /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */ -ul[role='list'], -ol[role='list'] { +ul[role="list"], +ol[role="list"] { list-style: none; } @@ -60,9 +60,9 @@ select { /* Remove all animations, transitions and smooth scroll for people that prefer not to see them */ @media (prefers-reduced-motion: reduce) { html:focus-within { - scroll-behavior: auto; + scroll-behavior: auto; } - + *, *::before, *::after { diff --git a/admin/src/index.tsx b/admin/src/index.tsx index 21decb92..bd9e0d62 100644 --- a/admin/src/index.tsx +++ b/admin/src/index.tsx @@ -23,7 +23,7 @@ ReactDOM.render( , - document.getElementById("root") + document.getElementById("root"), ); // If you want to start measuring performance in your app, pass a function diff --git a/admin/src/post.ts b/admin/src/post.ts index 084a6bce..f92dbdb1 100644 --- a/admin/src/post.ts +++ b/admin/src/post.ts @@ -1,6 +1,6 @@ export async function postRequest( route: string, - body: TBody + body: TBody, ): Promise { const resp = await fetch(route, { method: "post", diff --git a/admin/tsconfig.json b/admin/tsconfig.json index 43f09240..d26ce47e 100644 --- a/admin/tsconfig.json +++ b/admin/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,7 +16,5 @@ "noEmit": true, "jsx": "preserve" }, - "include": [ - "src" - ] -} \ No newline at end of file + "include": ["src"] +} diff --git a/game/analysis_options.yaml b/game/analysis_options.yaml index df0fea39..67ca129c 100644 --- a/game/analysis_options.yaml +++ b/game/analysis_options.yaml @@ -22,6 +22,5 @@ linter: # producing the lint. rules: # avoid_print: false # Uncomment to disable the `avoid_print` rule - # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/game/android/app/google-services.json b/game/android/app/google-services.json index 60a30e64..be461b3d 100644 --- a/game/android/app/google-services.json +++ b/game/android/app/google-services.json @@ -109,4 +109,4 @@ } ], "configuration_version": "1" -} \ No newline at end of file +} diff --git a/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index d36b1fab..e882ab98 100644 --- a/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,122 +1,122 @@ { - "images" : [ + "images": [ { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" + "size": "20x20", + "idiom": "iphone", + "filename": "Icon-App-20x20@2x.png", + "scale": "2x" }, { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" + "size": "20x20", + "idiom": "iphone", + "filename": "Icon-App-20x20@3x.png", + "scale": "3x" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" + "size": "29x29", + "idiom": "iphone", + "filename": "Icon-App-29x29@1x.png", + "scale": "1x" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" + "size": "29x29", + "idiom": "iphone", + "filename": "Icon-App-29x29@2x.png", + "scale": "2x" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" + "size": "29x29", + "idiom": "iphone", + "filename": "Icon-App-29x29@3x.png", + "scale": "3x" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" + "size": "40x40", + "idiom": "iphone", + "filename": "Icon-App-40x40@2x.png", + "scale": "2x" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" + "size": "40x40", + "idiom": "iphone", + "filename": "Icon-App-40x40@3x.png", + "scale": "3x" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" + "size": "60x60", + "idiom": "iphone", + "filename": "Icon-App-60x60@2x.png", + "scale": "2x" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" + "size": "60x60", + "idiom": "iphone", + "filename": "Icon-App-60x60@3x.png", + "scale": "3x" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" + "size": "20x20", + "idiom": "ipad", + "filename": "Icon-App-20x20@1x.png", + "scale": "1x" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" + "size": "20x20", + "idiom": "ipad", + "filename": "Icon-App-20x20@2x.png", + "scale": "2x" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" + "size": "29x29", + "idiom": "ipad", + "filename": "Icon-App-29x29@1x.png", + "scale": "1x" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" + "size": "29x29", + "idiom": "ipad", + "filename": "Icon-App-29x29@2x.png", + "scale": "2x" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" + "size": "40x40", + "idiom": "ipad", + "filename": "Icon-App-40x40@1x.png", + "scale": "1x" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" + "size": "40x40", + "idiom": "ipad", + "filename": "Icon-App-40x40@2x.png", + "scale": "2x" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" + "size": "76x76", + "idiom": "ipad", + "filename": "Icon-App-76x76@1x.png", + "scale": "1x" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" + "size": "76x76", + "idiom": "ipad", + "filename": "Icon-App-76x76@2x.png", + "scale": "2x" }, { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "Icon-App-83.5x83.5@2x.png", + "scale": "2x" }, { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "Icon-App-1024x1024@1x.png", + "scale": "1x" } ], - "info" : { - "version" : 1, - "author" : "xcode" + "info": { + "version": 1, + "author": "xcode" } } diff --git a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json index 0bedcf2f..781d7cdc 100644 --- a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -1,23 +1,23 @@ { - "images" : [ + "images": [ { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" + "idiom": "universal", + "filename": "LaunchImage.png", + "scale": "1x" }, { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" + "idiom": "universal", + "filename": "LaunchImage@2x.png", + "scale": "2x" }, { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" + "idiom": "universal", + "filename": "LaunchImage@3x.png", + "scale": "3x" } ], - "info" : { - "version" : 1, - "author" : "xcode" + "info": { + "version": 1, + "author": "xcode" } } diff --git a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md index 89c2725b..b5b843ad 100644 --- a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -2,4 +2,4 @@ You can customize the launch screen with your own desired assets by replacing the image files in this directory. -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. diff --git a/game/web/index.html b/game/web/index.html index 0b273656..237724a8 100644 --- a/game/web/index.html +++ b/game/web/index.html @@ -1,6 +1,6 @@ - + - + - + - - - + + + - - - - + + + + game - - - + + + - - + diff --git a/game/web/manifest.json b/game/web/manifest.json index f49858d2..61475e8f 100644 --- a/game/web/manifest.json +++ b/game/web/manifest.json @@ -1,23 +1,23 @@ { - "name": "game", - "short_name": "game", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] + "name": "game", + "short_name": "game", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] } diff --git a/scripts/dbreset.js b/scripts/dbreset.js index 13c424c9..7e1900a0 100644 --- a/scripts/dbreset.js +++ b/scripts/dbreset.js @@ -16,7 +16,7 @@ async function main() { process.env[`DB_RESET`] = "true"; console.log("Setting up database through docker"); execSync( - "docker compose up --build --no-attach postgres --exit-code-from server" + "docker compose up --build --no-attach postgres --exit-code-from server", ); console.log("Successfully reset the database!"); diff --git a/scripts/tests.js b/scripts/tests.js index e78f9c48..a0a45278 100644 --- a/scripts/tests.js +++ b/scripts/tests.js @@ -1,10 +1,18 @@ const { execSync } = require("child_process"); -const { rmSync, existsSync, mkdirSync, readdirSync, lstatSync, copyFileSync } = require("fs"); +const { + rmSync, + existsSync, + mkdirSync, + readdirSync, + lstatSync, + copyFileSync, +} = require("fs"); const path = require("path"); -function copyFolderSync(from, to) { // https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js +function copyFolderSync(from, to) { + // https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js mkdirSync(to); - readdirSync(from).forEach(element => { + readdirSync(from).forEach((element) => { if (lstatSync(path.join(from, element)).isFile()) { copyFileSync(path.join(from, element), path.join(to, element)); } else { @@ -14,7 +22,7 @@ function copyFolderSync(from, to) { // https://stackoverflow.com/questions/13786 } async function main() { - if (process.argv.length < 3 || !(["unit", "e2e"].includes(process.argv[2]))) { + if (process.argv.length < 3 || !["unit", "e2e"].includes(process.argv[2])) { console.log("USAGE: npm run tests -- "); return; } @@ -28,7 +36,9 @@ async function main() { if (testType === "UNIT") { try { console.log("Executing unit tests"); - execSync(`docker compose up --no-deps --build server --exit-code-from server`); + execSync( + `docker compose up --no-deps --build server --exit-code-from server`, + ); console.log("Tests ran successfully!"); } catch (err) { console.log("Test execution failed!"); @@ -50,7 +60,9 @@ async function main() { console.log("Setting up test database"); execSync("npm run dbreset"); console.log("Executing e2e tests"); - execSync(`docker compose up --build --no-attach postgres --exit-code-from server`); + execSync( + `docker compose up --build --no-attach postgres --exit-code-from server`, + ); console.log("Tests ran successfully!"); } catch (err) { console.log("Test execution failed!"); diff --git a/scripts/updateapi-lib/apiscanner.js b/scripts/updateapi-lib/apiscanner.js index 89016e2f..288019f2 100644 --- a/scripts/updateapi-lib/apiscanner.js +++ b/scripts/updateapi-lib/apiscanner.js @@ -3,54 +3,52 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getApiDefinitions = void 0; const ts_morph_1 = require("ts-morph"); function getApiDefinitions() { - const project = new ts_morph_1.Project({}); - project.addSourceFilesAtPaths("server/src/*/*.gateway.ts"); - project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); - project.addSourceFileAtPath("server/src/client/client.service.ts"); - const apiDefs = { - serverEntrypoints: new Map(), - clientEntrypoints: new Map(), - }; - const clientFile = project.getSourceFileOrThrow("client.service.ts"); - const clientApiDef = clientFile.getTypeAliasOrThrow("ClientApiDef"); - for (const prop of clientApiDef.getType().getProperties()) { - const ev = prop.getValueDeclarationOrThrow().getChildAtIndex(0).getText(); - const dto = prop.getValueDeclarationOrThrow().getChildAtIndex(2).getText(); - apiDefs.clientEntrypoints.set(ev, dto); - } - console.log(`Processed ${apiDefs.clientEntrypoints.size} client functions!`); - console.log(); - for (const file of project.getSourceFiles("server/src/*/*.gateway.ts")) { - for (const _class of file.getClasses()) { - const funcs = _class - .getMethods() - .filter((met) => !!met.getDecorator("SubscribeMessage")); - if (!_class.getName()?.includes("Gateway")) - continue; - console.log(`${file.getBaseName()}: ${funcs.length} message functions`); - if (funcs.length === 0) - continue; - for (const func of funcs) { - const ev = func - .getDecoratorOrThrow("SubscribeMessage") - .getArguments()[0] - .asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral) - .getLiteralValue(); - let dto = func - .getParameterOrThrow((param) => !!param.getDecorator("MessageBody")) - .getType() - .getText(); - if (dto.includes(".")) { - dto = dto.split(".").pop(); - } - apiDefs.serverEntrypoints.set(ev, dto); - } - break; + const project = new ts_morph_1.Project({}); + project.addSourceFilesAtPaths("server/src/*/*.gateway.ts"); + project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); + project.addSourceFileAtPath("server/src/client/client.service.ts"); + const apiDefs = { + serverEntrypoints: new Map(), + clientEntrypoints: new Map(), + }; + const clientFile = project.getSourceFileOrThrow("client.service.ts"); + const clientApiDef = clientFile.getTypeAliasOrThrow("ClientApiDef"); + for (const prop of clientApiDef.getType().getProperties()) { + const ev = prop.getValueDeclarationOrThrow().getChildAtIndex(0).getText(); + const dto = prop.getValueDeclarationOrThrow().getChildAtIndex(2).getText(); + apiDefs.clientEntrypoints.set(ev, dto); + } + console.log(`Processed ${apiDefs.clientEntrypoints.size} client functions!`); + console.log(); + for (const file of project.getSourceFiles("server/src/*/*.gateway.ts")) { + for (const _class of file.getClasses()) { + const funcs = _class + .getMethods() + .filter((met) => !!met.getDecorator("SubscribeMessage")); + if (!_class.getName()?.includes("Gateway")) continue; + console.log(`${file.getBaseName()}: ${funcs.length} message functions`); + if (funcs.length === 0) continue; + for (const func of funcs) { + const ev = func + .getDecoratorOrThrow("SubscribeMessage") + .getArguments()[0] + .asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral) + .getLiteralValue(); + let dto = func + .getParameterOrThrow((param) => !!param.getDecorator("MessageBody")) + .getType() + .getText(); + if (dto.includes(".")) { + dto = dto.split(".").pop(); } + apiDefs.serverEntrypoints.set(ev, dto); + } + break; } - console.log(); - console.log(`Processed ${apiDefs.serverEntrypoints.size} server functions!`); - console.log(); - return apiDefs; + } + console.log(); + console.log(`Processed ${apiDefs.serverEntrypoints.size} server functions!`); + console.log(); + return apiDefs; } exports.getApiDefinitions = getApiDefinitions; diff --git a/scripts/updateapi-lib/dartgen.js b/scripts/updateapi-lib/dartgen.js index db1c4f37..fa3c6288 100644 --- a/scripts/updateapi-lib/dartgen.js +++ b/scripts/updateapi-lib/dartgen.js @@ -1,175 +1,172 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getDartServerApiFile = exports.getDartClientApiFile = exports.genDartDtoFile = void 0; +exports.getDartServerApiFile = + exports.getDartClientApiFile = + exports.genDartDtoFile = + void 0; function toDartType(tsType, tsName) { - switch (tsType) { - case "string": - return "String"; - case "number": - return tsName.endsWith("Float") || tsName.endsWith("F") - ? "double" - : "int"; - case "boolean": - return "bool"; - default: - return tsType; - } + switch (tsType) { + case "string": + return "String"; + case "number": + return tsName.endsWith("Float") || tsName.endsWith("F") + ? "double" + : "int"; + case "boolean": + return "bool"; + default: + return tsType; + } } function genDartDtoFile(dtoDefs) { - let dartCode = ` + let dartCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! `; - for (const [name, fields] of dtoDefs.enumDtos.entries()) { - dartCode += `enum ${name} {\n`; - for (const field of fields) { - dartCode += ` ${field},\n`; - } - dartCode += "}\n\n"; + for (const [name, fields] of dtoDefs.enumDtos.entries()) { + dartCode += `enum ${name} {\n`; + for (const field of fields) { + dartCode += ` ${field},\n`; } - for (const [name, propMap] of dtoDefs.baseDtos.entries()) { - dartCode += `class ${name} {\n`; - dartCode += ` Map toJson() {\n`; - dartCode += ` Map fields = {};\n`; - for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { - const dartType = toDartType(typeName, propName); - if (isOptional) { - dartCode += ` if (${propName} != null) {\n `; - } - dartCode += ` fields['${propName}'] = `; - if (fieldType == "PRIMITIVE" || fieldType == "PRIMITIVE[]") { - dartCode += `${propName}`; - } - else if (fieldType == "DEPENDENT_DTO") { - dartCode += `${propName}!.toJson()`; - } - else if (fieldType == "DEPENDENT_DTO[]") { - dartCode += ` + dartCode += "}\n\n"; + } + for (const [name, propMap] of dtoDefs.baseDtos.entries()) { + dartCode += `class ${name} {\n`; + dartCode += ` Map toJson() {\n`; + dartCode += ` Map fields = {};\n`; + for (const [ + propName, + [typeName, fieldType, isOptional], + ] of propMap.entries()) { + const dartType = toDartType(typeName, propName); + if (isOptional) { + dartCode += ` if (${propName} != null) {\n `; + } + dartCode += ` fields['${propName}'] = `; + if (fieldType == "PRIMITIVE" || fieldType == "PRIMITIVE[]") { + dartCode += `${propName}`; + } else if (fieldType == "DEPENDENT_DTO") { + dartCode += `${propName}!.toJson()`; + } else if (fieldType == "DEPENDENT_DTO[]") { + dartCode += ` ${propName}! .map>( (dynamic val) => val!.toJson() ).toList() `; - } - else if (fieldType == "ENUM_DTO") { - dartCode += `${propName}!.name`; - } - else if (fieldType == "ENUM_DTO[]") { - dartCode += ` + } else if (fieldType == "ENUM_DTO") { + dartCode += `${propName}!.name`; + } else if (fieldType == "ENUM_DTO[]") { + dartCode += ` ${propName}! .map( (dynamic val) => val!.name ).toList() `; - } - if (isOptional) { - dartCode += `;\n }\n`; - } - else { - dartCode += ";\n"; - } - } - dartCode += " return fields;\n"; - dartCode += " }\n\n"; - dartCode += ` ${name}.fromJson(Map fields) {\n`; - for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { - const dartType = toDartType(typeName, propName); - dartCode += ` ${propName} = `; - if (isOptional) { - dartCode += `fields.containsKey('${propName}') ? (\n `; - } - if (fieldType == "PRIMITIVE" && dartType == "double") { - dartCode += `fields["${propName}"]!.toDouble()`; - } - else if (fieldType == "PRIMITIVE[]" && dartType == "double") { - dartCode += ` + } + if (isOptional) { + dartCode += `;\n }\n`; + } else { + dartCode += ";\n"; + } + } + dartCode += " return fields;\n"; + dartCode += " }\n\n"; + dartCode += ` ${name}.fromJson(Map fields) {\n`; + for (const [ + propName, + [typeName, fieldType, isOptional], + ] of propMap.entries()) { + const dartType = toDartType(typeName, propName); + dartCode += ` ${propName} = `; + if (isOptional) { + dartCode += `fields.containsKey('${propName}') ? (\n `; + } + if (fieldType == "PRIMITIVE" && dartType == "double") { + dartCode += `fields["${propName}"]!.toDouble()`; + } else if (fieldType == "PRIMITIVE[]" && dartType == "double") { + dartCode += ` fields["${propName}"]! .map<${dartType}>( (dynamic val) => val!.toDouble() ).toList() `; - } - else if (fieldType == "PRIMITIVE") { - dartCode += `fields["${propName}"]`; - } - else if (fieldType == "PRIMITIVE[]") { - dartCode += `List<${dartType}>.from(fields['${propName}'])`; - } - else if (fieldType == "DEPENDENT_DTO") { - dartCode += `${dartType}.fromJson(fields['${propName}'])`; - } - else if (fieldType == "DEPENDENT_DTO[]") { - dartCode += ` + } else if (fieldType == "PRIMITIVE") { + dartCode += `fields["${propName}"]`; + } else if (fieldType == "PRIMITIVE[]") { + dartCode += `List<${dartType}>.from(fields['${propName}'])`; + } else if (fieldType == "DEPENDENT_DTO") { + dartCode += `${dartType}.fromJson(fields['${propName}'])`; + } else if (fieldType == "DEPENDENT_DTO[]") { + dartCode += ` fields["${propName}"] .map<${dartType}>( (dynamic val) => ${dartType}.fromJson(val) ).toList() `; - } - else if (fieldType == "ENUM_DTO") { - dartCode += `${dartType}.values.byName(fields['${propName}'])`; - } - else if (fieldType == "ENUM_DTO[]") { - dartCode += ` + } else if (fieldType == "ENUM_DTO") { + dartCode += `${dartType}.values.byName(fields['${propName}'])`; + } else if (fieldType == "ENUM_DTO[]") { + dartCode += ` fields["${propName}"] .map<${dartType}>( (dynamic val) => ${dartType}.values.byName(val) ).toList() `; - } - if (isOptional) { - dartCode += `\n ) : null;\n`; - } - else { - dartCode += ";\n"; - } - } - dartCode += " }\n\n"; - dartCode += ` void partialUpdate(${name} other) {`; - for (const [propName, [_, __, isOptional]] of propMap.entries()) { - if (isOptional) { - dartCode += ` ${propName} = other.${propName} == null ? ${propName} : other.${propName};\n`; - } - else { - dartCode += ` ${propName} = other.${propName};\n`; - } - } - dartCode += " }\n\n"; - if (propMap.size > 0) { - dartCode += ` ${name}({`; - for (const [propName, [_, __, isOptional]] of propMap.entries()) { - if (isOptional) { - dartCode += `\nthis.${propName}, `; - } - else { - dartCode += `\nrequired this.${propName}, `; - } - } - dartCode += " });\n\n"; - } - else { - dartCode += ` ${name}();`; - } - for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { - const dartType = toDartType(typeName, propName); - const isArray = fieldType == "ENUM_DTO[]" || - fieldType == "DEPENDENT_DTO[]" || - fieldType == "PRIMITIVE[]"; - const fullType = isArray ? `List<${dartType}>` : dartType; - dartCode += ` late ${fullType}`; - if (isOptional) { - dartCode += "?"; - } - dartCode += ` ${propName};\n`; + } + if (isOptional) { + dartCode += `\n ) : null;\n`; + } else { + dartCode += ";\n"; + } + } + dartCode += " }\n\n"; + dartCode += ` void partialUpdate(${name} other) {`; + for (const [propName, [_, __, isOptional]] of propMap.entries()) { + if (isOptional) { + dartCode += ` ${propName} = other.${propName} == null ? ${propName} : other.${propName};\n`; + } else { + dartCode += ` ${propName} = other.${propName};\n`; + } + } + dartCode += " }\n\n"; + if (propMap.size > 0) { + dartCode += ` ${name}({`; + for (const [propName, [_, __, isOptional]] of propMap.entries()) { + if (isOptional) { + dartCode += `\nthis.${propName}, `; + } else { + dartCode += `\nrequired this.${propName}, `; } - dartCode += "}\n\n"; + } + dartCode += " });\n\n"; + } else { + dartCode += ` ${name}();`; } - return dartCode; + for (const [ + propName, + [typeName, fieldType, isOptional], + ] of propMap.entries()) { + const dartType = toDartType(typeName, propName); + const isArray = + fieldType == "ENUM_DTO[]" || + fieldType == "DEPENDENT_DTO[]" || + fieldType == "PRIMITIVE[]"; + const fullType = isArray ? `List<${dartType}>` : dartType; + dartCode += ` late ${fullType}`; + if (isOptional) { + dartCode += "?"; + } + dartCode += ` ${propName};\n`; + } + dartCode += "}\n\n"; + } + return dartCode; } exports.genDartDtoFile = genDartDtoFile; function getDartClientApiFile(apiDefs) { - let dartCode = ` + let dartCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! @@ -181,16 +178,16 @@ function getDartClientApiFile(apiDefs) { class GameClientApi { `; - for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { - dartCode += ` + for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { + dartCode += ` final _${ev}Controller = StreamController<${dto}>.broadcast(sync: true); Stream<${dto}> get ${ev}Stream => _${ev}Controller.stream; `; - } - dartCode += ` + } + dartCode += ` final _reconnectedController = StreamController.broadcast(sync: true); Stream get reconnectedStream => _reconnectedController.stream; @@ -209,27 +206,27 @@ function getDartClientApiFile(apiDefs) { sock.onDisconnect((data) => disconnectedController.add(null)); `; - for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { - dartCode += ` + for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { + dartCode += ` sock.on( "${ev}", (data) => _${ev}Controller.add(${dto}.fromJson(data))); `; - } - dartCode += ` + } + dartCode += ` _connectedController.add(null); } GameClientApi() {} } `; - return dartCode; + return dartCode; } exports.getDartClientApiFile = getDartClientApiFile; function getDartServerApiFile(apiDefs) { - let dartCode = ` + let dartCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! @@ -272,14 +269,14 @@ function getDartServerApiFile(apiDefs) { _socket.emit(ev, data); } `; - for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { - dartCode += ` + for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + dartCode += ` void ${ev}(${dto} dto) => _invokeWithRefresh( "${ev}", dto.toJson()); `; - } - dartCode += "}"; - return dartCode; + } + dartCode += "}"; + return dartCode; } exports.getDartServerApiFile = getDartServerApiFile; diff --git a/scripts/updateapi-lib/dtoscanner.js b/scripts/updateapi-lib/dtoscanner.js index 33e3cebc..dc31b812 100644 --- a/scripts/updateapi-lib/dtoscanner.js +++ b/scripts/updateapi-lib/dtoscanner.js @@ -3,95 +3,106 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getDtoDefinitions = void 0; const ts_morph_1 = require("ts-morph"); function getDtoDefinitions() { - const project = new ts_morph_1.Project({}); - project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); - console.log(`Discovered ${project.getSourceFiles().length} DTO files!`); - console.log(); - const enumDtos = new Map(); - const baseDtos = new Map(); - for (const file of project.getSourceFiles()) { - const interfs = file.getInterfaces(); - const enums = file.getEnums(); - console.log(`${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs`); - for (const enum_ of enums) { - const vals = enum_.getMembers().map((val) => val.getName()); - enumDtos.set(enum_.getName(), vals); - } - for (const interf of interfs) { - const props = interf.getProperties(); - const baseDto = new Map(); - const interfName = interf.getName(); - baseDtos.set(interfName, baseDto); - for (const prop of props) { - const propType = prop.getType(); - const propName = prop.getName(); - const isOptional = prop.hasQuestionToken(); - if (propType.isString() || propType.getArrayElementType()?.isString()) { - // str - baseDto.set(propName, [ - "string", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } - else if (propType.isNumber() || - propType.getArrayElementType()?.isNumber()) { - // num - baseDto.set(propName, [ - "number", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } - else if (propType.isBoolean() || - propType.getArrayElementType()?.isBoolean()) { - // num - baseDto.set(propName, [ - "boolean", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } - else if (propType.isUnion() || - propType.getArrayElementType()?.isUnion()) { - // enum - const enumName = interfName.replace("Dto", "") + - propName[0].toUpperCase() + - propName.substring(1) + - "Dto"; - enumDtos.set(enumName, propType - .getUnionTypes() - .map((t) => t.getLiteralValue()?.toString() ?? "")); - baseDto.set(propName, [ - enumName, - propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", - isOptional, - ]); - } - else if (propType.isInterface() || - propType.getArrayElementType()?.isInterface() || - propType.isEnum() || - propType.getArrayElementType()?.isEnum()) { - let name = propType.isArray() - ? propType.getArrayElementTypeOrThrow().getText() - : propType.getText(); - const isEnum = propType.isEnum() || propType.getArrayElementType()?.isEnum(); - if (name.includes(".")) { - name = name.split(".").pop(); - } - const fieldType = propType.isArray() - ? isEnum - ? "ENUM_DTO[]" - : "DEPENDENT_DTO[]" - : isEnum - ? "ENUM_DTO" - : "DEPENDENT_DTO"; - baseDto.set(propName, [name, fieldType, isOptional]); - } - } + const project = new ts_morph_1.Project({}); + project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); + console.log(`Discovered ${project.getSourceFiles().length} DTO files!`); + console.log(); + const enumDtos = new Map(); + const baseDtos = new Map(); + for (const file of project.getSourceFiles()) { + const interfs = file.getInterfaces(); + const enums = file.getEnums(); + console.log( + `${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs`, + ); + for (const enum_ of enums) { + const vals = enum_.getMembers().map((val) => val.getName()); + enumDtos.set(enum_.getName(), vals); + } + for (const interf of interfs) { + const props = interf.getProperties(); + const baseDto = new Map(); + const interfName = interf.getName(); + baseDtos.set(interfName, baseDto); + for (const prop of props) { + const propType = prop.getType(); + const propName = prop.getName(); + const isOptional = prop.hasQuestionToken(); + if (propType.isString() || propType.getArrayElementType()?.isString()) { + // str + baseDto.set(propName, [ + "string", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } else if ( + propType.isNumber() || + propType.getArrayElementType()?.isNumber() + ) { + // num + baseDto.set(propName, [ + "number", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } else if ( + propType.isBoolean() || + propType.getArrayElementType()?.isBoolean() + ) { + // num + baseDto.set(propName, [ + "boolean", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } else if ( + propType.isUnion() || + propType.getArrayElementType()?.isUnion() + ) { + // enum + const enumName = + interfName.replace("Dto", "") + + propName[0].toUpperCase() + + propName.substring(1) + + "Dto"; + enumDtos.set( + enumName, + propType + .getUnionTypes() + .map((t) => t.getLiteralValue()?.toString() ?? ""), + ); + baseDto.set(propName, [ + enumName, + propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", + isOptional, + ]); + } else if ( + propType.isInterface() || + propType.getArrayElementType()?.isInterface() || + propType.isEnum() || + propType.getArrayElementType()?.isEnum() + ) { + let name = propType.isArray() + ? propType.getArrayElementTypeOrThrow().getText() + : propType.getText(); + const isEnum = + propType.isEnum() || propType.getArrayElementType()?.isEnum(); + if (name.includes(".")) { + name = name.split(".").pop(); + } + const fieldType = propType.isArray() + ? isEnum + ? "ENUM_DTO[]" + : "DEPENDENT_DTO[]" + : isEnum + ? "ENUM_DTO" + : "DEPENDENT_DTO"; + baseDto.set(propName, [name, fieldType, isOptional]); } + } } - console.log(); - return { enumDtos, baseDtos }; + } + console.log(); + return { enumDtos, baseDtos }; } exports.getDtoDefinitions = getDtoDefinitions; diff --git a/scripts/updateapi-lib/dtoscanner.ts b/scripts/updateapi-lib/dtoscanner.ts index b7a8293c..3f54960f 100644 --- a/scripts/updateapi-lib/dtoscanner.ts +++ b/scripts/updateapi-lib/dtoscanner.ts @@ -17,7 +17,7 @@ export function getDtoDefinitions(): DtoDefs { const enums = file.getEnums(); console.log( - `${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs` + `${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs`, ); for (const enum_ of enums) { @@ -78,7 +78,7 @@ export function getDtoDefinitions(): DtoDefs { enumName, propType .getUnionTypes() - .map((t) => t.getLiteralValue()?.toString() ?? "") + .map((t) => t.getLiteralValue()?.toString() ?? ""), ); baseDto.set(propName, [ @@ -108,8 +108,8 @@ export function getDtoDefinitions(): DtoDefs { ? "ENUM_DTO[]" : "DEPENDENT_DTO[]" : isEnum - ? "ENUM_DTO" - : "DEPENDENT_DTO"; + ? "ENUM_DTO" + : "DEPENDENT_DTO"; baseDto.set(propName, [name, fieldType, isOptional]); } diff --git a/scripts/updateapi-lib/tsgen.js b/scripts/updateapi-lib/tsgen.js index 6aebdd42..3f6c717d 100644 --- a/scripts/updateapi-lib/tsgen.js +++ b/scripts/updateapi-lib/tsgen.js @@ -2,38 +2,42 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getAdminApiFile = exports.genTsDtoFile = void 0; function genTsDtoFile(dtoDefs) { - let tsCode = ` + let tsCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! `; - for (const [name, fields] of dtoDefs.enumDtos.entries()) { - tsCode += `export enum ${name} {`; - for (const field of fields) { - tsCode += `\n ${field} = "${field}",`; - } - tsCode += "}\n\n"; + for (const [name, fields] of dtoDefs.enumDtos.entries()) { + tsCode += `export enum ${name} {`; + for (const field of fields) { + tsCode += `\n ${field} = "${field}",`; } - for (const [name, propMap] of dtoDefs.baseDtos.entries()) { - tsCode += `export interface ${name} {\n`; - for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { - const isArray = fieldType == "ENUM_DTO[]" || - fieldType == "DEPENDENT_DTO[]" || - fieldType == "PRIMITIVE[]"; - const fullType = isArray ? `${typeName}[]` : typeName; - tsCode += ` ${propName}`; - if (isOptional) { - tsCode += "?"; - } - tsCode += `: ${fullType};\n`; - } - tsCode += "}\n\n"; + tsCode += "}\n\n"; + } + for (const [name, propMap] of dtoDefs.baseDtos.entries()) { + tsCode += `export interface ${name} {\n`; + for (const [ + propName, + [typeName, fieldType, isOptional], + ] of propMap.entries()) { + const isArray = + fieldType == "ENUM_DTO[]" || + fieldType == "DEPENDENT_DTO[]" || + fieldType == "PRIMITIVE[]"; + const fullType = isArray ? `${typeName}[]` : typeName; + tsCode += ` ${propName}`; + if (isOptional) { + tsCode += "?"; + } + tsCode += `: ${fullType};\n`; } - return tsCode; + tsCode += "}\n\n"; + } + return tsCode; } exports.genTsDtoFile = genTsDtoFile; function getAdminApiFile(apiDefs) { - let tsCode = ` + let tsCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! @@ -50,25 +54,25 @@ function getAdminApiFile(apiDefs) { } `; - for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { - tsCode += ` + for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + tsCode += ` ${ev}(data: dto.${dto}) { this.send("${ev}", data); } `; - } - for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { - const formattedName = ev[0].toUpperCase() + ev.substring(1); - tsCode += ` + } + for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { + const formattedName = ev[0].toUpperCase() + ev.substring(1); + tsCode += ` on${formattedName}(callback: (data: dto.${dto}) => void) { this.socket.removeAllListeners("${ev}"); this.socket.on("${ev}", (data) => callback(data)); } `; - } - tsCode += "}"; - return tsCode; + } + tsCode += "}"; + return tsCode; } exports.getAdminApiFile = getAdminApiFile; diff --git a/scripts/updateapi.js b/scripts/updateapi.js index a6e6cf15..7aa117ec 100644 --- a/scripts/updateapi.js +++ b/scripts/updateapi.js @@ -19,16 +19,25 @@ const tsDtoCode = (0, tsgen_1.genTsDtoFile)(dtos); const tsApiCode = (0, tsgen_1.getAdminApiFile)(apis); const dartClientApiCode = (0, dartgen_1.getDartClientApiFile)(apis); const dartServerApiCode = (0, dartgen_1.getDartServerApiFile)(apis); -(0, fs_1.writeFileSync)(flutterDtoPath, dartDtoCode, { encoding: "utf8", flag: "w" }); -(0, fs_1.writeFileSync)(adminDtoPath, tsDtoCode, { encoding: "utf8", flag: "w" }); -(0, fs_1.writeFileSync)(adminApiPath, tsApiCode, { encoding: "utf8", flag: "w" }); +(0, fs_1.writeFileSync)(flutterDtoPath, dartDtoCode, { + encoding: "utf8", + flag: "w", +}); +(0, fs_1.writeFileSync)(adminDtoPath, tsDtoCode, { + encoding: "utf8", + flag: "w", +}); +(0, fs_1.writeFileSync)(adminApiPath, tsApiCode, { + encoding: "utf8", + flag: "w", +}); (0, fs_1.writeFileSync)(flutterClientApiPath, dartClientApiCode, { - encoding: "utf8", - flag: "w", + encoding: "utf8", + flag: "w", }); (0, fs_1.writeFileSync)(flutterServerApiPath, dartServerApiCode, { - encoding: "utf8", - flag: "w", + encoding: "utf8", + flag: "w", }); (0, process_1.chdir)("./game"); (0, child_process_1.execSync)("dart format lib"); diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index ddb5c456..3c4b0fe1 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -195,5 +195,4 @@ export class AchievementService { achievementType: ach.achievementType as AchievementTypeDto, }; } - } diff --git a/server/src/challenge/challenge.gateway.ts b/server/src/challenge/challenge.gateway.ts index f73a1953..30f767de 100644 --- a/server/src/challenge/challenge.gateway.ts +++ b/server/src/challenge/challenge.gateway.ts @@ -90,9 +90,8 @@ export class ChallengeGateway { ) { if (await this.challengeService.completeChallenge(user, data.challengeId)) { const group = await this.groupService.getGroupForUser(user); - const tracker = await this.eventService.getCurrentEventTrackerForUser( - user, - ); + const tracker = + await this.eventService.getCurrentEventTrackerForUser(user); await this.groupService.emitUpdateGroupData(group, false); await this.eventService.emitUpdateEventTracker(tracker); diff --git a/server/src/event/event.e2e-spec.ts b/server/src/event/event.e2e-spec.ts index b0c01acd..2d790464 100644 --- a/server/src/event/event.e2e-spec.ts +++ b/server/src/event/event.e2e-spec.ts @@ -114,9 +114,8 @@ describe('EventModule E2E', () => { describe('Playthrough testing', () => { it('Should successfully complete a playthrough', async () => { await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney1.id }); - const tracker1 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker1 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker1.eventId).toEqual(exJourney1.id); expect(tracker1.score).toEqual(0); expect( @@ -132,16 +131,14 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker2 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker2 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker2.curChallengeId).toEqual(journey1Chal.id); expect(tracker2.score).toEqual(1); await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney2.id }); - const tracker3 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker3 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker3.eventId).toEqual(exJourney2.id); expect(tracker3.score).toEqual(0); expect( @@ -163,9 +160,8 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker4 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker4 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker4.curChallengeId).toEqual(journey2Chal.id); expect(tracker4.score).toEqual(1); expect( @@ -181,9 +177,8 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker5 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker5 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker5.curChallengeId).toEqual(journey2Chal.id); expect(tracker5.score).toEqual(2); @@ -191,9 +186,8 @@ describe('EventModule E2E', () => { eventId: exChallenge1.id, }); - const tracker6 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker6 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker6.eventId).toEqual(exChallenge1.id); expect(tracker6.score).toEqual(0); expect( @@ -209,17 +203,15 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker7 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker7 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker7.curChallengeId).toEqual(tracker6.curChallengeId); expect(tracker7.score).toEqual(1); await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney1.id }); - const tracker8 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker8 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker8.eventId).toEqual(exJourney1.id); expect(tracker8.curChallengeId).toEqual(journey1Chal.id); expect(tracker8.score).toEqual(1); @@ -236,9 +228,8 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker9 = await eventService.getCurrentEventTrackerForUser( - exPlayer, - ); + const tracker9 = + await eventService.getCurrentEventTrackerForUser(exPlayer); expect(tracker9.eventId).toEqual(exJourney1.id); expect(tracker9.curChallengeId).toEqual(journey1Chal.id); expect(tracker9.score).toEqual(2); diff --git a/server/src/event/event.service.ts b/server/src/event/event.service.ts index 3ec5d1ab..2ebbf506 100644 --- a/server/src/event/event.service.ts +++ b/server/src/event/event.service.ts @@ -226,8 +226,8 @@ export class EventService { user.id } = p."B") order by ((ev."latitude" - ${data.latitudeF})^2 + (ev."longitude" - ${ - data.longitudeF - })^2) + data.longitudeF + })^2) fetch first ${data.count ?? 4} rows only `; return evs; @@ -273,8 +273,8 @@ export class EventService { ev.difficulty === DifficultyMode.EASY ? 'Easy' : ev.difficulty === DifficultyMode.NORMAL - ? 'Normal' - : 'Hard', + ? 'Normal' + : 'Hard', latitudeF: ev.latitude, longitudeF: ev.longitude, }; @@ -437,8 +437,8 @@ export class EventService { (event.difficulty === 'Easy' ? DifficultyMode.EASY : event.difficulty === 'Normal' - ? DifficultyMode.NORMAL - : DifficultyMode.HARD), + ? DifficultyMode.NORMAL + : DifficultyMode.HARD), latitude: event.latitudeF, longitude: event.longitudeF, category: event.category, diff --git a/server/src/group/group.service.ts b/server/src/group/group.service.ts index 43b2b05c..d9245d7c 100644 --- a/server/src/group/group.service.ts +++ b/server/src/group/group.service.ts @@ -281,9 +281,8 @@ export class GroupService { const memberDtos = await Promise.all( members.map(async mem => { - const tracker = await this.eventService.getCurrentEventTrackerForUser( - mem, - ); + const tracker = + await this.eventService.getCurrentEventTrackerForUser(mem); return { id: mem.id, name: mem.username, diff --git a/server/src/organization/organization.e2e-spec.ts b/server/src/organization/organization.e2e-spec.ts index 260605f6..64b60433 100644 --- a/server/src/organization/organization.e2e-spec.ts +++ b/server/src/organization/organization.e2e-spec.ts @@ -139,15 +139,12 @@ describe('OrganizationModule E2E', () => { ); defaultEv = (await eventService.getEventById( - ( - await eventService.getCurrentEventTrackerForUser(basicUser) - ).eventId, + (await eventService.getCurrentEventTrackerForUser(basicUser)).eventId, ))!; defaultChal = (await challengeService.getChallengeById( - ( - await eventService.getCurrentEventTrackerForUser(basicUser) - ).curChallengeId, + (await eventService.getCurrentEventTrackerForUser(basicUser)) + .curChallengeId, ))!; managerGroup = await groupService.getGroupForUser(managerUser); From 9329f18e725e003cde9ef6c29e540ec7769038a7 Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Mon, 22 Apr 2024 15:03:14 -0400 Subject: [PATCH 13/17] Revert "Fixed code style across entire codebase" This reverts commit bc318b84c8bf668618fd3fa284cc1938a2ce8312. --- .github/PULL_REQUEST_TEMPLATE.md | 3 +- .vs/ProjectSettings.json | 2 +- .vs/VSWorkspaceState.json | 9 +- .vscode/launch.json | 2 +- .vscode/settings.json | 6 +- admin/public/index.html | 2 +- admin/src/components/Challenges.tsx | 16 +- admin/src/components/EntryModal.tsx | 6 +- admin/src/components/Events.tsx | 12 +- admin/src/components/Groups.tsx | 6 +- admin/src/components/Modal.tsx | 9 +- admin/src/components/Organizations.tsx | 6 +- admin/src/components/ServerApi.tsx | 6 +- admin/src/components/ServerData.tsx | 18 +- admin/src/components/Users.tsx | 4 +- admin/src/index.css | 8 +- admin/src/index.tsx | 2 +- admin/src/post.ts | 2 +- admin/tsconfig.json | 12 +- game/analysis_options.yaml | 1 + game/android/app/google-services.json | 2 +- .../AppIcon.appiconset/Contents.json | 160 +++++----- .../LaunchImage.imageset/Contents.json | 26 +- .../LaunchImage.imageset/README.md | 2 +- game/web/index.html | 39 +-- game/web/manifest.json | 42 +-- scripts/dbreset.js | 2 +- scripts/tests.js | 24 +- scripts/updateapi-lib/apiscanner.js | 92 +++--- scripts/updateapi-lib/dartgen.js | 291 +++++++++--------- scripts/updateapi-lib/dtoscanner.js | 187 ++++++----- scripts/updateapi-lib/dtoscanner.ts | 8 +- scripts/updateapi-lib/tsgen.js | 68 ++-- scripts/updateapi.js | 23 +- server/src/achievement/achievement.service.ts | 1 + server/src/challenge/challenge.gateway.ts | 5 +- server/src/event/event.e2e-spec.ts | 45 +-- server/src/event/event.service.ts | 12 +- server/src/group/group.service.ts | 5 +- .../src/organization/organization.e2e-spec.ts | 9 +- 40 files changed, 588 insertions(+), 587 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7a2d4b5d..db6101c9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,6 +21,7 @@ Remaining TODOs: Depends on #{number of PR} + ### Test Plan @@ -35,7 +36,7 @@ Depends on #{number of PR} - A newly discovered dependency that hasn’t been addressed -### Breaking Changes +### Breaking Changes diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json index ba2c8694..0cf5ea50 100644 --- a/.vs/ProjectSettings.json +++ b/.vs/ProjectSettings.json @@ -1,3 +1,3 @@ { "CurrentProjectSetting": "No Configurations" -} +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json index 1bdf5932..246622a9 100644 --- a/.vs/VSWorkspaceState.json +++ b/.vs/VSWorkspaceState.json @@ -1,5 +1,10 @@ { - "ExpandedNodes": ["", "\\game", "\\game\\lib", "\\game\\test"], + "ExpandedNodes": [ + "", + "\\game", + "\\game\\lib", + "\\game\\test" + ], "SelectedNode": "\\game\\lib\\main.dart", "PreviewInSolutionExplorer": false -} +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 16907b59..12bff38c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -25,4 +25,4 @@ "flutterMode": "release" } ] -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 59186479..7498308f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { "typescript.preferences.importModuleSpecifier": "relative", - "githubPullRequests.ignoredPullRequestBranches": ["master"], + "githubPullRequests.ignoredPullRequestBranches": [ + "master" + ], "typescript.tsdk": "node_modules/typescript/lib" -} +} \ No newline at end of file diff --git a/admin/public/index.html b/admin/public/index.html index 75feb39a..81fb533f 100644 --- a/admin/public/index.html +++ b/admin/public/index.html @@ -1,4 +1,4 @@ - + diff --git a/admin/src/components/Challenges.tsx b/admin/src/components/Challenges.tsx index 490a236e..0b2d9728 100644 --- a/admin/src/components/Challenges.tsx +++ b/admin/src/components/Challenges.tsx @@ -151,7 +151,7 @@ function toForm(challenge: ChallengeDto) { function fromForm( form: EntryForm[], eventId: string, - id: string, + id: string ): ChallengeDto { return { id, @@ -194,7 +194,7 @@ export function Challenges() { entryButtonText="CREATE" onEntry={() => { serverData.updateChallenge( - fromForm(form, serverData.selectedEvent, ""), + fromForm(form, serverData.selectedEvent, "") ); setCreateModalOpen(false); }} @@ -209,7 +209,7 @@ export function Challenges() { entryButtonText="EDIT" onEntry={() => { serverData.updateChallenge( - fromForm(form, serverData.selectedEvent, currentId), + fromForm(form, serverData.selectedEvent, currentId) ); setEditModalOpen(false); }} @@ -255,7 +255,7 @@ export function Challenges() { : compareTwoStrings(b.name ?? "", query) - compareTwoStrings(a.name ?? "", query) + compareTwoStrings(b.description ?? "", query) - - compareTwoStrings(a.description ?? "", query), + compareTwoStrings(a.description ?? "", query) ) .map((chal: ChallengeDto) => ( id === chal.id, - ) ?? 0, + (id: string) => id === chal.id + ) ?? 0 ); serverData.updateEvent(selectedEvent); }} @@ -276,8 +276,8 @@ export function Challenges() { selectedEvent.challenges = moveDown( selectedEvent.challenges, selectedEvent.challenges.findIndex( - (id: string) => id === chal.id, - ), + (id: string) => id === chal.id + ) ); serverData.updateEvent(selectedEvent); }} diff --git a/admin/src/components/EntryModal.tsx b/admin/src/components/EntryModal.tsx index 9aecb949..c0510bc2 100644 --- a/admin/src/components/EntryModal.tsx +++ b/admin/src/components/EntryModal.tsx @@ -75,7 +75,7 @@ function OptionEntryFormBox(props: { form: OptionEntryForm }) { value={val} onChange={(e) => setVal( - props.form.options[(props.form.value = e.target.selectedIndex)], + props.form.options[(props.form.value = e.target.selectedIndex)] ) } > @@ -111,7 +111,7 @@ function DateEntryFormBox(props: { form: DateEntryForm }) { useEffect( () => setVal(props.form.date.toISOString().slice(0, -1)), - [props.form], + [props.form] ); return ( @@ -175,7 +175,7 @@ function DraggableMarker(props: { } }, }), - [], + [] ); return ( diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index dc856527..91c41d97 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -57,8 +57,8 @@ function EventCard(props: { props.event.difficulty === "Easy" ? "Easy" : props.event.difficulty === "Normal" - ? "Normal" - : "Hard"; + ? "Normal" + : "Hard"; let categoryInput = props.event.category as string; const categoryType = @@ -154,8 +154,8 @@ function fromForm(form: EntryForm[], id: string): EventDto { (form[5] as OptionEntryForm).value === 0 ? EventDifficultyDto.Easy : (form[5] as OptionEntryForm).value === 1 - ? EventDifficultyDto.Normal - : EventDifficultyDto.Hard, + ? EventDifficultyDto.Normal + : EventDifficultyDto.Hard, latitudeF: 0, longitudeF: 0, @@ -284,14 +284,14 @@ export function Events() { serverData.organizations .get(serverData.selectedOrg) ?.events?.map((evId: string) => serverData.events.get(evId)!) - .filter((ev?: EventDto) => !!ev) ?? [], + .filter((ev?: EventDto) => !!ev) ?? [] ) .sort( (a: EventDto, b: EventDto) => compareTwoStrings(b.name ?? "", query) - compareTwoStrings(a.name ?? "", query) + compareTwoStrings(b.description ?? "", query) - - compareTwoStrings(a.description ?? "", query), + compareTwoStrings(a.description ?? "", query) ) .map((ev) => ( r.id === data.id, + (r) => r.id === data.id ); rowsClone.splice(deletedRowIndex, 1); const newData = Array.from(serverData.groups.values()).map((gr) => - toForm(gr), + toForm(gr) ); newData.forEach((gr) => { let index = rowsClone.findIndex((r) => r.id === gr.id); @@ -185,7 +185,7 @@ function getColumns(setRowsData: any, serverData: any) { export function Groups() { const serverData = useContext(ServerDataContext); const [rowsData, setRowsData] = useState( - Array.from(serverData.groups.values()).map((gr) => toForm(gr)), + Array.from(serverData.groups.values()).map((gr) => toForm(gr)) ); return ( diff --git a/admin/src/components/Modal.tsx b/admin/src/components/Modal.tsx index 745b28bc..8a281070 100644 --- a/admin/src/components/Modal.tsx +++ b/admin/src/components/Modal.tsx @@ -14,9 +14,10 @@ const ModalBackground = styled.div<{ opacity: number }>` transition: opacity 0.15s; overflow-y: auto; - ${(props) => css` - opacity: ${props.opacity}; - `} + ${(props) => + css` + opacity: ${props.opacity}; + `} `; const ModalLayout = styled.div` @@ -96,7 +97,7 @@ export function Modal(props: { , - document.getElementById("modal-root")!, + document.getElementById("modal-root")! ); } return <>; diff --git a/admin/src/components/Organizations.tsx b/admin/src/components/Organizations.tsx index e25cef62..d0477b27 100644 --- a/admin/src/components/Organizations.tsx +++ b/admin/src/components/Organizations.tsx @@ -76,7 +76,7 @@ function makeForm() { function fromForm( form: EntryForm[], id: string, - oldDto: OrganizationDto, + oldDto: OrganizationDto ): OrganizationDto { return { ...oldDto, @@ -176,7 +176,7 @@ export function Organizations() { .sort( (a, b) => compareTwoStrings(b.name ?? "", query) - - compareTwoStrings(a.name ?? "", query), + compareTwoStrings(a.name ?? "", query) ) .map((org) => ( void, + callback: (data: dto.UpdateAchievementDataDto) => void ) { this.socket.removeAllListeners("updateAchievementData"); this.socket.on("updateAchievementData", (data) => callback(data)); } onUpdateAchievementTrackerData( - callback: (data: dto.AchievementTrackerDto) => void, + callback: (data: dto.AchievementTrackerDto) => void ) { this.socket.removeAllListeners("updateAchievementTrackerData"); this.socket.on("updateAchievementTrackerData", (data) => callback(data)); @@ -184,7 +184,7 @@ export class ServerApi { } onUpdateOrganizationData( - callback: (data: dto.UpdateOrganizationDataDto) => void, + callback: (data: dto.UpdateOrganizationDataDto) => void ) { this.socket.removeAllListeners("updateOrganizationData"); this.socket.on("updateOrganizationData", (data) => callback(data)); diff --git a/admin/src/components/ServerData.tsx b/admin/src/components/ServerData.tsx index 53fee1c5..e17a123c 100644 --- a/admin/src/components/ServerData.tsx +++ b/admin/src/components/ServerData.tsx @@ -52,7 +52,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { const sock = useMemo( () => new ServerApi(connection.connection!), - [connection], + [connection] ); const [serverData, setServerData] = useState(() => ({ ...defaultData })); @@ -118,7 +118,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { sock.updateOrganizationData({ organization: { id }, deleted: true }); }, }), - [serverData, setServerData, sock], + [serverData, setServerData, sock] ); useEffect(() => { @@ -142,13 +142,13 @@ export function ServerDataProvider(props: { children: ReactNode }) { sock.requestChallengeData({ challenges: (data.event as EventDto).challenges?.filter( - (chal: string) => !(chal in oldChallenges), + (chal: string) => !(chal in oldChallenges) ) ?? [], }); serverData.events.set( (data.event as EventDto).id, - data.event as EventDto, + data.event as EventDto ); } @@ -160,7 +160,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { } else { serverData.challenges.set( (data.challenge as ChallengeDto).id, - data.challenge as ChallengeDto, + data.challenge as ChallengeDto ); } @@ -181,7 +181,7 @@ export function ServerDataProvider(props: { children: ReactNode }) { } else { serverData.groups.set( (data.group as GroupDto).id, - data.group as GroupDto, + data.group as GroupDto ); } @@ -193,18 +193,18 @@ export function ServerDataProvider(props: { children: ReactNode }) { } else { const oldEvents = serverData.organizations.get( - (data.organization as OrganizationDto).id, + (data.organization as OrganizationDto).id )?.events ?? []; sock.requestEventData({ events: (data.organization as OrganizationDto).events?.filter( - (ev: string) => !(ev in oldEvents), + (ev: string) => !(ev in oldEvents) ), }); serverData.organizations.set( (data.organization as OrganizationDto).id, - data.organization as OrganizationDto, + data.organization as OrganizationDto ); } diff --git a/admin/src/components/Users.tsx b/admin/src/components/Users.tsx index 91566f1e..13590d64 100644 --- a/admin/src/components/Users.tsx +++ b/admin/src/components/Users.tsx @@ -191,7 +191,7 @@ function getColumns(setRowsData: any, serverData: any) { let rowsClone = [...tableManager.rowsApi.rows]; let updatedRowIndex = rowsClone.findIndex( - (r) => r.id === data.id, + (r) => r.id === data.id ); rowsClone[updatedRowIndex] = data; @@ -213,7 +213,7 @@ function getColumns(setRowsData: any, serverData: any) { export function Users() { const serverData = useContext(ServerDataContext); const [rowsData, setRowsData] = useState( - Array.from(serverData.users.values()).map((us) => toForm(us)), + Array.from(serverData.users.values()).map((us) => toForm(us)) ); return ( diff --git a/admin/src/index.css b/admin/src/index.css index 69e48f23..3d5577e1 100644 --- a/admin/src/index.css +++ b/admin/src/index.css @@ -20,8 +20,8 @@ dd { } /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */ -ul[role="list"], -ol[role="list"] { +ul[role='list'], +ol[role='list'] { list-style: none; } @@ -60,9 +60,9 @@ select { /* Remove all animations, transitions and smooth scroll for people that prefer not to see them */ @media (prefers-reduced-motion: reduce) { html:focus-within { - scroll-behavior: auto; + scroll-behavior: auto; } - + *, *::before, *::after { diff --git a/admin/src/index.tsx b/admin/src/index.tsx index bd9e0d62..21decb92 100644 --- a/admin/src/index.tsx +++ b/admin/src/index.tsx @@ -23,7 +23,7 @@ ReactDOM.render( , - document.getElementById("root"), + document.getElementById("root") ); // If you want to start measuring performance in your app, pass a function diff --git a/admin/src/post.ts b/admin/src/post.ts index f92dbdb1..084a6bce 100644 --- a/admin/src/post.ts +++ b/admin/src/post.ts @@ -1,6 +1,6 @@ export async function postRequest( route: string, - body: TBody, + body: TBody ): Promise { const resp = await fetch(route, { method: "post", diff --git a/admin/tsconfig.json b/admin/tsconfig.json index d26ce47e..43f09240 100644 --- a/admin/tsconfig.json +++ b/admin/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -16,5 +20,7 @@ "noEmit": true, "jsx": "preserve" }, - "include": ["src"] -} + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/game/analysis_options.yaml b/game/analysis_options.yaml index 67ca129c..df0fea39 100644 --- a/game/analysis_options.yaml +++ b/game/analysis_options.yaml @@ -22,5 +22,6 @@ linter: # producing the lint. rules: # avoid_print: false # Uncomment to disable the `avoid_print` rule + # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/game/android/app/google-services.json b/game/android/app/google-services.json index be461b3d..60a30e64 100644 --- a/game/android/app/google-services.json +++ b/game/android/app/google-services.json @@ -109,4 +109,4 @@ } ], "configuration_version": "1" -} +} \ No newline at end of file diff --git a/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index e882ab98..d36b1fab 100644 --- a/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/game/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,122 +1,122 @@ { - "images": [ + "images" : [ { - "size": "20x20", - "idiom": "iphone", - "filename": "Icon-App-20x20@2x.png", - "scale": "2x" + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" }, { - "size": "20x20", - "idiom": "iphone", - "filename": "Icon-App-20x20@3x.png", - "scale": "3x" + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" }, { - "size": "29x29", - "idiom": "iphone", - "filename": "Icon-App-29x29@1x.png", - "scale": "1x" + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" }, { - "size": "29x29", - "idiom": "iphone", - "filename": "Icon-App-29x29@2x.png", - "scale": "2x" + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" }, { - "size": "29x29", - "idiom": "iphone", - "filename": "Icon-App-29x29@3x.png", - "scale": "3x" + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" }, { - "size": "40x40", - "idiom": "iphone", - "filename": "Icon-App-40x40@2x.png", - "scale": "2x" + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" }, { - "size": "40x40", - "idiom": "iphone", - "filename": "Icon-App-40x40@3x.png", - "scale": "3x" + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" }, { - "size": "60x60", - "idiom": "iphone", - "filename": "Icon-App-60x60@2x.png", - "scale": "2x" + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" }, { - "size": "60x60", - "idiom": "iphone", - "filename": "Icon-App-60x60@3x.png", - "scale": "3x" + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" }, { - "size": "20x20", - "idiom": "ipad", - "filename": "Icon-App-20x20@1x.png", - "scale": "1x" + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" }, { - "size": "20x20", - "idiom": "ipad", - "filename": "Icon-App-20x20@2x.png", - "scale": "2x" + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" }, { - "size": "29x29", - "idiom": "ipad", - "filename": "Icon-App-29x29@1x.png", - "scale": "1x" + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" }, { - "size": "29x29", - "idiom": "ipad", - "filename": "Icon-App-29x29@2x.png", - "scale": "2x" + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" }, { - "size": "40x40", - "idiom": "ipad", - "filename": "Icon-App-40x40@1x.png", - "scale": "1x" + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" }, { - "size": "40x40", - "idiom": "ipad", - "filename": "Icon-App-40x40@2x.png", - "scale": "2x" + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" }, { - "size": "76x76", - "idiom": "ipad", - "filename": "Icon-App-76x76@1x.png", - "scale": "1x" + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" }, { - "size": "76x76", - "idiom": "ipad", - "filename": "Icon-App-76x76@2x.png", - "scale": "2x" + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" }, { - "size": "83.5x83.5", - "idiom": "ipad", - "filename": "Icon-App-83.5x83.5@2x.png", - "scale": "2x" + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" }, { - "size": "1024x1024", - "idiom": "ios-marketing", - "filename": "Icon-App-1024x1024@1x.png", - "scale": "1x" + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" } ], - "info": { - "version": 1, - "author": "xcode" + "info" : { + "version" : 1, + "author" : "xcode" } } diff --git a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json index 781d7cdc..0bedcf2f 100644 --- a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -1,23 +1,23 @@ { - "images": [ + "images" : [ { - "idiom": "universal", - "filename": "LaunchImage.png", - "scale": "1x" + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" }, { - "idiom": "universal", - "filename": "LaunchImage@2x.png", - "scale": "2x" + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" }, { - "idiom": "universal", - "filename": "LaunchImage@3x.png", - "scale": "3x" + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" } ], - "info": { - "version": 1, - "author": "xcode" + "info" : { + "version" : 1, + "author" : "xcode" } } diff --git a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md index b5b843ad..89c2725b 100644 --- a/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ b/game/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -2,4 +2,4 @@ You can customize the launch screen with your own desired assets by replacing the image files in this directory. -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/game/web/index.html b/game/web/index.html index 237724a8..0b273656 100644 --- a/game/web/index.html +++ b/game/web/index.html @@ -1,6 +1,6 @@ - + - + - + - - - + + + - - - - + + + + game - - - + + + + - + diff --git a/game/web/manifest.json b/game/web/manifest.json index 61475e8f..f49858d2 100644 --- a/game/web/manifest.json +++ b/game/web/manifest.json @@ -1,23 +1,23 @@ { - "name": "game", - "short_name": "game", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] + "name": "game", + "short_name": "game", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] } diff --git a/scripts/dbreset.js b/scripts/dbreset.js index 7e1900a0..13c424c9 100644 --- a/scripts/dbreset.js +++ b/scripts/dbreset.js @@ -16,7 +16,7 @@ async function main() { process.env[`DB_RESET`] = "true"; console.log("Setting up database through docker"); execSync( - "docker compose up --build --no-attach postgres --exit-code-from server", + "docker compose up --build --no-attach postgres --exit-code-from server" ); console.log("Successfully reset the database!"); diff --git a/scripts/tests.js b/scripts/tests.js index a0a45278..e78f9c48 100644 --- a/scripts/tests.js +++ b/scripts/tests.js @@ -1,18 +1,10 @@ const { execSync } = require("child_process"); -const { - rmSync, - existsSync, - mkdirSync, - readdirSync, - lstatSync, - copyFileSync, -} = require("fs"); +const { rmSync, existsSync, mkdirSync, readdirSync, lstatSync, copyFileSync } = require("fs"); const path = require("path"); -function copyFolderSync(from, to) { - // https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js +function copyFolderSync(from, to) { // https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js mkdirSync(to); - readdirSync(from).forEach((element) => { + readdirSync(from).forEach(element => { if (lstatSync(path.join(from, element)).isFile()) { copyFileSync(path.join(from, element), path.join(to, element)); } else { @@ -22,7 +14,7 @@ function copyFolderSync(from, to) { } async function main() { - if (process.argv.length < 3 || !["unit", "e2e"].includes(process.argv[2])) { + if (process.argv.length < 3 || !(["unit", "e2e"].includes(process.argv[2]))) { console.log("USAGE: npm run tests -- "); return; } @@ -36,9 +28,7 @@ async function main() { if (testType === "UNIT") { try { console.log("Executing unit tests"); - execSync( - `docker compose up --no-deps --build server --exit-code-from server`, - ); + execSync(`docker compose up --no-deps --build server --exit-code-from server`); console.log("Tests ran successfully!"); } catch (err) { console.log("Test execution failed!"); @@ -60,9 +50,7 @@ async function main() { console.log("Setting up test database"); execSync("npm run dbreset"); console.log("Executing e2e tests"); - execSync( - `docker compose up --build --no-attach postgres --exit-code-from server`, - ); + execSync(`docker compose up --build --no-attach postgres --exit-code-from server`); console.log("Tests ran successfully!"); } catch (err) { console.log("Test execution failed!"); diff --git a/scripts/updateapi-lib/apiscanner.js b/scripts/updateapi-lib/apiscanner.js index 288019f2..89016e2f 100644 --- a/scripts/updateapi-lib/apiscanner.js +++ b/scripts/updateapi-lib/apiscanner.js @@ -3,52 +3,54 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getApiDefinitions = void 0; const ts_morph_1 = require("ts-morph"); function getApiDefinitions() { - const project = new ts_morph_1.Project({}); - project.addSourceFilesAtPaths("server/src/*/*.gateway.ts"); - project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); - project.addSourceFileAtPath("server/src/client/client.service.ts"); - const apiDefs = { - serverEntrypoints: new Map(), - clientEntrypoints: new Map(), - }; - const clientFile = project.getSourceFileOrThrow("client.service.ts"); - const clientApiDef = clientFile.getTypeAliasOrThrow("ClientApiDef"); - for (const prop of clientApiDef.getType().getProperties()) { - const ev = prop.getValueDeclarationOrThrow().getChildAtIndex(0).getText(); - const dto = prop.getValueDeclarationOrThrow().getChildAtIndex(2).getText(); - apiDefs.clientEntrypoints.set(ev, dto); - } - console.log(`Processed ${apiDefs.clientEntrypoints.size} client functions!`); - console.log(); - for (const file of project.getSourceFiles("server/src/*/*.gateway.ts")) { - for (const _class of file.getClasses()) { - const funcs = _class - .getMethods() - .filter((met) => !!met.getDecorator("SubscribeMessage")); - if (!_class.getName()?.includes("Gateway")) continue; - console.log(`${file.getBaseName()}: ${funcs.length} message functions`); - if (funcs.length === 0) continue; - for (const func of funcs) { - const ev = func - .getDecoratorOrThrow("SubscribeMessage") - .getArguments()[0] - .asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral) - .getLiteralValue(); - let dto = func - .getParameterOrThrow((param) => !!param.getDecorator("MessageBody")) - .getType() - .getText(); - if (dto.includes(".")) { - dto = dto.split(".").pop(); + const project = new ts_morph_1.Project({}); + project.addSourceFilesAtPaths("server/src/*/*.gateway.ts"); + project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); + project.addSourceFileAtPath("server/src/client/client.service.ts"); + const apiDefs = { + serverEntrypoints: new Map(), + clientEntrypoints: new Map(), + }; + const clientFile = project.getSourceFileOrThrow("client.service.ts"); + const clientApiDef = clientFile.getTypeAliasOrThrow("ClientApiDef"); + for (const prop of clientApiDef.getType().getProperties()) { + const ev = prop.getValueDeclarationOrThrow().getChildAtIndex(0).getText(); + const dto = prop.getValueDeclarationOrThrow().getChildAtIndex(2).getText(); + apiDefs.clientEntrypoints.set(ev, dto); + } + console.log(`Processed ${apiDefs.clientEntrypoints.size} client functions!`); + console.log(); + for (const file of project.getSourceFiles("server/src/*/*.gateway.ts")) { + for (const _class of file.getClasses()) { + const funcs = _class + .getMethods() + .filter((met) => !!met.getDecorator("SubscribeMessage")); + if (!_class.getName()?.includes("Gateway")) + continue; + console.log(`${file.getBaseName()}: ${funcs.length} message functions`); + if (funcs.length === 0) + continue; + for (const func of funcs) { + const ev = func + .getDecoratorOrThrow("SubscribeMessage") + .getArguments()[0] + .asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral) + .getLiteralValue(); + let dto = func + .getParameterOrThrow((param) => !!param.getDecorator("MessageBody")) + .getType() + .getText(); + if (dto.includes(".")) { + dto = dto.split(".").pop(); + } + apiDefs.serverEntrypoints.set(ev, dto); + } + break; } - apiDefs.serverEntrypoints.set(ev, dto); - } - break; } - } - console.log(); - console.log(`Processed ${apiDefs.serverEntrypoints.size} server functions!`); - console.log(); - return apiDefs; + console.log(); + console.log(`Processed ${apiDefs.serverEntrypoints.size} server functions!`); + console.log(); + return apiDefs; } exports.getApiDefinitions = getApiDefinitions; diff --git a/scripts/updateapi-lib/dartgen.js b/scripts/updateapi-lib/dartgen.js index fa3c6288..db1c4f37 100644 --- a/scripts/updateapi-lib/dartgen.js +++ b/scripts/updateapi-lib/dartgen.js @@ -1,172 +1,175 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getDartServerApiFile = - exports.getDartClientApiFile = - exports.genDartDtoFile = - void 0; +exports.getDartServerApiFile = exports.getDartClientApiFile = exports.genDartDtoFile = void 0; function toDartType(tsType, tsName) { - switch (tsType) { - case "string": - return "String"; - case "number": - return tsName.endsWith("Float") || tsName.endsWith("F") - ? "double" - : "int"; - case "boolean": - return "bool"; - default: - return tsType; - } + switch (tsType) { + case "string": + return "String"; + case "number": + return tsName.endsWith("Float") || tsName.endsWith("F") + ? "double" + : "int"; + case "boolean": + return "bool"; + default: + return tsType; + } } function genDartDtoFile(dtoDefs) { - let dartCode = ` + let dartCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! `; - for (const [name, fields] of dtoDefs.enumDtos.entries()) { - dartCode += `enum ${name} {\n`; - for (const field of fields) { - dartCode += ` ${field},\n`; + for (const [name, fields] of dtoDefs.enumDtos.entries()) { + dartCode += `enum ${name} {\n`; + for (const field of fields) { + dartCode += ` ${field},\n`; + } + dartCode += "}\n\n"; } - dartCode += "}\n\n"; - } - for (const [name, propMap] of dtoDefs.baseDtos.entries()) { - dartCode += `class ${name} {\n`; - dartCode += ` Map toJson() {\n`; - dartCode += ` Map fields = {};\n`; - for (const [ - propName, - [typeName, fieldType, isOptional], - ] of propMap.entries()) { - const dartType = toDartType(typeName, propName); - if (isOptional) { - dartCode += ` if (${propName} != null) {\n `; - } - dartCode += ` fields['${propName}'] = `; - if (fieldType == "PRIMITIVE" || fieldType == "PRIMITIVE[]") { - dartCode += `${propName}`; - } else if (fieldType == "DEPENDENT_DTO") { - dartCode += `${propName}!.toJson()`; - } else if (fieldType == "DEPENDENT_DTO[]") { - dartCode += ` + for (const [name, propMap] of dtoDefs.baseDtos.entries()) { + dartCode += `class ${name} {\n`; + dartCode += ` Map toJson() {\n`; + dartCode += ` Map fields = {};\n`; + for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { + const dartType = toDartType(typeName, propName); + if (isOptional) { + dartCode += ` if (${propName} != null) {\n `; + } + dartCode += ` fields['${propName}'] = `; + if (fieldType == "PRIMITIVE" || fieldType == "PRIMITIVE[]") { + dartCode += `${propName}`; + } + else if (fieldType == "DEPENDENT_DTO") { + dartCode += `${propName}!.toJson()`; + } + else if (fieldType == "DEPENDENT_DTO[]") { + dartCode += ` ${propName}! .map>( (dynamic val) => val!.toJson() ).toList() `; - } else if (fieldType == "ENUM_DTO") { - dartCode += `${propName}!.name`; - } else if (fieldType == "ENUM_DTO[]") { - dartCode += ` + } + else if (fieldType == "ENUM_DTO") { + dartCode += `${propName}!.name`; + } + else if (fieldType == "ENUM_DTO[]") { + dartCode += ` ${propName}! .map( (dynamic val) => val!.name ).toList() `; - } - if (isOptional) { - dartCode += `;\n }\n`; - } else { - dartCode += ";\n"; - } - } - dartCode += " return fields;\n"; - dartCode += " }\n\n"; - dartCode += ` ${name}.fromJson(Map fields) {\n`; - for (const [ - propName, - [typeName, fieldType, isOptional], - ] of propMap.entries()) { - const dartType = toDartType(typeName, propName); - dartCode += ` ${propName} = `; - if (isOptional) { - dartCode += `fields.containsKey('${propName}') ? (\n `; - } - if (fieldType == "PRIMITIVE" && dartType == "double") { - dartCode += `fields["${propName}"]!.toDouble()`; - } else if (fieldType == "PRIMITIVE[]" && dartType == "double") { - dartCode += ` + } + if (isOptional) { + dartCode += `;\n }\n`; + } + else { + dartCode += ";\n"; + } + } + dartCode += " return fields;\n"; + dartCode += " }\n\n"; + dartCode += ` ${name}.fromJson(Map fields) {\n`; + for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { + const dartType = toDartType(typeName, propName); + dartCode += ` ${propName} = `; + if (isOptional) { + dartCode += `fields.containsKey('${propName}') ? (\n `; + } + if (fieldType == "PRIMITIVE" && dartType == "double") { + dartCode += `fields["${propName}"]!.toDouble()`; + } + else if (fieldType == "PRIMITIVE[]" && dartType == "double") { + dartCode += ` fields["${propName}"]! .map<${dartType}>( (dynamic val) => val!.toDouble() ).toList() `; - } else if (fieldType == "PRIMITIVE") { - dartCode += `fields["${propName}"]`; - } else if (fieldType == "PRIMITIVE[]") { - dartCode += `List<${dartType}>.from(fields['${propName}'])`; - } else if (fieldType == "DEPENDENT_DTO") { - dartCode += `${dartType}.fromJson(fields['${propName}'])`; - } else if (fieldType == "DEPENDENT_DTO[]") { - dartCode += ` + } + else if (fieldType == "PRIMITIVE") { + dartCode += `fields["${propName}"]`; + } + else if (fieldType == "PRIMITIVE[]") { + dartCode += `List<${dartType}>.from(fields['${propName}'])`; + } + else if (fieldType == "DEPENDENT_DTO") { + dartCode += `${dartType}.fromJson(fields['${propName}'])`; + } + else if (fieldType == "DEPENDENT_DTO[]") { + dartCode += ` fields["${propName}"] .map<${dartType}>( (dynamic val) => ${dartType}.fromJson(val) ).toList() `; - } else if (fieldType == "ENUM_DTO") { - dartCode += `${dartType}.values.byName(fields['${propName}'])`; - } else if (fieldType == "ENUM_DTO[]") { - dartCode += ` + } + else if (fieldType == "ENUM_DTO") { + dartCode += `${dartType}.values.byName(fields['${propName}'])`; + } + else if (fieldType == "ENUM_DTO[]") { + dartCode += ` fields["${propName}"] .map<${dartType}>( (dynamic val) => ${dartType}.values.byName(val) ).toList() `; - } - if (isOptional) { - dartCode += `\n ) : null;\n`; - } else { - dartCode += ";\n"; - } - } - dartCode += " }\n\n"; - dartCode += ` void partialUpdate(${name} other) {`; - for (const [propName, [_, __, isOptional]] of propMap.entries()) { - if (isOptional) { - dartCode += ` ${propName} = other.${propName} == null ? ${propName} : other.${propName};\n`; - } else { - dartCode += ` ${propName} = other.${propName};\n`; - } - } - dartCode += " }\n\n"; - if (propMap.size > 0) { - dartCode += ` ${name}({`; - for (const [propName, [_, __, isOptional]] of propMap.entries()) { - if (isOptional) { - dartCode += `\nthis.${propName}, `; - } else { - dartCode += `\nrequired this.${propName}, `; + } + if (isOptional) { + dartCode += `\n ) : null;\n`; + } + else { + dartCode += ";\n"; + } } - } - dartCode += " });\n\n"; - } else { - dartCode += ` ${name}();`; - } - for (const [ - propName, - [typeName, fieldType, isOptional], - ] of propMap.entries()) { - const dartType = toDartType(typeName, propName); - const isArray = - fieldType == "ENUM_DTO[]" || - fieldType == "DEPENDENT_DTO[]" || - fieldType == "PRIMITIVE[]"; - const fullType = isArray ? `List<${dartType}>` : dartType; - dartCode += ` late ${fullType}`; - if (isOptional) { - dartCode += "?"; - } - dartCode += ` ${propName};\n`; + dartCode += " }\n\n"; + dartCode += ` void partialUpdate(${name} other) {`; + for (const [propName, [_, __, isOptional]] of propMap.entries()) { + if (isOptional) { + dartCode += ` ${propName} = other.${propName} == null ? ${propName} : other.${propName};\n`; + } + else { + dartCode += ` ${propName} = other.${propName};\n`; + } + } + dartCode += " }\n\n"; + if (propMap.size > 0) { + dartCode += ` ${name}({`; + for (const [propName, [_, __, isOptional]] of propMap.entries()) { + if (isOptional) { + dartCode += `\nthis.${propName}, `; + } + else { + dartCode += `\nrequired this.${propName}, `; + } + } + dartCode += " });\n\n"; + } + else { + dartCode += ` ${name}();`; + } + for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { + const dartType = toDartType(typeName, propName); + const isArray = fieldType == "ENUM_DTO[]" || + fieldType == "DEPENDENT_DTO[]" || + fieldType == "PRIMITIVE[]"; + const fullType = isArray ? `List<${dartType}>` : dartType; + dartCode += ` late ${fullType}`; + if (isOptional) { + dartCode += "?"; + } + dartCode += ` ${propName};\n`; + } + dartCode += "}\n\n"; } - dartCode += "}\n\n"; - } - return dartCode; + return dartCode; } exports.genDartDtoFile = genDartDtoFile; function getDartClientApiFile(apiDefs) { - let dartCode = ` + let dartCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! @@ -178,16 +181,16 @@ function getDartClientApiFile(apiDefs) { class GameClientApi { `; - for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { - dartCode += ` + for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { + dartCode += ` final _${ev}Controller = StreamController<${dto}>.broadcast(sync: true); Stream<${dto}> get ${ev}Stream => _${ev}Controller.stream; `; - } - dartCode += ` + } + dartCode += ` final _reconnectedController = StreamController.broadcast(sync: true); Stream get reconnectedStream => _reconnectedController.stream; @@ -206,27 +209,27 @@ function getDartClientApiFile(apiDefs) { sock.onDisconnect((data) => disconnectedController.add(null)); `; - for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { - dartCode += ` + for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { + dartCode += ` sock.on( "${ev}", (data) => _${ev}Controller.add(${dto}.fromJson(data))); `; - } - dartCode += ` + } + dartCode += ` _connectedController.add(null); } GameClientApi() {} } `; - return dartCode; + return dartCode; } exports.getDartClientApiFile = getDartClientApiFile; function getDartServerApiFile(apiDefs) { - let dartCode = ` + let dartCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! @@ -269,14 +272,14 @@ function getDartServerApiFile(apiDefs) { _socket.emit(ev, data); } `; - for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { - dartCode += ` + for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + dartCode += ` void ${ev}(${dto} dto) => _invokeWithRefresh( "${ev}", dto.toJson()); `; - } - dartCode += "}"; - return dartCode; + } + dartCode += "}"; + return dartCode; } exports.getDartServerApiFile = getDartServerApiFile; diff --git a/scripts/updateapi-lib/dtoscanner.js b/scripts/updateapi-lib/dtoscanner.js index dc31b812..33e3cebc 100644 --- a/scripts/updateapi-lib/dtoscanner.js +++ b/scripts/updateapi-lib/dtoscanner.js @@ -3,106 +3,95 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getDtoDefinitions = void 0; const ts_morph_1 = require("ts-morph"); function getDtoDefinitions() { - const project = new ts_morph_1.Project({}); - project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); - console.log(`Discovered ${project.getSourceFiles().length} DTO files!`); - console.log(); - const enumDtos = new Map(); - const baseDtos = new Map(); - for (const file of project.getSourceFiles()) { - const interfs = file.getInterfaces(); - const enums = file.getEnums(); - console.log( - `${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs`, - ); - for (const enum_ of enums) { - const vals = enum_.getMembers().map((val) => val.getName()); - enumDtos.set(enum_.getName(), vals); - } - for (const interf of interfs) { - const props = interf.getProperties(); - const baseDto = new Map(); - const interfName = interf.getName(); - baseDtos.set(interfName, baseDto); - for (const prop of props) { - const propType = prop.getType(); - const propName = prop.getName(); - const isOptional = prop.hasQuestionToken(); - if (propType.isString() || propType.getArrayElementType()?.isString()) { - // str - baseDto.set(propName, [ - "string", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } else if ( - propType.isNumber() || - propType.getArrayElementType()?.isNumber() - ) { - // num - baseDto.set(propName, [ - "number", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } else if ( - propType.isBoolean() || - propType.getArrayElementType()?.isBoolean() - ) { - // num - baseDto.set(propName, [ - "boolean", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } else if ( - propType.isUnion() || - propType.getArrayElementType()?.isUnion() - ) { - // enum - const enumName = - interfName.replace("Dto", "") + - propName[0].toUpperCase() + - propName.substring(1) + - "Dto"; - enumDtos.set( - enumName, - propType - .getUnionTypes() - .map((t) => t.getLiteralValue()?.toString() ?? ""), - ); - baseDto.set(propName, [ - enumName, - propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", - isOptional, - ]); - } else if ( - propType.isInterface() || - propType.getArrayElementType()?.isInterface() || - propType.isEnum() || - propType.getArrayElementType()?.isEnum() - ) { - let name = propType.isArray() - ? propType.getArrayElementTypeOrThrow().getText() - : propType.getText(); - const isEnum = - propType.isEnum() || propType.getArrayElementType()?.isEnum(); - if (name.includes(".")) { - name = name.split(".").pop(); - } - const fieldType = propType.isArray() - ? isEnum - ? "ENUM_DTO[]" - : "DEPENDENT_DTO[]" - : isEnum - ? "ENUM_DTO" - : "DEPENDENT_DTO"; - baseDto.set(propName, [name, fieldType, isOptional]); + const project = new ts_morph_1.Project({}); + project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); + console.log(`Discovered ${project.getSourceFiles().length} DTO files!`); + console.log(); + const enumDtos = new Map(); + const baseDtos = new Map(); + for (const file of project.getSourceFiles()) { + const interfs = file.getInterfaces(); + const enums = file.getEnums(); + console.log(`${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs`); + for (const enum_ of enums) { + const vals = enum_.getMembers().map((val) => val.getName()); + enumDtos.set(enum_.getName(), vals); + } + for (const interf of interfs) { + const props = interf.getProperties(); + const baseDto = new Map(); + const interfName = interf.getName(); + baseDtos.set(interfName, baseDto); + for (const prop of props) { + const propType = prop.getType(); + const propName = prop.getName(); + const isOptional = prop.hasQuestionToken(); + if (propType.isString() || propType.getArrayElementType()?.isString()) { + // str + baseDto.set(propName, [ + "string", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } + else if (propType.isNumber() || + propType.getArrayElementType()?.isNumber()) { + // num + baseDto.set(propName, [ + "number", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } + else if (propType.isBoolean() || + propType.getArrayElementType()?.isBoolean()) { + // num + baseDto.set(propName, [ + "boolean", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } + else if (propType.isUnion() || + propType.getArrayElementType()?.isUnion()) { + // enum + const enumName = interfName.replace("Dto", "") + + propName[0].toUpperCase() + + propName.substring(1) + + "Dto"; + enumDtos.set(enumName, propType + .getUnionTypes() + .map((t) => t.getLiteralValue()?.toString() ?? "")); + baseDto.set(propName, [ + enumName, + propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", + isOptional, + ]); + } + else if (propType.isInterface() || + propType.getArrayElementType()?.isInterface() || + propType.isEnum() || + propType.getArrayElementType()?.isEnum()) { + let name = propType.isArray() + ? propType.getArrayElementTypeOrThrow().getText() + : propType.getText(); + const isEnum = propType.isEnum() || propType.getArrayElementType()?.isEnum(); + if (name.includes(".")) { + name = name.split(".").pop(); + } + const fieldType = propType.isArray() + ? isEnum + ? "ENUM_DTO[]" + : "DEPENDENT_DTO[]" + : isEnum + ? "ENUM_DTO" + : "DEPENDENT_DTO"; + baseDto.set(propName, [name, fieldType, isOptional]); + } + } } - } } - } - console.log(); - return { enumDtos, baseDtos }; + console.log(); + return { enumDtos, baseDtos }; } exports.getDtoDefinitions = getDtoDefinitions; diff --git a/scripts/updateapi-lib/dtoscanner.ts b/scripts/updateapi-lib/dtoscanner.ts index 3f54960f..b7a8293c 100644 --- a/scripts/updateapi-lib/dtoscanner.ts +++ b/scripts/updateapi-lib/dtoscanner.ts @@ -17,7 +17,7 @@ export function getDtoDefinitions(): DtoDefs { const enums = file.getEnums(); console.log( - `${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs`, + `${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs` ); for (const enum_ of enums) { @@ -78,7 +78,7 @@ export function getDtoDefinitions(): DtoDefs { enumName, propType .getUnionTypes() - .map((t) => t.getLiteralValue()?.toString() ?? ""), + .map((t) => t.getLiteralValue()?.toString() ?? "") ); baseDto.set(propName, [ @@ -108,8 +108,8 @@ export function getDtoDefinitions(): DtoDefs { ? "ENUM_DTO[]" : "DEPENDENT_DTO[]" : isEnum - ? "ENUM_DTO" - : "DEPENDENT_DTO"; + ? "ENUM_DTO" + : "DEPENDENT_DTO"; baseDto.set(propName, [name, fieldType, isOptional]); } diff --git a/scripts/updateapi-lib/tsgen.js b/scripts/updateapi-lib/tsgen.js index 3f6c717d..6aebdd42 100644 --- a/scripts/updateapi-lib/tsgen.js +++ b/scripts/updateapi-lib/tsgen.js @@ -2,42 +2,38 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getAdminApiFile = exports.genTsDtoFile = void 0; function genTsDtoFile(dtoDefs) { - let tsCode = ` + let tsCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! `; - for (const [name, fields] of dtoDefs.enumDtos.entries()) { - tsCode += `export enum ${name} {`; - for (const field of fields) { - tsCode += `\n ${field} = "${field}",`; + for (const [name, fields] of dtoDefs.enumDtos.entries()) { + tsCode += `export enum ${name} {`; + for (const field of fields) { + tsCode += `\n ${field} = "${field}",`; + } + tsCode += "}\n\n"; } - tsCode += "}\n\n"; - } - for (const [name, propMap] of dtoDefs.baseDtos.entries()) { - tsCode += `export interface ${name} {\n`; - for (const [ - propName, - [typeName, fieldType, isOptional], - ] of propMap.entries()) { - const isArray = - fieldType == "ENUM_DTO[]" || - fieldType == "DEPENDENT_DTO[]" || - fieldType == "PRIMITIVE[]"; - const fullType = isArray ? `${typeName}[]` : typeName; - tsCode += ` ${propName}`; - if (isOptional) { - tsCode += "?"; - } - tsCode += `: ${fullType};\n`; + for (const [name, propMap] of dtoDefs.baseDtos.entries()) { + tsCode += `export interface ${name} {\n`; + for (const [propName, [typeName, fieldType, isOptional],] of propMap.entries()) { + const isArray = fieldType == "ENUM_DTO[]" || + fieldType == "DEPENDENT_DTO[]" || + fieldType == "PRIMITIVE[]"; + const fullType = isArray ? `${typeName}[]` : typeName; + tsCode += ` ${propName}`; + if (isOptional) { + tsCode += "?"; + } + tsCode += `: ${fullType};\n`; + } + tsCode += "}\n\n"; } - tsCode += "}\n\n"; - } - return tsCode; + return tsCode; } exports.genTsDtoFile = genTsDtoFile; function getAdminApiFile(apiDefs) { - let tsCode = ` + let tsCode = ` // CODE AUTOGENERATED BY npm run updateapi // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! @@ -54,25 +50,25 @@ function getAdminApiFile(apiDefs) { } `; - for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { - tsCode += ` + for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + tsCode += ` ${ev}(data: dto.${dto}) { this.send("${ev}", data); } `; - } - for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { - const formattedName = ev[0].toUpperCase() + ev.substring(1); - tsCode += ` + } + for (const [ev, dto] of apiDefs.clientEntrypoints.entries()) { + const formattedName = ev[0].toUpperCase() + ev.substring(1); + tsCode += ` on${formattedName}(callback: (data: dto.${dto}) => void) { this.socket.removeAllListeners("${ev}"); this.socket.on("${ev}", (data) => callback(data)); } `; - } - tsCode += "}"; - return tsCode; + } + tsCode += "}"; + return tsCode; } exports.getAdminApiFile = getAdminApiFile; diff --git a/scripts/updateapi.js b/scripts/updateapi.js index 7aa117ec..a6e6cf15 100644 --- a/scripts/updateapi.js +++ b/scripts/updateapi.js @@ -19,25 +19,16 @@ const tsDtoCode = (0, tsgen_1.genTsDtoFile)(dtos); const tsApiCode = (0, tsgen_1.getAdminApiFile)(apis); const dartClientApiCode = (0, dartgen_1.getDartClientApiFile)(apis); const dartServerApiCode = (0, dartgen_1.getDartServerApiFile)(apis); -(0, fs_1.writeFileSync)(flutterDtoPath, dartDtoCode, { - encoding: "utf8", - flag: "w", -}); -(0, fs_1.writeFileSync)(adminDtoPath, tsDtoCode, { - encoding: "utf8", - flag: "w", -}); -(0, fs_1.writeFileSync)(adminApiPath, tsApiCode, { - encoding: "utf8", - flag: "w", -}); +(0, fs_1.writeFileSync)(flutterDtoPath, dartDtoCode, { encoding: "utf8", flag: "w" }); +(0, fs_1.writeFileSync)(adminDtoPath, tsDtoCode, { encoding: "utf8", flag: "w" }); +(0, fs_1.writeFileSync)(adminApiPath, tsApiCode, { encoding: "utf8", flag: "w" }); (0, fs_1.writeFileSync)(flutterClientApiPath, dartClientApiCode, { - encoding: "utf8", - flag: "w", + encoding: "utf8", + flag: "w", }); (0, fs_1.writeFileSync)(flutterServerApiPath, dartServerApiCode, { - encoding: "utf8", - flag: "w", + encoding: "utf8", + flag: "w", }); (0, process_1.chdir)("./game"); (0, child_process_1.execSync)("dart format lib"); diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index 3c4b0fe1..ddb5c456 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -195,4 +195,5 @@ export class AchievementService { achievementType: ach.achievementType as AchievementTypeDto, }; } + } diff --git a/server/src/challenge/challenge.gateway.ts b/server/src/challenge/challenge.gateway.ts index 30f767de..f73a1953 100644 --- a/server/src/challenge/challenge.gateway.ts +++ b/server/src/challenge/challenge.gateway.ts @@ -90,8 +90,9 @@ export class ChallengeGateway { ) { if (await this.challengeService.completeChallenge(user, data.challengeId)) { const group = await this.groupService.getGroupForUser(user); - const tracker = - await this.eventService.getCurrentEventTrackerForUser(user); + const tracker = await this.eventService.getCurrentEventTrackerForUser( + user, + ); await this.groupService.emitUpdateGroupData(group, false); await this.eventService.emitUpdateEventTracker(tracker); diff --git a/server/src/event/event.e2e-spec.ts b/server/src/event/event.e2e-spec.ts index 2d790464..b0c01acd 100644 --- a/server/src/event/event.e2e-spec.ts +++ b/server/src/event/event.e2e-spec.ts @@ -114,8 +114,9 @@ describe('EventModule E2E', () => { describe('Playthrough testing', () => { it('Should successfully complete a playthrough', async () => { await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney1.id }); - const tracker1 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker1 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker1.eventId).toEqual(exJourney1.id); expect(tracker1.score).toEqual(0); expect( @@ -131,14 +132,16 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker2 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker2 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker2.curChallengeId).toEqual(journey1Chal.id); expect(tracker2.score).toEqual(1); await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney2.id }); - const tracker3 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker3 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker3.eventId).toEqual(exJourney2.id); expect(tracker3.score).toEqual(0); expect( @@ -160,8 +163,9 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker4 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker4 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker4.curChallengeId).toEqual(journey2Chal.id); expect(tracker4.score).toEqual(1); expect( @@ -177,8 +181,9 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker5 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker5 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker5.curChallengeId).toEqual(journey2Chal.id); expect(tracker5.score).toEqual(2); @@ -186,8 +191,9 @@ describe('EventModule E2E', () => { eventId: exChallenge1.id, }); - const tracker6 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker6 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker6.eventId).toEqual(exChallenge1.id); expect(tracker6.score).toEqual(0); expect( @@ -203,15 +209,17 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker7 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker7 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker7.curChallengeId).toEqual(tracker6.curChallengeId); expect(tracker7.score).toEqual(1); await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney1.id }); - const tracker8 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker8 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker8.eventId).toEqual(exJourney1.id); expect(tracker8.curChallengeId).toEqual(journey1Chal.id); expect(tracker8.score).toEqual(1); @@ -228,8 +236,9 @@ describe('EventModule E2E', () => { ), ).toBeFalsy(); - const tracker9 = - await eventService.getCurrentEventTrackerForUser(exPlayer); + const tracker9 = await eventService.getCurrentEventTrackerForUser( + exPlayer, + ); expect(tracker9.eventId).toEqual(exJourney1.id); expect(tracker9.curChallengeId).toEqual(journey1Chal.id); expect(tracker9.score).toEqual(2); diff --git a/server/src/event/event.service.ts b/server/src/event/event.service.ts index 2ebbf506..3ec5d1ab 100644 --- a/server/src/event/event.service.ts +++ b/server/src/event/event.service.ts @@ -226,8 +226,8 @@ export class EventService { user.id } = p."B") order by ((ev."latitude" - ${data.latitudeF})^2 + (ev."longitude" - ${ - data.longitudeF - })^2) + data.longitudeF + })^2) fetch first ${data.count ?? 4} rows only `; return evs; @@ -273,8 +273,8 @@ export class EventService { ev.difficulty === DifficultyMode.EASY ? 'Easy' : ev.difficulty === DifficultyMode.NORMAL - ? 'Normal' - : 'Hard', + ? 'Normal' + : 'Hard', latitudeF: ev.latitude, longitudeF: ev.longitude, }; @@ -437,8 +437,8 @@ export class EventService { (event.difficulty === 'Easy' ? DifficultyMode.EASY : event.difficulty === 'Normal' - ? DifficultyMode.NORMAL - : DifficultyMode.HARD), + ? DifficultyMode.NORMAL + : DifficultyMode.HARD), latitude: event.latitudeF, longitude: event.longitudeF, category: event.category, diff --git a/server/src/group/group.service.ts b/server/src/group/group.service.ts index d9245d7c..43b2b05c 100644 --- a/server/src/group/group.service.ts +++ b/server/src/group/group.service.ts @@ -281,8 +281,9 @@ export class GroupService { const memberDtos = await Promise.all( members.map(async mem => { - const tracker = - await this.eventService.getCurrentEventTrackerForUser(mem); + const tracker = await this.eventService.getCurrentEventTrackerForUser( + mem, + ); return { id: mem.id, name: mem.username, diff --git a/server/src/organization/organization.e2e-spec.ts b/server/src/organization/organization.e2e-spec.ts index 64b60433..260605f6 100644 --- a/server/src/organization/organization.e2e-spec.ts +++ b/server/src/organization/organization.e2e-spec.ts @@ -139,12 +139,15 @@ describe('OrganizationModule E2E', () => { ); defaultEv = (await eventService.getEventById( - (await eventService.getCurrentEventTrackerForUser(basicUser)).eventId, + ( + await eventService.getCurrentEventTrackerForUser(basicUser) + ).eventId, ))!; defaultChal = (await challengeService.getChallengeById( - (await eventService.getCurrentEventTrackerForUser(basicUser)) - .curChallengeId, + ( + await eventService.getCurrentEventTrackerForUser(basicUser) + ).curChallengeId, ))!; managerGroup = await groupService.getGroupForUser(managerUser); From cea9484f22d5ee20aadde6fd56961c016fd11222 Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Mon, 22 Apr 2024 15:07:34 -0400 Subject: [PATCH 14/17] Fix code style --- server/src/achievement/achievement.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index ddb5c456..3c4b0fe1 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -195,5 +195,4 @@ export class AchievementService { achievementType: ach.achievementType as AchievementTypeDto, }; } - } From 3550ddbe0babccd682786b215980e3cd95f03a2d Mon Sep 17 00:00:00 2001 From: Jasmine Li Date: Mon, 22 Apr 2024 17:21:12 -0400 Subject: [PATCH 15/17] Added documentation, added afterAll to achievements e2e, removed debug lines --- .vscode/settings.json | 7 +++---- server/src/achievement/achievement.dto.ts | 2 +- server/src/achievement/achievement.e2e-spec.ts | 12 ++++++++++++ server/src/achievement/achievement.gateway.ts | 1 - server/src/achievement/achievement.service.ts | 1 - 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7498308f..6b2120bb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,6 @@ { "typescript.preferences.importModuleSpecifier": "relative", - "githubPullRequests.ignoredPullRequestBranches": [ - "master" - ], + "editor.formatOnSave": true, + "githubPullRequests.ignoredPullRequestBranches": ["master"], "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file +} diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index 3936cfe2..0eceb6ad 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -30,7 +30,7 @@ export interface UpdateAchievementDataDto { achievement: AchievementDto; deleted: boolean; } - +/** DTO for requestAchievementData */ export interface RequestAchievementDataDto { achievements: string[]; } diff --git a/server/src/achievement/achievement.e2e-spec.ts b/server/src/achievement/achievement.e2e-spec.ts index dd8d180c..da5eb1e1 100644 --- a/server/src/achievement/achievement.e2e-spec.ts +++ b/server/src/achievement/achievement.e2e-spec.ts @@ -40,6 +40,9 @@ describe('AchievementModule E2E', () => { let fullAbility: AppAbility; let orgUsage: OrganizationSpecialUsage; + /** beforeAll runs before anything else. It adds new users and prerequisites. + * afterAll runs after all the tests. It removes lingering values in the database. + */ beforeAll(async () => { moduleRef = await Test.createTestingModule({ imports: [AppModule], @@ -194,4 +197,13 @@ describe('AchievementModule E2E', () => { expect(achres).toEqual(null); }); }); + + afterAll(async () => { + await prisma.user.delete({ + where: { + id: user.id, + }, + }); + await app.close(); + }); }); diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index 2efc274e..5685d60b 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -67,7 +67,6 @@ export class AchievementGateway { @CallingUser() user: User, @MessageBody() data: UpdateAchievementDataDto, ) { - // TODO: need to change organizations when removing achievement const achievement = await this.achievementService.getAchievementFromId( data.achievement.id, ); diff --git a/server/src/achievement/achievement.service.ts b/server/src/achievement/achievement.service.ts index 3c4b0fe1..e0bb21c9 100644 --- a/server/src/achievement/achievement.service.ts +++ b/server/src/achievement/achievement.service.ts @@ -70,7 +70,6 @@ export class AchievementService { ], }, })) > 0; - console.log(canUpdateOrg); const canUpdateAch = (await this.prisma.achievement.count({ From 951d1288be22193347830cb8c62f3f57d03d61ba Mon Sep 17 00:00:00 2001 From: neketka Date: Mon, 22 Apr 2024 21:09:11 -0400 Subject: [PATCH 16/17] Fixed naming --- admin/src/all.dto.ts | 38 +++++++----------------- game/lib/api/game_client_dto.dart | 43 ++++++++------------------- scripts/updateapi-lib/dtoscanner.js | 33 +++++++++++---------- scripts/updateapi-lib/dtoscanner.ts | 46 ++++++++++++++--------------- 4 files changed, 62 insertions(+), 98 deletions(-) diff --git a/admin/src/all.dto.ts b/admin/src/all.dto.ts index adc38bd3..b8a8921d 100644 --- a/admin/src/all.dto.ts +++ b/admin/src/all.dto.ts @@ -8,24 +8,6 @@ export enum AchievementTypeDto { TotalChallengesOrJourneys = "TotalChallengesOrJourneys", } -export enum AchievementLocationTypeDto { - ENG_QUAD = "ENG_QUAD", - ARTS_QUAD = "ARTS_QUAD", - AG_QUAD = "AG_QUAD", - NORTH_CAMPUS = "NORTH_CAMPUS", - WEST_CAMPUS = "WEST_CAMPUS", - COLLEGETOWN = "COLLEGETOWN", - ITHACA_COMMONS = "ITHACA_COMMONS", - ANY = "ANY", -} - -export enum AchievementAchievementTypeDto { - TOTAL_POINTS = "TOTAL_POINTS", - TOTAL_CHALLENGES = "TOTAL_CHALLENGES", - TOTAL_JOURNEYS = "TOTAL_JOURNEYS", - TOTAL_CHALLENGES_OR_JOURNEYS = "TOTAL_CHALLENGES_OR_JOURNEYS", -} - export enum LoginAudDto { android = "android", ios = "ios", @@ -41,14 +23,14 @@ export enum LoginEnrollmentTypeDto { } export enum ChallengeLocationDto { - ENG_QUAD = "ENG_QUAD", - ARTS_QUAD = "ARTS_QUAD", - AG_QUAD = "AG_QUAD", - NORTH_CAMPUS = "NORTH_CAMPUS", - WEST_CAMPUS = "WEST_CAMPUS", - COLLEGETOWN = "COLLEGETOWN", - ITHACA_COMMONS = "ITHACA_COMMONS", - ANY = "ANY", + EngQuad = "EngQuad", + ArtsQuad = "ArtsQuad", + AgQuad = "AgQuad", + NorthCampus = "NorthCampus", + WestCampus = "WestCampus", + Collegetown = "Collegetown", + IthacaCommons = "IthacaCommons", + Any = "Any", } export enum EventCategoryDto { @@ -97,8 +79,8 @@ export interface AchievementDto { name?: string; description?: string; imageUrl?: string; - locationType?: AchievementLocationTypeDto; - achievementType?: AchievementAchievementTypeDto; + locationType?: ChallengeLocationDto; + achievementType?: AchievementTypeDto; initialOrganizationId?: string; } diff --git a/game/lib/api/game_client_dto.dart b/game/lib/api/game_client_dto.dart index 04621812..211812e3 100644 --- a/game/lib/api/game_client_dto.dart +++ b/game/lib/api/game_client_dto.dart @@ -8,24 +8,6 @@ enum AchievementTypeDto { TotalChallengesOrJourneys, } -enum AchievementLocationTypeDto { - ENG_QUAD, - ARTS_QUAD, - AG_QUAD, - NORTH_CAMPUS, - WEST_CAMPUS, - COLLEGETOWN, - ITHACA_COMMONS, - ANY, -} - -enum AchievementAchievementTypeDto { - TOTAL_POINTS, - TOTAL_CHALLENGES, - TOTAL_JOURNEYS, - TOTAL_CHALLENGES_OR_JOURNEYS, -} - enum LoginAudDto { android, ios, @@ -41,14 +23,14 @@ enum LoginEnrollmentTypeDto { } enum ChallengeLocationDto { - ENG_QUAD, - ARTS_QUAD, - AG_QUAD, - NORTH_CAMPUS, - WEST_CAMPUS, - COLLEGETOWN, - ITHACA_COMMONS, - ANY, + EngQuad, + ArtsQuad, + AgQuad, + NorthCampus, + WestCampus, + Collegetown, + IthacaCommons, + Any, } enum EventCategoryDto { @@ -132,11 +114,10 @@ class AchievementDto { fields.containsKey('description') ? (fields["description"]) : null; imageUrl = fields.containsKey('imageUrl') ? (fields["imageUrl"]) : null; locationType = fields.containsKey('locationType') - ? (AchievementLocationTypeDto.values.byName(fields['locationType'])) + ? (ChallengeLocationDto.values.byName(fields['locationType'])) : null; achievementType = fields.containsKey('achievementType') - ? (AchievementAchievementTypeDto.values - .byName(fields['achievementType'])) + ? (AchievementTypeDto.values.byName(fields['achievementType'])) : null; initialOrganizationId = fields.containsKey('initialOrganizationId') ? (fields["initialOrganizationId"]) @@ -178,8 +159,8 @@ class AchievementDto { late String? name; late String? description; late String? imageUrl; - late AchievementLocationTypeDto? locationType; - late AchievementAchievementTypeDto? achievementType; + late ChallengeLocationDto? locationType; + late AchievementTypeDto? achievementType; late String? initialOrganizationId; } diff --git a/scripts/updateapi-lib/dtoscanner.js b/scripts/updateapi-lib/dtoscanner.js index 33e3cebc..a69d020f 100644 --- a/scripts/updateapi-lib/dtoscanner.js +++ b/scripts/updateapi-lib/dtoscanner.js @@ -52,22 +52,6 @@ function getDtoDefinitions() { isOptional, ]); } - else if (propType.isUnion() || - propType.getArrayElementType()?.isUnion()) { - // enum - const enumName = interfName.replace("Dto", "") + - propName[0].toUpperCase() + - propName.substring(1) + - "Dto"; - enumDtos.set(enumName, propType - .getUnionTypes() - .map((t) => t.getLiteralValue()?.toString() ?? "")); - baseDto.set(propName, [ - enumName, - propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", - isOptional, - ]); - } else if (propType.isInterface() || propType.getArrayElementType()?.isInterface() || propType.isEnum() || @@ -88,9 +72,26 @@ function getDtoDefinitions() { : "DEPENDENT_DTO"; baseDto.set(propName, [name, fieldType, isOptional]); } + else if (propType.isUnion() || + propType.getArrayElementType()?.isUnion()) { + // enum + const enumName = interfName.replace("Dto", "") + + propName[0].toUpperCase() + + propName.substring(1) + + "Dto"; + enumDtos.set(enumName, propType + .getUnionTypes() + .map((t) => t.getLiteralValue()?.toString() ?? "")); + baseDto.set(propName, [ + enumName, + propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", + isOptional, + ]); + } } } } + console.log(new Array(enumDtos.keys()), new Array(baseDtos.keys())); console.log(); return { enumDtos, baseDtos }; } diff --git a/scripts/updateapi-lib/dtoscanner.ts b/scripts/updateapi-lib/dtoscanner.ts index b7a8293c..4c45c940 100644 --- a/scripts/updateapi-lib/dtoscanner.ts +++ b/scripts/updateapi-lib/dtoscanner.ts @@ -63,29 +63,6 @@ export function getDtoDefinitions(): DtoDefs { propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", isOptional, ]); - } else if ( - propType.isUnion() || - propType.getArrayElementType()?.isUnion() - ) { - // enum - const enumName = - interfName.replace("Dto", "") + - propName[0].toUpperCase() + - propName.substring(1) + - "Dto"; - - enumDtos.set( - enumName, - propType - .getUnionTypes() - .map((t) => t.getLiteralValue()?.toString() ?? "") - ); - - baseDto.set(propName, [ - enumName, - propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", - isOptional, - ]); } else if ( propType.isInterface() || propType.getArrayElementType()?.isInterface() || @@ -112,6 +89,29 @@ export function getDtoDefinitions(): DtoDefs { : "DEPENDENT_DTO"; baseDto.set(propName, [name, fieldType, isOptional]); + } else if ( + propType.isUnion() || + propType.getArrayElementType()?.isUnion() + ) { + // enum + const enumName = + interfName.replace("Dto", "") + + propName[0].toUpperCase() + + propName.substring(1) + + "Dto"; + + enumDtos.set( + enumName, + propType + .getUnionTypes() + .map((t) => t.getLiteralValue()?.toString() ?? "") + ); + + baseDto.set(propName, [ + enumName, + propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", + isOptional, + ]); } } } From 850c484d0352a11c74f3c118cf0973c80c1e7493 Mon Sep 17 00:00:00 2001 From: neketka Date: Mon, 22 Apr 2024 22:05:20 -0400 Subject: [PATCH 17/17] Fix admin errors --- admin/src/all.dto.ts | 24 +++++++++---------- game/lib/api/game_client_dto.dart | 24 +++++++++---------- server/src/achievement/achievement.dto.ts | 8 +++---- .../src/achievement/achievement.e2e-spec.ts | 8 +++---- server/src/challenge/challenge.dto.ts | 16 ++++++------- server/src/challenge/challenge.e2e-spec.ts | 6 ++--- .../src/organization/organization.e2e-spec.ts | 2 +- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/admin/src/all.dto.ts b/admin/src/all.dto.ts index b8a8921d..58f9b7f3 100644 --- a/admin/src/all.dto.ts +++ b/admin/src/all.dto.ts @@ -2,10 +2,10 @@ // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! export enum AchievementTypeDto { - TotalPoints = "TotalPoints", - TotalChallenges = "TotalChallenges", - TotalJourneys = "TotalJourneys", - TotalChallengesOrJourneys = "TotalChallengesOrJourneys", + TOTAL_POINTS = "TOTAL_POINTS", + TOTAL_CHALLENGES = "TOTAL_CHALLENGES", + TOTAL_JOURNEYS = "TOTAL_JOURNEYS", + TOTAL_CHALLENGES_OR_JOURNEYS = "TOTAL_CHALLENGES_OR_JOURNEYS", } export enum LoginAudDto { @@ -23,14 +23,14 @@ export enum LoginEnrollmentTypeDto { } export enum ChallengeLocationDto { - EngQuad = "EngQuad", - ArtsQuad = "ArtsQuad", - AgQuad = "AgQuad", - NorthCampus = "NorthCampus", - WestCampus = "WestCampus", - Collegetown = "Collegetown", - IthacaCommons = "IthacaCommons", - Any = "Any", + ENG_QUAD = "ENG_QUAD", + ARTS_QUAD = "ARTS_QUAD", + AG_QUAD = "AG_QUAD", + NORTH_CAMPUS = "NORTH_CAMPUS", + WEST_CAMPUS = "WEST_CAMPUS", + COLLEGETOWN = "COLLEGETOWN", + ITHACA_COMMONS = "ITHACA_COMMONS", + ANY = "ANY", } export enum EventCategoryDto { diff --git a/game/lib/api/game_client_dto.dart b/game/lib/api/game_client_dto.dart index 211812e3..1a73ac1e 100644 --- a/game/lib/api/game_client_dto.dart +++ b/game/lib/api/game_client_dto.dart @@ -2,10 +2,10 @@ // IF YOU MODIFY THIS FILE, MAKE SURE TO ALSO MODIFY THE updateapi SCRIPT! // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! enum AchievementTypeDto { - TotalPoints, - TotalChallenges, - TotalJourneys, - TotalChallengesOrJourneys, + TOTAL_POINTS, + TOTAL_CHALLENGES, + TOTAL_JOURNEYS, + TOTAL_CHALLENGES_OR_JOURNEYS, } enum LoginAudDto { @@ -23,14 +23,14 @@ enum LoginEnrollmentTypeDto { } enum ChallengeLocationDto { - EngQuad, - ArtsQuad, - AgQuad, - NorthCampus, - WestCampus, - Collegetown, - IthacaCommons, - Any, + ENG_QUAD, + ARTS_QUAD, + AG_QUAD, + NORTH_CAMPUS, + WEST_CAMPUS, + COLLEGETOWN, + ITHACA_COMMONS, + ANY, } enum EventCategoryDto { diff --git a/server/src/achievement/achievement.dto.ts b/server/src/achievement/achievement.dto.ts index 0eceb6ad..b26c85da 100644 --- a/server/src/achievement/achievement.dto.ts +++ b/server/src/achievement/achievement.dto.ts @@ -1,10 +1,10 @@ import { ChallengeLocationDto } from '../challenge/challenge.dto'; export enum AchievementTypeDto { - TotalPoints = 'TOTAL_POINTS', - TotalChallenges = 'TOTAL_CHALLENGES', - TotalJourneys = 'TOTAL_JOURNEYS', - TotalChallengesOrJourneys = 'TOTAL_CHALLENGES_OR_JOURNEYS', + TOTAL_POINTS = 'TOTAL_POINTS', + TOTAL_CHALLENGES = 'TOTAL_CHALLENGES', + TOTAL_JOURNEYS = 'TOTAL_JOURNEYS', + TOTAL_CHALLENGES_OR_JOURNEYS = 'TOTAL_CHALLENGES_OR_JOURNEYS', } export interface AchievementDto { diff --git a/server/src/achievement/achievement.e2e-spec.ts b/server/src/achievement/achievement.e2e-spec.ts index da5eb1e1..2509ea8c 100644 --- a/server/src/achievement/achievement.e2e-spec.ts +++ b/server/src/achievement/achievement.e2e-spec.ts @@ -114,8 +114,8 @@ describe('AchievementModule E2E', () => { description: 'ach dto', requiredPoints: 1, imageUrl: 'url', - locationType: ChallengeLocationDto.EngQuad, - achievementType: AchievementTypeDto.TotalChallengesOrJourneys, + locationType: ChallengeLocationDto.ENG_QUAD, + achievementType: AchievementTypeDto.TOTAL_CHALLENGES_OR_JOURNEYS, initialOrganizationId: orgId, }; @@ -166,8 +166,8 @@ describe('AchievementModule E2E', () => { description: 'ach dto', requiredPoints: 1, imageUrl: 'update test', - locationType: ChallengeLocationDto.EngQuad, - achievementType: AchievementTypeDto.TotalChallengesOrJourneys, + locationType: ChallengeLocationDto.ENG_QUAD, + achievementType: AchievementTypeDto.TOTAL_CHALLENGES_OR_JOURNEYS, initialOrganizationId: orgId, }; diff --git a/server/src/challenge/challenge.dto.ts b/server/src/challenge/challenge.dto.ts index 98d46b40..3b3c2ea2 100644 --- a/server/src/challenge/challenge.dto.ts +++ b/server/src/challenge/challenge.dto.ts @@ -4,14 +4,14 @@ export interface CompletedChallengeDto { } export enum ChallengeLocationDto { - EngQuad = 'ENG_QUAD', - ArtsQuad = 'ARTS_QUAD', - AgQuad = 'AG_QUAD', - NorthCampus = 'NORTH_CAMPUS', - WestCampus = 'WEST_CAMPUS', - Collegetown = 'COLLEGETOWN', - IthacaCommons = 'ITHACA_COMMONS', - Any = 'ANY', + ENG_QUAD = 'ENG_QUAD', + ARTS_QUAD = 'ARTS_QUAD', + AG_QUAD = 'AG_QUAD', + NORTH_CAMPUS = 'NORTH_CAMPUS', + WEST_CAMPUS = 'WEST_CAMPUS', + COLLEGETOWN = 'COLLEGETOWN', + ITHACA_COMMONS = 'ITHACA_COMMONS', + ANY = 'ANY', } export interface ChallengeDto { diff --git a/server/src/challenge/challenge.e2e-spec.ts b/server/src/challenge/challenge.e2e-spec.ts index 2226e4d6..db83b5f8 100644 --- a/server/src/challenge/challenge.e2e-spec.ts +++ b/server/src/challenge/challenge.e2e-spec.ts @@ -135,7 +135,7 @@ describe('ChallengeModule E2E', () => { const chalDto: ChallengeDto = { id: '12345', name: 'test', - location: ChallengeLocationDto.EngQuad, + location: ChallengeLocationDto.ENG_QUAD, description: 'chal dto', points: 1, imageUrl: 'url', @@ -181,7 +181,7 @@ describe('ChallengeModule E2E', () => { const secondChalDto: ChallengeDto = { id: '123', name: 'test', - location: ChallengeLocationDto.Any, + location: ChallengeLocationDto.ANY, description: 'chal dto', points: 1, imageUrl: 'update test', @@ -210,7 +210,7 @@ describe('ChallengeModule E2E', () => { const chalDto: ChallengeDto = { id: chalID, name: 'test', - location: ChallengeLocationDto.EngQuad, + location: ChallengeLocationDto.ENG_QUAD, description: 'chal dto', points: 1, imageUrl: 'update test', diff --git a/server/src/organization/organization.e2e-spec.ts b/server/src/organization/organization.e2e-spec.ts index 260605f6..c90d2564 100644 --- a/server/src/organization/organization.e2e-spec.ts +++ b/server/src/organization/organization.e2e-spec.ts @@ -109,7 +109,7 @@ describe('OrganizationModule E2E', () => { exChal = (await challengeService.upsertChallengeFromDto(fullAbility, { id: '', linkedEventId: exEv.id, - location: ChallengeLocationDto.ArtsQuad, + location: ChallengeLocationDto.ARTS_QUAD, }))!; managerUser = await userService.register(