From 415d5a179098145cc1a249774d4c8bca0edf3075 Mon Sep 17 00:00:00 2001 From: Leonard Techel Date: Sun, 25 Jul 2021 11:21:42 +0200 Subject: [PATCH] feat: Implement displaying user scope depending on game manifest :) --- app/src/common/hooks/api/useGame.test.ts | 19 +++++++++++ app/src/common/hooks/api/useGame.ts | 26 +++++++++++++++ app/src/common/types/Game.ts | 8 +++++ app/src/config.ts | 2 ++ app/src/features/parameters/Parameters.tsx | 39 ++++++++++++---------- app/src/mocks/data/game-not-found.json | 9 +++++ app/src/mocks/handlers.ts | 6 ++++ docs/ABC-DPT.v1.yaml | 3 +- 8 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 app/src/common/hooks/api/useGame.test.ts create mode 100644 app/src/common/hooks/api/useGame.ts create mode 100644 app/src/common/types/Game.ts create mode 100644 app/src/mocks/data/game-not-found.json diff --git a/app/src/common/hooks/api/useGame.test.ts b/app/src/common/hooks/api/useGame.test.ts new file mode 100644 index 0000000..813884c --- /dev/null +++ b/app/src/common/hooks/api/useGame.test.ts @@ -0,0 +1,19 @@ +import { renderHook } from "@testing-library/react-hooks"; + +import useGame from "./useGame"; + +it("loads the game", async () => { + const { result, waitForValueToChange } = renderHook(() => useGame()); + + await waitForValueToChange(() => result.current.data); + expect(result.current.data).toEqual(require("../../../mocks/data/game.json")); +}); + +it("returns an error when the game is not found", async () => { + const { result, waitForValueToChange } = renderHook(() => useGame()); + + await waitForValueToChange(() => result.current.error); + expect(result.current.error).toEqual( + require("../../../mocks/data/game-not-found.json") + ); +}); diff --git a/app/src/common/hooks/api/useGame.ts b/app/src/common/hooks/api/useGame.ts new file mode 100644 index 0000000..aceb27d --- /dev/null +++ b/app/src/common/hooks/api/useGame.ts @@ -0,0 +1,26 @@ +import useSWR from "swr"; + +import { Game } from "../../types/Game"; +import ApiError from "./helper/ApiError"; +import config from "../../../config"; +import errorAwareFetcher from "./helper/errorAwareFetcher"; +import useApiUrl from "./useApiUrl"; + +interface GameApiResponse { + data: Game; +} + +/** + * A React hook that retrieves the game manifest + * + * @returns The game manifest. May be null during initial load + */ +const useGame = () => { + const url = useApiUrl((gameId) => config.apiEndpoints.game(gameId)); + return useSWR( + () => url, + (url) => errorAwareFetcher(() => fetch(url)) + ); +}; + +export default useGame; diff --git a/app/src/common/types/Game.ts b/app/src/common/types/Game.ts new file mode 100644 index 0000000..4a28186 --- /dev/null +++ b/app/src/common/types/Game.ts @@ -0,0 +1,8 @@ +export interface Game { + type: "game"; + id: string; + attributes: { + hasMessages: boolean; + hasUserParameterScope: boolean; + }; +} diff --git a/app/src/config.ts b/app/src/config.ts index b88a965..0d55143 100644 --- a/app/src/config.ts +++ b/app/src/config.ts @@ -5,6 +5,7 @@ interface Config { apiEndpoints: { clock: (gameId: string) => string; code: (codeId: string, gameId: string) => string; + game: (gameId: string) => string; parameters: (gameId: string) => string; }; @@ -19,6 +20,7 @@ const config: Config = { clock: (gameId: string) => `${process.env.API_ROOT}/games/${gameId}/clock`, code: (codeId: string, gameId: string) => `${process.env.API_ROOT}/games/${gameId}/codes/${codeId}`, + game: (gameId: string) => `${process.env.API_ROOT}/games/${gameId}`, parameters: (gameId: string) => `${process.env.API_ROOT}/games/${gameId}/parameters`, }, diff --git a/app/src/features/parameters/Parameters.tsx b/app/src/features/parameters/Parameters.tsx index 6d34aae..eb81824 100644 --- a/app/src/features/parameters/Parameters.tsx +++ b/app/src/features/parameters/Parameters.tsx @@ -1,35 +1,40 @@ import { FormattedMessage } from "react-intl"; import * as React from "react"; -import ParameterCard from "./ParameterCard"; import { useParameters, useScopedParameterResponse, } from "../../common/hooks/api/useParameters"; +import ParameterCard from "./ParameterCard"; +import useGame from "../../common/hooks/api/useGame"; const Parameters = () => { + const game = useGame(); + const { data } = useParameters(); const userParams = useScopedParameterResponse("user", data); const globalParams = useScopedParameterResponse("global", data); return ( <> - - } - description={ - - } - params={userParams} - numSkeletonParams={1} - /> + {game.data?.data.attributes.hasUserParameterScope && ( + + } + description={ + + } + params={userParams} + numSkeletonParams={1} + /> + )} { + if (req.params.gameId === "not-found") { + return res( + ctx.status(404), + ctx.json(require("./data/game-not-found.json")) + ); + } return res(ctx.json(require("./data/game.json"))); }), ]; diff --git a/docs/ABC-DPT.v1.yaml b/docs/ABC-DPT.v1.yaml index 775922b..76127fa 100644 --- a/docs/ABC-DPT.v1.yaml +++ b/docs/ABC-DPT.v1.yaml @@ -367,7 +367,8 @@ paths: required: true get: summary: Your GET endpoint - tags: [] + tags: + - App responses: "200": description: OK