do not allow editing task data for process instances that are not suspended and some code cleanup w/ burnettk

This commit is contained in:
jasquat 2022-12-19 11:54:22 -05:00
parent e68d19d8b3
commit a096605047
6 changed files with 78 additions and 64 deletions

View File

@ -1,5 +1,3 @@
from __future__ import with_statement
import logging import logging
from logging.config import fileConfig from logging.config import fileConfig

View File

@ -26,6 +26,10 @@ class ProcessInstanceNotFoundError(Exception):
"""ProcessInstanceNotFoundError.""" """ProcessInstanceNotFoundError."""
class ProcessInstanceTaskDataCannotBeUpdatedError(Exception):
"""ProcessInstanceTaskDataCannotBeUpdatedError."""
class NavigationItemSchema(Schema): class NavigationItemSchema(Schema):
"""NavigationItemSchema.""" """NavigationItemSchema."""

View File

@ -56,6 +56,9 @@ from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSche
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.process_instance import (
ProcessInstanceTaskDataCannotBeUpdatedError,
)
from spiffworkflow_backend.models.process_instance_metadata import ( from spiffworkflow_backend.models.process_instance_metadata import (
ProcessInstanceMetadataModel, ProcessInstanceMetadataModel,
) )
@ -2110,6 +2113,11 @@ def update_task_data(
ProcessInstanceModel.id == int(process_instance_id) ProcessInstanceModel.id == int(process_instance_id)
).first() ).first()
if process_instance: if process_instance:
if process_instance.status != "suspended":
raise ProcessInstanceTaskDataCannotBeUpdatedError(
f"The process instance needs to be suspended to udpate the task-data. It is currently: {process_instance.status}"
)
process_instance_bpmn_json_dict = json.loads(process_instance.bpmn_json) process_instance_bpmn_json_dict = json.loads(process_instance.bpmn_json)
if "new_task_data" in body: if "new_task_data" in body:
new_task_data_str: str = body["new_task_data"] new_task_data_str: str = body["new_task_data"]

View File

@ -100,7 +100,7 @@ class GitService:
branch_name_to_use, branch_name_to_use,
git_username, git_username,
git_email, git_email,
current_app.config['GIT_USER_PASSWORD'] current_app.config["GIT_USER_PASSWORD"],
] ]
return cls.run_shell_command_to_get_stdout(shell_command) return cls.run_shell_command_to_get_stdout(shell_command)

View File

@ -52,6 +52,10 @@ export interface ProcessInstance {
id: number; id: number;
process_model_identifier: string; process_model_identifier: string;
process_model_display_name: string; process_model_display_name: string;
status: string;
start_in_seconds: number | null;
end_in_seconds: number | null;
bpmn_xml_file_contents?: string;
spiff_step?: number; spiff_step?: number;
} }

View File

