Skip to content

Commit 4fb3c9f

Browse files
Add async_update_and_abort method to config flow (home-assistant#153146)
Co-authored-by: Erik Montnemery <[email protected]>
1 parent 1e5f5f4 commit 4fb3c9f

File tree

2 files changed

+164
-17
lines changed

2 files changed

+164
-17
lines changed

homeassistant/config_entries.py

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,6 +3210,76 @@ def async_create_entry( # type: ignore[override]
32103210

32113211
return result
32123212

3213+
@callback
3214+
def __async_update(
3215+
self,
3216+
entry: ConfigEntry,
3217+
*,
3218+
unique_id: str | None | UndefinedType,
3219+
title: str | UndefinedType,
3220+
data: Mapping[str, Any] | UndefinedType,
3221+
data_updates: Mapping[str, Any] | UndefinedType,
3222+
options: Mapping[str, Any] | UndefinedType,
3223+
) -> bool:
3224+
"""Update config entry and return result.
3225+
3226+
Internal to be used by update_and_abort and update_reload_and_abort methods only.
3227+
"""
3228+
3229+
if data_updates is not UNDEFINED:
3230+
if data is not UNDEFINED:
3231+
raise ValueError("Cannot set both data and data_updates")
3232+
data = entry.data | data_updates
3233+
return self.hass.config_entries.async_update_entry(
3234+
entry=entry,
3235+
unique_id=unique_id,
3236+
title=title,
3237+
data=data,
3238+
options=options,
3239+
)
3240+
3241+
@callback
3242+
def async_update_and_abort(
3243+
self,
3244+
entry: ConfigEntry,
3245+
*,
3246+
unique_id: str | None | UndefinedType = UNDEFINED,
3247+
title: str | UndefinedType = UNDEFINED,
3248+
data: Mapping[str, Any] | UndefinedType = UNDEFINED,
3249+
data_updates: Mapping[str, Any] | UndefinedType = UNDEFINED,
3250+
options: Mapping[str, Any] | UndefinedType = UNDEFINED,
3251+
reason: str | UndefinedType = UNDEFINED,
3252+
) -> ConfigFlowResult:
3253+
"""Update config entry and finish config flow.
3254+
3255+
Args:
3256+
entry: config entry to update
3257+
unique_id: replace the unique_id of the entry
3258+
title: replace the title of the entry
3259+
data: replace the entry data with new data
3260+
data_updates: add items from data_updates to entry data - existing keys
3261+
are overridden
3262+
options: replace the entry options with new options
3263+
reason: set the reason for the abort, defaults to
3264+
`reauth_successful` or `reconfigure_successful` based on flow source
3265+
3266+
Returns:
3267+
ConfigFlowResult: The result of the config flow.
3268+
"""
3269+
self.__async_update(
3270+
entry=entry,
3271+
unique_id=unique_id,
3272+
title=title,
3273+
data=data,
3274+
data_updates=data_updates,
3275+
options=options,
3276+
)
3277+
if reason is UNDEFINED:
3278+
reason = "reauth_successful"
3279+
if self.source == SOURCE_RECONFIGURE:
3280+
reason = "reconfigure_successful"
3281+
return self.async_abort(reason=reason)
3282+
32133283
@callback
32143284
def async_update_reload_and_abort(
32153285
self,
@@ -3225,28 +3295,28 @@ def async_update_reload_and_abort(
32253295
) -> ConfigFlowResult:
32263296
"""Update config entry, reload config entry and finish config flow.
32273297
3228-
:param data: replace the entry data with new data
3229-
:param data_updates: add items from data_updates to entry data - existing keys
3230-
are overridden
3231-
:param options: replace the entry options with new options
3232-
:param title: replace the title of the entry
3233-
:param unique_id: replace the unique_id of the entry
3234-
3235-
:param reason: set the reason for the abort, defaults to
3236-
`reauth_successful` or `reconfigure_successful` based on flow source
3237-
3238-
:param reload_even_if_entry_is_unchanged: set this to `False` if the entry
3239-
should not be reloaded if it is unchanged
3298+
Args:
3299+
entry: config entry to update and reload
3300+
unique_id: replace the unique_id of the entry
3301+
title: replace the title of the entry
3302+
data: replace the entry data with new data
3303+
data_updates: add items from data_updates to entry data - existing keys
3304+
are overridden
3305+
options: replace the entry options with new options
3306+
reason: set the reason for the abort, defaults to
3307+
`reauth_successful` or `reconfigure_successful` based on flow source
3308+
reload_even_if_entry_is_unchanged: set this to `False` if the entry
3309+
should not be reloaded if it is unchanged
3310+
3311+
Returns:
3312+
ConfigFlowResult: The result of the config flow.
32403313
"""
3241-
if data_updates is not UNDEFINED:
3242-
if data is not UNDEFINED:
3243-
raise ValueError("Cannot set both data and data_updates")
3244-
data = entry.data | data_updates
3245-
result = self.hass.config_entries.async_update_entry(
3314+
result = self.__async_update(
32463315
entry=entry,
32473316
unique_id=unique_id,
32483317
title=title,
32493318
data=data,
3319+
data_updates=data_updates,
32503320
options=options,
32513321
)
32523322
if reload_even_if_entry_is_unchanged or result:

tests/test_config_entries.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6626,6 +6626,83 @@ async def async_step_reconfigure(self, data):
66266626
assert len(comp.async_unload_entry.mock_calls) == calls_entry_load_unload[1]
66276627

66286628

6629+
@pytest.mark.parametrize(
6630+
("source", "reason"),
6631+
[
6632+
(config_entries.SOURCE_REAUTH, "reauth_successful"),
6633+
(config_entries.SOURCE_RECONFIGURE, "reconfigure_successful"),
6634+
],
6635+
)
6636+
async def test_update_entry_without_reload(
6637+
hass: HomeAssistant,
6638+
source: str,
6639+
reason: str,
6640+
) -> None:
6641+
"""Test updating an entry without reloading."""
6642+
entry = MockConfigEntry(
6643+
domain="comp",
6644+
unique_id="1234",
6645+
title="Test",
6646+
data={"vendor": "data"},
6647+
options={"vendor": "options"},
6648+
)
6649+
entry.add_to_hass(hass)
6650+
6651+
comp = MockModule(
6652+
"comp",
6653+
async_setup_entry=AsyncMock(return_value=True),
6654+
async_unload_entry=AsyncMock(return_value=True),
6655+
)
6656+
mock_integration(hass, comp)
6657+
mock_platform(hass, "comp.config_flow", None)
6658+
6659+
await hass.config_entries.async_setup(entry.entry_id)
6660+
6661+
class MockFlowHandler(config_entries.ConfigFlow):
6662+
"""Define a mock flow handler."""
6663+
6664+
VERSION = 1
6665+
6666+
async def async_step_reauth(self, data):
6667+
"""Mock Reauth."""
6668+
return self.async_update_and_abort(
6669+
entry,
6670+
unique_id="5678",
6671+
title="Updated title",
6672+
data={"vendor": "data2"},
6673+
options={"vendor": "options2"},
6674+
)
6675+
6676+
async def async_step_reconfigure(self, data):
6677+
"""Mock Reconfigure."""
6678+
return self.async_update_and_abort(
6679+
entry,
6680+
unique_id="5678",
6681+
title="Updated title",
6682+
data={"vendor": "data2"},
6683+
options={"vendor": "options2"},
6684+
)
6685+
6686+
with mock_config_flow("comp", MockFlowHandler):
6687+
if source == config_entries.SOURCE_REAUTH:
6688+
result = await entry.start_reauth_flow(hass)
6689+
elif source == config_entries.SOURCE_RECONFIGURE:
6690+
result = await entry.start_reconfigure_flow(hass)
6691+
6692+
await hass.async_block_till_done()
6693+
6694+
assert entry.title == "Updated title"
6695+
assert entry.unique_id == "5678"
6696+
assert entry.data == {"vendor": "data2"}
6697+
assert entry.options == {"vendor": "options2"}
6698+
assert entry.state == config_entries.ConfigEntryState.LOADED
6699+
assert result["type"] == FlowResultType.ABORT
6700+
assert result["reason"] == reason
6701+
# Assert entry is not reloaded
6702+
assert len(comp.async_setup_entry.mock_calls) == 1
6703+
assert len(comp.async_unload_entry.mock_calls) == 0
6704+
6705+
66296706
@pytest.mark.parametrize(
66306707
(
66316708
"kwargs",

0 commit comments

Comments
 (0)