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:
commit
acf4b5596e
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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>
|
|
@ -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'])
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue