Add shared notify service migration repair helper (#117213)

* Add shared notifiy service migration repair helper

* Delete ecobee repairs.py

* Update dependency

* Fix file test

* Fix homematic tests

* Improve tests for file  and homematic
This commit is contained in:
Jan Bouwhuis 2024-05-11 21:13:44 +02:00 committed by GitHub
parent d1525b1edf
commit 7eb8f265fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 133 additions and 89 deletions

View file

@ -3,7 +3,6 @@
"name": "ecobee",
"codeowners": [],
"config_flow": true,
"dependencies": ["http", "repairs"],
"documentation": "https://www.home-assistant.io/integrations/ecobee",
"homekit": {
"models": ["EB", "ecobee*"]

View file

@ -9,6 +9,7 @@ from homeassistant.components.notify import (
ATTR_TARGET,
BaseNotificationService,
NotifyEntity,
migrate_notify_issue,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -18,7 +19,6 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import Ecobee, EcobeeData
from .const import DOMAIN
from .entity import EcobeeBaseEntity
from .repairs import migrate_notify_issue
def get_service(
@ -43,7 +43,7 @@ class EcobeeNotificationService(BaseNotificationService):
async def async_send_message(self, message: str = "", **kwargs: Any) -> None:
"""Send a message and raise issue."""
migrate_notify_issue(self.hass)
migrate_notify_issue(self.hass, DOMAIN, "Ecobee", "2024.11.0")
await self.hass.async_add_executor_job(
partial(self.send_message, message, **kwargs)
)

View file

@ -163,18 +163,5 @@
}
}
}
},
"issues": {
"migrate_notify": {
"title": "Migration of Ecobee notify service",
"fix_flow": {
"step": {
"confirm": {
"description": "The Ecobee `notify` service has been migrated. A new `notify` entity per Thermostat is available now.\n\nUpdate any automations to use the new `notify.send_message` exposed by these new entities. When this is done, fix this issue and restart Home Assistant.",
"title": "Disable legacy Ecobee notify service"
}
}
}
}
}
}

View file

@ -4,7 +4,7 @@
"after_dependencies": ["panel_custom"],
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"],
"config_flow": true,
"dependencies": ["file_upload", "repairs", "websocket_api"],
"dependencies": ["file_upload", "websocket_api"],
"documentation": "https://www.home-assistant.io/integrations/knx",
"integration_type": "hub",
"iot_class": "local_push",

View file

@ -8,7 +8,11 @@ from xknx import XKNX
from xknx.devices import Notification as XknxNotification
from homeassistant import config_entries
from homeassistant.components.notify import BaseNotificationService, NotifyEntity
from homeassistant.components.notify import (
BaseNotificationService,
NotifyEntity,
migrate_notify_issue,
)
from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, CONF_TYPE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -16,7 +20,6 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS
from .knx_entity import KnxEntity
from .repairs import migrate_notify_issue
async def async_get_service(
@ -57,7 +60,7 @@ class KNXNotificationService(BaseNotificationService):
async def async_send_message(self, message: str = "", **kwargs: Any) -> None:
"""Send a notification to knx bus."""
migrate_notify_issue(self.hass)
migrate_notify_issue(self.hass, DOMAIN, "KNX", "2024.11.0")
if "target" in kwargs:
await self._async_send_to_device(message, kwargs["target"])
else:

View file

@ -1,36 +0,0 @@
"""Repairs support for KNX."""
from __future__ import annotations
from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import issue_registry as ir
from .const import DOMAIN
@callback
def migrate_notify_issue(hass: HomeAssistant) -> None:
"""Create issue for notify service deprecation."""
ir.async_create_issue(
hass,
DOMAIN,
"migrate_notify",
breaks_in_ha_version="2024.11.0",
issue_domain=Platform.NOTIFY.value,
is_fixable=True,
is_persistent=True,
translation_key="migrate_notify",
severity=ir.IssueSeverity.WARNING,
)
async def async_create_fix_flow(
hass: HomeAssistant,
issue_id: str,
data: dict[str, str | int | float | None] | None,
) -> RepairsFlow:
"""Create flow."""
assert issue_id == "migrate_notify"
return ConfirmRepairFlow()

View file

@ -384,18 +384,5 @@
"name": "[%key:common::action::reload%]",
"description": "Reloads the KNX integration."
}
},
"issues": {
"migrate_notify": {
"title": "Migration of KNX notify service",
"fix_flow": {
"step": {
"confirm": {
"description": "The KNX `notify` service has been migrated. New `notify` entities are available now.\n\nUpdate any automations to use the new `notify.send_message` exposed by these new entities. When this is done, fix this issue and restart Home Assistant.",
"title": "Disable legacy KNX notify service"
}
}
}
}
}
}

