Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/map submission misc #1072

Merged
merged 8 commits into from
Nov 16, 2024
29 changes: 23 additions & 6 deletions apps/backend-e2e/src/maps-2.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
import {
ActivityType,
AdminActivityType,
CombinedMapStatuses,
MapStatuses,
Gamemode,
MapCreditType,
MapStatus,
Expand Down Expand Up @@ -1141,7 +1141,7 @@ describe('Maps Part 2', () => {
});

for (const status of Enum.values(MapStatus)) {
const shouldPass = CombinedMapStatuses.IN_SUBMISSION.includes(status);
const shouldPass = MapStatuses.IN_SUBMISSION.includes(status);
const expectedStatus = shouldPass ? 200 : 403;

it(`should ${expectedStatus} if the map is not in the ${MapStatus[status]} state`, async () => {
Expand Down Expand Up @@ -1178,7 +1178,9 @@ describe('Maps Part 2', () => {

beforeAll(async () => {
[[u1, token], u2, u3, map] = await Promise.all([
db.createAndLoginUser(),
db.createAndLoginUser({
data: { steamID: BigInt(Number.MAX_SAFE_INTEGER) * 2n }
}),
db.createUser(),
db.createUser(),
db.createMapWithFullLeaderboards() // Creates a bunch of ahop leaderboards
Expand Down Expand Up @@ -1288,12 +1290,27 @@ describe('Maps Part 2', () => {
token
}));

it('should respond with filtered runs given using the filterUserID param', async () => {
it('should respond with filtered runs given using the userIDs param', async () => {
const res = await req.get({
url: `maps/${map.id}/leaderboard`,
query: {
gamemode: Gamemode.AHOP,
userIDs: `${u1.id},${u3.id}`
},
validatePaged: { type: MinimalLeaderboardRunDto, count: 2 },
token
});

expect(res.body.data[0].userID).toBe(u1.id);
expect(res.body.data[1].userID).toBe(u3.id);
});

it('should respond with filtered runs given using the steamIDs param', async () => {
const res = await req.get({
url: `maps/${map.id}/leaderboard`,
query: {
gamemode: Gamemode.AHOP,
filterUserIDs: `${u1.id},${u3.id}`
steamIDs: `${u1.steamID},${u3.steamID}`
},
validatePaged: { type: MinimalLeaderboardRunDto, count: 2 },
token
Expand Down Expand Up @@ -1377,7 +1394,7 @@ describe('Maps Part 2', () => {
query: {
gamemode: Gamemode.AHOP,
filter: 'around',
filterUserIDs: u7.id
userIDs: u7.id
},
status: 200,
token: u7Token,
Expand Down
8 changes: 4 additions & 4 deletions apps/backend-e2e/src/maps.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import crypto from 'node:crypto';
import {
ActivityType,
Ban,
CombinedMapStatuses,
MapStatuses,
Gamemode as GM,
Gamemode,
MapCreditType,
Expand Down Expand Up @@ -984,7 +984,7 @@ describe('Maps', () => {

await db.createMap({
submitter: { connect: { id: user.id } },
status: CombinedMapStatuses.IN_SUBMISSION[1]
status: MapStatuses.IN_SUBMISSION[1]
});

await req.postAttach({
Expand Down Expand Up @@ -1012,7 +1012,7 @@ describe('Maps', () => {
});

describe('Permission checks', () => {
for (const status of CombinedMapStatuses.IN_SUBMISSION) {
for (const status of MapStatuses.IN_SUBMISSION) {
it(`should 403 if the user already has a map in ${MapStatus[status]} and is not a MAPPER`, async () => {
await db.createMap({
submitter: { connect: { id: user.id } },
Expand Down Expand Up @@ -2529,7 +2529,7 @@ describe('Maps', () => {
Promise.all([db.cleanup('mMap'), fileStore.deleteDirectory('maplist')])
);

for (const status of CombinedMapStatuses.IN_SUBMISSION) {
for (const status of MapStatuses.IN_SUBMISSION) {
it(`should allow the submitter to change most data during ${MapStatus[status]}`, async () => {
const map = await db.createMap({ ...createMapData, status });

Expand Down
10 changes: 8 additions & 2 deletions apps/backend/src/app/dto/queries/map-queries.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
IntQueryProperty,
SingleExpandQueryProperty,
SkipQueryProperty,
StringCsvQueryProperty,
StringQueryProperty,
TakeQueryProperty
} from '../decorators';
Expand Down Expand Up @@ -228,8 +229,13 @@ export class MapLeaderboardGetQueryDto
})
readonly filter?: MapRunsGetFilter;

@IntCsvQueryProperty({ description: 'List of users to limit results to' })
readonly filterUserIDs?: number[];
@IntCsvQueryProperty({ description: 'List of user IDs to limit results to' })
readonly userIDs?: number[];

@StringCsvQueryProperty({
description: 'List of user Steam IDs to limit results to'
})
readonly steamIDs?: string[];

@BooleanQueryProperty({
description: 'Whether to order by date or not (false for reverse)'
Expand Down
9 changes: 4 additions & 5 deletions apps/backend/src/app/dto/queries/user-queries.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
EnumQueryProperty,
ExpandQueryProperty,
IntCsvQueryProperty,
IntQueryProperty
IntQueryProperty,
StringCsvQueryProperty
} from '../decorators';
import { IsBigInt } from '../../validators';
import { PagedQueryDto } from './pagination.dto';
Expand Down Expand Up @@ -64,12 +65,10 @@ export class UsersGetAllQueryDto
@IsOptional()
readonly steamID?: string;

@IntCsvQueryProperty({
@StringCsvQueryProperty({
description: 'Filter by CSV list of Steam Community IDs',
example: '123135674,7987347263,98312287631',
bigint: true
example: '123135674,7987347263,98312287631'
})
@IsBigInt({ each: true })
@IsOptional()
readonly steamIDs?: string[];

Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/app/modules/maps/map-credits.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Prisma } from '@prisma/client';
import * as Bitflags from '@momentum/bitflags';
import {
ActivityType,
CombinedMapStatuses,
MapStatuses,
CombinedRoles,
MapCreditsGetExpand,
MapCreditType
Expand Down Expand Up @@ -114,7 +114,7 @@ export class MapCreditsService {
throw new ForbiddenException('User is not the submitter of this map');
}

if (!CombinedMapStatuses.IN_SUBMISSION.includes(map.status)) {
if (!MapStatuses.IN_SUBMISSION.includes(map.status)) {
throw new ForbiddenException('Cannot change map in its current state');
}
}
Expand Down
6 changes: 2 additions & 4 deletions apps/backend/src/app/modules/maps/map-image.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@nestjs/common';
import {
AdminActivityType,
CombinedMapStatuses,
MapStatuses,
CombinedRoles,
imgLargePath,
imgMediumPath,
Expand Down Expand Up @@ -66,9 +66,7 @@ export class MapImageService {

const isMod = Bitflags.has(user.roles, CombinedRoles.MOD_OR_ADMIN);
const isSubmitter = map.submitterID === userID;
const isInSubmission = CombinedMapStatuses.IN_SUBMISSION.includes(
map.status
);
const isInSubmission = MapStatuses.IN_SUBMISSION.includes(map.status);

if (!isMod) {
if (!isSubmitter)
Expand Down
7 changes: 6 additions & 1 deletion apps/backend/src/app/modules/maps/maps.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,13 @@ export class MapsController {
description:
"When the filtering by 'friends', and the user doesn't have any Steam friends"
})
@ApiConflictResponse({
description:
"When filtering by 'friends', the user's friend list is private"
})
@ApiServiceUnavailableResponse({
description: "Steam fails to return the user's friends list (Tuesdays lol)"
description:
"When filtering by 'friends', and Steam fails to return the user's friends list (Tuesdays lol)"
})
getLeaderboards(
@Param('mapID', ParseIntSafePipe) mapID: number,
Expand Down
16 changes: 8 additions & 8 deletions apps/backend/src/app/modules/maps/maps.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
AdminActivityType,
Ban,
bspPath,
CombinedMapStatuses,
MapStatuses,
CombinedRoles,
FlatMapList,
LeaderboardType,
Expand Down Expand Up @@ -204,8 +204,8 @@ export class MapsService {
if (Bitflags.has(CombinedRoles.MOD_OR_ADMIN, roles)) {
where.status = {
in: filter
? intersection(filter, CombinedMapStatuses.IN_SUBMISSION)
: CombinedMapStatuses.IN_SUBMISSION
? intersection(filter, MapStatuses.IN_SUBMISSION)
: MapStatuses.IN_SUBMISSION
};
} else if (Bitflags.has(Role.REVIEWER, roles)) {
if (filter?.length > 0) {
Expand Down Expand Up @@ -639,7 +639,7 @@ export class MapsService {
throw new ForbiddenException('User is banned from map submission');
}

if (!CombinedMapStatuses.IN_SUBMISSION.includes(map.status)) {
if (!MapStatuses.IN_SUBMISSION.includes(map.status)) {
throw new ForbiddenException('Map does not allow editing');
}

Expand Down Expand Up @@ -759,7 +759,7 @@ export class MapsService {
if (
!Bitflags.has(user.roles, CombinedRoles.MAPPER_AND_ABOVE) &&
user.submittedMaps.some((map) =>
CombinedMapStatuses.IN_SUBMISSION.includes(map.status)
MapStatuses.IN_SUBMISSION.includes(map.status)
)
) {
throw new ForbiddenException(
Expand Down Expand Up @@ -1022,7 +1022,7 @@ export class MapsService {
if (userID !== map.submitterID)
throw new ForbiddenException('User is not the map submitter');

if (!CombinedMapStatuses.IN_SUBMISSION.includes(map.status))
if (!MapStatuses.IN_SUBMISSION.includes(map.status))
throw new ForbiddenException('Map can only be edited during submission');

// Force the submitter to keep their suggestions in sync with their zones.
Expand Down Expand Up @@ -1246,7 +1246,7 @@ export class MapsService {

const numInSubmissionStatuses = intersection(
[newStatus, oldStatus],
CombinedMapStatuses.IN_SUBMISSION
MapStatuses.IN_SUBMISSION
).length;

// If going from submission -> submission just update submission list,
Expand Down Expand Up @@ -1615,7 +1615,7 @@ export class MapsService {
);

await this.mapListService.updateMapList(
CombinedMapStatuses.IN_SUBMISSION.includes(map.status)
MapStatuses.IN_SUBMISSION.includes(map.status)
? FlatMapList.SUBMISSION
: FlatMapList.APPROVED
);
Expand Down
24 changes: 11 additions & 13 deletions apps/backend/src/app/modules/runs/leaderboard-runs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Inject,
Injectable,
NotFoundException,
ServiceUnavailableException,
UnauthorizedException
} from '@nestjs/common';
import { Prisma } from '@prisma/client';
Expand Down Expand Up @@ -76,8 +75,12 @@ export class LeaderboardRunsService {
style: query.style
};

if (query.filterUserIDs) {
where.userID = { in: query.filterUserIDs };
if (query.userIDs) {
where.userID = { in: query.userIDs };
}

if (query.steamIDs) {
where.user = { steamID: { in: query.steamIDs.map(BigInt) } };
}

const select = {
Expand Down Expand Up @@ -107,10 +110,6 @@ export class LeaderboardRunsService {
}
};

// if (query.filterUserIDs) {
// where.userID = { in: query.filterUserIDs };
// }

const userRun = await this.db.leaderboardRun.findUnique({
where: whereAround
});
Expand All @@ -125,16 +124,15 @@ export class LeaderboardRunsService {
} else if (query.filter?.[0] === 'friends') {
// Regular skip/take should work fine here.

const steamFriends = await this.steamService
.getSteamFriends(loggedInUserSteamID)
.catch(() => {
throw new ServiceUnavailableException();
});
// Fetch Steam friends, leave errors uncaught, this function will throw
// an appropriate response.
const steamFriends =
await this.steamService.getSteamFriends(loggedInUserSteamID);

if (steamFriends.length === 0)
throw new GoneException('No friends detected :(');

// Doing this with a window function is gonna be fun...
// Overrides filterSteamIDs if exists
where.user = {
steamID: { in: steamFriends.map((item) => BigInt(item.steamid)) }
};
Expand Down
Loading