Files
boxmaker/docs/plans/M4a/10-gatewayd-mm.md
T
kyleandClaude Opus 5.5 0339dc13b2 Plan M4a: gatewayd in 15 tasks, with skeletons and given tests
Each task's tests were run against a reference at its end state; the end states were replayed
from master in order with the gate at each step (650 to 762 tests); each skeleton compiles
against its tests and fails them. The reference is kept off this machine. Lessons T27 (every
wait in a test has a limit) and T28 (mutate the reference before hand-over) come from this work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:05:44 -07:00

4.1 KiB

M4a task 10: Mattermost's events and REST calls

Branch: m4a (run git switch m4a; git status --short must be empty, otherwise stop) Commit subject: gatewayd: mm, Mattermost's events and the four REST calls, typed

Goal

Mattermost's JSON, typed, and the REST calls gatewayd makes. This is another program's format: unknown fields are ignored. But every id we keep ends up in a session id or a URL path, so every id must be a valid Mattermost id (26 characters of a-z0-9), or the whole post is refused.

Facts from Mattermost's source at the owner's server version, v11.11.0 (checked by the design model; the tests use these shapes):

  • A WebSocket event is {"event", "data", "broadcast", "seq"}. The first is hello. posted carries the post as a JSON string in data.post (a string holding JSON, not an object), and the channel's type in data.channel_type: D direct, G group, O open, P private. A reply to one of our requests has no event.
  • user_typing is a request we send: {"action": "user_typing", "seq": <n>, "data": {"channel_id", "parent_id"}}; it shows the bot as typing in that thread.
  • GET /api/v4/channels/{id}/posts?since=<ms> returns {"order": [ids], "posts": {id: post}} with the posts changed after that time. Only the ids in order changed; posts also holds the roots of their threads, which may be old. Edited and deleted posts come back too. The server takes at most 1000 (SINCE_LIMIT); a full answer may have left some out.
  • REST errors: 401 or 403 is a refused token, 429 is a rate limit (wait for X-Ratelimit-Reset), 5xx is worth trying again.

Files

  • Copy: crates/gatewayd/tests/mm_json.rs, crates/gatewayd/tests/mm_rest.rs, crates/gatewayd/tests/support/http_server.rs, and the skeletons crates/gatewayd/src/mm/mod.rs and crates/gatewayd/src/mm/rest.rs
  • Modify: crates/gatewayd/src/lib.rs (pub mod mm;), docs/implementer-log.md

The skeletons

mm/mod.rs, written: SINCE_LIMIT; MmError (Net, Auth(u16), RateLimited(Duration), Status(u16, String), Json) and its Display, which quotes a server's body ({:?}) so that a newline in it cannot forge a log line; Me { id, username }; Post { id, user_id, channel_id, root_id, message, create_at, delete_at, kind } (kind is the JSON type); Event (Hello, Posted { post, channel_type }, Other(String)); Since { posts, full }; and the private serde shapes RawEvent and PostList. To fill: Post::check, json, parse_event, typing, since_list.

mm/rest.rs, written: RETRY_5XX (500 ms), RETRIES (2), BODY_KEPT (200), Client. To fill: new, connector, token, once, call, me, create_post, posts_since, direct_channel. The token reaches the wire only in once, as Authorization: Bearer <token>; nowhere else calls expose().

Steps

  • 1. Copy. git switch m4a, then mkdir -p crates/gatewayd/src/mm && cp docs/plans/M4a/files/crates/gatewayd/src/mm/mod.rs docs/plans/M4a/files/crates/gatewayd/src/mm/rest.rs crates/gatewayd/src/mm/ && cp docs/plans/M4a/files/crates/gatewayd/tests/mm_json.rs docs/plans/M4a/files/crates/gatewayd/tests/mm_rest.rs crates/gatewayd/tests/ && cp docs/plans/M4a/files/crates/gatewayd/tests/support/http_server.rs crates/gatewayd/tests/support/. Add pub mod mm; to lib.rs.
  • 2. See it fail. cargo test -p gatewayd --no-fail-fast --test mm_json --test mm_rest. Expected: it compiles; mm_json 6 fail and 1 passes (the Display test), mm_rest 10 fail.
  • 3. Fill mm/mod.rs, then mm/rest.rs, one function at a time, cargo check -p gatewayd after each.
  • 4. See it pass. cargo test -p gatewayd --test mm_json --test mm_rest. Expected: 7 and 10 passed. mm_rest takes about 3 s: two tests wait out a rate limit on purpose.
  • 5. Run the gate. cargo fmt --all, then make gate. Expected last line: gate: ok.
  • 6. Log and commit. git add crates/gatewayd docs/implementer-log.md Cargo.lock && git commit

Done when

  • Both suites pass; make gate prints gate: ok.

Stop and report if

  • A test needs a post with an invalid id to be accepted.