View file

@ -41,6 +41,7 @@ from .legacy import ( # noqa: F401
async_setup_legacy,
check_templates_warn,
)
from .repairs import migrate_notify_issue # noqa: F401
# mypy: disallow-any-generics

View file

@ -2,6 +2,7 @@
"domain": "notify",
"name": "Notifications",
"codeowners": ["@home-assistant/core"],
"dependencies": ["repairs"],
"documentation": "https://www.home-assistant.io/integrations/notify",
"integration_type": "entity",
"quality_scale": "internal"

View file

@ -1,8 +1,7 @@
"""Repairs support for Ecobee."""
"""Repairs support for notify integration."""
from __future__ import annotations
from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN
from homeassistant.components.repairs import RepairsFlow
from homeassistant.components.repairs.issue_handler import ConfirmRepairFlow
from homeassistant.core import HomeAssistant, callback
@ -12,17 +11,23 @@ from .const import DOMAIN
@callback
def migrate_notify_issue(hass: HomeAssistant) -> None:
def migrate_notify_issue(
hass: HomeAssistant, domain: str, integration_title: str, breaks_in_ha_version: str
) -> None:
"""Ensure an issue is registered."""
ir.async_create_issue(
hass,
DOMAIN,
"migrate_notify",
breaks_in_ha_version="2024.11.0",
issue_domain=NOTIFY_DOMAIN,
f"migrate_notify_{domain}",
breaks_in_ha_version=breaks_in_ha_version,
issue_domain=domain,
is_fixable=True,
is_persistent=True,
translation_key="migrate_notify",
translation_placeholders={
"domain": domain,
"integration_title": integration_title,
},
severity=ir.IssueSeverity.WARNING,
)
@ -33,5 +38,5 @@ async def async_create_fix_flow(
data: dict[str, str | int | float | None] | None,
) -> RepairsFlow:
"""Create flow."""
assert issue_id == "migrate_notify"
assert issue_id.startswith("migrate_notify_")
return ConfirmRepairFlow()

View file

@ -60,5 +60,18 @@
}
}
}
},
"issues": {
"migrate_notify": {
"title": "Migration of {integration_title} notify service",
"fix_flow": {
"step": {
"confirm": {
"description": "The {integration_title} `notify` service(s) are migrated. A new `notify` entity is available now to replace each legacy `notify` service.\n\nUpdate any automations to use the new `notify.send_message` service exposed with this new entity. When this is done, fix this issue and restart Home Assistant.",
"title": "Migrate legacy {integration_title} notify service for domain `{domain}`"
}
}
}
}
}
}

View file

@ -48,14 +48,14 @@ async def test_ecobee_repair_flow(
# Assert the issue is present
assert issue_registry.async_get_issue(
domain=DOMAIN,
issue_id="migrate_notify",
domain="notify",
issue_id=f"migrate_notify_{DOMAIN}",
)
assert len(issue_registry.issues) == 1
url = RepairsFlowIndexView.url
resp = await http_client.post(
url, json={"handler": DOMAIN, "issue_id": "migrate_notify"}
url, json={"handler": "notify", "issue_id": f"migrate_notify_{DOMAIN}"}
)
assert resp.status == HTTPStatus.OK
data = await resp.json()
@ -73,7 +73,7 @@ async def test_ecobee_repair_flow(
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(
domain=DOMAIN,
domain="notify",
issue_id="migrate_notify",
)
assert len(issue_registry.issues) == 0

View file

@ -22,7 +22,7 @@ from tests.common import MockConfigEntry, assert_setup_component
async def test_bad_config(hass: HomeAssistant) -> None:
"""Test set up the platform with bad/missing config."""
config = {notify.DOMAIN: {"name": "test", "platform": "file"}}
with assert_setup_component(0) as handle_config:
with assert_setup_component(0, domain="notify") as handle_config:
assert await async_setup_component(hass, notify.DOMAIN, config)
await hass.async_block_till_done()
assert not handle_config[notify.DOMAIN]

View file

@ -14,7 +14,7 @@ async def test_setup_full(hass: HomeAssistant) -> None:
"homematic",
{"homematic": {"hosts": {"ccu2": {"host": "127.0.0.1"}}}},
)
with assert_setup_component(1) as handle_config:
with assert_setup_component(1, domain="notify") as handle_config:
assert await async_setup_component(
hass,
"notify",
@ -40,7 +40,7 @@ async def test_setup_without_optional(hass: HomeAssistant) -> None:
"homematic",
{"homematic": {"hosts": {"ccu2": {"host": "127.0.0.1"}}}},
)
with assert_setup_component(1) as handle_config:
with assert_setup_component(1, domain="notify") as handle_config:
assert await async_setup_component(
hass,
"notify",
@ -61,6 +61,6 @@ async def test_setup_without_optional(hass: HomeAssistant) -> None:
async def test_bad_config(hass: HomeAssistant) -> None:
"""Test invalid configuration."""
config = {notify_comp.DOMAIN: {"name": "test", "platform": "homematic"}}
with assert_setup_component(0) as handle_config:
with assert_setup_component(0, domain="notify") as handle_config:
assert await async_setup_component(hass, notify_comp.DOMAIN, config)
assert not handle_config[notify_comp.DOMAIN]

View file

@ -54,14 +54,14 @@ async def test_knx_notify_service_issue(
# Assert the issue is present
assert len(issue_registry.issues) == 1
assert issue_registry.async_get_issue(
domain=DOMAIN,
issue_id="migrate_notify",
domain="notify",
issue_id=f"migrate_notify_{DOMAIN}",
)
# Test confirm step in repair flow
resp = await http_client.post(
RepairsFlowIndexView.url,
json={"handler": DOMAIN, "issue_id": "migrate_notify"},
json={"handler": "notify", "issue_id": f"migrate_notify_{DOMAIN}"},
)
assert resp.status == HTTPStatus.OK
data = await resp.json()
@ -78,7 +78,7 @@ async def test_knx_notify_service_issue(
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(
domain=DOMAIN,
issue_id="migrate_notify",
domain="notify",
issue_id=f"migrate_notify_{DOMAIN}",
)
assert len(issue_registry.issues) == 0

View file

@ -0,0 +1,84 @@
"""Test repairs for notify entity component."""
from http import HTTPStatus
from unittest.mock import AsyncMock
from homeassistant.components.notify import (
DOMAIN as NOTIFY_DOMAIN,
migrate_notify_issue,
)
from homeassistant.components.repairs.issue_handler import (
async_process_repairs_platforms,
)
from homeassistant.components.repairs.websocket_api import (
RepairsFlowIndexView,
RepairsFlowResourceView,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry, MockModule, mock_integration
from tests.typing import ClientSessionGenerator
THERMOSTAT_ID = 0
async def test_notify_migration_repair_flow(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
issue_registry: ir.IssueRegistry,
config_flow_fixture: None,
) -> None:
"""Test the notify service repair flow is triggered."""
await async_setup_component(hass, NOTIFY_DOMAIN, {})
await hass.async_block_till_done()
await async_process_repairs_platforms(hass)
http_client = await hass_client()
await hass.async_block_till_done()
config_entry = MockConfigEntry(domain="test")
config_entry.add_to_hass(hass)
mock_integration(
hass,
MockModule(
"test",
async_setup_entry=AsyncMock(return_value=True),
),
)
assert await hass.config_entries.async_setup(config_entry.entry_id)
# Simulate legacy service being used and issue being registered
migrate_notify_issue(hass, "test", "Test", "2024.12.0")
await hass.async_block_till_done()
# Assert the issue is present
assert issue_registry.async_get_issue(
domain=NOTIFY_DOMAIN,
issue_id="migrate_notify_test",
)
assert len(issue_registry.issues) == 1
url = RepairsFlowIndexView.url
resp = await http_client.post(
url, json={"handler": NOTIFY_DOMAIN, "issue_id": "migrate_notify_test"}
)
assert resp.status == HTTPStatus.OK
data = await resp.json()
flow_id = data["flow_id"]
assert data["step_id"] == "confirm"
url = RepairsFlowResourceView.url.format(flow_id=flow_id)
resp = await http_client.post(url)
assert resp.status == HTTPStatus.OK
data = await resp.json()
assert data["type"] == "create_entry"
# Test confirm step in repair flow
await hass.async_block_till_done()
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(
domain=NOTIFY_DOMAIN,
issue_id="migrate_notify_test",
)
assert len(issue_registry.issues) == 0