Mealie min version check (#121677)

This commit is contained in:
Andrew Jackson 2024-07-10 20:25:49 +01:00 committed by GitHub
parent 71e5ffb2bd
commit a9c9963f0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 126 additions and 11 deletions

View File

@ -12,7 +12,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN from .const import DOMAIN, MIN_REQUIRED_MEALIE_VERSION
from .coordinator import ( from .coordinator import (
MealieConfigEntry, MealieConfigEntry,
MealieData, MealieData,
@ -20,6 +20,7 @@ from .coordinator import (
MealieShoppingListCoordinator, MealieShoppingListCoordinator,
) )
from .services import setup_services from .services import setup_services
from .utils import create_version
PLATFORMS: list[Platform] = [Platform.CALENDAR, Platform.TODO] PLATFORMS: list[Platform] = [Platform.CALENDAR, Platform.TODO]
@ -41,11 +42,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: MealieConfigEntry) -> bo
) )
try: try:
about = await client.get_about() about = await client.get_about()
version = create_version(about.version)
except MealieAuthenticationError as error: except MealieAuthenticationError as error:
raise ConfigEntryError("Authentication failed") from error raise ConfigEntryError("Authentication failed") from error
except MealieConnectionError as error: except MealieConnectionError as error:
raise ConfigEntryNotReady(error) from error raise ConfigEntryNotReady(error) from error
if not version.valid or version < MIN_REQUIRED_MEALIE_VERSION:
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="version_error",
translation_placeholders={
"mealie_version": about.version,
"min_version": MIN_REQUIRED_MEALIE_VERSION,
},
)
assert entry.unique_id assert entry.unique_id
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
device_registry.async_get_or_create( device_registry.async_get_or_create(

View File

@ -9,7 +9,8 @@ from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_TOKEN, CONF_HOST from homeassistant.const import CONF_API_TOKEN, CONF_HOST
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN, LOGGER from .const import DOMAIN, LOGGER, MIN_REQUIRED_MEALIE_VERSION
from .utils import create_version
SCHEMA = vol.Schema( SCHEMA = vol.Schema(
{ {
@ -35,6 +36,8 @@ class MealieConfigFlow(ConfigFlow, domain=DOMAIN):
) )
try: try:
info = await client.get_user_info() info = await client.get_user_info()
about = await client.get_about()
version = create_version(about.version)
except MealieConnectionError: except MealieConnectionError:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except MealieAuthenticationError: except MealieAuthenticationError:
@ -43,12 +46,15 @@ class MealieConfigFlow(ConfigFlow, domain=DOMAIN):
LOGGER.exception("Unexpected error") LOGGER.exception("Unexpected error")
errors["base"] = "unknown" errors["base"] = "unknown"
else: else:
await self.async_set_unique_id(info.user_id) if not version.valid or version < MIN_REQUIRED_MEALIE_VERSION:
self._abort_if_unique_id_configured() errors["base"] = "mealie_version"
return self.async_create_entry( else:
title="Mealie", await self.async_set_unique_id(info.user_id)
data=user_input, self._abort_if_unique_id_configured()
) return self.async_create_entry(
title="Mealie",
data=user_input,
)
return self.async_show_form( return self.async_show_form(
step_id="user", step_id="user",
data_schema=SCHEMA, data_schema=SCHEMA,

View File

@ -2,6 +2,8 @@
import logging import logging
from awesomeversion import AwesomeVersion
DOMAIN = "mealie" DOMAIN = "mealie"
LOGGER = logging.getLogger(__package__) LOGGER = logging.getLogger(__package__)
@ -12,3 +14,5 @@ ATTR_END_DATE = "end_date"
ATTR_RECIPE_ID = "recipe_id" ATTR_RECIPE_ID = "recipe_id"
ATTR_URL = "url" ATTR_URL = "url"
ATTR_INCLUDE_TAGS = "include_tags" ATTR_INCLUDE_TAGS = "include_tags"
MIN_REQUIRED_MEALIE_VERSION = AwesomeVersion("v1.0.0")

View File

@ -14,7 +14,8 @@
"error": { "error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]" "unknown": "[%key:common::config_flow::error::unknown%]",
"mealie_version": "Minimum required version is v1.0.0. Please upgrade Mealie and then retry."
}, },
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]" "already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
@ -66,6 +67,9 @@
}, },
"item_not_found_error": { "item_not_found_error": {
"message": "Item {shopping_list_item} not found." "message": "Item {shopping_list_item} not found."
},
"version_error": {
"message": "You are running {mealie_version} of Mealie. Minimum required version is {min_version}. Please upgrade Mealie and then retry."
} }
}, },
"services": { "services": {

View File

@ -0,0 +1,10 @@
"""Mealie util functions."""
from __future__ import annotations
from awesomeversion import AwesomeVersion
def create_version(version: str) -> AwesomeVersion:
"""Convert beta versions to PEP440."""
return AwesomeVersion(version.replace("beta-", "b"))

View File

@ -2,7 +2,7 @@
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
from aiomealie import MealieAuthenticationError, MealieConnectionError from aiomealie import About, MealieAuthenticationError, MealieConnectionError
import pytest import pytest
from homeassistant.components.mealie.const import DOMAIN from homeassistant.components.mealie.const import DOMAIN
@ -83,6 +83,40 @@ async def test_flow_errors(
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
@pytest.mark.parametrize(
("version"),
[
("v1.0.0beta-5"),
("v1.0.0-RC2"),
("v0.1.0"),
("something"),
],
)
async def test_flow_version_error(
hass: HomeAssistant,
mock_mealie_client: AsyncMock,
mock_setup_entry: AsyncMock,
version,
) -> None:
"""Test flow version error."""
mock_mealie_client.get_about.return_value = About(version=version)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "demo.mealie.io", CONF_API_TOKEN: "token"},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "mealie_version"}
async def test_duplicate( async def test_duplicate(
hass: HomeAssistant, hass: HomeAssistant,
mock_mealie_client: AsyncMock, mock_mealie_client: AsyncMock,

View File

@ -2,7 +2,7 @@
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
from aiomealie import MealieAuthenticationError, MealieConnectionError from aiomealie import About, MealieAuthenticationError, MealieConnectionError
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
@ -32,6 +32,51 @@ async def test_device_info(
assert device_entry == snapshot assert device_entry == snapshot
@pytest.mark.parametrize(
("exc", "state"),
[
(MealieConnectionError, ConfigEntryState.SETUP_RETRY),
(MealieAuthenticationError, ConfigEntryState.SETUP_ERROR),
],
)
async def test_setup_failure(
hass: HomeAssistant,
mock_mealie_client: AsyncMock,
mock_config_entry: MockConfigEntry,
exc: Exception,
state: ConfigEntryState,
) -> None:
"""Test setup failure."""
mock_mealie_client.get_about.side_effect = exc
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is state
@pytest.mark.parametrize(
("version"),
[
("v1.0.0beta-5"),
("v1.0.0-RC2"),
("v0.1.0"),
("something"),
],
)
async def test_setup_too_old(
hass: HomeAssistant,
mock_mealie_client: AsyncMock,
mock_config_entry: MockConfigEntry,
version,
) -> None:
"""Test setup of Mealie entry with too old version of Mealie."""
mock_mealie_client.get_about.return_value = About(version=version)
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
async def test_load_unload_entry( async def test_load_unload_entry(
hass: HomeAssistant, hass: HomeAssistant,
mock_mealie_client: AsyncMock, mock_mealie_client: AsyncMock,