diff --git a/README.md b/README.md index 9917aeb..791186e 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Click the function names to open their complete docs on the docs site. ### Leaderboard - [`getLeaderboardEntries()`](https://api-docs.retroachievements.org/v1/get-leaderboard-entries.html) - Get a given leaderboard's entries. +- [`getUserGameLeaderboards()`](https://api-docs.retroachievements.org/v1/get-user-game-leaderboards.html) - Get a user's list of leaderboards for a given game. ### System diff --git a/src/leaderboard/getUserGameLeaderboards.test.ts b/src/leaderboard/getUserGameLeaderboards.test.ts new file mode 100644 index 0000000..3028a6d --- /dev/null +++ b/src/leaderboard/getUserGameLeaderboards.test.ts @@ -0,0 +1,88 @@ +/* eslint-disable sonarjs/no-duplicate-string */ + +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; + +import { apiBaseUrl } from "../utils/internal"; +import { buildAuthorization } from "../utils/public"; +import { getUserGameLeaderboards } from "./getUserGameLeaderboards"; +import type { GetUserGameLeaderboardsResponse } from "./models"; + +const server = setupServer(); + +describe("Function: getUserGameLeaderboards", () => { + // MSW Setup + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + + it("is defined #sanity", () => { + // ASSERT + expect(getUserGameLeaderboards).toBeDefined(); + }); + + it("given a game ID, retrieves the users leaderboards", async () => { + // ARRANGE + const authorization = buildAuthorization({ + username: "mockUserName", + webApiKey: "mockWebApiKey", + }); + + const mockResponse: GetUserGameLeaderboardsResponse = { + Count: 10, + Total: 64, + Results: [ + { + ID: 19_062, + RankAsc: true, + Title: "New Zealand One", + Description: "Complete New Zealand S1 in least time", + Format: "MILLISECS", + UserEntry: { + User: "zuliman92", + ULID: "00003EMFWR7XB8SDPEHB3K56ZQ", + Score: 12_620, + FormattedScore: "2:06.20", + Rank: 2, + DateUpdated: "2024-12-12T16:40:59+00:00", + }, + }, + ], + }; + + server.use( + http.get(`${apiBaseUrl}/API_GetUserGameLeaderboards.php`, () => + HttpResponse.json(mockResponse) + ) + ); + + // ACT + const response = await getUserGameLeaderboards(authorization, { + gameId: 1, + username: "zuliman92", + }); + + // ASSERT + expect(response).toEqual({ + count: 10, + total: 64, + results: [ + { + id: 19_062, + rankAsc: true, + title: "New Zealand One", + description: "Complete New Zealand S1 in least time", + format: "MILLISECS", + userEntry: { + user: "zuliman92", + ulid: "00003EMFWR7XB8SDPEHB3K56ZQ", + score: 12_620, + formattedScore: "2:06.20", + rank: 2, + dateUpdated: "2024-12-12T16:40:59+00:00", + }, + }, + ], + }); + }); +}); diff --git a/src/leaderboard/getUserGameLeaderboards.ts b/src/leaderboard/getUserGameLeaderboards.ts new file mode 100644 index 0000000..8f8d729 --- /dev/null +++ b/src/leaderboard/getUserGameLeaderboards.ts @@ -0,0 +1,85 @@ +import type { ID } from "../utils/internal"; +import { + apiBaseUrl, + buildRequestUrl, + call, + serializeProperties, +} from "../utils/internal"; +import type { AuthObject } from "../utils/public"; +import type { + GetUserGameLeaderboardsResponse, + UserGameLeaderboards, +} from "./models"; + +/** + * A call to this endpoint will retrieve a user's list of leaderboards for a given game, targeted by the game's ID. + * + * @param authorization An object containing your username and webApiKey. + * This can be constructed with `buildAuthorization()`. + * + * @param payload.gameId The target game ID. + * + * @param payload.offset Defaults to 0. The number of entries to skip. + * + * @param payload.count Defaults to 100, has a max of 500. + * + * @example + * ``` + * const gameLeaderboards = await getUserGameLeaderboards( + * authorization, + * { gameId: 14402 } + * ); + * ``` + * + * @returns An object containing user game leaderboard's. + * ```json + * { + * "count": 10, + * "total": 64, + * "results": [ + * { + * "id": 19062, + * "rankAsc": true, + * "title": "New Zealand One", + * "description": "Complete New Zealand S1 in least time", + * "format": "MILLISECS", + * "userEntry": { + * "user": "zuliman92", + * "ulid": "00003EMFWR7XB8SDPEHB3K56ZQ", + * "score": 12620, + * "formattedScore": "2:06.20", + * "rank": 2, + * "dateUpdated": "2024-12-12T16:40:59+00:00" + * } + * } + * ] + * } + * ``` + */ +export const getUserGameLeaderboards = async ( + authorization: AuthObject, + payload: { gameId: ID; username?: string; offset?: number; count?: number } +): Promise => { + const queryParams: Record = {}; + queryParams.i = payload.gameId; + if (payload?.username) { + queryParams.u = payload.username; + } + if (payload?.offset) { + queryParams.o = payload.offset; + } + if (payload?.count) { + queryParams.c = payload.count; + } + + const url = buildRequestUrl( + apiBaseUrl, + "/API_GetUserGameLeaderboards.php", + authorization, + queryParams + ); + + const rawResponse = await call({ url }); + + return serializeProperties(rawResponse); +}; diff --git a/src/leaderboard/models/get-user-game-leaderboards-response.model.ts b/src/leaderboard/models/get-user-game-leaderboards-response.model.ts new file mode 100644 index 0000000..1a5eb94 --- /dev/null +++ b/src/leaderboard/models/get-user-game-leaderboards-response.model.ts @@ -0,0 +1,19 @@ +export interface GetUserGameLeaderboardsResponse { + Count: number; + Total: number; + Results: Array<{ + ID: number; + RankAsc: boolean; + Title: string; + Description: string; + Format: string; + UserEntry: { + User: string; + ULID: string; + Score: number; + FormattedScore: string; + Rank: number; + DateUpdated: string; + }; + }>; +} diff --git a/src/leaderboard/models/index.ts b/src/leaderboard/models/index.ts index 2d99e66..190c102 100644 --- a/src/leaderboard/models/index.ts +++ b/src/leaderboard/models/index.ts @@ -1,2 +1,4 @@ export * from "./get-leaderboard-entries-response.model"; +export * from "./get-user-game-leaderboards-response.model"; export * from "./leaderboard-entries.model"; +export * from "./user-game-leaderboards.model"; diff --git a/src/leaderboard/models/user-game-leaderboards.model.ts b/src/leaderboard/models/user-game-leaderboards.model.ts new file mode 100644 index 0000000..37c9c34 --- /dev/null +++ b/src/leaderboard/models/user-game-leaderboards.model.ts @@ -0,0 +1,19 @@ +export interface UserGameLeaderboards { + count: number; + total: number; + results: Array<{ + id: number; + rankAsc: boolean; + title: string; + description: string; + format: string; + userEntry: { + user: string; + ulid: string; + score: number; + formattedScore: string; + rank: number; + dateUpdated: string; + }; + }>; +}