Skip to content

Commit 10d2e38

Browse files
authored
Firefly add reconfigure flow (home-assistant#155530)
1 parent 5299690 commit 10d2e38

File tree

6 files changed

+200
-14
lines changed

6 files changed

+200
-14
lines changed

homeassistant/components/firefly_iii/config_flow.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,51 @@ async def async_step_reauth_confirm(
129129
errors=errors,
130130
)
131131

132+
async def async_step_reconfigure(
133+
self, user_input: dict[str, Any] | None = None
134+
) -> ConfigFlowResult:
135+
"""Handle reconfiguration of the integration."""
136+
errors: dict[str, str] = {}
137+
reconf_entry = self._get_reconfigure_entry()
138+
139+
if user_input:
140+
try:
141+
await _validate_input(
142+
self.hass,
143+
data={
144+
**reconf_entry.data,
145+
**user_input,
146+
},
147+
)
148+
except CannotConnect:
149+
errors["base"] = "cannot_connect"
150+
except InvalidAuth:
151+
errors["base"] = "invalid_auth"
152+
except FireflyClientTimeout:
153+
errors["base"] = "timeout_connect"
154+
except Exception:
155+
_LOGGER.exception("Unexpected exception")
156+
errors["base"] = "unknown"
157+
else:
158+
self._async_abort_entries_match({CONF_URL: user_input[CONF_URL]})
159+
return self.async_update_reload_and_abort(
160+
reconf_entry,
161+
data_updates={
162+
CONF_URL: user_input[CONF_URL],
163+
CONF_API_KEY: user_input[CONF_API_KEY],
164+
CONF_VERIFY_SSL: user_input[CONF_VERIFY_SSL],
165+
},
166+
)
167+
168+
return self.async_show_form(
169+
step_id="reconfigure",
170+
data_schema=self.add_suggested_values_to_schema(
171+
data_schema=STEP_USER_DATA_SCHEMA,
172+
suggested_values=user_input or reconf_entry.data.copy(),
173+
),
174+
errors=errors,
175+
)
176+
132177

133178
class CannotConnect(HomeAssistantError):
134179
"""Error to indicate we cannot connect."""

homeassistant/components/firefly_iii/strings.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"config": {
33
"abort": {
44
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
5-
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
5+
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
6+
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
67
},
78
"error": {
89
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@@ -20,6 +21,20 @@
2021
},
2122
"description": "The access token for your Firefly III instance is invalid and needs to be updated. Go to **Options > Profile** and select the **OAuth** tab. Create a new personal access token and copy it (it will only display once)."
2223
},
24+
"reconfigure": {
25+
"data": {
26+
"api_key": "[%key:common::config_flow::data::api_key%]",
27+
"url": "[%key:common::config_flow::data::url%]",
28+
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
29+
},
30+
"data_description": {
31+
"api_key": "[%key:component::firefly_iii::config::step::user::data_description::api_key%]",
32+
"url": "[%key:common::config_flow::data::url%]",
33+
"verify_ssl": "[%key:component::firefly_iii::config::step::user::data_description::verify_ssl%]"
34+
},
35+
"description": "Use the following form to reconfigure your Firefly III instance.",
36+
"title": "Reconfigure Firefly III Integration"
37+
},
2338
"user": {
2439
"data": {
2540
"api_key": "[%key:common::config_flow::data::api_key%]",

tests/components/firefly_iii/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,5 @@ def mock_config_entry() -> MockConfigEntry:
9292
title="Firefly III test",
9393
data=MOCK_TEST_CONFIG,
9494
entry_id="firefly_iii_test_entry_123",
95-
unique_id="firefly_iii_test_unique_id_123",
95+
unique_id=MOCK_TEST_CONFIG[CONF_API_KEY],
9696
)

tests/components/firefly_iii/snapshots/test_diagnostics.ambr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
'subentries': list([
2222
]),
2323
'title': 'Firefly III test',
24-
'unique_id': 'firefly_iii_test_unique_id_123',
24+
'unique_id': 'test_api_key',
2525
'version': 1,
2626
}),
2727
'data': dict({

tests/components/firefly_iii/snapshots/test_sensor.ambr

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
'suggested_object_id': None,
3333
'supported_features': 0,
3434
'translation_key': 'budget',
35-
'unique_id': 'firefly_iii_test_unique_id_123_budget_2_budget',
35+
'unique_id': 'test_api_key_budget_2_budget',
3636
'unit_of_measurement': 'AMS',
3737
})
3838
# ---
@@ -85,7 +85,7 @@
8585
'suggested_object_id': None,
8686
'supported_features': 0,
8787
'translation_key': 'account_balance',
88-
'unique_id': 'firefly_iii_test_unique_id_123_account_4_account_balance',
88+
'unique_id': 'test_api_key_account_4_account_balance',
8989
'unit_of_measurement': 'AMS',
9090
})
9191
# ---
@@ -136,7 +136,7 @@
136136
'suggested_object_id': None,
137137
'supported_features': 0,
138138
'translation_key': 'account_role',
139-
'unique_id': 'firefly_iii_test_unique_id_123_account_4_account_role',
139+
'unique_id': 'test_api_key_account_4_account_role',
140140
'unit_of_measurement': None,
141141
})
142142
# ---
@@ -184,7 +184,7 @@
184184
'suggested_object_id': None,
185185
'supported_features': 0,
186186
'translation_key': 'account_type',
187-
'unique_id': 'firefly_iii_test_unique_id_123_account_4_account_type',
187+
'unique_id': 'test_api_key_account_4_account_type',
188188
'unit_of_measurement': None,
189189
})
190190
# ---
@@ -235,7 +235,7 @@
235235
'suggested_object_id': None,
236236
'supported_features': 0,
237237
'translation_key': 'category',
238-
'unique_id': 'firefly_iii_test_unique_id_123_category_2_category',
238+
'unique_id': 'test_api_key_category_2_category',
239239
'unit_of_measurement': 'AMS',
240240
})
241241
# ---
@@ -288,7 +288,7 @@
288288
'suggested_object_id': None,
289289
'supported_features': 0,
290290
'translation_key': 'account_balance',
291-
'unique_id': 'firefly_iii_test_unique_id_123_account_2_account_balance',
291+
'unique_id': 'test_api_key_account_2_account_balance',
292292
'unit_of_measurement': 'AMS',
293293
})
294294
# ---
@@ -339,7 +339,7 @@
339339
'suggested_object_id': None,
340340
'supported_features': 0,
341341
'translation_key': 'account_role',
342-
'unique_id': 'firefly_iii_test_unique_id_123_account_2_account_role',
342+
'unique_id': 'test_api_key_account_2_account_role',
343343
'unit_of_measurement': None,
344344
})
345345
# ---
@@ -387,7 +387,7 @@
387387
'suggested_object_id': None,
388388
'supported_features': 0,
389389
'translation_key': 'account_type',
390-
'unique_id': 'firefly_iii_test_unique_id_123_account_2_account_type',
390+
'unique_id': 'test_api_key_account_2_account_type',
391391
'unit_of_measurement': None,
392392
})
393393
# ---
@@ -438,7 +438,7 @@
438438
'suggested_object_id': None,
439439
'supported_features': 0,
440440
'translation_key': 'account_balance',
441-
'unique_id': 'firefly_iii_test_unique_id_123_account_3_account_balance',
441+
'unique_id': 'test_api_key_account_3_account_balance',
442442
'unit_of_measurement': 'AMS',
443443
})
444444
# ---
@@ -489,7 +489,7 @@
489489
'suggested_object_id': None,
490490
'supported_features': 0,
491491
'translation_key': 'account_role',
492-
'unique_id': 'firefly_iii_test_unique_id_123_account_3_account_role',
492+
'unique_id': 'test_api_key_account_3_account_role',
493493
'unit_of_measurement': None,
494494
})
495495
# ---
@@ -537,7 +537,7 @@
537537
'suggested_object_id': None,
538538
'supported_features': 0,
539539
'translation_key': 'account_type',
540-
'unique_id': 'firefly_iii_test_unique_id_123_account_3_account_type',
540+
'unique_id': 'test_api_key_account_3_account_type',
541541
'unit_of_measurement': None,
542542
})
543543
# ---

