mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 08:17:08 +00:00
Migrate Mastodon unique id (#123877)
* Migrate unique id * Fix unique id check * Switch to minor version and other fixes
This commit is contained in:
parent
d50bac3b3e
commit
ac223e64f9
@ -17,10 +17,11 @@ from homeassistant.const import (
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
from .const import CONF_BASE_URL, DOMAIN
|
from .const import CONF_BASE_URL, DOMAIN, LOGGER
|
||||||
from .coordinator import MastodonCoordinator
|
from .coordinator import MastodonCoordinator
|
||||||
from .utils import create_mastodon_client
|
from .utils import construct_mastodon_username, create_mastodon_client
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.NOTIFY, Platform.SENSOR]
|
PLATFORMS: list[Platform] = [Platform.NOTIFY, Platform.SENSOR]
|
||||||
|
|
||||||
@ -80,6 +81,39 @@ async def async_unload_entry(hass: HomeAssistant, entry: MastodonConfigEntry) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Migrate old config."""
|
||||||
|
|
||||||
|
if entry.version == 1 and entry.minor_version == 1:
|
||||||
|
# Version 1.1 had the unique_id as client_id, this isn't necessarily unique
|
||||||
|
LOGGER.debug("Migrating config entry from version %s", entry.version)
|
||||||
|
|
||||||
|
try:
|
||||||
|
_, instance, account = await hass.async_add_executor_job(
|
||||||
|
setup_mastodon,
|
||||||
|
entry,
|
||||||
|
)
|
||||||
|
except MastodonError as ex:
|
||||||
|
LOGGER.error("Migration failed with error %s", ex)
|
||||||
|
return False
|
||||||
|
|
||||||
|
entry.minor_version = 2
|
||||||
|
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry,
|
||||||
|
unique_id=slugify(construct_mastodon_username(instance, account)),
|
||||||
|
)
|
||||||
|
|
||||||
|
LOGGER.info(
|
||||||
|
"Entry %s successfully migrated to version %s.%s",
|
||||||
|
entry.entry_id,
|
||||||
|
entry.version,
|
||||||
|
entry.minor_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup_mastodon(entry: ConfigEntry) -> tuple[Mastodon, dict, dict]:
|
def setup_mastodon(entry: ConfigEntry) -> tuple[Mastodon, dict, dict]:
|
||||||
"""Get mastodon details."""
|
"""Get mastodon details."""
|
||||||
client = create_mastodon_client(
|
client = create_mastodon_client(
|
||||||
|
@ -20,6 +20,7 @@ from homeassistant.helpers.selector import (
|
|||||||
TextSelectorType,
|
TextSelectorType,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
from .const import CONF_BASE_URL, DEFAULT_URL, DOMAIN, LOGGER
|
from .const import CONF_BASE_URL, DEFAULT_URL, DOMAIN, LOGGER
|
||||||
from .utils import construct_mastodon_username, create_mastodon_client
|
from .utils import construct_mastodon_username, create_mastodon_client
|
||||||
@ -47,6 +48,7 @@ class MastodonConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
"""Handle a config flow."""
|
"""Handle a config flow."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
MINOR_VERSION = 2
|
||||||
config_entry: ConfigEntry
|
config_entry: ConfigEntry
|
||||||
|
|
||||||
def check_connection(
|
def check_connection(
|
||||||
@ -105,10 +107,6 @@ class MastodonConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
"""Handle a flow initialized by the user."""
|
"""Handle a flow initialized by the user."""
|
||||||
errors: dict[str, str] | None = None
|
errors: dict[str, str] | None = None
|
||||||
if user_input:
|
if user_input:
|
||||||
self._async_abort_entries_match(
|
|
||||||
{CONF_CLIENT_ID: user_input[CONF_CLIENT_ID]}
|
|
||||||
)
|
|
||||||
|
|
||||||
instance, account, errors = await self.hass.async_add_executor_job(
|
instance, account, errors = await self.hass.async_add_executor_job(
|
||||||
self.check_connection,
|
self.check_connection,
|
||||||
user_input[CONF_BASE_URL],
|
user_input[CONF_BASE_URL],
|
||||||
@ -119,7 +117,8 @@ class MastodonConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
if not errors:
|
if not errors:
|
||||||
name = construct_mastodon_username(instance, account)
|
name = construct_mastodon_username(instance, account)
|
||||||
await self.async_set_unique_id(user_input[CONF_CLIENT_ID])
|
await self.async_set_unique_id(slugify(name))
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=name,
|
title=name,
|
||||||
data=user_input,
|
data=user_input,
|
||||||
@ -148,7 +147,8 @@ class MastodonConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not errors:
|
if not errors:
|
||||||
await self.async_set_unique_id(client_id)
|
name = construct_mastodon_username(instance, account)
|
||||||
|
await self.async_set_unique_id(slugify(name))
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
if not name:
|
if not name:
|
||||||
|
@ -53,5 +53,7 @@ def mock_config_entry() -> MockConfigEntry:
|
|||||||
CONF_ACCESS_TOKEN: "access_token",
|
CONF_ACCESS_TOKEN: "access_token",
|
||||||
},
|
},
|
||||||
entry_id="01J35M4AH9HYRC2V0G6RNVNWJH",
|
entry_id="01J35M4AH9HYRC2V0G6RNVNWJH",
|
||||||
unique_id="client_id",
|
unique_id="trwnh_mastodon_social",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
|
33
tests/components/mastodon/snapshots/test_init.ambr
Normal file
33
tests/components/mastodon/snapshots/test_init.ambr
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_device_info
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': <DeviceEntryType.SERVICE: 'service'>,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'mastodon',
|
||||||
|
'trwnh_mastodon_social',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Mastodon gGmbH',
|
||||||
|
'model': '@trwnh@mastodon.social',
|
||||||
|
'model_id': None,
|
||||||
|
'name': 'Mastodon @trwnh@mastodon.social',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': None,
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '4.0.0rc1',
|
||||||
|
'via_device_id': None,
|
||||||
|
})
|
||||||
|
# ---
|
@ -30,7 +30,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'followers',
|
'translation_key': 'followers',
|
||||||
'unique_id': 'client_id_followers',
|
'unique_id': 'trwnh_mastodon_social_followers',
|
||||||
'unit_of_measurement': 'accounts',
|
'unit_of_measurement': 'accounts',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -80,7 +80,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'following',
|
'translation_key': 'following',
|
||||||
'unique_id': 'client_id_following',
|
'unique_id': 'trwnh_mastodon_social_following',
|
||||||
'unit_of_measurement': 'accounts',
|
'unit_of_measurement': 'accounts',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -130,7 +130,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'posts',
|
'translation_key': 'posts',
|
||||||
'unique_id': 'client_id_posts',
|
'unique_id': 'trwnh_mastodon_social_posts',
|
||||||
'unit_of_measurement': 'posts',
|
'unit_of_measurement': 'posts',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
@ -44,7 +44,7 @@ async def test_full_flow(
|
|||||||
CONF_CLIENT_SECRET: "client_secret",
|
CONF_CLIENT_SECRET: "client_secret",
|
||||||
CONF_ACCESS_TOKEN: "access_token",
|
CONF_ACCESS_TOKEN: "access_token",
|
||||||
}
|
}
|
||||||
assert result["result"].unique_id == "client_id"
|
assert result["result"].unique_id == "trwnh_mastodon_social"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -3,15 +3,36 @@
|
|||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from mastodon.Mastodon import MastodonError
|
from mastodon.Mastodon import MastodonError
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.mastodon.config_flow import MastodonConfigFlow
|
||||||
|
from homeassistant.components.mastodon.const import CONF_BASE_URL, DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
|
||||||
from . import setup_integration
|
from . import setup_integration
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_info(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
mock_mastodon_client: AsyncMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test device registry integration."""
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
device_entry = device_registry.async_get_device(
|
||||||
|
identifiers={(DOMAIN, mock_config_entry.unique_id)}
|
||||||
|
)
|
||||||
|
assert device_entry is not None
|
||||||
|
assert device_entry == snapshot
|
||||||
|
|
||||||
|
|
||||||
async def test_initialization_failure(
|
async def test_initialization_failure(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_mastodon_client: AsyncMock,
|
mock_mastodon_client: AsyncMock,
|
||||||
@ -23,3 +44,39 @@ async def test_initialization_failure(
|
|||||||
await setup_integration(hass, mock_config_entry)
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migrate(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_mastodon_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test migration."""
|
||||||
|
# Setup the config entry
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_BASE_URL: "https://mastodon.social",
|
||||||
|
CONF_CLIENT_ID: "client_id",
|
||||||
|
CONF_CLIENT_SECRET: "client_secret",
|
||||||
|
CONF_ACCESS_TOKEN: "access_token",
|
||||||
|
},
|
||||||
|
title="@trwnh@mastodon.social",
|
||||||
|
unique_id="client_id",
|
||||||
|
version=1,
|
||||||
|
minor_version=1,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Check migration was successful
|
||||||
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
assert config_entry.data == {
|
||||||
|
CONF_BASE_URL: "https://mastodon.social",
|
||||||
|
CONF_CLIENT_ID: "client_id",
|
||||||
|
CONF_CLIENT_SECRET: "client_secret",
|
||||||
|
CONF_ACCESS_TOKEN: "access_token",
|
||||||
|
}
|
||||||
|
assert config_entry.version == MastodonConfigFlow.VERSION
|
||||||
|
assert config_entry.minor_version == MastodonConfigFlow.MINOR_VERSION
|
||||||
|
assert config_entry.unique_id == "trwnh_mastodon_social"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user