diff --git a/homeassistant/components/backup/__init__.py b/homeassistant/components/backup/__init__.py index 73bbdefbbdf..0680e4b27e5 100644 --- a/homeassistant/components/backup/__init__.py +++ b/homeassistant/components/backup/__init__.py @@ -8,7 +8,7 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.hassio import is_hassio from homeassistant.helpers.typing import ConfigType -from .agent import BackupAgent, BackupAgentPlatformProtocol, UploadedBackup +from .agent import BackupAgent, BackupAgentPlatformProtocol from .const import DOMAIN, LOGGER from .http import async_register_http_views from .manager import Backup, BackupManager, BackupPlatformProtocol @@ -22,7 +22,6 @@ __all__ = [ "BackupPlatformProtocol", "BackupUploadMetadata", "BaseBackup", - "UploadedBackup", ] CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN) diff --git a/homeassistant/components/backup/agent.py b/homeassistant/components/backup/agent.py index f0b48523cb0..ca1d3933f74 100644 --- a/homeassistant/components/backup/agent.py +++ b/homeassistant/components/backup/agent.py @@ -3,7 +3,6 @@ from __future__ import annotations import abc -from dataclasses import dataclass from pathlib import Path from typing import Any, Protocol @@ -23,13 +22,6 @@ class BackupAgentUnreachableError(BackupAgentError): _message = "The backup agent is unreachable." -@dataclass(slots=True) -class UploadedBackup(BaseBackup): - """Uploaded backup class.""" - - id: str - - class BackupAgent(abc.ABC): """Backup agent interface.""" @@ -38,14 +30,14 @@ class BackupAgent(abc.ABC): @abc.abstractmethod async def async_download_backup( self, + backup_id: str, *, - id: str, path: Path, **kwargs: Any, ) -> None: """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. """ @@ -64,16 +56,15 @@ class BackupAgent(abc.ABC): """ @abc.abstractmethod - async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]: + async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]: """List backups.""" @abc.abstractmethod async def async_get_backup( self, - *, - slug: str, + backup_id: str, **kwargs: Any, - ) -> UploadedBackup | None: + ) -> BaseBackup | None: """Return a backup.""" @@ -81,10 +72,10 @@ class LocalBackupAgent(BackupAgent): """Local backup agent.""" @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. - 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. """ diff --git a/homeassistant/components/backup/backup.py b/homeassistant/components/backup/backup.py index cf279486d68..e208d5833bc 100644 --- a/homeassistant/components/backup/backup.py +++ b/homeassistant/components/backup/backup.py @@ -10,9 +10,9 @@ from typing import Any from homeassistant.core import HomeAssistant -from .agent import BackupAgent, LocalBackupAgent, UploadedBackup +from .agent import BackupAgent, LocalBackupAgent from .const import LOGGER -from .models import BackupUploadMetadata +from .models import BackupUploadMetadata, BaseBackup from .util import read_backup @@ -25,7 +25,7 @@ async def async_get_backup_agents( @dataclass(slots=True) -class LocalBackup(UploadedBackup): +class LocalBackup(BaseBackup): """Local backup class.""" path: Path @@ -62,23 +62,22 @@ class CoreLocalBackupAgent(LocalBackupAgent): try: base_backup = read_backup(backup_path) backup = LocalBackup( - id=base_backup.slug, - slug=base_backup.slug, + backup_id=base_backup.backup_id, name=base_backup.name, date=base_backup.date, path=backup_path, size=round(backup_path.stat().st_size / 1_048_576, 2), protected=base_backup.protected, ) - backups[backup.slug] = backup + backups[backup.backup_id] = backup except (OSError, TarError, json.JSONDecodeError, KeyError) as err: LOGGER.warning("Unable to read backup %s: %s", backup_path, err) return backups async def async_download_backup( self, + backup_id: str, *, - id: str, path: Path, **kwargs: Any, ) -> None: @@ -93,17 +92,16 @@ class CoreLocalBackupAgent(LocalBackupAgent): **kwargs: Any, ) -> None: """Upload a backup.""" - self._backups[metadata.slug] = LocalBackup( - id=metadata.slug, # Do we need another ID? - slug=metadata.slug, - name=metadata.name, + self._backups[metadata.backup_id] = LocalBackup( + backup_id=metadata.backup_id, date=metadata.date, + name=metadata.name, path=path, - size=round(path.stat().st_size / 1_048_576, 2), 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.""" if not self._loaded_backups: await self.load_backups() @@ -111,15 +109,14 @@ class CoreLocalBackupAgent(LocalBackupAgent): async def async_get_backup( self, - *, - slug: str, + backup_id: str, **kwargs: Any, - ) -> UploadedBackup | None: + ) -> LocalBackup | None: """Return a backup.""" if not self._loaded_backups: await self.load_backups() - if not (backup := self._backups.get(slug)): + if not (backup := self._backups.get(backup_id)): return None 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" " path %s" ), - backup.slug, + backup.backup_id, backup.path, ) - self._backups.pop(slug) + self._backups.pop(backup_id) return None 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 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.""" - if (backup := await self.async_get_backup(slug=slug)) is None: + if (backup := await self.async_get_backup(backup_id)) is None: return - await self._hass.async_add_executor_job(backup.path.unlink, True) # type: ignore[attr-defined] - LOGGER.debug("Removed backup located at %s", backup.path) # type: ignore[attr-defined] - self._backups.pop(slug) + await self._hass.async_add_executor_job(backup.path.unlink, True) + LOGGER.debug("Removed backup located at %s", backup.path) + self._backups.pop(backup_id) diff --git a/homeassistant/components/backup/http.py b/homeassistant/components/backup/http.py index 3503b16d9a0..07d03e95dce 100644 --- a/homeassistant/components/backup/http.py +++ b/homeassistant/components/backup/http.py @@ -31,13 +31,13 @@ def async_register_http_views(hass: HomeAssistant) -> None: class DownloadBackupView(HomeAssistantView): """Generate backup view.""" - url = "/api/backup/download/{slug}" + url = "/api/backup/download/{backup_id}" name = "api:backup:download" async def get( self, request: Request, - slug: str, + backup_id: str, ) -> FileResponse | Response: """Download a backup file.""" if not request["hass_user"].is_admin: @@ -51,7 +51,7 @@ class DownloadBackupView(HomeAssistantView): if agent_id not in manager.backup_agents: return Response(status=HTTPStatus.BAD_REQUEST) 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 # that @@ -60,10 +60,10 @@ class DownloadBackupView(HomeAssistantView): if agent_id in manager.local_backup_agents: 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: - path = manager.temp_backup_dir / f"{slug}.tar" - await agent.async_download_backup(id=backup.id, path=path) + path = manager.temp_backup_dir / f"{backup_id}.tar" + await agent.async_download_backup(backup_id, path=path) # TODO: We need a callback to remove the temp file once the download is complete return FileResponse( diff --git a/homeassistant/components/backup/manager.py b/homeassistant/components/backup/manager.py index 5a5fa66d32a..f52d01b3fc2 100644 --- a/homeassistant/components/backup/manager.py +++ b/homeassistant/components/backup/manager.py @@ -53,7 +53,7 @@ _BackupT = TypeVar("_BackupT", bound=BaseBackup, default=BaseBackup) class NewBackup: """New backup class.""" - slug: str + backup_id: str @dataclass(slots=True) @@ -185,7 +185,7 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]): @abc.abstractmethod async def async_restore_backup( self, - slug: str, + backup_id: str, *, agent_id: str, password: str | None, @@ -218,17 +218,17 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]): ) -> tuple[dict[str, Backup], dict[str, Exception]]: """Get backups. - Return a dictionary of Backup instances keyed by their slug. + Return a dictionary of Backup instances keyed by their ID. """ @abc.abstractmethod async def async_get_backup( - self, *, slug: str, **kwargs: Any + self, backup_id: str, **kwargs: Any ) -> tuple[_BackupT | None, dict[str, Exception]]: """Get a backup.""" @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.""" @abc.abstractmethod @@ -265,12 +265,12 @@ class BackupManager(BaseBackupManager[Backup]): self.backup_agents[agent_id].async_upload_backup( path=path, metadata=BackupUploadMetadata( - homeassistant=HAVERSION, - size=backup.size, + backup_id=backup.backup_id, date=backup.date, - slug=backup.slug, + homeassistant=HAVERSION, name=backup.name, protected=backup.protected, + size=backup.size, ), ) for agent_id in agent_ids @@ -304,21 +304,21 @@ class BackupManager(BaseBackupManager[Backup]): if isinstance(result, BaseException): raise result for agent_backup in result: - if agent_backup.slug not in backups: - backups[agent_backup.slug] = Backup( - slug=agent_backup.slug, - name=agent_backup.name, - date=agent_backup.date, + if agent_backup.backup_id not in backups: + backups[agent_backup.backup_id] = Backup( agent_ids=[], - size=agent_backup.size, + backup_id=agent_backup.backup_id, + date=agent_backup.date, + name=agent_backup.name, 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) async def async_get_backup( - self, *, slug: str, **kwargs: Any + self, backup_id: str, **kwargs: Any ) -> tuple[Backup | None, dict[str, Exception]]: """Return a backup.""" backup: Backup | None = None @@ -327,7 +327,7 @@ class BackupManager(BaseBackupManager[Backup]): 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() ), return_exceptions=True, @@ -342,23 +342,23 @@ class BackupManager(BaseBackupManager[Backup]): continue if backup is None: backup = Backup( - slug=result.slug, - name=result.name, - date=result.date, agent_ids=[], - size=result.size, + backup_id=result.backup_id, + date=result.date, + name=result.name, protected=result.protected, + size=result.size, ) backup.agent_ids.append(agent_ids[idx]) 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.""" for agent in self.backup_agents.values(): if not hasattr(agent, "async_remove_backup"): continue - await agent.async_remove_backup(slug=slug) + await agent.async_remove_backup(backup_id) async def async_receive_backup( self, @@ -415,7 +415,7 @@ class BackupManager(BaseBackupManager[Backup]): if local_file_paths: tar_file_path = local_file_paths[0] 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: shutil.copy(target_temp_file, local_path) temp_dir_handler.cleanup() @@ -430,7 +430,7 @@ class BackupManager(BaseBackupManager[Backup]): return 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 if agent_id in self.local_backup_agents ] @@ -464,23 +464,23 @@ class BackupManager(BaseBackupManager[Backup]): raise HomeAssistantError("Invalid agent selected") backup_name = name or f"Core {HAVERSION}" 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._async_create_backup( addons_included=addons_included, agent_ids=agent_ids, + backup_id=backup_id, backup_name=backup_name, database_included=database_included, date_str=date_str, folders_included=folders_included, on_progress=on_progress, password=password, - slug=slug, ), name="backup_manager_create_backup", 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( self, @@ -493,13 +493,13 @@ class BackupManager(BaseBackupManager[Backup]): folders_included: list[str] | None, on_progress: Callable[[BackupProgress], None] | None, password: str | None, - slug: str, + backup_id: str, ) -> BaseBackup: """Generate a backup.""" success = False 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 if agent_id in self.local_backup_agents ] @@ -508,17 +508,17 @@ class BackupManager(BaseBackupManager[Backup]): await self.async_pre_backup_actions() backup_data = { - "slug": slug, - "name": backup_name, + "compressed": True, "date": date_str, - "type": "partial", "folders": ["homeassistant"], "homeassistant": { "exclude_database": not database_included, "version": HAVERSION, }, - "compressed": True, + "name": backup_name, "protected": password is not None, + "slug": backup_id, + "type": "partial", } tar_file_path, size_in_bytes = await self.hass.async_add_executor_job( @@ -529,15 +529,15 @@ class BackupManager(BaseBackupManager[Backup]): password, ) backup = BaseBackup( - slug=slug, - name=backup_name, + backup_id=backup_id, date=date_str, - size=round(size_in_bytes / 1_048_576, 2), + name=backup_name, protected=password is not None, + size=round(size_in_bytes / 1_048_576, 2), ) LOGGER.debug( - "Generated new backup with slug %s, uploading to agents %s", - slug, + "Generated new backup with backup_id %s, uploading to agents %s", + backup_id, agent_ids, ) await self._async_upload_backup( @@ -564,7 +564,7 @@ class BackupManager(BaseBackupManager[Backup]): if tar_file_paths: tar_file_path = tar_file_paths[0] 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(): LOGGER.debug("Creating backup directory %s", backup_dir) backup_dir.mkdir() @@ -600,7 +600,7 @@ class BackupManager(BaseBackupManager[Backup]): async def async_restore_backup( self, - slug: str, + backup_id: str, *, agent_id: str, password: str | None, @@ -614,17 +614,21 @@ class BackupManager(BaseBackupManager[Backup]): if agent_id in self.local_backup_agents: local_agent = self.local_backup_agents[agent_id] - if not await local_agent.async_get_backup(slug=slug): - raise HomeAssistantError(f"Backup {slug} not found in agent {agent_id}") - path = local_agent.get_backup_path(slug=slug) + if not await local_agent.async_get_backup(backup_id): + raise HomeAssistantError( + f"Backup {backup_id} not found in agent {agent_id}" + ) + path = local_agent.get_backup_path(backup_id) else: - path = self.temp_backup_dir / f"{slug}.tar" + path = self.temp_backup_dir / f"{backup_id}.tar" agent = self.backup_agents[agent_id] - if not (backup := await agent.async_get_backup(slug=slug)): - raise HomeAssistantError(f"Backup {slug} not found in agent {agent_id}") - await agent.async_download_backup(id=backup.id, path=path) + if not await agent.async_get_backup(backup_id): + raise HomeAssistantError( + 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: """Write the restore file.""" @@ -637,6 +641,6 @@ class BackupManager(BaseBackupManager[Backup]): await self.hass.services.async_call("homeassistant", "restart", {}) -def _generate_slug(date: str, name: str) -> str: - """Generate a backup slug.""" +def _generate_backup_id(date: str, name: str) -> str: + """Generate a backup ID.""" return hashlib.sha1(f"{date} - {name}".lower().encode()).hexdigest()[:8] diff --git a/homeassistant/components/backup/models.py b/homeassistant/components/backup/models.py index 98fd15c9475..f9a913affde 100644 --- a/homeassistant/components/backup/models.py +++ b/homeassistant/components/backup/models.py @@ -7,10 +7,10 @@ from dataclasses import asdict, dataclass class BaseBackup: """Base backup class.""" + backup_id: str date: str name: str protected: bool - slug: str size: float def as_dict(self) -> dict: @@ -22,9 +22,9 @@ class BaseBackup: class BackupUploadMetadata: """Backup upload metadata.""" + backup_id: str # The ID of the backup 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 + name: str # The name of the backup protected: bool # If the backup is protected + size: float # The size of the backup (in bytes) diff --git a/homeassistant/components/backup/util.py b/homeassistant/components/backup/util.py index b92ef3f921f..c507f69ec6e 100644 --- a/homeassistant/components/backup/util.py +++ b/homeassistant/components/backup/util.py @@ -20,9 +20,9 @@ def read_backup(backup_path: Path) -> BaseBackup: raise KeyError("backup.json not found in tar file") data = json_loads_object(data_file.read()) return BaseBackup( - slug=cast(str, data["slug"]), - name=cast(str, data["name"]), + backup_id=cast(str, data["slug"]), 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)), + size=round(backup_path.stat().st_size / 1_048_576, 2), ) diff --git a/homeassistant/components/backup/websocket.py b/homeassistant/components/backup/websocket.py index 8f4654f7ce9..18f3aadad71 100644 --- a/homeassistant/components/backup/websocket.py +++ b/homeassistant/components/backup/websocket.py @@ -60,7 +60,7 @@ async def handle_info( @websocket_api.websocket_command( { vol.Required("type"): "backup/details", - vol.Required("slug"): str, + vol.Required("backup_id"): str, } ) @websocket_api.async_response @@ -69,9 +69,9 @@ async def handle_details( connection: websocket_api.ActiveConnection, msg: dict[str, Any], ) -> 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( - slug=msg["slug"] + msg["backup_id"] ) connection.send_result( msg["id"], @@ -88,7 +88,7 @@ async def handle_details( @websocket_api.websocket_command( { vol.Required("type"): "backup/remove", - vol.Required("slug"): str, + vol.Required("backup_id"): str, } ) @websocket_api.async_response @@ -98,7 +98,7 @@ async def handle_remove( msg: dict[str, Any], ) -> None: """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"]) @@ -106,7 +106,7 @@ async def handle_remove( @websocket_api.websocket_command( { vol.Required("type"): "backup/restore", - vol.Required("slug"): str, + vol.Required("backup_id"): str, vol.Required("agent_id"): str, vol.Optional("password"): str, } @@ -119,7 +119,7 @@ async def handle_restore( ) -> None: """Restore a backup.""" await hass.data[DATA_MANAGER].async_restore_backup( - slug=msg["slug"], + msg["backup_id"], agent_id=msg["agent_id"], password=msg.get("password"), ) @@ -245,7 +245,6 @@ async def backup_agents_list_backups( vol.Required("type"): "backup/agents/download", vol.Required("agent_id"): str, vol.Required("backup_id"): str, - vol.Required("slug"): str, } ) @websocket_api.async_response @@ -262,9 +261,9 @@ async def backup_agents_download( ) return 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( - id=msg["backup_id"], + msg["backup_id"], path=path, ) except Exception as err: # noqa: BLE001 diff --git a/homeassistant/components/kitchen_sink/backup.py b/homeassistant/components/kitchen_sink/backup.py index 3f93b5eb8f1..3755d351140 100644 --- a/homeassistant/components/kitchen_sink/backup.py +++ b/homeassistant/components/kitchen_sink/backup.py @@ -5,12 +5,11 @@ from __future__ import annotations import logging from pathlib import Path from typing import Any -from uuid import uuid4 from homeassistant.components.backup import ( BackupAgent, BackupUploadMetadata, - UploadedBackup, + BaseBackup, ) from homeassistant.core import HomeAssistant @@ -32,25 +31,23 @@ class KitchenSinkBackupAgent(BackupAgent): super().__init__() self.name = name self._uploads = [ - UploadedBackup( - id="def456", + BaseBackup( + backup_id="abc123", + date="1970-01-01T00:00:00Z", name="Kitchen sink syncer", protected=False, - slug="abc123", size=1234, - date="1970-01-01T00:00:00Z", ) ] async def async_download_backup( self, - *, - id: str, + backup_id: str, path: Path, **kwargs: Any, ) -> None: """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( self, @@ -62,28 +59,26 @@ class KitchenSinkBackupAgent(BackupAgent): """Upload a backup.""" LOGGER.info("Uploading backup %s %s", path.name, metadata) self._uploads.append( - UploadedBackup( - id=uuid4().hex, + BaseBackup( + backup_id=metadata.backup_id, + date=metadata.date, name=metadata.name, protected=metadata.protected, - slug=metadata.slug, 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.""" return self._uploads async def async_get_backup( self, - *, - slug: str, + backup_id: str, **kwargs: Any, - ) -> UploadedBackup | None: + ) -> BaseBackup | None: """Return a backup.""" for backup in self._uploads: - if backup.slug == slug: + if backup.backup_id == backup_id: return backup return None diff --git a/tests/components/backup/common.py b/tests/components/backup/common.py index 0f12f552fa4..dcee4b1f39e 100644 --- a/tests/components/backup/common.py +++ b/tests/components/backup/common.py @@ -10,12 +10,11 @@ from homeassistant.components.backup import ( DOMAIN, BackupAgent, BackupUploadMetadata, - UploadedBackup, + BaseBackup, ) from homeassistant.components.backup.backup import LocalBackup from homeassistant.components.backup.const import DATA_MANAGER from homeassistant.components.backup.manager import Backup -from homeassistant.components.backup.models import BaseBackup from homeassistant.core import HomeAssistant from homeassistant.helpers.typing import ConfigType from homeassistant.setup import async_setup_component @@ -23,29 +22,28 @@ from homeassistant.setup import async_setup_component LOCAL_AGENT_ID = f"{DOMAIN}.local" TEST_BASE_BACKUP = BaseBackup( - slug="abc123", - name="Test", + backup_id="abc123", date="1970-01-01T00:00:00.000Z", - size=0.0, + name="Test", protected=False, + size=0.0, ) TEST_BACKUP = Backup( agent_ids=["backup.local"], - slug="abc123", - name="Test", + backup_id="abc123", date="1970-01-01T00:00:00.000Z", - size=0.0, + name="Test", protected=False, + size=0.0, ) TEST_BACKUP_PATH = Path("abc123.tar") TEST_LOCAL_BACKUP = LocalBackup( - id="abc123", - slug="abc123", - name="Test", date="1970-01-01T00:00:00.000Z", + backup_id="abc123", + name="Test", path=Path("abc123.tar"), - size=0.0, protected=False, + size=0.0, ) @@ -74,35 +72,33 @@ class BackupAgentTest(BackupAgent): ) -> None: """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.""" return [ - UploadedBackup( - id="abc123", + BaseBackup( + backup_id="abc123", date="1970-01-01T00:00:00Z", name="Test", protected=False, size=13.37, - slug="abc123", ) ] async def async_get_backup( self, *, - slug: str, + backup_id: str, **kwargs: Any, - ) -> UploadedBackup | None: + ) -> BaseBackup | None: """Return a backup.""" - if slug != "abc123": + if backup_id != "abc123": return None - return UploadedBackup( - id="abc123", + return BaseBackup( + backup_id="abc123", date="1970-01-01T00:00:00Z", name="Test", protected=False, size=13.37, - slug="abc123", ) @@ -119,7 +115,7 @@ async def setup_backup_integration( return result 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 return result diff --git a/tests/components/backup/snapshots/test_websocket.ambr b/tests/components/backup/snapshots/test_websocket.ambr index 3ca2c6fa2be..51a504bae7f 100644 --- a/tests/components/backup/snapshots/test_websocket.ambr +++ b/tests/components/backup/snapshots/test_websocket.ambr @@ -79,12 +79,11 @@ 'result': list([ dict({ 'agent_id': 'domain.test', + 'backup_id': 'abc123', 'date': '1970-01-01T00:00:00Z', - 'id': 'abc123', 'name': 'Test', 'protected': False, 'size': 13.37, - 'slug': 'abc123', }), ]), 'success': True, @@ -97,12 +96,11 @@ 'result': list([ dict({ 'agent_id': 'domain.test', + 'backup_id': 'abc123', 'date': '1970-01-01T00:00:00Z', - 'id': 'abc123', 'name': 'Test', 'protected': False, 'size': 13.37, - 'slug': 'abc123', }), ]), 'success': True, @@ -367,11 +365,11 @@ 'agent_ids': list([ 'backup.local', ]), + 'backup_id': 'abc123', 'date': '1970-01-01T00:00:00.000Z', 'name': 'Test', 'protected': False, 'size': 0.0, - 'slug': 'abc123', }), }), 'success': True, @@ -401,11 +399,11 @@ 'agent_ids': list([ 'backup.local', ]), + 'backup_id': 'abc123', 'date': '1970-01-01T00:00:00.000Z', 'name': 'Test', 'protected': False, 'size': 0.0, - 'slug': 'abc123', }), }), 'success': True, @@ -460,7 +458,7 @@ dict({ 'id': 1, 'result': dict({ - 'slug': '27f5c632', + 'backup_id': '27f5c632', }), 'success': True, 'type': 'result', @@ -481,7 +479,7 @@ dict({ 'id': 1, 'result': dict({ - 'slug': '27f5c632', + 'backup_id': '27f5c632', }), 'success': True, 'type': 'result', @@ -502,7 +500,7 @@ dict({ 'id': 1, 'result': dict({ - 'slug': '27f5c632', + 'backup_id': '27f5c632', }), 'success': True, 'type': 'result', @@ -523,7 +521,7 @@ dict({ 'id': 1, 'result': dict({ - 'slug': 'abc123', + 'backup_id': 'abc123', }), 'success': True, 'type': 'result', @@ -533,7 +531,7 @@ dict({ 'id': 1, 'result': dict({ - 'slug': 'abc123', + 'backup_id': 'abc123', }), 'success': True, 'type': 'result', @@ -562,11 +560,11 @@ 'agent_ids': list([ 'backup.local', ]), + 'backup_id': 'abc123', 'date': '1970-01-01T00:00:00.000Z', 'name': 'Test', 'protected': False, 'size': 0.0, - 'slug': 'abc123', }), ]), }), @@ -587,11 +585,11 @@ 'agent_ids': list([ 'backup.local', ]), + 'backup_id': 'abc123', 'date': '1970-01-01T00:00:00.000Z', 'name': 'Test', 'protected': False, 'size': 0.0, - 'slug': 'abc123', }), ]), }), diff --git a/tests/components/backup/test_manager.py b/tests/components/backup/test_manager.py index 4a355e8f67a..da95f81762c 100644 --- a/tests/components/backup/test_manager.py +++ b/tests/components/backup/test_manager.py @@ -133,9 +133,9 @@ async def test_load_backups(hass: HomeAssistant) -> None: patch( "homeassistant.components.backup.util.json_loads_object", return_value={ - "slug": TEST_LOCAL_BACKUP.slug, - "name": TEST_LOCAL_BACKUP.name, "date": TEST_LOCAL_BACKUP.date, + "name": TEST_LOCAL_BACKUP.name, + "slug": TEST_LOCAL_BACKUP.backup_id, }, ), patch( @@ -145,7 +145,7 @@ async def test_load_backups(hass: HomeAssistant) -> None: ): await manager.backup_agents[LOCAL_AGENT_ID].load_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 == {} @@ -181,11 +181,11 @@ async def test_removing_backup( await manager.load_platforms() 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 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 @@ -199,7 +199,7 @@ async def test_removing_non_existing_backup( await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform) 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 @@ -214,18 +214,18 @@ async def test_getting_backup_that_does_not_exist( await manager.load_platforms() 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 with patch("pathlib.Path.exists", return_value=False): backup, agent_errors = await manager.async_get_backup( - slug=TEST_LOCAL_BACKUP.slug + TEST_LOCAL_BACKUP.backup_id ) assert backup is None assert agent_errors == {} 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}" ) in caplog.text @@ -278,7 +278,7 @@ async def test_async_create_backup( 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 "Loaded 0 platforms" in caplog.text assert "Loaded 1 agents" in caplog.text @@ -457,7 +457,7 @@ async def test_async_trigger_restore( await manager.load_platforms() 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 with ( @@ -466,7 +466,7 @@ async def test_async_trigger_restore( patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, ): 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 ( mocked_write_text.call_args[0][0] @@ -486,7 +486,7 @@ async def test_async_trigger_restore_with_password( await manager.load_platforms() 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 with ( @@ -495,7 +495,7 @@ async def test_async_trigger_restore_with_password( patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, ): 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 ( 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"): 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 ) diff --git a/tests/components/backup/test_websocket.py b/tests/components/backup/test_websocket.py index 331ab8c3842..9509bf4a29f 100644 --- a/tests/components/backup/test_websocket.py +++ b/tests/components/backup/test_websocket.py @@ -108,7 +108,9 @@ async def test_details( await hass.async_block_till_done() 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 @@ -132,7 +134,9 @@ async def test_details_with_errors( 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"}) + await client.send_json_auto_id( + {"type": "backup/details", "backup_id": "abc123"} + ) assert await client.receive_json() == snapshot @@ -158,7 +162,7 @@ async def test_remove( with patch( "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 @@ -281,7 +285,11 @@ async def test_restore( "homeassistant.components.backup.manager.BackupManager.async_restore_backup", ): 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 @@ -481,15 +489,14 @@ async def test_agents_download( await client.send_json_auto_id( { "type": "backup/agents/download", - "slug": "abc123", "agent_id": "domain.test", "backup_id": "abc123", } ) with patch.object(BackupAgentTest, "async_download_backup") as download_mock: assert await client.receive_json() == snapshot + assert download_mock.call_args[0] == ("abc123",) assert download_mock.call_args[1] == { - "id": "abc123", "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( { "type": "backup/agents/download", - "slug": "abc123", "agent_id": "domain.test", "backup_id": "abc123", } @@ -533,7 +539,6 @@ async def test_agents_download_unknown_agent( await client.send_json_auto_id( { "type": "backup/agents/download", - "slug": "abc123", "agent_id": "domain.test", "backup_id": "abc123", } diff --git a/tests/components/kitchen_sink/test_backup.py b/tests/components/kitchen_sink/test_backup.py index fef5600ec42..603a9eec1c1 100644 --- a/tests/components/kitchen_sink/test_backup.py +++ b/tests/components/kitchen_sink/test_backup.py @@ -3,7 +3,6 @@ from collections.abc import AsyncGenerator from io import StringIO from unittest.mock import patch -from uuid import UUID import pytest @@ -69,12 +68,11 @@ async def test_agents_list_backups( assert response["result"] == [ { "agent_id": "kitchen_sink.syncer", + "backup_id": "abc123", "date": "1970-01-01T00:00:00Z", - "id": "def456", - "slug": "abc123", - "size": 1234, "name": "Kitchen sink syncer", "protected": False, + "size": 1234, } ] @@ -86,13 +84,11 @@ async def test_agents_download( ) -> None: """Test backup agents download.""" client = await hass_ws_client(hass) - backup_id = "def456" - slug = "abc123" + backup_id = "abc123" await client.send_json_auto_id( { "type": "backup/agents/download", - "slug": slug, "agent_id": "kitchen_sink.syncer", "backup_id": backup_id, } @@ -100,7 +96,7 @@ async def test_agents_download( response = await client.receive_json() 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 @@ -114,18 +110,16 @@ async def test_agents_upload( """Test backup agents upload.""" ws_client = await hass_ws_client(hass, hass_supervisor_access_token) client = await hass_client() - slug = "test-backup" + backup_id = "test-backup" test_backup = BaseBackup( - slug=slug, - name="Test", + backup_id=backup_id, date="1970-01-01T00:00:00.000Z", - size=0.0, + name="Test", protected=False, + size=0.0, ) - uuid = UUID(int=123456) with ( - patch("homeassistant.components.kitchen_sink.backup.uuid4", return_value=uuid), patch( "homeassistant.components.backup.manager.BackupManager.async_get_backup", ) as fetch_backup, @@ -141,22 +135,20 @@ async def test_agents_upload( ) assert resp.status == 201 - backup_name = f"{slug}.tar" + backup_name = f"{backup_id}.tar" 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"}) - response = await ws_client.receive_json() + await ws_client.send_json_auto_id({"type": "backup/agents/list_backups"}) + response = await ws_client.receive_json() assert response["success"] backup_list = response["result"] assert len(backup_list) == 2 assert backup_list[1] == { "agent_id": "kitchen_sink.syncer", + "backup_id": backup_id, "date": test_backup.date, - "id": uuid.hex, - "slug": slug, - "size": 0.0, "name": test_backup.name, "protected": test_backup.protected, + "size": 0.0, }