Fix process data get subprocess 3 (#1515)
* Fix process data get subprocess (#1493) * added test to make sure we can get the data object of a sub process * avoid the processor altogether to get data objects but use the db directly --------- Co-authored-by: jasquat <jasquat@users.noreply.github.com> * get the data object value from the bpmn process that defines it w/ burnettk --------- Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
parent
adab730bd9
commit
d5e0ab6989
|
@ -30,6 +30,7 @@ from spiffworkflow_backend.exceptions.error import HumanTaskNotFoundError
|
|||
from spiffworkflow_backend.exceptions.error import UserDoesNotHaveAccessToTaskError
|
||||
from spiffworkflow_backend.exceptions.process_entity_not_found_error import ProcessEntityNotFoundError
|
||||
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
|
||||
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.human_task import HumanTaskModel
|
||||
from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel
|
||||
|
@ -129,6 +130,44 @@ def process_caller_list(bpmn_process_identifiers: list[str]) -> Any:
|
|||
return ReferenceSchema(many=True).dump(references)
|
||||
|
||||
|
||||
def _get_bpmn_process_with_data_object(
|
||||
process_data_identifier: str,
|
||||
bpmn_processes_by_id: dict[int, BpmnProcessModel],
|
||||
bpmn_process_definitions_by_id: dict[int, BpmnProcessDefinitionModel],
|
||||
current_bp: BpmnProcessModel,
|
||||
) -> Any:
|
||||
current_bpd = bpmn_process_definitions_by_id[current_bp.bpmn_process_definition_id]
|
||||
if "data_objects" in current_bpd.properties_json and process_data_identifier in current_bpd.properties_json["data_objects"]:
|
||||
return current_bp
|
||||
elif current_bp.direct_parent_process_id in bpmn_processes_by_id:
|
||||
return _get_bpmn_process_with_data_object(
|
||||
process_data_identifier,
|
||||
bpmn_processes_by_id,
|
||||
bpmn_process_definitions_by_id,
|
||||
bpmn_processes_by_id[current_bp.direct_parent_process_id],
|
||||
)
|
||||
else:
|
||||
return current_bp
|
||||
|
||||
|
||||
def _get_data_object_from_bpmn_process(
|
||||
process_data_identifier: str,
|
||||
bpmn_process: BpmnProcessModel,
|
||||
bpmn_process_guid: str | None,
|
||||
process_instance: ProcessInstanceModel,
|
||||
) -> Any:
|
||||
bpmn_process_data = JsonDataModel.find_data_dict_by_hash(bpmn_process.json_data_hash)
|
||||
if bpmn_process_data is None:
|
||||
raise ApiError(
|
||||
error_code="bpmn_process_data_not_found",
|
||||
message=f"Cannot find a bpmn process data with guid '{bpmn_process_guid}' for process instance {process_instance.id}",
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
data_objects = bpmn_process_data.get("data_objects", {})
|
||||
return data_objects.get(process_data_identifier)
|
||||
|
||||
|
||||
def _process_data_fetcher(
|
||||
process_instance_id: int,
|
||||
process_data_identifier: str,
|
||||
|
@ -148,18 +187,40 @@ def _process_data_fetcher(
|
|||
status_code=404,
|
||||
)
|
||||
|
||||
bpmn_process_data = JsonDataModel.find_data_dict_by_hash(bpmn_process.json_data_hash)
|
||||
if bpmn_process_data is None:
|
||||
raise ApiError(
|
||||
error_code="bpmn_process_data_not_found",
|
||||
message=f"Cannot find a bpmn process data with guid '{bpmn_process_guid}' for process instance {process_instance.id}",
|
||||
status_code=404,
|
||||
data_object_value = _get_data_object_from_bpmn_process(
|
||||
process_data_identifier=process_data_identifier,
|
||||
bpmn_process=bpmn_process,
|
||||
bpmn_process_guid=bpmn_process_guid,
|
||||
process_instance=process_instance,
|
||||
)
|
||||
|
||||
data_objects = bpmn_process_data["data_objects"]
|
||||
data_object = data_objects.get(process_data_identifier)
|
||||
# if the data object value cannot be found with the given bpmn process then attempt to get it from the parent that defines it
|
||||
if data_object_value is None:
|
||||
all_bpmn_processes = None
|
||||
if bpmn_process.top_level_process_id is not None:
|
||||
all_bpmn_processes = BpmnProcessModel.query.filter(
|
||||
or_(
|
||||
BpmnProcessModel.top_level_process_id == bpmn_process.top_level_process_id,
|
||||
BpmnProcessModel.id == bpmn_process.top_level_process_id,
|
||||
)
|
||||
).all()
|
||||
all_bpmn_def_ids = [bp.bpmn_process_definition_id for bp in all_bpmn_processes]
|
||||
all_bpmn_process_definitions = BpmnProcessDefinitionModel.query.filter(
|
||||
BpmnProcessDefinitionModel.id.in_(all_bpmn_def_ids) # type: ignore
|
||||
).all()
|
||||
bpmn_processes_by_id = {bp.id: bp for bp in all_bpmn_processes}
|
||||
bpmn_process_definitions_by_id = {bpd.id: bpd for bpd in all_bpmn_process_definitions}
|
||||
bp = _get_bpmn_process_with_data_object(
|
||||
process_data_identifier, bpmn_processes_by_id, bpmn_process_definitions_by_id, bpmn_process
|
||||
)
|
||||
data_object_value = _get_data_object_from_bpmn_process(
|
||||
process_data_identifier=process_data_identifier,
|
||||
bpmn_process=bp,
|
||||
bpmn_process_guid=bpmn_process_guid,
|
||||
process_instance=process_instance,
|
||||
)
|
||||
|
||||
if data_object is None:
|
||||
if data_object_value is None:
|
||||
raise ApiError(
|
||||
error_code="data_object_not_found",
|
||||
message=(
|
||||
|
@ -169,21 +230,20 @@ def _process_data_fetcher(
|
|||
status_code=404,
|
||||
)
|
||||
|
||||
if hasattr(data_object, "category") and data_object.category is not None:
|
||||
if data_object.category != category:
|
||||
if hasattr(data_object_value, "category") and data_object_value.category is not None:
|
||||
if data_object_value.category != category:
|
||||
raise ApiError(
|
||||
error_code="data_object_category_mismatch",
|
||||
message=f"The desired data object has category '{data_object.category}' instead of the expected '{category}'",
|
||||
message=f"The desired data object has category '{data_object_value.category}' "
|
||||
"instead of the expected '{category}'",
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
process_data_value = bpmn_process_data.get("data_objects", bpmn_process_data).get(process_data_identifier)
|
||||
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"process_data_identifier": process_data_identifier,
|
||||
"process_data_value": process_data_value,
|
||||
"process_data_value": data_object_value,
|
||||
}
|
||||
),
|
||||
200,
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
<?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_data_object_in_subprocess_naa791d" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_17db3yp</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_17db3yp" sourceRef="StartEvent_1" targetRef="subprocess1" />
|
||||
<bpmn:endEvent id="EndEvent_1">
|
||||
<bpmn:incoming>Flow_0ogjg7w</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1qd0afw" sourceRef="subprocess1" targetRef="subprocess2" />
|
||||
<bpmn:subProcess id="subprocess1" name="Subprocess1">
|
||||
<bpmn:incoming>Flow_17db3yp</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1qd0afw</bpmn:outgoing>
|
||||
<bpmn:dataOutputAssociation id="DataOutputAssociation_0piaeh8">
|
||||
<bpmn:targetRef>DataObjectReference_0vvdt0g</bpmn:targetRef>
|
||||
</bpmn:dataOutputAssociation>
|
||||
<bpmn:startEvent id="Event_1ln0thm">
|
||||
<bpmn:outgoing>Flow_1j5xvbb</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1j5xvbb" sourceRef="Event_1ln0thm" targetRef="Activity_1rt0k9h" />
|
||||
<bpmn:endEvent id="Event_0ocmrhe">
|
||||
<bpmn:incoming>Flow_1g3kgd6</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1g3kgd6" sourceRef="Activity_1rt0k9h" targetRef="Event_0ocmrhe" />
|
||||
<bpmn:scriptTask id="Activity_1rt0k9h">
|
||||
<bpmn:incoming>Flow_1j5xvbb</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1g3kgd6</bpmn:outgoing>
|
||||
<bpmn:script>our_data_object = "HEY"</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
</bpmn:subProcess>
|
||||
<bpmn:sequenceFlow id="Flow_0ogjg7w" sourceRef="subprocess2" targetRef="EndEvent_1" />
|
||||
<bpmn:subProcess id="subprocess2" name="Subprocess2">
|
||||
<bpmn:incoming>Flow_1qd0afw</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0ogjg7w</bpmn:outgoing>
|
||||
<bpmn:startEvent id="Event_0ohesmq">
|
||||
<bpmn:outgoing>Flow_1cbazwx</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1cbazwx" sourceRef="Event_0ohesmq" targetRef="Subprocess2_ScriptTask" />
|
||||
<bpmn:endEvent id="Event_0fgm95v">
|
||||
<bpmn:incoming>Flow_1ovr7kv</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1ovr7kv" sourceRef="Subprocess2_ScriptTask" targetRef="Event_0fgm95v" />
|
||||
<bpmn:scriptTask id="Subprocess2_ScriptTask" name="Subprocess2_ScriptTask">
|
||||
<bpmn:incoming>Flow_1cbazwx</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1ovr7kv</bpmn:outgoing>
|
||||
<bpmn:property id="Property_1q20lug" name="__targetRef_placeholder" />
|
||||
<bpmn:dataInputAssociation id="DataInputAssociation_0n80m5c">
|
||||
<bpmn:sourceRef>DataObjectReference_1vhsytv</bpmn:sourceRef>
|
||||
<bpmn:targetRef>Property_1q20lug</bpmn:targetRef>
|
||||
</bpmn:dataInputAssociation>
|
||||
<bpmn:script>hey = f"{our_data_object}_NO"</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:dataObjectReference id="DataObjectReference_1vhsytv" name="our_data_object" dataObjectRef="our_data_object" />
|
||||
</bpmn:subProcess>
|
||||
<bpmn:dataObjectReference id="DataObjectReference_0vvdt0g" name="our_data_object" dataObjectRef="our_data_object" />
|
||||
<bpmn:dataObject id="our_data_object" name="our_data_object" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_td_hey_naa791d">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="112" y="159" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_14za570_di" bpmnElement="EndEvent_1">
|
||||
<dc:Bounds x="432" y="159" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_03m2m68_di" bpmnElement="subprocess1">
|
||||
<dc:Bounds x="170" y="137" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1ccsja3_di" bpmnElement="subprocess2">
|
||||
<dc:Bounds x="300" y="137" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="DataObjectReference_0vvdt0g_di" bpmnElement="DataObjectReference_0vvdt0g">
|
||||
<dc:Bounds x="172" y="295" width="36" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="151" y="352" width="79" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_17db3yp_di" bpmnElement="Flow_17db3yp">
|
||||
<di:waypoint x="148" y="177" />
|
||||
<di:waypoint x="170" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1qd0afw_di" bpmnElement="Flow_1qd0afw">
|
||||
<di:waypoint x="270" y="177" />
|
||||
<di:waypoint x="300" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="DataOutputAssociation_0piaeh8_di" bpmnElement="DataOutputAssociation_0piaeh8">
|
||||
<di:waypoint x="211" y="217" />
|
||||
<di:waypoint x="193" y="295" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0ogjg7w_di" bpmnElement="Flow_0ogjg7w">
|
||||
<di:waypoint x="400" y="177" />
|
||||
<di:waypoint x="432" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_14tf7jl">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_0u3vw6z" bpmnElement="subprocess1">
|
||||
<bpmndi:BPMNShape id="Event_1ln0thm_di" bpmnElement="Event_1ln0thm">
|
||||
<dc:Bounds x="542" y="242" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0ocmrhe_di" bpmnElement="Event_0ocmrhe">
|
||||
<dc:Bounds x="782" y="242" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_04ig3ob_di" bpmnElement="Activity_1rt0k9h">
|
||||
<dc:Bounds x="630" y="220" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_1j5xvbb_di" bpmnElement="Flow_1j5xvbb">
|
||||
<di:waypoint x="578" y="260" />
|
||||
<di:waypoint x="630" y="260" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1g3kgd6_di" bpmnElement="Flow_1g3kgd6">
|
||||
<di:waypoint x="730" y="260" />
|
||||
<di:waypoint x="782" y="260" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_01zi3um">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_0yyv2r4" bpmnElement="subprocess2">
|
||||
<bpmndi:BPMNShape id="Event_0ohesmq_di" bpmnElement="Event_0ohesmq">
|
||||
<dc:Bounds x="532" y="212" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_06vgk4z_di" bpmnElement="Subprocess2_ScriptTask">
|
||||
<dc:Bounds x="620" y="190" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="DataObjectReference_1vhsytv_di" bpmnElement="DataObjectReference_1vhsytv">
|
||||
<dc:Bounds x="652" y="335" width="36" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="631" y="392" width="79" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0fgm95v_di" bpmnElement="Event_0fgm95v">
|
||||
<dc:Bounds x="782" y="212" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_1cbazwx_di" bpmnElement="Flow_1cbazwx">
|
||||
<di:waypoint x="568" y="230" />
|
||||
<di:waypoint x="620" y="230" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1ovr7kv_di" bpmnElement="Flow_1ovr7kv">
|
||||
<di:waypoint x="720" y="230" />
|
||||
<di:waypoint x="782" y="230" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="DataInputAssociation_0n80m5c_di" bpmnElement="DataInputAssociation_0n80m5c">
|
||||
<di:waypoint x="670" y="335" />
|
||||
<di:waypoint x="670" y="270" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -3375,6 +3375,43 @@ class TestProcessApi(BaseTest):
|
|||
assert response.json is not None
|
||||
assert response.json["process_data_value"] == "d"
|
||||
|
||||
def test_process_data_show_with_sub_process_from_top_level(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
process_model = load_test_spec(
|
||||
"test_group/data-object-in-subprocess",
|
||||
process_model_source_directory="data-object-in-subprocess",
|
||||
)
|
||||
process_instance_one = self.create_process_instance_from_process_model(process_model)
|
||||
processor = ProcessInstanceProcessor(process_instance_one)
|
||||
processor.do_engine_steps(save=True)
|
||||
assert process_instance_one.status == "complete"
|
||||
|
||||
process_identifier = "subprocess2"
|
||||
bpmn_processes = (
|
||||
BpmnProcessModel.query.join(
|
||||
BpmnProcessDefinitionModel, BpmnProcessDefinitionModel.id == BpmnProcessModel.bpmn_process_definition_id
|
||||
)
|
||||
.filter(BpmnProcessDefinitionModel.bpmn_identifier == process_identifier)
|
||||
.all()
|
||||
)
|
||||
assert len(bpmn_processes) == 1
|
||||
bpmn_process = bpmn_processes[0]
|
||||
|
||||
response = client.get(
|
||||
f"/v1.0/process-data/default/{self.modify_process_identifier_for_path_param(process_model.id)}/our_data_object/"
|
||||
f"{process_instance_one.id}?process_identifier={process_identifier}&bpmn_process_guid={bpmn_process.guid}",
|
||||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json is not None
|
||||
assert response.json["process_data_value"] == "HEY"
|
||||
|
||||
def _setup_testing_instance(
|
||||
self,
|
||||
client: FlaskClient,
|
||||
|
|
Loading…
Reference in New Issue