Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
88 changes: 88 additions & 0 deletions src/leaderboard/getUserGameLeaderboards.test.ts
Original file line number Diff line number Diff line change
@@ -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",
},
},
],
});
});
});
85 changes: 85 additions & 0 deletions src/leaderboard/getUserGameLeaderboards.ts
Original file line number Diff line number Diff line change
@@ -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<UserGameLeaderboards> => {
const queryParams: Record<string, any> = {};
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<GetUserGameLeaderboardsResponse>({ url });

return serializeProperties(rawResponse);
};
Original file line number Diff line number Diff line change
@@ -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;
};
}>;
}
2 changes: 2 additions & 0 deletions src/leaderboard/models/index.ts
Original file line number Diff line number Diff line change
@@ -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";
19 changes: 19 additions & 0 deletions src/leaderboard/models/user-game-leaderboards.model.ts
Original file line number Diff line number Diff line change
@@ -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;
};
}>;
}