fix call activities to get the form from the process called and added tests to ensure they are working w/ burnettk

This commit is contained in:
jasquat 2022-09-12 14:17:18 -04:00
parent 5b43326fb6
commit 6eef611095
8 changed files with 165 additions and 33 deletions

View File

@ -1,5 +1,6 @@
"""APIs for dealing with process groups, process models, and process instances."""
import json
import os
import uuid
from typing import Any
from typing import Dict
@ -44,6 +45,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel
from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService
from spiffworkflow_backend.services.file_system_service import FileSystemService
from spiffworkflow_backend.services.message_service import MessageService
from spiffworkflow_backend.services.process_instance_processor import (
ProcessInstanceProcessor,
@ -773,9 +775,25 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
form_ui_schema_file_name = properties["formUiSchemaFilename"]
task = ProcessInstanceService.spiff_task_to_api_task(spiff_task)
task.data = spiff_task.data
task.process_name = process_model.id
task.process_model_display_name = process_model.display_name
process_model_with_form = process_model
if task.process_name != process_model.primary_process_id:
bpmn_file_full_path = (
ProcessInstanceProcessor.bpmn_file_full_path_from_bpmn_process_identifier(
task.process_name
)
)
relative_path = os.path.relpath(
bpmn_file_full_path, start=FileSystemService.root_path()
)
process_model_relative_path = os.path.dirname(relative_path)
process_model_with_form = (
ProcessModelService.get_process_model_from_relative_path(
process_model_relative_path
)
)
if task.type == "UserTask":
if not form_schema_file_name:
raise (
@ -789,7 +807,7 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
form_contents = prepare_form_data(
form_schema_file_name,
task.data,
process_model,
process_model_with_form,
)
if form_contents:
@ -799,7 +817,7 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
ui_form_contents = prepare_form_data(
form_ui_schema_file_name,
task.data,
process_model,
process_model_with_form,
)
if ui_form_contents:
task.form_ui_schema = ui_form_contents

View File

@ -462,7 +462,7 @@ class ProcessInstanceProcessor:
@staticmethod
def backfill_missing_bpmn_process_id_lookup_records(
bpmn_process_identifier: None,
bpmn_process_identifier: str,
) -> Optional[str]:
"""Backfill_missing_bpmn_process_id_lookup_records."""
process_models = ProcessModelService().get_process_models()
@ -494,6 +494,34 @@ class ProcessInstanceProcessor:
)
return None
@staticmethod
def bpmn_file_full_path_from_bpmn_process_identifier(
bpmn_process_identifier: str,
) -> str:
"""Bpmn_file_full_path_from_bpmn_process_identifier."""
bpmn_process_id_lookup = BpmnProcessIdLookup.query.filter_by(
bpmn_process_identifier=bpmn_process_identifier
).first()
bpmn_file_full_path = None
if bpmn_process_id_lookup is None:
bpmn_file_full_path = ProcessInstanceProcessor.backfill_missing_bpmn_process_id_lookup_records(
bpmn_process_identifier
)
else:
bpmn_file_full_path = os.path.join(
FileSystemService.root_path(),
bpmn_process_id_lookup.bpmn_file_relative_path,
)
if bpmn_file_full_path is None:
raise (
ApiError(
code="could_not_find_bpmn_process_identifier",
message="Could not find the the given bpmn process identifier from any sources: %s"
% bpmn_process_identifier,
)
)
return os.path.abspath(bpmn_file_full_path)
@staticmethod
def update_spiff_parser_with_all_process_dependency_files(
parser: BpmnDmnParser,
@ -504,7 +532,7 @@ class ProcessInstanceProcessor:
processed_identifiers = set()
processor_dependencies = parser.get_process_dependencies()
processor_dependencies_new = processor_dependencies - processed_identifiers
bpmn_process_identifiers_in_parser = parser.find_all_specs().keys()
bpmn_process_identifiers_in_parser = parser.get_process_ids()
new_bpmn_files = set()
for bpmn_process_identifier in processor_dependencies_new:
@ -513,28 +541,9 @@ class ProcessInstanceProcessor:
if bpmn_process_identifier in bpmn_process_identifiers_in_parser:
continue
bpmn_process_id_lookup = BpmnProcessIdLookup.query.filter_by(
bpmn_process_identifier=bpmn_process_identifier
).first()
new_bpmn_file_full_path = None
if bpmn_process_id_lookup is None:
new_bpmn_file_full_path = ProcessInstanceProcessor.backfill_missing_bpmn_process_id_lookup_records(
new_bpmn_file_full_path = ProcessInstanceProcessor.bpmn_file_full_path_from_bpmn_process_identifier(
bpmn_process_identifier
)
else:
new_bpmn_file_full_path = os.path.join(
FileSystemService.root_path(),
bpmn_process_id_lookup.bpmn_file_relative_path,
)
if new_bpmn_file_full_path is None:
raise (
ApiError(
code="could_not_find_bpmn_process_identifier",
message="Could not find the the given bpmn process identifier from any sources: %s"
% bpmn_process_identifier,
)
)
new_bpmn_files.add(new_bpmn_file_full_path)
processed_identifiers.add(bpmn_process_identifier)
@ -739,7 +748,7 @@ class ProcessInstanceProcessor:
db.session.commit()
def do_engine_steps(self, exit_at: None = None) -> None:
def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None:
"""Do_engine_steps."""
try:
self.bpmn_process_instance.refresh_waiting_tasks()
@ -747,6 +756,9 @@ class ProcessInstanceProcessor:
self.process_bpmn_messages()
self.queue_waiting_receive_messages()
if save:
self.save()
except WorkflowTaskExecException as we:
raise ApiError.from_workflow_exception("task_error", str(we), we) from we

View File

@ -102,6 +102,16 @@ class ProcessModelService(FileSystemService):
return self.__scan_spec(path, FileSystemService.MASTER_SPECIFICATION)
return None
@classmethod
def get_process_model_from_relative_path(
cls, relative_path: str
) -> ProcessModelInfo:
"""Get_process_model_from_relative_path."""
process_group_identifier = os.path.dirname(relative_path)
process_group = cls().get_process_group(process_group_identifier)
path = os.path.join(FileSystemService.root_path(), relative_path)
return cls().__scan_spec(path, process_group=process_group)
def get_process_model(
self, process_model_id: str, group_id: Optional[str] = None
) -> ProcessModelInfo:
@ -268,7 +278,10 @@ class ProcessModelService(FileSystemService):
return process_group
def __scan_spec(
self, path: str, name: str, process_group: Optional[ProcessGroup] = None
self,
path: str,
name: Optional[str] = None,
process_group: Optional[ProcessGroup] = None,
) -> ProcessModelInfo:
"""__scan_spec."""
spec_path = os.path.join(path, self.WF_JSON_FILE)
@ -284,6 +297,12 @@ class ProcessModelService(FileSystemService):
message=f"We could not load the process_model from disk with data: {data}",
)
else:
if name is None:
raise ApiError(
code="missing_name_of_process_model",
message="Missing name of process model. It should be given",
)
spec = ProcessModelInfo(
id=name,
library=False,

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="Process_bd2e724" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_1rcteeq</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_1rcteeq" sourceRef="StartEvent_1" targetRef="the_call_activity" />
<bpmn:callActivity id="the_call_activity" name="The Call Activity" calledElement="process_to_call">
<bpmn:incoming>Flow_1rcteeq</bpmn:incoming>
<bpmn:outgoing>Flow_1rid3w7</bpmn:outgoing>
</bpmn:callActivity>
<bpmn:endEvent id="Event_1w7nqwy">
<bpmn:incoming>Flow_1rid3w7</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1rid3w7" sourceRef="the_call_activity" targetRef="Event_1w7nqwy" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_bd2e724">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0pmcny7_di" bpmnElement="the_call_activity">
<dc:Bounds x="270" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1w7nqwy_di" bpmnElement="Event_1w7nqwy">
<dc:Bounds x="432" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1rcteeq_di" bpmnElement="Flow_1rcteeq">
<di:waypoint x="215" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1rid3w7_di" bpmnElement="Flow_1rid3w7">
<di:waypoint x="370" y="177" />
<di:waypoint x="432" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="process_to_call" name="Process to call" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0ogjqo9</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:endEvent id="Event_08g7f08">
<bpmn:incoming>Flow_0ogjqo9</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0ogjqo9" sourceRef="StartEvent_1" targetRef="Event_08g7f08" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="process_to_call">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_08g7f08_di" bpmnElement="Event_08g7f08">
<dc:Bounds x="402" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0ogjqo9_di" bpmnElement="Flow_0ogjqo9">
<di:waypoint x="215" y="177" />
<di:waypoint x="402" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -22,7 +22,23 @@ class TestProcessModel(BaseTest):
assert process_model_one.files == []
assert process_model_one.libraries == []
def test_can_run_process_model_with_call_activities(
def test_can_run_process_model_with_call_activities_when_in_same_process_model_directory(
self, app: Flask, with_db_and_bpmn_file_cleanup: None
) -> None:
"""Test_can_run_process_model_with_call_activities."""
process_model = load_test_spec(
"call_activity_test",
process_model_source_directory="call_activity_same_directory",
)
process_instance = self.create_process_instance_from_process_model(
process_model
)
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps(save=True)
assert process_instance.status == "complete"
def test_can_run_process_model_with_call_activities_when_not_in_same_directory(
self, app: Flask, with_db_and_bpmn_file_cleanup: None
) -> None:
"""Test_can_run_process_model_with_call_activities."""
@ -47,7 +63,8 @@ class TestProcessModel(BaseTest):
process_model
)
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps()
processor.do_engine_steps(save=True)
assert process_instance.status == "complete"
def test_can_run_process_model_with_call_activities_when_process_identifier_is_not_in_database(
self, app: Flask, with_db_and_bpmn_file_cleanup: None
@ -78,7 +95,8 @@ class TestProcessModel(BaseTest):
# process model when running the process
db.session.query(BpmnProcessIdLookup).delete()
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps()
processor.do_engine_steps(save=True)
assert process_instance.status == "complete"
def create_test_process_model(self, id: str, display_name: str) -> ProcessModelInfo:
"""Create_test_process_model."""

View File

@ -58,7 +58,7 @@ class TestSpecFileService(BaseTest):
with pytest.raises(ApiError) as exception:
load_test_spec(
"call_activity_nested_duplicate",
process_model_source_directory="call_activity_nested",
process_model_source_directory="call_activity_duplicate",
bpmn_file_name="call_activity_nested_duplicate",
)
assert f"Process id ({bpmn_process_identifier}) has already been used" in str(