From a1fae0e0ce785ffd3c4ee992b937950f50e6a34a Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Tue, 9 Jul 2024 11:55:12 +0200 Subject: [PATCH] Fix missing validation and service definition in Mealie (#121578) --- homeassistant/components/mealie/icons.json | 3 +- homeassistant/components/mealie/services.py | 17 ++++++-- homeassistant/components/mealie/services.yaml | 11 +++++ homeassistant/components/mealie/strings.json | 3 ++ tests/components/mealie/test_services.py | 40 ++++++++++++++++--- 5 files changed, 65 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/mealie/icons.json b/homeassistant/components/mealie/icons.json index da1688e218c..9d683f1ce81 100644 --- a/homeassistant/components/mealie/icons.json +++ b/homeassistant/components/mealie/icons.json @@ -1,5 +1,6 @@ { "services": { - "get_mealplan": "mdi:food" + "get_mealplan": "mdi:food", + "get_recipe": "mdi:map" } } diff --git a/homeassistant/components/mealie/services.py b/homeassistant/components/mealie/services.py index 9b999690067..61323018b17 100644 --- a/homeassistant/components/mealie/services.py +++ b/homeassistant/components/mealie/services.py @@ -4,7 +4,7 @@ from dataclasses import asdict from datetime import date from typing import cast -from aiomealie.exceptions import MealieNotFoundError +from aiomealie.exceptions import MealieConnectionError, MealieNotFoundError import voluptuous as vol from homeassistant.config_entries import ConfigEntryState @@ -14,7 +14,7 @@ from homeassistant.core import ( ServiceResponse, SupportsResponse, ) -from homeassistant.exceptions import ServiceValidationError +from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from .const import ( ATTR_CONFIG_ENTRY_ID, @@ -74,7 +74,13 @@ def setup_services(hass: HomeAssistant) -> None: translation_key="end_date_before_start_date", ) client = cast(MealieConfigEntry, entry).runtime_data.client - mealplans = await client.get_mealplans(start_date, end_date) + try: + mealplans = await client.get_mealplans(start_date, end_date) + except MealieConnectionError as err: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="connection_error", + ) from err return {"mealplan": [asdict(x) for x in mealplans.items]} async def async_get_recipe(call: ServiceCall) -> ServiceResponse: @@ -84,6 +90,11 @@ def setup_services(hass: HomeAssistant) -> None: client = entry.runtime_data.client try: recipe = await client.get_recipe(recipe_id) + except MealieConnectionError as err: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="connection_error", + ) from err except MealieNotFoundError as err: raise ServiceValidationError( translation_domain=DOMAIN, diff --git a/homeassistant/components/mealie/services.yaml b/homeassistant/components/mealie/services.yaml index daa162b3cd9..a74935c1b31 100644 --- a/homeassistant/components/mealie/services.yaml +++ b/homeassistant/components/mealie/services.yaml @@ -11,3 +11,14 @@ get_mealplan: end_date: selector: date: +get_recipe: + fields: + config_entry_id: + required: true + selector: + config_entry: + integration: mealie + recipe_id: + required: true + selector: + text: diff --git a/homeassistant/components/mealie/strings.json b/homeassistant/components/mealie/strings.json index 4759d12b95c..4b7cdc6d780 100644 --- a/homeassistant/components/mealie/strings.json +++ b/homeassistant/components/mealie/strings.json @@ -46,6 +46,9 @@ "end_date_before_start_date": { "message": "End date must be after start date." }, + "connection_error": { + "message": "Error connecting to Mealie instance." + }, "recipe_not_found": { "message": "Recipe with ID or slug `{recipe_id}` not found." } diff --git a/tests/components/mealie/test_services.py b/tests/components/mealie/test_services.py index fbc56eff9d7..401e915f4b0 100644 --- a/tests/components/mealie/test_services.py +++ b/tests/components/mealie/test_services.py @@ -3,7 +3,7 @@ from datetime import date from unittest.mock import AsyncMock -from aiomealie.exceptions import MealieNotFoundError +from aiomealie.exceptions import MealieConnectionError, MealieNotFoundError from freezegun.api import FrozenDateTimeFactory import pytest from syrupy import SnapshotAssertion @@ -20,7 +20,7 @@ from homeassistant.components.mealie.services import ( SERVICE_GET_RECIPE, ) from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ServiceValidationError +from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from . import setup_integration @@ -136,18 +136,27 @@ async def test_service_recipe( assert response == snapshot -async def test_service_recipe_not_found( +@pytest.mark.parametrize( + ("exception", "raised_exception"), + [ + (MealieNotFoundError, ServiceValidationError), + (MealieConnectionError, HomeAssistantError), + ], +) +async def test_service_recipe_exceptions( hass: HomeAssistant, mock_mealie_client: AsyncMock, mock_config_entry: MockConfigEntry, + exception: Exception, + raised_exception: type[Exception], ) -> None: """Test the get_recipe service.""" await setup_integration(hass, mock_config_entry) - mock_mealie_client.get_recipe.side_effect = MealieNotFoundError + mock_mealie_client.get_recipe.side_effect = exception - with pytest.raises(ServiceValidationError): + with pytest.raises(raised_exception): await hass.services.async_call( DOMAIN, SERVICE_GET_RECIPE, @@ -160,6 +169,27 @@ async def test_service_recipe_not_found( ) +async def test_service_mealplan_connection_error( + hass: HomeAssistant, + mock_mealie_client: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test a connection error in the get_mealplans service.""" + + await setup_integration(hass, mock_config_entry) + + mock_mealie_client.get_mealplans.side_effect = MealieConnectionError + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + DOMAIN, + SERVICE_GET_MEALPLAN, + {ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id}, + blocking=True, + return_response=True, + ) + + async def test_service_mealplan_without_entry( hass: HomeAssistant, mock_config_entry: MockConfigEntry,