From baeb55e313074e545e7d9e55ae0a9cbdb2c3e571 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 21 Jul 2022 11:11:51 +0200 Subject: [PATCH] Add sync methods for create/deleting issues in repairs (#75557) --- homeassistant/components/repairs/__init__.py | 9 ++- .../components/repairs/issue_handler.py | 42 +++++++++++ tests/components/repairs/test_init.py | 73 ++++++++++++++++++- 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/repairs/__init__.py b/homeassistant/components/repairs/__init__.py index f9a478514aa..4471def0dcd 100644 --- a/homeassistant/components/repairs/__init__.py +++ b/homeassistant/components/repairs/__init__.py @@ -6,13 +6,20 @@ from homeassistant.helpers.typing import ConfigType from . import issue_handler, websocket_api from .const import DOMAIN -from .issue_handler import async_create_issue, async_delete_issue +from .issue_handler import ( + async_create_issue, + async_delete_issue, + create_issue, + delete_issue, +) from .issue_registry import async_load as async_load_issue_registry from .models import IssueSeverity, RepairsFlow __all__ = [ "async_create_issue", "async_delete_issue", + "create_issue", + "delete_issue", "DOMAIN", "IssueSeverity", "RepairsFlow", diff --git a/homeassistant/components/repairs/issue_handler.py b/homeassistant/components/repairs/issue_handler.py index ff23c1c70a4..8eff4ac64fe 100644 --- a/homeassistant/components/repairs/issue_handler.py +++ b/homeassistant/components/repairs/issue_handler.py @@ -1,6 +1,7 @@ """The repairs integration.""" from __future__ import annotations +import functools as ft from typing import Any from awesomeversion import AwesomeVersion, AwesomeVersionStrategy @@ -11,6 +12,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.integration_platform import ( async_process_integration_platforms, ) +from homeassistant.util.async_ import run_callback_threadsafe from .const import DOMAIN from .issue_registry import async_get as async_get_issue_registry @@ -113,6 +115,36 @@ def async_create_issue( ) +def create_issue( + hass: HomeAssistant, + domain: str, + issue_id: str, + *, + breaks_in_ha_version: str | None = None, + is_fixable: bool, + learn_more_url: str | None = None, + severity: IssueSeverity, + translation_key: str, + translation_placeholders: dict[str, str] | None = None, +) -> None: + """Create an issue, or replace an existing one.""" + return run_callback_threadsafe( + hass.loop, + ft.partial( + async_create_issue, + hass, + domain, + issue_id, + breaks_in_ha_version=breaks_in_ha_version, + is_fixable=is_fixable, + learn_more_url=learn_more_url, + severity=severity, + translation_key=translation_key, + translation_placeholders=translation_placeholders, + ), + ).result() + + @callback def async_delete_issue(hass: HomeAssistant, domain: str, issue_id: str) -> None: """Delete an issue. @@ -123,6 +155,16 @@ def async_delete_issue(hass: HomeAssistant, domain: str, issue_id: str) -> None: issue_registry.async_delete(domain, issue_id) +def delete_issue(hass: HomeAssistant, domain: str, issue_id: str) -> None: + """Delete an issue. + + It is not an error to delete an issue that does not exist. + """ + return run_callback_threadsafe( + hass.loop, async_delete_issue, hass, domain, issue_id + ).result() + + @callback def async_ignore_issue( hass: HomeAssistant, domain: str, issue_id: str, ignore: bool diff --git a/tests/components/repairs/test_init.py b/tests/components/repairs/test_init.py index fbf240e740d..2f82a084968 100644 --- a/tests/components/repairs/test_init.py +++ b/tests/components/repairs/test_init.py @@ -1,15 +1,23 @@ """Test the repairs websocket API.""" +from collections.abc import Awaitable, Callable from unittest.mock import AsyncMock, Mock +from aiohttp import ClientWebSocketResponse from freezegun import freeze_time import pytest -from homeassistant.components.repairs import async_create_issue, async_delete_issue +from homeassistant.components.repairs import ( + async_create_issue, + async_delete_issue, + create_issue, + delete_issue, +) from homeassistant.components.repairs.const import DOMAIN from homeassistant.components.repairs.issue_handler import ( async_ignore_issue, async_process_repairs_platforms, ) +from homeassistant.components.repairs.models import IssueSeverity from homeassistant.const import __version__ as ha_version from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component @@ -446,3 +454,66 @@ async def test_non_compliant_platform(hass: HomeAssistant, hass_ws_client) -> No await async_process_repairs_platforms(hass) assert list(hass.data[DOMAIN]["platforms"].keys()) == ["fake_integration"] + + +@freeze_time("2022-07-21 08:22:00") +async def test_sync_methods( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], +) -> None: + """Test sync method for creating and deleting an issue.""" + + assert await async_setup_component(hass, DOMAIN, {}) + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await client.receive_json() + + assert msg["success"] + assert msg["result"] == {"issues": []} + + def _create_issue() -> None: + create_issue( + hass, + "fake_integration", + "sync_issue", + breaks_in_ha_version="2022.9", + is_fixable=True, + learn_more_url="https://theuselessweb.com", + severity=IssueSeverity.ERROR, + translation_key="abc_123", + translation_placeholders={"abc": "123"}, + ) + + await hass.async_add_executor_job(_create_issue) + await client.send_json({"id": 2, "type": "repairs/list_issues"}) + msg = await client.receive_json() + + assert msg["success"] + assert msg["result"] == { + "issues": [ + { + "breaks_in_ha_version": "2022.9", + "created": "2022-07-21T08:22:00+00:00", + "dismissed_version": None, + "domain": "fake_integration", + "ignored": False, + "is_fixable": True, + "issue_id": "sync_issue", + "learn_more_url": "https://theuselessweb.com", + "severity": "error", + "translation_key": "abc_123", + "translation_placeholders": {"abc": "123"}, + } + ] + } + + await hass.async_add_executor_job( + delete_issue, hass, "fake_integration", "sync_issue" + ) + await client.send_json({"id": 3, "type": "repairs/list_issues"}) + msg = await client.receive_json() + + assert msg["success"] + assert msg["result"] == {"issues": []}