From ba0804fefa60968c3e7a091aa37f7703eeecc85b Mon Sep 17 00:00:00 2001 From: tronikos Date: Wed, 18 Mar 2026 10:51:07 -0700 Subject: [PATCH] Add exception translations to Google Drive (#165932) --- .../components/google_drive/__init__.py | 19 ++++++++++++---- homeassistant/components/google_drive/api.py | 15 ++++++++++--- .../components/google_drive/strings.json | 17 ++++++++++++++ tests/components/google_drive/test_init.py | 22 ++++++++++++++++++- 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/google_drive/__init__.py b/homeassistant/components/google_drive/__init__.py index d3fe021a5a2..a566f57f7e0 100644 --- a/homeassistant/components/google_drive/__init__.py +++ b/homeassistant/components/google_drive/__init__.py @@ -12,6 +12,7 @@ from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import instance_id from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, OAuth2Session, async_get_config_entry_implementation, ) @@ -30,11 +31,17 @@ _PLATFORMS = (Platform.SENSOR,) async def async_setup_entry(hass: HomeAssistant, entry: GoogleDriveConfigEntry) -> bool: """Set up Google Drive from a config entry.""" + try: + implementation = await async_get_config_entry_implementation(hass, entry) + except ImplementationUnavailableError as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="oauth2_implementation_unavailable", + ) from err + auth = AsyncConfigEntryAuth( async_get_clientsession(hass), - OAuth2Session( - hass, entry, await async_get_config_entry_implementation(hass, entry) - ), + OAuth2Session(hass, entry, implementation), ) # Test we can refresh the token and raise ConfigEntryAuthFailed or ConfigEntryNotReady if not @@ -46,7 +53,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: GoogleDriveConfigEntry) try: folder_id, _ = await client.async_create_ha_root_folder_if_not_exists() except GoogleDriveApiError as err: - raise ConfigEntryNotReady from err + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="failed_to_get_folder", + translation_placeholders={"folder": "Home Assistant"}, + ) from err def async_notify_backup_listeners() -> None: for listener in hass.data.get(DATA_BACKUP_AGENT_LISTENERS, []): diff --git a/homeassistant/components/google_drive/api.py b/homeassistant/components/google_drive/api.py index 035c19717b8..909b85bb713 100644 --- a/homeassistant/components/google_drive/api.py +++ b/homeassistant/components/google_drive/api.py @@ -22,6 +22,8 @@ from homeassistant.exceptions import ( ) from homeassistant.helpers import config_entry_oauth2_flow +from .const import DOMAIN + _UPLOAD_AND_DOWNLOAD_TIMEOUT = 12 * 3600 _UPLOAD_MAX_RETRIES = 20 @@ -61,14 +63,21 @@ class AsyncConfigEntryAuth(AbstractAuth): ): if isinstance(ex, ClientResponseError) and 400 <= ex.status < 500: raise ConfigEntryAuthFailed( - "OAuth session is not valid, reauth required" + translation_domain=DOMAIN, + translation_key="authentication_not_valid", ) from ex - raise ConfigEntryNotReady from ex + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="authentication_failed", + ) from ex if hasattr(ex, "status") and ex.status == 400: self._oauth_session.config_entry.async_start_reauth( self._oauth_session.hass ) - raise HomeAssistantError(ex) from ex + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="authentication_failed", + ) from ex return str(self._oauth_session.token[CONF_ACCESS_TOKEN]) diff --git a/homeassistant/components/google_drive/strings.json b/homeassistant/components/google_drive/strings.json index 8a64cddd68a..d11e0cf92d9 100644 --- a/homeassistant/components/google_drive/strings.json +++ b/homeassistant/components/google_drive/strings.json @@ -62,5 +62,22 @@ "name": "Used storage in Drive Trash" } } + }, + "exceptions": { + "authentication_failed": { + "message": "Authentication failed" + }, + "authentication_not_valid": { + "message": "OAuth session is not valid, reauth required" + }, + "failed_to_get_folder": { + "message": "Failed to get {folder} folder" + }, + "invalid_response_google_drive_error": { + "message": "Invalid response from Google Drive: {error}" + }, + "oauth2_implementation_unavailable": { + "message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]" + } } } diff --git a/tests/components/google_drive/test_init.py b/tests/components/google_drive/test_init.py index 2c97455016d..cc53e0de619 100644 --- a/tests/components/google_drive/test_init.py +++ b/tests/components/google_drive/test_init.py @@ -4,7 +4,7 @@ from collections.abc import Awaitable, Callable, Coroutine import http import time from typing import Any -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import AsyncMock, MagicMock, patch from google_drive_api.exceptions import GoogleDriveApiError import pytest @@ -12,6 +12,9 @@ import pytest from homeassistant.components.google_drive.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, +) from tests.common import MockConfigEntry from tests.test_util.aiohttp import AiohttpClientMocker @@ -154,3 +157,20 @@ async def test_expired_token_refresh_failure( # Verify a transient failure has occurred entries = hass.config_entries.async_entries(DOMAIN) assert entries[0].state is expected_state + + +async def test_oauth_implementation_not_available( + hass: HomeAssistant, + config_entry: MockConfigEntry, +) -> None: + """Test that unavailable OAuth implementation raises ConfigEntryNotReady.""" + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.google_drive.async_get_config_entry_implementation", + side_effect=ImplementationUnavailableError, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.SETUP_RETRY