Add data entry flow show progress step (#42419)

This commit is contained in:
Martin Hjelmare 2020-11-09 18:39:28 +01:00 committed by GitHub
parent 3380b69d54
commit 1338c4a425
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 118 additions and 4 deletions

View file

@ -14,8 +14,10 @@ RESULT_TYPE_CREATE_ENTRY = "create_entry"
RESULT_TYPE_ABORT = "abort" RESULT_TYPE_ABORT = "abort"
RESULT_TYPE_EXTERNAL_STEP = "external" RESULT_TYPE_EXTERNAL_STEP = "external"
RESULT_TYPE_EXTERNAL_STEP_DONE = "external_done" RESULT_TYPE_EXTERNAL_STEP_DONE = "external_done"
RESULT_TYPE_SHOW_PROGRESS = "progress"
RESULT_TYPE_SHOW_PROGRESS_DONE = "progress_done"
# Event that is fired when a flow is progressed via external source. # Event that is fired when a flow is progressed via external or progress source.
EVENT_DATA_ENTRY_FLOW_PROGRESSED = "data_entry_flow_progressed" EVENT_DATA_ENTRY_FLOW_PROGRESSED = "data_entry_flow_progressed"
@ -152,8 +154,8 @@ class FlowManager(abc.ABC):
result = await self._async_handle_step(flow, cur_step["step_id"], user_input) result = await self._async_handle_step(flow, cur_step["step_id"], user_input)
if cur_step["type"] == RESULT_TYPE_EXTERNAL_STEP: if cur_step["type"] in (RESULT_TYPE_EXTERNAL_STEP, RESULT_TYPE_SHOW_PROGRESS):
if result["type"] not in ( if cur_step["type"] == RESULT_TYPE_EXTERNAL_STEP and result["type"] not in (
RESULT_TYPE_EXTERNAL_STEP, RESULT_TYPE_EXTERNAL_STEP,
RESULT_TYPE_EXTERNAL_STEP_DONE, RESULT_TYPE_EXTERNAL_STEP_DONE,
): ):
@ -161,10 +163,20 @@ class FlowManager(abc.ABC):
"External step can only transition to " "External step can only transition to "
"external step or external step done." "external step or external step done."
) )
if cur_step["type"] == RESULT_TYPE_SHOW_PROGRESS and result["type"] not in (
RESULT_TYPE_SHOW_PROGRESS,
RESULT_TYPE_SHOW_PROGRESS_DONE,
):
raise ValueError(
"Show progress can only transition to show progress or show progress done."
)
# If the result has changed from last result, fire event to update # If the result has changed from last result, fire event to update
# the frontend. # the frontend.
if cur_step["step_id"] != result.get("step_id"): if (
cur_step["step_id"] != result.get("step_id")
or result["type"] == RESULT_TYPE_SHOW_PROGRESS
):
# Tell frontend to reload the flow state. # Tell frontend to reload the flow state.
self.hass.bus.async_fire( self.hass.bus.async_fire(
EVENT_DATA_ENTRY_FLOW_PROGRESSED, EVENT_DATA_ENTRY_FLOW_PROGRESSED,
@ -217,6 +229,8 @@ class FlowManager(abc.ABC):
RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_CREATE_ENTRY,
RESULT_TYPE_ABORT, RESULT_TYPE_ABORT,
RESULT_TYPE_EXTERNAL_STEP_DONE, RESULT_TYPE_EXTERNAL_STEP_DONE,
RESULT_TYPE_SHOW_PROGRESS,
RESULT_TYPE_SHOW_PROGRESS_DONE,
): ):
raise ValueError(f"Handler returned incorrect type: {result['type']}") raise ValueError(f"Handler returned incorrect type: {result['type']}")
@ -224,6 +238,8 @@ class FlowManager(abc.ABC):
RESULT_TYPE_FORM, RESULT_TYPE_FORM,
RESULT_TYPE_EXTERNAL_STEP, RESULT_TYPE_EXTERNAL_STEP,
RESULT_TYPE_EXTERNAL_STEP_DONE, RESULT_TYPE_EXTERNAL_STEP_DONE,
RESULT_TYPE_SHOW_PROGRESS,
RESULT_TYPE_SHOW_PROGRESS_DONE,
): ):
flow.cur_step = result flow.cur_step = result
return result return result
@ -348,6 +364,34 @@ class FlowHandler:
"step_id": next_step_id, "step_id": next_step_id,
} }
@callback
def async_show_progress(
self,
*,
step_id: str,
progress_action: str,
description_placeholders: Optional[Dict] = None,
) -> Dict[str, Any]:
"""Show a progress message to the user, without user input allowed."""
return {
"type": RESULT_TYPE_SHOW_PROGRESS,
"flow_id": self.flow_id,
"handler": self.handler,
"step_id": step_id,
"progress_action": progress_action,
"description_placeholders": description_placeholders,
}
@callback
def async_show_progress_done(self, *, next_step_id: str) -> Dict[str, Any]:
"""Mark the progress done."""
return {
"type": RESULT_TYPE_SHOW_PROGRESS_DONE,
"flow_id": self.flow_id,
"handler": self.handler,
"step_id": next_step_id,
}
@callback @callback
def _create_abort_data( def _create_abort_data(

View file

@ -285,6 +285,76 @@ async def test_external_step(hass, manager):
assert result["title"] == "Hello" assert result["title"] == "Hello"
async def test_show_progress(hass, manager):
"""Test show progress logic."""
manager.hass = hass
@manager.mock_reg_handler("test")
class TestFlow(data_entry_flow.FlowHandler):
VERSION = 5
data = None
task_one_done = False
async def async_step_init(self, user_input=None):
if not user_input:
if not self.task_one_done:
self.task_one_done = True
progress_action = "task_one"
else:
progress_action = "task_two"
return self.async_show_progress(
step_id="init",
progress_action=progress_action,
)
self.data = user_input
return self.async_show_progress_done(next_step_id="finish")
async def async_step_finish(self, user_input=None):
return self.async_create_entry(title=self.data["title"], data=self.data)
events = async_capture_events(
hass, data_entry_flow.EVENT_DATA_ENTRY_FLOW_PROGRESSED
)
result = await manager.async_init("test")
assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS
assert result["progress_action"] == "task_one"
assert len(manager.async_progress()) == 1
# Mimic task one done and moving to task two
# Called by integrations: `hass.config_entries.flow.async_configure(…)`
result = await manager.async_configure(result["flow_id"])
assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS
assert result["progress_action"] == "task_two"
await hass.async_block_till_done()
assert len(events) == 1
assert events[0].data == {
"handler": "test",
"flow_id": result["flow_id"],
"refresh": True,
}
# Mimic task two done and continuing step
# Called by integrations: `hass.config_entries.flow.async_configure(…)`
result = await manager.async_configure(result["flow_id"], {"title": "Hello"})
assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS_DONE
await hass.async_block_till_done()
assert len(events) == 2
assert events[1].data == {
"handler": "test",
"flow_id": result["flow_id"],
"refresh": True,
}
# Frontend refreshes the flow
result = await manager.async_configure(result["flow_id"])
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "Hello"
async def test_abort_flow_exception(manager): async def test_abort_flow_exception(manager):
"""Test that the AbortFlow exception works.""" """Test that the AbortFlow exception works."""