support bpmn executable property (#934)
* support executable property * process model with no primary file is not executable * filter by executable and primary file and avoid snagging g.user from service * kill Start buttons on process group list as well * use more similar code to check is executable and write integration test * add a test and improve a variable name --------- Co-authored-by: burnettk <burnettk@users.noreply.github.com>
This commit is contained in:
parent
aaec9f6ffc
commit
020519a724
|
@ -39,6 +39,7 @@ class ProcessModelInfo:
|
||||||
description: str
|
description: str
|
||||||
primary_file_name: str | None = None
|
primary_file_name: str | None = None
|
||||||
primary_process_id: str | None = None
|
primary_process_id: str | None = None
|
||||||
|
is_executable: bool = True
|
||||||
fault_or_suspend_on_exception: str = NotificationType.fault.value
|
fault_or_suspend_on_exception: str = NotificationType.fault.value
|
||||||
exception_notification_addresses: list[str] = field(default_factory=list)
|
exception_notification_addresses: list[str] = field(default_factory=list)
|
||||||
metadata_extraction_paths: list[dict[str, str]] | None = None
|
metadata_extraction_paths: list[dict[str, str]] | None = None
|
||||||
|
|
|
@ -62,6 +62,7 @@ def extension_list() -> flask.wrappers.Response:
|
||||||
process_model_extensions = []
|
process_model_extensions = []
|
||||||
if current_app.config["SPIFFWORKFLOW_BACKEND_EXTENSIONS_API_ENABLED"]:
|
if current_app.config["SPIFFWORKFLOW_BACKEND_EXTENSIONS_API_ENABLED"]:
|
||||||
process_model_extensions = ProcessModelService.get_process_models_for_api(
|
process_model_extensions = ProcessModelService.get_process_models_for_api(
|
||||||
|
user=g.user,
|
||||||
process_group_id=current_app.config["SPIFFWORKFLOW_BACKEND_EXTENSIONS_PROCESS_MODEL_PREFIX"],
|
process_group_id=current_app.config["SPIFFWORKFLOW_BACKEND_EXTENSIONS_PROCESS_MODEL_PREFIX"],
|
||||||
recursive=True,
|
recursive=True,
|
||||||
filter_runnable_as_extension=True,
|
filter_runnable_as_extension=True,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
"""APIs for dealing with process groups, process models, and process instances."""
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
@ -24,6 +23,7 @@ from spiffworkflow_backend.models.process_group import ProcessGroup
|
||||||
from spiffworkflow_backend.models.process_instance_report import ProcessInstanceReportModel
|
from spiffworkflow_backend.models.process_instance_report import ProcessInstanceReportModel
|
||||||
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
||||||
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
|
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
|
||||||
|
from spiffworkflow_backend.models.reference_cache import ReferenceCacheModel
|
||||||
from spiffworkflow_backend.routes.process_api_blueprint import _commit_and_push_to_git
|
from spiffworkflow_backend.routes.process_api_blueprint import _commit_and_push_to_git
|
||||||
from spiffworkflow_backend.routes.process_api_blueprint import _get_process_model
|
from spiffworkflow_backend.routes.process_api_blueprint import _get_process_model
|
||||||
from spiffworkflow_backend.routes.process_api_blueprint import _un_modify_modified_process_model_id
|
from spiffworkflow_backend.routes.process_api_blueprint import _un_modify_modified_process_model_id
|
||||||
|
@ -165,10 +165,23 @@ def process_model_show(modified_process_model_identifier: str, include_file_refe
|
||||||
files = FileSystemService.get_sorted_files(process_model)
|
files = FileSystemService.get_sorted_files(process_model)
|
||||||
process_model.files = files
|
process_model.files = files
|
||||||
|
|
||||||
|
reference_cache_processes = (
|
||||||
|
ReferenceCacheModel.basic_query()
|
||||||
|
.filter_by(
|
||||||
|
type="process",
|
||||||
|
identifier=process_model.primary_process_id,
|
||||||
|
relative_location=process_model.id,
|
||||||
|
file_name=process_model.primary_file_name,
|
||||||
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
ProcessModelService.embellish_with_is_executable_property([process_model], reference_cache_processes)
|
||||||
|
|
||||||
if include_file_references:
|
if include_file_references:
|
||||||
for file in process_model.files:
|
for file in process_model.files:
|
||||||
file.references = SpecFileService.get_references_for_file(file, process_model)
|
refs = SpecFileService.get_references_for_file(file, process_model)
|
||||||
|
file.references = refs
|
||||||
process_model.parent_groups = ProcessModelService.get_parent_group_array(process_model.id)
|
process_model.parent_groups = ProcessModelService.get_parent_group_array(process_model.id)
|
||||||
try:
|
try:
|
||||||
current_git_revision = GitService.get_current_revision()
|
current_git_revision = GitService.get_current_revision()
|
||||||
|
@ -215,6 +228,7 @@ def process_model_list(
|
||||||
per_page: int = 100,
|
per_page: int = 100,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
process_models = ProcessModelService.get_process_models_for_api(
|
process_models = ProcessModelService.get_process_models_for_api(
|
||||||
|
user=g.user,
|
||||||
process_group_id=process_group_identifier,
|
process_group_id=process_group_identifier,
|
||||||
recursive=recursive,
|
recursive=recursive,
|
||||||
filter_runnable_by_user=filter_runnable_by_user,
|
filter_runnable_by_user=filter_runnable_by_user,
|
||||||
|
|
|
@ -19,6 +19,8 @@ from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
from spiffworkflow_backend.models.process_model import PROCESS_MODEL_SUPPORTED_KEYS_FOR_DISK_SERIALIZATION
|
from spiffworkflow_backend.models.process_model import PROCESS_MODEL_SUPPORTED_KEYS_FOR_DISK_SERIALIZATION
|
||||||
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
||||||
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
|
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
|
||||||
|
from spiffworkflow_backend.models.reference_cache import Reference
|
||||||
|
from spiffworkflow_backend.models.reference_cache import ReferenceCacheModel
|
||||||
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||||
|
@ -207,6 +209,7 @@ class ProcessModelService(FileSystemService):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_process_models_for_api(
|
def get_process_models_for_api(
|
||||||
cls,
|
cls,
|
||||||
|
user: UserModel,
|
||||||
process_group_id: str | None = None,
|
process_group_id: str | None = None,
|
||||||
recursive: bool | None = False,
|
recursive: bool | None = False,
|
||||||
filter_runnable_by_user: bool | None = False,
|
filter_runnable_by_user: bool | None = False,
|
||||||
|
@ -226,17 +229,14 @@ class ProcessModelService(FileSystemService):
|
||||||
|
|
||||||
permission_to_check = "read"
|
permission_to_check = "read"
|
||||||
permission_base_uri = "/v1.0/process-models"
|
permission_base_uri = "/v1.0/process-models"
|
||||||
user = UserService.current_user()
|
extension_prefix = current_app.config["SPIFFWORKFLOW_BACKEND_EXTENSIONS_PROCESS_MODEL_PREFIX"]
|
||||||
if filter_runnable_by_user:
|
if filter_runnable_by_user:
|
||||||
permission_to_check = "create"
|
permission_to_check = "create"
|
||||||
permission_base_uri = "/v1.0/process-instances"
|
permission_base_uri = "/v1.0/process-instances"
|
||||||
if filter_runnable_as_extension:
|
if filter_runnable_as_extension:
|
||||||
permission_to_check = "create"
|
permission_to_check = "create"
|
||||||
permission_base_uri = "/v1.0/extensions"
|
permission_base_uri = "/v1.0/extensions"
|
||||||
process_model_identifiers = [
|
process_model_identifiers = [p.id.replace(f"{extension_prefix}/", "") for p in process_models]
|
||||||
p.id.replace(f"{current_app.config['SPIFFWORKFLOW_BACKEND_EXTENSIONS_PROCESS_MODEL_PREFIX']}/", "")
|
|
||||||
for p in process_models
|
|
||||||
]
|
|
||||||
|
|
||||||
# these are the ones (identifiers, at least) you are allowed to start
|
# these are the ones (identifiers, at least) you are allowed to start
|
||||||
permitted_process_model_identifiers = cls.process_model_identifiers_with_permission_for_user(
|
permitted_process_model_identifiers = cls.process_model_identifiers_with_permission_for_user(
|
||||||
|
@ -246,17 +246,70 @@ class ProcessModelService(FileSystemService):
|
||||||
process_model_identifiers=process_model_identifiers,
|
process_model_identifiers=process_model_identifiers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
reference_cache_processes = ReferenceCacheModel.basic_query().filter_by(type="process").all()
|
||||||
|
process_models = cls.embellish_with_is_executable_property(process_models, reference_cache_processes)
|
||||||
|
|
||||||
|
if filter_runnable_by_user or filter_runnable_as_extension:
|
||||||
|
process_models = cls.filter_by_runnable(process_models, reference_cache_processes)
|
||||||
|
|
||||||
permitted_process_models = []
|
permitted_process_models = []
|
||||||
for process_model in process_models:
|
for process_model in process_models:
|
||||||
process_model_identifier = process_model.id
|
process_model_identifier = process_model.id
|
||||||
if filter_runnable_as_extension:
|
if filter_runnable_as_extension:
|
||||||
process_model_identifier = process_model.id.replace(
|
process_model_identifier = process_model.id.replace(f"{extension_prefix}/", "")
|
||||||
f"{current_app.config['SPIFFWORKFLOW_BACKEND_EXTENSIONS_PROCESS_MODEL_PREFIX']}/", ""
|
|
||||||
)
|
|
||||||
if process_model_identifier in permitted_process_model_identifiers:
|
if process_model_identifier in permitted_process_model_identifiers:
|
||||||
permitted_process_models.append(process_model)
|
permitted_process_models.append(process_model)
|
||||||
|
|
||||||
return permitted_process_models
|
return permitted_process_models
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def embellish_with_is_executable_property(
|
||||||
|
cls, process_models: list[ProcessModelInfo], reference_cache_processes: list[ReferenceCacheModel]
|
||||||
|
) -> list[ProcessModelInfo]:
|
||||||
|
for process_model in process_models:
|
||||||
|
matching_reference_cache_process = cls.find_reference_cache_process_for_process_model(
|
||||||
|
reference_cache_processes, process_model
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
matching_reference_cache_process
|
||||||
|
and matching_reference_cache_process.properties
|
||||||
|
and "is_executable" in matching_reference_cache_process.properties
|
||||||
|
and matching_reference_cache_process.properties["is_executable"] is False
|
||||||
|
):
|
||||||
|
process_model.is_executable = False
|
||||||
|
else:
|
||||||
|
process_model.is_executable = True
|
||||||
|
|
||||||
|
return process_models
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_by_runnable(
|
||||||
|
cls, process_models: list[ProcessModelInfo], reference_cache_processes: list[ReferenceCacheModel]
|
||||||
|
) -> list[ProcessModelInfo]:
|
||||||
|
runnable_process_models = []
|
||||||
|
for process_model in process_models:
|
||||||
|
# if you want to be able to run a process model, it must have a primary file in addition to being executable
|
||||||
|
if (
|
||||||
|
process_model.primary_file_name is not None
|
||||||
|
and process_model.primary_file_name != ""
|
||||||
|
and process_model.is_executable
|
||||||
|
):
|
||||||
|
runnable_process_models.append(process_model)
|
||||||
|
return runnable_process_models
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find_reference_cache_process_for_process_model(
|
||||||
|
cls, reference_cache_processes: list[ReferenceCacheModel], process_model: ProcessModelInfo
|
||||||
|
) -> ReferenceCacheModel | None:
|
||||||
|
for reference_cache_process in reference_cache_processes:
|
||||||
|
if (
|
||||||
|
reference_cache_process.identifier == process_model.primary_process_id
|
||||||
|
and reference_cache_process.file_name == process_model.primary_file_name
|
||||||
|
and reference_cache_process.relative_location == process_model.id
|
||||||
|
):
|
||||||
|
return reference_cache_process
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def process_model_identifiers_with_permission_for_user(
|
def process_model_identifiers_with_permission_for_user(
|
||||||
cls, user: UserModel, permission_to_check: str, permission_base_uri: str, process_model_identifiers: list[str]
|
cls, user: UserModel, permission_to_check: str, permission_base_uri: str, process_model_identifiers: list[str]
|
||||||
|
@ -314,6 +367,13 @@ class ProcessModelService(FileSystemService):
|
||||||
parent_group_array.append({"id": parent_group.id, "display_name": parent_group.display_name})
|
parent_group_array.append({"id": parent_group.id, "display_name": parent_group.display_name})
|
||||||
return {"cache": process_group_cache, "process_groups": parent_group_array}
|
return {"cache": process_group_cache, "process_groups": parent_group_array}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reference_for_primary_file(cls, references: list[Reference], primary_file: str) -> Reference | None:
|
||||||
|
for reference in references:
|
||||||
|
if reference.file_name == primary_file:
|
||||||
|
return reference
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_parent_group_array(cls, process_identifier: str) -> list[ProcessGroupLite]:
|
def get_parent_group_array(cls, process_identifier: str) -> list[ProcessGroupLite]:
|
||||||
parent_group_lites_with_cache = cls.get_parent_group_array_and_cache_it(process_identifier, {})
|
parent_group_lites_with_cache = cls.get_parent_group_array_and_cache_it(process_identifier, {})
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?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:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" 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_wip" isExecutable="false">
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>Flow_17db3yp</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_17db3yp" sourceRef="StartEvent_1" targetRef="Activity_0qpzdpu" />
|
||||||
|
<bpmn:endEvent id="EndEvent_1">
|
||||||
|
<bpmn:extensionElements>
|
||||||
|
<spiffworkflow:instructionsForEndUser>The process instance completed successfully.</spiffworkflow:instructionsForEndUser>
|
||||||
|
</bpmn:extensionElements>
|
||||||
|
<bpmn:incoming>Flow_12pkbxb</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_12pkbxb" sourceRef="Activity_0qpzdpu" targetRef="EndEvent_1" />
|
||||||
|
<bpmn:manualTask id="Activity_0qpzdpu" name="Example manual task">
|
||||||
|
<bpmn:extensionElements>
|
||||||
|
<spiffworkflow:instructionsForEndUser>This is an example **Manual Task**. A **Manual Task** is designed to allow someone to complete a task outside of the system and then report back that it is complete. You can click the *Continue* button to proceed. When you are done running this process, you can edit the **Process Model** to include a:
|
||||||
|
|
||||||
|
* **Script Task** - write a short snippet of python code to update some data
|
||||||
|
* **User Task** - generate a form that collects information from a user
|
||||||
|
* **Service Task** - communicate with an external API to fetch or update some data.
|
||||||
|
|
||||||
|
You can also change the text you are reading here by updating the *Instructions* on this example manual task.</spiffworkflow:instructionsForEndUser>
|
||||||
|
</bpmn:extensionElements>
|
||||||
|
<bpmn:incoming>Flow_17db3yp</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>Flow_12pkbxb</bpmn:outgoing>
|
||||||
|
</bpmn:manualTask>
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_wip">
|
||||||
|
<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_14za570_di" bpmnElement="EndEvent_1">
|
||||||
|
<dc:Bounds x="432" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Activity_0zqotmb_di" bpmnElement="Activity_0qpzdpu">
|
||||||
|
<dc:Bounds x="270" y="137" width="100" height="80" />
|
||||||
|
<bpmndi:BPMNLabel />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_17db3yp_di" bpmnElement="Flow_17db3yp">
|
||||||
|
<di:waypoint x="215" y="177" />
|
||||||
|
<di:waypoint x="270" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_12pkbxb_di" bpmnElement="Flow_12pkbxb">
|
||||||
|
<di:waypoint x="370" y="177" />
|
||||||
|
<di:waypoint x="432" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
|
@ -7,6 +7,7 @@ from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.spec_file_service import SpecFileService
|
from spiffworkflow_backend.services.spec_file_service import SpecFileService
|
||||||
|
|
||||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||||
|
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
|
||||||
|
|
||||||
|
|
||||||
class TestProcessModelsController(BaseTest):
|
class TestProcessModelsController(BaseTest):
|
||||||
|
@ -60,3 +61,58 @@ class TestProcessModelsController(BaseTest):
|
||||||
assert response.json["message"].startswith(
|
assert response.json["message"].startswith(
|
||||||
"NotAuthorizedError: You are not authorized to use one or more processes as a called element"
|
"NotAuthorizedError: You are not authorized to use one or more processes as a called element"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_process_model_show(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
client: FlaskClient,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_super_admin_user: UserModel,
|
||||||
|
) -> None:
|
||||||
|
process_model_1 = load_test_spec(
|
||||||
|
"test_group/hello_world",
|
||||||
|
bpmn_file_name="hello_world.bpmn",
|
||||||
|
process_model_source_directory="hello_world",
|
||||||
|
)
|
||||||
|
process_model_2 = load_test_spec(
|
||||||
|
"test_group/non_executable",
|
||||||
|
bpmn_file_name="non_executable.bpmn",
|
||||||
|
process_model_source_directory="non_executable",
|
||||||
|
)
|
||||||
|
json = self._get_process_show_show_response(
|
||||||
|
client, with_super_admin_user, process_model_1.modified_process_model_identifier()
|
||||||
|
)
|
||||||
|
assert json["id"] == "test_group/hello_world"
|
||||||
|
assert json["is_executable"] is True
|
||||||
|
|
||||||
|
json = self._get_process_show_show_response(
|
||||||
|
client, with_super_admin_user, process_model_2.modified_process_model_identifier()
|
||||||
|
)
|
||||||
|
assert json["id"] == "test_group/non_executable"
|
||||||
|
assert json["is_executable"] is False
|
||||||
|
|
||||||
|
def test_process_model_show_when_not_found(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
client: FlaskClient,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_super_admin_user: UserModel,
|
||||||
|
) -> None:
|
||||||
|
json = self._get_process_show_show_response(
|
||||||
|
client, with_super_admin_user, "bad-model-does-not-exist", expected_response=400
|
||||||
|
)
|
||||||
|
assert json["error_code"] == "process_model_cannot_be_found"
|
||||||
|
|
||||||
|
def _get_process_show_show_response(
|
||||||
|
self, client: FlaskClient, user: UserModel, process_model_id: str, expected_response: int = 200
|
||||||
|
) -> dict:
|
||||||
|
url = f"/v1.0/process-models/{process_model_id}"
|
||||||
|
response = client.get(
|
||||||
|
url,
|
||||||
|
follow_redirects=True,
|
||||||
|
headers=self.logged_in_headers(user),
|
||||||
|
)
|
||||||
|
assert response.status_code == expected_response
|
||||||
|
assert response.json is not None
|
||||||
|
process_model_data: dict = response.json
|
||||||
|
return process_model_data
|
||||||
|
|
|
@ -129,3 +129,44 @@ class TestProcessModelService(BaseTest):
|
||||||
pg_identifiers = [pg.id for pg in process_groups]
|
pg_identifiers = [pg.id for pg in process_groups]
|
||||||
assert len(pg_identifiers) == 1
|
assert len(pg_identifiers) == 1
|
||||||
assert process_groups[0].id == "a1/b2"
|
assert process_groups[0].id == "a1/b2"
|
||||||
|
|
||||||
|
def test_get_process_models_for_api(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
) -> None:
|
||||||
|
user = BaseTest.create_user_with_permission("super_admin")
|
||||||
|
process_model = load_test_spec(
|
||||||
|
"test_group/hello_world",
|
||||||
|
bpmn_file_name="hello_world.bpmn",
|
||||||
|
process_model_source_directory="hello_world",
|
||||||
|
)
|
||||||
|
assert process_model.display_name == "test_group/hello_world"
|
||||||
|
|
||||||
|
primary_process_id = process_model.primary_process_id
|
||||||
|
assert primary_process_id == "Process_HelloWorld"
|
||||||
|
|
||||||
|
process_models = ProcessModelService.get_process_models_for_api(user=user, recursive=True, filter_runnable_by_user=True)
|
||||||
|
assert len(process_models) == 1
|
||||||
|
assert process_model.primary_process_id == primary_process_id
|
||||||
|
|
||||||
|
process_model = load_test_spec(
|
||||||
|
"test_group/hello_world_2",
|
||||||
|
bpmn_file_name="hello_world.bpmn",
|
||||||
|
process_model_source_directory="hello_world",
|
||||||
|
)
|
||||||
|
|
||||||
|
# this model should not show up in results because it has no primary_file_name
|
||||||
|
ProcessModelService.update_process_model(process_model, {"primary_file_name": None})
|
||||||
|
process_models = ProcessModelService.get_process_models_for_api(user=user, recursive=True, filter_runnable_by_user=True)
|
||||||
|
assert len(process_models) == 1
|
||||||
|
|
||||||
|
process_model = load_test_spec(
|
||||||
|
"non_executable/non_executable",
|
||||||
|
bpmn_file_name="non_executable.bpmn",
|
||||||
|
process_model_source_directory="non_executable",
|
||||||
|
)
|
||||||
|
|
||||||
|
# this model should not show up in results because it is not executable
|
||||||
|
process_models = ProcessModelService.get_process_models_for_api(user=user, recursive=True, filter_runnable_by_user=True)
|
||||||
|
assert len(process_models) == 1
|
||||||
|
|
|
@ -137,7 +137,9 @@ export default function ProcessInstanceRun({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const startButton = (
|
let startButton = null;
|
||||||
|
if (processModel.primary_file_name && processModel.is_executable) {
|
||||||
|
startButton = (
|
||||||
<Button
|
<Button
|
||||||
data-qa="start-process-instance"
|
data-qa="start-process-instance"
|
||||||
onClick={processInstanceCreateAndRun}
|
onClick={processInstanceCreateAndRun}
|
||||||
|
@ -148,6 +150,7 @@ export default function ProcessInstanceRun({
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// if checkPermissions is false then assume the page using this component has already checked the permissions
|
// if checkPermissions is false then assume the page using this component has already checked the permissions
|
||||||
if (checkPermissions) {
|
if (checkPermissions) {
|
||||||
|
|
|
@ -289,6 +289,7 @@ export interface ProcessModel {
|
||||||
fault_or_suspend_on_exception?: string;
|
fault_or_suspend_on_exception?: string;
|
||||||
exception_notification_addresses?: string[];
|
exception_notification_addresses?: string[];
|
||||||
bpmn_version_control_identifier?: string;
|
bpmn_version_control_identifier?: string;
|
||||||
|
is_executable?: boolean;
|
||||||
actions?: ApiActions;
|
actions?: ApiActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ export default function ProcessModelShow() {
|
||||||
setSelectedTabIndex(newTabIndex);
|
setSelectedTabIndex(newTabIndex);
|
||||||
};
|
};
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-models/${modifiedProcessModelId}`,
|
path: `/process-models/${modifiedProcessModelId}?include_file_references=true`,
|
||||||
successCallback: processResult,
|
successCallback: processResult,
|
||||||
});
|
});
|
||||||
}, [reloadModel, modifiedProcessModelId]);
|
}, [reloadModel, modifiedProcessModelId]);
|
||||||
|
@ -795,7 +795,9 @@ export default function ProcessModelShow() {
|
||||||
</Can>
|
</Can>
|
||||||
</Stack>
|
</Stack>
|
||||||
<p className="process-description">{processModel.description}</p>
|
<p className="process-description">{processModel.description}</p>
|
||||||
{processModel.primary_file_name ? processStartButton : null}
|
{processModel.primary_file_name && processModel.is_executable
|
||||||
|
? processStartButton
|
||||||
|
: null}
|
||||||
<div className="with-top-margin">{tabArea()}</div>
|
<div className="with-top-margin">{tabArea()}</div>
|
||||||
{permissionsLoaded ? (
|
{permissionsLoaded ? (
|
||||||
<span data-qa="process-model-show-permissions-loaded" />
|
<span data-qa="process-model-show-permissions-loaded" />
|
||||||
|
|
Loading…
Reference in New Issue