Skip to content

Commit 56f892c

Browse files
authored
feat: add getUserGameLeaderboards() (#122)
1 parent 27d8d34 commit 56f892c

File tree

6 files changed

+214
-0
lines changed

6 files changed

+214
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ Click the function names to open their complete docs on the docs site.
104104
### Leaderboard
105105

106106
- [`getLeaderboardEntries()`](https://api-docs.retroachievements.org/v1/get-leaderboard-entries.html) - Get a given leaderboard's entries.
107+
- [`getUserGameLeaderboards()`](https://api-docs.retroachievements.org/v1/get-user-game-leaderboards.html) - Get a user's list of leaderboards for a given game.
107108

108109
### System
109110

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/* eslint-disable sonarjs/no-duplicate-string */
2+
3+
import { http, HttpResponse } from "msw";
4+
import { setupServer } from "msw/node";
5+
6+
import { apiBaseUrl } from "../utils/internal";
7+
import { buildAuthorization } from "../utils/public";
8+
import { getUserGameLeaderboards } from "./getUserGameLeaderboards";
9+
import type { GetUserGameLeaderboardsResponse } from "./models";
10+
11+
const server = setupServer();
12+
13+
describe("Function: getUserGameLeaderboards", () => {
14+
// MSW Setup
15+
beforeAll(() => server.listen());
16+
afterEach(() => server.resetHandlers());
17+
afterAll(() => server.close());
18+
19+
it("is defined #sanity", () => {
20+
// ASSERT
21+
expect(getUserGameLeaderboards).toBeDefined();
22+
});
23+
24+
it("given a game ID, retrieves the users leaderboards", async () => {
25+
// ARRANGE
26+
const authorization = buildAuthorization({
27+
username: "mockUserName",
28+
webApiKey: "mockWebApiKey",
29+
});
30+
31+
const mockResponse: GetUserGameLeaderboardsResponse = {
32+
Count: 10,
33+
Total: 64,
34+
Results: [
35+
{
36+
ID: 19_062,
37+
RankAsc: true,
38+
Title: "New Zealand One",
39+
Description: "Complete New Zealand S1 in least time",
40+
Format: "MILLISECS",
41+
UserEntry: {
42+
User: "zuliman92",
43+
ULID: "00003EMFWR7XB8SDPEHB3K56ZQ",
44+
Score: 12_620,
45+
FormattedScore: "2:06.20",
46+
Rank: 2,
47+
DateUpdated: "2024-12-12T16:40:59+00:00",
48+
},
49+
},
50+
],
51+
};
52+
53+
server.use(
54+
http.get(`${apiBaseUrl}/API_GetUserGameLeaderboards.php`, () =>
55+
HttpResponse.json(mockResponse)
56+
)
57+
);
58+
59+
// ACT
60+
const response = await getUserGameLeaderboards(authorization, {
61+
gameId: 1,
62+
username: "zuliman92",
63+
});
64+
65+
// ASSERT
66+
expect(response).toEqual({
67+
count: 10,
68+
total: 64,
69+
results: [
70+
{
71+
id: 19_062,
72+
rankAsc: true,
73+
title: "New Zealand One",
74+
description: "Complete New Zealand S1 in least time",
75+
format: "MILLISECS",
76+
userEntry: {
77+
user: "zuliman92",
78+
ulid: "00003EMFWR7XB8SDPEHB3K56ZQ",
79+
score: 12_620,
80+
formattedScore: "2:06.20",
81+
rank: 2,
82+
dateUpdated: "2024-12-12T16:40:59+00:00",
83+
},
84+
},
85+
],
86+
});
87+
});
88+
});
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import type { ID } from "../utils/internal";
2+
import {
3+
apiBaseUrl,
4+
buildRequestUrl,
5+
call,
6+
serializeProperties,
7+
} from "../utils/internal";
8+
import type { AuthObject } from "../utils/public";
9+
import type {
10+
GetUserGameLeaderboardsResponse,
11+
UserGameLeaderboards,
12+
} from "./models";
13+
14+
/**
15+
* A call to this endpoint will retrieve a user's list of leaderboards for a given game, targeted by the game's ID.
16+
*
17+
* @param authorization An object containing your username and webApiKey.
18+
* This can be constructed with `buildAuthorization()`.
19+
*
20+
* @param payload.gameId The target game ID.
21+
*
22+
* @param payload.offset Defaults to 0. The number of entries to skip.
23+
*
24+
* @param payload.count Defaults to 100, has a max of 500.
25+
*
26+
* @example
27+
* ```
28+
* const gameLeaderboards = await getUserGameLeaderboards(
29+
* authorization,
30+
* { gameId: 14402 }
31+
* );
32+
* ```
33+
*
34+
* @returns An object containing user game leaderboard's.
35+
* ```json
36+
* {
37+
* "count": 10,
38+
* "total": 64,
39+
* "results": [
40+
* {
41+
* "id": 19062,
42+
* "rankAsc": true,
43+
* "title": "New Zealand One",
44+
* "description": "Complete New Zealand S1 in least time",
45+
* "format": "MILLISECS",
46+
* "userEntry": {
47+
* "user": "zuliman92",
48+
* "ulid": "00003EMFWR7XB8SDPEHB3K56ZQ",
49+
* "score": 12620,
50+
* "formattedScore": "2:06.20",
51+
* "rank": 2,
52+
* "dateUpdated": "2024-12-12T16:40:59+00:00"
53+
* }
54+
* }
55+
* ]
56+
* }
57+
* ```
58+
*/
59+
export const getUserGameLeaderboards = async (
60+
authorization: AuthObject,
61+
payload: { gameId: ID; username?: string; offset?: number; count?: number }
62+
): Promise<UserGameLeaderboards> => {
63+
const queryParams: Record<string, any> = {};
64+
queryParams.i = payload.gameId;
65+
if (payload?.username) {
66+
queryParams.u = payload.username;
67+
}
68+
if (payload?.offset) {
69+
queryParams.o = payload.offset;
70+
}
71+
if (payload?.count) {
72+
queryParams.c = payload.count;
73+
}
74+
75+
const url = buildRequestUrl(
76+
apiBaseUrl,
77+
"/API_GetUserGameLeaderboards.php",
78+
authorization,
79+
queryParams
80+
);
81+
82+
const rawResponse = await call<GetUserGameLeaderboardsResponse>({ url });
83+
84+
return serializeProperties(rawResponse);
85+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export interface GetUserGameLeaderboardsResponse {
2+
Count: number;
3+
Total: number;
4+
Results: Array<{
5+
ID: number;
6+
RankAsc: boolean;
7+
Title: string;
8+
Description: string;
9+
Format: string;
10+
UserEntry: {
11+
User: string;
12+
ULID: string;
13+
Score: number;
14+
FormattedScore: string;
15+
Rank: number;
16+
DateUpdated: string;
17+
};
18+
}>;
19+
}

src/leaderboard/models/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
export * from "./get-leaderboard-entries-response.model";
2+
export * from "./get-user-game-leaderboards-response.model";
23
export * from "./leaderboard-entries.model";
4+
export * from "./user-game-leaderboards.model";
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export interface UserGameLeaderboards {
2+
count: number;
3+
total: number;
4+
results: Array<{
5+
id: number;
6+
rankAsc: boolean;
7+
title: string;
8+
description: string;
9+
format: string;
10+
userEntry: {
11+
user: string;
12+
ulid: string;
13+
score: number;
14+
formattedScore: string;
15+
rank: number;
16+
dateUpdated: string;
17+
};
18+
}>;
19+
}

0 commit comments

Comments
 (0)