diff --git a/pyoverkiz/client.py b/pyoverkiz/client.py index a43d32b0..14dfceaa 100644 --- a/pyoverkiz/client.py +++ b/pyoverkiz/client.py @@ -65,6 +65,8 @@ Gateway, HistoryExecution, LocalToken, + Option, + OptionParameter, OverkizServer, Place, Scenario, @@ -758,6 +760,64 @@ async def execute_scheduled_scenario(self, oid: str, timestamp: int) -> str: response = await self.__post(f"exec/schedule/{oid}/{timestamp}") return cast(str, response["triggerId"]) + @backoff.on_exception( + backoff.expo, + (NotAuthenticatedException, ServerDisconnectedError), + max_tries=2, + on_backoff=relogin, + ) + async def get_setup_options(self) -> list[Option]: + """ + This operation returns all subscribed options of a given setup. + Per-session rate-limit : 1 calls per 1d period for this particular operation (bulk-load) + Access scope : Full enduser API access (enduser/*) + """ + response = await self.__get("setup/options") + options = [Option(**o) for o in humps.decamelize(response)] + + return options + + @backoff.on_exception( + backoff.expo, + (NotAuthenticatedException, ServerDisconnectedError), + max_tries=2, + on_backoff=relogin, + ) + async def get_setup_option(self, option: str) -> Option | None: + """ + This operation returns the selected subscribed option of a given setup. + For example `developerMode-{gateway_id}` to understand if developer mode is on. + """ + response = await self.__get(f"setup/options/{option}") + + if response: + return Option(**humps.decamelize(response)) + + return None + + @backoff.on_exception( + backoff.expo, + (NotAuthenticatedException, ServerDisconnectedError), + max_tries=2, + on_backoff=relogin, + ) + async def get_setup_option_parameter( + self, option: str, parameter: str + ) -> OptionParameter | None: + """ + This operation returns the selected parameters of a given setup and option. + For example `developerMode-{gateway_id}` and `gatewayId` to understand if developer mode is on. + + If the option is not available, an OverkizException will be thrown. + If the parameter is not available you will receive None. + """ + response = await self.__get(f"setup/options/{option}/{parameter}") + + if response: + return OptionParameter(**humps.decamelize(response)) + + return None + async def __get(self, path: str) -> Any: """Make a GET request to the OverKiz API""" headers = {} diff --git a/pyoverkiz/models.py b/pyoverkiz/models.py index c61d8696..ab681326 100644 --- a/pyoverkiz/models.py +++ b/pyoverkiz/models.py @@ -221,7 +221,6 @@ def __init__( @define(init=False, kw_only=True) class StateDefinition: - qualified_name: str type: str | None = None values: list[str] | None = None @@ -797,3 +796,36 @@ class LocalToken: gateway_creation_time: int uuid: str scope: str + + +@define(kw_only=True) +class OptionParameter: + name: str + value: str + + +@define(init=False, kw_only=True) +class Option: + creation_time: int + last_update_time: int + option_id: str + start_date: int + parameters: list[OptionParameter] | None + + def __init__( + self, + *, + creation_time: int, + last_update_time: int, + option_id: str, + start_date: int, + parameters: list[dict[str, Any]] | None, + **_: Any, + ) -> None: + self.creation_time = creation_time + self.last_update_time = last_update_time + self.option_id = option_id + self.start_date = start_date + self.parameters = ( + [OptionParameter(**p) for p in parameters] if parameters else [] + ) diff --git a/tests/fixtures/endpoints/setup-options-developerMode.json b/tests/fixtures/endpoints/setup-options-developerMode.json new file mode 100644 index 00000000..6196c889 --- /dev/null +++ b/tests/fixtures/endpoints/setup-options-developerMode.json @@ -0,0 +1,12 @@ +{ + "creationTime": 1649319764000, + "lastUpdateTime": 1649319764000, + "optionId": "developerMode-1234-5678-1234", + "startDate": 1649319764000, + "parameters": [ + { + "name": "gatewayId", + "value": "1234-5678-1234" + } + ] +} diff --git a/tests/fixtures/endpoints/setup-options-empty.json b/tests/fixtures/endpoints/setup-options-empty.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/fixtures/endpoints/setup-options-empty.json @@ -0,0 +1 @@ +{} diff --git a/tests/fixtures/endpoints/setup-options.json b/tests/fixtures/endpoints/setup-options.json new file mode 100644 index 00000000..c19da72f --- /dev/null +++ b/tests/fixtures/endpoints/setup-options.json @@ -0,0 +1,42 @@ +[ + { + "creationTime": 1586341958000, + "lastUpdateTime": 1586341958000, + "optionId": "emailUsage", + "startDate": 1586341958000, + "parameters": [ + { + "name": "emailAddresses", + "value": "name@domain.com" + } + ] + }, + { + "creationTime": 1649319764000, + "lastUpdateTime": 1649319764000, + "optionId": "developerMode-1234-5678-1234", + "startDate": 1649319764000, + "parameters": [ + { + "name": "gatewayId", + "value": "1234-5678-1234" + } + ] + }, + { + "creationTime": 1586341958000, + "lastUpdateTime": 1586341958000, + "optionId": "protocolAccess-1234-5678-1234-13", + "startDate": 1586341958000, + "parameters": [ + { + "name": "gatewayId", + "value": "1234-5678-1234" + }, + { + "name": "protocolType", + "value": "13" + } + ] + } +] diff --git a/tests/test_client.py b/tests/test_client.py index e3be5e4c..5bd32817 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import os from unittest.mock import patch @@ -10,6 +12,7 @@ from pyoverkiz.client import OverkizClient from pyoverkiz.const import SUPPORTED_SERVERS from pyoverkiz.enums import APIType, DataType +from pyoverkiz.models import Option from pyoverkiz.utils import generate_local_server CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -338,6 +341,57 @@ async def test_check_response_exception_handling( await client.check_response(resp) + @pytest.mark.asyncio + async def test_get_setup_options( + self, + client: OverkizClient, + ): + with open( + os.path.join(CURRENT_DIR, "fixtures/endpoints/setup-options.json"), + encoding="utf-8", + ) as raw_events: + resp = MockResponse(raw_events.read()) + + with patch.object(aiohttp.ClientSession, "get", return_value=resp): + options = await client.get_setup_options() + assert len(options) == 3 + + for option in options: + assert isinstance(option, Option) + + @pytest.mark.parametrize( + "fixture_name, option_name, instance", + [ + ( + "setup-options-developerMode.json", + "developerMode-1234-5678-1234", + Option, + ), + ("setup-options-empty.json", "test", None), + ], + ) + @pytest.mark.asyncio + async def test_get_setup_option( + self, + client: OverkizClient, + fixture_name: str, + option_name: str, + instance: Option | None, + ): + with open( + os.path.join(CURRENT_DIR, "fixtures/endpoints/" + fixture_name), + encoding="utf-8", + ) as raw_events: + resp = MockResponse(raw_events.read()) + + with patch.object(aiohttp.ClientSession, "get", return_value=resp): + option = await client.get_setup_option(option_name) + + if instance is None: + assert option is None + else: + assert isinstance(option, instance) + class MockResponse: def __init__(self, text, status=200, url=""):