Skip to content

Commit 8bc23b7

Browse files
authored
fix: remove user from XP leaderboard if opt-out, reset or deleted (@fehmer) (monkeytypegame#6290)
Ensure user is removed from the weekly XP leaderboards if they either `opt-out`, `reset` their account or `delete` their account.
1 parent 0dd044b commit 8bc23b7

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

backend/__tests__/api/controllers/user.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import _ from "lodash";
2929
import { MonkeyMail, UserStreak } from "@monkeytype/contracts/schemas/users";
3030
import { isFirebaseError } from "../../../src/utils/error";
3131
import { LeaderboardEntry } from "@monkeytype/contracts/schemas/leaderboards";
32+
import * as WeeklyXpLeaderboard from "../../../src/services/weekly-xp-leaderboard";
3233

3334
const mockApp = request(app);
3435
const configuration = Configuration.getCachedConfiguration();
@@ -600,6 +601,10 @@ describe("user controller test", () => {
600601
DailyLeaderboards,
601602
"purgeUserFromDailyLeaderboards"
602603
);
604+
const purgeUserFromXpLeaderboardsMock = vi.spyOn(
605+
WeeklyXpLeaderboard,
606+
"purgeUserFromXpLeaderboards"
607+
);
603608
const blocklistAddMock = vi.spyOn(BlocklistDal, "add");
604609

605610
beforeEach(() => {
@@ -611,6 +616,7 @@ describe("user controller test", () => {
611616
deleteAllPresetsMock,
612617
deleteConfigMock,
613618
purgeUserFromDailyLeaderboardsMock,
619+
purgeUserFromXpLeaderboardsMock,
614620
].forEach((it) => it.mockResolvedValue(undefined));
615621

616622
deleteAllResultMock.mockResolvedValue({} as any);
@@ -627,6 +633,7 @@ describe("user controller test", () => {
627633
deleteAllApeKeysMock,
628634
deleteAllPresetsMock,
629635
purgeUserFromDailyLeaderboardsMock,
636+
purgeUserFromXpLeaderboardsMock,
630637
].forEach((it) => it.mockReset());
631638
});
632639

@@ -661,6 +668,10 @@ describe("user controller test", () => {
661668
uid,
662669
(await configuration).dailyLeaderboards
663670
);
671+
expect(purgeUserFromXpLeaderboardsMock).toHaveBeenCalledWith(
672+
uid,
673+
(await configuration).leaderboards.weeklyXp
674+
);
664675
});
665676
it("should delete user without adding to blocklist if not banned", async () => {
666677
//GIVEN
@@ -692,6 +703,10 @@ describe("user controller test", () => {
692703
uid,
693704
(await configuration).dailyLeaderboards
694705
);
706+
expect(purgeUserFromXpLeaderboardsMock).toHaveBeenCalledWith(
707+
uid,
708+
(await configuration).leaderboards.weeklyXp
709+
);
695710
});
696711
});
697712
describe("resetUser", () => {
@@ -705,6 +720,10 @@ describe("user controller test", () => {
705720
DailyLeaderboards,
706721
"purgeUserFromDailyLeaderboards"
707722
);
723+
const purgeUserFromXpLeaderboardsMock = vi.spyOn(
724+
WeeklyXpLeaderboard,
725+
"purgeUserFromXpLeaderboards"
726+
);
708727

709728
const unlinkDiscordMock = vi.spyOn(GeorgeQueue, "unlinkDiscord");
710729
const addImportantLogMock = vi.spyOn(LogDal, "addImportantLog");
@@ -723,6 +742,7 @@ describe("user controller test", () => {
723742
deleteAllResultsMock,
724743
deleteConfigMock,
725744
purgeUserFromDailyLeaderboardsMock,
745+
purgeUserFromXpLeaderboardsMock,
726746
unlinkDiscordMock,
727747
addImportantLogMock,
728748
].forEach((it) => it.mockReset());
@@ -754,6 +774,10 @@ describe("user controller test", () => {
754774
uid,
755775
(await Configuration.getLiveConfiguration()).dailyLeaderboards
756776
);
777+
expect(purgeUserFromXpLeaderboardsMock).toHaveBeenCalledWith(
778+
uid,
779+
(await configuration).leaderboards.weeklyXp
780+
);
757781
expect(unlinkDiscordMock).not.toHaveBeenCalled();
758782
expect(addImportantLogMock).toHaveBeenCalledWith(
759783
"user_reset",

backend/src/api/controllers/user.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { deleteConfig } from "../../dal/config";
2121
import { verify } from "../../utils/captcha";
2222
import * as LeaderboardsDAL from "../../dal/leaderboards";
2323
import { purgeUserFromDailyLeaderboards } from "../../utils/daily-leaderboards";
24+
import { purgeUserFromXpLeaderboards } from "../../services/weekly-xp-leaderboard";
2425
import { v4 as uuidv4 } from "uuid";
2526
import { ObjectId } from "mongodb";
2627
import * as ReportDAL from "../../dal/report";
@@ -273,6 +274,10 @@ export async function deleteUser(req: MonkeyRequest): Promise<MonkeyResponse> {
273274
uid,
274275
req.ctx.configuration.dailyLeaderboards
275276
),
277+
purgeUserFromXpLeaderboards(
278+
uid,
279+
req.ctx.configuration.leaderboards.weeklyXp
280+
),
276281
]);
277282

278283
//delete user from
@@ -310,6 +315,10 @@ export async function resetUser(req: MonkeyRequest): Promise<MonkeyResponse> {
310315
uid,
311316
req.ctx.configuration.dailyLeaderboards
312317
),
318+
purgeUserFromXpLeaderboards(
319+
uid,
320+
req.ctx.configuration.leaderboards.weeklyXp
321+
),
313322
];
314323

315324
if (userInfo.discordId !== undefined && userInfo.discordId !== "") {
@@ -383,6 +392,10 @@ export async function optOutOfLeaderboards(
383392
uid,
384393
req.ctx.configuration.dailyLeaderboards
385394
);
395+
await purgeUserFromXpLeaderboards(
396+
uid,
397+
req.ctx.configuration.leaderboards.weeklyXp
398+
);
386399
void addImportantLog("user_opted_out_of_leaderboards", "", uid);
387400

388401
return new MonkeyResponse("User opted out of leaderboards", null);

backend/src/services/weekly-xp-leaderboard.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,21 @@ export function get(
252252

253253
return new WeeklyXpLeaderboard(customTimestamp);
254254
}
255+
256+
export async function purgeUserFromXpLeaderboards(
257+
uid: string,
258+
weeklyXpLeaderboardConfig: Configuration["leaderboards"]["weeklyXp"]
259+
): Promise<void> {
260+
const connection = RedisClient.getConnection();
261+
if (!connection || !weeklyXpLeaderboardConfig.enabled) {
262+
return;
263+
}
264+
265+
// @ts-expect-error we are doing some weird file to function mapping, thats why its any
266+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
267+
await connection.purgeResults(
268+
0,
269+
uid,
270+
weeklyXpLeaderboardLeaderboardNamespace
271+
);
272+
}

0 commit comments

Comments
 (0)