Squashed 'spiffworkflow-frontend/' changes from c22d22ba5..4fd946be4

4fd946be4 Merging main
453bdb246 run_pyl
b5a8ff01b Needed an additional check for empty correlation keys - which on a RECEIVE message, should always match anything.
b583f84cc lint w/ burnettk
fd4d7d13b removed some unused code from task and fixed the logs table a bit w/ burnettk
6f17e71e6 avoid using task-data endpoint for task data and only use it to get tasks based on spiff step instead
669c29595 removed task-data endpoints since we no longer need them w/ burnettk
5f25fffe0 added api to get task data and do not return from task data list anymore w/ burnettk
373c4f184 Merge remote-tracking branch 'origin/main' into feature/message_fixes
e40c12ac8 turn on sentry detailed tracing for task-data w/ burnettk
d7861aae2 Merge branch 'main' into feature/message_fixes
0a9f2480d work in progress - * Link between message instance and correlations is now a link table and many-to-many relationships as recommended by SQLAlchemy * Use the correlation keys, not the process id when accepting api messages.

git-subtree-dir: spiffworkflow-frontend
git-subtree-split: 4fd946be4ff716bfd1664aca8d75cb6f709b53eb
This commit is contained in:
burnettk 2023-02-27 14:59:38 -05:00
parent ce1e54dced
commit 7a5f961b2b
7 changed files with 162 additions and 83 deletions

4
package-lock.json generated
View File

