From 18c12fb9aae12de66441bb98df8032a61b9822d1 Mon Sep 17 00:00:00 2001 From: Joshua Raphael <2054394+joshraphael@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:09:44 -0700 Subject: [PATCH] feat: add new getGameHashes() function (#114) --- README.md | 1 + src/console/getConsoleIds.test.ts | 5 +- src/feed/getRecentGameAwards.test.ts | 7 +- src/game/getGameHashes.test.ts | 77 +++++++++++++++++++ src/game/getGameHashes.ts | 66 ++++++++++++++++ src/game/index.ts | 1 + src/game/models/game-hashes.model.ts | 10 +++ .../models/get-game-hashes-response.model.ts | 10 +++ src/game/models/index.ts | 2 + 9 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 src/game/getGameHashes.test.ts create mode 100644 src/game/getGameHashes.ts create mode 100644 src/game/models/game-hashes.model.ts create mode 100644 src/game/models/get-game-hashes-response.model.ts diff --git a/README.md b/README.md index 7a75baa..4ee325b 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ Click the function names to open their complete docs on the docs site. - [`getGame()`](https://api-docs.retroachievements.org/v1/get-game.html) - Get basic metadata about a game. - [`getGameExtended()`](https://api-docs.retroachievements.org/v1/get-game-extended.html) - Get extended metadata about a game. +- [`getGameHashes()`](https://api-docs.retroachievements.org/v1/get-game-hashes.html) - Get a list of hashes linked to a game. - [`getAchievementCount()`](https://api-docs.retroachievements.org/v1/get-achievement-count.html) - Get the list of achievement IDs for a game. - [`getAchievementDistribution()`](https://api-docs.retroachievements.org/v1/get-achievement-distribution.html) - Get how many players have unlocked how many achievements for a game. - [`getGameRankAndScore()`](https://api-docs.retroachievements.org/v1/get-game-rank-and-score.html) - Get a list of either the latest masters or highest hardcore points earners for a game. diff --git a/src/console/getConsoleIds.test.ts b/src/console/getConsoleIds.test.ts index 323e17f..471754c 100644 --- a/src/console/getConsoleIds.test.ts +++ b/src/console/getConsoleIds.test.ts @@ -62,7 +62,10 @@ describe("Function: getConsoleIds", () => { ); // ACT - const response = await getConsoleIds(authorization); + const response = await getConsoleIds(authorization, { + shouldOnlyRetrieveActiveSystems: true, + shouldOnlyRetrieveGameSystems: true, + }); // ASSERT const expectedResponse: FetchedSystem[] = [ diff --git a/src/feed/getRecentGameAwards.test.ts b/src/feed/getRecentGameAwards.test.ts index 746738e..25b6b7e 100644 --- a/src/feed/getRecentGameAwards.test.ts +++ b/src/feed/getRecentGameAwards.test.ts @@ -49,7 +49,12 @@ describe("Function: getRecentGameAwards", () => { ); // ACT - const response = await getRecentGameAwards(authorization); + const response = await getRecentGameAwards(authorization, { + startDate: "2025-01-05", + offset: 10, + count: 10, + desiredAwardKinds: ["completed"], + }); const expectedResponse: RecentGameAwards = { count: 1, diff --git a/src/game/getGameHashes.test.ts b/src/game/getGameHashes.test.ts new file mode 100644 index 0000000..db64350 --- /dev/null +++ b/src/game/getGameHashes.test.ts @@ -0,0 +1,77 @@ +/* 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 { getGameHashes } from "./getGameHashes"; +import type { GetGameHashesResponse } from "./models"; + +const server = setupServer(); + +describe("Function: getGameExtended", () => { + // MSW Setup + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + + it("is defined #sanity", () => { + // ASSERT + expect(getGameHashes).toBeDefined(); + }); + + it("given a game ID, retrieves extended metadata about the game", async () => { + // ARRANGE + const authorization = buildAuthorization({ + username: "mockUserName", + webApiKey: "mockWebApiKey", + }); + + const mockResponse: GetGameHashesResponse = { + Results: [ + { + Name: "Sonic The Hedgehog (USA, Europe) (Ru) (NewGame).md", + MD5: "1b1d9ac862c387367e904036114c4825", + Labels: ["nointro", "rapatches"], + PatchUrl: + "https://github.com/RetroAchievements/RAPatches/raw/main/MD/Translation/Russian/1-Sonic1-Russian.zip", + }, + { + Name: "Sonic The Hedgehog (USA, Europe).md", + MD5: "1bc674be034e43c96b86487ac69d9293", + Labels: ["nointro"], + PatchUrl: null, + }, + ], + }; + + server.use( + http.get(`${apiBaseUrl}/API_GetGameHashes.php`, () => + HttpResponse.json(mockResponse) + ) + ); + + // ACT + const response = await getGameHashes(authorization, { gameId: 1 }); + + // ASSERT + expect(response).toEqual({ + results: [ + { + name: "Sonic The Hedgehog (USA, Europe) (Ru) (NewGame).md", + md5: "1b1d9ac862c387367e904036114c4825", + labels: ["nointro", "rapatches"], + patchUrl: + "https://github.com/RetroAchievements/RAPatches/raw/main/MD/Translation/Russian/1-Sonic1-Russian.zip", + }, + { + name: "Sonic The Hedgehog (USA, Europe).md", + md5: "1bc674be034e43c96b86487ac69d9293", + labels: ["nointro"], + patchUrl: null, + }, + ], + }); + }); +}); diff --git a/src/game/getGameHashes.ts b/src/game/getGameHashes.ts new file mode 100644 index 0000000..7e5e650 --- /dev/null +++ b/src/game/getGameHashes.ts @@ -0,0 +1,66 @@ +import type { ID } from "../utils/internal"; +import { + apiBaseUrl, + buildRequestUrl, + call, + serializeProperties, +} from "../utils/internal"; +import type { AuthObject } from "../utils/public"; +import type { GameHashes, GetGameHashesResponse } from "./models"; + +/** + * A call to this function will retrieve a list of hashes linked to a game. + * + * @param authorization An object containing your username and webApiKey. + * This can be constructed with `buildAuthorization()`. + * + * @param payload.gameId The unique game ID. If you are unsure, open the + * game's page on the RetroAchievements.org website. For example, Dragster's + * URL is https://retroachievements.org/game/14402. We can see from the + * URL that the game ID is "14402". + * + * @example + * ``` + * const game = await getGameHashes( + * authorization, + * { gameId: 14402 } + * ); + * ``` + * + * @returns An object containing a list of game hashes. + * ```json + * { + * "Results": [ + * { + * "MD5": "1b1d9ac862c387367e904036114c4825", + * "Name": "Sonic The Hedgehog (USA, Europe) (Ru) (NewGame).md", + * "Labels": ["nointro", "rapatches"], + * "PatchUrl": "https://github.com/RetroAchievements/RAPatches/raw/main/MD/Translation/Russian/1-Sonic1-Russian.zip" + * }, + * { + * "MD5": "1bc674be034e43c96b86487ac69d9293", + * "Name": "Sonic The Hedgehog (USA, Europe).md", + * "Labels": ["nointro"], + * "PatchUrl": null + * } + * ] + * } + * ``` + */ +export const getGameHashes = async ( + authorization: AuthObject, + payload: { gameId: ID } +): Promise => { + const { gameId } = payload; + + const url = buildRequestUrl( + apiBaseUrl, + "/API_GetGameHashes.php", + authorization, + { i: gameId } + ); + + const rawResponse = await call({ url }); + + return serializeProperties(rawResponse); +}; diff --git a/src/game/index.ts b/src/game/index.ts index f14a0ec..aa6246e 100644 --- a/src/game/index.ts +++ b/src/game/index.ts @@ -2,6 +2,7 @@ export * from "./getAchievementCount"; export * from "./getAchievementDistribution"; export * from "./getGame"; export * from "./getGameExtended"; +export * from "./getGameHashes"; export * from "./getGameRankAndScore"; export * from "./getGameRating"; export * from "./models"; diff --git a/src/game/models/game-hashes.model.ts b/src/game/models/game-hashes.model.ts new file mode 100644 index 0000000..55eea2d --- /dev/null +++ b/src/game/models/game-hashes.model.ts @@ -0,0 +1,10 @@ +interface GameHash { + md5: string; + name: string; + labels: string[]; + patchUrl: string; +} + +export interface GameHashes { + results: GameHash[]; +} diff --git a/src/game/models/get-game-hashes-response.model.ts b/src/game/models/get-game-hashes-response.model.ts new file mode 100644 index 0000000..0ae7391 --- /dev/null +++ b/src/game/models/get-game-hashes-response.model.ts @@ -0,0 +1,10 @@ +interface GameHashResult { + MD5: string; + Name: string; + Labels: string[]; + PatchUrl: string; +} + +export interface GetGameHashesResponse { + Results: GameHashResult[]; +} diff --git a/src/game/models/index.ts b/src/game/models/index.ts index d9f80b2..3649378 100644 --- a/src/game/models/index.ts +++ b/src/game/models/index.ts @@ -4,11 +4,13 @@ export * from "./game.model"; export * from "./game-extended.model"; export * from "./game-extended-achievement-entity.model"; export * from "./game-extended-claim-entity.model"; +export * from "./game-hashes.model"; export * from "./game-rank-and-score-entity.model"; export * from "./game-rating.model"; export * from "./get-achievement-count-response.model"; export * from "./get-achievement-distribution-response.model"; export * from "./get-game-extended-response.model"; +export * from "./get-game-hashes-response.model"; export * from "./get-game-rank-and-score-response.model"; export * from "./get-game-rating-response.model"; export * from "./get-game-response.model";