diff --git a/README.md b/README.md index fc9b44e..bdf4c34 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Click the function names to open their complete docs on the docs site. - [`getUserCompletedGames()`](https://api-docs.retroachievements.org/v1/get-user-completed-games.html) - Deprecated function. Get hardcore and softcore completion metadata about games a user has played. - [`getUserWantToPlayList()`](https://api-docs.retroachievements.org/v1/get-user-want-to-play-list.html) - Get a user's "Want to Play Games" list. - [`getUsersIFollow()`](https://api-docs.retroachievements.org/v1/get-users-i-follow.html) - Get the caller's "Following" users list. +- [`getUserSetRequests()`](https://api-docs.retroachievements.org/v1/get-user-set-requests.html) - Get a given user's set requests, maximum total requests, and points until next request. ### Game diff --git a/src/user/getUserSetRequests.test.ts b/src/user/getUserSetRequests.test.ts new file mode 100644 index 0000000..7e353ea --- /dev/null +++ b/src/user/getUserSetRequests.test.ts @@ -0,0 +1,149 @@ +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; + +import { apiBaseUrl } from "../utils/internal"; +import { buildAuthorization } from "../utils/public"; +import { getUserSetRequests } from "./getUserSetRequests"; +import type { GetUserSetRequestsResponse, UserSetRequests } from "./models"; +import { RequestListType } from "./models"; + +const server = setupServer(); + +describe("Function: getUserSetRequests", () => { + // MSW Setup + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + + it("is defined #sanity", () => { + // ASSERT + expect(getUserSetRequests).toBeDefined(); + }); + + it("using defaults, retrieves the list of set requests of the given user", async () => { + // ARRANGE + const authorization = buildAuthorization({ + username: "mockUserName", + webApiKey: "mockWebApiKey", + }); + + const mockResponse = mockGetUserSetRequestsResponse; + + server.use( + http.get(`${apiBaseUrl}/API_GetUserSetRequests.php`, (info) => { + const url = new URL(info.request.url); + expect(url.searchParams.get("u")).toEqual(mockOtherUsername); + expect(url.searchParams.has("t")).toBeFalsy(); + return HttpResponse.json(mockResponse); + }) + ); + + // ACT + const response = await getUserSetRequests(authorization, { + username: mockOtherUsername, + }); + expect(response).toEqual(mockUserSetRequestsValue); + }); + + it.each([ + { requestListType: RequestListType.ActiveRequests }, + { requestListType: RequestListType.AllRequests }, + ])( + "calls the 'User Set Requests' endpoint with a given request list type ($requestListType)", + async ({ requestListType: expectedRequestListType }) => { + // ARRANGE + const authorization = buildAuthorization({ + username: "mockUserName", + webApiKey: "mockWebApiKey", + }); + + server.use( + http.get(`${apiBaseUrl}/API_GetUserSetRequests.php`, (info) => { + const url = new URL(info.request.url); + expect(url.searchParams.get("u")).toEqual(mockOtherUsername); + expect(url.searchParams.get("t")).toEqual( + String(expectedRequestListType) + ); + return HttpResponse.json(mockGetUserSetRequestsResponse); + }) + ); + + // ACT + await getUserSetRequests(authorization, { + username: mockOtherUsername, + requestListType: expectedRequestListType, + }); + } + ); + + it.each([ + { status: 503, statusText: "The API is currently down" }, + { status: 422, statusText: "HTTP Error: Status 422 Unprocessable Entity" }, + ])( + "given the API returns a $status, throws an error", + async ({ status, statusText }) => { + // ARRANGE + const authorization = buildAuthorization({ + username: "mockUserName", + webApiKey: "mockWebApiKey", + }); + + const mockResponse = `
${statusText}`; + + server.use( + http.get(`${apiBaseUrl}/API_GetUserSetRequests.php`, () => + HttpResponse.json(mockResponse, { status, statusText }) + ) + ); + + // ASSERT + await expect( + getUserSetRequests(authorization, { username: mockOtherUsername }) + ).rejects.toThrow(); + } + ); +}); + +const mockOtherUsername = "otherMockUser"; + +const mockGetUserSetRequestsResponse: GetUserSetRequestsResponse = { + RequestedSets: [ + { + GameID: 8149, + Title: "Example Set 1", + ConsoleID: 0, + ConsoleName: "Example Console", + ImageIcon: "/Images/000001.png", + }, + { + GameID: 9001, + Title: "Example Set 2", + ConsoleID: 2, + ConsoleName: "Example Console 2", + ImageIcon: "/Images/000002.png", + }, + ], + TotalRequests: 5, + PointsForNext: 5000, +}; + +const mockUserSetRequestsValue: UserSetRequests = { + requestedSets: [ + { + gameId: 8149, + title: "Example Set 1", + consoleId: 0, + consoleName: "Example Console", + imageIcon: "/Images/000001.png", + }, + { + gameId: 9001, + title: "Example Set 2", + consoleId: 2, + consoleName: "Example Console 2", + imageIcon: "/Images/000002.png", + }, + ], + totalRequests: 5, + pointsForNext: 5000, +}; diff --git a/src/user/getUserSetRequests.ts b/src/user/getUserSetRequests.ts new file mode 100644 index 0000000..9b13549 --- /dev/null +++ b/src/user/getUserSetRequests.ts @@ -0,0 +1,92 @@ +import { + apiBaseUrl, + buildRequestUrl, + call, + serializeProperties, +} from "../utils/internal"; +import type { AuthObject } from "../utils/public"; +import type { + GetUserSetRequestsResponse, + RequestListType, + UserSetRequests, +} from "./models"; + +/** + * A call to this function will retrieve a given user's set requests. + * + * @param authorization An object containing your username and webApiKey. + * This can be constructed with `buildAuthorization()`. + * + * @param payload.username The user for which to retrieve the set requests + * for. + * + * @param payload.requestListType An optional parameter to filter set requests + * by their current status. If omittted, the API will return only active + * requests. + * + * @example + * ``` + * const userSetRequests = await getUserSetRequests(authorization, { + * username: "ExampleUser" + * }); + * ``` + * + * @returns An object containing a list of requested sets that the + * given user made. + * ```json + * { + * "requestedSets": [ + * { + * "gameId": 8149, + * "title": "Example Set 1", + * "consoleId": 0, + * "consoleName": "Example Console", + * "imageIcon": "/Images/000001.png" + * }, + * { + * "gameId": 9001, + * "title": "Example Set 2", + * "consoleId": 2, + * "consoleName": "Example Console 2", + * "imageIcon": "/Images/000002.png" + * } + * ], + * "totalRequests": 5, + * "pointsForNext": 5000 + * } + * ``` + * + * @throws If the API was given invalid parameters (422) or if the + * API is currently down (503). + */ +export const getUserSetRequests = async ( + authorization: AuthObject, + payload: { username: string; requestListType?: RequestListType } +): Promise