diff --git a/src/waylay/sdk/client.py b/src/waylay/sdk/client.py index becb5b4..c462e67 100644 --- a/src/waylay/sdk/client.py +++ b/src/waylay/sdk/client.py @@ -3,6 +3,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional +from waylay.sdk.services.gateway import GatewayService + from .plugin.client import WithServicesAndTools from .config.client import WaylayConfig, WithConfig, HttpClientOptions from .api import ApiClient @@ -23,6 +25,7 @@ class WaylayClient(WithConfig, WithServicesAndTools): """REST client for the Waylay Platform.""" + gateway: GatewayService alarms: "AlarmsService" data: "DataService" registry: "RegistryService" diff --git a/src/waylay/sdk/plugin/loader.py b/src/waylay/sdk/plugin/loader.py index 8197739..006308b 100644 --- a/src/waylay/sdk/plugin/loader.py +++ b/src/waylay/sdk/plugin/loader.py @@ -3,6 +3,6 @@ from typing import Type, List from .base import WaylayPlugin +from waylay.sdk.services.gateway import GatewayService -# no default services or tools for now. -PLUGINS: List[Type[WaylayPlugin]] = [] +PLUGINS: List[Type[WaylayPlugin]] = [GatewayService] diff --git a/src/waylay/sdk/services/gateway.py b/src/waylay/sdk/services/gateway.py new file mode 100644 index 0000000..c6ce9ee --- /dev/null +++ b/src/waylay/sdk/services/gateway.py @@ -0,0 +1,160 @@ +"""Gateway Service.""" + +from __future__ import annotations + +from typing import Any, Dict, Literal, TypeVar, overload +from pydantic import ConfigDict + +from waylay.sdk.api.client import ApiClient +from waylay.sdk.api.http import HeaderTypes, QueryParamTypes, Response +from waylay.sdk.plugin.base import WaylayService, WithApiClient +from waylay.sdk.api._models import BaseModel as WaylayBaseModel, Model + +T = TypeVar("T") + + +class GatewayService(WaylayService): + """Gateway Service Class.""" + + name = "gateway" + title = "Gateway Service" + + about: AboutApi + + def __init__(self, api_client: ApiClient): + """Create the gateway service.""" + super().__init__(api_client) + self.about = AboutApi(api_client) + + +class AboutApi(WithApiClient): + """About service methods.""" + + @overload + async def get( + self, + *, + query: QueryParamTypes | None = None, + raw_response: Literal[False] = False, + select_path: Literal[""] = "", + response_type: Literal[None] = None, + headers: HeaderTypes | None = None, + **kwargs, + ) -> GatewayResponse: ... + + @overload + async def get( + self, + *, + query: QueryParamTypes | None = None, + raw_response: Literal[False] = False, + select_path: Literal[""] = "", + response_type: T, + headers: HeaderTypes | None = None, + **kwargs, + ) -> T: ... + + @overload + async def get( + self, + *, + query: QueryParamTypes | None = None, + raw_response: Literal[True], + select_path: Literal["_not_used_"] = "_not_used_", + response_type: Literal[None] = None, # not used + headers: HeaderTypes | None = None, + **kwargs, + ) -> Response: ... + + @overload + async def get( + self, + *, + query: QueryParamTypes | None = None, + raw_response: Literal[False] = False, + select_path: str, + response_type: Literal[None] = None, + headers: HeaderTypes | None = None, + **kwargs, + ) -> Model: ... + + @overload + async def get( + self, + *, + query: QueryParamTypes | None = None, + raw_response: Literal[False] = False, + select_path: str, + response_type: T, + headers: HeaderTypes | None = None, + **kwargs, + ) -> T: ... + + async def get( + self, + *, + query: QueryParamTypes | None = None, + raw_response: bool = False, + select_path: str = "", + response_type: T | None = None, + headers: HeaderTypes | None = None, + **kwargs, + ) -> GatewayResponse | T | Response | Model: + """Get the status of the gateway. + + :param query: URL Query parameters. + :type query: QueryParamTypes, optional + :param raw_response: If true, return the http Response object instead of returning an api model object, or throwing an ApiError. + :param select_path: Denotes the json path applied to the response object before returning it. + Set it to the empty string `""` to receive the full response object. + :param response_type: If specified, the response is parsed into an instance of the specified type. + :param headers: Header parameters for this request + :type headers: dict, optional + :param `**kwargs`: Additional parameters passed on to the http client. + See below. + :Keyword Arguments: + * timeout: a single numeric timeout in seconds, + or a tuple of _connect_, _read_, _write_ and _pool_ timeouts. + * stream: if true, the response will be in streaming mode + * cookies + * extensions + * auth + * follow_redirects: bool + + :return: Returns the result object if the http request succeeded with status code '2XX'. + :raises APIError: If the http request has a status code different from `2XX`. This + object wraps both the http Response and any parsed data. + """ + response_type_map: Dict[str, Any] = ( + {"2XX": response_type} + if response_type is not None + else { + "200": GatewayResponse if not select_path else Model, + } + ) + return await self.api_client.request( + method="GET", + resource_path="/", + path_params={}, + params=query, + headers=headers, + response_type=response_type_map, + select_path=select_path, + raw_response=raw_response, + **kwargs, + ) + + +class GatewayResponse(WaylayBaseModel): + """Gateway status response.""" + + name: str + version: str + health: str + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + extra="allow", + ) diff --git a/test/unit/services/gateway_test.py b/test/unit/services/gateway_test.py new file mode 100644 index 0000000..a6b08aa --- /dev/null +++ b/test/unit/services/gateway_test.py @@ -0,0 +1,26 @@ +import pytest +from pytest_httpx import HTTPXMock +from typeguard import check_type + +from waylay.sdk.client import WaylayClient +from waylay.sdk.services.gateway import GatewayResponse, GatewayService + + +def test_gateway_service_loaded(client: WaylayClient): + """Test plugin classes are loaded.""" + assert isinstance(client.gateway, GatewayService) + + +@pytest.mark.asyncio +async def test_gateway_about(client: WaylayClient, httpx_mock: HTTPXMock): + """Test gateway about status check""" + httpx_mock.add_response( + method="GET", + url=client.config.gateway_url + "/", + json={"name": "gateway", "version": "1.2.3", "health": "OK"}, + status_code=200, + ) + client.api_client.set_options({"auth": None}) + async with client: + resp = await client.gateway.about.get() + check_type(resp, GatewayResponse)