tests/components/firefly_iii/test_config_flow.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
CONF_VERIFY_SSL: True,
2626
}
2727

28+
USER_INPUT_RECONFIGURE = {
29+
CONF_URL: "https://new_domain:9000/",
30+
CONF_API_KEY: "new_api_key",
31+
CONF_VERIFY_SSL: True,
32+
}
33+
2834

2935
async def test_form_and_flow(
3036
hass: HomeAssistant,
@@ -225,3 +231,123 @@ async def test_reauth_flow_exceptions(
225231
assert result["reason"] == "reauth_successful"
226232
assert mock_config_entry.data[CONF_API_KEY] == "new_api_key"
227233
assert len(mock_setup_entry.mock_calls) == 1
234+
235+
236+
async def test_full_flow_reconfigure(
237+
hass: HomeAssistant,
238+
mock_firefly_client: AsyncMock,
239+
mock_setup_entry: MagicMock,
240+
mock_config_entry: MockConfigEntry,
241+
) -> None:
242+
"""Test the full flow of the config flow."""
243+
mock_config_entry.add_to_hass(hass)
244+
245+
result = await mock_config_entry.start_reconfigure_flow(hass)
246+
assert result["type"] is FlowResultType.FORM
247+
assert result["step_id"] == "reconfigure"
248+
249+
result = await hass.config_entries.flow.async_configure(
250+
result["flow_id"],
251+
user_input=USER_INPUT_RECONFIGURE,
252+
)
253+
254+
assert result["type"] is FlowResultType.ABORT
255+
assert result["reason"] == "reconfigure_successful"
256+
assert mock_config_entry.data[CONF_API_KEY] == "new_api_key"
257+
assert mock_config_entry.data[CONF_URL] == "https://new_domain:9000/"
258+
assert mock_config_entry.data[CONF_VERIFY_SSL] is True
259+
assert len(mock_setup_entry.mock_calls) == 1
260+
261+
262+
async def test_full_flow_reconfigure_unique_id(
263+
hass: HomeAssistant,
264+
mock_firefly_client: AsyncMock,
265+
mock_setup_entry: MagicMock,
266+
mock_config_entry: MockConfigEntry,
267+
) -> None:
268+
"""Test the full flow of the config flow, this time with a known unique ID."""
269+
mock_config_entry.add_to_hass(hass)
270+
duplicate_entry = MockConfigEntry(
271+
domain="firefly_iii",
272+
data={
273+
CONF_URL: "https://duplicate-url/",
274+
CONF_API_KEY: "other_key",
275+
CONF_VERIFY_SSL: True,
276+
},
277+
unique_id="very-annoying-duplicate",
278+
)
279+
duplicate_entry.add_to_hass(hass)
280+
281+
result = await mock_config_entry.start_reconfigure_flow(hass)
282+
assert result["type"] is FlowResultType.FORM
283+
assert result["step_id"] == "reconfigure"
284+
285+
result = await hass.config_entries.flow.async_configure(
286+
result["flow_id"],
287+
user_input={
288+
CONF_URL: "https://duplicate-url/",
289+
CONF_API_KEY: "new_key",
290+
CONF_VERIFY_SSL: True,
291+
},
292+
)
293+
294+
assert result["type"] is FlowResultType.ABORT
295+
assert result["reason"] == "already_configured"
296+
297+
298+
@pytest.mark.parametrize(
299+
("exception", "reason"),
300+
[
301+
(
302+
FireflyAuthenticationError,
303+
"invalid_auth",
304+
),
305+
(
306+
FireflyConnectionError,
307+
"cannot_connect",
308+
),
309+
(
310+
FireflyTimeoutError,
311+
"timeout_connect",
312+
),
313+
(
314+
Exception("Some other error"),
315+
"unknown",
316+
),
317+
],
318+
)
319+
async def test_full_flow_reconfigure_exceptions(
320+
hass: HomeAssistant,
321+
mock_firefly_client: AsyncMock,
322+
mock_setup_entry: MagicMock,
323+
mock_config_entry: MockConfigEntry,
324+
exception: Exception,
325+
reason: str,
326+
) -> None:
327+
"""Test the full flow of the config flow, this time with exceptions."""
328+
mock_config_entry.add_to_hass(hass)
329+
result = await mock_config_entry.start_reconfigure_flow(hass)
330+
assert result["type"] is FlowResultType.FORM
331+
assert result["step_id"] == "reconfigure"
332+
333+
mock_firefly_client.get_about.side_effect = exception
334+
result = await hass.config_entries.flow.async_configure(
335+
result["flow_id"],
336+
user_input=USER_INPUT_RECONFIGURE,
337+
)
338+
339+
assert result["type"] is FlowResultType.FORM
340+
assert result["errors"] == {"base": reason}
341+
342+
mock_firefly_client.get_about.side_effect = None
343+
result = await hass.config_entries.flow.async_configure(
344+
result["flow_id"],
345+
user_input=USER_INPUT_RECONFIGURE,
346+
)
347+
348+
assert result["type"] is FlowResultType.ABORT
349+
assert result["reason"] == "reconfigure_successful"
350+
assert mock_config_entry.data[CONF_API_KEY] == "new_api_key"
351+
assert mock_config_entry.data[CONF_URL] == "https://new_domain:9000/"
352+
assert mock_config_entry.data[CONF_VERIFY_SSL] is True
353+
assert len(mock_setup_entry.mock_calls) == 1

0 commit comments

Comments
 (0)