refactor(shared): move REST wire types into the shared crate #105

Merged
icub3d merged 1 commit from feature/82-shared-rest-types into main 2026-07-09 19:48:42 +00:00
Owner

Problem

Every REST request/response body was hand-duplicated between server/src/**/models.rs and sdk/rust/src/rest/models.rs (whose own doc-comment admitted the two "must stay in lockstep"). Because serde silently ignores unknown fields, drift produced no compile error — just missing data on the wire.

What this does (checkboxes 1, 2, 3, 5)

  • Defines each REST wire type once in shared::rest with #[derive(Serialize, Deserialize)]. Request types keep their skip_serializing_if (SDK side) and the three-valued Option<Option<String>> deserialize_with visitor (server side) merged onto one struct — each attribute only affects the end that uses it.
  • server/src/**/models.rs now re-export the shared types under their existing *Response names and keep only the storage→wire From impls. The two inherent constructors that can't be attached to a foreign type (MessageResponse::from_message, InviteResponse::from_invite) become free functions message_response / invite_response.
  • sdk/rust/src/rest/models.rs collapses from ~370 lines to a single pub use shared::rest::….
  • profiles handler drops its inline ProfileResponse / RoleSummary / UpdateProfileRequest.
  • SDK gateway client commands now serialize through the shared ClientCommand enum instead of raw "SUBSCRIBE" / "UNSUBSCRIBE" / "HEARTBEAT_ACK" string literals. ClientCommand gained a wire-format Serialize (the derived one was externally-tagged and wrong), so encode and decode share one source of truth for the op strings.

No JSON field names, #[serde(rename)]s, or status codes change — this is wire-compatible.

Deferred to a follow-up (checkbox 4)

Unifying gateway event payload types between the server broadcaster and bot-sdk. That requires per-op reconciliation of what the server broadcasts vs. what bot-sdk decodes (e.g. MemberJoin's payload vs. the full Member) and changes bot-sdk's public Event payload types, so it's a distinct, riskier unit.

Verification

  • cargo build --workspace — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test --workspace — all pass (server 130, shared 9 incl. new three-valued + client-command roundtrip tests, SDK 8, bot-sdk green)

Net −205 lines.

Part of #82

🤖 Generated with Claude Code

## Problem Every REST request/response body was hand-duplicated between `server/src/**/models.rs` and `sdk/rust/src/rest/models.rs` (whose own doc-comment admitted the two "must stay in lockstep"). Because serde silently ignores unknown fields, drift produced no compile error — just missing data on the wire. ## What this does (checkboxes 1, 2, 3, 5) - **Defines each REST wire type once in `shared::rest`** with `#[derive(Serialize, Deserialize)]`. Request types keep their `skip_serializing_if` (SDK side) and the three-valued `Option<Option<String>>` `deserialize_with` visitor (server side) merged onto one struct — each attribute only affects the end that uses it. - **`server/src/**/models.rs`** now re-export the shared types under their existing `*Response` names and keep only the storage→wire `From` impls. The two inherent constructors that can't be attached to a foreign type (`MessageResponse::from_message`, `InviteResponse::from_invite`) become free functions `message_response` / `invite_response`. - **`sdk/rust/src/rest/models.rs`** collapses from ~370 lines to a single `pub use shared::rest::…`. - **profiles handler** drops its inline `ProfileResponse` / `RoleSummary` / `UpdateProfileRequest`. - **SDK gateway client commands** now serialize through the shared `ClientCommand` enum instead of raw `"SUBSCRIBE"` / `"UNSUBSCRIBE"` / `"HEARTBEAT_ACK"` string literals. `ClientCommand` gained a wire-format `Serialize` (the derived one was externally-tagged and wrong), so encode and decode share one source of truth for the op strings. No JSON field names, `#[serde(rename)]`s, or status codes change — this is wire-compatible. ## Deferred to a follow-up (checkbox 4) Unifying gateway event **payload** types between the server broadcaster and bot-sdk. That requires per-op reconciliation of what the server broadcasts vs. what bot-sdk decodes (e.g. `MemberJoin`'s payload vs. the full `Member`) and changes bot-sdk's public `Event` payload types, so it's a distinct, riskier unit. ## Verification - `cargo build --workspace` — clean - `cargo clippy --workspace --all-targets -- -D warnings` — clean - `cargo test --workspace` — all pass (server 130, shared 9 incl. new three-valued + client-command roundtrip tests, SDK 8, bot-sdk green) Net **−205 lines**. Part of #82 🤖 Generated with [Claude Code](https://claude.com/claude-code)
refactor(shared): move REST wire types into the shared crate
Some checks failed
Security Audit / PNPM Audit (JS SCA) (pull_request) Failing after 22s
Security Audit / Trivy (SCA & IaC) (pull_request) Failing after 23s
Security Audit / Gitleaks (Secrets) (pull_request) Successful in 8s
CI / Client — test & lint (pull_request) Successful in 45s
Security Audit / Semgrep (SAST) (pull_request) Failing after 1m6s
CI / Server — test & lint (pull_request) Successful in 2m49s
Security Audit / Cargo Audit (Rust SCA) (pull_request) Failing after 3m13s
6e8b58ee74
Every REST request/response body was hand-duplicated between
server/src/**/models.rs and sdk/rust/src/rest/models.rs (whose own doc
admitted the two "must stay in lockstep"). Because serde ignores unknown
fields, drift produced no error — just silently missing data.

Define each wire type once in shared::rest (Serialize + Deserialize) and
have both ends consume it:
- server models.rs now re-export the shared types under their existing
  *Response names and keep only the storage -> wire From impls. The two
  inherent constructors that can't live on a foreign type
  (MessageResponse::from_message, InviteResponse::from_invite) become the
  free functions message_response/invite_response.
- sdk/rust/src/rest/models.rs is now a single pub use of shared::rest.
- profiles handler drops its inline ProfileResponse/RoleSummary/
  UpdateProfileRequest in favor of the shared types.

Also route the SDK's gateway client commands through the shared
ClientCommand enum instead of raw "SUBSCRIBE"/"UNSUBSCRIBE"/"HEARTBEAT_ACK"
string literals: ClientCommand now has a wire-format Serialize (the derived
one was externally tagged and wrong) so encode and decode share one source
of truth for the op strings.

Deferred to a follow-up (checkbox 4): unifying gateway event *payload*
types between the server broadcaster and bot-sdk, which requires per-op
reconciliation of broadcast vs decoded shapes and changes bot-sdk's public
Event payloads.

Part of #82

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
icub3d merged commit 1567643de7 into main 2026-07-09 19:48:42 +00:00
icub3d deleted branch feature/82-shared-rest-types 2026-07-09 19:48:42 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
icub3d/decentcom!105
No description provided.