mundane.api.app =============== .. py:module:: mundane.api.app .. autoapi-nested-parse:: The Litestar application: HTTP in, engine actions out, state back as JSON. The API is a thin translator. It never mutates :class:`GameState` directly — it parses requests into engine actions and calls ``Game.submit``, then maps the engine's :class:`IllegalAction` onto HTTP 422. All game legality lives in the engine. Loading a game's cards (allowlist, fetch, schema-validate, snapshot) lives in :mod:`mundane.api.set_loader`; the engine receives only the resolved pool. The game store is an in-memory ``dict``, supplied via dependency injection. It is **volatile** (lost on restart); see the README. Keeping it behind the small :class:`GameStore` interface (create / get / save) makes swapping it for Redis or SQLite a localised change. Attributes ---------- .. autoapisummary:: mundane.api.app.app Classes ------- .. autoapisummary:: mundane.api.app.GameStore Functions --------- .. autoapisummary:: mundane.api.app._unprocessable_handler mundane.api.app._bad_gateway_handler mundane.api.app._parse_set_urls mundane.api.app.create_game mundane.api.app.read_game mundane.api.app.submit_action mundane.api.app.export_game mundane.api.app.create_app Module Contents --------------- .. py:class:: GameStore(*, fetch: mundane.api.set_loader.Fetcher = default_fetch) In-memory game store. Volatile: its contents are lost when the process restarts. Deliberately an opaque service (not a dataclass), so the framework treats it as an injected dependency rather than request data to introspect. Swapping in Redis or SQLite later means reimplementing just create / get / save behind this same interface. ``fetch`` is injectable so tests can resolve set URLs from a local fixture instead of the network. .. py:attribute:: games :type: dict[str, mundane.engine.game.Game] .. py:attribute:: _fetch .. py:method:: create(set_urls: collections.abc.Sequence[str] | None = None) -> tuple[str, mundane.engine.game.Game] Resolve ``set_urls`` (default: the core set), then create and store a new game. Loading happens **before** the store is touched, so any loader error leaves it unchanged. .. py:method:: get(game_id: str) -> mundane.engine.game.Game Return the stored game, or raise ``NotFoundException`` (-> 404) if there is none. .. py:method:: save(game_id: str, game: mundane.engine.game.Game) -> None Persist the game. A no-op for the in-memory store (games already mutate in place). .. py:function:: _unprocessable_handler(_request: litestar.Request[Any, Any, Any], exc: Exception) -> litestar.Response[dict[str, str]] Map a rejected move or bad set input onto HTTP 422; the stored game is left unchanged. .. py:function:: _bad_gateway_handler(_request: litestar.Request[Any, Any, Any], exc: Exception) -> litestar.Response[dict[str, str]] Map an upstream set-fetch failure (network/timeout/oversize) onto HTTP 502. .. py:function:: _parse_set_urls(data: dict[str, object] | None) -> collections.abc.Sequence[str] | None Pull ``set_urls`` out of the request body, rejecting anything that isn't a list of strings. .. py:function:: create_game(store: GameStore, data: dict[str, object] | None = None) -> dict[str, object] Create a new game from ``set_urls`` (default: the core set); return its id and initial state. .. py:function:: read_game(game_id: str, store: GameStore) -> dict[str, object] Return the current state of a game. .. py:function:: submit_action(game_id: str, data: dict[str, object], store: GameStore) -> dict[str, object] Submit a move. An illegal move raises ``IllegalAction`` (-> 422) and changes nothing. .. py:function:: export_game(game_id: str, store: GameStore) -> litestar.Response[dict[str, object]] Return the game's log, final state, and card snapshot as a downloadable JSON attachment. The snapshot (resolved cards + content hash) makes the download self-contained: it replays without reaching the cards repo. The Content-Disposition attachment header is what the "Download game log" button on the game-over screen points at. .. py:function:: create_app(store: GameStore | None = None) -> litestar.Litestar Build the Litestar app. Pass a ``store`` to share/inspect it (tests do); else a fresh one. .. py:data:: app