Coverage for src / lexigram / admin / realtime / ws_handler_registry.py: 61%
46 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-13 22:14 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-13 22:14 +0800
1"""WebSocket message handler registry for real-time communication."""
3from __future__ import annotations
5from typing import Any, Protocol
8class WSMessageHandler(Protocol):
9 """Protocol for WebSocket message handlers."""
11 async def handle(
12 self,
13 websocket: Any,
14 msg: Any,
15 connection_id: str,
16 manager: Any,
17 ) -> None:
18 """Handle the WebSocket message."""
19 ...
22class PingHandler:
23 """Handler for PING messages."""
25 async def handle(
26 self,
27 websocket: Any,
28 msg: Any,
29 connection_id: str,
30 manager: Any,
31 ) -> None:
32 from lexigram.admin.realtime.websocket import WSMessage, WSMessageType
34 await websocket.send_json(
35 WSMessage(
36 type=WSMessageType.PONG,
37 id=msg.id,
38 ).to_dict(),
39 )
42class SubscribeHandler:
43 """Handler for SUBSCRIBE messages."""
45 async def handle(
46 self,
47 websocket: Any,
48 msg: Any,
49 connection_id: str,
50 manager: Any,
51 ) -> None:
52 from lexigram.admin.realtime.websocket import WSMessage, WSMessageType
54 resources = msg.data.get("resources", [])
55 await manager.subscribe(connection_id, resources)
56 await websocket.send_json(
57 WSMessage(
58 type=WSMessageType.ACK,
59 data={"subscribed": resources},
60 id=msg.id,
61 ).to_dict(),
62 )
65class UnsubscribeHandler:
66 """Handler for UNSUBSCRIBE messages."""
68 async def handle(
69 self,
70 websocket: Any,
71 msg: Any,
72 connection_id: str,
73 manager: Any,
74 ) -> None:
75 from lexigram.admin.realtime.websocket import WSMessage, WSMessageType
77 resources = msg.data.get("resources", [])
78 await manager.unsubscribe(connection_id, resources)
79 await websocket.send_json(
80 WSMessage(
81 type=WSMessageType.ACK,
82 data={"unsubscribed": resources},
83 id=msg.id,
84 ).to_dict(),
85 )
88class ActionHandler:
89 """Handler for ACTION messages."""
91 async def handle(
92 self,
93 websocket: Any,
94 msg: Any,
95 connection_id: str,
96 manager: Any,
97 ) -> None:
98 from lexigram.admin.realtime.websocket import WebSocketHandler
100 handler = WebSocketHandler(manager) # type: ignore[call-arg]
101 await handler._handle_action(websocket, msg) # type: ignore[attr-defined]
104class WSMessageTypeRegistry:
105 """Central registry for WebSocket message type handlers."""
107 def __init__(self) -> None:
108 self._handlers: dict[Any, WSMessageHandler] = {}
110 @classmethod
111 def with_defaults(cls) -> WSMessageTypeRegistry:
112 """Return a new instance with default message handlers registered."""
113 from lexigram.admin.realtime.websocket import WSMessageType
115 registry = cls()
116 registry._handlers[WSMessageType.PING] = PingHandler()
117 registry._handlers[WSMessageType.SUBSCRIBE] = SubscribeHandler()
118 registry._handlers[WSMessageType.UNSUBSCRIBE] = UnsubscribeHandler()
119 registry._handlers[WSMessageType.ACTION] = ActionHandler()
120 return registry
122 def register(self, msg_type: Any, handler: WSMessageHandler) -> None:
123 """Register a new message handler."""
124 self._handlers[msg_type] = handler
126 async def handle_message(
127 self,
128 msg_type: Any,
129 websocket: Any,
130 msg: Any,
131 connection_id: str,
132 manager: Any,
133 ) -> None:
134 """Handle a message using the appropriate handler."""
135 handler = self._handlers.get(msg_type)
136 if handler:
137 await handler.handle(websocket, msg, connection_id, manager)
140_ws_message_type_registry = WSMessageTypeRegistry.with_defaults()
143def get_ws_message_type_registry() -> WSMessageTypeRegistry:
144 """Get the global WebSocket message type registry."""
145 return _ws_message_type_registry