Merge pull request #72 from sartography/feature/view_call_activity_diagram
Feature/view call activity diagram
This commit is contained in:
commit
aca3c0abbb
|
@ -699,6 +699,12 @@ paths:
|
||||||
description: The unique id of an existing process instance.
|
description: The unique id of an existing process instance.
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
|
- name: process_identifier
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The identifier of the process to use for the diagram. Useful for displaying the diagram for a call activity.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Process Instances
|
- Process Instances
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
"""Spiff_step_details."""
|
"""Process_instance_metadata."""
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from flask_bpmn.models.db import db
|
from flask_bpmn.models.db import db
|
||||||
|
|
|
@ -8,6 +8,10 @@ from marshmallow import INCLUDE
|
||||||
from sqlalchemy import UniqueConstraint
|
from sqlalchemy import UniqueConstraint
|
||||||
|
|
||||||
|
|
||||||
|
class SpecReferenceNotFoundError(Exception):
|
||||||
|
"""SpecReferenceNotFoundError."""
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
class SpecReference:
|
class SpecReference:
|
||||||
"""File Reference Information.
|
"""File Reference Information.
|
||||||
|
|
|
@ -21,7 +21,7 @@ class SpiffStepDetailsModel(SpiffworkflowBaseDBModel):
|
||||||
ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore
|
ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore
|
||||||
)
|
)
|
||||||
spiff_step: int = db.Column(db.Integer, nullable=False)
|
spiff_step: int = db.Column(db.Integer, nullable=False)
|
||||||
task_json: str = deferred(db.Column(db.JSON, nullable=False)) # type: ignore
|
task_json: dict = deferred(db.Column(db.JSON, nullable=False)) # type: ignore
|
||||||
timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False)
|
timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False)
|
||||||
completed_by_user_id: int = db.Column(db.Integer, nullable=True)
|
completed_by_user_id: int = db.Column(db.Integer, nullable=True)
|
||||||
lane_assignment_id: Optional[int] = db.Column(
|
lane_assignment_id: Optional[int] = db.Column(
|
||||||
|
|
|
@ -108,7 +108,7 @@ class Task:
|
||||||
multi_instance_type: Union[MultiInstanceType, None] = None,
|
multi_instance_type: Union[MultiInstanceType, None] = None,
|
||||||
multi_instance_count: str = "",
|
multi_instance_count: str = "",
|
||||||
multi_instance_index: str = "",
|
multi_instance_index: str = "",
|
||||||
process_name: str = "",
|
process_identifier: str = "",
|
||||||
properties: Union[dict, None] = None,
|
properties: Union[dict, None] = None,
|
||||||
process_instance_id: Union[int, None] = None,
|
process_instance_id: Union[int, None] = None,
|
||||||
process_instance_status: Union[str, None] = None,
|
process_instance_status: Union[str, None] = None,
|
||||||
|
@ -118,6 +118,7 @@ class Task:
|
||||||
form_schema: Union[str, None] = None,
|
form_schema: Union[str, None] = None,
|
||||||
form_ui_schema: Union[str, None] = None,
|
form_ui_schema: Union[str, None] = None,
|
||||||
parent: Optional[str] = None,
|
parent: Optional[str] = None,
|
||||||
|
call_activity_process_identifier: Optional[str] = None,
|
||||||
):
|
):
|
||||||
"""__init__."""
|
"""__init__."""
|
||||||
self.id = id
|
self.id = id
|
||||||
|
@ -129,6 +130,7 @@ class Task:
|
||||||
self.documentation = documentation
|
self.documentation = documentation
|
||||||
self.lane = lane
|
self.lane = lane
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
self.call_activity_process_identifier = call_activity_process_identifier
|
||||||
|
|
||||||
self.data = data
|
self.data = data
|
||||||
if self.data is None:
|
if self.data is None:
|
||||||
|
@ -151,7 +153,7 @@ class Task:
|
||||||
self.multi_instance_index = (
|
self.multi_instance_index = (
|
||||||
multi_instance_index # And the index of the currently repeating task.
|
multi_instance_index # And the index of the currently repeating task.
|
||||||
)
|
)
|
||||||
self.process_name = process_name
|
self.process_identifier = process_identifier
|
||||||
|
|
||||||
self.properties = properties # Arbitrary extension properties from BPMN editor.
|
self.properties = properties # Arbitrary extension properties from BPMN editor.
|
||||||
if self.properties is None:
|
if self.properties is None:
|
||||||
|
@ -177,7 +179,7 @@ class Task:
|
||||||
"multi_instance_type": multi_instance_type,
|
"multi_instance_type": multi_instance_type,
|
||||||
"multi_instance_count": self.multi_instance_count,
|
"multi_instance_count": self.multi_instance_count,
|
||||||
"multi_instance_index": self.multi_instance_index,
|
"multi_instance_index": self.multi_instance_index,
|
||||||
"process_name": self.process_name,
|
"process_identifier": self.process_identifier,
|
||||||
"properties": self.properties,
|
"properties": self.properties,
|
||||||
"process_instance_id": self.process_instance_id,
|
"process_instance_id": self.process_instance_id,
|
||||||
"process_instance_status": self.process_instance_status,
|
"process_instance_status": self.process_instance_status,
|
||||||
|
@ -187,6 +189,7 @@ class Task:
|
||||||
"form_schema": self.form_schema,
|
"form_schema": self.form_schema,
|
||||||
"form_ui_schema": self.form_ui_schema,
|
"form_ui_schema": self.form_ui_schema,
|
||||||
"parent": self.parent,
|
"parent": self.parent,
|
||||||
|
"call_activity_process_identifier": self.call_activity_process_identifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -282,7 +285,7 @@ class TaskSchema(Schema):
|
||||||
"multi_instance_type",
|
"multi_instance_type",
|
||||||
"multi_instance_count",
|
"multi_instance_count",
|
||||||
"multi_instance_index",
|
"multi_instance_index",
|
||||||
"process_name",
|
"process_identifier",
|
||||||
"properties",
|
"properties",
|
||||||
"process_instance_id",
|
"process_instance_id",
|
||||||
"form_schema",
|
"form_schema",
|
||||||
|
@ -293,7 +296,7 @@ class TaskSchema(Schema):
|
||||||
documentation = marshmallow.fields.String(required=False, allow_none=True)
|
documentation = marshmallow.fields.String(required=False, allow_none=True)
|
||||||
# form = marshmallow.fields.Nested(FormSchema, required=False, allow_none=True)
|
# form = marshmallow.fields.Nested(FormSchema, required=False, allow_none=True)
|
||||||
title = marshmallow.fields.String(required=False, allow_none=True)
|
title = marshmallow.fields.String(required=False, allow_none=True)
|
||||||
process_name = marshmallow.fields.String(required=False, allow_none=True)
|
process_identifier = marshmallow.fields.String(required=False, allow_none=True)
|
||||||
lane = marshmallow.fields.String(required=False, allow_none=True)
|
lane = marshmallow.fields.String(required=False, allow_none=True)
|
||||||
|
|
||||||
@marshmallow.post_load
|
@marshmallow.post_load
|
||||||
|
|
|
@ -66,6 +66,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
|
||||||
from spiffworkflow_backend.models.secret_model import SecretModel
|
from spiffworkflow_backend.models.secret_model import SecretModel
|
||||||
from spiffworkflow_backend.models.secret_model import SecretModelSchema
|
from spiffworkflow_backend.models.secret_model import SecretModelSchema
|
||||||
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
|
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
|
||||||
|
from spiffworkflow_backend.models.spec_reference import SpecReferenceNotFoundError
|
||||||
from spiffworkflow_backend.models.spec_reference import SpecReferenceSchema
|
from spiffworkflow_backend.models.spec_reference import SpecReferenceSchema
|
||||||
from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel
|
from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel
|
||||||
from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel
|
from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel
|
||||||
|
@ -1073,25 +1074,48 @@ def process_instance_report_column_list() -> flask.wrappers.Response:
|
||||||
|
|
||||||
|
|
||||||
def process_instance_show(
|
def process_instance_show(
|
||||||
modified_process_model_identifier: str, process_instance_id: int
|
modified_process_model_identifier: str,
|
||||||
|
process_instance_id: int,
|
||||||
|
process_identifier: Optional[str] = None,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Create_process_instance."""
|
"""Create_process_instance."""
|
||||||
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
||||||
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
||||||
current_version_control_revision = GitService.get_current_revision()
|
current_version_control_revision = GitService.get_current_revision()
|
||||||
process_model = get_process_model(process_model_identifier)
|
|
||||||
|
|
||||||
if process_model.primary_file_name:
|
process_model_with_diagram = None
|
||||||
|
name_of_file_with_diagram = None
|
||||||
|
if process_identifier:
|
||||||
|
spec_reference = SpecReferenceCache.query.filter_by(
|
||||||
|
identifier=process_identifier
|
||||||
|
).first()
|
||||||
|
if spec_reference is None:
|
||||||
|
raise SpecReferenceNotFoundError(
|
||||||
|
f"Could not find given process identifier in the cache: {process_identifier}"
|
||||||
|
)
|
||||||
|
|
||||||
|
process_model_with_diagram = ProcessModelService.get_process_model(
|
||||||
|
spec_reference.process_model_id
|
||||||
|
)
|
||||||
|
name_of_file_with_diagram = spec_reference.file_name
|
||||||
|
else:
|
||||||
|
process_model_with_diagram = get_process_model(process_model_identifier)
|
||||||
|
if process_model_with_diagram.primary_file_name:
|
||||||
|
name_of_file_with_diagram = process_model_with_diagram.primary_file_name
|
||||||
|
|
||||||
|
if process_model_with_diagram and name_of_file_with_diagram:
|
||||||
if (
|
if (
|
||||||
process_instance.bpmn_version_control_identifier
|
process_instance.bpmn_version_control_identifier
|
||||||
== current_version_control_revision
|
== current_version_control_revision
|
||||||
):
|
):
|
||||||
bpmn_xml_file_contents = SpecFileService.get_data(
|
bpmn_xml_file_contents = SpecFileService.get_data(
|
||||||
process_model, process_model.primary_file_name
|
process_model_with_diagram, name_of_file_with_diagram
|
||||||
).decode("utf-8")
|
).decode("utf-8")
|
||||||
else:
|
else:
|
||||||
bpmn_xml_file_contents = GitService.get_instance_file_contents_for_revision(
|
bpmn_xml_file_contents = GitService.get_instance_file_contents_for_revision(
|
||||||
process_model, process_instance.bpmn_version_control_identifier
|
process_model_with_diagram,
|
||||||
|
process_instance.bpmn_version_control_identifier,
|
||||||
|
file_name=name_of_file_with_diagram,
|
||||||
)
|
)
|
||||||
process_instance.bpmn_xml_file_contents = bpmn_xml_file_contents
|
process_instance.bpmn_xml_file_contents = bpmn_xml_file_contents
|
||||||
|
|
||||||
|
@ -1409,7 +1433,8 @@ def process_instance_task_list(
|
||||||
)
|
)
|
||||||
if step_detail is not None and process_instance.bpmn_json is not None:
|
if step_detail is not None and process_instance.bpmn_json is not None:
|
||||||
bpmn_json = json.loads(process_instance.bpmn_json)
|
bpmn_json = json.loads(process_instance.bpmn_json)
|
||||||
bpmn_json["tasks"] = step_detail.task_json
|
bpmn_json["tasks"] = step_detail.task_json["tasks"]
|
||||||
|
bpmn_json["subprocesses"] = step_detail.task_json["subprocesses"]
|
||||||
process_instance.bpmn_json = json.dumps(bpmn_json)
|
process_instance.bpmn_json = json.dumps(bpmn_json)
|
||||||
|
|
||||||
processor = ProcessInstanceProcessor(process_instance)
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
|
|
|
@ -46,18 +46,24 @@ class GitService:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_instance_file_contents_for_revision(
|
def get_instance_file_contents_for_revision(
|
||||||
cls, process_model: ProcessModelInfo, revision: str
|
cls,
|
||||||
|
process_model: ProcessModelInfo,
|
||||||
|
revision: str,
|
||||||
|
file_name: Optional[str] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Get_instance_file_contents_for_revision."""
|
"""Get_instance_file_contents_for_revision."""
|
||||||
bpmn_spec_absolute_dir = current_app.config["BPMN_SPEC_ABSOLUTE_DIR"]
|
bpmn_spec_absolute_dir = current_app.config["BPMN_SPEC_ABSOLUTE_DIR"]
|
||||||
process_model_relative_path = FileSystemService.process_model_relative_path(
|
process_model_relative_path = FileSystemService.process_model_relative_path(
|
||||||
process_model
|
process_model
|
||||||
)
|
)
|
||||||
|
file_name_to_use = file_name
|
||||||
|
if file_name_to_use is None:
|
||||||
|
file_name_to_use = process_model.primary_file_name
|
||||||
with FileSystemService.cd(bpmn_spec_absolute_dir):
|
with FileSystemService.cd(bpmn_spec_absolute_dir):
|
||||||
shell_command = [
|
shell_command = [
|
||||||
"git",
|
"git",
|
||||||
"show",
|
"show",
|
||||||
f"{revision}:{process_model_relative_path}/{process_model.primary_file_name}",
|
f"{revision}:{process_model_relative_path}/{file_name_to_use}",
|
||||||
]
|
]
|
||||||
return cls.run_shell_command_to_get_stdout(shell_command)
|
return cls.run_shell_command_to_get_stdout(shell_command)
|
||||||
|
|
||||||
|
|
|
@ -551,7 +551,7 @@ class ProcessInstanceProcessor:
|
||||||
"""SaveSpiffStepDetails."""
|
"""SaveSpiffStepDetails."""
|
||||||
bpmn_json = self.serialize()
|
bpmn_json = self.serialize()
|
||||||
wf_json = json.loads(bpmn_json)
|
wf_json = json.loads(bpmn_json)
|
||||||
task_json = wf_json["tasks"]
|
task_json = {"tasks": wf_json["tasks"], "subprocesses": wf_json["subprocesses"]}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"process_instance_id": self.process_instance_model.id,
|
"process_instance_id": self.process_instance_model.id,
|
||||||
|
|
|
@ -302,6 +302,11 @@ class ProcessInstanceService:
|
||||||
else:
|
else:
|
||||||
lane = None
|
lane = None
|
||||||
|
|
||||||
|
if hasattr(spiff_task.task_spec, "spec"):
|
||||||
|
call_activity_process_identifier = spiff_task.task_spec.spec
|
||||||
|
else:
|
||||||
|
call_activity_process_identifier = None
|
||||||
|
|
||||||
parent_id = None
|
parent_id = None
|
||||||
if spiff_task.parent:
|
if spiff_task.parent:
|
||||||
parent_id = spiff_task.parent.id
|
parent_id = spiff_task.parent.id
|
||||||
|
@ -316,9 +321,10 @@ class ProcessInstanceService:
|
||||||
multi_instance_type=mi_type,
|
multi_instance_type=mi_type,
|
||||||
multi_instance_count=info["mi_count"],
|
multi_instance_count=info["mi_count"],
|
||||||
multi_instance_index=info["mi_index"],
|
multi_instance_index=info["mi_index"],
|
||||||
process_name=spiff_task.task_spec._wf_spec.description,
|
process_identifier=spiff_task.task_spec._wf_spec.name,
|
||||||
properties=props,
|
properties=props,
|
||||||
parent=parent_id,
|
parent=parent_id,
|
||||||
|
call_activity_process_identifier=call_activity_process_identifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
return task
|
return task
|
||||||
|
|
|
@ -171,13 +171,18 @@ class SpecFileService(FileSystemService):
|
||||||
ref.is_primary = True
|
ref.is_primary = True
|
||||||
|
|
||||||
if ref.is_primary:
|
if ref.is_primary:
|
||||||
ProcessModelService.update_process_model(
|
update_hash = {}
|
||||||
process_model_info,
|
if not process_model_info.primary_file_name:
|
||||||
{
|
update_hash["primary_process_id"] = ref.identifier
|
||||||
"primary_process_id": ref.identifier,
|
update_hash["primary_file_name"] = file_name
|
||||||
"primary_file_name": file_name,
|
elif file_name == process_model_info.primary_file_name:
|
||||||
},
|
update_hash["primary_process_id"] = ref.identifier
|
||||||
)
|
|
||||||
|
if len(update_hash) > 0:
|
||||||
|
ProcessModelService.update_process_model(
|
||||||
|
process_model_info,
|
||||||
|
update_hash,
|
||||||
|
)
|
||||||
SpecFileService.update_caches(ref)
|
SpecFileService.update_caches(ref)
|
||||||
return file
|
return file
|
||||||
|
|
||||||
|
|
|
@ -1167,6 +1167,60 @@ class TestProcessApi(BaseTest):
|
||||||
xml_file_contents = f_open.read()
|
xml_file_contents = f_open.read()
|
||||||
assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents
|
assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents
|
||||||
|
|
||||||
|
def test_process_instance_show_with_specified_process_identifier(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
client: FlaskClient,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_super_admin_user: UserModel,
|
||||||
|
) -> None:
|
||||||
|
"""Test_process_instance_show_with_specified_process_identifier."""
|
||||||
|
process_model_id = "call_activity_nested"
|
||||||
|
process_model_identifier = self.create_group_and_model_with_bpmn(
|
||||||
|
client=client,
|
||||||
|
user=with_super_admin_user,
|
||||||
|
process_group_id="test_group_two",
|
||||||
|
process_model_id=process_model_id,
|
||||||
|
bpmn_file_location="call_activity_nested",
|
||||||
|
)
|
||||||
|
spec_reference = SpecReferenceCache.query.filter_by(
|
||||||
|
identifier="Level2b"
|
||||||
|
).first()
|
||||||
|
assert spec_reference
|
||||||
|
modified_process_model_identifier = (
|
||||||
|
self.modify_process_identifier_for_path_param(process_model_identifier)
|
||||||
|
)
|
||||||
|
headers = self.logged_in_headers(with_super_admin_user)
|
||||||
|
create_response = self.create_process_instance_from_process_model_id(
|
||||||
|
client, process_model_identifier, headers
|
||||||
|
)
|
||||||
|
assert create_response.json is not None
|
||||||
|
assert create_response.status_code == 201
|
||||||
|
process_instance_id = create_response.json["id"]
|
||||||
|
client.post(
|
||||||
|
f"/v1.0/process-instances/{modified_process_model_identifier}/{process_instance_id}/run",
|
||||||
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
|
)
|
||||||
|
show_response = client.get(
|
||||||
|
f"/v1.0/process-instances/{modified_process_model_identifier}/{process_instance_id}?process_identifier={spec_reference.identifier}",
|
||||||
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
|
)
|
||||||
|
assert show_response.json is not None
|
||||||
|
assert show_response.status_code == 200
|
||||||
|
file_system_root = FileSystemService.root_path()
|
||||||
|
process_instance_file_path = (
|
||||||
|
f"{file_system_root}/{process_model_identifier}/{process_model_id}.bpmn"
|
||||||
|
)
|
||||||
|
with open(process_instance_file_path) as f_open:
|
||||||
|
xml_file_contents = f_open.read()
|
||||||
|
assert show_response.json["bpmn_xml_file_contents"] != xml_file_contents
|
||||||
|
spec_reference_file_path = os.path.join(
|
||||||
|
file_system_root, spec_reference.relative_path
|
||||||
|
)
|
||||||
|
with open(spec_reference_file_path) as f_open:
|
||||||
|
xml_file_contents = f_open.read()
|
||||||
|
assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents
|
||||||
|
|
||||||
def test_message_start_when_starting_process_instance(
|
def test_message_start_when_starting_process_instance(
|
||||||
self,
|
self,
|
||||||
app: Flask,
|
app: Flask,
|
||||||
|
|
|
@ -300,8 +300,13 @@ export default function ProcessInstanceListTable({
|
||||||
|
|
||||||
checkFiltersAndRun();
|
checkFiltersAndRun();
|
||||||
if (autoReload) {
|
if (autoReload) {
|
||||||
refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, checkFiltersAndRun);
|
return refreshAtInterval(
|
||||||
|
REFRESH_INTERVAL,
|
||||||
|
REFRESH_TIMEOUT,
|
||||||
|
checkFiltersAndRun
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}, [
|
}, [
|
||||||
autoReload,
|
autoReload,
|
||||||
searchParams,
|
searchParams,
|
||||||
|
|
|
@ -58,14 +58,14 @@ import HttpService from '../services/HttpService';
|
||||||
import ButtonWithConfirmation from './ButtonWithConfirmation';
|
import ButtonWithConfirmation from './ButtonWithConfirmation';
|
||||||
import { makeid } from '../helpers';
|
import { makeid } from '../helpers';
|
||||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||||
import { PermissionsToCheck } from '../interfaces';
|
import { PermissionsToCheck, ProcessInstanceTask } from '../interfaces';
|
||||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
processModelId: string;
|
processModelId: string;
|
||||||
diagramType: string;
|
diagramType: string;
|
||||||
readyOrWaitingBpmnTaskIds?: string[] | null;
|
readyOrWaitingProcessInstanceTasks?: ProcessInstanceTask[] | null;
|
||||||
completedTasksBpmnIds?: string[] | null;
|
completedProcessInstanceTasks?: ProcessInstanceTask[] | null;
|
||||||
saveDiagram?: (..._args: any[]) => any;
|
saveDiagram?: (..._args: any[]) => any;
|
||||||
onDeleteFile?: (..._args: any[]) => any;
|
onDeleteFile?: (..._args: any[]) => any;
|
||||||
onSetPrimaryFile?: (..._args: any[]) => any;
|
onSetPrimaryFile?: (..._args: any[]) => any;
|
||||||
|
@ -88,8 +88,8 @@ type OwnProps = {
|
||||||
export default function ReactDiagramEditor({
|
export default function ReactDiagramEditor({
|
||||||
processModelId,
|
processModelId,
|
||||||
diagramType,
|
diagramType,
|
||||||
readyOrWaitingBpmnTaskIds,
|
readyOrWaitingProcessInstanceTasks,
|
||||||
completedTasksBpmnIds,
|
completedProcessInstanceTasks,
|
||||||
saveDiagram,
|
saveDiagram,
|
||||||
onDeleteFile,
|
onDeleteFile,
|
||||||
onSetPrimaryFile,
|
onSetPrimaryFile,
|
||||||
|
@ -227,7 +227,9 @@ export default function ReactDiagramEditor({
|
||||||
|
|
||||||
function handleElementClick(event: any) {
|
function handleElementClick(event: any) {
|
||||||
if (onElementClick) {
|
if (onElementClick) {
|
||||||
onElementClick(event.element);
|
const canvas = diagramModeler.get('canvas');
|
||||||
|
const rootElement = canvas.getRootElement();
|
||||||
|
onElementClick(event.element, rootElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,12 +352,15 @@ export default function ReactDiagramEditor({
|
||||||
|
|
||||||
function highlightBpmnIoElement(
|
function highlightBpmnIoElement(
|
||||||
canvas: any,
|
canvas: any,
|
||||||
taskBpmnId: string,
|
processInstanceTask: ProcessInstanceTask,
|
||||||
bpmnIoClassName: string
|
bpmnIoClassName: string,
|
||||||
|
bpmnRootElementId: string
|
||||||
) {
|
) {
|
||||||
if (checkTaskCanBeHighlighted(taskBpmnId)) {
|
if (checkTaskCanBeHighlighted(processInstanceTask.name)) {
|
||||||
try {
|
try {
|
||||||
canvas.addMarker(taskBpmnId, bpmnIoClassName);
|
if (bpmnRootElementId === processInstanceTask.process_identifier) {
|
||||||
|
canvas.addMarker(processInstanceTask.name, bpmnIoClassName);
|
||||||
|
}
|
||||||
} catch (bpmnIoError: any) {
|
} catch (bpmnIoError: any) {
|
||||||
// the task list also contains task for processes called from call activities which will
|
// the task list also contains task for processes called from call activities which will
|
||||||
// not exist in this diagram so just ignore them for now.
|
// not exist in this diagram so just ignore them for now.
|
||||||
|
@ -394,21 +399,25 @@ export default function ReactDiagramEditor({
|
||||||
// highlighting a field
|
// highlighting a field
|
||||||
// Option 3 at:
|
// Option 3 at:
|
||||||
// https://github.com/bpmn-io/bpmn-js-examples/tree/master/colors
|
// https://github.com/bpmn-io/bpmn-js-examples/tree/master/colors
|
||||||
if (readyOrWaitingBpmnTaskIds) {
|
if (readyOrWaitingProcessInstanceTasks) {
|
||||||
readyOrWaitingBpmnTaskIds.forEach((readyOrWaitingBpmnTaskId) => {
|
const rootElement = canvas.getRootElement();
|
||||||
|
readyOrWaitingProcessInstanceTasks.forEach((readyOrWaitingBpmnTask) => {
|
||||||
highlightBpmnIoElement(
|
highlightBpmnIoElement(
|
||||||
canvas,
|
canvas,
|
||||||
readyOrWaitingBpmnTaskId,
|
readyOrWaitingBpmnTask,
|
||||||
'active-task-highlight'
|
'active-task-highlight',
|
||||||
|
rootElement.id
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (completedTasksBpmnIds) {
|
if (completedProcessInstanceTasks) {
|
||||||
completedTasksBpmnIds.forEach((completedTaskBpmnId) => {
|
const rootElement = canvas.getRootElement();
|
||||||
|
completedProcessInstanceTasks.forEach((completedTask) => {
|
||||||
highlightBpmnIoElement(
|
highlightBpmnIoElement(
|
||||||
canvas,
|
canvas,
|
||||||
completedTaskBpmnId,
|
completedTask,
|
||||||
'completed-task-highlight'
|
'completed-task-highlight',
|
||||||
|
rootElement.id
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -484,8 +493,8 @@ export default function ReactDiagramEditor({
|
||||||
diagramType,
|
diagramType,
|
||||||
diagramXML,
|
diagramXML,
|
||||||
diagramXMLString,
|
diagramXMLString,
|
||||||
readyOrWaitingBpmnTaskIds,
|
readyOrWaitingProcessInstanceTasks,
|
||||||
completedTasksBpmnIds,
|
completedProcessInstanceTasks,
|
||||||
fileName,
|
fileName,
|
||||||
performingXmlUpdates,
|
performingXmlUpdates,
|
||||||
processModelId,
|
processModelId,
|
||||||
|
|
|
@ -41,7 +41,7 @@ export default function MyOpenProcesses() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
getTasks();
|
getTasks();
|
||||||
refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
const buildTable = () => {
|
const buildTable = () => {
|
||||||
|
|
|
@ -41,7 +41,7 @@ export default function TasksWaitingForMyGroups() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
getTasks();
|
getTasks();
|
||||||
refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
const buildTable = () => {
|
const buildTable = () => {
|
||||||
|
|
|
@ -208,5 +208,8 @@ export const refreshAtInterval = (
|
||||||
() => clearInterval(intervalRef),
|
() => clearInterval(intervalRef),
|
||||||
timeout * 1000
|
timeout * 1000
|
||||||
);
|
);
|
||||||
return [intervalRef, timeoutRef];
|
return () => {
|
||||||
|
clearInterval(intervalRef);
|
||||||
|
clearTimeout(timeoutRef);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,13 @@ export interface RecentProcessModel {
|
||||||
processModelDisplayName: string;
|
processModelDisplayName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProcessInstanceTask {
|
||||||
|
id: string;
|
||||||
|
state: string;
|
||||||
|
process_identifier: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProcessReference {
|
export interface ProcessReference {
|
||||||
name: string; // The process or decision Display name.
|
name: string; // The process or decision Display name.
|
||||||
identifier: string; // The unique id of the process
|
identifier: string; // The unique id of the process
|
||||||
|
@ -39,6 +46,7 @@ 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;
|
||||||
|
spiff_step?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessageCorrelationProperties {
|
export interface MessageCorrelationProperties {
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { useContext, useEffect, useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
import Editor from '@monaco-editor/react';
|
import Editor from '@monaco-editor/react';
|
||||||
import { useParams, useNavigate, Link } from 'react-router-dom';
|
import {
|
||||||
|
useParams,
|
||||||
|
useNavigate,
|
||||||
|
Link,
|
||||||
|
useSearchParams,
|
||||||
|
} from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
TrashCan,
|
TrashCan,
|
||||||
StopOutline,
|
StopOutline,
|
||||||
|
@ -34,15 +39,21 @@ import {
|
||||||
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
||||||
import ErrorContext from '../contexts/ErrorContext';
|
import ErrorContext from '../contexts/ErrorContext';
|
||||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||||
import { PermissionsToCheck } from '../interfaces';
|
import {
|
||||||
|
PermissionsToCheck,
|
||||||
|
ProcessInstance,
|
||||||
|
ProcessInstanceTask,
|
||||||
|
} from '../interfaces';
|
||||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||||
|
|
||||||
export default function ProcessInstanceShow() {
|
export default function ProcessInstanceShow() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const [processInstance, setProcessInstance] = useState(null);
|
const [processInstance, setProcessInstance] =
|
||||||
const [tasks, setTasks] = useState<Array<object> | null>(null);
|
useState<ProcessInstance | null>(null);
|
||||||
|
const [tasks, setTasks] = useState<ProcessInstanceTask[] | null>(null);
|
||||||
const [tasksCallHadError, setTasksCallHadError] = useState<boolean>(false);
|
const [tasksCallHadError, setTasksCallHadError] = useState<boolean>(false);
|
||||||
const [taskToDisplay, setTaskToDisplay] = useState<object | null>(null);
|
const [taskToDisplay, setTaskToDisplay] = useState<object | null>(null);
|
||||||
const [taskDataToDisplay, setTaskDataToDisplay] = useState<string>('');
|
const [taskDataToDisplay, setTaskDataToDisplay] = useState<string>('');
|
||||||
|
@ -80,8 +91,13 @@ export default function ProcessInstanceShow() {
|
||||||
const processTaskFailure = () => {
|
const processTaskFailure = () => {
|
||||||
setTasksCallHadError(true);
|
setTasksCallHadError(true);
|
||||||
};
|
};
|
||||||
|
let queryParams = '';
|
||||||
|
const processIdentifier = searchParams.get('process_identifier');
|
||||||
|
if (processIdentifier) {
|
||||||
|
queryParams = `?process_identifier=${processIdentifier}`;
|
||||||
|
}
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}`,
|
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}${queryParams}`,
|
||||||
successCallback: setProcessInstance,
|
successCallback: setProcessInstance,
|
||||||
});
|
});
|
||||||
let taskParams = '?all_tasks=true';
|
let taskParams = '?all_tasks=true';
|
||||||
|
@ -98,7 +114,14 @@ export default function ProcessInstanceShow() {
|
||||||
setTasksCallHadError(true);
|
setTasksCallHadError(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [params, modifiedProcessModelId, permissionsLoaded, ability, targetUris]);
|
}, [
|
||||||
|
params,
|
||||||
|
modifiedProcessModelId,
|
||||||
|
permissionsLoaded,
|
||||||
|
ability,
|
||||||
|
targetUris,
|
||||||
|
searchParams,
|
||||||
|
]);
|
||||||
|
|
||||||
const deleteProcessInstance = () => {
|
const deleteProcessInstance = () => {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
|
@ -140,12 +163,12 @@ export default function ProcessInstanceShow() {
|
||||||
const getTaskIds = () => {
|
const getTaskIds = () => {
|
||||||
const taskIds = { completed: [], readyOrWaiting: [] };
|
const taskIds = { completed: [], readyOrWaiting: [] };
|
||||||
if (tasks) {
|
if (tasks) {
|
||||||
tasks.forEach(function getUserTasksElement(task: any) {
|
tasks.forEach(function getUserTasksElement(task: ProcessInstanceTask) {
|
||||||
if (task.state === 'COMPLETED') {
|
if (task.state === 'COMPLETED') {
|
||||||
(taskIds.completed as any).push(task.name);
|
(taskIds.completed as any).push(task);
|
||||||
}
|
}
|
||||||
if (task.state === 'READY' || task.state === 'WAITING') {
|
if (task.state === 'READY' || task.state === 'WAITING') {
|
||||||
(taskIds.readyOrWaiting as any).push(task.name);
|
(taskIds.readyOrWaiting as any).push(task);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -175,15 +198,18 @@ export default function ProcessInstanceShow() {
|
||||||
label: any,
|
label: any,
|
||||||
distance: number
|
distance: number
|
||||||
) => {
|
) => {
|
||||||
|
const processIdentifier = searchParams.get('process_identifier');
|
||||||
|
let queryParams = '';
|
||||||
|
if (processIdentifier) {
|
||||||
|
queryParams = `?process_identifier=${processIdentifier}`;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
reloadDocument
|
reloadDocument
|
||||||
data-qa="process-instance-step-link"
|
data-qa="process-instance-step-link"
|
||||||
to={`/admin/process-instances/${
|
to={`/admin/process-instances/${params.process_model_id}/${
|
||||||
params.process_model_id
|
params.process_instance_id
|
||||||
}/process-instances/${params.process_instance_id}/${
|
}/${currentSpiffStep(processInstanceToUse) + distance}${queryParams}`}
|
||||||
currentSpiffStep(processInstanceToUse) + distance
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -364,10 +390,15 @@ export default function ProcessInstanceShow() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickedDiagramTask = (shapeElement: any) => {
|
const handleClickedDiagramTask = (
|
||||||
|
shapeElement: any,
|
||||||
|
bpmnRootElement: any
|
||||||
|
) => {
|
||||||
if (tasks) {
|
if (tasks) {
|
||||||
const matchingTask: any = tasks.find(
|
const matchingTask: any = tasks.find(
|
||||||
(task: any) => task.name === shapeElement.id
|
(task: any) =>
|
||||||
|
task.name === shapeElement.id &&
|
||||||
|
task.process_identifier === bpmnRootElement.id
|
||||||
);
|
);
|
||||||
if (matchingTask) {
|
if (matchingTask) {
|
||||||
setTaskToDisplay(matchingTask);
|
setTaskToDisplay(matchingTask);
|
||||||
|
@ -471,19 +502,28 @@ export default function ProcessInstanceShow() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (task.type === 'Call Activity') {
|
||||||
|
buttons.push(
|
||||||
|
<Link
|
||||||
|
data-qa="go-to-call-activity-result"
|
||||||
|
to={`${window.location.pathname}?process_identifier=${task.call_activity_process_identifier}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
View Call Activity Diagram
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (canEditTaskData(task)) {
|
if (canEditTaskData(task)) {
|
||||||
if (editingTaskData) {
|
if (editingTaskData) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<Button
|
<Button data-qa="save-task-data-button" onClick={saveTaskData}>
|
||||||
data-qa="create-script-unit-test-button"
|
|
||||||
onClick={saveTaskData}
|
|
||||||
>
|
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<Button
|
<Button
|
||||||
data-qa="create-script-unit-test-button"
|
data-qa="cancel-task-data-edit-button"
|
||||||
onClick={cancelEditingTaskData}
|
onClick={cancelEditingTaskData}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
@ -492,7 +532,7 @@ export default function ProcessInstanceShow() {
|
||||||
} else {
|
} else {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<Button
|
<Button
|
||||||
data-qa="create-script-unit-test-button"
|
data-qa="edit-task-data-button"
|
||||||
onClick={() => setEditingTaskData(true)}
|
onClick={() => setEditingTaskData(true)}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
|
@ -622,8 +662,8 @@ export default function ProcessInstanceShow() {
|
||||||
processModelId={processModelId || ''}
|
processModelId={processModelId || ''}
|
||||||
diagramXML={processInstanceToUse.bpmn_xml_file_contents || ''}
|
diagramXML={processInstanceToUse.bpmn_xml_file_contents || ''}
|
||||||
fileName={processInstanceToUse.bpmn_xml_file_contents || ''}
|
fileName={processInstanceToUse.bpmn_xml_file_contents || ''}
|
||||||
readyOrWaitingBpmnTaskIds={taskIds.readyOrWaiting}
|
readyOrWaitingProcessInstanceTasks={taskIds.readyOrWaiting}
|
||||||
completedTasksBpmnIds={taskIds.completed}
|
completedProcessInstanceTasks={taskIds.completed}
|
||||||
diagramType="readonly"
|
diagramType="readonly"
|
||||||
onElementClick={handleClickedDiagramTask}
|
onElementClick={handleClickedDiagramTask}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue