From 0e73239d0866416ca950aa11fc7c8980c6fdf5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mose=20M=C3=BCller?= Date: Thu, 25 Jul 2024 13:24:34 +0200 Subject: [PATCH] adds API versioning --- src/pydase/server/web_server/api/__init__.py | 24 ++++++++++ .../server/web_server/api/v1/__init__.py | 0 .../{api.py => api/v1/application.py} | 46 +++++-------------- .../server/web_server/api/v1/endpoints.py | 32 +++++++++++++ src/pydase/server/web_server/sio_setup.py | 8 ++-- 5 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 src/pydase/server/web_server/api/__init__.py create mode 100644 src/pydase/server/web_server/api/v1/__init__.py rename src/pydase/server/web_server/{api.py => api/v1/application.py} (52%) create mode 100644 src/pydase/server/web_server/api/v1/endpoints.py diff --git a/src/pydase/server/web_server/api/__init__.py b/src/pydase/server/web_server/api/__init__.py new file mode 100644 index 0000000..c63dafb --- /dev/null +++ b/src/pydase/server/web_server/api/__init__.py @@ -0,0 +1,24 @@ +import logging + +import aiohttp.web +import aiohttp_middlewares.error + +import pydase.server.web_server.api.v1.application +from pydase.data_service.state_manager import StateManager + +logger = logging.getLogger(__name__) + + +def create_api_application(state_manager: StateManager) -> aiohttp.web.Application: + api_application = aiohttp.web.Application( + middlewares=(aiohttp_middlewares.error.error_middleware(),) + ) + + api_application.add_subapp( + "/v1/", + pydase.server.web_server.api.v1.application.create_api_application( + state_manager + ), + ) + + return api_application diff --git a/src/pydase/server/web_server/api/v1/__init__.py b/src/pydase/server/web_server/api/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/pydase/server/web_server/api.py b/src/pydase/server/web_server/api/v1/application.py similarity index 52% rename from src/pydase/server/web_server/api.py rename to src/pydase/server/web_server/api/v1/application.py index d0d6805..41868a7 100644 --- a/src/pydase/server/web_server/api.py +++ b/src/pydase/server/web_server/api/v1/application.py @@ -1,45 +1,25 @@ import logging -from typing import Any +from typing import TYPE_CHECKING import aiohttp.web import aiohttp_middlewares.error from pydase.data_service.state_manager import StateManager -from pydase.server.web_server.sio_setup import TriggerMethodDict, UpdateDict -from pydase.utils.helpers import get_object_attr_from_path -from pydase.utils.serialization.deserializer import loads +from pydase.server.web_server.api.v1.endpoints import ( + get_value, + trigger_method, + update_value, +) from pydase.utils.serialization.serializer import dump -from pydase.utils.serialization.types import SerializedObject + +if TYPE_CHECKING: + from pydase.server.web_server.sio_setup import TriggerMethodDict, UpdateDict logger = logging.getLogger(__name__) API_VERSION = "v1" -def update_value(state_manager: StateManager, data: UpdateDict) -> None: - path = data["access_path"] - - state_manager.set_service_attribute_value_by_path( - path=path, serialized_value=data["value"] - ) - - -def get_value(state_manager: StateManager, access_path: str) -> SerializedObject: - return state_manager._data_service_cache.get_value_dict_from_cache(access_path) - - -def trigger_method(state_manager: StateManager, data: TriggerMethodDict) -> Any: - method = get_object_attr_from_path(state_manager.service, data["access_path"]) - - serialized_args = data.get("args", None) - args = loads(serialized_args) if serialized_args else [] - - serialized_kwargs = data.get("kwargs", None) - kwargs: dict[str, Any] = loads(serialized_kwargs) if serialized_kwargs else {} - - return dump(method(*args, **kwargs)) - - def create_api_application(state_manager: StateManager) -> aiohttp.web.Application: api_application = aiohttp.web.Application( middlewares=(aiohttp_middlewares.error.error_middleware(),) @@ -47,8 +27,6 @@ def create_api_application(state_manager: StateManager) -> aiohttp.web.Applicati async def _get_value(request: aiohttp.web.Request) -> aiohttp.web.Response: logger.info("Handle api request: %s", request) - api_version = request.match_info["version"] - logger.info("Version number: %s", api_version) access_path = request.rel_url.query["access_path"] @@ -81,8 +59,8 @@ def create_api_application(state_manager: StateManager) -> aiohttp.web.Applicati logger.exception(e) return aiohttp.web.json_response(dump(e)) - api_application.router.add_get("/{version}/get_value", _get_value) - api_application.router.add_post("/{version}/update_value", _update_value) - api_application.router.add_post("/{version}/trigger_method", _trigger_method) + api_application.router.add_get("/get_value", _get_value) + api_application.router.add_post("/update_value", _update_value) + api_application.router.add_post("/trigger_method", _trigger_method) return api_application diff --git a/src/pydase/server/web_server/api/v1/endpoints.py b/src/pydase/server/web_server/api/v1/endpoints.py new file mode 100644 index 0000000..bad0dcf --- /dev/null +++ b/src/pydase/server/web_server/api/v1/endpoints.py @@ -0,0 +1,32 @@ +from typing import Any + +from pydase.data_service.state_manager import StateManager +from pydase.server.web_server.sio_setup import TriggerMethodDict, UpdateDict +from pydase.utils.helpers import get_object_attr_from_path +from pydase.utils.serialization.deserializer import loads +from pydase.utils.serialization.serializer import dump +from pydase.utils.serialization.types import SerializedObject + + +def update_value(state_manager: StateManager, data: UpdateDict) -> None: + path = data["access_path"] + + state_manager.set_service_attribute_value_by_path( + path=path, serialized_value=data["value"] + ) + + +def get_value(state_manager: StateManager, access_path: str) -> SerializedObject: + return state_manager._data_service_cache.get_value_dict_from_cache(access_path) + + +def trigger_method(state_manager: StateManager, data: TriggerMethodDict) -> Any: + method = get_object_attr_from_path(state_manager.service, data["access_path"]) + + serialized_args = data.get("args", None) + args = loads(serialized_args) if serialized_args else [] + + serialized_kwargs = data.get("kwargs", None) + kwargs: dict[str, Any] = loads(serialized_kwargs) if serialized_kwargs else {} + + return dump(method(*args, **kwargs)) diff --git a/src/pydase/server/web_server/sio_setup.py b/src/pydase/server/web_server/sio_setup.py index 1b2c3fc..5187f85 100644 --- a/src/pydase/server/web_server/sio_setup.py +++ b/src/pydase/server/web_server/sio_setup.py @@ -11,7 +11,7 @@ else: import click import socketio # type: ignore[import-untyped] -import pydase.server.web_server.api +import pydase.server.web_server.api.v1.endpoints import pydase.utils.serialization.deserializer import pydase.utils.serialization.serializer from pydase.data_service.data_service_observer import DataServiceObserver @@ -145,7 +145,7 @@ def setup_sio_events(sio: socketio.AsyncServer, state_manager: StateManager) -> @sio.event async def update_value(sid: str, data: UpdateDict) -> SerializedObject | None: try: - pydase.server.web_server.api.update_value( + pydase.server.web_server.api.v1.endpoints.update_value( state_manager=state_manager, data=data ) except Exception as e: @@ -156,7 +156,7 @@ def setup_sio_events(sio: socketio.AsyncServer, state_manager: StateManager) -> @sio.event async def get_value(sid: str, access_path: str) -> SerializedObject: try: - return pydase.server.web_server.api.get_value( + return pydase.server.web_server.api.v1.endpoints.get_value( state_manager=state_manager, access_path=access_path ) except Exception as e: @@ -166,7 +166,7 @@ def setup_sio_events(sio: socketio.AsyncServer, state_manager: StateManager) -> @sio.event async def trigger_method(sid: str, data: TriggerMethodDict) -> Any: try: - return pydase.server.web_server.api.trigger_method( + return pydase.server.web_server.api.v1.endpoints.trigger_method( state_manager=state_manager, data=data ) except Exception as e: