mirror of
https://github.com/home-assistant/core.git
synced 2025-08-12 09:05:15 +02:00
Include backup agent error in response to WS command backup/details (#130892)
This commit is contained in:
@@ -221,7 +221,9 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
async def async_get_backup(self, *, slug: str, **kwargs: Any) -> _BackupT | None:
|
||||
async def async_get_backup(
|
||||
self, *, slug: str, **kwargs: Any
|
||||
) -> tuple[_BackupT | None, dict[str, Exception]]:
|
||||
"""Get a backup."""
|
||||
|
||||
@abc.abstractmethod
|
||||
@@ -314,25 +316,41 @@ class BackupManager(BaseBackupManager[Backup]):
|
||||
|
||||
return (backups, agent_errors)
|
||||
|
||||
async def async_get_backup(self, *, slug: str, **kwargs: Any) -> Backup | None:
|
||||
async def async_get_backup(
|
||||
self, *, slug: str, **kwargs: Any
|
||||
) -> tuple[Backup | None, dict[str, Exception]]:
|
||||
"""Return a backup."""
|
||||
backup: Backup | None = None
|
||||
agent_errors: dict[str, Exception] = {}
|
||||
agent_ids = list(self.backup_agents.keys())
|
||||
|
||||
for agent_id, agent in self.backup_agents.items():
|
||||
if not (agent_backup := await agent.async_get_backup(slug=slug)):
|
||||
get_backup_results = await asyncio.gather(
|
||||
*(
|
||||
agent.async_get_backup(slug=slug)
|
||||
for agent in self.backup_agents.values()
|
||||
),
|
||||
return_exceptions=True,
|
||||
)
|
||||
for idx, result in enumerate(get_backup_results):
|
||||
if isinstance(result, BackupAgentError):
|
||||
agent_errors[agent_ids[idx]] = result
|
||||
continue
|
||||
if isinstance(result, BaseException):
|
||||
raise result
|
||||
if not result:
|
||||
continue
|
||||
if backup is None:
|
||||
backup = Backup(
|
||||
slug=agent_backup.slug,
|
||||
name=agent_backup.name,
|
||||
date=agent_backup.date,
|
||||
slug=result.slug,
|
||||
name=result.name,
|
||||
date=result.date,
|
||||
agent_ids=[],
|
||||
size=agent_backup.size,
|
||||
protected=agent_backup.protected,
|
||||
size=result.size,
|
||||
protected=result.protected,
|
||||
)
|
||||
backup.agent_ids.append(agent_id)
|
||||
backup.agent_ids.append(agent_ids[idx])
|
||||
|
||||
return backup
|
||||
return (backup, agent_errors)
|
||||
|
||||
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None:
|
||||
"""Remove a backup."""
|
||||
|
@@ -67,10 +67,15 @@ async def handle_details(
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Get backup details for a specific slug."""
|
||||
backup = await hass.data[DATA_MANAGER].async_get_backup(slug=msg["slug"])
|
||||
backup, agent_errors = await hass.data[DATA_MANAGER].async_get_backup(
|
||||
slug=msg["slug"]
|
||||
)
|
||||
connection.send_result(
|
||||
msg["id"],
|
||||
{
|
||||
"agent_errors": {
|
||||
agent_id: str(err) for agent_id, err in agent_errors.items()
|
||||
},
|
||||
"backup": backup,
|
||||
},
|
||||
)
|
||||
|
@@ -283,6 +283,8 @@
|
||||
dict({
|
||||
'id': 1,
|
||||
'result': dict({
|
||||
'agent_errors': dict({
|
||||
}),
|
||||
'backup': dict({
|
||||
'agent_ids': list([
|
||||
'backup.local',
|
||||
@@ -302,12 +304,47 @@
|
||||
dict({
|
||||
'id': 1,
|
||||
'result': dict({
|
||||
'agent_errors': dict({
|
||||
}),
|
||||
'backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_details_with_errors[BackupAgentUnreachableError]
|
||||
dict({
|
||||
'id': 1,
|
||||
'result': dict({
|
||||
'agent_errors': dict({
|
||||
'domain.test': 'The backup agent is unreachable.',
|
||||
}),
|
||||
'backup': dict({
|
||||
'agent_ids': list([
|
||||
'backup.local',
|
||||
]),
|
||||
'date': '1970-01-01T00:00:00.000Z',
|
||||
'name': 'Test',
|
||||
'protected': False,
|
||||
'size': 0.0,
|
||||
'slug': 'abc123',
|
||||
}),
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_details_with_errors[side_effect0]
|
||||
dict({
|
||||
'error': dict({
|
||||
'code': 'home_assistant_error',
|
||||
'message': 'Boom!',
|
||||
}),
|
||||
'id': 1,
|
||||
'success': False,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_generate[with_hassio-None]
|
||||
dict({
|
||||
'error': dict({
|
||||
|
@@ -217,8 +217,11 @@ async def test_getting_backup_that_does_not_exist(
|
||||
local_agent._loaded_backups = True
|
||||
|
||||
with patch("pathlib.Path.exists", return_value=False):
|
||||
backup = await manager.async_get_backup(slug=TEST_LOCAL_BACKUP.slug)
|
||||
backup, agent_errors = await manager.async_get_backup(
|
||||
slug=TEST_LOCAL_BACKUP.slug
|
||||
)
|
||||
assert backup is None
|
||||
assert agent_errors == {}
|
||||
|
||||
assert (
|
||||
f"Removing tracked backup ({TEST_LOCAL_BACKUP.slug}) that "
|
||||
|
@@ -112,6 +112,30 @@ async def test_details(
|
||||
assert await client.receive_json() == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"side_effect", [HomeAssistantError("Boom!"), BackupAgentUnreachableError]
|
||||
)
|
||||
async def test_details_with_errors(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
side_effect: Exception,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test getting backup info with one unavailable agent."""
|
||||
await setup_backup_integration(hass, with_hassio=False, backups=[TEST_LOCAL_BACKUP])
|
||||
hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test")
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with (
|
||||
patch("pathlib.Path.exists", return_value=True),
|
||||
patch.object(BackupAgentTest, "async_get_backup", side_effect=side_effect),
|
||||
):
|
||||
await client.send_json_auto_id({"type": "backup/details", "slug": "abc123"})
|
||||
assert await client.receive_json() == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"with_hassio",
|
||||
[
|
||||
|
Reference in New Issue
Block a user