orchestrata_ultimate.gateway.handlers.state

Reference › orchestrata_ultimate › gateway › handlers › state

StateHandler serves the client state-sync RPCs state.getAll, state.putAll, state.put and state.delete, used by the VizInt Command Center to keep UI state on the server. It borrows the Redis client from the ring_buffer and stores each connection's state as a Redis hash under a key built by _state_redis_key from the connection id. Values are saved as JSON with a timestamp. handle_get_all returns the entries, decoding JSON where possible; handle_put requires a key and raises RPCError with code -32602 when it is missing; handle_put_all and handle_delete follow the same pattern. When Redis is unavailable, reads return empty entries and writes return an error status. A Redis failure during a write raises RPCError. rpc wires it. See gateway.

At a glance

Source src/orchestrata_ultimate/gateway/handlers/state.py
Lines of code 186
Classes 1
Functions 1
Imports (internal) orchestrata_ultimate.core.base.traces, orchestrata_ultimate.gateway.exceptions, orchestrata_ultimate.gateway.ring_buffer
Imported by orchestrata_ultimate.gateway.handlers, orchestrata_ultimate.gateway.rpc
Tests that import it tests/gateway/handlers/test_state.py, tests/gateway/test_handlers_behavioral.py, tests/gateway/test_rpc_behavioral.py, tests/generated_gateway/test_handlers_state_generated.py

Class diagram

Class diagram orchestrata_ultimate.gateway.handlers.state

Dependency diagram

Dependency diagram orchestrata_ultimate.gateway.handlers.state

API

Classes

StateHandler

Decorators: traces(spec='016', story='State Sync', reqs=['REQ-016-274']) · Line 21

Redis-backed per-client state sync methods.

Method Signature Summary
__init__ __init__(ring_buffer: RedisRingBuffer) -> None Wire handler to state subsystems.
handle_get_all async handle_get_all(connection_id: str, params: dict[str, Any]) -> dict[str, Any] Handle state.getAll -- get all state from Redis.
handle_put_all async handle_put_all(connection_id: str, params: dict[str, Any]) -> dict[str, Any] Handle state.putAll -- set multiple keys.
handle_put async handle_put(connection_id: str, params: dict[str, Any]) -> dict[str, Any] Handle state.put -- set one key.
handle_delete async handle_delete(connection_id: str, params: dict[str, Any]) -> dict[str, Any] Handle state.delete -- delete one key.

Functions

Function Signature Summary
_state_redis_key _state_redis_key(connection_id: str) -> str Build the Redis hash key for state sync.

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_ultimate/gateway/handlers/state.py

# mypy: ignore-errors
"""State sync RPC handlers -- getAll, putAll, put, delete.

Implements SPEC-014 state synchronisation methods for UI.
"""

import json
import logging
import time
from collections.abc import Awaitable
from typing import Any, cast

from orchestrata_ultimate.core.base.traces import traces
from orchestrata_ultimate.gateway.exceptions import RPCError
from orchestrata_ultimate.gateway.ring_buffer import RedisRingBuffer

logger = logging.getLogger(__name__)


