Merge pull request #428 from sartography/bug/555_timer_event

We were not correctly handing the possibility of there only being a w…
This commit is contained in:
Mike Cullerton 2021-11-30 13:44:37 -05:00 committed by GitHub
commit acf4b5596e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 100 additions and 7 deletions

View File

@ -398,6 +398,14 @@ class WorkflowProcessor(object):
# Get a list of all ready tasks # Get a list of all ready tasks
ready_tasks = self.bpmn_workflow.get_tasks(SpiffTask.READY) ready_tasks = self.bpmn_workflow.get_tasks(SpiffTask.READY)
if len(ready_tasks) == 0:
# If no ready tasks exist, check for a waiting task.
waiting_tasks = self.bpmn_workflow.get_tasks(SpiffTask.WAITING)
if len(waiting_tasks) > 0:
return waiting_tasks[0]
else:
return # We have not tasks to return.
# Get a list of all completed user tasks (Non engine tasks) # Get a list of all completed user tasks (Non engine tasks)
completed_user_tasks = self.completed_user_tasks() completed_user_tasks = self.completed_user_tasks()

View File

@ -177,14 +177,17 @@ class WorkflowService(object):
WorkflowService.populate_form_with_random_data(task, task_api, required_only) WorkflowService.populate_form_with_random_data(task, task_api, required_only)
processor.complete_task(task) processor.complete_task(task)
if test_until == task.task_spec.name: if test_until == task.task_spec.name:
raise ApiError.from_task("validation_break", raise ApiError.from_task(
f"The validation has been exited early on task '{task.task_spec.name}' and was parented by ", "validation_break",
task.parent) f"The validation has been exited early on task '{task.task_spec.name}' "
f"and was parented by ",
task.parent)
count += 1 count += 1
if count >= 100: if count >= 100:
raise ApiError.from_task(code='validation_loop', raise ApiError(code='unending_validation',
message=f'There appears to be an infinite loop in the validation. Task is {task.task_spec.description}', message=f'There appears to be no way to complete this workflow,'
task=task) f' halting validation.')
WorkflowService._process_documentation(processor.bpmn_workflow.last_task.parent.parent) WorkflowService._process_documentation(processor.bpmn_workflow.last_task.parent.parent)
except WorkflowException as we: except WorkflowException as we:

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0ilr8m3" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.10.0">
<bpmn:process id="timer" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_1pahvlr</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:manualTask id="Activity_1ybpou3" name="Get Coffee">
<bpmn:incoming>Flow_1pahvlr</bpmn:incoming>
<bpmn:outgoing>Flow_1pvkgnu</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:intermediateCatchEvent id="Event_0bxivgz" name="Take 5">
<bpmn:incoming>Flow_1pvkgnu</bpmn:incoming>
<bpmn:outgoing>Flow_1elbn9u</bpmn:outgoing>
<bpmn:timerEventDefinition id="TimerEventDefinition_1jrn73k">
<bpmn:timeDuration xsi:type="bpmn:tFormalExpression">timedelta(seconds=.25)</bpmn:timeDuration>
</bpmn:timerEventDefinition>
</bpmn:intermediateCatchEvent>
<bpmn:manualTask id="Activity_1rbj8j1" name="Get Back To Work">
<bpmn:incoming>Flow_1elbn9u</bpmn:incoming>
<bpmn:outgoing>Flow_1ekgt3x</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:endEvent id="Event_03w65sk">
<bpmn:incoming>Flow_1ekgt3x</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1ekgt3x" sourceRef="Activity_1rbj8j1" targetRef="Event_03w65sk" />
<bpmn:sequenceFlow id="Flow_1elbn9u" sourceRef="Event_0bxivgz" targetRef="Activity_1rbj8j1" />
<bpmn:sequenceFlow id="Flow_1pvkgnu" sourceRef="Activity_1ybpou3" targetRef="Event_0bxivgz" />
<bpmn:sequenceFlow id="Flow_1pahvlr" sourceRef="StartEvent_1" targetRef="Activity_1ybpou3" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="timer">
<bpmndi:BPMNEdge id="Flow_1pahvlr_di" bpmnElement="Flow_1pahvlr">
<di:waypoint x="215" y="117" />
<di:waypoint x="260" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1pvkgnu_di" bpmnElement="Flow_1pvkgnu">
<di:waypoint x="360" y="117" />
<di:waypoint x="422" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1elbn9u_di" bpmnElement="Flow_1elbn9u">
<di:waypoint x="458" y="117" />
<di:waypoint x="530" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1ekgt3x_di" bpmnElement="Flow_1ekgt3x">
<di:waypoint x="630" y="117" />
<di:waypoint x="682" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0tjl9dd_di" bpmnElement="Activity_1ybpou3">
<dc:Bounds x="260" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_15zi5m4_di" bpmnElement="Activity_1rbj8j1">
<dc:Bounds x="530" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_03w65sk_di" bpmnElement="Event_03w65sk">
<dc:Bounds x="682" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0bljn9v_di" bpmnElement="Event_0bxivgz">
<dc:Bounds x="422" y="99" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="424" y="142" width="33" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -15,4 +15,4 @@ class TestWorkflowInfiniteLoop(BaseTest):
spec_model = self.load_test_spec('infinite_loop') spec_model = self.load_test_spec('infinite_loop')
rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers())
json_data = json.loads(rv.get_data(as_text=True)) json_data = json.loads(rv.get_data(as_text=True))
self.assertIn('There appears to be an infinite loop', json_data[0]['message']) self.assertIn('There appears to be no way to complete this workflow, halting validation.', json_data[0]['message'])

View File

@ -164,6 +164,20 @@ class TestWorkflowProcessor(BaseTest):
self.assertIn("FactService", task.data) self.assertIn("FactService", task.data)
self.assertIsInstance(task.task_spec, EndEvent) self.assertIsInstance(task.task_spec, EndEvent)
def test_workflow_processor_returns_waiting_task_if_no_ready_tasks_exist(self):
self.load_example_data()
workflow_spec_model = self.load_test_spec("timer_inline")
study = session.query(StudyModel).first()
processor = self.get_processor(study, workflow_spec_model)
processor.do_engine_steps()
task = processor.next_task()
task.data = {"type": "buzzword"}
processor.complete_task(task)
processor.do_engine_steps()
task = processor.next_task()
self.assertIsNotNone(task)
self.assertEqual(task.state, task.WAITING)
def test_workflow_validation_error_is_properly_raised(self): def test_workflow_validation_error_is_properly_raised(self):
self.load_example_data() self.load_example_data()
workflow_spec_model = self.load_test_spec("invalid_spec") workflow_spec_model = self.load_test_spec("invalid_spec")