Move issue_registry to homeassistant.helpers (#77299)

* Move issue_registry to homeassistant.helpers

* Add backwards compatibility
This commit is contained in:
Erik Montnemery 2022-08-25 11:32:06 +02:00 committed by GitHub
parent 79ab794e6a
commit dfed3ba75e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 174 additions and 159 deletions

View File

@ -24,7 +24,13 @@ from .const import (
SIGNAL_BOOTSTRAP_INTEGRATONS, SIGNAL_BOOTSTRAP_INTEGRATONS,
) )
from .exceptions import HomeAssistantError from .exceptions import HomeAssistantError
from .helpers import area_registry, device_registry, entity_registry, recorder from .helpers import (
area_registry,
device_registry,
entity_registry,
issue_registry,
recorder,
)
from .helpers.dispatcher import async_dispatcher_send from .helpers.dispatcher import async_dispatcher_send
from .helpers.typing import ConfigType from .helpers.typing import ConfigType
from .setup import ( from .setup import (
@ -521,9 +527,10 @@ async def _async_set_up_integrations(
# Load the registries and cache the result of platform.uname().processor # Load the registries and cache the result of platform.uname().processor
await asyncio.gather( await asyncio.gather(
area_registry.async_load(hass),
device_registry.async_load(hass), device_registry.async_load(hass),
entity_registry.async_load(hass), entity_registry.async_load(hass),
area_registry.async_load(hass), issue_registry.async_load(hass),
hass.async_add_executor_job(_cache_uname_processor), hass.async_add_executor_job(_cache_uname_processor),
) )

View File

@ -2,19 +2,19 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.issue_registry import (
IssueSeverity,
from . import issue_handler, websocket_api
from .const import DOMAIN
from .issue_handler import (
ConfirmRepairFlow,
async_create_issue, async_create_issue,
async_delete_issue, async_delete_issue,
create_issue, create_issue,
delete_issue, delete_issue,
) )
from .issue_registry import async_load as async_load_issue_registry from homeassistant.helpers.typing import ConfigType
from .models import IssueSeverity, RepairsFlow
from . import issue_handler, websocket_api
from .const import DOMAIN
from .issue_handler import ConfirmRepairFlow
from .models import RepairsFlow
__all__ = [ __all__ = [
"async_create_issue", "async_create_issue",
@ -34,6 +34,5 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
issue_handler.async_setup(hass) issue_handler.async_setup(hass)
websocket_api.async_setup(hass) websocket_api.async_setup(hass)
await async_load_issue_registry(hass)
return True return True

View File

@ -1,10 +1,8 @@
"""The repairs integration.""" """The repairs integration."""
from __future__ import annotations from __future__ import annotations
import functools as ft
from typing import Any from typing import Any
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
import voluptuous as vol import voluptuous as vol
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
@ -13,11 +11,16 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.integration_platform import ( from homeassistant.helpers.integration_platform import (
async_process_integration_platforms, async_process_integration_platforms,
) )
from homeassistant.util.async_ import run_callback_threadsafe
# pylint: disable-next=unused-import
from homeassistant.helpers.issue_registry import ( # noqa: F401; Remove when integrations have been updated
async_create_issue,
async_delete_issue,
async_get as async_get_issue_registry,
)
from .const import DOMAIN from .const import DOMAIN
from .issue_registry import async_get as async_get_issue_registry from .models import RepairsFlow, RepairsProtocol
from .models import IssueSeverity, RepairsFlow, RepairsProtocol
class ConfirmRepairFlow(RepairsFlow): class ConfirmRepairFlow(RepairsFlow):
@ -111,112 +114,3 @@ async def _register_repairs_platform(
if not hasattr(platform, "async_create_fix_flow"): if not hasattr(platform, "async_create_fix_flow"):
raise HomeAssistantError(f"Invalid repairs platform {platform}") raise HomeAssistantError(f"Invalid repairs platform {platform}")
hass.data[DOMAIN]["platforms"][integration_domain] = platform hass.data[DOMAIN]["platforms"][integration_domain] = platform
@callback
def async_create_issue(
hass: HomeAssistant,
domain: str,
issue_id: str,
*,
breaks_in_ha_version: str | None = None,
data: dict[str, str | int | float | None] | None = None,
is_fixable: bool,
is_persistent: bool = False,
issue_domain: str | None = None,
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."""
# Verify the breaks_in_ha_version is a valid version string
if breaks_in_ha_version:
AwesomeVersion(
breaks_in_ha_version,
ensure_strategy=AwesomeVersionStrategy.CALVER,
find_first_match=False,
)
issue_registry = async_get_issue_registry(hass)
issue_registry.async_get_or_create(
domain,
issue_id,
breaks_in_ha_version=breaks_in_ha_version,
data=data,
is_fixable=is_fixable,
is_persistent=is_persistent,
issue_domain=issue_domain,
learn_more_url=learn_more_url,
severity=severity,
translation_key=translation_key,
translation_placeholders=translation_placeholders,
)
def create_issue(
hass: HomeAssistant,
domain: str,
issue_id: str,
*,
breaks_in_ha_version: str | None = None,
data: dict[str, str | int | float | None] | None = None,
is_fixable: bool,
is_persistent: bool = False,
issue_domain: str | None = None,
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,
data=data,
is_fixable=is_fixable,
is_persistent=is_persistent,
issue_domain=issue_domain,
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.
It is not an error to delete an issue that does not exist.
"""
issue_registry = async_get_issue_registry(hass)
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
) -> None:
"""Ignore an issue.
Will raise if the issue does not exist.
"""
issue_registry = async_get_issue_registry(hass)
issue_registry.async_ignore(domain, issue_id, ignore)

View File

@ -4,16 +4,12 @@ from __future__ import annotations
from typing import Protocol from typing import Protocol
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.backports.enum import StrEnum
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
# pylint: disable-next=unused-import
class IssueSeverity(StrEnum): from homeassistant.helpers.issue_registry import ( # noqa: F401; Remove when integrations have been updated
"""Issue severity.""" IssueSeverity,
)
CRITICAL = "critical"
ERROR = "error"
WARNING = "warning"
class RepairsFlow(data_entry_flow.FlowHandler): class RepairsFlow(data_entry_flow.FlowHandler):

View File

@ -18,10 +18,12 @@ from homeassistant.helpers.data_entry_flow import (
FlowManagerIndexView, FlowManagerIndexView,
FlowManagerResourceView, FlowManagerResourceView,
) )
from homeassistant.helpers.issue_registry import (
async_get as async_get_issue_registry,
async_ignore_issue,
)
from .const import DOMAIN from .const import DOMAIN
from .issue_handler import async_ignore_issue
from .issue_registry import async_get as async_get_issue_registry
@callback @callback

View File

@ -3,14 +3,18 @@ from __future__ import annotations
import dataclasses import dataclasses
from datetime import datetime from datetime import datetime
import functools as ft
from typing import Any, cast from typing import Any, cast
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
from homeassistant.backports.enum import StrEnum
from homeassistant.const import __version__ as ha_version from homeassistant.const import __version__ as ha_version
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.storage import Store from homeassistant.util.async_ import run_callback_threadsafe
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from .models import IssueSeverity from .storage import Store
DATA_REGISTRY = "issue_registry" DATA_REGISTRY = "issue_registry"
EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED = "repairs_issue_registry_updated" EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED = "repairs_issue_registry_updated"
@ -20,6 +24,14 @@ STORAGE_VERSION_MINOR = 2
SAVE_DELAY = 10 SAVE_DELAY = 10
class IssueSeverity(StrEnum):
"""Issue severity."""
CRITICAL = "critical"
ERROR = "error"
WARNING = "warning"
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class IssueEntry: class IssueEntry:
"""Issue Registry Entry.""" """Issue Registry Entry."""
@ -267,3 +279,112 @@ async def async_load(hass: HomeAssistant) -> None:
assert DATA_REGISTRY not in hass.data assert DATA_REGISTRY not in hass.data
hass.data[DATA_REGISTRY] = IssueRegistry(hass) hass.data[DATA_REGISTRY] = IssueRegistry(hass)
await hass.data[DATA_REGISTRY].async_load() await hass.data[DATA_REGISTRY].async_load()
@callback
def async_create_issue(
hass: HomeAssistant,
domain: str,
issue_id: str,
*,
breaks_in_ha_version: str | None = None,
data: dict[str, str | int | float | None] | None = None,
is_fixable: bool,
is_persistent: bool = False,
issue_domain: str | None = None,
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."""
# Verify the breaks_in_ha_version is a valid version string
if breaks_in_ha_version:
AwesomeVersion(
breaks_in_ha_version,
ensure_strategy=AwesomeVersionStrategy.CALVER,
find_first_match=False,
)
issue_registry = async_get(hass)
issue_registry.async_get_or_create(
domain,
issue_id,
breaks_in_ha_version=breaks_in_ha_version,
data=data,
is_fixable=is_fixable,
is_persistent=is_persistent,
issue_domain=issue_domain,
learn_more_url=learn_more_url,
severity=severity,
translation_key=translation_key,
translation_placeholders=translation_placeholders,
)
def create_issue(
hass: HomeAssistant,
domain: str,
issue_id: str,
*,
breaks_in_ha_version: str | None = None,
data: dict[str, str | int | float | None] | None = None,
is_fixable: bool,
is_persistent: bool = False,
issue_domain: str | None = None,
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,
data=data,
is_fixable=is_fixable,
is_persistent=is_persistent,
issue_domain=issue_domain,
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.
It is not an error to delete an issue that does not exist.
"""
issue_registry = async_get(hass)
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
) -> None:
"""Ignore an issue.
Will raise if the issue does not exist.
"""
issue_registry = async_get(hass)
issue_registry.async_ignore(domain, issue_id, ignore)

View File

@ -50,6 +50,7 @@ from homeassistant.helpers import (
entity_platform, entity_platform,
entity_registry, entity_registry,
intent, intent,
issue_registry,
recorder as recorder_helper, recorder as recorder_helper,
restore_state, restore_state,
storage, storage,
@ -297,9 +298,10 @@ async def async_test_home_assistant(loop, load_registries=True):
# Load the registries # Load the registries
if load_registries: if load_registries:
await asyncio.gather( await asyncio.gather(
area_registry.async_load(hass),
device_registry.async_load(hass), device_registry.async_load(hass),
entity_registry.async_load(hass), entity_registry.async_load(hass),
area_registry.async_load(hass), issue_registry.async_load(hass),
) )
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -14,12 +14,11 @@ from homeassistant.components.repairs import (
) )
from homeassistant.components.repairs.const import DOMAIN from homeassistant.components.repairs.const import DOMAIN
from homeassistant.components.repairs.issue_handler import ( from homeassistant.components.repairs.issue_handler import (
async_ignore_issue,
async_process_repairs_platforms, async_process_repairs_platforms,
) )
from homeassistant.components.repairs.models import IssueSeverity
from homeassistant.const import __version__ as ha_version from homeassistant.const import __version__ as ha_version
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.issue_registry import IssueSeverity, async_ignore_issue
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import mock_platform from tests.common import mock_platform

View File

@ -9,14 +9,11 @@ import pytest
import voluptuous as vol import voluptuous as vol
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components.repairs import ( from homeassistant.components.repairs import RepairsFlow, async_create_issue
RepairsFlow,
async_create_issue,
issue_registry,
)
from homeassistant.components.repairs.const import DOMAIN from homeassistant.components.repairs.const import DOMAIN
from homeassistant.const import __version__ as ha_version from homeassistant.const import __version__ as ha_version
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import mock_platform from tests.common import mock_platform

View File

@ -1,20 +1,14 @@
"""Test the repairs websocket API.""" """Test the repairs websocket API."""
from homeassistant.components.repairs import async_create_issue, issue_registry import pytest
from homeassistant.components.repairs.const import DOMAIN
from homeassistant.components.repairs.issue_handler import (
async_delete_issue,
async_ignore_issue,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component from homeassistant.helpers import issue_registry
from tests.common import async_capture_events, flush_store from tests.common import async_capture_events, flush_store
async def test_load_issues(hass: HomeAssistant) -> None: async def test_load_issues(hass: HomeAssistant) -> None:
"""Make sure that we can load/save data correctly.""" """Make sure that we can load/save data correctly."""
assert await async_setup_component(hass, DOMAIN, {})
issues = [ issues = [
{ {
"breaks_in_ha_version": "2022.9", "breaks_in_ha_version": "2022.9",
@ -68,7 +62,7 @@ async def test_load_issues(hass: HomeAssistant) -> None:
) )
for issue in issues: for issue in issues:
async_create_issue( issue_registry.async_create_issue(
hass, hass,
issue["domain"], issue["domain"],
issue["issue_id"], issue["issue_id"],
@ -105,7 +99,9 @@ async def test_load_issues(hass: HomeAssistant) -> None:
"issue_id": "issue_4", "issue_id": "issue_4",
} }
async_ignore_issue(hass, issues[0]["domain"], issues[0]["issue_id"], True) issue_registry.async_ignore_issue(
hass, issues[0]["domain"], issues[0]["issue_id"], True
)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(events) == 5 assert len(events) == 5
@ -115,7 +111,7 @@ async def test_load_issues(hass: HomeAssistant) -> None:
"issue_id": "issue_1", "issue_id": "issue_1",
} }
async_delete_issue(hass, issues[2]["domain"], issues[2]["issue_id"]) issue_registry.async_delete_issue(hass, issues[2]["domain"], issues[2]["issue_id"])
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(events) == 6 assert len(events) == 6
@ -175,6 +171,7 @@ async def test_load_issues(hass: HomeAssistant) -> None:
assert issue4_registry2 == issue4 assert issue4_registry2 == issue4
@pytest.mark.parametrize("load_registries", [False])
async def test_loading_issues_from_storage(hass: HomeAssistant, hass_storage) -> None: async def test_loading_issues_from_storage(hass: HomeAssistant, hass_storage) -> None:
"""Test loading stored issues on start.""" """Test loading stored issues on start."""
hass_storage[issue_registry.STORAGE_KEY] = { hass_storage[issue_registry.STORAGE_KEY] = {
@ -215,12 +212,13 @@ async def test_loading_issues_from_storage(hass: HomeAssistant, hass_storage) ->
}, },
} }
assert await async_setup_component(hass, DOMAIN, {}) await issue_registry.async_load(hass)
registry: issue_registry.IssueRegistry = hass.data[issue_registry.DATA_REGISTRY] registry: issue_registry.IssueRegistry = hass.data[issue_registry.DATA_REGISTRY]
assert len(registry.issues) == 3 assert len(registry.issues) == 3
@pytest.mark.parametrize("load_registries", [False])
async def test_migration_1_1(hass: HomeAssistant, hass_storage) -> None: async def test_migration_1_1(hass: HomeAssistant, hass_storage) -> None:
"""Test migration from version 1.1.""" """Test migration from version 1.1."""
hass_storage[issue_registry.STORAGE_KEY] = { hass_storage[issue_registry.STORAGE_KEY] = {
@ -244,7 +242,7 @@ async def test_migration_1_1(hass: HomeAssistant, hass_storage) -> None:
}, },
} }
assert await async_setup_component(hass, DOMAIN, {}) await issue_registry.async_load(hass)
registry: issue_registry.IssueRegistry = hass.data[issue_registry.DATA_REGISTRY] registry: issue_registry.IssueRegistry = hass.data[issue_registry.DATA_REGISTRY]
assert len(registry.issues) == 2 assert len(registry.issues) == 2