mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Catch PermissionDenied(Route API disabled) in google_travel_time (#145722)
Catch PermissionDenied(Route API disabled)
This commit is contained in:
parent
376008940b
commit
481639bcf9
@ -50,7 +50,12 @@ from .const import (
|
|||||||
UNITS_IMPERIAL,
|
UNITS_IMPERIAL,
|
||||||
UNITS_METRIC,
|
UNITS_METRIC,
|
||||||
)
|
)
|
||||||
from .helpers import InvalidApiKeyException, UnknownException, validate_config_entry
|
from .helpers import (
|
||||||
|
InvalidApiKeyException,
|
||||||
|
PermissionDeniedException,
|
||||||
|
UnknownException,
|
||||||
|
validate_config_entry,
|
||||||
|
)
|
||||||
|
|
||||||
RECONFIGURE_SCHEMA = vol.Schema(
|
RECONFIGURE_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
@ -188,6 +193,8 @@ async def validate_input(
|
|||||||
user_input[CONF_ORIGIN],
|
user_input[CONF_ORIGIN],
|
||||||
user_input[CONF_DESTINATION],
|
user_input[CONF_DESTINATION],
|
||||||
)
|
)
|
||||||
|
except PermissionDeniedException:
|
||||||
|
return {"base": "permission_denied"}
|
||||||
except InvalidApiKeyException:
|
except InvalidApiKeyException:
|
||||||
return {"base": "invalid_auth"}
|
return {"base": "invalid_auth"}
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
|
@ -7,6 +7,7 @@ from google.api_core.exceptions import (
|
|||||||
Forbidden,
|
Forbidden,
|
||||||
GatewayTimeout,
|
GatewayTimeout,
|
||||||
GoogleAPIError,
|
GoogleAPIError,
|
||||||
|
PermissionDenied,
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
)
|
)
|
||||||
from google.maps.routing_v2 import (
|
from google.maps.routing_v2 import (
|
||||||
@ -19,10 +20,18 @@ from google.maps.routing_v2 import (
|
|||||||
from google.type import latlng_pb2
|
from google.type import latlng_pb2
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.helpers.issue_registry import (
|
||||||
|
IssueSeverity,
|
||||||
|
async_create_issue,
|
||||||
|
async_delete_issue,
|
||||||
|
)
|
||||||
from homeassistant.helpers.location import find_coordinates
|
from homeassistant.helpers.location import find_coordinates
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -67,6 +76,9 @@ async def validate_config_entry(
|
|||||||
await client.compute_routes(
|
await client.compute_routes(
|
||||||
request, metadata=[("x-goog-fieldmask", field_mask)]
|
request, metadata=[("x-goog-fieldmask", field_mask)]
|
||||||
)
|
)
|
||||||
|
except PermissionDenied as permission_error:
|
||||||
|
_LOGGER.error("Permission denied: %s", permission_error.message)
|
||||||
|
raise PermissionDeniedException from permission_error
|
||||||
except (Unauthorized, Forbidden) as unauthorized_error:
|
except (Unauthorized, Forbidden) as unauthorized_error:
|
||||||
_LOGGER.error("Request denied: %s", unauthorized_error.message)
|
_LOGGER.error("Request denied: %s", unauthorized_error.message)
|
||||||
raise InvalidApiKeyException from unauthorized_error
|
raise InvalidApiKeyException from unauthorized_error
|
||||||
@ -84,3 +96,30 @@ class InvalidApiKeyException(Exception):
|
|||||||
|
|
||||||
class UnknownException(Exception):
|
class UnknownException(Exception):
|
||||||
"""Unknown API Error."""
|
"""Unknown API Error."""
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionDeniedException(Exception):
|
||||||
|
"""Permission Denied Error."""
|
||||||
|
|
||||||
|
|
||||||
|
def create_routes_api_disabled_issue(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
|
"""Create an issue for the Routes API being disabled."""
|
||||||
|
async_create_issue(
|
||||||
|
hass,
|
||||||
|
DOMAIN,
|
||||||
|
f"routes_api_disabled_{entry.entry_id}",
|
||||||
|
learn_more_url="https://www.home-assistant.io/integrations/google_travel_time#setup",
|
||||||
|
is_fixable=False,
|
||||||
|
severity=IssueSeverity.ERROR,
|
||||||
|
translation_key="routes_api_disabled",
|
||||||
|
translation_placeholders={
|
||||||
|
"entry_title": entry.title,
|
||||||
|
"enable_api_url": "https://cloud.google.com/endpoints/docs/openapi/enable-api",
|
||||||
|
"api_key_restrictions_url": "https://cloud.google.com/docs/authentication/api-keys#adding-api-restrictions",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_routes_api_disabled_issue(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
|
"""Delete the issue for the Routes API being disabled."""
|
||||||
|
async_delete_issue(hass, DOMAIN, f"routes_api_disabled_{entry.entry_id}")
|
||||||
|
@ -7,7 +7,7 @@ import logging
|
|||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from google.api_core.client_options import ClientOptions
|
from google.api_core.client_options import ClientOptions
|
||||||
from google.api_core.exceptions import GoogleAPIError
|
from google.api_core.exceptions import GoogleAPIError, PermissionDenied
|
||||||
from google.maps.routing_v2 import (
|
from google.maps.routing_v2 import (
|
||||||
ComputeRoutesRequest,
|
ComputeRoutesRequest,
|
||||||
Route,
|
Route,
|
||||||
@ -58,7 +58,11 @@ from .const import (
|
|||||||
TRAVEL_MODES_TO_GOOGLE_SDK_ENUM,
|
TRAVEL_MODES_TO_GOOGLE_SDK_ENUM,
|
||||||
UNITS_TO_GOOGLE_SDK_ENUM,
|
UNITS_TO_GOOGLE_SDK_ENUM,
|
||||||
)
|
)
|
||||||
from .helpers import convert_to_waypoint
|
from .helpers import (
|
||||||
|
convert_to_waypoint,
|
||||||
|
create_routes_api_disabled_issue,
|
||||||
|
delete_routes_api_disabled_issue,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -274,6 +278,11 @@ class GoogleTravelTimeSensor(SensorEntity):
|
|||||||
_LOGGER.debug("Received response: %s", response)
|
_LOGGER.debug("Received response: %s", response)
|
||||||
if response is not None and len(response.routes) > 0:
|
if response is not None and len(response.routes) > 0:
|
||||||
self._route = response.routes[0]
|
self._route = response.routes[0]
|
||||||
|
delete_routes_api_disabled_issue(self.hass, self._config_entry)
|
||||||
|
except PermissionDenied:
|
||||||
|
_LOGGER.error("Routes API is disabled for this API key")
|
||||||
|
create_routes_api_disabled_issue(self.hass, self._config_entry)
|
||||||
|
self._route = None
|
||||||
except GoogleAPIError as ex:
|
except GoogleAPIError as ex:
|
||||||
_LOGGER.error("Error getting travel time: %s", ex)
|
_LOGGER.error("Error getting travel time: %s", ex)
|
||||||
self._route = None
|
self._route = None
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
"permission_denied": "The Routes API is not enabled for this API key. Please see the setup instructions for detailed information.",
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]"
|
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]"
|
||||||
@ -100,5 +101,11 @@
|
|||||||
"fewer_transfers": "Fewer transfers"
|
"fewer_transfers": "Fewer transfers"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"routes_api_disabled": {
|
||||||
|
"title": "The Routes API must be enabled",
|
||||||
|
"description": "Your Google Travel Time integration `{entry_title}` uses an API key which does not have the Routes API enabled.\n\n Please follow the instructions to [enable the API for your project]({enable_api_url}) and make sure your [API key restrictions]({api_key_restrictions_url}) allow access to the Routes API.\n\n After enabling the API this issue will be resolved automatically."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
from google.api_core.exceptions import GatewayTimeout, GoogleAPIError, Unauthorized
|
from google.api_core.exceptions import (
|
||||||
|
GatewayTimeout,
|
||||||
|
GoogleAPIError,
|
||||||
|
PermissionDenied,
|
||||||
|
Unauthorized,
|
||||||
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.google_travel_time.const import (
|
from homeassistant.components.google_travel_time.const import (
|
||||||
@ -98,6 +103,12 @@ async def test_minimum_fields(hass: HomeAssistant) -> None:
|
|||||||
(GoogleAPIError("test"), "cannot_connect"),
|
(GoogleAPIError("test"), "cannot_connect"),
|
||||||
(GatewayTimeout("Timeout error."), "timeout_connect"),
|
(GatewayTimeout("Timeout error."), "timeout_connect"),
|
||||||
(Unauthorized("Invalid API key."), "invalid_auth"),
|
(Unauthorized("Invalid API key."), "invalid_auth"),
|
||||||
|
(
|
||||||
|
PermissionDenied(
|
||||||
|
"Requests to this API routes.googleapis.com method google.maps.routing.v2.Routes.ComputeRoutes are blocked."
|
||||||
|
),
|
||||||
|
"permission_denied",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_errors(
|
async def test_errors(
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
from google.api_core.exceptions import GoogleAPIError
|
from google.api_core.exceptions import GoogleAPIError, PermissionDenied
|
||||||
from google.maps.routing_v2 import Units
|
from google.maps.routing_v2 import Units
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -20,6 +20,7 @@ from homeassistant.components.google_travel_time.const import (
|
|||||||
from homeassistant.components.google_travel_time.sensor import SCAN_INTERVAL
|
from homeassistant.components.google_travel_time.sensor import SCAN_INTERVAL
|
||||||
from homeassistant.const import CONF_MODE, STATE_UNKNOWN
|
from homeassistant.const import CONF_MODE, STATE_UNKNOWN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import issue_registry as ir
|
||||||
from homeassistant.util.unit_system import (
|
from homeassistant.util.unit_system import (
|
||||||
METRIC_SYSTEM,
|
METRIC_SYSTEM,
|
||||||
US_CUSTOMARY_SYSTEM,
|
US_CUSTOMARY_SYSTEM,
|
||||||
@ -170,3 +171,26 @@ async def test_sensor_exception(
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass.states.get("sensor.google_travel_time").state == STATE_UNKNOWN
|
assert hass.states.get("sensor.google_travel_time").state == STATE_UNKNOWN
|
||||||
assert "Error getting travel time" in caplog.text
|
assert "Error getting travel time" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("data", "options"),
|
||||||
|
[(MOCK_CONFIG, DEFAULT_OPTIONS)],
|
||||||
|
)
|
||||||
|
async def test_sensor_routes_api_disabled(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
routes_mock: AsyncMock,
|
||||||
|
mock_config: MockConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
issue_registry: ir.IssueRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test that exception gets caught and issue created."""
|
||||||
|
routes_mock.compute_routes.side_effect = PermissionDenied("Errormessage")
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert hass.states.get("sensor.google_travel_time").state == STATE_UNKNOWN
|
||||||
|
assert "Routes API is disabled for this API key" in caplog.text
|
||||||
|
|
||||||
|
assert len(issue_registry.issues) == 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user