Abort if a flow is removed during a step (#142138)

* Abort if a flow is removed during a step

* Reorganize code

* Only call _set_pending_import_done if an entry is created

* Try a new approach

* Add tests

* Update tests
This commit is contained in:
Erik Montnemery
2025-04-09 19:04:41 +02:00
committed by GitHub
parent 7f4d178781
commit f344314762
4 changed files with 95 additions and 33 deletions

View File

@@ -1503,6 +1503,22 @@ class ConfigEntriesFlowManager(
future.set_result(None)
self._discovery_event_debouncer.async_shutdown()
@callback
def async_flow_removed(
self,
flow: data_entry_flow.FlowHandler[ConfigFlowContext, ConfigFlowResult],
) -> None:
"""Handle a removed config flow."""
flow = cast(ConfigFlow, flow)
# Clean up issue if this is a reauth flow
if flow.context["source"] == SOURCE_REAUTH:
if (entry_id := flow.context.get("entry_id")) is not None and (
entry := self.config_entries.async_get_entry(entry_id)
) is not None:
issue_id = f"config_entry_reauth_{entry.domain}_{entry.entry_id}"
ir.async_delete_issue(self.hass, HOMEASSISTANT_DOMAIN, issue_id)
async def async_finish_flow(
self,
flow: data_entry_flow.FlowHandler[ConfigFlowContext, ConfigFlowResult],
@@ -1515,20 +1531,6 @@ class ConfigEntriesFlowManager(
"""
flow = cast(ConfigFlow, flow)
# Mark the step as done.
# We do this to avoid a circular dependency where async_finish_flow sets up a
# new entry, which needs the integration to be set up, which is waiting for
# init to be done.
self._set_pending_import_done(flow)
# Clean up issue if this is a reauth flow
if flow.context["source"] == SOURCE_REAUTH:
if (entry_id := flow.context.get("entry_id")) is not None and (
entry := self.config_entries.async_get_entry(entry_id)
) is not None:
issue_id = f"config_entry_reauth_{entry.domain}_{entry.entry_id}"
ir.async_delete_issue(self.hass, HOMEASSISTANT_DOMAIN, issue_id)
if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY:
# If there's a config entry with a matching unique ID,
# update the discovery key.
@@ -1567,6 +1569,12 @@ class ConfigEntriesFlowManager(
)
return result
# Mark the step as done.
# We do this to avoid a circular dependency where async_finish_flow sets up a
# new entry, which needs the integration to be set up, which is waiting for
# init to be done.
self._set_pending_import_done(flow)
# Avoid adding a config entry for a integration
# that only supports a single config entry, but already has an entry
if (