@ -8065,7 +8065,7 @@
},
"node_modules/bpmn-js-spiffworkflow": {
"version": "0.0.8",
"resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#aca23dc56e5d37aa1ed0a3cf11acb55f76a36da7",
"resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#f1f008e3e39be43b016718fca6a38b248ab4ecf7",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.4",
@ -38214,7 +38214,7 @@
}
},
"bpmn-js-spiffworkflow": {
"version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#aca23dc56e5d37aa1ed0a3cf11acb55f76a36da7",
"version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#f1f008e3e39be43b016718fca6a38b248ab4ecf7",
"from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main",
"requires": {
"inherits": "^2.0.4",

View File

@ -17,7 +17,7 @@ export const useUriListForPermissions = () => {
processInstanceResumePath: `/v1.0/process-instance-resume/${params.process_model_id}/${params.process_instance_id}`,
processInstanceSuspendPath: `/v1.0/process-instance-suspend/${params.process_model_id}/${params.process_instance_id}`,
processInstanceResetPath: `/v1.0/process-instance-reset/${params.process_model_id}/${params.process_instance_id}`,
processInstanceTaskListDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`,
processInstanceTaskDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`,
processInstanceSendEventPath: `/v1.0/send-event/${params.process_model_id}/${params.process_instance_id}`,
processInstanceCompleteTaskPath: `/v1.0/complete-task/${params.process_model_id}/${params.process_instance_id}`,
processInstanceTaskListPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}/task-info`,

View File

@ -112,12 +112,13 @@ export interface MessageInstance {
process_model_identifier: string;
process_model_display_name: string;
process_instance_id: number;
message_identifier: string;
name: string;
message_type: string;
failure_cause: string;
status: string;
created_at_in_seconds: number;
message_correlations?: MessageCorrelations;
correlation_keys: any;
}
export interface ReportFilter {

View File

@ -64,17 +64,13 @@ export default function MessageInstanceList() {
open={!!messageInstanceForModal}
passiveModal
onRequestClose={handleCorrelationDisplayClose}
modalHeading={`Message ${messageInstanceForModal.id} (${messageInstanceForModal.message_identifier}) ${messageInstanceForModal.message_type} data:`}
modalHeading={`Message ${messageInstanceForModal.id} (${messageInstanceForModal.name}) ${messageInstanceForModal.message_type} data:`}
modalLabel="Details"
>
{failureCausePre}
<p>Correlations:</p>
<pre>
{JSON.stringify(
messageInstanceForModal.message_correlations,
null,
2
)}
{JSON.stringify(messageInstanceForModal.correlation_keys, null, 2)}
</pre>
</Modal>
);
@ -95,21 +91,27 @@ export default function MessageInstanceList() {
</>
);
}
let processLink = <span>External Call</span>;
let instanceLink = <span />;
if (row.process_instance_id != null) {
processLink = FormatProcessModelDisplayName(row);
instanceLink = (
<Link
data-qa="process-instance-show-link"
to={`/admin/process-instances/${modifyProcessIdentifierForPathParam(
row.process_model_identifier
)}/${row.process_instance_id}`}
>
{row.process_instance_id}
</Link>
);
}
return (
<tr key={row.id}>
<td>{row.id}</td>
<td>{FormatProcessModelDisplayName(row)}</td>
<td>
<Link
data-qa="process-instance-show-link"
to={`/admin/process-instances/${modifyProcessIdentifierForPathParam(
row.process_model_identifier
)}/${row.process_instance_id}`}
>
{row.process_instance_id}
</Link>
</td>
<td>{row.message_identifier}</td>
<td>{processLink}</td>
<td>{instanceLink}</td>
<td>{row.name}</td>
<td>{row.message_type}</td>
<td>
<Button

View File

@ -6,11 +6,11 @@ import PaginationForTable from '../components/PaginationForTable';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import {
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
convertSecondsToFormattedDateTime,
} from '../helpers';
import HttpService from '../services/HttpService';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import UserService from '../services/UserService';
type OwnProps = {
variant: string;
@ -29,6 +29,8 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) {
processInstanceShowPageBaseUrl = `/admin/process-instances/${params.process_model_id}`;
}
const userEmail = UserService.getUserEmail();
useEffect(() => {
const setProcessInstanceLogListFromResult = (result: any) => {
setProcessInstanceLogs(result.results);
@ -46,56 +48,91 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) {
isDetailedView,
]);
const getTableRow = (row: any) => {
const tableRow = [];
const taskNameCell = (
<td>
{row.bpmn_task_name ||
(row.bpmn_task_type === 'Default Start Event'
? 'Process Started'
: '') ||
(row.bpmn_task_type === 'End Event' ? 'Process Ended' : '')}
</td>
);
if (isDetailedView) {
tableRow.push(
<>
<td data-qa="paginated-entity-id">{row.id}</td>
<td>{row.bpmn_process_identifier}</td>
{taskNameCell}
</>
);
} else {
tableRow.push(
<>
{taskNameCell}
<td>{row.bpmn_process_identifier}</td>
</>
);
}
if (isDetailedView) {
tableRow.push(
<>
<td>{row.bpmn_task_type}</td>
<td>{row.message}</td>
<td>{row.username === userEmail ? 'me 🔥' : row.username}</td>
</>
);
}
tableRow.push(
<td>
<Link
data-qa="process-instance-show-link"
to={`${processInstanceShowPageBaseUrl}/${row.process_instance_id}/${row.spiff_step}`}
>
{convertSecondsToFormattedDateTime(row.timestamp)}
</Link>
</td>
);
return <tr key={row.id}>{tableRow}</tr>;
};
const buildTable = () => {
const rows = processInstanceLogs.map((row) => {
const rowToUse = row as any;
return (
<tr key={rowToUse.id}>
<td data-qa="paginated-entity-id">{rowToUse.id}</td>
<td>
{rowToUse.bpmn_task_name ||
(rowToUse.bpmn_task_type === 'Default Start Event'
? 'Process Started'
: '') ||
(rowToUse.bpmn_task_type === 'End Event' ? 'Process Ended' : '')}
</td>
{isDetailedView && (
<>
<td>{rowToUse.message}</td>
<td>{rowToUse.bpmn_task_identifier}</td>
<td>{rowToUse.bpmn_task_type}</td>
</>
)}
<td>{rowToUse.bpmn_process_identifier}</td>
<td>{rowToUse.username}</td>
<td>
<Link
data-qa="process-instance-show-link"
to={`${processInstanceShowPageBaseUrl}/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`}
>
{convertSecondsToFormattedDateTime(rowToUse.timestamp)}
</Link>
</td>
</tr>
);
return getTableRow(row);
});
const tableHeaders = [];
if (isDetailedView) {
tableHeaders.push(
<>
<th>Id</th>
<th>Bpmn Process</th>
<th>Task Name</th>
</>
);
} else {
tableHeaders.push(
<>
<th>Event</th>
<th>Bpmn Process</th>
</>
);
}
if (isDetailedView) {
tableHeaders.push(
<>
<th>Task Type</th>
<th>Message</th>
<th>User</th>
</>
);
}
tableHeaders.push(<th>Timestamp</th>);
return (
<Table size="lg">
<thead>
<tr>
<th>Id</th>
<th>Task Name</th>
{isDetailedView && (
<>
<th>Message</th>
<th>Task Identifier</th>
<th>Task Type</th>
</>
)}
<th>Bpmn Process Identifier</th>
<th>User</th>
<th>Timestamp</th>
</tr>
<tr>{tableHeaders}</tr>
</thead>
<tbody>{rows}</tbody>
</Table>

View File

@ -27,6 +27,7 @@ import {
Modal,
Dropdown,
Stack,
Loading,
// @ts-ignore
} from '@carbon/react';
import { Can } from '@casl/react';
@ -65,8 +66,12 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
useState<ProcessInstance | null>(null);
const [tasks, setTasks] = useState<ProcessInstanceTask[] | null>(null);
const [tasksCallHadError, setTasksCallHadError] = useState<boolean>(false);
const [taskToDisplay, setTaskToDisplay] = useState<object | null>(null);
const [taskToDisplay, setTaskToDisplay] =
useState<ProcessInstanceTask | null>(null);
const [taskDataToDisplay, setTaskDataToDisplay] = useState<string>('');
const [showTaskDataLoading, setShowTaskDataLoading] =
useState<boolean>(false);
const [processDataToDisplay, setProcessDataToDisplay] =
useState<ProcessData | null>(null);
const [editingTaskData, setEditingTaskData] = useState<boolean>(false);
@ -99,7 +104,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
[targetUris.messageInstanceListPath]: ['GET'],
[targetUris.processInstanceActionPath]: ['DELETE'],
[targetUris.processInstanceLogListPath]: ['GET'],
[targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'],
[targetUris.processInstanceTaskDataPath]: ['GET', 'PUT'],
[targetUris.processInstanceSendEventPath]: ['POST'],
[targetUris.processInstanceCompleteTaskPath]: ['POST'],
[targetUris.processModelShowPath]: ['PUT'],
@ -145,9 +150,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
taskParams = `${taskParams}&spiff_step=${params.spiff_step}`;
}
let taskPath = '';
if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) {
taskPath = `${targetUris.processInstanceTaskListDataPath}${taskParams}`;
} else if (ability.can('GET', taskListPath)) {
if (ability.can('GET', taskListPath)) {
taskPath = `${taskListPath}${taskParams}`;
}
if (taskPath) {
@ -557,11 +560,33 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
return <div />;
};
const initializeTaskDataToDisplay = (task: any) => {
if (task == null) {
const processTaskResult = (result: ProcessInstanceTask) => {
if (result == null) {
setTaskDataToDisplay('');
} else {
setTaskDataToDisplay(JSON.stringify(task.data, null, 2));
setTaskDataToDisplay(JSON.stringify(result.data, null, 2));
}
setShowTaskDataLoading(false);
};
const initializeTaskDataToDisplay = (task: ProcessInstanceTask | null) => {
if (
task &&
task.state === 'COMPLETED' &&
ability.can('GET', targetUris.processInstanceTaskDataPath)
) {
setShowTaskDataLoading(true);
HttpService.makeCallToBackend({
path: `${targetUris.processInstanceTaskDataPath}/${task.task_spiff_step}`,
httpMethod: 'GET',
successCallback: processTaskResult,
failureCallback: (error: any) => {
setTaskDataToDisplay(`ERROR: ${error.message}`);
setShowTaskDataLoading(false);
},
});
} else {
setTaskDataToDisplay('');
}
};
@ -668,7 +693,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const canEditTaskData = (task: any) => {
return (
processInstance &&
ability.can('PUT', targetUris.processInstanceTaskListDataPath) &&
ability.can('PUT', targetUris.processInstanceTaskDataPath) &&
isCurrentTask(task) &&
processInstance.status === 'suspended' &&
showingLastSpiffStep()
@ -742,8 +767,13 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const saveTaskDataResult = (_: any) => {
setEditingTaskData(false);
const dataObject = taskDataStringToObject(taskDataToDisplay);
const taskToDisplayCopy = { ...taskToDisplay, data: dataObject }; // spread operator
setTaskToDisplay(taskToDisplayCopy);
if (taskToDisplay) {
const taskToDisplayCopy: ProcessInstanceTask = {
...taskToDisplay,
data: dataObject,
}; // spread operator
setTaskToDisplay(taskToDisplayCopy);
}
refreshPage();
};
@ -757,7 +787,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
// taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };
HttpService.makeCallToBackend({
path: `${targetUris.processInstanceTaskListDataPath}/${taskToUse.id}`,
path: `${targetUris.processInstanceTaskDataPath}/${taskToUse.id}`,
httpMethod: 'PUT',
successCallback: saveTaskDataResult,
failureCallback: addError,
@ -901,6 +931,10 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
};
const taskDataContainer = () => {
let taskDataClassName = '';
if (taskDataToDisplay.startsWith('ERROR:')) {
taskDataClassName = 'failure-string';
}
return editingTaskData ? (
<Editor
height={600}
@ -910,7 +944,12 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
onChange={(value) => setTaskDataToDisplay(value || '')}
/>
) : (
<pre>{taskDataToDisplay}</pre>
<>
{showTaskDataLoading ? (
<Loading className="some-class" withOverlay={false} small />
) : null}
<pre className={taskDataClassName}>{taskDataToDisplay}</pre>
</>
);
};

View File

@ -32,10 +32,10 @@ export default function TaskShow() {
useEffect(() => {
const processResult = (result: ProcessInstanceTask) => {
setTask(result);
const url = `/task-data/${modifyProcessIdentifierForPathParam(
const url = `/v1.0/process-instances/for-me/${modifyProcessIdentifierForPathParam(
result.process_model_identifier
)}/${params.process_instance_id}`;
// if user is unauthorized to get task-data then don't do anything
)}/${params.process_instance_id}/task-info`;
// if user is unauthorized to get process-instance task-info then don't do anything
// Checking like this so we can dynamically create the url with the correct process model
// instead of passing the process model identifier in through the params
HttpService.makeCallToBackend({