diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index 5d8463b73..bc9840f8f 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -3089,7 +3089,7 @@ lxml = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "a844b34f9327a572d8f26110a01af21e22c548d1" +resolved_reference = "23d54e524db2ebfccd9a05c6f988af26179248b6" [[package]] name = "sqlalchemy" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/api_error.py b/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/api_error.py index 9cd2c4d31..03582b2d2 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/api_error.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/api_error.py @@ -15,9 +15,9 @@ from flask import jsonify from flask import make_response from sentry_sdk import capture_exception from sentry_sdk import set_tag +from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore from SpiffWorkflow.exceptions import WorkflowException -from SpiffWorkflow.exceptions import WorkflowTaskException from SpiffWorkflow.specs.base import TaskSpec # type: ignore from SpiffWorkflow.task import Task # type: ignore diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index 2105952de..88b6bca81 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -20,8 +20,8 @@ from flask import make_response from flask import stream_with_context from flask.wrappers import Response from jinja2 import TemplateSyntaxError +from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore -from SpiffWorkflow.exceptions import WorkflowTaskException # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState from sqlalchemy import and_ @@ -471,15 +471,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st def get_ready_engine_step_count(bpmn_process_instance: BpmnWorkflow) -> int: - return len( - list( - [ - t - for t in bpmn_process_instance.get_tasks(TaskState.READY) - if bpmn_process_instance._is_engine_task(t.task_spec) - ] - ) - ) + return len(list([t for t in bpmn_process_instance.get_tasks(TaskState.READY) if not t.task_spec.manual])) def _dequeued_interstitial_stream(process_instance_id: int) -> Generator[Optional[str], Optional[str], None]: 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 aa25020c3..c044228f7 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -28,6 +28,7 @@ from flask import current_app from lxml import etree # type: ignore from lxml.etree import XMLSyntaxError # type: ignore from RestrictedPython import safe_globals # type: ignore +from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngineEnvironment import BasePythonScriptEngineEnvironment # type: ignore @@ -39,12 +40,9 @@ from SpiffWorkflow.bpmn.serializer.task_spec import ( # type: ignore ) from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnProcessSpec # type: ignore -from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent # type: ignore -from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent # type: ignore from SpiffWorkflow.bpmn.specs.SubWorkflowTask import SubWorkflowTask # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.exceptions import WorkflowException # type: ignore -from SpiffWorkflow.exceptions import WorkflowTaskException from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser # type: ignore from SpiffWorkflow.spiff.serializer.config import SPIFF_SPEC_CONFIG # type: ignore @@ -68,10 +66,6 @@ from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.human_task import HumanTaskModel from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel from spiffworkflow_backend.models.json_data import JsonDataModel -from spiffworkflow_backend.models.message_instance import MessageInstanceModel -from spiffworkflow_backend.models.message_instance_correlation import ( - MessageInstanceCorrelationRuleModel, -) from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType @@ -901,9 +895,8 @@ class ProcessInstanceProcessor: bpmn_process_definition.bpmn_identifier, bpmn_process_definition=bpmn_process_definition, ) - for task_bpmn_identifier, task_bpmn_properties in task_specs.items(): - task_bpmn_name = task_bpmn_properties["description"] + task_bpmn_name = task_bpmn_properties["bpmn_name"] task_definition = TaskDefinitionModel( bpmn_process_definition=bpmn_process_definition, bpmn_identifier=task_bpmn_identifier, @@ -1036,7 +1029,7 @@ class ProcessInstanceProcessor: for ready_or_waiting_task in ready_or_waiting_tasks: # filter out non-usertasks task_spec = ready_or_waiting_task.task_spec - if not self.bpmn_process_instance._is_engine_task(task_spec): + if task_spec.manual: potential_owner_hash = self.get_potential_owner_ids_from_task(ready_or_waiting_task) extensions = task_spec.extensions @@ -1073,8 +1066,8 @@ class ProcessInstanceProcessor: ui_form_file_name=ui_form_file_name, task_model_id=task_model.id, task_id=task_guid, - task_name=ready_or_waiting_task.task_spec.name, - task_title=ready_or_waiting_task.task_spec.description, + task_name=ready_or_waiting_task.task_spec.bpmn_id, + task_title=ready_or_waiting_task.task_spec.bpmn_name, task_type=ready_or_waiting_task.task_spec.__class__.__name__, task_status=ready_or_waiting_task.get_state_name(), lane_assignment_id=potential_owner_hash["lane_assignment_id"], @@ -1134,7 +1127,7 @@ class ProcessInstanceProcessor: # We have to get to the actual start event for spiff_task in self.bpmn_process_instance.get_tasks(workflow=subprocess): spiff_task.run() - if isinstance(spiff_task.task_spec, StartEvent): + if spiff_task.task_spec.__class__.__name__ == "StartEvent": break else: spiff_task.run() @@ -1146,7 +1139,7 @@ class ProcessInstanceProcessor: spiff_task.workflow.last_task = spiff_task end_in_seconds = time.time() - if isinstance(spiff_task.task_spec, EndEvent): + if spiff_task.task_spec.__class__.__name__ == "EndEvent": for task in self.bpmn_process_instance.get_tasks(TaskState.DEFINITE_MASK, workflow=spiff_task.workflow): task.complete() @@ -1387,56 +1380,6 @@ class ProcessInstanceProcessor: # current_app.logger.debug(f"the_status: {the_status} for instance {self.process_instance_model.id}") return the_status - def process_bpmn_messages(self) -> None: - """Process_bpmn_messages.""" - bpmn_messages = self.bpmn_process_instance.get_bpmn_messages() - for bpmn_message in bpmn_messages: - message_instance = MessageInstanceModel( - process_instance_id=self.process_instance_model.id, - user_id=self.process_instance_model.process_initiator_id, # TODO: use the correct swimlane user when that is set up - message_type="send", - name=bpmn_message.name, - payload=bpmn_message.payload, - correlation_keys=self.bpmn_process_instance.correlations, - ) - db.session.add(message_instance) - db.session.commit() - - def queue_waiting_receive_messages(self) -> None: - """Queue_waiting_receive_messages.""" - waiting_events = self.bpmn_process_instance.waiting_events() - waiting_message_events = filter(lambda e: e["event_type"] == "Message", waiting_events) - - for event in waiting_message_events: - # Ensure we are only creating one message instance for each waiting message - if ( - MessageInstanceModel.query.filter_by( - process_instance_id=self.process_instance_model.id, - message_type="receive", - name=event["name"], - ).count() - > 0 - ): - continue - - # Create a new Message Instance - message_instance = MessageInstanceModel( - process_instance_id=self.process_instance_model.id, - user_id=self.process_instance_model.process_initiator_id, - message_type="receive", - name=event["name"], - correlation_keys=self.bpmn_process_instance.correlations, - ) - for correlation_property in event["value"]: - message_correlation = MessageInstanceCorrelationRuleModel( - message_instance_id=message_instance.id, - name=correlation_property.name, - retrieval_expression=correlation_property.retrieval_expression, - ) - message_instance.correlation_rules.append(message_correlation) - db.session.add(message_instance) - db.session.commit() - def element_unit_specs_loader(self, process_id: str, element_id: str) -> Optional[Dict[str, Any]]: full_process_model_hash = self.process_instance_model.bpmn_process_definition.full_process_model_hash if full_process_model_hash is None: @@ -1463,7 +1406,7 @@ class ProcessInstanceProcessor: tasks = self.bpmn_process_instance.get_tasks(TaskState.DEFINITE_MASK) loaded_specs = set(self.bpmn_process_instance.subprocess_specs.keys()) for task in tasks: - if task.task_spec.spec_type != "Call Activity": + if task.task_spec.description != "Call Activity": continue spec_to_check = task.task_spec.spec @@ -1587,10 +1530,13 @@ class ProcessInstanceProcessor: endtasks = [] if self.bpmn_process_instance.is_completed(): - for task in SpiffTask.Iterator(self.bpmn_process_instance.task_tree, TaskState.ANY_MASK): + for spiff_task in SpiffTask.Iterator(self.bpmn_process_instance.task_tree, TaskState.ANY_MASK): # Assure that we find the end event for this process_instance, and not for any sub-process_instances. - if isinstance(task.task_spec, EndEvent) and task.workflow == self.bpmn_process_instance: - endtasks.append(task) + if ( + spiff_task.task_spec.__class__.__name__ == "EndEvent" + and spiff_task.workflow == self.bpmn_process_instance + ): + endtasks.append(spiff_task) if len(endtasks) > 0: return endtasks[-1] @@ -1645,7 +1591,7 @@ class ProcessInstanceProcessor: user_tasks.reverse() user_tasks = list( filter( - lambda task: not self.bpmn_process_instance._is_engine_task(task.task_spec), + lambda task: task.task_spec.manual, user_tasks, ) ) @@ -1741,16 +1687,11 @@ class ProcessInstanceProcessor: def get_all_user_tasks(self) -> List[SpiffTask]: """Get_all_user_tasks.""" all_tasks = self.bpmn_process_instance.get_tasks(TaskState.ANY_MASK) - return [t for t in all_tasks if not self.bpmn_process_instance._is_engine_task(t.task_spec)] + return [t for t in all_tasks if t.task_spec.manual] def get_all_completed_tasks(self) -> list[SpiffTask]: all_tasks = self.bpmn_process_instance.get_tasks(TaskState.ANY_MASK) - return [ - t - for t in all_tasks - if not self.bpmn_process_instance._is_engine_task(t.task_spec) - and t.state in [TaskState.COMPLETED, TaskState.CANCELLED] - ] + return [t for t in all_tasks if t.task_spec.manual and t.state in [TaskState.COMPLETED, TaskState.CANCELLED]] def get_all_waiting_tasks(self) -> list[SpiffTask]: """Get_all_ready_or_waiting_tasks.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index 94b9976c8..3315c3327 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -98,8 +98,8 @@ class ProcessInstanceService: # # example: {'event_type': 'Duration Timer', 'name': None, 'value': '2023-04-27T20:15:10.626656+00:00'} # - event_type = waiting_event.get("event_type") - if event_type == "Duration Timer": + spiff_event_type = waiting_event.get("event_type") + if spiff_event_type == "DurationTimerEventDefinition": event_value = waiting_event.get("value") if event_value is not None: event_datetime = TimerEventDefinition.get_datetime(event_value) @@ -456,7 +456,7 @@ class ProcessInstanceService: calling_subprocess_task_id: Optional[str] = None, ) -> Task: """Spiff_task_to_api_task.""" - task_type = spiff_task.task_spec.spec_type + task_type = spiff_task.task_spec.description props = {} if hasattr(spiff_task.task_spec, "extensions"): @@ -500,8 +500,8 @@ class ProcessInstanceService: task = Task( spiff_task.id, - spiff_task.task_spec.name, - spiff_task.task_spec.description, + spiff_task.task_spec.bpmn_id, + spiff_task.task_spec.bpmn_name, task_type, spiff_task.get_state_name(), can_complete=can_complete, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_tmp_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_tmp_service.py index d681bdbbc..69b4a49eb 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_tmp_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_tmp_service.py @@ -4,7 +4,7 @@ from typing import Optional from typing import Tuple from flask import g -from SpiffWorkflow.exceptions import WorkflowTaskException # type: ignore +from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.process_instance import ProcessInstanceModel diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/script_unit_test_runner.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/script_unit_test_runner.py index 63c9fe381..b3ac532db 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/script_unit_test_runner.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/script_unit_test_runner.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from typing import Any from typing import Optional -from SpiffWorkflow.exceptions import WorkflowTaskException # type: ignore +from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore from spiffworkflow_backend.services.process_instance_processor import ( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index 1f7d1e0f2..e8771738c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -131,12 +131,12 @@ class SpecFileService(FileSystemService): has_lanes = sub_parser.has_lanes() is_executable = sub_parser.process_executable start_messages = sub_parser.start_messages() - is_primary = sub_parser.get_id() == process_model_info.primary_process_id + is_primary = sub_parser.bpmn_id == process_model_info.primary_process_id called_element_ids = sub_parser.called_element_ids() references.append( SpecReference( - identifier=sub_parser.get_id(), + identifier=sub_parser.bpmn_id, display_name=sub_parser.get_name(), process_model_id=process_model_info.id, type=parser_type, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 0f2071b25..d48698b72 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -9,10 +9,10 @@ from typing import Dict from typing import Optional from uuid import UUID +from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore -from SpiffWorkflow.exceptions import WorkflowTaskException from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState @@ -99,13 +99,7 @@ class ExecutionStrategy: self.delegate.save(bpmn_process_instance) def get_ready_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]: - tasks = list( - [ - t - for t in bpmn_process_instance.get_tasks(TaskState.READY) - if bpmn_process_instance._is_engine_task(t.task_spec) - ] - ) + tasks = list([t for t in bpmn_process_instance.get_tasks(TaskState.READY) if not t.task_spec.manual]) if len(tasks) > 0: self.subprocess_spec_loader() @@ -297,7 +291,7 @@ class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy): engine_steps = self.get_ready_engine_steps(bpmn_process_instance) while engine_steps: for spiff_task in engine_steps: - if spiff_task.task_spec.spec_type == "Service Task": + if spiff_task.task_spec.description == "Service Task": return self.delegate.will_complete_task(spiff_task) spiff_task.run() @@ -427,7 +421,6 @@ class WorkflowExecutionService: self.process_instance_saver() def process_bpmn_messages(self) -> None: - """Process_bpmn_messages.""" bpmn_messages = self.bpmn_process_instance.get_bpmn_messages() for bpmn_message in bpmn_messages: message_instance = MessageInstanceModel( @@ -452,9 +445,8 @@ class WorkflowExecutionService: db.session.commit() def queue_waiting_receive_messages(self) -> None: - """Queue_waiting_receive_messages.""" waiting_events = self.bpmn_process_instance.waiting_events() - waiting_message_events = filter(lambda e: e["event_type"] == "Message", waiting_events) + waiting_message_events = filter(lambda e: e["event_type"] == "MessageEventDefinition", waiting_events) for event in waiting_message_events: # Ensure we are only creating one message instance for each waiting message 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 e7817523c..9ccafdc51 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 @@ -1,6 +1,6 @@ - + Flow_0stlaxe diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index efcb81452..2cd160639 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1778,7 +1778,6 @@ class TestProcessApi(BaseTest): # The second script task should produce rendered jinja text # The Manual Task should then return a message as well. assert len(results) == 2 - # import pdb; pdb.set_trace() assert json_results[0]["task"]["state"] == "READY" assert json_results[0]["task"]["title"] == "Script Task #2" assert json_results[0]["task"]["properties"]["instructionsForEndUser"] == "I am Script Task 2" @@ -2740,7 +2739,7 @@ class TestProcessApi(BaseTest): ) assert response.status_code == 200 assert response.json is not None - assert response.json["type"] == "End Event" + assert response.json["type"] == "Default End Event" assert response.json["state"] == "COMPLETED" response = client.get( diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_service.py index 9d25adda1..54e996738 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_service.py @@ -226,14 +226,18 @@ class TestProcessInstanceService(BaseTest): def test_does_skip_duration_timer_events_for_the_future(self) -> None: assert ProcessInstanceService.waiting_event_can_be_skipped( - {"event_type": "Duration Timer", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"}, + {"event_type": "DurationTimerEventDefinition", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"}, datetime.fromisoformat("2023-04-26T20:15:10.626656+00:00"), ) def test_does_not_skip_duration_timer_events_for_the_past(self) -> None: assert not ( ProcessInstanceService.waiting_event_can_be_skipped( - {"event_type": "Duration Timer", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"}, + { + "event_type": "DurationTimerEventDefinition", + "name": None, + "value": "2023-04-27T20:15:10.626656+00:00", + }, datetime.fromisoformat("2023-04-28T20:15:10.626656+00:00"), ) ) @@ -241,7 +245,11 @@ class TestProcessInstanceService(BaseTest): def test_does_not_skip_duration_timer_events_for_now(self) -> None: assert not ( ProcessInstanceService.waiting_event_can_be_skipped( - {"event_type": "Duration Timer", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"}, + { + "event_type": "DurationTimerEventDefinition", + "name": None, + "value": "2023-04-27T20:15:10.626656+00:00", + }, datetime.fromisoformat("2023-04-27T20:15:10.626656+00:00"), ) ) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_task_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_task_service.py index d10643171..963f0b0a8 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_task_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_task_service.py @@ -181,5 +181,3 @@ class TestTaskService(BaseTest): assert signal_event["event"]["name"] == "eat_spam" assert signal_event["event"]["typename"] == "SignalEventDefinition" assert signal_event["label"] == "Eat Spam" - - print(events)