mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 17:04:04 -04:00
Adjust monzo to not access DeviceEntry.config_entries (#181751)
This commit is contained in:
@@ -88,18 +88,7 @@ TRANSFER_SCHEMA = vol.Schema(
|
||||
|
||||
|
||||
@callback
|
||||
def _async_get_device(call: ServiceCall, field: str) -> dr.AnyDeviceEntry:
|
||||
"""Get a selected device."""
|
||||
if (device := dr.async_get(call.hass).async_get(call.data[field])) is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_device",
|
||||
)
|
||||
return device
|
||||
|
||||
|
||||
@callback
|
||||
def _async_get_resource_id(device: dr.AnyDeviceEntry) -> str:
|
||||
def _async_get_resource_id(device: dr.DeviceEntry) -> str:
|
||||
"""Get the Monzo resource ID represented by a device."""
|
||||
for domain, resource_id in device.identifiers:
|
||||
if domain == DOMAIN:
|
||||
@@ -110,7 +99,7 @@ def _async_get_resource_id(device: dr.AnyDeviceEntry) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _device_name(device: dr.AnyDeviceEntry) -> str:
|
||||
def _device_name(device: dr.DeviceEntry) -> str:
|
||||
"""Return the best available name for a device."""
|
||||
return device.name_by_user or device.name or device.id
|
||||
|
||||
@@ -120,65 +109,61 @@ def _async_resolve_transfer(
|
||||
call: ServiceCall,
|
||||
) -> tuple[MonzoCoordinator, str, str]:
|
||||
"""Resolve and validate the account and pot selected for a transfer."""
|
||||
account_device = _async_get_device(call, ATTR_ACCOUNT)
|
||||
pot_device = _async_get_device(call, ATTR_POT)
|
||||
account_device, account_entry = service.async_get_device_and_config_entry(
|
||||
call.hass, DOMAIN, call.data[ATTR_ACCOUNT]
|
||||
)
|
||||
pot_device, pot_entry = service.async_get_device_and_config_entry(
|
||||
call.hass, DOMAIN, call.data[ATTR_POT]
|
||||
)
|
||||
if account_entry.entry_id != pot_entry.entry_id:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="different_entries",
|
||||
)
|
||||
account_id = _async_get_resource_id(account_device)
|
||||
pot_id = _async_get_resource_id(pot_device)
|
||||
|
||||
for entry_id in account_device.config_entries & pot_device.config_entries:
|
||||
config_entry = call.hass.config_entries.async_get_entry(entry_id)
|
||||
if config_entry is None or config_entry.domain != DOMAIN:
|
||||
continue
|
||||
|
||||
entry = cast(
|
||||
MonzoConfigEntry,
|
||||
service.async_get_config_entry(call.hass, DOMAIN, entry_id),
|
||||
entry = cast(MonzoConfigEntry, account_entry)
|
||||
coordinator = entry.runtime_data.coordinator
|
||||
if (account := coordinator.data.accounts.get(account_id)) is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key=(
|
||||
"pot_selected_as_account"
|
||||
if account_id in coordinator.data.pots
|
||||
else "invalid_account"
|
||||
),
|
||||
translation_placeholders={"device_name": _device_name(account_device)},
|
||||
)
|
||||
coordinator = entry.runtime_data.coordinator
|
||||
if (account := coordinator.data.accounts.get(account_id)) is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key=(
|
||||
"pot_selected_as_account"
|
||||
if account_id in coordinator.data.pots
|
||||
else "invalid_account"
|
||||
),
|
||||
translation_placeholders={"device_name": _device_name(account_device)},
|
||||
)
|
||||
if account["type"] in NON_TRANSFER_ACCOUNT_TYPES:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_transfer_account",
|
||||
translation_placeholders={"device_name": _device_name(account_device)},
|
||||
)
|
||||
if (pot := coordinator.data.pots.get(pot_id)) is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key=(
|
||||
"account_selected_as_pot"
|
||||
if pot_id in coordinator.data.accounts
|
||||
else "invalid_pot"
|
||||
),
|
||||
translation_placeholders={"device_name": _device_name(pot_device)},
|
||||
)
|
||||
if pot["current_account_id"] != account_id:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="pot_account_mismatch",
|
||||
translation_placeholders={
|
||||
"account_name": account["name"],
|
||||
"pot_name": pot["name"],
|
||||
"pot_account_name": coordinator.data.accounts[
|
||||
pot["current_account_id"]
|
||||
]["name"],
|
||||
},
|
||||
)
|
||||
return coordinator, account_id, pot_id
|
||||
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="different_entries",
|
||||
)
|
||||
if account["type"] in NON_TRANSFER_ACCOUNT_TYPES:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_transfer_account",
|
||||
translation_placeholders={"device_name": _device_name(account_device)},
|
||||
)
|
||||
if (pot := coordinator.data.pots.get(pot_id)) is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key=(
|
||||
"account_selected_as_pot"
|
||||
if pot_id in coordinator.data.accounts
|
||||
else "invalid_pot"
|
||||
),
|
||||
translation_placeholders={"device_name": _device_name(pot_device)},
|
||||
)
|
||||
if pot["current_account_id"] != account_id:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="pot_account_mismatch",
|
||||
translation_placeholders={
|
||||
"account_name": account["name"],
|
||||
"pot_name": pot["name"],
|
||||
"pot_account_name": coordinator.data.accounts[
|
||||
pot["current_account_id"]
|
||||
]["name"],
|
||||
},
|
||||
)
|
||||
return coordinator, account_id, pot_id
|
||||
|
||||
|
||||
async def _async_transfer(
|
||||
|
||||
@@ -23,7 +23,8 @@ from homeassistant.components.monzo.services import (
|
||||
SERVICE_DEPOSIT_INTO_POT,
|
||||
SERVICE_WITHDRAW_FROM_POT,
|
||||
)
|
||||
from homeassistant.core import Context, HomeAssistant
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, Context, HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
OAuth2TokenRequestReauthError,
|
||||
@@ -218,7 +219,7 @@ async def test_missing_device(
|
||||
transfer_devices: TransferDevices,
|
||||
) -> None:
|
||||
"""Test a missing selected device is rejected."""
|
||||
with pytest.raises(ServiceValidationError):
|
||||
with pytest.raises(ServiceValidationError) as error:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_DEPOSIT_INTO_POT,
|
||||
@@ -230,22 +231,76 @@ async def test_missing_device(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert error.value.translation_domain == HOMEASSISTANT_DOMAIN
|
||||
assert error.value.translation_key == "service_device_not_found"
|
||||
|
||||
async def test_devices_from_different_entries(
|
||||
|
||||
async def test_device_from_other_integration(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
transfer_devices: TransferDevices,
|
||||
) -> None:
|
||||
"""Test the account and pot must belong to the same config entry."""
|
||||
"""Test a device not owned by a Monzo config entry is rejected."""
|
||||
other_entry = MockConfigEntry(domain="other")
|
||||
other_entry.add_to_hass(hass)
|
||||
other_device = device_registry.async_get_or_create(
|
||||
config_entry_id=other_entry.entry_id,
|
||||
identifiers={("other", "other-device")},
|
||||
name="Other device",
|
||||
)
|
||||
|
||||
with pytest.raises(ServiceValidationError) as error:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_DEPOSIT_INTO_POT,
|
||||
{
|
||||
ATTR_ACCOUNT: transfer_devices.account_device_id,
|
||||
ATTR_POT: other_device.id,
|
||||
ATTR_AMOUNT: 1,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert error.value.translation_domain == HOMEASSISTANT_DOMAIN
|
||||
assert error.value.translation_key == "service_device_wrong_domain"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("other_entry_state", "translation_domain", "translation_key"),
|
||||
[
|
||||
pytest.param(
|
||||
ConfigEntryState.LOADED,
|
||||
DOMAIN,
|
||||
"different_entries",
|
||||
id="loaded-entry",
|
||||
),
|
||||
pytest.param(
|
||||
ConfigEntryState.NOT_LOADED,
|
||||
HOMEASSISTANT_DOMAIN,
|
||||
"service_config_entry_not_loaded",
|
||||
id="not-loaded-entry",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_devices_from_different_entries(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
transfer_devices: TransferDevices,
|
||||
other_entry_state: ConfigEntryState,
|
||||
translation_domain: str,
|
||||
translation_key: str,
|
||||
) -> None:
|
||||
"""Test the account and pot must belong to the same loaded config entry."""
|
||||
other_entry = MockConfigEntry(domain=DOMAIN)
|
||||
other_entry.add_to_hass(hass)
|
||||
other_entry.mock_state(hass, other_entry_state)
|
||||
other_pot = device_registry.async_get_or_create(
|
||||
config_entry_id=other_entry.entry_id,
|
||||
identifiers={(DOMAIN, "other-pot")},
|
||||
name="Other pot",
|
||||
)
|
||||
|
||||
with pytest.raises(ServiceValidationError):
|
||||
with pytest.raises(ServiceValidationError) as error:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_DEPOSIT_INTO_POT,
|
||||
@@ -257,6 +312,9 @@ async def test_devices_from_different_entries(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert error.value.translation_domain == translation_domain
|
||||
assert error.value.translation_key == translation_key
|
||||
|
||||
|
||||
async def test_pot_must_belong_to_selected_account(
|
||||
hass: HomeAssistant,
|
||||
|
||||
Reference in New Issue
Block a user