From 22cc54597c99f0cbcb174281815be49e69e8968f Mon Sep 17 00:00:00 2001 From: tsa96 Date: Sun, 17 Nov 2024 05:00:36 +0000 Subject: [PATCH] feat(back): allow reviewers to request unlimited maps in submission Closes #1071 --- apps/backend-e2e/src/maps.e2e-spec.ts | 47 +++++++++++++++++++ apps/backend/src/app/dto/decorators.ts | 9 +++- .../src/app/dto/queries/map-queries.dto.ts | 7 ++- .../src/app/modules/maps/maps.service.ts | 10 +++- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/apps/backend-e2e/src/maps.e2e-spec.ts b/apps/backend-e2e/src/maps.e2e-spec.ts index 4f6b1d805..317ec0d51 100644 --- a/apps/backend-e2e/src/maps.e2e-spec.ts +++ b/apps/backend-e2e/src/maps.e2e-spec.ts @@ -4155,6 +4155,53 @@ describe('Maps', () => { ); }); + it('should limit -1 take to 100 for non-reviewers', async () => { + // Magic number sorry, if additional maps are added above in setup + // this'll break. + const newMaps = await db + .createMaps(98, { + status: MapStatus.PUBLIC_TESTING + }) + .then((maps) => maps.map((m) => m.id)); + + await req.get({ + url: 'maps/submissions', + status: 200, + query: { take: -1 }, + validatePaged: { type: MapDto, returnCount: 100, totalCount: 101 }, + token: u1Token + }); + + await prisma.mMap.deleteMany({ where: { id: { in: newMaps } } }); + }); + + it('should not limit -1 take reviewers', async () => { + await prisma.user.update({ + where: { id: u1.id }, + data: { roles: Role.REVIEWER } + }); + + const newMaps = await db + .createMaps(97, { + status: MapStatus.PUBLIC_TESTING + }) + .then((maps) => maps.map((m) => m.id)); + + await req.get({ + url: 'maps/submissions', + status: 200, + query: { take: -1 }, + validatePaged: { type: MapDto, returnCount: 101, totalCount: 101 }, + token: u1Token + }); + + await prisma.mMap.deleteMany({ where: { id: { in: newMaps } } }); + await prisma.user.update({ + where: { id: u1.id }, + data: { roles: 0 } + }); + }); + it('should 401 when no access token is provided', () => req.unauthorizedTest('maps/submissions', 'get')); }); diff --git a/apps/backend/src/app/dto/decorators.ts b/apps/backend/src/app/dto/decorators.ts index b7c6e20a3..51ac78a3f 100644 --- a/apps/backend/src/app/dto/decorators.ts +++ b/apps/backend/src/app/dto/decorators.ts @@ -231,8 +231,13 @@ export function SkipQueryProperty(def: number): PropertyDecorator { * Decorator collection for take queries * @param def - The default `take` value * @param max - The maximum allowed `take` value + * @param min - The minimum allowed `take` value */ -export function TakeQueryProperty(def: number, max = 100): PropertyDecorator { +export function TakeQueryProperty( + def: number, + max = 100, + min = 0 +): PropertyDecorator { return applyDecorators( ApiPropertyOptional({ name: 'take', @@ -241,7 +246,7 @@ export function TakeQueryProperty(def: number, max = 100): PropertyDecorator { description: 'Take this many records' }), TypeDecorator(() => Number), - Min(0), + Min(min), IsInt(), Max(max), IsOptional() diff --git a/apps/backend/src/app/dto/queries/map-queries.dto.ts b/apps/backend/src/app/dto/queries/map-queries.dto.ts index 280648a5d..6e3c3018d 100644 --- a/apps/backend/src/app/dto/queries/map-queries.dto.ts +++ b/apps/backend/src/app/dto/queries/map-queries.dto.ts @@ -50,8 +50,8 @@ class MapsGetAllBaseQueryDto extends QueryDto { @SkipQueryProperty(0) skip = 0; - @TakeQueryProperty(100) - take = 100; + @TakeQueryProperty(10, 100) + take = 10; @StringQueryProperty({ description: 'Filter by partial map name match (contains)', @@ -133,6 +133,9 @@ export class MapsGetAllSubmissionQueryDto extends MapsGetAllBaseQueryDto implements MapsGetAllSubmissionQuery { + @TakeQueryProperty(10, 100, -1) + override readonly take = 10; + @ExpandQueryProperty([ 'leaderboards', 'info', diff --git a/apps/backend/src/app/modules/maps/maps.service.ts b/apps/backend/src/app/modules/maps/maps.service.ts index 280369dd4..141e98188 100644 --- a/apps/backend/src/app/modules/maps/maps.service.ts +++ b/apps/backend/src/app/modules/maps/maps.service.ts @@ -119,6 +119,7 @@ export class MapsService { ): Promise> { // Where const where: Prisma.MMapWhereInput = {}; + let take: number | undefined = query.take; if (query.search) where.name = { contains: query.search }; if (query.searchStartsWith) where.name = { startsWith: query.searchStartsWith }; @@ -187,6 +188,13 @@ export class MapsService { if (roles == null) throw new BadRequestException(); + // Allow unlimited take for reviewers and above + if (take === -1) { + take = Bitflags.has(roles, CombinedRoles.REVIEWER_AND_ABOVE) + ? undefined + : 100; + } + // Logic here is a nightmare, for a breakdown of permissions see // MapsService.getMapAndCheckReadAccess. const filter = query.filter; @@ -363,7 +371,7 @@ export class MapsService { include, orderBy: { createdAt: 'desc' }, skip: query.skip, - take: query.take + take }); if (incPB || incWR) {