Skip to content
Open
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 @@ -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

Expand Down
149 changes: 149 additions & 0 deletions src/user/getUserSetRequests.test.ts
Original file line number Diff line number Diff line change
@@ -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 = `<html><body>${statusText}</body></html>`;

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,
};
92 changes: 92 additions & 0 deletions src/user/getUserSetRequests.ts
Original file line number Diff line number Diff line change
@@ -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<UserSetRequests> => {
const queryParams: Record<string, number | string> = {};
queryParams.u = payload.username;
if (
payload.requestListType !== null &&
payload.requestListType !== undefined
) {
queryParams.t = payload.requestListType;
}

const url = buildRequestUrl(
apiBaseUrl,
"/API_GetUserSetRequests.php",
authorization,
queryParams
);

const rawResponse = await call<GetUserSetRequestsResponse>({ url });

return serializeProperties(rawResponse, {
shouldCastToNumbers: [
"GameID",
"ConsoleID",
"TotalRequests",
"PointsForNext",
],
});
};
1 change: 1 addition & 0 deletions src/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from "./getUserProfile";
export * from "./getUserProgress";
export * from "./getUserRecentAchievements";
export * from "./getUserRecentlyPlayedGames";
export * from "./getUserSetRequests";
export * from "./getUsersIFollow";
export * from "./getUserSummary";
export * from "./getUserWantToPlayList";
Expand Down
11 changes: 11 additions & 0 deletions src/user/models/get-user-set-requests-response.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface GetUserSetRequestsResponse {
RequestedSets: Array<{
GameID: number | string;
Title: string;
ConsoleID: number | string;
ConsoleName: string;
ImageIcon: string;
}>;
TotalRequests: number;
PointsForNext: number;
}
3 changes: 3 additions & 0 deletions src/user/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ export * from "./get-user-profile-response.model";
export * from "./get-user-progress-response.model";
export * from "./get-user-recent-achievements-response.model";
export * from "./get-user-recently-played-games-response.model";
export * from "./get-user-set-requests-response.model";
export * from "./get-user-summary-response.model";
export * from "./get-user-want-to-play-list-response.model";
export * from "./get-users-i-follow-response.model";
export * from "./request-list-type.enum";
export * from "./user-awards.model";
export * from "./user-claims.model";
export * from "./user-claims-response.model";
Expand All @@ -27,6 +29,7 @@ export * from "./user-profile.model";
export * from "./user-progress.model";
export * from "./user-recent-achievement.model";
export * from "./user-recently-played-games.model";
export * from "./user-set-requests.model";
export * from "./user-summary.model";
export * from "./user-want-to-play-list.model";
export * from "./users-i-follow.model";
4 changes: 4 additions & 0 deletions src/user/models/request-list-type.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum RequestListType {
ActiveRequests = 0,
AllRequests = 1,
}
11 changes: 11 additions & 0 deletions src/user/models/user-set-requests.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface UserSetRequests {
requestedSets: Array<{
gameId: number;
title: string;
consoleId: number;
consoleName: string;
imageIcon: string;
}>;
totalRequests: number;
pointsForNext: number;
}