diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
index 4486d91a..53b62bfb 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
@@ -404,6 +404,9 @@ def interstitial(process_instance_id: int):
last_task = spiff_task
processor.do_engine_steps(execution_strategy_name="one_at_a_time")
spiff_task = processor.next_task()
+ # Note, this has to be done in case someone leaves the page,
+ # which can cancel this function before saving.
+ processor.save() # Fixme - maybe find a way not to do this on every method?
return
# return Response(get_data(), mimetype='text/event-stream')
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py
index 4ef3e013..6158d1df 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py
@@ -331,6 +331,15 @@ class WorkflowExecutionService:
# execution_strategy.spiff_run
# spiff.[some_run_task_method]
def run_and_save(self, exit_at: None = None, save: bool = False) -> None:
+ """Do_engine_steps."""
+ with safe_assertion(ProcessInstanceLockService.has_lock(self.process_instance_model.id)) as tripped:
+ if tripped:
+ raise AssertionError(
+ "The current thread has not obtained a lock for this process"
+ f" instance ({self.process_instance_model.id})."
+ )
+
+ try:
self.bpmn_process_instance.refresh_waiting_tasks()
# TODO: implicit re-entrant locks here `with_dequeued`
@@ -339,9 +348,17 @@ class WorkflowExecutionService:
if self.bpmn_process_instance.is_completed():
self.process_instance_completer(self.bpmn_process_instance)
- self.execution_strategy.spiff_run(self.bpmn_process_instance, exit_at)
- if self.bpmn_process_instance.is_completed():
- self.process_instance_completer(self.bpmn_process_instance)
+ self.process_bpmn_messages()
+ self.queue_waiting_receive_messages()
+ except SpiffWorkflowException as swe:
+ raise ApiError.from_workflow_exception("task_error", str(swe), swe) from swe
+
+ finally:
+ self.execution_strategy.save(self.bpmn_process_instance)
+ db.session.commit()
+
+ if save:
+ self.process_instance_saver()
def process_bpmn_messages(self) -> None:
"""Process_bpmn_messages."""
diff --git a/spiffworkflow-frontend/src/components/InstructionsForEndUser.tsx b/spiffworkflow-frontend/src/components/InstructionsForEndUser.tsx
new file mode 100644
index 00000000..a53f6c55
--- /dev/null
+++ b/spiffworkflow-frontend/src/components/InstructionsForEndUser.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+// @ts-ignore
+import MDEditor from '@uiw/react-md-editor';
+
+export default function InstructionsForEndUser({ task }: any) {
+ if (!task) {
+ return null;
+ }
+ let instructions = '';
+ console.log("I was passed a task: ", task);
+ const { properties } = task;
+ const { instructionsForEndUser } = properties;
+ if (instructionsForEndUser) {
+ instructions = instructionsForEndUser;
+ }
+ return (
+
+ {/*
+ https://www.npmjs.com/package/@uiw/react-md-editor switches to dark mode by default by respecting @media (prefers-color-scheme: dark)
+ This makes it look like our site is broken, so until the rest of the site supports dark mode, turn off dark mode for this component.
+ */}
+
+
+
+
+ );
+}
diff --git a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx
index ea6d61d6..9f8a94bd 100644
--- a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx
@@ -1,12 +1,16 @@
import { useEffect, useState } from 'react';
-import { useParams } from 'react-router-dom';
+import {useNavigate, useParams} from 'react-router-dom';
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { BACKEND_BASE_URL } from '../config';
import { getBasicHeaders } from '../services/HttpService';
+import {Task} from '../interfaces';
+import InstructionsForEndUser from '../components/InstructionsForEndUser';
export default function ProcessInterstitial() {
const [data, setData] = useState([]);
+ const [lastTask, setLastTask] = useState(null);
const params = useParams();
+ const navigate = useNavigate();
useEffect(() => {
fetchEventSource(
@@ -15,12 +19,53 @@ export default function ProcessInterstitial() {
headers: getBasicHeaders(),
onmessage(ev) {
console.log(data, ev.data);
- const parsedData = JSON.parse(ev.data);
- setData((data) => [...data, parsedData]);
+ const task = JSON.parse(ev.data);
+ setData((prevData) => [...prevData, task]);
+ setLastTask(task);
+ },
+ onclose() {
+ console.log("Connection Closed by the Server");
},
}
);
}, []);
- return The last streamed item was: {data}
;
+ useEffect(() => {
+ // Added this seperate use effect so that the timer interval will be cleared if
+ // we end up redirecting back to the TaskShow page.
+ if (lastTask && ['User Task', 'Manual Task'].includes(lastTask.type)) {
+ const timerId = setInterval(() => {
+ navigate(`/tasks/${lastTask.process_instance_id}/${lastTask.id}`);
+ }, 1000);
+ return () => clearInterval(timerId);
+ }
+ return undefined;
+ }, [lastTask]);
+
+
+ return (
+
+
React - Display a list of items
+
+
+
+ Task Title |
+
+
+
+ {data &&
+ data.map((d) => (
+
+ {d.title} |
+ {d.state} |
+ {d.type} |
+
+
+ |
+
+ ))}
+
+
+
+ );
}
diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx
index 5561efad..de15aa3c 100644
--- a/spiffworkflow-frontend/src/routes/TaskShow.tsx
+++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx
@@ -23,6 +23,7 @@ import useAPIError from '../hooks/UseApiError';
import { modifyProcessIdentifierForPathParam } from '../helpers';
import { ProcessInstanceTask } from '../interfaces';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
+import InstructionsForEndUser from '../components/InstructionsForEndUser';
// TODO: move this somewhere else
function TypeAheadWidget({
@@ -402,27 +403,6 @@ export default function TaskShow() {
);
};
- const instructionsElement = () => {
- if (!task) {
- return null;
- }
- let instructions = '';
- if (task.properties.instructionsForEndUser) {
- instructions = task.properties.instructionsForEndUser;
- }
- return (
-
- {/*
- https://www.npmjs.com/package/@uiw/react-md-editor switches to dark mode by default by respecting @media (prefers-color-scheme: dark)
- This makes it look like our site is broken, so until the rest of the site supports dark mode, turn off dark mode for this component.
- */}
-
-
-
-
- );
- };
-
if (task) {
let statusString = '';
if (task.state !== 'READY') {
@@ -446,7 +426,7 @@ export default function TaskShow() {
Task: {task.title} ({task.process_model_display_name}){statusString}
- {instructionsElement()}
+
{formElement()}
);