jmri_server.envelope
The JMRI JSON message envelope.
Wire format (see contracts/json-schema/schema/json-client.json):
- Client → server:
{type, data?, list?, method?, id?} - Server → client:
{type, data, method?, id?}
Where method ∈ {"get", "put", "post", "delete", "list"}. id is
echoed unchanged on responses. The list shorthand {type: "list",
list: "<topic>"} is equivalent to {type: "<topic>", method: "list"}.
1"""The JMRI JSON message envelope. 2 3Wire format (see ``contracts/json-schema/schema/json-client.json``): 4 5* Client → server: ``{type, data?, list?, method?, id?}`` 6* Server → client: ``{type, data, method?, id?}`` 7 8Where ``method`` ∈ ``{"get", "put", "post", "delete", "list"}``. ``id`` is 9echoed unchanged on responses. The ``list`` shorthand ``{type: "list", 10list: "<topic>"}`` is equivalent to ``{type: "<topic>", method: "list"}``. 11""" 12from __future__ import annotations 13 14from typing import Any, Literal 15 16from pydantic import BaseModel, ConfigDict, Field 17 18Method = Literal["get", "put", "post", "delete", "list"] 19 20 21class JmriRequest(BaseModel): 22 """A single client → server message.""" 23 model_config = ConfigDict(extra="forbid") 24 25 type: str 26 data: dict[str, Any] | None = None 27 method: Method | None = None 28 list: str | None = None 29 id: int | None = None 30 31 32class JmriResponse(BaseModel): 33 """A single server → client message with payload.""" 34 model_config = ConfigDict(extra="forbid") 35 36 type: str 37 data: dict[str, Any] | list[dict[str, Any]] = Field(default_factory=dict) 38 method: Method | None = None 39 id: int | None = None 40 41 42def normalize_request(req: JmriRequest) -> tuple[str, Method, dict[str, Any] | None]: 43 """Resolve the ``list`` shorthand and apply the default method. 44 45 Returns ``(type, method, data)`` ready for dispatch. 46 """ 47 if req.type == "list" and req.list is not None: 48 # ``{type: "list", list: "<topic>"}`` shorthand → list of the topic. 49 return req.list, "list", req.data 50 method: Method = req.method if req.method is not None else "get" 51 return req.type, method, req.data
Method =
typing.Literal['get', 'put', 'post', 'delete', 'list']
class
JmriRequest(pydantic.main.BaseModel):
22class JmriRequest(BaseModel): 23 """A single client → server message.""" 24 model_config = ConfigDict(extra="forbid") 25 26 type: str 27 data: dict[str, Any] | None = None 28 method: Method | None = None 29 list: str | None = None 30 id: int | None = None
A single client → server message.
class
JmriResponse(pydantic.main.BaseModel):
33class JmriResponse(BaseModel): 34 """A single server → client message with payload.""" 35 model_config = ConfigDict(extra="forbid") 36 37 type: str 38 data: dict[str, Any] | list[dict[str, Any]] = Field(default_factory=dict) 39 method: Method | None = None 40 id: int | None = None
A single server → client message with payload.
def
normalize_request( req: JmriRequest) -> tuple[str, typing.Literal['get', 'put', 'post', 'delete', 'list'], dict[str, typing.Any] | None]:
43def normalize_request(req: JmriRequest) -> tuple[str, Method, dict[str, Any] | None]: 44 """Resolve the ``list`` shorthand and apply the default method. 45 46 Returns ``(type, method, data)`` ready for dispatch. 47 """ 48 if req.type == "list" and req.list is not None: 49 # ``{type: "list", list: "<topic>"}`` shorthand → list of the topic. 50 return req.list, "list", req.data 51 method: Method = req.method if req.method is not None else "get" 52 return req.type, method, req.data
Resolve the list shorthand and apply the default method.
Returns (type, method, data) ready for dispatch.