refactor(#1375): make untyped RPC request and send internal (#1395)

This commit is contained in:
m57
2026-09-30 01:22:39 +03:00
committed by GitHub
parent 26c60a10c0
commit 6baade0f7f
3 changed files with 26 additions and 6 deletions
+15
View File
@@ -137,6 +137,21 @@ explicitly disconnected on logout and reconnected after login:
The singleton's `connect()` has a guard (`if connected → skip`) so it's safe to The singleton's `connect()` has a guard (`if connected → skip`) so it's safe to
call unconditionally. call unconditionally.
### Typed Gateway RPC
Every gateway method in the contract (`app/src/test/resources/gateway-contract/openrpc.json`) is called through a
typed descriptor: `HermesWsClient.call(RpcMethods.X, params)` (suspending), `requestTyped(...)`, or the typed
`send(RpcMethods.X, params, onSent)`. Params are `@Serializable` classes under `data/ws/contract/`; results are
`JsonElement` passthrough or typed. To add a method: add the params class, register it in `RpcMethods` (and
`RpcMethods.all`), and `GatewayContractTest` checks it against the contract.
- The untyped `request(String, Map)` / `send(String, Map)` are `internal`. Use them only for `gateway.ping` and the
legacy `*.respond` fallbacks (`clarify`, `sudo`, `secret`, `vault.*`) that have no contract entry.
- Optional params are `T? = null`: the client Json has `encodeDefaults = false`, so unset fields are omitted and an
explicit `false`/`""` is still sent. Do not add a `profile` field unless callers pass an explicit override;
`WsProfileParams.decorate` injects it for profile-scoped methods.
- `RawRpcParamsRatchetTest.BASELINE` must stay empty.
### Shared Components ### Shared Components
- **`HermesScaffold`** — drawer-aware Scaffold + TopAppBar with refresh slot, - **`HermesScaffold`** — drawer-aware Scaffold + TopAppBar with refresh slot,
@@ -800,8 +800,11 @@ object HermesWsClient {
* The deferred is completed on the matching [WsEvent.RpcResult] / * The deferred is completed on the matching [WsEvent.RpcResult] /
* [WsEvent.RpcError], rejected after [timeoutMs] (no response), or * [WsEvent.RpcError], rejected after [timeoutMs] (no response), or
* rejected by [rejectAllPending] when the socket closes. * rejected by [rejectAllPending] when the socket closes.
*
* Untyped escape hatch, module-internal on purpose: gateway methods in the contract go through
* [call] / [requestTyped] with an `RpcMethods` descriptor. Only `gateway.ping` and tests use this.
*/ */
fun request( internal fun request(
method: String, method: String,
params: Map<String, Any> = emptyMap(), params: Map<String, Any> = emptyMap(),
timeoutMs: Long = REQUEST_TIMEOUT_MS, timeoutMs: Long = REQUEST_TIMEOUT_MS,
@@ -975,9 +978,12 @@ object HermesWsClient {
/** /**
* Send a JSON-RPC request with the given [method] and optional [params]. * Send a JSON-RPC request with the given [method] and optional [params].
*
* Untyped escape hatch, module-internal on purpose: contract methods use the typed [send] overload.
* Kept for the legacy `*.respond` fallbacks (clarify, sudo, secret, vault) that have no contract entry.
* @return the request id used (can be matched against [WsEvent.RpcResult]). * @return the request id used (can be matched against [WsEvent.RpcResult]).
*/ */
fun send( internal fun send(
method: String, method: String,
params: Map<String, Any> = emptyMap(), params: Map<String, Any> = emptyMap(),
onSent: ((String) -> Unit)? = null, onSent: ((String) -> Unit)? = null,
@@ -309,10 +309,9 @@ class RawRpcParamsRatchetTest {
} }
/** /**
* Multiset of remaining unmigrated raw RPC call sites in `app/src/main/java/com/m57/hermescontrol/`. * Multiset of remaining raw RPC call sites for contract methods in `app/src/main/java/com/m57/hermescontrol/`.
* Excludes `data/ws/HermesWsClient.kt::SESSION_EVENTS_SINCE` which is migrated to typed `RpcMethods`. * Empty: every contract method goes through `RpcMethods`. Keep it empty; new contract methods must be typed.
* Notice HermesWsClient.kt retains its other raw uses: `CLIENT_CAPABILITIES`, `PROMPT_SUBMIT`, * The untyped `request`/`send` stay module-internal for `gateway.ping` and the legacy `*.respond` fallbacks.
* and `SESSION_REDIRECT`.
*/ */
val BASELINE: List<String> = emptyList() val BASELINE: List<String> = emptyList()
} }