Rename backup slug to backup_id (#130902)

This commit is contained in:
Erik Montnemery
2024-11-18 22:24:51 +01:00
committed by GitHub
parent c7e435693e
commit 78053b487c
14 changed files with 192 additions and 216 deletions

View File

@@ -8,7 +8,7 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.hassio import is_hassio from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .agent import BackupAgent, BackupAgentPlatformProtocol, UploadedBackup from .agent import BackupAgent, BackupAgentPlatformProtocol
from .const import DOMAIN, LOGGER from .const import DOMAIN, LOGGER
from .http import async_register_http_views from .http import async_register_http_views
from .manager import Backup, BackupManager, BackupPlatformProtocol from .manager import Backup, BackupManager, BackupPlatformProtocol
@@ -22,7 +22,6 @@ __all__ = [
"BackupPlatformProtocol", "BackupPlatformProtocol",
"BackupUploadMetadata", "BackupUploadMetadata",
"BaseBackup", "BaseBackup",
"UploadedBackup",
] ]
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN) CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)

View File

@@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
import abc import abc
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any, Protocol from typing import Any, Protocol
@@ -23,13 +22,6 @@ class BackupAgentUnreachableError(BackupAgentError):
_message = "The backup agent is unreachable." _message = "The backup agent is unreachable."
@dataclass(slots=True)
class UploadedBackup(BaseBackup):
"""Uploaded backup class."""
id: str
class BackupAgent(abc.ABC): class BackupAgent(abc.ABC):
"""Backup agent interface.""" """Backup agent interface."""
@@ -38,14 +30,14 @@ class BackupAgent(abc.ABC):
@abc.abstractmethod @abc.abstractmethod
async def async_download_backup( async def async_download_backup(
self, self,
backup_id: str,
*, *,
id: str,
path: Path, path: Path,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Download a backup file. """Download a backup file.
:param id: The ID of the backup that was returned in async_list_backups. :param backup_id: The ID of the backup that was returned in async_list_backups.
:param path: The full file path to download the backup to. :param path: The full file path to download the backup to.
""" """
@@ -64,16 +56,15 @@ class BackupAgent(abc.ABC):
""" """
@abc.abstractmethod @abc.abstractmethod
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]: async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
"""List backups.""" """List backups."""
@abc.abstractmethod @abc.abstractmethod
async def async_get_backup( async def async_get_backup(
self, self,
*, backup_id: str,
slug: str,
**kwargs: Any, **kwargs: Any,
) -> UploadedBackup | None: ) -> BaseBackup | None:
"""Return a backup.""" """Return a backup."""
@@ -81,10 +72,10 @@ class LocalBackupAgent(BackupAgent):
"""Local backup agent.""" """Local backup agent."""
@abc.abstractmethod @abc.abstractmethod
def get_backup_path(self, slug: str) -> Path: def get_backup_path(self, backup_id: str) -> Path:
"""Return the local path to a backup. """Return the local path to a backup.
The method should return the path to the backup file with the specified slug. The method should return the path to the backup file with the specified id.
""" """

View File

@@ -10,9 +10,9 @@ from typing import Any
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .agent import BackupAgent, LocalBackupAgent, UploadedBackup from .agent import BackupAgent, LocalBackupAgent
from .const import LOGGER from .const import LOGGER
from .models import BackupUploadMetadata from .models import BackupUploadMetadata, BaseBackup
from .util import read_backup from .util import read_backup
@@ -25,7 +25,7 @@ async def async_get_backup_agents(
@dataclass(slots=True) @dataclass(slots=True)
class LocalBackup(UploadedBackup): class LocalBackup(BaseBackup):
"""Local backup class.""" """Local backup class."""
path: Path path: Path
@@ -62,23 +62,22 @@ class CoreLocalBackupAgent(LocalBackupAgent):
try: try:
base_backup = read_backup(backup_path) base_backup = read_backup(backup_path)
backup = LocalBackup( backup = LocalBackup(
id=base_backup.slug, backup_id=base_backup.backup_id,
slug=base_backup.slug,
name=base_backup.name, name=base_backup.name,
date=base_backup.date, date=base_backup.date,
path=backup_path, path=backup_path,
size=round(backup_path.stat().st_size / 1_048_576, 2), size=round(backup_path.stat().st_size / 1_048_576, 2),
protected=base_backup.protected, protected=base_backup.protected,
) )
backups[backup.slug] = backup backups[backup.backup_id] = backup
except (OSError, TarError, json.JSONDecodeError, KeyError) as err: except (OSError, TarError, json.JSONDecodeError, KeyError) as err:
LOGGER.warning("Unable to read backup %s: %s", backup_path, err) LOGGER.warning("Unable to read backup %s: %s", backup_path, err)
return backups return backups
async def async_download_backup( async def async_download_backup(
self, self,
backup_id: str,
*, *,
id: str,
path: Path, path: Path,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
@@ -93,17 +92,16 @@ class CoreLocalBackupAgent(LocalBackupAgent):
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Upload a backup.""" """Upload a backup."""
self._backups[metadata.slug] = LocalBackup( self._backups[metadata.backup_id] = LocalBackup(
id=metadata.slug, # Do we need another ID? backup_id=metadata.backup_id,
slug=metadata.slug,
name=metadata.name,
date=metadata.date, date=metadata.date,
name=metadata.name,
path=path, path=path,
size=round(path.stat().st_size / 1_048_576, 2),
protected=metadata.protected, protected=metadata.protected,
size=round(path.stat().st_size / 1_048_576, 2),
) )
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]: async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
"""List backups.""" """List backups."""
if not self._loaded_backups: if not self._loaded_backups:
await self.load_backups() await self.load_backups()
@@ -111,15 +109,14 @@ class CoreLocalBackupAgent(LocalBackupAgent):
async def async_get_backup( async def async_get_backup(
self, self,
*, backup_id: str,
slug: str,
**kwargs: Any, **kwargs: Any,
) -> UploadedBackup | None: ) -> LocalBackup | None:
"""Return a backup.""" """Return a backup."""
if not self._loaded_backups: if not self._loaded_backups:
await self.load_backups() await self.load_backups()
if not (backup := self._backups.get(slug)): if not (backup := self._backups.get(backup_id)):
return None return None
if not await self._hass.async_add_executor_job(backup.path.exists): if not await self._hass.async_add_executor_job(backup.path.exists):
@@ -128,23 +125,23 @@ class CoreLocalBackupAgent(LocalBackupAgent):
"Removing tracked backup (%s) that does not exists on the expected" "Removing tracked backup (%s) that does not exists on the expected"
" path %s" " path %s"
), ),
backup.slug, backup.backup_id,
backup.path, backup.path,
) )
self._backups.pop(slug) self._backups.pop(backup_id)
return None return None
return backup return backup
def get_backup_path(self, slug: str) -> Path: def get_backup_path(self, backup_id: str) -> Path:
"""Return the local path to a backup.""" """Return the local path to a backup."""
return self._backup_dir / f"{slug}.tar" return self._backup_dir / f"{backup_id}.tar"
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None: async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None:
"""Remove a backup.""" """Remove a backup."""
if (backup := await self.async_get_backup(slug=slug)) is None: if (backup := await self.async_get_backup(backup_id)) is None:
return return
await self._hass.async_add_executor_job(backup.path.unlink, True) # type: ignore[attr-defined] await self._hass.async_add_executor_job(backup.path.unlink, True)
LOGGER.debug("Removed backup located at %s", backup.path) # type: ignore[attr-defined] LOGGER.debug("Removed backup located at %s", backup.path)
self._backups.pop(slug) self._backups.pop(backup_id)

