From d6684124fdf5b4e514ceb92d6c9a758f05a38135 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 20 Mar 2023 16:51:29 -0400 Subject: [PATCH] use task table for process instance show page. spiff steps are not working yet and neither is data w/ burnettk --- spiffworkflow-backend/conftest.py | 3 +- spiffworkflow-backend/migrations/env.py | 2 + .../{b652c232839f_.py => 4255f548bfb4_.py} | 18 +++-- .../src/spiffworkflow_backend/api.yml | 12 +++ .../models/bpmn_process.py | 7 +- .../routes/process_instances_controller.py | 42 ++++++++-- .../services/process_instance_processor.py | 2 +- .../services/task_service.py | 51 +++++++++--- .../manual_task_with_subprocesses.bpmn | 2 +- .../test_process_to_call.bpmn | 79 +++++++++++++------ .../unit/test_process_instance_processor.py | 21 ++++- .../src/components/ReactDiagramEditor.tsx | 14 ++-- spiffworkflow-frontend/src/interfaces.ts | 13 ++- .../src/routes/ProcessInstanceShow.tsx | 77 +++++++++--------- 14 files changed, 240 insertions(+), 103 deletions(-) rename spiffworkflow-backend/migrations/versions/{b652c232839f_.py => 4255f548bfb4_.py} (97%) diff --git a/spiffworkflow-backend/conftest.py b/spiffworkflow-backend/conftest.py index 304008d0..9d05dfe5 100644 --- a/spiffworkflow-backend/conftest.py +++ b/spiffworkflow-backend/conftest.py @@ -47,7 +47,8 @@ def app() -> Flask: def with_db_and_bpmn_file_cleanup() -> None: """Do it cleanly!""" meta = db.metadata - db.session.execute(db.update(BpmnProcessModel, values={"parent_process_id": None})) + db.session.execute(db.update(BpmnProcessModel, values={"top_level_process_id": None})) + db.session.execute(db.update(BpmnProcessModel, values={"direct_parent_process_id": None})) for table in reversed(meta.sorted_tables): db.session.execute(table.delete()) diff --git a/spiffworkflow-backend/migrations/env.py b/spiffworkflow-backend/migrations/env.py index 630e381a..68feded2 100644 --- a/spiffworkflow-backend/migrations/env.py +++ b/spiffworkflow-backend/migrations/env.py @@ -1,3 +1,5 @@ +from __future__ import with_statement + import logging from logging.config import fileConfig diff --git a/spiffworkflow-backend/migrations/versions/b652c232839f_.py b/spiffworkflow-backend/migrations/versions/4255f548bfb4_.py similarity index 97% rename from spiffworkflow-backend/migrations/versions/b652c232839f_.py rename to spiffworkflow-backend/migrations/versions/4255f548bfb4_.py index dbf5b276..a66c074b 100644 --- a/spiffworkflow-backend/migrations/versions/b652c232839f_.py +++ b/spiffworkflow-backend/migrations/versions/4255f548bfb4_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: b652c232839f +Revision ID: 4255f548bfb4 Revises: -Create Date: 2023-03-17 16:50:32.774216 +Create Date: 2023-03-20 13:00:28.655387 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa from sqlalchemy.dialects import mysql # revision identifiers, used by Alembic. -revision = 'b652c232839f' +revision = '4255f548bfb4' down_revision = None branch_labels = None depends_on = None @@ -115,19 +115,22 @@ def upgrade(): sa.Column('id', sa.Integer(), nullable=False), sa.Column('guid', sa.String(length=36), nullable=True), sa.Column('bpmn_process_definition_id', sa.Integer(), nullable=False), - sa.Column('parent_process_id', sa.Integer(), nullable=True), + sa.Column('top_level_process_id', sa.Integer(), nullable=True), + sa.Column('direct_parent_process_id', sa.Integer(), nullable=True), sa.Column('properties_json', sa.JSON(), nullable=False), sa.Column('json_data_hash', sa.String(length=255), nullable=False), sa.Column('start_in_seconds', sa.DECIMAL(precision=17, scale=6), nullable=True), sa.Column('end_in_seconds', sa.DECIMAL(precision=17, scale=6), nullable=True), sa.ForeignKeyConstraint(['bpmn_process_definition_id'], ['bpmn_process_definition.id'], ), - sa.ForeignKeyConstraint(['parent_process_id'], ['bpmn_process.id'], ), + sa.ForeignKeyConstraint(['direct_parent_process_id'], ['bpmn_process.id'], ), + sa.ForeignKeyConstraint(['top_level_process_id'], ['bpmn_process.id'], ), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('guid') ) op.create_index(op.f('ix_bpmn_process_bpmn_process_definition_id'), 'bpmn_process', ['bpmn_process_definition_id'], unique=False) + op.create_index(op.f('ix_bpmn_process_direct_parent_process_id'), 'bpmn_process', ['direct_parent_process_id'], unique=False) op.create_index(op.f('ix_bpmn_process_json_data_hash'), 'bpmn_process', ['json_data_hash'], unique=False) - op.create_index(op.f('ix_bpmn_process_parent_process_id'), 'bpmn_process', ['parent_process_id'], unique=False) + op.create_index(op.f('ix_bpmn_process_top_level_process_id'), 'bpmn_process', ['top_level_process_id'], unique=False) op.create_table('bpmn_process_definition_relationship', sa.Column('id', sa.Integer(), nullable=False), sa.Column('bpmn_process_definition_parent_id', sa.Integer(), nullable=False), @@ -519,8 +522,9 @@ def downgrade(): op.drop_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_child_id'), table_name='bpmn_process_definition_relationship') op.drop_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_parent_id'), table_name='bpmn_process_definition_relationship') op.drop_table('bpmn_process_definition_relationship') - op.drop_index(op.f('ix_bpmn_process_parent_process_id'), table_name='bpmn_process') + op.drop_index(op.f('ix_bpmn_process_top_level_process_id'), table_name='bpmn_process') op.drop_index(op.f('ix_bpmn_process_json_data_hash'), table_name='bpmn_process') + op.drop_index(op.f('ix_bpmn_process_direct_parent_process_id'), table_name='bpmn_process') op.drop_index(op.f('ix_bpmn_process_bpmn_process_definition_id'), table_name='bpmn_process') op.drop_table('bpmn_process') op.drop_index(op.f('ix_user_service_id'), table_name='user') diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index b71bed93..7cffde1c 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -919,6 +919,12 @@ paths: description: If true, this wil return only the most recent tasks. schema: type: boolean + - name: bpmn_process_guid + in: query + required: false + description: The guid of the bpmn process to get the tasks for. + schema: + type: string get: tags: - Process Instances @@ -972,6 +978,12 @@ paths: description: If true, this wil return only the most recent tasks. schema: type: boolean + - name: bpmn_process_guid + in: query + required: false + description: The guid of the bpmn process to get the tasks for. + schema: + type: string get: tags: - Process Instances diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process.py index 22bdfa70..c38fed7b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process.py @@ -8,6 +8,10 @@ from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel +class BpmnProcessNotFoundError(Exception): + pass + + # properties_json attributes: # "last_task", # guid generated by spiff # "root", # guid generated by spiff @@ -24,7 +28,8 @@ class BpmnProcessModel(SpiffworkflowBaseDBModel): ) bpmn_process_definition = relationship(BpmnProcessDefinitionModel) - parent_process_id: int | None = db.Column(ForeignKey("bpmn_process.id"), nullable=True, index=True) + top_level_process_id: int | None = db.Column(ForeignKey("bpmn_process.id"), nullable=True, index=True) + direct_parent_process_id: int | None = db.Column(ForeignKey("bpmn_process.id"), nullable=True, index=True) properties_json: dict = db.Column(db.JSON, nullable=False) json_data_hash: str = db.Column(db.String(255), nullable=False, index=True) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 59399f2f..f75df6c1 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -1,5 +1,7 @@ """APIs for dealing with process groups, process models, and process instances.""" import base64 +from spiffworkflow_backend.services.task_service import TaskService +from sqlalchemy.orm import aliased from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel import json from typing import Any @@ -560,6 +562,7 @@ def process_instance_task_list_without_task_data_for_me( all_tasks: bool = False, spiff_step: int = 0, most_recent_tasks_only: bool = False, + bpmn_process_guid: Optional[str] = None, ) -> flask.wrappers.Response: """Process_instance_task_list_without_task_data_for_me.""" process_instance = _find_process_instance_for_me_or_raise(process_instance_id) @@ -569,6 +572,7 @@ def process_instance_task_list_without_task_data_for_me( all_tasks=all_tasks, spiff_step=spiff_step, most_recent_tasks_only=most_recent_tasks_only, + bpmn_process_guid=bpmn_process_guid ) @@ -578,6 +582,7 @@ def process_instance_task_list_without_task_data( all_tasks: bool = False, spiff_step: int = 0, most_recent_tasks_only: bool = False, + bpmn_process_guid: Optional[str] = None, ) -> flask.wrappers.Response: """Process_instance_task_list_without_task_data.""" process_instance = _find_process_instance_by_id_or_raise(process_instance_id) @@ -587,12 +592,14 @@ def process_instance_task_list_without_task_data( all_tasks=all_tasks, spiff_step=spiff_step, most_recent_tasks_only=most_recent_tasks_only, + bpmn_process_guid=bpmn_process_guid ) def process_instance_task_list( _modified_process_model_identifier: str, process_instance: ProcessInstanceModel, + bpmn_process_guid: Optional[str] = None, all_tasks: bool = False, spiff_step: int = 0, to_task_guid: Optional[str] = None, @@ -644,9 +651,14 @@ def process_instance_task_list( # state: string; # typename: string; - # calling_subprocess_task_guid: string; - # call_activity_process_bpmn_identifier?: string; + # calling_subprocess_task_guid: string; -> bpmn_process_direct_parent_guid + # call_activity_process_bpmn_identifier?: string; -> bpmn_process_direct_parent_bpmn_identifier + bpmn_process_ids = [] + if bpmn_process_guid: + bpmn_process = BpmnProcessModel.query.filter_by(guid=bpmn_process_guid).first() + bpmn_processes = TaskService.bpmn_process_and_descendants([bpmn_process]) + bpmn_process_ids = [p.id for p in bpmn_processes] task_model_query = db.session.query(TaskModel).filter( TaskModel.process_instance_id == process_instance.id, @@ -664,23 +676,39 @@ def process_instance_task_list( ) task_model_query = task_model_query.filter(TaskModel.end_in_seconds <= to_task_model.end_in_seconds) + bpmn_process_alias = aliased(BpmnProcessModel) + direct_parent_bpmn_process_alias = aliased(BpmnProcessModel) + direct_parent_bpmn_process_definition_alias = aliased(BpmnProcessDefinitionModel) + task_model_query = ( task_model_query.order_by( - ProcessInstanceEventModel.timestamp.desc(), ProcessInstanceEventModel.id.desc() # type: ignore + TaskModel.id.desc() # type: ignore ) .join(TaskDefinitionModel, TaskDefinitionModel.id == TaskModel.task_definition_id) - .join(BpmnProcessModel, BpmnProcessModel.id == TaskModel.bpmn_process_id) + .join(bpmn_process_alias, bpmn_process_alias.id == TaskModel.bpmn_process_id) + .outerjoin(direct_parent_bpmn_process_alias, direct_parent_bpmn_process_alias.id == bpmn_process_alias.direct_parent_process_id) + .outerjoin(direct_parent_bpmn_process_definition_alias, direct_parent_bpmn_process_definition_alias.id == direct_parent_bpmn_process_alias.bpmn_process_definition_id) .join( BpmnProcessDefinitionModel, BpmnProcessDefinitionModel.id == TaskDefinitionModel.bpmn_process_definition_id ) .add_columns( BpmnProcessDefinitionModel.bpmn_identifier.label("bpmn_process_definition_identifier"), # type: ignore BpmnProcessDefinitionModel.bpmn_name.label("bpmn_process_definition_name"), # type: ignore - TaskDefinitionModel.bpmn_identifier.label("task_definition_identifier"), # type: ignore - TaskDefinitionModel.bpmn_name.label("task_definition_name"), # type: ignore - TaskDefinitionModel.typename.label("bpmn_task_type"), # type: ignore + direct_parent_bpmn_process_alias.guid.label("bpmn_process_direct_parent_guid"), + direct_parent_bpmn_process_definition_alias.bpmn_identifier.label("bpmn_process_direct_parent_bpmn_identifier"), + TaskDefinitionModel.bpmn_identifier, + TaskDefinitionModel.bpmn_name, + TaskDefinitionModel.typename, + TaskDefinitionModel.properties_json.label('task_definition_properties_json'), # type: ignore ) ) + + if len(bpmn_process_ids) > 0: + print(f"bpmn_process_ids: {bpmn_process_ids}") + task_model_query = ( + task_model_query.filter(bpmn_process_alias.id.in_(bpmn_process_ids)) + ) + task_models = task_model_query.all() # processor = ProcessInstanceProcessor(process_instance) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index ea59c414..fdd42cb9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -687,7 +687,7 @@ class ProcessInstanceProcessor: single_bpmn_process_dict = cls._get_bpmn_process_dict(bpmn_process, get_tasks=True) spiff_bpmn_process_dict.update(single_bpmn_process_dict) - bpmn_subprocesses = BpmnProcessModel.query.filter_by(parent_process_id=bpmn_process.id).all() + bpmn_subprocesses = BpmnProcessModel.query.filter_by(top_level_process_id=bpmn_process.id).all() bpmn_subprocess_id_to_guid_mappings = {} for bpmn_subprocess in bpmn_subprocesses: bpmn_subprocess_id_to_guid_mappings[bpmn_subprocess.id] = bpmn_subprocess.guid diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 5a03f387..fa902406 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -13,7 +13,7 @@ from SpiffWorkflow.task import TaskStateNames from sqlalchemy.dialects.mysql import insert as mysql_insert from sqlalchemy.dialects.postgresql import insert as postgres_insert -from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel +from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel, BpmnProcessNotFoundError from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @@ -144,7 +144,7 @@ class TaskService: bpmn_process, new_task_models, new_json_data_dicts = cls.add_bpmn_process( bpmn_process_dict=serializer.workflow_to_dict(subprocess), process_instance=process_instance, - bpmn_process_parent=process_instance.bpmn_process, + top_level_process=process_instance.bpmn_process, bpmn_process_guid=subprocess_guid, bpmn_definition_to_task_definitions_mappings=bpmn_definition_to_task_definitions_mappings, spiff_workflow=spiff_workflow, @@ -160,7 +160,7 @@ class TaskService: bpmn_definition_to_task_definitions_mappings: dict, spiff_workflow: BpmnWorkflow, serializer: BpmnWorkflowSerializer, - bpmn_process_parent: Optional[BpmnProcessModel] = None, + top_level_process: Optional[BpmnProcessModel] = None, bpmn_process_guid: Optional[str] = None, ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataDict]]: """This creates and adds a bpmn_process to the Db session. @@ -182,9 +182,9 @@ class TaskService: new_json_data_dicts: dict[str, JsonDataDict] = {} bpmn_process = None - if bpmn_process_parent is not None: + if top_level_process is not None: bpmn_process = BpmnProcessModel.query.filter_by( - parent_process_id=bpmn_process_parent.id, guid=bpmn_process_guid + top_level_process_id=top_level_process.id, guid=bpmn_process_guid ).first() elif process_instance.bpmn_process_id is not None: bpmn_process = process_instance.bpmn_process @@ -194,6 +194,28 @@ class TaskService: bpmn_process_is_new = True bpmn_process = BpmnProcessModel(guid=bpmn_process_guid) + bpmn_process_definition = bpmn_definition_to_task_definitions_mappings[spiff_workflow.spec.name][ + "bpmn_process_definition" + ] + bpmn_process.bpmn_process_definition = bpmn_process_definition + + if top_level_process is not None: + subprocesses = spiff_workflow._get_outermost_workflow().subprocesses + direct_bpmn_process_parent = top_level_process + for subprocess_guid, subprocess in subprocesses.items(): + if subprocess == spiff_workflow.outer_workflow: + direct_bpmn_process_parent = BpmnProcessModel.query.filter_by(guid=str(subprocess_guid)).first() + if direct_bpmn_process_parent is None: + raise BpmnProcessNotFoundError( + f"Could not find bpmn process with guid: {str(subprocess_guid)} " + f"while searching for direct parent process of {bpmn_process_guid}." + ) + + if direct_bpmn_process_parent is None: + raise BpmnProcessNotFoundError(f"Could not find a direct bpmn process parent for guid: {bpmn_process_guid}") + + bpmn_process.direct_parent_process_id = direct_bpmn_process_parent.id + # Point the root id to the Start task instead of the Root task # since we are ignoring the Root task. for task_id, task_properties in tasks.items(): @@ -206,15 +228,10 @@ class TaskService: if bpmn_process_json_data is not None: new_json_data_dicts[bpmn_process_json_data["hash"]] = bpmn_process_json_data - if bpmn_process_parent is None: + if top_level_process is None: process_instance.bpmn_process = bpmn_process - elif bpmn_process.parent_process_id is None: - bpmn_process.parent_process_id = bpmn_process_parent.id - - bpmn_process_definition = bpmn_definition_to_task_definitions_mappings[spiff_workflow.spec.name][ - "bpmn_process_definition" - ] - bpmn_process.bpmn_process_definition = bpmn_process_definition + elif bpmn_process.top_level_process_id is None: + bpmn_process.top_level_process_id = top_level_process.id # Since we bulk insert tasks later we need to add the bpmn_process to the session # to ensure we have an id. @@ -285,6 +302,14 @@ class TaskService: setattr(task_model, task_model_data_column, task_data_hash) return json_data_dict + @classmethod + def bpmn_process_and_descendants(cls, bpmn_processes: list[BpmnProcessModel]) -> list[BpmnProcessModel]: + bpmn_process_ids = [p.id for p in bpmn_processes] + direct_children = BpmnProcessModel.query.filter(BpmnProcessModel.direct_parent_process_id.in_(bpmn_process_ids)).all() # type: ignore + if len(direct_children) > 0: + return bpmn_processes + cls.bpmn_process_and_descendants(direct_children) + return bpmn_processes + @classmethod def _create_task( cls, diff --git a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn index 939c8c0b..680903f5 100644 --- a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn +++ b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn @@ -151,4 +151,4 @@ except: - + \ No newline at end of file diff --git a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn index 299f078e..afda130a 100644 --- a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn +++ b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn @@ -1,38 +1,71 @@ - - Flow_06g687y - - - - Flow_01e21r0 + + Flow_095sred - - - Flow_06g687y - Flow_01e21r0 - set_in_test_process_to_call_script = 1 - + + Flow_1qsx5et + + + Flow_1qsx5et + Flow_095sred + + Flow_12zb3j0 + + + Flow_12zb3j0 + Flow_0iu4d71 + set_in_test_process_to_call_script = 1 + + + Flow_0iu4d71 + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - + + + - - - + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index 70f97328..0b80a46c 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -326,11 +326,11 @@ class TestProcessInstanceProcessor(BaseTest): "manual_task": first_data_set, "top_level_subprocess_script": second_data_set, "top_level_subprocess": second_data_set, - "test_process_to_call_script": third_data_set, + "test_process_to_call_subprocess_script": third_data_set, "top_level_call_activity": third_data_set, "end_event_of_manual_task_model": third_data_set, "top_level_subprocess_script_second": fourth_data_set, - "test_process_to_call_script_second": fourth_data_set, + "test_process_to_call_subprocess_script_second": fourth_data_set, } spiff_tasks_checked_once: list = [] @@ -365,7 +365,7 @@ class TestProcessInstanceProcessor(BaseTest): assert len(all_spiff_tasks) > 1 for spiff_task in all_spiff_tasks: assert spiff_task.state == TaskState.COMPLETED - assert_spiff_task_is_in_process("test_process_to_call_script", "test_process_to_call") + assert_spiff_task_is_in_process("test_process_to_call_subprocess_script", "test_process_to_call_subprocess") assert_spiff_task_is_in_process("top_level_subprocess_script", "top_level_subprocess") assert_spiff_task_is_in_process("top_level_script", "top_level_process") @@ -378,6 +378,21 @@ class TestProcessInstanceProcessor(BaseTest): assert bpmn_process_definition.bpmn_identifier == "test_process_to_call" assert bpmn_process_definition.bpmn_name == "Test Process To Call" + # Check that the direct parent of the called activity subprocess task is the + # name of the process that was called from the activity. + if spiff_task.task_spec.name == "test_process_to_call_subprocess_script": + task_model = TaskModel.query.filter_by(guid=str(spiff_task.id)).first() + assert task_model is not None + bpmn_process = task_model.bpmn_process + assert bpmn_process is not None + bpmn_process_definition = bpmn_process.bpmn_process_definition + assert bpmn_process_definition is not None + assert bpmn_process_definition.bpmn_identifier == "test_process_to_call_subprocess" + assert bpmn_process.direct_parent_process_id is not None + direct_parent_process = BpmnProcessModel.query.filter_by(id=bpmn_process.direct_parent_process_id).first() + assert direct_parent_process is not None + assert direct_parent_process.bpmn_process_definition.bpmn_identifier == "test_process_to_call" + assert processor.get_data() == fifth_data_set def test_does_not_recreate_human_tasks_on_multiple_saves( diff --git a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx index 126f0c4f..e3989c63 100644 --- a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx +++ b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx @@ -60,14 +60,14 @@ import HttpService from '../services/HttpService'; import ButtonWithConfirmation from './ButtonWithConfirmation'; import { getBpmnProcessIdentifiers, makeid } from '../helpers'; import { useUriListForPermissions } from '../hooks/UriListForPermissions'; -import { PermissionsToCheck, ProcessInstanceTask } from '../interfaces'; +import { PermissionsToCheck, Task } from '../interfaces'; import { usePermissionFetcher } from '../hooks/PermissionService'; type OwnProps = { processModelId: string; diagramType: string; - readyOrWaitingProcessInstanceTasks?: ProcessInstanceTask[] | null; - completedProcessInstanceTasks?: ProcessInstanceTask[] | null; + readyOrWaitingProcessInstanceTasks?: Task[] | null; + completedProcessInstanceTasks?: Task[] | null; saveDiagram?: (..._args: any[]) => any; onDeleteFile?: (..._args: any[]) => any; isPrimaryFile?: boolean; @@ -364,18 +364,18 @@ export default function ReactDiagramEditor({ function highlightBpmnIoElement( canvas: any, - processInstanceTask: ProcessInstanceTask, + task: Task, bpmnIoClassName: string, bpmnProcessIdentifiers: string[] ) { - if (checkTaskCanBeHighlighted(processInstanceTask.name)) { + if (checkTaskCanBeHighlighted(task.bpmn_identifier)) { try { if ( bpmnProcessIdentifiers.includes( - processInstanceTask.process_identifier + task.bpmn_process_definition_identifier ) ) { - canvas.addMarker(processInstanceTask.name, bpmnIoClassName); + canvas.addMarker(task.bpmn_identifier, bpmnIoClassName); } } catch (bpmnIoError: any) { // the task list also contains task for processes called from call activities which will diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index aaf11ade..8b61f474 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -21,17 +21,26 @@ export interface RecentProcessModel { processModelDisplayName: string; } +export interface TaskDefinitionPropertiesJson { + spec: string; +} + export interface Task { + id: number; guid: string; bpmn_identifier: string; bpmn_name?: string; - calling_subprocess_task_guid: string; + bpmn_process_direct_parent_guid: string; + bpmn_process_definition_identifier: string; data: any; state: string; typename: string; - call_activity_process_bpmn_identifier?: string; + task_definition_properties_json: TaskDefinitionPropertiesJson; + + // TOOD: DELETE THIS! + task_spiff_step?: number; } export interface ProcessInstanceTask { diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 36c06d23..1b555a04 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -46,7 +46,8 @@ import { ProcessData, ProcessInstance, ProcessInstanceMetadata, - ProcessInstanceTask, + Task, + TaskDefinitionPropertiesJson, } from '../interfaces'; import { usePermissionFetcher } from '../hooks/PermissionService'; import ProcessInstanceClass from '../classes/ProcessInstanceClass'; @@ -64,10 +65,9 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { const [processInstance, setProcessInstance] = useState(null); - const [tasks, setTasks] = useState(null); + const [tasks, setTasks] = useState(null); const [tasksCallHadError, setTasksCallHadError] = useState(false); - const [taskToDisplay, setTaskToDisplay] = - useState(null); + const [taskToDisplay, setTaskToDisplay] = useState(null); const [taskDataToDisplay, setTaskDataToDisplay] = useState(''); const [showTaskDataLoading, setShowTaskDataLoading] = useState(false); @@ -148,6 +148,10 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { if (typeof params.spiff_step !== 'undefined') { taskParams = `${taskParams}&spiff_step=${params.spiff_step}`; } + const bpmnProcessGuid = searchParams.get('bpmn_process_guid'); + if (bpmnProcessGuid) { + taskParams = `${taskParams}&bpmn_process_guid=${bpmnProcessGuid}`; + } let taskPath = ''; if (ability.can('GET', taskListPath)) { taskPath = `${taskListPath}${taskParams}`; @@ -213,14 +217,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { const getTaskIds = () => { const taskIds = { completed: [], readyOrWaiting: [] }; if (tasks) { - const callingSubprocessId = searchParams.get('call_activity_task_id'); - tasks.forEach(function getUserTasksElement(task: ProcessInstanceTask) { - if ( - callingSubprocessId && - callingSubprocessId !== task.calling_subprocess_task_id - ) { - return null; - } + tasks.forEach(function getUserTasksElement(task: Task) { if (task.state === 'COMPLETED') { (taskIds.completed as any).push(task); } @@ -251,13 +248,13 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { const spiffStepLink = (label: any, spiffStep: number) => { const processIdentifier = searchParams.get('process_identifier'); - const callActivityTaskId = searchParams.get('call_activity_task_id'); + const callActivityTaskId = searchParams.get('bpmn_process_guid'); const queryParamArray = []; if (processIdentifier) { queryParamArray.push(`process_identifier=${processIdentifier}`); } if (callActivityTaskId) { - queryParamArray.push(`call_activity_task_id=${callActivityTaskId}`); + queryParamArray.push(`bpmn_process_guid=${callActivityTaskId}`); } let queryParams = ''; if (queryParamArray.length > 0) { @@ -509,7 +506,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { return
; }; - const processTaskResult = (result: ProcessInstanceTask) => { + const processTaskResult = (result: Task) => { if (result == null) { setTaskDataToDisplay(''); } else { @@ -518,7 +515,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { setShowTaskDataLoading(false); }; - const initializeTaskDataToDisplay = (task: ProcessInstanceTask | null) => { + const initializeTaskDataToDisplay = (task: Task | null) => { if ( task && task.state === 'COMPLETED' && @@ -526,7 +523,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { ) { setShowTaskDataLoading(true); HttpService.makeCallToBackend({ - path: `${targetUris.processInstanceTaskDataPath}/${task.task_spiff_step}`, + path: `${targetUris.processInstanceTaskDataPath}/${task.id}`, httpMethod: 'GET', successCallback: processTaskResult, failureCallback: (error: any) => { @@ -577,13 +574,12 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { successCallback: handleProcessDataShowResponse, }); } else if (tasks) { - const matchingTask: any = tasks.find((task: any) => { - const callingSubprocessId = searchParams.get('call_activity_task_id'); + const matchingTask: Task | undefined = tasks.find((task: Task) => { return ( - (!callingSubprocessId || - callingSubprocessId === task.calling_subprocess_task_id) && - task.name === shapeElement.id && - bpmnProcessIdentifiers.includes(task.process_identifier) + task.bpmn_identifier === shapeElement.id && + bpmnProcessIdentifiers.includes( + task.bpmn_process_definition_identifier + ) ); }); if (matchingTask) { @@ -618,7 +614,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { httpMethod: 'POST', successCallback: processScriptUnitTestCreateResult, postBody: { - bpmn_task_identifier: taskToUse.name, + bpmn_task_identifier: taskToUse.bpmn_identifier, input_json: previousTask.data, expected_output_json: taskToUse.data, }, @@ -634,7 +630,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { ]; return ( (task.state === 'WAITING' && - subprocessTypes.filter((t) => t === task.type).length > 0) || + subprocessTypes.filter((t) => t === task.typename).length > 0) || task.state === 'READY' ); }; @@ -656,7 +652,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { processInstance && processInstance.status === 'waiting' && ability.can('POST', targetUris.processInstanceSendEventPath) && - taskTypes.filter((t) => t === task.type).length > 0 && + taskTypes.filter((t) => t === task.typename).length > 0 && task.state === 'WAITING' && showingLastSpiffStep() ); @@ -717,7 +713,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { setEditingTaskData(false); const dataObject = taskDataStringToObject(taskDataToDisplay); if (taskToDisplay) { - const taskToDisplayCopy: ProcessInstanceTask = { + const taskToDisplayCopy: Task = { ...taskToDisplay, data: dataObject, }; // spread operator @@ -768,11 +764,11 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { }); }; - const taskDisplayButtons = (task: any) => { + const taskDisplayButtons = (task: Task) => { const buttons = []; if ( - task.type === 'Script Task' && + task.typename === 'Script Task' && ability.can('PUT', targetUris.processModelShowPath) ) { buttons.push( @@ -785,11 +781,15 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { ); } - if (task.type === 'Call Activity') { + if (task.typename === 'CallActivity') { + console.log('task', task) + const taskDefinitionPropertiesJson: TaskDefinitionPropertiesJson = + task.task_definition_properties_json; + console.log('taskDefinitionPropertiesJson', taskDefinitionPropertiesJson) buttons.push( View Call Activity Diagram @@ -971,12 +971,15 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { }; const taskUpdateDisplayArea = () => { - const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay }; + if (!taskToDisplay) { + return null; + } + const taskToUse: Task = { ...taskToDisplay, data: taskDataToDisplay }; const candidateEvents: any = getEvents(taskToUse); if (taskToDisplay) { - let taskTitleText = taskToUse.id; - if (taskToUse.title) { - taskTitleText += ` (${taskToUse.title})`; + let taskTitleText = taskToUse.guid; + if (taskToUse.bpmn_name) { + taskTitleText += ` (${taskToUse.bpmn_name})`; } return ( - {taskToUse.name} ( - {taskToUse.type} + {taskToUse.bpmn_identifier} ( + {taskToUse.typename} ): {taskToUse.state} {taskDisplayButtons(taskToUse)}