mirror of
https://github.com/home-assistant/core.git
synced 2025-04-19 14:57:52 +00:00
Use aiortm library in remember the milk
This commit is contained in:
parent
cbc1899990
commit
f7511457c3
@ -1,12 +1,13 @@
|
||||
"""Support to interact with Remember The Milk."""
|
||||
|
||||
from rtmapi import Rtm
|
||||
from aiortm import AioRTMClient, Auth, AuthError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import configurator
|
||||
from homeassistant.const import CONF_API_KEY, CONF_ID, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
@ -82,11 +83,18 @@ async def _create_instance(
|
||||
stored_rtm_config: RememberTheMilkConfiguration,
|
||||
component: EntityComponent[RememberTheMilkEntity],
|
||||
) -> None:
|
||||
entity = RememberTheMilkEntity(
|
||||
account_name, api_key, shared_secret, token, stored_rtm_config
|
||||
client = AioRTMClient(
|
||||
Auth(
|
||||
async_get_clientsession(hass),
|
||||
api_key,
|
||||
shared_secret,
|
||||
token,
|
||||
permission="delete",
|
||||
)
|
||||
)
|
||||
entity = RememberTheMilkEntity(account_name, client, stored_rtm_config)
|
||||
LOGGER.debug("Instance created for account %s", entity.name)
|
||||
await entity.check_token(hass)
|
||||
await entity.check_token()
|
||||
await component.async_add_entities([entity])
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
@ -111,27 +119,29 @@ async def _register_new_account(
|
||||
component: EntityComponent[RememberTheMilkEntity],
|
||||
) -> None:
|
||||
"""Register a new account."""
|
||||
api = Rtm(api_key, shared_secret, "write", None)
|
||||
url, frob = await hass.async_add_executor_job(api.authenticate_desktop)
|
||||
auth = Auth(
|
||||
async_get_clientsession(hass), api_key, shared_secret, permission="write"
|
||||
)
|
||||
url, frob = await auth.authenticate_desktop()
|
||||
LOGGER.debug("Sent authentication request to server")
|
||||
|
||||
@callback
|
||||
def register_account_callback(fields: list[dict[str, str]]) -> None:
|
||||
"""Call for register the configurator."""
|
||||
hass.async_create_task(handle_token(api, frob))
|
||||
hass.async_create_task(handle_token(auth, frob))
|
||||
|
||||
async def handle_token(api: Rtm, frob: str) -> None:
|
||||
async def handle_token(auth: Auth, frob: str) -> None:
|
||||
"""Handle token."""
|
||||
await hass.async_add_executor_job(api.retrieve_token, frob)
|
||||
|
||||
token: str | None = api.token
|
||||
if token is None:
|
||||
try:
|
||||
auth_data = await auth.get_token(frob)
|
||||
except AuthError:
|
||||
LOGGER.error("Failed to register, please try again")
|
||||
configurator.async_notify_errors(
|
||||
hass, request_id, "Failed to register, please try again."
|
||||
)
|
||||
return
|
||||
|
||||
token: str = auth_data["token"]
|
||||
stored_rtm_config.set_token(account_name, token)
|
||||
LOGGER.debug("Retrieved new token from server")
|
||||
|
||||
|
@ -2,12 +2,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from rtmapi import Rtm, RtmRequestFailedException
|
||||
from aiortm import AioRTMClient, AioRTMError, AuthError
|
||||
|
||||
from homeassistant.const import CONF_ID, CONF_NAME, STATE_OK
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.core import ServiceCall
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import LOGGER
|
||||
@ -20,35 +18,43 @@ class RememberTheMilkEntity(Entity):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
api_key: str,
|
||||
shared_secret: str,
|
||||
token: str,
|
||||
client: AioRTMClient,
|
||||
rtm_config: RememberTheMilkConfiguration,
|
||||
) -> None:
|
||||
"""Create new instance of Remember The Milk component."""
|
||||
self._name = name
|
||||
self._api_key = api_key
|
||||
self._shared_secret = shared_secret
|
||||
self._token = token
|
||||
self._rtm_config = rtm_config
|
||||
self._rtm_api = Rtm(api_key, shared_secret, "delete", token)
|
||||
self._client = client
|
||||
self._token_valid = False
|
||||
|
||||
async def check_token(self, hass: HomeAssistant) -> None:
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Run when entity about to be added to hass."""
|
||||
await self.check_token()
|
||||
|
||||
async def check_token(self) -> None:
|
||||
"""Check if the API token is still valid.
|
||||
|
||||
If it is not valid any more, delete it from the configuration. This
|
||||
will trigger a new authentication process.
|
||||
"""
|
||||
valid = await hass.async_add_executor_job(self._rtm_api.token_valid)
|
||||
if valid:
|
||||
try:
|
||||
await self._client.rtm.api.check_token()
|
||||
except AuthError as err:
|
||||
LOGGER.error(
|
||||
"Token for account %s is invalid. You need to register again: %s",
|
||||
self.name,
|
||||
err,
|
||||
)
|
||||
except AioRTMError as err:
|
||||
LOGGER.error(
|
||||
"Error checking token for account %s. You need to register again: %s",
|
||||
self.name,
|
||||
err,
|
||||
)
|
||||
else:
|
||||
self._token_valid = True
|
||||
return
|
||||
|
||||
LOGGER.error(
|
||||
"Token for account %s is invalid. You need to register again!",
|
||||
self.name,
|
||||
)
|
||||
self._rtm_config.delete_token(self._name)
|
||||
self._token_valid = False
|
||||
|
||||
@ -67,10 +73,7 @@ class RememberTheMilkEntity(Entity):
|
||||
rtm_id = self._rtm_config.get_rtm_id(self._name, hass_id)
|
||||
|
||||
if rtm_id is None:
|
||||
result = await self.hass.async_add_executor_job(
|
||||
self._add_task,
|
||||
task_name,
|
||||
)
|
||||
rtm_id = await self._add_task(task_name)
|
||||
LOGGER.debug(
|
||||
"Created new task '%s' in account %s", task_name, self.name
|
||||
)
|
||||
@ -78,48 +81,51 @@ class RememberTheMilkEntity(Entity):
|
||||
self._rtm_config.set_rtm_id(
|
||||
self._name,
|
||||
hass_id,
|
||||
result.list.id,
|
||||
result.list.taskseries.id,
|
||||
result.list.taskseries.task.id,
|
||||
rtm_id[0],
|
||||
rtm_id[1],
|
||||
rtm_id[2],
|
||||
)
|
||||
else:
|
||||
await self.hass.async_add_executor_job(
|
||||
self._rename_task,
|
||||
rtm_id,
|
||||
task_name,
|
||||
)
|
||||
await self._rename_task(rtm_id, task_name)
|
||||
LOGGER.debug(
|
||||
"Updated task with id '%s' in account %s to name %s",
|
||||
hass_id,
|
||||
self.name,
|
||||
task_name,
|
||||
)
|
||||
except RtmRequestFailedException as rtm_exception:
|
||||
except AioRTMError as err:
|
||||
LOGGER.error(
|
||||
"Error creating new Remember The Milk task for account %s: %s",
|
||||
self._name,
|
||||
rtm_exception,
|
||||
err,
|
||||
)
|
||||
|
||||
def _add_task(self, task_name: str) -> Any:
|
||||
async def _add_task(self, task_name: str) -> tuple[str, str, str]:
|
||||
"""Add a task."""
|
||||
result = self._rtm_api.rtm.timelines.create()
|
||||
timeline = result.timeline.value
|
||||
return self._rtm_api.rtm.tasks.add(
|
||||
timeline_response = await self._client.rtm.timelines.create()
|
||||
timeline = timeline_response.timeline
|
||||
task_response = await self._client.rtm.tasks.add(
|
||||
timeline=timeline,
|
||||
name=task_name,
|
||||
parse="1",
|
||||
parse=True,
|
||||
)
|
||||
task_list = task_response.task_list
|
||||
task_list_id = task_list.id
|
||||
task_series = task_list.taskseries[0]
|
||||
task_series_id = task_series.id
|
||||
task = task_series.task[0]
|
||||
task_id = task.id
|
||||
return (str(task_list_id), str(task_series_id), str(task_id))
|
||||
|
||||
def _rename_task(self, rtm_id: tuple[str, str, str], task_name: str) -> None:
|
||||
async def _rename_task(self, rtm_id: tuple[str, str, str], task_name: str) -> None:
|
||||
"""Rename a task."""
|
||||
result = self._rtm_api.rtm.timelines.create()
|
||||
timeline = result.timeline.value
|
||||
self._rtm_api.rtm.tasks.setName(
|
||||
result = await self._client.rtm.timelines.create()
|
||||
timeline = result.timeline
|
||||
await self._client.rtm.tasks.set_name(
|
||||
name=task_name,
|
||||
list_id=rtm_id[0],
|
||||
taskseries_id=rtm_id[1],
|
||||
task_id=rtm_id[2],
|
||||
list_id=int(rtm_id[0]),
|
||||
taskseries_id=int(rtm_id[1]),
|
||||
task_id=int(rtm_id[2]),
|
||||
timeline=timeline,
|
||||
)
|
||||
|
||||
@ -138,26 +144,26 @@ class RememberTheMilkEntity(Entity):
|
||||
)
|
||||
return
|
||||
try:
|
||||
await self.hass.async_add_executor_job(self._complete_task, rtm_id)
|
||||
except RtmRequestFailedException as rtm_exception:
|
||||
await self._complete_task(rtm_id)
|
||||
except AioRTMError as err:
|
||||
LOGGER.error(
|
||||
"Error creating new Remember The Milk task for account %s: %s",
|
||||
self._name,
|
||||
rtm_exception,
|
||||
err,
|
||||
)
|
||||
return
|
||||
|
||||
self._rtm_config.delete_rtm_id(self._name, hass_id)
|
||||
LOGGER.debug("Completed task with id %s in account %s", hass_id, self._name)
|
||||
|
||||
def _complete_task(self, rtm_id: tuple[str, str, str]) -> None:
|
||||
async def _complete_task(self, rtm_id: tuple[str, str, str]) -> None:
|
||||
"""Complete a task."""
|
||||
result = self._rtm_api.rtm.timelines.create()
|
||||
timeline = result.timeline.value
|
||||
self._rtm_api.rtm.tasks.complete(
|
||||
list_id=rtm_id[0],
|
||||
taskseries_id=rtm_id[1],
|
||||
task_id=rtm_id[2],
|
||||
result = await self._client.rtm.timelines.create()
|
||||
timeline = result.timeline
|
||||
await self._client.rtm.tasks.complete(
|
||||
list_id=int(rtm_id[0]),
|
||||
taskseries_id=int(rtm_id[1]),
|
||||
task_id=int(rtm_id[2]),
|
||||
timeline=timeline,
|
||||
)
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
"codeowners": [],
|
||||
"dependencies": ["configurator"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/remember_the_milk",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["rtmapi"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aiortm"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["RtmAPI==0.7.2", "httplib2==0.20.4"]
|
||||
"requirements": ["aiortm==0.10.0"]
|
||||
}
|
||||
|
@ -5232,7 +5232,7 @@
|
||||
"name": "Remember The Milk",
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"iot_class": "cloud_push"
|
||||
"iot_class": "cloud_polling"
|
||||
},
|
||||
"renault": {
|
||||
"name": "Renault",
|
||||
|
9
requirements_all.txt
generated
9
requirements_all.txt
generated
@ -111,9 +111,6 @@ RachioPy==1.1.0
|
||||
# homeassistant.components.python_script
|
||||
RestrictedPython==8.0
|
||||
|
||||
# homeassistant.components.remember_the_milk
|
||||
RtmAPI==0.7.2
|
||||
|
||||
# homeassistant.components.recorder
|
||||
# homeassistant.components.sql
|
||||
SQLAlchemy==2.0.38
|
||||
@ -358,6 +355,9 @@ aiorecollect==2023.09.0
|
||||
# homeassistant.components.ridwell
|
||||
aioridwell==2024.01.0
|
||||
|
||||
# homeassistant.components.remember_the_milk
|
||||
aiortm==0.10.0
|
||||
|
||||
# homeassistant.components.ruckus_unleashed
|
||||
aioruckus==0.42
|
||||
|
||||
@ -1157,9 +1157,6 @@ homematicip==1.1.7
|
||||
# homeassistant.components.horizon
|
||||
horimote==0.4.1
|
||||
|
||||
# homeassistant.components.remember_the_milk
|
||||
httplib2==0.20.4
|
||||
|
||||
# homeassistant.components.huawei_lte
|
||||
huawei-lte-api==1.10.0
|
||||
|
||||
|
9
requirements_test_all.txt
generated
9
requirements_test_all.txt
generated
@ -105,9 +105,6 @@ RachioPy==1.1.0
|
||||
# homeassistant.components.python_script
|
||||
RestrictedPython==8.0
|
||||
|
||||
# homeassistant.components.remember_the_milk
|
||||
RtmAPI==0.7.2
|
||||
|
||||
# homeassistant.components.recorder
|
||||
# homeassistant.components.sql
|
||||
SQLAlchemy==2.0.38
|
||||
@ -340,6 +337,9 @@ aiorecollect==2023.09.0
|
||||
# homeassistant.components.ridwell
|
||||
aioridwell==2024.01.0
|
||||
|
||||
# homeassistant.components.remember_the_milk
|
||||
aiortm==0.10.0
|
||||
|
||||
# homeassistant.components.ruckus_unleashed
|
||||
aioruckus==0.42
|
||||
|
||||
@ -983,9 +983,6 @@ home-assistant-intents==2025.2.5
|
||||
# homeassistant.components.homematicip_cloud
|
||||
homematicip==1.1.7
|
||||
|
||||
# homeassistant.components.remember_the_milk
|
||||
httplib2==0.20.4
|
||||
|
||||
# homeassistant.components.huawei_lte
|
||||
huawei-lte-api==1.10.0
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Provide common pytest fixtures."""
|
||||
|
||||
from collections.abc import AsyncGenerator, Generator
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -10,20 +10,48 @@ from homeassistant.core import HomeAssistant
|
||||
from .const import TOKEN
|
||||
|
||||
|
||||
@pytest.fixture(name="auth", autouse=True)
|
||||
def auth_fixture() -> Generator[MagicMock]:
|
||||
"""Create a mock auth."""
|
||||
with patch(
|
||||
"homeassistant.components.remember_the_milk.Auth", autospec=True
|
||||
) as auth_class:
|
||||
auth = auth_class.return_value
|
||||
yield auth
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def client_fixture() -> Generator[MagicMock]:
|
||||
"""Create a mock client."""
|
||||
with patch("homeassistant.components.remember_the_milk.entity.Rtm") as client_class:
|
||||
with patch(
|
||||
"homeassistant.components.remember_the_milk.AioRTMClient"
|
||||
) as client_class:
|
||||
client = client_class.return_value
|
||||
client.token_valid.return_value = True
|
||||
client.rtm.api.check_token = AsyncMock(
|
||||
return_value={
|
||||
"token": "test-token",
|
||||
"perms": "delete",
|
||||
"user": {
|
||||
"id": "1234567",
|
||||
"username": "johnsmith",
|
||||
"fullname": "John Smith",
|
||||
},
|
||||
}
|
||||
)
|
||||
timelines = MagicMock()
|
||||
timelines.timeline.value = "1234"
|
||||
client.rtm.timelines.create.return_value = timelines
|
||||
add_response = MagicMock()
|
||||
add_response.list.id = "1"
|
||||
add_response.list.taskseries.id = "2"
|
||||
add_response.list.taskseries.task.id = "3"
|
||||
client.rtm.tasks.add.return_value = add_response
|
||||
timelines.timeline = 1234
|
||||
client.rtm.timelines.create = AsyncMock(return_value=timelines)
|
||||
response = MagicMock()
|
||||
response.task_list.id = 1
|
||||
task_series = MagicMock()
|
||||
task_series.id = 2
|
||||
task = MagicMock()
|
||||
task.id = 3
|
||||
task_series.task = [task]
|
||||
response.task_list.taskseries = [task_series]
|
||||
client.rtm.tasks.add = AsyncMock(return_value=response)
|
||||
client.rtm.tasks.complete = AsyncMock(return_value=response)
|
||||
client.rtm.tasks.set_name = AsyncMock(return_value=response)
|
||||
|
||||
yield client
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, call
|
||||
|
||||
from aiortm import AioRTMError, AuthError
|
||||
import pytest
|
||||
from rtmapi import RtmRequestFailedException
|
||||
|
||||
from homeassistant.components.remember_the_milk import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -19,18 +19,23 @@ CONFIG = {
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("storage")
|
||||
@pytest.mark.parametrize(
|
||||
("valid_token", "entity_state"), [(True, "ok"), (False, "API token invalid")]
|
||||
("check_token_side_effect", "entity_state"),
|
||||
[
|
||||
(None, "ok"),
|
||||
(AuthError("Boom!"), "API token invalid"),
|
||||
(AioRTMError("Boom!"), "API token invalid"),
|
||||
],
|
||||
)
|
||||
async def test_entity_state(
|
||||
hass: HomeAssistant,
|
||||
client: MagicMock,
|
||||
storage: MagicMock,
|
||||
valid_token: bool,
|
||||
check_token_side_effect: Exception | None,
|
||||
entity_state: str,
|
||||
) -> None:
|
||||
"""Test the entity state."""
|
||||
client.token_valid.return_value = valid_token
|
||||
client.rtm.api.check_token.side_effect = check_token_side_effect
|
||||
assert await async_setup_component(hass, DOMAIN, {DOMAIN: CONFIG})
|
||||
entity_id = f"{DOMAIN}.{PROFILE}"
|
||||
state = hass.states.get(entity_id)
|
||||
@ -65,9 +70,9 @@ async def test_entity_state(
|
||||
"rtm.tasks.add",
|
||||
1,
|
||||
call(
|
||||
timeline="1234",
|
||||
timeline=1234,
|
||||
name="Test 1",
|
||||
parse="1",
|
||||
parse=True,
|
||||
),
|
||||
"set_rtm_id",
|
||||
0,
|
||||
@ -83,9 +88,9 @@ async def test_entity_state(
|
||||
"rtm.tasks.add",
|
||||
1,
|
||||
call(
|
||||
timeline="1234",
|
||||
timeline=1234,
|
||||
name="Test 1",
|
||||
parse="1",
|
||||
parse=True,
|
||||
),
|
||||
"set_rtm_id",
|
||||
1,
|
||||
@ -98,14 +103,14 @@ async def test_entity_state(
|
||||
1,
|
||||
call(PROFILE, "test_1"),
|
||||
1,
|
||||
"rtm.tasks.setName",
|
||||
"rtm.tasks.set_name",
|
||||
1,
|
||||
call(
|
||||
name="Test 1",
|
||||
list_id="1",
|
||||
taskseries_id="2",
|
||||
task_id="3",
|
||||
timeline="1234",
|
||||
list_id=1,
|
||||
taskseries_id=2,
|
||||
task_id=3,
|
||||
timeline=1234,
|
||||
),
|
||||
"set_rtm_id",
|
||||
0,
|
||||
@ -121,10 +126,10 @@ async def test_entity_state(
|
||||
"rtm.tasks.complete",
|
||||
1,
|
||||
call(
|
||||
list_id="1",
|
||||
taskseries_id="2",
|
||||
task_id="3",
|
||||
timeline="1234",
|
||||
list_id=1,
|
||||
taskseries_id=2,
|
||||
task_id=3,
|
||||
timeline=1234,
|
||||
),
|
||||
"delete_rtm_id",
|
||||
1,
|
||||
@ -183,48 +188,48 @@ async def test_services(
|
||||
f"{PROFILE}_create_task",
|
||||
{"name": "Test 1"},
|
||||
"rtm.timelines.create",
|
||||
RtmRequestFailedException("rtm.timelines.create", "400", "Bad request"),
|
||||
"Request rtm.timelines.create failed. Status: 400, reason: Bad request.",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
(
|
||||
("1", "2", "3"),
|
||||
f"{PROFILE}_create_task",
|
||||
{"name": "Test 1"},
|
||||
"rtm.tasks.add",
|
||||
RtmRequestFailedException("rtm.tasks.add", "400", "Bad request"),
|
||||
"Request rtm.tasks.add failed. Status: 400, reason: Bad request.",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
(
|
||||
None,
|
||||
f"{PROFILE}_create_task",
|
||||
{"name": "Test 1", "id": "test_1"},
|
||||
"rtm.timelines.create",
|
||||
RtmRequestFailedException("rtm.timelines.create", "400", "Bad request"),
|
||||
"Request rtm.timelines.create failed. Status: 400, reason: Bad request.",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
(
|
||||
None,
|
||||
f"{PROFILE}_create_task",
|
||||
{"name": "Test 1", "id": "test_1"},
|
||||
"rtm.tasks.add",
|
||||
RtmRequestFailedException("rtm.tasks.add", "400", "Bad request"),
|
||||
"Request rtm.tasks.add failed. Status: 400, reason: Bad request.",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
(
|
||||
("1", "2", "3"),
|
||||
f"{PROFILE}_create_task",
|
||||
{"name": "Test 1", "id": "test_1"},
|
||||
"rtm.timelines.create",
|
||||
RtmRequestFailedException("rtm.timelines.create", "400", "Bad request"),
|
||||
"Request rtm.timelines.create failed. Status: 400, reason: Bad request.",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
(
|
||||
("1", "2", "3"),
|
||||
f"{PROFILE}_create_task",
|
||||
{"name": "Test 1", "id": "test_1"},
|
||||
"rtm.tasks.setName",
|
||||
RtmRequestFailedException("rtm.tasks.setName", "400", "Bad request"),
|
||||
"Request rtm.tasks.setName failed. Status: 400, reason: Bad request.",
|
||||
"rtm.tasks.set_name",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
(
|
||||
None,
|
||||
@ -242,16 +247,16 @@ async def test_services(
|
||||
f"{PROFILE}_complete_task",
|
||||
{"id": "test_1"},
|
||||
"rtm.timelines.create",
|
||||
RtmRequestFailedException("rtm.timelines.create", "400", "Bad request"),
|
||||
"Request rtm.timelines.create failed. Status: 400, reason: Bad request.",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
(
|
||||
("1", "2", "3"),
|
||||
f"{PROFILE}_complete_task",
|
||||
{"id": "test_1"},
|
||||
"rtm.tasks.complete",
|
||||
RtmRequestFailedException("rtm.tasks.complete", "400", "Bad request"),
|
||||
"Request rtm.tasks.complete failed. Status: 400, reason: Bad request.",
|
||||
AioRTMError("Boom!"),
|
||||
"Boom!",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user