# M4b task 09: editing a post, and a username **Branch:** `m4b` (run `git switch m4b`; `git status --short` must be empty, otherwise stop) **Commit subject:** `gatewayd: mm, patch_post and username` ## Goal When an approval ends, `gatewayd` edits its post to say how, naming who answered (M4b spec, section 6). Two REST calls, checked against Mattermost v11.11.0, and confirmed on the owner's server (the bot may edit its own posts): - `PUT /api/v4/posts/{post_id}/patch` with `{"message": "…"}`: replaces the post's text; the answer is the post. - `GET /api/v4/users/{user_id}`: the user, with `username`. Every id that goes into a path must be a Mattermost id first, or nothing is sent. ## Files - Copy: `crates/gatewayd/tests/mm_edit.rs` - Modify: `crates/gatewayd/src/mm/rest.rs`, `docs/implementer-log.md` ## The changes, exactly In `crates/gatewayd/src/mm/rest.rs`, after `struct Channel`: ```rust #[derive(Deserialize)] struct User { username: String, } /// A Mattermost username: 1 to 64 characters of `a-z`, `0-9`, `.`, `-` and `_`. fn valid_username(name: &str) -> bool { (1..=64).contains(&name.len()) && name.bytes().all(|b| { b.is_ascii_lowercase() || b.is_ascii_digit() || matches!(b, b'.' | b'-' | b'_') }) } ``` and two methods at the end of `impl Client`, written like `posts_since` and `create_post`: 1. `pub fn patch_post(&self, post: &str, message: &str) -> Result<(), MmError>`: - `post` not `valid_id`: `Err(MmError::Json("a post path must be a Mattermost id".to_string()))`, nothing sent. - `PUT` `/api/v4/posts/{post}/patch` with the body `serde_json::json!({ "message": message }).to_string()`, through `self.call`. - The answer is a `Post`: `json::(&body)?.check().map(|_| ())`. 2. `pub fn username(&self, user: &str) -> Result`: - `user` not `valid_id`: `Err(MmError::Json("a user path must be a Mattermost id".to_string()))`. - `GET` `/api/v4/users/{user}` into `User`; a username that is not `valid_username` is `Err(MmError::Json("the user has no valid username".to_string()))`. Else `Ok(username)`. ## Steps - [ ] **1. Copy.** `git switch m4b`, then `cp docs/plans/M4b/files/crates/gatewayd/tests/mm_edit.rs crates/gatewayd/tests/` - [ ] **2. See it fail.** `cargo test -p gatewayd --test mm_edit`. Expected: it does not compile. - [ ] **3. Make the changes.** Run `cargo fmt --all`. - [ ] **4. See it pass.** `cargo test -p gatewayd --test mm_edit --test mm_rest`. Expected: 4 and 10 passed. - [ ] **5. Run the gate.** `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`.