mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 01:11:51 -04:00
Return an error response when a REST API service call fails (#181590)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
e85b8a256e
commit
384c153186
@@ -39,9 +39,11 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import Event, EventStateChangedData, HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
InvalidEntityFormatError,
|
||||
InvalidStateError,
|
||||
ServiceNotFound,
|
||||
ServiceValidationError,
|
||||
TemplateError,
|
||||
Unauthorized,
|
||||
)
|
||||
@@ -455,6 +457,14 @@ class APIDomainServicesView(HomeAssistantView):
|
||||
)
|
||||
except (vol.Invalid, ServiceNotFound) as ex:
|
||||
raise HTTPBadRequest from ex
|
||||
except ServiceValidationError as ex:
|
||||
return self.json_message(str(ex), HTTPStatus.BAD_REQUEST)
|
||||
except Unauthorized:
|
||||
# Handled by the view wrapper, which maps it to 401
|
||||
raise
|
||||
except HomeAssistantError as ex:
|
||||
_LOGGER.error("Error during service call to %s.%s: %s", domain, service, ex)
|
||||
return self.json_message(str(ex), HTTPStatus.INTERNAL_SERVER_ERROR)
|
||||
finally:
|
||||
cancel_listen()
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@ from homeassistant.components.group import DOMAIN as GROUP_DOMAIN
|
||||
from homeassistant.components.logger import DOMAIN as LOGGER_DOMAIN
|
||||
from homeassistant.components.system_health import DOMAIN as SYSTEM_HEALTH_DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
ServiceValidationError,
|
||||
Unauthorized,
|
||||
)
|
||||
from homeassistant.loader import Integration
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.yaml.loader import JSON_TYPE
|
||||
@@ -943,6 +948,55 @@ async def test_api_call_service_not_found(
|
||||
assert resp.status == HTTPStatus.BAD_REQUEST
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("error", "status"),
|
||||
[
|
||||
pytest.param(
|
||||
ServiceValidationError("Bad input"),
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
id="service_validation_error",
|
||||
),
|
||||
pytest.param(
|
||||
HomeAssistantError("Something failed"),
|
||||
HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
id="home_assistant_error",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_api_call_service_raises(
|
||||
hass: HomeAssistant,
|
||||
mock_api_client: TestClient,
|
||||
error: HomeAssistantError,
|
||||
status: HTTPStatus,
|
||||
) -> None:
|
||||
"""Test the API returns a JSON error if the service raises."""
|
||||
|
||||
async def handler(service_call: ha.ServiceCall) -> None:
|
||||
"""Raise the configured error."""
|
||||
raise error
|
||||
|
||||
hass.services.async_register("test_domain", "test_service", handler)
|
||||
|
||||
resp = await mock_api_client.post("/api/services/test_domain/test_service")
|
||||
assert resp.status == status
|
||||
assert await resp.json() == {"message": str(error)}
|
||||
|
||||
|
||||
async def test_api_call_service_unauthorized(
|
||||
hass: HomeAssistant, mock_api_client: TestClient
|
||||
) -> None:
|
||||
"""Test the API returns 401 if the service denies permission."""
|
||||
|
||||
async def handler(service_call: ha.ServiceCall) -> None:
|
||||
"""Deny the call."""
|
||||
raise Unauthorized
|
||||
|
||||
hass.services.async_register("test_domain", "test_service", handler)
|
||||
|
||||
resp = await mock_api_client.post("/api/services/test_domain/test_service")
|
||||
assert resp.status == HTTPStatus.UNAUTHORIZED
|
||||
|
||||
|
||||
async def test_api_call_service_bad_data(
|
||||
hass: HomeAssistant, mock_api_client: TestClient
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user