Assure we save after every chunk of tasks get completed on the interstital endpoint.
Fix a merge screwup in the workflow execution service Add an instructions for end user component so we can reuse the logic in multiple places. Split up the intersitital page so that it clears out the interval timer.
This commit is contained in:
parent
19d4fb9981
commit
47a3332ed3
|
@ -404,6 +404,9 @@ def interstitial(process_instance_id: int):
|
||||||
last_task = spiff_task
|
last_task = spiff_task
|
||||||
processor.do_engine_steps(execution_strategy_name="one_at_a_time")
|
processor.do_engine_steps(execution_strategy_name="one_at_a_time")
|
||||||
spiff_task = processor.next_task()
|
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
|
||||||
|
|
||||||
# return Response(get_data(), mimetype='text/event-stream')
|
# return Response(get_data(), mimetype='text/event-stream')
|
||||||
|
|
|
@ -331,6 +331,15 @@ class WorkflowExecutionService:
|
||||||
# execution_strategy.spiff_run
|
# execution_strategy.spiff_run
|
||||||
# spiff.[some_run_task_method]
|
# spiff.[some_run_task_method]
|
||||||
def run_and_save(self, exit_at: None = None, save: bool = False) -> None:
|
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()
|
self.bpmn_process_instance.refresh_waiting_tasks()
|
||||||
|
|
||||||
# TODO: implicit re-entrant locks here `with_dequeued`
|
# TODO: implicit re-entrant locks here `with_dequeued`
|
||||||
|
@ -339,9 +348,17 @@ class WorkflowExecutionService:
|
||||||
if self.bpmn_process_instance.is_completed():
|
if self.bpmn_process_instance.is_completed():
|
||||||
self.process_instance_completer(self.bpmn_process_instance)
|
self.process_instance_completer(self.bpmn_process_instance)
|
||||||
|
|
||||||
self.execution_strategy.spiff_run(self.bpmn_process_instance, exit_at)
|
self.process_bpmn_messages()
|
||||||
if self.bpmn_process_instance.is_completed():
|
self.queue_waiting_receive_messages()
|
||||||
self.process_instance_completer(self.bpmn_process_instance)
|
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:
|
def process_bpmn_messages(self) -> None:
|
||||||
"""Process_bpmn_messages."""
|
"""Process_bpmn_messages."""
|
||||||
|
|
|
@ -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 (
|
||||||
|
<div className="markdown">
|
||||||
|
{/*
|
||||||
|
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.
|
||||||
|
*/}
|
||||||
|
<div data-color-mode="light">
|
||||||
|
<MDEditor.Markdown source={instructions} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,12 +1,16 @@
|
||||||
import { useEffect, useState } from 'react';
|
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 { fetchEventSource } from '@microsoft/fetch-event-source';
|
||||||
import { BACKEND_BASE_URL } from '../config';
|
import { BACKEND_BASE_URL } from '../config';
|
||||||
import { getBasicHeaders } from '../services/HttpService';
|
import { getBasicHeaders } from '../services/HttpService';
|
||||||
|
import {Task} from '../interfaces';
|
||||||
|
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
||||||
|
|
||||||
export default function ProcessInterstitial() {
|
export default function ProcessInterstitial() {
|
||||||
const [data, setData] = useState<any[]>([]);
|
const [data, setData] = useState<any[]>([]);
|
||||||
|
const [lastTask, setLastTask] = useState<any>(null);
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchEventSource(
|
fetchEventSource(
|
||||||
|
@ -15,12 +19,53 @@ export default function ProcessInterstitial() {
|
||||||
headers: getBasicHeaders(),
|
headers: getBasicHeaders(),
|
||||||
onmessage(ev) {
|
onmessage(ev) {
|
||||||
console.log(data, ev.data);
|
console.log(data, ev.data);
|
||||||
const parsedData = JSON.parse(ev.data);
|
const task = JSON.parse(ev.data);
|
||||||
setData((data) => [...data, parsedData]);
|
setData((prevData) => [...prevData, task]);
|
||||||
|
setLastTask(task);
|
||||||
|
},
|
||||||
|
onclose() {
|
||||||
|
console.log("Connection Closed by the Server");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div className="App">The last streamed item was: {data}</div>;
|
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 (
|
||||||
|
<div className="container">
|
||||||
|
<h3 className="p-3 text-center">React - Display a list of items</h3>
|
||||||
|
<table className="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Task Title</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data &&
|
||||||
|
data.map((d) => (
|
||||||
|
<tr key={d.id}>
|
||||||
|
<td>{d.title}</td>
|
||||||
|
<td>{d.state}</td>
|
||||||
|
<td>{d.type}</td>
|
||||||
|
<td>
|
||||||
|
<InstructionsForEndUser task={d} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import useAPIError from '../hooks/UseApiError';
|
||||||
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
||||||
import { ProcessInstanceTask } from '../interfaces';
|
import { ProcessInstanceTask } from '../interfaces';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
|
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
||||||
|
|
||||||
// TODO: move this somewhere else
|
// TODO: move this somewhere else
|
||||||
function TypeAheadWidget({
|
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 (
|
|
||||||
<div className="markdown">
|
|
||||||
{/*
|
|
||||||
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.
|
|
||||||
*/}
|
|
||||||
<div data-color-mode="light">
|
|
||||||
<MDEditor.Markdown source={instructions} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (task) {
|
if (task) {
|
||||||
let statusString = '';
|
let statusString = '';
|
||||||
if (task.state !== 'READY') {
|
if (task.state !== 'READY') {
|
||||||
|
@ -446,7 +426,7 @@ export default function TaskShow() {
|
||||||
<h3>
|
<h3>
|
||||||
Task: {task.title} ({task.process_model_display_name}){statusString}
|
Task: {task.title} ({task.process_model_display_name}){statusString}
|
||||||
</h3>
|
</h3>
|
||||||
{instructionsElement()}
|
<InstructionsForEndUser task={task} />
|
||||||
{formElement()}
|
{formElement()}
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue