diff --git a/robot-server/robot_server/client_data/router.py b/robot-server/robot_server/client_data/router.py index 35ec8c6e06c4..a70e94522aed 100644 --- a/robot-server/robot_server/client_data/router.py +++ b/robot-server/robot_server/client_data/router.py @@ -13,6 +13,10 @@ from robot_server.errors.error_responses import ErrorBody, ErrorDetails from robot_server.service.json_api.request import RequestModel from robot_server.service.json_api.response import SimpleBody, SimpleEmptyBody +from robot_server.service.notifications.publishers.client_data_publisher import ( + ClientDataPublisher, + get_client_data_publisher, +) router = fastapi.APIRouter() @@ -62,9 +66,13 @@ class ClientDataKeyDoesNotExist(ErrorDetails): async def put_client_data( # noqa: D103 key: Key, request_body: RequestModel[ClientData], - store: ClientDataStore = fastapi.Depends(get_client_data_store), + store: Annotated[ClientDataStore, fastapi.Depends(get_client_data_store)], + client_data_publisher: Annotated[ + ClientDataPublisher, fastapi.Depends(get_client_data_publisher) + ], ) -> SimpleBody[ClientData]: store.put(key, request_body.data) + await client_data_publisher.publish_client_data(key) return SimpleBody.construct(data=store.get(key)) @@ -104,7 +112,10 @@ async def get_client_data( # noqa: D103 ) async def delete_client_data( # noqa: D103 key: Key, - store: ClientDataStore = fastapi.Depends(get_client_data_store), + store: Annotated[ClientDataStore, fastapi.Depends(get_client_data_store)], + client_data_publisher: Annotated[ + ClientDataPublisher, fastapi.Depends(get_client_data_publisher) + ], ) -> SimpleEmptyBody: try: store.delete(key) @@ -113,6 +124,7 @@ async def delete_client_data( # noqa: D103 fastapi.status.HTTP_404_NOT_FOUND ) from e else: + await client_data_publisher.publish_client_data(key) return SimpleEmptyBody.construct() @@ -125,4 +137,8 @@ async def delete_all_client_data( # noqa: D103 store: ClientDataStore = fastapi.Depends(get_client_data_store), ) -> SimpleEmptyBody: store.delete_all() + # FIXME(mm, 2024-07-24): We need to notify MQTT subscribers to + # `robot-server/client/{key}` (for all values of `key`). + # We can do it like RunsPublisher and drive the notifications from the underlying + # store instead of driving notifications from these endpoint functions. return SimpleEmptyBody.construct() diff --git a/robot-server/robot_server/service/notifications/publishers/client_data_publisher.py b/robot-server/robot_server/service/notifications/publishers/client_data_publisher.py new file mode 100644 index 000000000000..77225368a353 --- /dev/null +++ b/robot-server/robot_server/service/notifications/publishers/client_data_publisher.py @@ -0,0 +1,29 @@ +from typing import Annotated +import fastapi +from robot_server.service.notifications import topics +from robot_server.service.notifications.notification_client import ( + NotificationClient, + get_notification_client, +) + + +class ClientDataPublisher: + """Publishes clientData topics.""" + + def __init__(self, client: NotificationClient) -> None: + self._client = client + + async def publish_client_data(self, client_data_key: str) -> None: + """Publish the equivalent of `GET /clientData/{key}`.""" + await self._client.publish_advise_refetch_async( + topics.client_data(client_data_key) + ) + + +async def get_client_data_publisher( + notification_client: Annotated[ + NotificationClient, fastapi.Depends(get_notification_client) + ], +) -> ClientDataPublisher: + """Return a ClientDataPublisher for use by FastAPI endpoints.""" + return ClientDataPublisher(notification_client)