View File

@@ -31,13 +31,13 @@ def async_register_http_views(hass: HomeAssistant) -> None:
class DownloadBackupView(HomeAssistantView): class DownloadBackupView(HomeAssistantView):
"""Generate backup view.""" """Generate backup view."""
url = "/api/backup/download/{slug}" url = "/api/backup/download/{backup_id}"
name = "api:backup:download" name = "api:backup:download"
async def get( async def get(
self, self,
request: Request, request: Request,
slug: str, backup_id: str,
) -> FileResponse | Response: ) -> FileResponse | Response:
"""Download a backup file.""" """Download a backup file."""
if not request["hass_user"].is_admin: if not request["hass_user"].is_admin:
@@ -51,7 +51,7 @@ class DownloadBackupView(HomeAssistantView):
if agent_id not in manager.backup_agents: if agent_id not in manager.backup_agents:
return Response(status=HTTPStatus.BAD_REQUEST) return Response(status=HTTPStatus.BAD_REQUEST)
agent = manager.backup_agents[agent_id] agent = manager.backup_agents[agent_id]
backup = await agent.async_get_backup(slug=slug) backup = await agent.async_get_backup(backup_id)
# We don't need to check if the path exists, aiohttp.FileResponse will handle # We don't need to check if the path exists, aiohttp.FileResponse will handle
# that # that
@@ -60,10 +60,10 @@ class DownloadBackupView(HomeAssistantView):
if agent_id in manager.local_backup_agents: if agent_id in manager.local_backup_agents:
local_agent = manager.local_backup_agents[agent_id] local_agent = manager.local_backup_agents[agent_id]
path = local_agent.get_backup_path(slug=slug) path = local_agent.get_backup_path(backup_id)
else: else:
path = manager.temp_backup_dir / f"{slug}.tar" path = manager.temp_backup_dir / f"{backup_id}.tar"
await agent.async_download_backup(id=backup.id, path=path) await agent.async_download_backup(backup_id, path=path)
# TODO: We need a callback to remove the temp file once the download is complete # TODO: We need a callback to remove the temp file once the download is complete
return FileResponse( return FileResponse(

View File

@@ -53,7 +53,7 @@ _BackupT = TypeVar("_BackupT", bound=BaseBackup, default=BaseBackup)
class NewBackup: class NewBackup:
"""New backup class.""" """New backup class."""
slug: str backup_id: str
@dataclass(slots=True) @dataclass(slots=True)
@@ -185,7 +185,7 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]):
@abc.abstractmethod @abc.abstractmethod
async def async_restore_backup( async def async_restore_backup(
self, self,
slug: str, backup_id: str,
*, *,
agent_id: str, agent_id: str,
password: str | None, password: str | None,
@@ -218,17 +218,17 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]):
) -> tuple[dict[str, Backup], dict[str, Exception]]: ) -> tuple[dict[str, Backup], dict[str, Exception]]:
"""Get backups. """Get backups.
Return a dictionary of Backup instances keyed by their slug. Return a dictionary of Backup instances keyed by their ID.
""" """
@abc.abstractmethod @abc.abstractmethod
async def async_get_backup( async def async_get_backup(
self, *, slug: str, **kwargs: Any self, backup_id: str, **kwargs: Any
) -> tuple[_BackupT | None, dict[str, Exception]]: ) -> tuple[_BackupT | None, dict[str, Exception]]:
"""Get a backup.""" """Get a backup."""
@abc.abstractmethod @abc.abstractmethod
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None: async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None:
"""Remove a backup.""" """Remove a backup."""
@abc.abstractmethod @abc.abstractmethod
@@ -265,12 +265,12 @@ class BackupManager(BaseBackupManager[Backup]):
self.backup_agents[agent_id].async_upload_backup( self.backup_agents[agent_id].async_upload_backup(
path=path, path=path,
metadata=BackupUploadMetadata( metadata=BackupUploadMetadata(
homeassistant=HAVERSION, backup_id=backup.backup_id,
size=backup.size,
date=backup.date, date=backup.date,
slug=backup.slug, homeassistant=HAVERSION,
name=backup.name, name=backup.name,
protected=backup.protected, protected=backup.protected,
size=backup.size,
), ),
) )
for agent_id in agent_ids for agent_id in agent_ids
@@ -304,21 +304,21 @@ class BackupManager(BaseBackupManager[Backup]):
if isinstance(result, BaseException): if isinstance(result, BaseException):
raise result raise result
for agent_backup in result: for agent_backup in result:
if agent_backup.slug not in backups: if agent_backup.backup_id not in backups:
backups[agent_backup.slug] = Backup( backups[agent_backup.backup_id] = Backup(
slug=agent_backup.slug,
name=agent_backup.name,
date=agent_backup.date,
agent_ids=[], agent_ids=[],
size=agent_backup.size, backup_id=agent_backup.backup_id,
date=agent_backup.date,
name=agent_backup.name,
protected=agent_backup.protected, protected=agent_backup.protected,
size=agent_backup.size,
) )
backups[agent_backup.slug].agent_ids.append(agent_ids[idx]) backups[agent_backup.backup_id].agent_ids.append(agent_ids[idx])
return (backups, agent_errors) return (backups, agent_errors)
async def async_get_backup( async def async_get_backup(
self, *, slug: str, **kwargs: Any self, backup_id: str, **kwargs: Any
) -> tuple[Backup | None, dict[str, Exception]]: ) -> tuple[Backup | None, dict[str, Exception]]:
"""Return a backup.""" """Return a backup."""
backup: Backup | None = None backup: Backup | None = None
@@ -327,7 +327,7 @@ class BackupManager(BaseBackupManager[Backup]):
get_backup_results = await asyncio.gather( get_backup_results = await asyncio.gather(
*( *(
agent.async_get_backup(slug=slug) agent.async_get_backup(backup_id)
for agent in self.backup_agents.values() for agent in self.backup_agents.values()
), ),
return_exceptions=True, return_exceptions=True,
@@ -342,23 +342,23 @@ class BackupManager(BaseBackupManager[Backup]):
continue continue
if backup is None: if backup is None:
backup = Backup( backup = Backup(
slug=result.slug,
name=result.name,
date=result.date,
agent_ids=[], agent_ids=[],
size=result.size, backup_id=result.backup_id,
date=result.date,
name=result.name,
protected=result.protected, protected=result.protected,
size=result.size,
) )
backup.agent_ids.append(agent_ids[idx]) backup.agent_ids.append(agent_ids[idx])
return (backup, agent_errors) return (backup, agent_errors)
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None: async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None:
"""Remove a backup.""" """Remove a backup."""
for agent in self.backup_agents.values(): for agent in self.backup_agents.values():
if not hasattr(agent, "async_remove_backup"): if not hasattr(agent, "async_remove_backup"):
continue continue
await agent.async_remove_backup(slug=slug) await agent.async_remove_backup(backup_id)
async def async_receive_backup( async def async_receive_backup(
self, self,
@@ -415,7 +415,7 @@ class BackupManager(BaseBackupManager[Backup]):
if local_file_paths: if local_file_paths:
tar_file_path = local_file_paths[0] tar_file_path = local_file_paths[0]
else: else:
tar_file_path = self.temp_backup_dir / f"{backup.slug}.tar" tar_file_path = self.temp_backup_dir / f"{backup.backup_id}.tar"
for local_path in local_file_paths: for local_path in local_file_paths:
shutil.copy(target_temp_file, local_path) shutil.copy(target_temp_file, local_path)
temp_dir_handler.cleanup() temp_dir_handler.cleanup()
@@ -430,7 +430,7 @@ class BackupManager(BaseBackupManager[Backup]):
return return
local_file_paths = [ local_file_paths = [
self.local_backup_agents[agent_id].get_backup_path(backup.slug) self.local_backup_agents[agent_id].get_backup_path(backup.backup_id)
for agent_id in agent_ids for agent_id in agent_ids
if agent_id in self.local_backup_agents if agent_id in self.local_backup_agents
] ]
@@ -464,23 +464,23 @@ class BackupManager(BaseBackupManager[Backup]):
raise HomeAssistantError("Invalid agent selected") raise HomeAssistantError("Invalid agent selected")
backup_name = name or f"Core {HAVERSION}" backup_name = name or f"Core {HAVERSION}"
date_str = dt_util.now().isoformat() date_str = dt_util.now().isoformat()
slug = _generate_slug(date_str, backup_name) backup_id = _generate_backup_id(date_str, backup_name)
self.backup_task = self.hass.async_create_task( self.backup_task = self.hass.async_create_task(
self._async_create_backup( self._async_create_backup(
addons_included=addons_included, addons_included=addons_included,
agent_ids=agent_ids, agent_ids=agent_ids,
backup_id=backup_id,
backup_name=backup_name, backup_name=backup_name,
database_included=database_included, database_included=database_included,
date_str=date_str, date_str=date_str,
folders_included=folders_included, folders_included=folders_included,
on_progress=on_progress, on_progress=on_progress,
password=password, password=password,
slug=slug,
), ),
name="backup_manager_create_backup", name="backup_manager_create_backup",
eager_start=False, # To ensure the task is not started before we return eager_start=False, # To ensure the task is not started before we return
) )
return NewBackup(slug=slug) return NewBackup(backup_id=backup_id)
async def _async_create_backup( async def _async_create_backup(
self, self,
@@ -493,13 +493,13 @@ class BackupManager(BaseBackupManager[Backup]):
folders_included: list[str] | None, folders_included: list[str] | None,
on_progress: Callable[[BackupProgress], None] | None, on_progress: Callable[[BackupProgress], None] | None,
password: str | None, password: str | None,
slug: str, backup_id: str,
) -> BaseBackup: ) -> BaseBackup:
"""Generate a backup.""" """Generate a backup."""
success = False success = False
local_file_paths = [ local_file_paths = [
self.local_backup_agents[agent_id].get_backup_path(slug) self.local_backup_agents[agent_id].get_backup_path(backup_id)
for agent_id in agent_ids for agent_id in agent_ids
if agent_id in self.local_backup_agents if agent_id in self.local_backup_agents
] ]
@@ -508,17 +508,17 @@ class BackupManager(BaseBackupManager[Backup]):
await self.async_pre_backup_actions() await self.async_pre_backup_actions()
backup_data = { backup_data = {
"slug": slug, "compressed": True,
"name": backup_name,
"date": date_str, "date": date_str,
"type": "partial",
"folders": ["homeassistant"], "folders": ["homeassistant"],
"homeassistant": { "homeassistant": {
"exclude_database": not database_included, "exclude_database": not database_included,
"version": HAVERSION, "version": HAVERSION,
}, },
"compressed": True, "name": backup_name,
"protected": password is not None, "protected": password is not None,
"slug": backup_id,
"type": "partial",
} }
tar_file_path, size_in_bytes = await self.hass.async_add_executor_job( tar_file_path, size_in_bytes = await self.hass.async_add_executor_job(
@@ -529,15 +529,15 @@ class BackupManager(BaseBackupManager[Backup]):
password, password,
) )
backup = BaseBackup( backup = BaseBackup(
slug=slug, backup_id=backup_id,
name=backup_name,
date=date_str, date=date_str,
size=round(size_in_bytes / 1_048_576, 2), name=backup_name,
protected=password is not None, protected=password is not None,
size=round(size_in_bytes / 1_048_576, 2),
) )
LOGGER.debug( LOGGER.debug(
"Generated new backup with slug %s, uploading to agents %s", "Generated new backup with backup_id %s, uploading to agents %s",
slug, backup_id,
agent_ids, agent_ids,
) )
await self._async_upload_backup( await self._async_upload_backup(
@@ -564,7 +564,7 @@ class BackupManager(BaseBackupManager[Backup]):
if tar_file_paths: if tar_file_paths:
tar_file_path = tar_file_paths[0] tar_file_path = tar_file_paths[0]
else: else:
tar_file_path = self.temp_backup_dir / f"{backup_data['slug']}.tar" tar_file_path = self.temp_backup_dir / f"{backup_data['backup_id']}.tar"
if not (backup_dir := tar_file_path.parent).exists(): if not (backup_dir := tar_file_path.parent).exists():
LOGGER.debug("Creating backup directory %s", backup_dir) LOGGER.debug("Creating backup directory %s", backup_dir)
backup_dir.mkdir() backup_dir.mkdir()
@@ -600,7 +600,7 @@ class BackupManager(BaseBackupManager[Backup]):
async def async_restore_backup( async def async_restore_backup(
self, self,
slug: str, backup_id: str,
*, *,
agent_id: str, agent_id: str,
password: str | None, password: str | None,
@@ -614,17 +614,21 @@ class BackupManager(BaseBackupManager[Backup]):
if agent_id in self.local_backup_agents: if agent_id in self.local_backup_agents:
local_agent = self.local_backup_agents[agent_id] local_agent = self.local_backup_agents[agent_id]
if not await local_agent.async_get_backup(slug=slug): if not await local_agent.async_get_backup(backup_id):
raise HomeAssistantError(f"Backup {slug} not found in agent {agent_id}") raise HomeAssistantError(
path = local_agent.get_backup_path(slug=slug) f"Backup {backup_id} not found in agent {agent_id}"
)
path = local_agent.get_backup_path(backup_id)
else: else:
path = self.temp_backup_dir / f"{slug}.tar" path = self.temp_backup_dir / f"{backup_id}.tar"
agent = self.backup_agents[agent_id] agent = self.backup_agents[agent_id]
if not (backup := await agent.async_get_backup(slug=slug)): if not await agent.async_get_backup(backup_id):
raise HomeAssistantError(f"Backup {slug} not found in agent {agent_id}") raise HomeAssistantError(
await agent.async_download_backup(id=backup.id, path=path) f"Backup {backup_id} not found in agent {agent_id}"
)
await agent.async_download_backup(backup_id, path=path)
path = local_agent.get_backup_path(slug) path = local_agent.get_backup_path(backup_id)
def _write_restore_file() -> None: def _write_restore_file() -> None:
"""Write the restore file.""" """Write the restore file."""
@@ -637,6 +641,6 @@ class BackupManager(BaseBackupManager[Backup]):
await self.hass.services.async_call("homeassistant", "restart", {}) await self.hass.services.async_call("homeassistant", "restart", {})
def _generate_slug(date: str, name: str) -> str: def _generate_backup_id(date: str, name: str) -> str:
"""Generate a backup slug.""" """Generate a backup ID."""
return hashlib.sha1(f"{date} - {name}".lower().encode()).hexdigest()[:8] return hashlib.sha1(f"{date} - {name}".lower().encode()).hexdigest()[:8]

View File

@@ -7,10 +7,10 @@ from dataclasses import asdict, dataclass
class BaseBackup: class BaseBackup:
"""Base backup class.""" """Base backup class."""
backup_id: str
date: str date: str
name: str name: str
protected: bool protected: bool
slug: str
size: float size: float
def as_dict(self) -> dict: def as_dict(self) -> dict:
@@ -22,9 +22,9 @@ class BaseBackup:
class BackupUploadMetadata: class BackupUploadMetadata:
"""Backup upload metadata.""" """Backup upload metadata."""
backup_id: str # The ID of the backup
date: str # The date the backup was created date: str # The date the backup was created
slug: str # The slug of the backup
size: float # The size of the backup (in bytes)
name: str # The name of the backup
homeassistant: str # The version of Home Assistant that created the backup homeassistant: str # The version of Home Assistant that created the backup
name: str # The name of the backup
protected: bool # If the backup is protected protected: bool # If the backup is protected
size: float # The size of the backup (in bytes)

View File

@@ -20,9 +20,9 @@ def read_backup(backup_path: Path) -> BaseBackup:
raise KeyError("backup.json not found in tar file") raise KeyError("backup.json not found in tar file")
data = json_loads_object(data_file.read()) data = json_loads_object(data_file.read())
return BaseBackup( return BaseBackup(
slug=cast(str, data["slug"]), backup_id=cast(str, data["slug"]),
name=cast(str, data["name"]),
date=cast(str, data["date"]), date=cast(str, data["date"]),
size=round(backup_path.stat().st_size / 1_048_576, 2), name=cast(str, data["name"]),
protected=cast(bool, data.get("protected", False)), protected=cast(bool, data.get("protected", False)),
size=round(backup_path.stat().st_size / 1_048_576, 2),
) )

View File

@@ -60,7 +60,7 @@ async def handle_info(
@websocket_api.websocket_command( @websocket_api.websocket_command(
{ {
vol.Required("type"): "backup/details", vol.Required("type"): "backup/details",
vol.Required("slug"): str, vol.Required("backup_id"): str,
} }
) )
@websocket_api.async_response @websocket_api.async_response
@@ -69,9 +69,9 @@ async def handle_details(
connection: websocket_api.ActiveConnection, connection: websocket_api.ActiveConnection,
msg: dict[str, Any], msg: dict[str, Any],
) -> None: ) -> None:
"""Get backup details for a specific slug.""" """Get backup details for a specific backup."""
backup, agent_errors = await hass.data[DATA_MANAGER].async_get_backup( backup, agent_errors = await hass.data[DATA_MANAGER].async_get_backup(
slug=msg["slug"] msg["backup_id"]
) )
connection.send_result( connection.send_result(
msg["id"], msg["id"],
@@ -88,7 +88,7 @@ async def handle_details(
@websocket_api.websocket_command( @websocket_api.websocket_command(
{ {
vol.Required("type"): "backup/remove", vol.Required("type"): "backup/remove",
vol.Required("slug"): str, vol.Required("backup_id"): str,
} }
) )
@websocket_api.async_response @websocket_api.async_response
@@ -98,7 +98,7 @@ async def handle_remove(
msg: dict[str, Any], msg: dict[str, Any],
) -> None: ) -> None:
"""Remove a backup.""" """Remove a backup."""
await hass.data[DATA_MANAGER].async_remove_backup(slug=msg["slug"]) await hass.data[DATA_MANAGER].async_remove_backup(msg["backup_id"])
connection.send_result(msg["id"]) connection.send_result(msg["id"])
@@ -106,7 +106,7 @@ async def handle_remove(
@websocket_api.websocket_command( @websocket_api.websocket_command(
{ {
vol.Required("type"): "backup/restore", vol.Required("type"): "backup/restore",
vol.Required("slug"): str, vol.Required("backup_id"): str,
vol.Required("agent_id"): str, vol.Required("agent_id"): str,
vol.Optional("password"): str, vol.Optional("password"): str,
} }
@@ -119,7 +119,7 @@ async def handle_restore(
) -> None: ) -> None:
"""Restore a backup.""" """Restore a backup."""
await hass.data[DATA_MANAGER].async_restore_backup( await hass.data[DATA_MANAGER].async_restore_backup(
slug=msg["slug"], msg["backup_id"],
agent_id=msg["agent_id"], agent_id=msg["agent_id"],
password=msg.get("password"), password=msg.get("password"),
) )
@@ -245,7 +245,6 @@ async def backup_agents_list_backups(
vol.Required("type"): "backup/agents/download", vol.Required("type"): "backup/agents/download",
vol.Required("agent_id"): str, vol.Required("agent_id"): str,
vol.Required("backup_id"): str, vol.Required("backup_id"): str,
vol.Required("slug"): str,
} }
) )
@websocket_api.async_response @websocket_api.async_response
@@ -262,9 +261,9 @@ async def backup_agents_download(
) )
return return
try: try:
path = manager.temp_backup_dir / f"{msg["slug"]}.tar" path = manager.temp_backup_dir / f"{msg["backup_id"]}.tar"
await agent.async_download_backup( await agent.async_download_backup(
id=msg["backup_id"], msg["backup_id"],
path=path, path=path,
) )
except Exception as err: # noqa: BLE001 except Exception as err: # noqa: BLE001

View File

@@ -5,12 +5,11 @@ from __future__ import annotations
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from uuid import uuid4
from homeassistant.components.backup import ( from homeassistant.components.backup import (
BackupAgent, BackupAgent,
BackupUploadMetadata, BackupUploadMetadata,
UploadedBackup, BaseBackup,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -32,25 +31,23 @@ class KitchenSinkBackupAgent(BackupAgent):
super().__init__() super().__init__()
self.name = name self.name = name
self._uploads = [ self._uploads = [
UploadedBackup( BaseBackup(
id="def456", backup_id="abc123",
date="1970-01-01T00:00:00Z",
name="Kitchen sink syncer", name="Kitchen sink syncer",
protected=False, protected=False,
slug="abc123",
size=1234, size=1234,
date="1970-01-01T00:00:00Z",
) )
] ]
async def async_download_backup( async def async_download_backup(
self, self,
*, backup_id: str,
id: str,
path: Path, path: Path,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Download a backup file.""" """Download a backup file."""
LOGGER.info("Downloading backup %s to %s", id, path) LOGGER.info("Downloading backup %s to %s", backup_id, path)
async def async_upload_backup( async def async_upload_backup(
self, self,
@@ -62,28 +59,26 @@ class KitchenSinkBackupAgent(BackupAgent):
"""Upload a backup.""" """Upload a backup."""
LOGGER.info("Uploading backup %s %s", path.name, metadata) LOGGER.info("Uploading backup %s %s", path.name, metadata)
self._uploads.append( self._uploads.append(
UploadedBackup( BaseBackup(
id=uuid4().hex, backup_id=metadata.backup_id,
date=metadata.date,
name=metadata.name, name=metadata.name,
protected=metadata.protected, protected=metadata.protected,
slug=metadata.slug,
size=metadata.size, size=metadata.size,
date=metadata.date,
) )
) )
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]: async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
"""List synced backups.""" """List synced backups."""
return self._uploads return self._uploads
async def async_get_backup( async def async_get_backup(
self, self,
*, backup_id: str,
slug: str,
**kwargs: Any, **kwargs: Any,
) -> UploadedBackup | None: ) -> BaseBackup | None:
"""Return a backup.""" """Return a backup."""
for backup in self._uploads: for backup in self._uploads:
if backup.slug == slug: if backup.backup_id == backup_id:
return backup return backup
return None return None

View File

@@ -10,12 +10,11 @@ from homeassistant.components.backup import (
DOMAIN, DOMAIN,
BackupAgent, BackupAgent,
BackupUploadMetadata, BackupUploadMetadata,
UploadedBackup, BaseBackup,
) )
from homeassistant.components.backup.backup import LocalBackup from homeassistant.components.backup.backup import LocalBackup
from homeassistant.components.backup.const import DATA_MANAGER from homeassistant.components.backup.const import DATA_MANAGER
from homeassistant.components.backup.manager import Backup from homeassistant.components.backup.manager import Backup
from homeassistant.components.backup.models import BaseBackup
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@@ -23,29 +22,28 @@ from homeassistant.setup import async_setup_component
LOCAL_AGENT_ID = f"{DOMAIN}.local" LOCAL_AGENT_ID = f"{DOMAIN}.local"
TEST_BASE_BACKUP = BaseBackup( TEST_BASE_BACKUP = BaseBackup(
slug="abc123", backup_id="abc123",
name="Test",
date="1970-01-01T00:00:00.000Z", date="1970-01-01T00:00:00.000Z",
size=0.0, name="Test",
protected=False, protected=False,
size=0.0,
) )
TEST_BACKUP = Backup( TEST_BACKUP = Backup(
agent_ids=["backup.local"], agent_ids=["backup.local"],
slug="abc123", backup_id="abc123",
name="Test",
date="1970-01-01T00:00:00.000Z", date="1970-01-01T00:00:00.000Z",
size=0.0, name="Test",
protected=False, protected=False,
size=0.0,
) )
TEST_BACKUP_PATH = Path("abc123.tar") TEST_BACKUP_PATH = Path("abc123.tar")
TEST_LOCAL_BACKUP = LocalBackup( TEST_LOCAL_BACKUP = LocalBackup(
id="abc123",
slug="abc123",
name="Test",
date="1970-01-01T00:00:00.000Z", date="1970-01-01T00:00:00.000Z",
backup_id="abc123",
name="Test",
path=Path("abc123.tar"), path=Path("abc123.tar"),
size=0.0,
protected=False, protected=False,
size=0.0,
) )
@@ -74,35 +72,33 @@ class BackupAgentTest(BackupAgent):
) -> None: ) -> None:
"""Upload a backup.""" """Upload a backup."""
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]: async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
"""List backups.""" """List backups."""
return [ return [
UploadedBackup( BaseBackup(
id="abc123", backup_id="abc123",
date="1970-01-01T00:00:00Z", date="1970-01-01T00:00:00Z",
name="Test", name="Test",
protected=False, protected=False,
size=13.37, size=13.37,
slug="abc123",
) )
] ]
async def async_get_backup( async def async_get_backup(
self, self,
*, *,
slug: str, backup_id: str,
**kwargs: Any, **kwargs: Any,
) -> UploadedBackup | None: ) -> BaseBackup | None:
"""Return a backup.""" """Return a backup."""
if slug != "abc123": if backup_id != "abc123":
return None return None
return UploadedBackup( return BaseBackup(
id="abc123", backup_id="abc123",
date="1970-01-01T00:00:00Z", date="1970-01-01T00:00:00Z",
name="Test", name="Test",
protected=False, protected=False,
size=13.37, size=13.37,
slug="abc123",
) )
@@ -119,7 +115,7 @@ async def setup_backup_integration(
return result return result
local_agent = hass.data[DATA_MANAGER].backup_agents[LOCAL_AGENT_ID] local_agent = hass.data[DATA_MANAGER].backup_agents[LOCAL_AGENT_ID]
local_agent._backups = {backups.slug: backups for backups in backups} local_agent._backups = {backup.backup_id: backup for backup in backups}
local_agent._loaded_backups = True local_agent._loaded_backups = True
return result return result

View File

@@ -79,12 +79,11 @@
'result': list([ 'result': list([
dict({ dict({
'agent_id': 'domain.test', 'agent_id': 'domain.test',
'backup_id': 'abc123',
'date': '1970-01-01T00:00:00Z', 'date': '1970-01-01T00:00:00Z',
'id': 'abc123',
'name': 'Test', 'name': 'Test',
'protected': False, 'protected': False,
'size': 13.37, 'size': 13.37,
'slug': 'abc123',
}), }),
]), ]),
'success': True, 'success': True,
@@ -97,12 +96,11 @@
'result': list([ 'result': list([
dict({ dict({
'agent_id': 'domain.test', 'agent_id': 'domain.test',
'backup_id': 'abc123',
'date': '1970-01-01T00:00:00Z', 'date': '1970-01-01T00:00:00Z',
'id': 'abc123',
'name': 'Test', 'name': 'Test',
'protected': False, 'protected': False,
'size': 13.37, 'size': 13.37,
'slug': 'abc123',
}), }),
]), ]),
'success': True, 'success': True,
@@ -367,11 +365,11 @@
'agent_ids': list([ 'agent_ids': list([
'backup.local', 'backup.local',
]), ]),
'backup_id': 'abc123',
'date': '1970-01-01T00:00:00.000Z', 'date': '1970-01-01T00:00:00.000Z',
'name': 'Test', 'name': 'Test',
'protected': False, 'protected': False,
'size': 0.0, 'size': 0.0,
'slug': 'abc123',
}), }),
}), }),
'success': True, 'success': True,
@@ -401,11 +399,11 @@
'agent_ids': list([ 'agent_ids': list([
'backup.local', 'backup.local',
]), ]),
'backup_id': 'abc123',
'date': '1970-01-01T00:00:00.000Z', 'date': '1970-01-01T00:00:00.000Z',
'name': 'Test', 'name': 'Test',
'protected': False, 'protected': False,
'size': 0.0, 'size': 0.0,
'slug': 'abc123',
}), }),
}), }),
'success': True, 'success': True,
@@ -460,7 +458,7 @@
dict({ dict({
'id': 1, 'id': 1,
'result': dict({ 'result': dict({
'slug': '27f5c632', 'backup_id': '27f5c632',
}), }),
'success': True, 'success': True,
'type': 'result', 'type': 'result',
@@ -481,7 +479,7 @@
dict({ dict({
'id': 1, 'id': 1,
'result': dict({ 'result': dict({
'slug': '27f5c632', 'backup_id': '27f5c632',
}), }),
'success': True, 'success': True,
'type': 'result', 'type': 'result',
@@ -502,7 +500,7 @@
dict({ dict({
'id': 1, 'id': 1,
'result': dict({ 'result': dict({
'slug': '27f5c632', 'backup_id': '27f5c632',
}), }),
'success': True, 'success': True,
'type': 'result', 'type': 'result',
@@ -523,7 +521,7 @@
dict({ dict({
'id': 1, 'id': 1,
'result': dict({ 'result': dict({
'slug': 'abc123', 'backup_id': 'abc123',
}), }),
'success': True, 'success': True,
'type': 'result', 'type': 'result',
@@ -533,7 +531,7 @@
dict({ dict({
'id': 1, 'id': 1,
'result': dict({ 'result': dict({
'slug': 'abc123', 'backup_id': 'abc123',
}), }),
'success': True, 'success': True,
'type': 'result', 'type': 'result',
@@ -562,11 +560,11 @@
'agent_ids': list([ 'agent_ids': list([
'backup.local', 'backup.local',
]), ]),
'backup_id': 'abc123',
'date': '1970-01-01T00:00:00.000Z', 'date': '1970-01-01T00:00:00.000Z',
'name': 'Test', 'name': 'Test',
'protected': False, 'protected': False,
'size': 0.0, 'size': 0.0,
'slug': 'abc123',
}), }),
]), ]),
}), }),
@@ -587,11 +585,11 @@
'agent_ids': list([ 'agent_ids': list([
'backup.local', 'backup.local',
]), ]),
'backup_id': 'abc123',
'date': '1970-01-01T00:00:00.000Z', 'date': '1970-01-01T00:00:00.000Z',
'name': 'Test', 'name': 'Test',
'protected': False, 'protected': False,
'size': 0.0, 'size': 0.0,
'slug': 'abc123',
}), }),
]), ]),
}), }),

View File

@@ -133,9 +133,9 @@ async def test_load_backups(hass: HomeAssistant) -> None:
patch( patch(
"homeassistant.components.backup.util.json_loads_object", "homeassistant.components.backup.util.json_loads_object",
return_value={ return_value={
"slug": TEST_LOCAL_BACKUP.slug,
"name": TEST_LOCAL_BACKUP.name,
"date": TEST_LOCAL_BACKUP.date, "date": TEST_LOCAL_BACKUP.date,
"name": TEST_LOCAL_BACKUP.name,
"slug": TEST_LOCAL_BACKUP.backup_id,
}, },
), ),
patch( patch(
@@ -145,7 +145,7 @@ async def test_load_backups(hass: HomeAssistant) -> None:
): ):
await manager.backup_agents[LOCAL_AGENT_ID].load_backups() await manager.backup_agents[LOCAL_AGENT_ID].load_backups()
backups, agent_errors = await manager.async_get_backups() backups, agent_errors = await manager.async_get_backups()
assert backups == {TEST_BACKUP.slug: TEST_BACKUP} assert backups == {TEST_BACKUP.backup_id: TEST_BACKUP}
assert agent_errors == {} assert agent_errors == {}
@@ -181,11 +181,11 @@ async def test_removing_backup(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP} local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
local_agent._loaded_backups = True local_agent._loaded_backups = True
with patch("pathlib.Path.exists", return_value=True): with patch("pathlib.Path.exists", return_value=True):
await manager.async_remove_backup(slug=TEST_LOCAL_BACKUP.slug) await manager.async_remove_backup(TEST_LOCAL_BACKUP.backup_id)
assert "Removed backup located at" in caplog.text assert "Removed backup located at" in caplog.text
@@ -199,7 +199,7 @@ async def test_removing_non_existing_backup(
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform) await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms() await manager.load_platforms()
await manager.async_remove_backup(slug="non_existing") await manager.async_remove_backup("non_existing")
assert "Removed backup located at" not in caplog.text assert "Removed backup located at" not in caplog.text
@@ -214,18 +214,18 @@ async def test_getting_backup_that_does_not_exist(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP} local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
local_agent._loaded_backups = True local_agent._loaded_backups = True
with patch("pathlib.Path.exists", return_value=False): with patch("pathlib.Path.exists", return_value=False):
backup, agent_errors = await manager.async_get_backup( backup, agent_errors = await manager.async_get_backup(
slug=TEST_LOCAL_BACKUP.slug TEST_LOCAL_BACKUP.backup_id
) )
assert backup is None assert backup is None
assert agent_errors == {} assert agent_errors == {}
assert ( assert (
f"Removing tracked backup ({TEST_LOCAL_BACKUP.slug}) that " f"Removing tracked backup ({TEST_LOCAL_BACKUP.backup_id}) that "
f"does not exists on the expected path {TEST_LOCAL_BACKUP.path}" f"does not exists on the expected path {TEST_LOCAL_BACKUP.path}"
) in caplog.text ) in caplog.text
@@ -278,7 +278,7 @@ async def test_async_create_backup(
hass, manager, mocked_json_bytes, mocked_tarfile, **params hass, manager, mocked_json_bytes, mocked_tarfile, **params
) )
assert "Generated new backup with slug " in caplog.text assert "Generated new backup with backup_id " in caplog.text
assert "Creating backup directory" in caplog.text assert "Creating backup directory" in caplog.text
assert "Loaded 0 platforms" in caplog.text assert "Loaded 0 platforms" in caplog.text
assert "Loaded 1 agents" in caplog.text assert "Loaded 1 agents" in caplog.text
@@ -457,7 +457,7 @@ async def test_async_trigger_restore(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP} local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
local_agent._loaded_backups = True local_agent._loaded_backups = True
with ( with (
@@ -466,7 +466,7 @@ async def test_async_trigger_restore(
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
): ):
await manager.async_restore_backup( await manager.async_restore_backup(
TEST_LOCAL_BACKUP.slug, agent_id=LOCAL_AGENT_ID, password=None TEST_LOCAL_BACKUP.backup_id, agent_id=LOCAL_AGENT_ID, password=None
) )
assert ( assert (
mocked_write_text.call_args[0][0] mocked_write_text.call_args[0][0]
@@ -486,7 +486,7 @@ async def test_async_trigger_restore_with_password(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP} local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
local_agent._loaded_backups = True local_agent._loaded_backups = True
with ( with (
@@ -495,7 +495,7 @@ async def test_async_trigger_restore_with_password(
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
): ):
await manager.async_restore_backup( await manager.async_restore_backup(
slug=TEST_LOCAL_BACKUP.slug, agent_id=LOCAL_AGENT_ID, password="abc123" TEST_LOCAL_BACKUP.backup_id, agent_id=LOCAL_AGENT_ID, password="abc123"
) )
assert ( assert (
mocked_write_text.call_args[0][0] mocked_write_text.call_args[0][0]
@@ -516,5 +516,5 @@ async def test_async_trigger_restore_missing_backup(hass: HomeAssistant) -> None
with pytest.raises(HomeAssistantError, match="Backup abc123 not found"): with pytest.raises(HomeAssistantError, match="Backup abc123 not found"):
await manager.async_restore_backup( await manager.async_restore_backup(
TEST_LOCAL_BACKUP.slug, agent_id=LOCAL_AGENT_ID, password=None TEST_LOCAL_BACKUP.backup_id, agent_id=LOCAL_AGENT_ID, password=None
) )

View File

@@ -108,7 +108,9 @@ async def test_details(
await hass.async_block_till_done() await hass.async_block_till_done()
with patch("pathlib.Path.exists", return_value=True): with patch("pathlib.Path.exists", return_value=True):
await client.send_json_auto_id({"type": "backup/details", "slug": "abc123"}) await client.send_json_auto_id(
{"type": "backup/details", "backup_id": "abc123"}
)
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot
@@ -132,7 +134,9 @@ async def test_details_with_errors(
patch("pathlib.Path.exists", return_value=True), patch("pathlib.Path.exists", return_value=True),
patch.object(BackupAgentTest, "async_get_backup", side_effect=side_effect), patch.object(BackupAgentTest, "async_get_backup", side_effect=side_effect),
): ):
await client.send_json_auto_id({"type": "backup/details", "slug": "abc123"}) await client.send_json_auto_id(
{"type": "backup/details", "backup_id": "abc123"}
)
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot
@@ -158,7 +162,7 @@ async def test_remove(
with patch( with patch(
"homeassistant.components.backup.manager.BackupManager.async_remove_backup", "homeassistant.components.backup.manager.BackupManager.async_remove_backup",
): ):
await client.send_json_auto_id({"type": "backup/remove", "slug": "abc123"}) await client.send_json_auto_id({"type": "backup/remove", "backup_id": "abc123"})
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot
@@ -281,7 +285,11 @@ async def test_restore(
"homeassistant.components.backup.manager.BackupManager.async_restore_backup", "homeassistant.components.backup.manager.BackupManager.async_restore_backup",
): ):
await client.send_json_auto_id( await client.send_json_auto_id(
{"type": "backup/restore", "slug": "abc123", "agent_id": "backup.local"} {
"type": "backup/restore",
"backup_id": "abc123",
"agent_id": "backup.local",
}
) )
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot
@@ -481,15 +489,14 @@ async def test_agents_download(
await client.send_json_auto_id( await client.send_json_auto_id(
{ {
"type": "backup/agents/download", "type": "backup/agents/download",
"slug": "abc123",
"agent_id": "domain.test", "agent_id": "domain.test",
"backup_id": "abc123", "backup_id": "abc123",
} }
) )
with patch.object(BackupAgentTest, "async_download_backup") as download_mock: with patch.object(BackupAgentTest, "async_download_backup") as download_mock:
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot
assert download_mock.call_args[0] == ("abc123",)
assert download_mock.call_args[1] == { assert download_mock.call_args[1] == {
"id": "abc123",
"path": Path(hass.config.path("tmp_backups"), "abc123.tar"), "path": Path(hass.config.path("tmp_backups"), "abc123.tar"),
} }
@@ -509,7 +516,6 @@ async def test_agents_download_exception(
await client.send_json_auto_id( await client.send_json_auto_id(
{ {
"type": "backup/agents/download", "type": "backup/agents/download",
"slug": "abc123",
"agent_id": "domain.test", "agent_id": "domain.test",
"backup_id": "abc123", "backup_id": "abc123",
} }
@@ -533,7 +539,6 @@ async def test_agents_download_unknown_agent(
await client.send_json_auto_id( await client.send_json_auto_id(
{ {
"type": "backup/agents/download", "type": "backup/agents/download",
"slug": "abc123",
"agent_id": "domain.test", "agent_id": "domain.test",
"backup_id": "abc123", "backup_id": "abc123",
} }

View File

@@ -3,7 +3,6 @@
from collections.abc import AsyncGenerator from collections.abc import AsyncGenerator
from io import StringIO from io import StringIO
from unittest.mock import patch from unittest.mock import patch
from uuid import UUID
import pytest import pytest
@@ -69,12 +68,11 @@ async def test_agents_list_backups(
assert response["result"] == [ assert response["result"] == [
{ {
"agent_id": "kitchen_sink.syncer", "agent_id": "kitchen_sink.syncer",
"backup_id": "abc123",
"date": "1970-01-01T00:00:00Z", "date": "1970-01-01T00:00:00Z",
"id": "def456",
"slug": "abc123",
"size": 1234,
"name": "Kitchen sink syncer", "name": "Kitchen sink syncer",
"protected": False, "protected": False,
"size": 1234,
} }
] ]
@@ -86,13 +84,11 @@ async def test_agents_download(
) -> None: ) -> None:
"""Test backup agents download.""" """Test backup agents download."""
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
backup_id = "def456" backup_id = "abc123"
slug = "abc123"
await client.send_json_auto_id( await client.send_json_auto_id(
{ {
"type": "backup/agents/download", "type": "backup/agents/download",
"slug": slug,
"agent_id": "kitchen_sink.syncer", "agent_id": "kitchen_sink.syncer",
"backup_id": backup_id, "backup_id": backup_id,
} }
@@ -100,7 +96,7 @@ async def test_agents_download(
response = await client.receive_json() response = await client.receive_json()
assert response["success"] assert response["success"]
path = hass.config.path(f"tmp_backups/{slug}.tar") path = hass.config.path(f"tmp_backups/{backup_id}.tar")
assert f"Downloading backup {backup_id} to {path}" in caplog.text assert f"Downloading backup {backup_id} to {path}" in caplog.text
@@ -114,18 +110,16 @@ async def test_agents_upload(
"""Test backup agents upload.""" """Test backup agents upload."""
ws_client = await hass_ws_client(hass, hass_supervisor_access_token) ws_client = await hass_ws_client(hass, hass_supervisor_access_token)
client = await hass_client() client = await hass_client()
slug = "test-backup" backup_id = "test-backup"
test_backup = BaseBackup( test_backup = BaseBackup(
slug=slug, backup_id=backup_id,
name="Test",
date="1970-01-01T00:00:00.000Z", date="1970-01-01T00:00:00.000Z",
size=0.0, name="Test",
protected=False, protected=False,
size=0.0,
) )
uuid = UUID(int=123456)
with ( with (
patch("homeassistant.components.kitchen_sink.backup.uuid4", return_value=uuid),
patch( patch(
"homeassistant.components.backup.manager.BackupManager.async_get_backup", "homeassistant.components.backup.manager.BackupManager.async_get_backup",
) as fetch_backup, ) as fetch_backup,
@@ -141,22 +135,20 @@ async def test_agents_upload(
) )
assert resp.status == 201 assert resp.status == 201
backup_name = f"{slug}.tar" backup_name = f"{backup_id}.tar"
assert f"Uploading backup {backup_name}" in caplog.text assert f"Uploading backup {backup_name}" in caplog.text
with patch("homeassistant.components.kitchen_sink.backup.uuid4", return_value=uuid): await ws_client.send_json_auto_id({"type": "backup/agents/list_backups"})
await ws_client.send_json_auto_id({"type": "backup/agents/list_backups"}) response = await ws_client.receive_json()
response = await ws_client.receive_json()
assert response["success"] assert response["success"]
backup_list = response["result"] backup_list = response["result"]
assert len(backup_list) == 2 assert len(backup_list) == 2
assert backup_list[1] == { assert backup_list[1] == {
"agent_id": "kitchen_sink.syncer", "agent_id": "kitchen_sink.syncer",
"backup_id": backup_id,
"date": test_backup.date, "date": test_backup.date,
"id": uuid.hex,
"slug": slug,
"size": 0.0,
"name": test_backup.name, "name": test_backup.name,
"protected": test_backup.protected, "protected": test_backup.protected,
"size": 0.0,
} }