From f70e0480766688e90e160d821f8f2d6651b6daf7 Mon Sep 17 00:00:00 2001 From: wescopeland Date: Tue, 23 May 2023 19:40:05 -0400 Subject: [PATCH] feat: add getUserAwards function --- README.md | 1 + docs/.vitepress/config.js | 8 ++ docs/v1/users/get-user-awards.md | 49 +++++++++++ docs/v1/users/get-user-recent-achievements.md | 2 +- src/user/getUserAwards.test.ts | 83 +++++++++++++++++++ src/user/getUserAwards.ts | 70 ++++++++++++++++ src/user/getUserRecentAchievements.ts | 2 +- src/user/index.ts | 1 + src/user/models/award-type.model.ts | 7 ++ .../models/get-user-awards-response.model.ts | 23 +++++ src/user/models/index.ts | 3 + src/user/models/user-awards.model.ts | 23 +++++ src/utils/internal/serializeProperties.ts | 3 + 13 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 docs/v1/users/get-user-awards.md create mode 100644 src/user/getUserAwards.test.ts create mode 100644 src/user/getUserAwards.ts create mode 100644 src/user/models/award-type.model.ts create mode 100644 src/user/models/get-user-awards-response.model.ts create mode 100644 src/user/models/user-awards.model.ts diff --git a/README.md b/README.md index 852ed5a..c38e007 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ Click the function names to open their complete docs on the docs site. - [`getAchievementsEarnedBetween()`](https://retroachievements-api-js.vercel.app/v1/users/get-achievements-earned-between.html) - Get a list of achievements earned by a user between two dates. - [`getAchievementsEarnedOnDay()`](https://retroachievements-api-js.vercel.app/v1/users/get-achievements-earned-on-day.html) - Get a list of achievements earned by a user on a given date. - [`getGameInfoAndUserProgress()`](https://retroachievements-api-js.vercel.app/v1/users/get-game-info-and-user-progress.html) - Get metadata about a game as well as a user's progress on that game. +- [`getUserAwards()`](https://retroachievements-api-js.vercel.app/v1/users/get-user-awards.html) - Get a list of a user's site awards/badges. - [`getUserClaims()`](https://retroachievements-api-js.vercel.app/v1/users/get-user-claims.html) - Get a list of set claims made over the lifetime of a user. - [`getUserCompletedGames()`](https://retroachievements-api-js.vercel.app/v1/users/get-user-completed-games.html) - Get hardcore and softcore completion metadata about games a user has played. - [`getUserGameRankAndScore()`](https://retroachievements-api-js.vercel.app/v1/users/get-user-game-rank-and-score.html) - Get metadata about how a user has performed on a given game. diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 2a720f2..9a497d4 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -61,6 +61,10 @@ export default { text: "Users", collapsible: true, items: [ + { + text: "Recent Achievements", + link: "/v1/users/get-user-recent-achievements" + }, { text: "Achievements Earned Between Days", link: "/v1/users/get-achievements-earned-between" @@ -73,6 +77,10 @@ export default { text: "Progress for Game with Game Info", link: "/v1/users/get-game-info-and-user-progress" }, + { + text: "Awards / Badges", + link: "/v1/users/get-user-awards" + }, { text: "Set Claims", link: "/v1/users/get-user-claims" diff --git a/docs/v1/users/get-user-awards.md b/docs/v1/users/get-user-awards.md new file mode 100644 index 0000000..dbb0e80 --- /dev/null +++ b/docs/v1/users/get-user-awards.md @@ -0,0 +1,49 @@ +# getUserAwards + +A call to this function will retrieve metadata about the target user's site awards, via their username. + +## Examples + +```ts +import { getUserAwards } from "@retroachievements/api"; + +const userAwards = await getUserAwards(authorization, { userName: "xelnia" }); +``` + +## Returns + +```json +{ + "totalAwardsCount": 10, + "hiddenAwardsCount": 2, + "masteryAwardsCount": 6, + "completionAwardsCount": 0, + "eventAwardsCount": 0, + "siteAwardsCount": 2, + "visibleUserAwards": [ + { + "awardedAt": "2022-08-26T19:34:43+00:00", + "awardType": "Mastery/Completion", + "awardData": 802, + "awardDataExtra": 1, + "displayOrder": 114, + "title": "WarioWare, Inc.: Mega Microgames!", + "consoleName": "Game Boy Advance", + "flags": null, + "imageIcon": "/Images/034678.png" + } + ] +} +``` + +## Parameters + +| Name | Type | Description | +| :-------------- | :------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------- | +| `authorization` | [`AuthObject`](/v1/data-models/auth-object) | An object that must contain a `userName` and a `webApiKey`. See [this page](/getting-started) for how to create this object. | +| `userName` | `string` | The user for which to retrieve the site awards for. | + +## Source + +[@retroachievements/api, getUserAwards.ts](https://github.dev/RetroAchievements/retroachievements-api-js/blob/main/src/user/getUserAwards.ts) +[RAWeb, API_GetUserAwards.php](https://github.dev/RetroAchievements/RAWeb/blob/master/public/API/API_GetUserAwards.php) diff --git a/docs/v1/users/get-user-recent-achievements.md b/docs/v1/users/get-user-recent-achievements.md index ea53733..1f57254 100644 --- a/docs/v1/users/get-user-recent-achievements.md +++ b/docs/v1/users/get-user-recent-achievements.md @@ -51,7 +51,7 @@ const userRecentAchievements = await getUserRecentAchievements(authorization, { | Name | Type | Description | | :-------------- | :------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------- | | `authorization` | [`AuthObject`](/v1/data-models/auth-object) | An object that must contain a `userName` and a `webApiKey`. See [this page](/getting-started) for how to create this object. | -| `userName` | `string` | The user for which to retrieve the recently played games for. | +| `userName` | `string` | The user for which to retrieve the recently earned achievements for. | | `recentMinutes` | `number?` | Optional. Defaults to 60. How many minutes back to fetch for the given user. | ## Source diff --git a/src/user/getUserAwards.test.ts b/src/user/getUserAwards.test.ts new file mode 100644 index 0000000..2e5e04d --- /dev/null +++ b/src/user/getUserAwards.test.ts @@ -0,0 +1,83 @@ +import { rest } from "msw"; +import { setupServer } from "msw/node"; + +import { apiBaseUrl } from "../utils/internal"; +import { buildAuthorization } from "../utils/public"; +import { getUserAwards } from "./getUserAwards"; +import type { GetUserAwardsResponse } from "./models"; + +const server = setupServer(); + +describe("Function: getUserAwards", () => { + // MSW Setup + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + + it("is defined #sanity", () => { + // ASSERT + expect(getUserAwards).toBeDefined(); + }); + + it("retrieves a list of a target user awards", async () => { + // ARRANGE + const authorization = buildAuthorization({ + userName: "mockUserName", + webApiKey: "mockWebApiKey" + }); + + const mockResponse: GetUserAwardsResponse = { + TotalAwardsCount: 10, + HiddenAwardsCount: 5, + MasteryAwardsCount: 5, + CompletionAwardsCount: 0, + EventAwardsCount: 0, + SiteAwardsCount: 0, + VisibleUserAwards: [ + { + AwardedAt: "2022-08-26T19:34:43+00:00", + AwardType: "Mastery/Completion", + AwardData: 802, + AwardDataExtra: 1, + DisplayOrder: 114, + Title: "WarioWare, Inc.: Mega Microgames!", + ConsoleName: "Game Boy Advance", + Flags: null, + ImageIcon: "/Images/034678.png" + } + ] + }; + + server.use( + rest.get(`${apiBaseUrl}/API_GetUserAwards.php`, (_, res, ctx) => + res(ctx.json(mockResponse)) + ) + ); + + // ACT + const response = await getUserAwards(authorization, { userName: "xelnia" }); + + // ASSERT + expect(response).toEqual({ + totalAwardsCount: 10, + hiddenAwardsCount: 5, + masteryAwardsCount: 5, + completionAwardsCount: 0, + eventAwardsCount: 0, + siteAwardsCount: 0, + visibleUserAwards: [ + { + awardedAt: "2022-08-26T19:34:43+00:00", + awardType: "Mastery/Completion", + awardData: 802, + awardDataExtra: 1, + displayOrder: 114, + title: "WarioWare, Inc.: Mega Microgames!", + consoleName: "Game Boy Advance", + flags: null, + imageIcon: "/Images/034678.png" + } + ] + }); + }); +}); diff --git a/src/user/getUserAwards.ts b/src/user/getUserAwards.ts new file mode 100644 index 0000000..d2f7676 --- /dev/null +++ b/src/user/getUserAwards.ts @@ -0,0 +1,70 @@ +import { + apiBaseUrl, + buildRequestUrl, + call, + serializeProperties +} from "../utils/internal"; +import type { AuthObject } from "../utils/public"; +import type { GetUserAwardsResponse, UserAwards } from "./models"; + +/** + * A call to this function will retrieve metadata about the target user's + * site awards, via their username. + * + * @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 site awards for. + * + * @example + * ``` + * const userAwards = await getUserAwards( + * authorization, + * { userName: "xelnia" } + * ) + * ``` + * + * @returns + * ```json + * { + * totalAwardsCount: 10, + * hiddenAwardsCount: 2, + * masteryAwardsCount: 6, + * completionAwardsCount: 0, + * eventAwardsCount: 0, + * siteAwardsCount: 2, + * visibleUserAwards: [ + * { + * awardedAt: "2022-08-26T19:34:43+00:00", + * awardType: "Mastery/Completion", + * awardData: 802, + * awardDataExtra: 1, + * displayOrder: 114, + * title: "WarioWare, Inc.: Mega Microgames!", + * consoleName: "Game Boy Advance", + * flags: null, + * imageIcon: "/Images/034678.png" + * } + * ] + * } + * ``` + */ +export const getUserAwards = async ( + authorization: AuthObject, + payload: { userName: string } +): Promise => { + const { userName } = payload; + + const queryParams: Record = { u: userName }; + + const url = buildRequestUrl( + apiBaseUrl, + "/API_GetUserAwards.php", + authorization, + queryParams + ); + + const rawResponse = await call({ url }); + + return serializeProperties(rawResponse); +}; diff --git a/src/user/getUserRecentAchievements.ts b/src/user/getUserRecentAchievements.ts index 682bdb5..66acc38 100644 --- a/src/user/getUserRecentAchievements.ts +++ b/src/user/getUserRecentAchievements.ts @@ -18,7 +18,7 @@ import type { * @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 summary for. + * @param payload.userName The user for which to retrieve the recent achievements for. * * @param payload.recentMinutes Optional. Defaults to 60. How many minutes * back to fetch for the given user. diff --git a/src/user/index.ts b/src/user/index.ts index 1068fdc..2d7744b 100644 --- a/src/user/index.ts +++ b/src/user/index.ts @@ -1,6 +1,7 @@ export * from "./getAchievementsEarnedBetween"; export * from "./getAchievementsEarnedOnDay"; export * from "./getGameInfoAndUserProgress"; +export * from "./getUserAwards"; export * from "./getUserClaims"; export * from "./getUserCompletedGames"; export * from "./getUserGameRankAndScore"; diff --git a/src/user/models/award-type.model.ts b/src/user/models/award-type.model.ts new file mode 100644 index 0000000..ebaf6c9 --- /dev/null +++ b/src/user/models/award-type.model.ts @@ -0,0 +1,7 @@ +export type AwardType = + | "Mastery/Completion" + | "Achievement Unlocks Yield" + | "Achievement Points Yield" + | "Patreon Supporter" + | "Certified Legend" + | "Invalid or deprecated award type"; diff --git a/src/user/models/get-user-awards-response.model.ts b/src/user/models/get-user-awards-response.model.ts new file mode 100644 index 0000000..d8a4f2c --- /dev/null +++ b/src/user/models/get-user-awards-response.model.ts @@ -0,0 +1,23 @@ +import type { AwardType } from "./award-type.model"; + +interface GetUserAwardsEntity { + AwardedAt: string; + AwardType: AwardType; + AwardData: number; + AwardDataExtra: number; + DisplayOrder: number; + Title: string; + ConsoleName: string; + Flags: number | null; + ImageIcon: string; +} + +export interface GetUserAwardsResponse { + TotalAwardsCount: number; + HiddenAwardsCount: number; + MasteryAwardsCount: number; + CompletionAwardsCount: number; + EventAwardsCount: number; + SiteAwardsCount: number; + VisibleUserAwards: GetUserAwardsEntity[]; +} diff --git a/src/user/models/index.ts b/src/user/models/index.ts index 3bcc0dc..848bbed 100644 --- a/src/user/models/index.ts +++ b/src/user/models/index.ts @@ -1,7 +1,9 @@ +export * from "./award-type.model"; export * from "./dated-user-achievement.model"; export * from "./dated-user-achievements-response.model"; export * from "./game-info-and-user-progress.model"; export * from "./get-game-info-and-user-progress-response.model"; +export * from "./get-user-awards-response.model"; export * from "./get-user-completed-games-response.model"; export * from "./get-user-game-rank-and-score-response.model"; export * from "./get-user-points-response.model"; @@ -9,6 +11,7 @@ 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-summary-response.model"; +export * from "./user-awards.model"; export * from "./user-claims.model"; export * from "./user-claims-response.model"; export * from "./user-completed-games.model"; diff --git a/src/user/models/user-awards.model.ts b/src/user/models/user-awards.model.ts new file mode 100644 index 0000000..9dbbe3e --- /dev/null +++ b/src/user/models/user-awards.model.ts @@ -0,0 +1,23 @@ +import type { AwardType } from "./award-type.model"; + +interface UserAward { + awardedAt: string; + awardType: AwardType; + awardData: number; + awardDataExtra: number; + displayOrder: number; + title: string; + consoleName: string; + flags: number | null; + imageIcon: string; +} + +export interface UserAwards { + totalAwardsCount: number; + hiddenAwardsCount: number; + masteryAwardsCount: number; + completionAwardsCount: number; + eventAwardsCount: number; + siteAwardsCount: number; + visibleUserAwards: UserAward[]; +} diff --git a/src/utils/internal/serializeProperties.ts b/src/utils/internal/serializeProperties.ts index 70a6db0..5eeb48c 100644 --- a/src/utils/internal/serializeProperties.ts +++ b/src/utils/internal/serializeProperties.ts @@ -71,5 +71,8 @@ const naiveCamelCase = (originalValue: string) => { // "rAPoints" -> "raPoints" camelCased = camelCased.replace(/rA/g, "ra"); + // "visibleUserawards" -> "visibleUserAwards" + camelCased = camelCased.replace(/visibleUserawards/g, "visibleUserAwards"); + return camelCased; };