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>
This commit is contained in:
jasquat 2024-05-03 15:10:55 +00:00 committed by GitHub
parent 52bbd10f9c
commit 84feef321d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 30 deletions

View File

@ -2068,7 +2068,7 @@ paths:
- name: process_identifier
in: query
required: false
description: The identifier of the process the data object is in.
description: "DEPRECATED - only the bpmn_process_guid is needed now: The identifier of the process the data object is in."
schema:
type: string
- name: bpmn_process_guid

View File

@ -3,7 +3,6 @@ import os
import uuid
from typing import Any
from typing import TypedDict
from uuid import UUID
import flask.wrappers
import sentry_sdk
@ -30,9 +29,11 @@ from spiffworkflow_backend.exceptions.error import HumanTaskAlreadyCompletedErro
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.db import db
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.principal import PrincipalModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
@ -135,43 +136,35 @@ def _process_data_fetcher(
bpmn_process_guid: str | None = None,
process_identifier: str | None = None,
) -> flask.wrappers.Response:
if process_identifier and bpmn_process_guid is None:
raise ApiError(
error_code="missing_required_parameter",
message="process_identifier was given but bpmn_process_guid was not. Both must be provided if either is required.",
status_code=404,
)
if process_identifier is None and bpmn_process_guid:
raise ApiError(
error_code="missing_required_parameter",
message="bpmn_process_guid was given but process_identifier was not. Both must be provided if either is required.",
status_code=404,
)
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
processor = ProcessInstanceProcessor(process_instance)
if bpmn_process_guid is not None:
bpmn_process = BpmnProcessModel.query.filter_by(guid=bpmn_process_guid).first()
else:
bpmn_process = process_instance.bpmn_process
if bpmn_process is None:
raise ApiError(
error_code="bpmn_process_not_found",
message=f"Cannot find a bpmn process with guid '{bpmn_process_guid}' for process instance {process_instance.id}",
status_code=404,
)
bpmn_process_instance = processor.bpmn_process_instance
bpmn_process_data = processor.get_data()
if process_identifier and bpmn_process_instance.spec.name != process_identifier:
bpmn_process_instance = processor.bpmn_process_instance.subprocesses.get(UUID(bpmn_process_guid))
if bpmn_process_instance is None:
raise ApiError(
error_code="bpmn_process_not_found",
message=f"Cannot find a bpmn process with guid '{bpmn_process_guid}' for process instance {process_instance.id}",
status_code=404,
)
bpmn_process_data = bpmn_process_instance.data
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_instance.spec.data_objects
data_objects = bpmn_process_data["data_objects"]
data_object = data_objects.get(process_data_identifier)
if data_object is None:
raise ApiError(
error_code="data_object_not_found",
message=(
f"Cannot find a data object with identifier '{process_data_identifier}' for bpmn process '{process_identifier}'"
f" in process instance {process_instance.id}"
f"Cannot find a data object with identifier '{process_data_identifier}' for bpmn process"
f" '{bpmn_process.bpmn_process_definition.bpmn_identifier}' in process instance {process_instance.id}"
),
status_code=404,
)

View File

@ -5,6 +5,7 @@ import os
import time
from hashlib import sha256
from typing import Any
from unittest.mock import patch
import flask
import pytest
@ -12,6 +13,8 @@ from flask.app import Flask
from flask.testing import FlaskClient
from SpiffWorkflow.util.task import TaskState # type: ignore
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.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
@ -3323,6 +3326,55 @@ class TestProcessApi(BaseTest):
assert response.json is not None
assert response.json["process_data_value"] == "hey"
def test_process_data_show_with_sub_process(
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/with-service-task-call-activity-sub-process",
process_model_source_directory="with-service-task-call-activity-sub-process",
)
process_instance_one = self.create_process_instance_from_process_model(process_model)
processor = ProcessInstanceProcessor(process_instance_one)
connector_response = {
"body": '{"ok": true}',
"mimetype": "application/json",
"http_status": 200,
"operator_identifier": "http/GetRequestV2",
}
with patch("requests.post") as mock_post:
mock_post.return_value.status_code = 200
mock_post.return_value.ok = True
mock_post.return_value.text = json.dumps(connector_response)
processor.do_engine_steps(save=True)
self.complete_next_manual_task(processor, execution_mode="synchronous")
self.complete_next_manual_task(processor, execution_mode="synchronous", data={"firstName": "Chuck"})
assert process_instance_one.status == "complete"
process_identifier = "call_activity_sub_process"
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)}/sub_level_data_object_three/"
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"] == "d"
def _setup_testing_instance(
self,
client: FlaskClient,