Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setup option endpoint #897

Merged
merged 4 commits into from
Jul 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions pyoverkiz/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
Gateway,
HistoryExecution,
LocalToken,
Option,
OptionParameter,
OverkizServer,
Place,
Scenario,
Expand Down Expand Up @@ -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(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for the fact that this throws a very generic and ugly exception on the Overkiz side, if the option does not exist...

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 = {}
Expand Down
34 changes: 33 additions & 1 deletion pyoverkiz/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 []
)
12 changes: 12 additions & 0 deletions tests/fixtures/endpoints/setup-options-developerMode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"creationTime": 1649319764000,
"lastUpdateTime": 1649319764000,
"optionId": "developerMode-1234-5678-1234",
"startDate": 1649319764000,
"parameters": [
{
"name": "gatewayId",
"value": "1234-5678-1234"
}
]
}
1 change: 1 addition & 0 deletions tests/fixtures/endpoints/setup-options-empty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
42 changes: 42 additions & 0 deletions tests/fixtures/endpoints/setup-options.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
]
54 changes: 54 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import json
import os
from unittest.mock import patch
Expand All @@ -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__))
Expand Down Expand Up @@ -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=""):
Expand Down