@ -183,29 +183,23 @@ export default function ProcessInstanceShow() {
return taskIds; return taskIds;
}; };
const currentSpiffStep = (processInstanceToUse: any) => { const currentSpiffStep = () => {
if (typeof params.spiff_step === 'undefined') { if (processInstance && typeof params.spiff_step === 'undefined') {
return processInstanceToUse.spiff_step; return processInstance.spiff_step || 0;
} }
return Number(params.spiff_step); return Number(params.spiff_step);
}; };
const showingFirstSpiffStep = (processInstanceToUse: any) => { const showingFirstSpiffStep = () => {
return currentSpiffStep(processInstanceToUse) === 1; return currentSpiffStep() === 1;
}; };
const showingLastSpiffStep = (processInstanceToUse: any) => { const showingLastSpiffStep = () => {
return ( return processInstance && currentSpiffStep() === processInstance.spiff_step;
currentSpiffStep(processInstanceToUse) === processInstanceToUse.spiff_step
);
}; };
const spiffStepLink = ( const spiffStepLink = (label: any, distance: number) => {
processInstanceToUse: any,
label: any,
distance: number
) => {
const processIdentifier = searchParams.get('process_identifier'); const processIdentifier = searchParams.get('process_identifier');
let queryParams = ''; let queryParams = '';
if (processIdentifier) { if (processIdentifier) {
@ -217,32 +211,35 @@ export default function ProcessInstanceShow() {
data-qa="process-instance-step-link" data-qa="process-instance-step-link"
to={`/admin/process-instances/${params.process_model_id}/${ to={`/admin/process-instances/${params.process_model_id}/${
params.process_instance_id params.process_instance_id
}/${currentSpiffStep(processInstanceToUse) + distance}${queryParams}`} }/${currentSpiffStep() + distance}${queryParams}`}
> >
{label} {label}
</Link> </Link>
); );
}; };
const previousStepLink = (processInstanceToUse: any) => { const previousStepLink = () => {
if (showingFirstSpiffStep(processInstanceToUse)) { if (showingFirstSpiffStep()) {
return null; return null;
} }
return spiffStepLink(processInstanceToUse, <CaretLeft />, -1); return spiffStepLink(<CaretLeft />, -1);
}; };
const nextStepLink = (processInstanceToUse: any) => { const nextStepLink = () => {
if (showingLastSpiffStep(processInstanceToUse)) { if (showingLastSpiffStep()) {
return null; return null;
} }
return spiffStepLink(processInstanceToUse, <CaretRight />, 1); return spiffStepLink(<CaretRight />, 1);
}; };
const getInfoTag = (processInstanceToUse: any) => { const getInfoTag = () => {
if (!processInstance) {
return null;
}
const currentEndDate = convertSecondsToFormattedDateTime( const currentEndDate = convertSecondsToFormattedDateTime(
processInstanceToUse.end_in_seconds processInstance.end_in_seconds || 0
); );
let currentEndDateTag; let currentEndDateTag;
if (currentEndDate) { if (currentEndDate) {
@ -253,7 +250,7 @@ export default function ProcessInstanceShow() {
</Column> </Column>
<Column sm={3} md={3} lg={3} className="grid-date"> <Column sm={3} md={3} lg={3} className="grid-date">
{convertSecondsToFormattedDateTime( {convertSecondsToFormattedDateTime(
processInstanceToUse.end_in_seconds processInstance.end_in_seconds || 0
) || 'N/A'} ) || 'N/A'}
</Column> </Column>
</Grid> </Grid>
@ -261,13 +258,13 @@ export default function ProcessInstanceShow() {
} }
let statusIcon = <InProgress />; let statusIcon = <InProgress />;
if (processInstanceToUse.status === 'suspended') { if (processInstance.status === 'suspended') {
statusIcon = <PauseOutline />; statusIcon = <PauseOutline />;
} else if (processInstanceToUse.status === 'complete') { } else if (processInstance.status === 'complete') {
statusIcon = <Checkmark />; statusIcon = <Checkmark />;
} else if (processInstanceToUse.status === 'terminated') { } else if (processInstance.status === 'terminated') {
statusIcon = <StopOutline />; statusIcon = <StopOutline />;
} else if (processInstanceToUse.status === 'error') { } else if (processInstance.status === 'error') {
statusIcon = <Warning />; statusIcon = <Warning />;
} }
@ -279,7 +276,7 @@ export default function ProcessInstanceShow() {
</Column> </Column>
<Column sm={3} md={3} lg={3} className="grid-date"> <Column sm={3} md={3} lg={3} className="grid-date">
{convertSecondsToFormattedDateTime( {convertSecondsToFormattedDateTime(
processInstanceToUse.start_in_seconds processInstance.start_in_seconds || 0
)} )}
</Column> </Column>
</Grid> </Grid>
@ -290,7 +287,7 @@ export default function ProcessInstanceShow() {
</Column> </Column>
<Column sm={3} md={3} lg={3}> <Column sm={3} md={3} lg={3}>
<Tag type="gray" size="sm" className="span-tag"> <Tag type="gray" size="sm" className="span-tag">
{processInstanceToUse.status} {statusIcon} {processInstance.status} {statusIcon}
</Tag> </Tag>
</Column> </Column>
</Grid> </Grid>
@ -333,11 +330,10 @@ export default function ProcessInstanceShow() {
); );
}; };
const terminateButton = (processInstanceToUse: any) => { const terminateButton = () => {
if ( if (
['complete', 'terminated', 'error'].indexOf( processInstance &&
processInstanceToUse.status !['complete', 'terminated', 'error'].includes(processInstance.status)
) === -1
) { ) {
return ( return (
<ButtonWithConfirmation <ButtonWithConfirmation
@ -345,7 +341,7 @@ export default function ProcessInstanceShow() {
renderIcon={StopOutline} renderIcon={StopOutline}
iconDescription="Terminate" iconDescription="Terminate"
hasIconOnly hasIconOnly
description={`Terminate Process Instance: ${processInstanceToUse.id}`} description={`Terminate Process Instance: ${processInstance.id}`}
onConfirmation={terminateProcessInstance} onConfirmation={terminateProcessInstance}
confirmButtonLabel="Terminate" confirmButtonLabel="Terminate"
/> />
@ -354,11 +350,12 @@ export default function ProcessInstanceShow() {
return <div />; return <div />;
}; };
const suspendButton = (processInstanceToUse: any) => { const suspendButton = () => {
if ( if (
['complete', 'terminated', 'error', 'suspended'].indexOf( processInstance &&
processInstanceToUse.status !['complete', 'terminated', 'error', 'suspended'].includes(
) === -1 processInstance.status
)
) { ) {
return ( return (
<Button <Button
@ -374,8 +371,8 @@ export default function ProcessInstanceShow() {
return <div />; return <div />;
}; };
const resumeButton = (processInstanceToUse: any) => { const resumeButton = () => {
if (processInstanceToUse.status === 'suspended') { if (processInstance && processInstance.status === 'suspended') {
return ( return (
<Button <Button
onClick={resumeProcessInstance} onClick={resumeProcessInstance}
@ -450,9 +447,11 @@ export default function ProcessInstanceShow() {
const canEditTaskData = (task: any) => { const canEditTaskData = (task: any) => {
return ( return (
processInstance &&
ability.can('PUT', targetUris.processInstanceTaskListDataPath) && ability.can('PUT', targetUris.processInstanceTaskListDataPath) &&
task.state === 'READY' && task.state === 'READY' &&
showingLastSpiffStep(processInstance as any) processInstance.status === 'suspended' &&
showingLastSpiffStep()
); );
}; };
@ -475,7 +474,7 @@ export default function ProcessInstanceShow() {
}; };
const saveTaskDataFailure = (result: any) => { const saveTaskDataFailure = (result: any) => {
setErrorMessage({ message: result.toString() }); setErrorMessage({ message: result.message });
}; };
const saveTaskData = () => { const saveTaskData = () => {
@ -591,35 +590,37 @@ export default function ProcessInstanceShow() {
return null; return null;
}; };
const stepsElement = (processInstanceToUse: any) => { const stepsElement = () => {
if (!processInstance) {
return null;
}
return ( return (
<Grid condensed fullWidth> <Grid condensed fullWidth>
<Column sm={3} md={3} lg={3}> <Column sm={3} md={3} lg={3}>
<Stack orientation="horizontal" gap={3} className="smaller-text"> <Stack orientation="horizontal" gap={3} className="smaller-text">
{previousStepLink(processInstanceToUse)} {previousStepLink()}
Step {currentSpiffStep(processInstanceToUse)} of{' '} Step {currentSpiffStep()} of {processInstance.spiff_step}
{processInstanceToUse.spiff_step} {nextStepLink()}
{nextStepLink(processInstanceToUse)}
</Stack> </Stack>
</Column> </Column>
</Grid> </Grid>
); );
}; };
const buttonIcons = (processInstanceToUse: any) => { const buttonIcons = () => {
const elements = []; const elements = [];
if ( if (
ability.can('POST', `${targetUris.processInstanceActionPath}/terminate`) ability.can('POST', `${targetUris.processInstanceActionPath}/terminate`)
) { ) {
elements.push(terminateButton(processInstanceToUse)); elements.push(terminateButton());
} }
if ( if (
ability.can('POST', `${targetUris.processInstanceActionPath}/suspend`) ability.can('POST', `${targetUris.processInstanceActionPath}/suspend`)
) { ) {
elements.push(suspendButton(processInstanceToUse)); elements.push(suspendButton());
} }
if (ability.can('POST', `${targetUris.processInstanceActionPath}/resume`)) { if (ability.can('POST', `${targetUris.processInstanceActionPath}/resume`)) {
elements.push(resumeButton(processInstanceToUse)); elements.push(resumeButton());
} }
if (ability.can('DELETE', targetUris.processInstanceActionPath)) { if (ability.can('DELETE', targetUris.processInstanceActionPath)) {
elements.push( elements.push(
@ -629,7 +630,7 @@ export default function ProcessInstanceShow() {
renderIcon={TrashCan} renderIcon={TrashCan}
iconDescription="Delete" iconDescription="Delete"
hasIconOnly hasIconOnly
description={`Delete Process Instance: ${processInstanceToUse.id}`} description={`Delete Process Instance: ${processInstance}`}
onConfirmation={deleteProcessInstance} onConfirmation={deleteProcessInstance}
confirmButtonLabel="Delete" confirmButtonLabel="Delete"
/> />
@ -639,7 +640,6 @@ export default function ProcessInstanceShow() {
}; };
if (processInstance && (tasks || tasksCallHadError)) { if (processInstance && (tasks || tasksCallHadError)) {
const processInstanceToUse = processInstance as any;
const taskIds = getTaskIds(); const taskIds = getTaskIds();
const processModelId = unModifyProcessIdentifierForPathParam( const processModelId = unModifyProcessIdentifierForPathParam(
params.process_model_id ? params.process_model_id : '' params.process_model_id ? params.process_model_id : ''
@ -655,26 +655,26 @@ export default function ProcessInstanceShow() {
entityType: 'process-model-id', entityType: 'process-model-id',
linkLastItem: true, linkLastItem: true,
}, },
[`Process Instance Id: ${processInstanceToUse.id}`], [`Process Instance Id: ${processInstance.id}`],
]} ]}
/> />
<Stack orientation="horizontal" gap={1}> <Stack orientation="horizontal" gap={1}>
<h1 className="with-icons"> <h1 className="with-icons">
Process Instance Id: {processInstanceToUse.id} Process Instance Id: {processInstance.id}
</h1> </h1>
{buttonIcons(processInstanceToUse)} {buttonIcons()}
</Stack> </Stack>
<br /> <br />
<br /> <br />
{getInfoTag(processInstanceToUse)} {getInfoTag()}
<br /> <br />
{taskDataDisplayArea()} {taskDataDisplayArea()}
{stepsElement(processInstanceToUse)} {stepsElement()}
<br /> <br />
<ReactDiagramEditor <ReactDiagramEditor
processModelId={processModelId || ''} processModelId={processModelId || ''}
diagramXML={processInstanceToUse.bpmn_xml_file_contents || ''} diagramXML={processInstance.bpmn_xml_file_contents || ''}
fileName={processInstanceToUse.bpmn_xml_file_contents || ''} fileName={processInstance.bpmn_xml_file_contents || ''}
readyOrWaitingProcessInstanceTasks={taskIds.readyOrWaiting} readyOrWaitingProcessInstanceTasks={taskIds.readyOrWaiting}
completedProcessInstanceTasks={taskIds.completed} completedProcessInstanceTasks={taskIds.completed}
diagramType="readonly" diagramType="readonly"