Log a warning when replacing existing config entry with same unique id (#130567)

* Log a warning when replacing existing config entry with same unique id

* Exclude mobile_app

* Ignore custom integrations

* Apply suggestions from code review

* Apply suggestions from code review

* Update config_entries.py

* Fix handler

* Adjust and add tests

* Apply suggestions from code review

* Apply suggestions from code review

* Update comment

* Update config_entries.py

* Apply suggestions from code review
This commit is contained in:
epenet 2025-02-28 14:20:39 +01:00 committed by GitHub
parent d157919da2
commit 030a1460de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 0 deletions

View File

@ -1628,6 +1628,23 @@ class ConfigEntriesFlowManager(
result["handler"], flow.unique_id
)
if existing_entry is not None and flow.handler != "mobile_app":
# This causes the old entry to be removed and replaced, when the flow
# should instead be aborted.
# In case of manual flows, integrations should implement options, reauth,
# reconfigure to allow the user to change settings.
# In case of non user visible flows, the integration should optionally
# update the existing entry before aborting.
# see https://developers.home-assistant.io/blog/2025/01/16/config-flow-unique-id/
report_usage(
"creates a config entry when another entry with the same unique ID "
"exists",
core_behavior=ReportBehavior.LOG,
core_integration_behavior=ReportBehavior.LOG,
custom_integration_behavior=ReportBehavior.LOG,
integration_domain=flow.handler,
)
# Unload the entry before setting up the new one.
if existing_entry is not None and existing_entry.state.recoverable:
await self.config_entries.async_unload(existing_entry.entry_id)

View File

@ -8899,3 +8899,63 @@ async def test_add_description_placeholder_automatically_not_overwrites(
result = await hass.config_entries.flow.async_configure(flows[0]["flow_id"], None)
assert result["type"] == FlowResultType.FORM
assert result["description_placeholders"] == {"name": "Custom title"}
@pytest.mark.parametrize(
("domain", "expected_log"),
[
("some_integration", True),
("mobile_app", False),
],
)
async def test_create_entry_existing_unique_id(
hass: HomeAssistant,
domain: str,
expected_log: bool,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test to highlight unexpected behavior on create_entry."""
entry = MockConfigEntry(
title="From config flow",
domain=domain,
entry_id="01J915Q6T9F6G5V0QJX6HBC94T",
data={"host": "any", "port": 123},
unique_id="mock-unique-id",
)
entry.add_to_hass(hass)
assert len(hass.config_entries.async_entries(domain)) == 1
mock_setup_entry = AsyncMock(return_value=True)
mock_integration(hass, MockModule(domain, async_setup_entry=mock_setup_entry))
mock_platform(hass, f"{domain}.config_flow", None)
class TestFlow(config_entries.ConfigFlow):
"""Test flow."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Test user step."""
await self.async_set_unique_id("mock-unique-id")
return self.async_create_entry(title="mock-title", data={})
with (
mock_config_flow(domain, TestFlow),
patch.object(frame, "_REPORTED_INTEGRATIONS", set()),
):
result = await hass.config_entries.flow.async_init(
domain, context={"source": config_entries.SOURCE_USER}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert len(hass.config_entries.async_entries(domain)) == 1
log_text = (
f"Detected that integration '{domain}' creates a config entry "
"when another entry with the same unique ID exists. Please "
"create a bug report at https:"
)
assert (log_text in caplog.text) == expected_log