@traces(spec="016", story="State Sync", reqs=["REQ-016-274"])
class StateHandler:
    """Redis-backed per-client state sync methods."""

    def __init__(self, ring_buffer: RedisRingBuffer) -> None:
        """Wire handler to state subsystems.

        Args:
            ring_buffer: Redis ring buffer (used for client access).
        """
        self._ring_buffer = ring_buffer

    async def handle_get_all(
        self,
        connection_id: str,
        params: dict[str, Any],
    ) -> dict[str, Any]:
        """Handle state.getAll -- get all state from Redis.

        Args:
            connection_id: Caller connection ID.
            params: Ignored.

        Returns:
            All key-value entries for this client.
        """
        redis_client = self._ring_buffer.client
        if not redis_client:
            return {"entries": {}}
        key = _state_redis_key(connection_id)
        try:
            # redis-py's command mixins are shared between the sync and
            # async clients, so `hgetall()` is stub-typed `Awaitable[T] | T`;
            # `redis_client` here is always the async client, so it always
            # actually returns an awaitable.
            raw = await cast("Awaitable[dict[str, str]]", redis_client.hgetall(key))
            entries: dict[str, Any] = {}
            for k, v in raw.items():
                try:
                    entries[k] = json.loads(v)
                except json.JSONDecodeError, TypeError:
                    entries[k] = v
            return {"entries": entries}
        except Exception:
            logger.warning("state.getAll failed")
            return {"entries": {}}

    async def handle_put_all(
        self,
        connection_id: str,
        params: dict[str, Any],
    ) -> dict[str, Any]:
        """Handle state.putAll -- set multiple keys.

        Args:
            connection_id: Caller connection ID.
            params: entries dict of key -> {value, timestamp}.

        Returns:
            Status with count of keys set.
        """
        entries = params.get("entries", {})
        if not entries:
            return {"status": "ok", "count": 0}
        redis_client = self._ring_buffer.client
        if not redis_client:
            return {
                "status": "error",
                "error": "Redis unavailable",
            }
        key = _state_redis_key(connection_id)
        mapping: dict[str, str] = {}
        for k, v in entries.items():
            mapping[k] = json.dumps(v)
        try:
            # Stub deficiency (see handle_get_all's hgetall cast).
            await cast("Awaitable[int]", redis_client.hset(key, mapping=mapping))
            return {"status": "ok", "count": len(mapping)}
        except Exception as exc:
            raise RPCError(f"state.putAll failed: {exc}")

    async def handle_put(
        self,
        connection_id: str,
        params: dict[str, Any],
    ) -> dict[str, Any]:
        """Handle state.put -- set one key.

        Args:
            connection_id: Caller connection ID.
            params: key, value, optional timestamp.

        Returns:
            Status confirmation.
        """
        state_key = params.get("key", "")
        if not state_key:
            raise RPCError(
                "Missing 'key' parameter",
                rpc_code=-32602,
            )
        value = params.get("value")
        redis_client = self._ring_buffer.client
        if not redis_client:
            return {
                "status": "error",
                "error": "Redis unavailable",
            }
        key = _state_redis_key(connection_id)
        payload = json.dumps(
            {
                "value": value,
                "timestamp": params.get("timestamp", time.time()),
            }
        )
        try:
            # Stub deficiency (see handle_get_all's hgetall cast).
            await cast("Awaitable[int]", redis_client.hset(key, state_key, payload))
            return {"status": "ok", "key": state_key}
        except Exception as exc:
            raise RPCError(f"state.put failed: {exc}")

    async def handle_delete(
        self,
        connection_id: str,
        params: dict[str, Any],
    ) -> dict[str, Any]:
        """Handle state.delete -- delete one key.

        Args:
            connection_id: Caller connection ID.
            params: key to delete.

        Returns:
            Status confirmation.
        """
        state_key = params.get("key", "")
        if not state_key:
            raise RPCError(
                "Missing 'key' parameter",
                rpc_code=-32602,
            )
        redis_client = self._ring_buffer.client
        if not redis_client:
            return {
                "status": "error",
                "error": "Redis unavailable",
            }
        key = _state_redis_key(connection_id)
        try:
            # Stub deficiency (see handle_get_all's hgetall cast).
            await cast("Awaitable[int]", redis_client.hdel(key, state_key))
            return {"status": "ok", "key": state_key}
        except Exception as exc:
            raise RPCError(f"state.delete failed: {exc}")


def _state_redis_key(connection_id: str) -> str:
    """Build the Redis hash key for state sync.

    Args:
        connection_id: Client connection identifier.

    Returns:
        Redis key string.
    """
    return f"orchestrata:commandcenter:state:{connection_id}"