Skip to content

Proxy /api/session/runs on the web client so the Run History panel is filled in (#82) - #83

Merged
dmccoystephenson merged 2 commits into
mainfrom
feature/session-runs-proxy
Aug 8, 2026
Merged

Proxy /api/session/runs on the web client so the Run History panel is filled in (#82)#83
dmccoystephenson merged 2 commits into
mainfrom
feature/session-runs-proxy

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

  • The game page is served by the web client, so its fetch('/api/session/runs', ...) is made against the web client's own origin — but no route existed there. The request was answered with a 404 and the Run History panel stayed empty anywhere /api/* is not routed straight to the backend by an external reverse proxy, including the docker-compose up / localhost:3000 setup the README documents.
  • GET /api/session/runs is now proxied by WebController/BackendService, following the pattern already used by /api/session/ruler-stats: the browser's Cookie header is forwarded to the backend and failures are translated through proxy(...), so a backend 401 reaches the browser as a 401 and the page's re-login flow still fires.
  • The backend's RunHistory / RunRecord response shape is mirrored as web-client models (alongside the existing RulerStats mirror) so the whole record is relayed rather than the subset the panel happens to read today.
  • A new GamePageProxyCoverageTest reads the API calls out of the rendered game page and asserts each one has a matching route on the web client. The calls are extracted from the markup rather than listed in the test, so an endpoint added later is covered without the test being touched — this class of drift cannot reopen silently.

Module(s) touched

web-client only. The backend is unchanged; its GET /api/session/runs endpoint and the README entry describing it were already correct — the web-client half of the contract was what was missing.

Test plan

  • cd backend && mvn test — 267 tests, 0 failures (unchanged module, re-run to confirm nothing regressed)
  • cd web-client && mvn test — 21 tests, 0 failures (17 before this change)
  • Regression evidence: with the new WebController mapping stashed, GamePageProxyCoverageTest.everyApiCallTheGamePageMakesIsServedByTheWebClient and both new WebControllerTest cases fail (404 where 200/401 was expected); with it restored, all pass.
  • Not run locally: the Node game-page test suite (node --test web-client/src/test/js/) — no Node interpreter is present in this environment. No JavaScript was changed by this PR, and CI runs that job.

Documentation

  • CHANGELOG.md — entry added under Unreleased → Web Client.
  • README.md — the Technical Architecture → Web Client section now states that the page's /api/* calls are answered and proxied by the web client, and points at the new guard test.
  • PLAYER_GUIDE.md, MVP.md, DOCS.md — checked; no change needed, as they describe the Run History panel's intended behaviour, which this change restores rather than alters.

Deferred this cycle

Closes #82


This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

dmccoystephenson and others added 2 commits August 8, 2026 01:37
The page fetches /api/session/runs from the web client that serves it,
but only the backend had that route, so the Run History panel was
answered with a 404 and stayed empty wherever /api/* is not routed
straight to the backend — including the documented docker-compose setup.
The web client now proxies the endpoint like every other per-player one,
forwarding the auth cookie and passing the backend's status through so a
401 still triggers the re-login flow.

A new GamePageProxyCoverageTest reads the API calls out of the rendered
page and fails if any of them has no route here, so the next endpoint
cannot slip through the same gap.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guard only read the game page, so the same missing-proxy gap could
reopen on the pages that post credentials. It now renders every page the
web client serves and checks each call they make, and says so in its
name.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review

Scored against the diff and command output at head 96ed76d, with both CI jobs green on that SHA.

Item Verdict Evidence
Scope PASS Nine files, each required: two proxy edits, two response models, three test files, and the two documentation sources the change makes stale. No unrelated formatting or renames were swept in.
Tests-new PASS BackendService.sessionRuns is exercised by BackendServiceTest.sessionRunsForwardsAuthCookieAndRelaysTheWholeRecord; WebController.getSessionRuns by sessionRunsReturnsBackendRunHistoryToBrowser and sessionRunsPassesBackend401ThroughToBrowser.
Tests-fix PASS Measured, not reasoned: with WebController.java stashed, mvn test -Dtest='GamePageProxyCoverageTest,WebControllerTest' failed 3 of 6 — The game page calls GET /api/session/runs, but the web client serves no such route plus Status expected:<401> but was:<404> and Status expected:<200> but was:<404>. All six pass with the mapping restored.
Sibling structure PASS RunHistory/RunRecord mirror the backend models field for field and follow the RulerStats mirror already in web-client/.../model; sessionRuns and getSessionRuns are copied from the sessionRulerStats pair immediately above them.
Sibling renames PASS GamePageProxyCoverageTest was renamed to ProxyRouteCoverageTest once its scope grew past the game page, and both references to the old name (README, CHANGELOG) were updated in the same commit.
Docs PASS CHANGELOG.md gained an Unreleased → Web Client entry; README.md's Technical Architecture → Web Client section now records that pages' /api/* calls are answered and proxied here. The README Backend API list already described GET /api/session/runs correctly — the backend half of the contract was never wrong. PLAYER_GUIDE.md, MVP.md, and DOCS.md were read and need no change: they describe the panel's intended behaviour, which is restored rather than altered.
Issue resolution PASS Both acceptance criteria on #82 are met — the panel is served on the web client's own origin, and a test fails if the mapping is removed.
CI PASS Backend Build and Test and Web Client Build and Test both pass on 96ed76d; the web-client job includes the node --test game-page suite.
Both-modules PASS Backend was not modified and its 267 tests were re-run locally (0 failures) alongside the web client's 21 (17 before). Neither CI job was allowed to stand in for the other.
API-contract PASS The proxied path, method, and response shape match GameController.sessionRuns field for field, including the id, username, armiesRemaining, soldiersRemaining, and finishedAt fields the panel does not currently read.
Changelog PASS The player-visible effect (an empty Run History panel being filled in) is recorded.
Constructor-injection PASS No new Spring component was introduced; WebController keeps its Lombok @RequiredArgsConstructor and BackendService its explicit constructor. No field @Autowired was added to production code.
Override-correct n/a No @Override is added by this diff.

Findings

These are limitations worth stating rather than defects to block on; none were left unfixed where a fix was mechanical.

  • web-client/src/test/java/com/barony/webclient/ProxyRouteCoverageTest.java:44 — the guard finds calls by matching the literal fetch('/... form in the rendered markup. A call whose URL is composed at runtime, and a mapping registered under a URL pattern rather than a literal path, are both invisible to it. Neither exists today; a pattern mapping added later would surface here as a false failure. This is stated in the class javadoc so the next reader is not surprised by it.
  • web-client/src/test/java/com/barony/webclient/ProxyRouteCoverageTest.java:47 — a call's HTTP method is inferred from the method: '...' option appearing between one fetch( and the next. Unrelated text carrying that literal between two calls would misattribute the earlier call's method. The failure would be loud and specific rather than silent, so the heuristic was preferred over parsing the script.
  • web-client/src/main/java/com/barony/webclient/model/RunRecord.java:17id and username are modelled although the Run History panel reads neither. This is deliberate: an unmodelled field would be dropped by the proxy, which is the same class of silent drift this PR exists to close.
  • Verification is confined to MockMvc and MockRestServiceServer; no request was made against a running backend, because this environment has neither Docker nor a UserAuth instance. A manual smoke test of the Run History panel through docker-compose up is recommended before release.
  • The Node game-page suite (node --test web-client/src/test/js/) could not be run locally, as no Node interpreter is present here. No JavaScript was changed, and the CI web-client job ran that suite and passed.

Two of the three test files exceeded what the original fix strictly needed, deliberately: the guard test was widened from the game page to every page the web client serves, so the login and register pages that post credentials are covered by the same check.


This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

@dmccoystephenson
dmccoystephenson merged commit 76c8fee into main Aug 8, 2026
2 checks passed
@dmccoystephenson
dmccoystephenson deleted the feature/session-runs-proxy branch August 8, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run History panel 404s on the web client: /api/session/runs has no proxy mapping

1 participant