diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock
index 469dfdce..c578ff05 100644
--- a/spiffworkflow-backend/poetry.lock
+++ b/spiffworkflow-backend/poetry.lock
@@ -1945,8 +1945,6 @@ files = [
{file = "psycopg2-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:426f9f29bde126913a20a96ff8ce7d73fd8a216cfb323b1f04da402d452853c3"},
{file = "psycopg2-2.9.9-cp311-cp311-win32.whl", hash = "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372"},
{file = "psycopg2-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981"},
- {file = "psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024"},
- {file = "psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693"},
{file = "psycopg2-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:5e0d98cade4f0e0304d7d6f25bbfbc5bd186e07b38eac65379309c4ca3193efa"},
{file = "psycopg2-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:7e2dacf8b009a1c1e843b5213a87f7c544b2b042476ed7755be813eaf4e8347a"},
{file = "psycopg2-2.9.9-cp38-cp38-win32.whl", hash = "sha256:ff432630e510709564c01dafdbe996cb552e0b9f3f065eb89bdce5bd31fabf4c"},
@@ -2286,7 +2284,6 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
- {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
@@ -2663,51 +2660,37 @@ python-versions = ">=3.6"
files = [
{file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"},
{file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"},
+ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"},
{file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"},
- {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"},
- {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"},
- {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"},
{file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"},
{file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"},
{file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"},
{file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"},
+ {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"},
{file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"},
- {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"},
- {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"},
- {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"},
{file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"},
{file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"},
{file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"},
{file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"},
+ {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"},
{file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"},
- {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"},
- {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"},
- {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"},
- {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"},
- {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"},
{file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"},
{file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"},
{file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"},
- {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"},
+ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"},
{file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"},
- {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"},
- {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"},
{file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"},
{file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"},
{file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"},
{file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"},
- {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"},
+ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"},
{file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"},
- {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"},
- {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"},
{file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"},
{file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"},
{file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"},
{file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"},
- {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"},
+ {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"},
{file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"},
- {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"},
- {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"},
{file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"},
{file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"},
{file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"},
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml
index 9c322083..4668787f 100755
--- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml
@@ -562,7 +562,7 @@ paths:
schema:
$ref: "#/components/schemas/ProcessModel"
- /process-model-tests/{modified_process_model_identifier}:
+ /process-model-tests/run/{modified_process_model_identifier}:
parameters:
- name: modified_process_model_identifier
in: path
@@ -589,13 +589,9 @@ paths:
- Process Model Tests
requestBody:
content:
- multipart/form-data:
+ application/json:
schema:
- type: object
- properties:
- file:
- type: string
- format: binary
+ $ref: "#/components/schemas/AwesomeUnspecifiedPayload"
responses:
"201":
description: Metadata about the uploaded file, but not the file content.
@@ -604,6 +600,47 @@ paths:
schema:
$ref: "#/components/schemas/File"
+ /process-model-tests/create/{modified_process_model_identifier}:
+ parameters:
+ - name: modified_process_model_identifier
+ in: path
+ required: true
+ description: The process_model_id, modified to replace slashes (/)
+ schema:
+ type: string
+ - name: test_case_identifier
+ in: query
+ required: false
+ description: The name of the test case file to run
+ schema:
+ type: string
+ post:
+ operationId: spiffworkflow_backend.routes.process_models_controller.process_model_test_generate
+ summary: Create a bpmn unit test from a process instance
+ tags:
+ - Process Model Tests
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ process_instance_id:
+ description: The id of the associated process instance
+ type: number
+ example: 2
+ nullable: false
+ test_case_identifier:
+ description: The name for the new test case
+ type: string
+ nullable: true
+ responses:
+ "201":
+ description: The generated bpmn unit test
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AwesomeUnspecifiedPayload"
+
/process-models/{modified_process_model_identifier}/files:
parameters:
- name: modified_process_model_identifier
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/error.py b/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/error.py
index 7a7fdaad..61a39143 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/error.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/exceptions/error.py
@@ -55,3 +55,7 @@ class InvalidPermissionError(Exception):
class InvalidRedirectUrlError(Exception):
pass
+
+
+class TaskMismatchError(Exception):
+ pass
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py
index a3bf51db..67cfed72 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py
@@ -25,16 +25,19 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo
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 _find_process_instance_by_id_or_raise
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.services.file_system_service import FileSystemService
from spiffworkflow_backend.services.git_service import GitCommandError
from spiffworkflow_backend.services.git_service import GitService
from spiffworkflow_backend.services.git_service import MissingGitConfigsError
+from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor
from spiffworkflow_backend.services.process_instance_report_service import ProcessInstanceReportNotFoundError
from spiffworkflow_backend.services.process_instance_report_service import ProcessInstanceReportService
from spiffworkflow_backend.services.process_model_service import ProcessModelService
from spiffworkflow_backend.services.process_model_service import ProcessModelWithInstancesNotDeletableError
+from spiffworkflow_backend.services.process_model_test_generator_service import ProcessModelTestGeneratorService
from spiffworkflow_backend.services.process_model_test_runner_service import ProcessModelTestRunner
from spiffworkflow_backend.services.spec_file_service import ProcessModelFileInvalidError
from spiffworkflow_backend.services.spec_file_service import SpecFileService
@@ -352,6 +355,41 @@ def process_model_test_run(
return make_response(jsonify(response_json), 200)
+def process_model_test_generate(modified_process_model_identifier: str, body: dict[str, str | int]) -> flask.wrappers.Response:
+ process_instance_id = body["process_instance_id"]
+
+ if process_instance_id is None:
+ raise ApiError(
+ error_code="missing_process_instance_id",
+ message="Process instance id is required to be in the body of request.",
+ status_code=400,
+ )
+
+ test_case_identifier = body.get("test_case_identifier", f"test_case_for_process_instance_{process_instance_id}")
+ process_instance = _find_process_instance_by_id_or_raise(int(process_instance_id))
+ processor = ProcessInstanceProcessor(process_instance, include_task_data_for_completed_tasks=True)
+ process_instance_dict = processor.serialize()
+ test_case_dict = ProcessModelTestGeneratorService.generate_test_from_process_instance_dict(
+ process_instance_dict, test_case_identifier=str(test_case_identifier)
+ )
+
+ process_model_identifier = modified_process_model_identifier.replace(":", "/")
+ process_model = _get_process_model(process_model_identifier)
+
+ if process_model.primary_file_name is None:
+ raise ApiError(
+ error_code="process_model_primary_file_not_set",
+ message="The primary file is not set for the given process model.",
+ status_code=400,
+ )
+
+ primary_file_name_without_extension = ".".join(process_model.primary_file_name.split(".")[0:-1])
+ test_case_file_name = f"test_{primary_file_name_without_extension}.json"
+ ProcessModelService.add_json_data_to_json_file(process_model, test_case_file_name, test_case_dict)
+
+ return make_response(jsonify(test_case_dict), 200)
+
+
# {
# "natural_language_text": "Create a bug tracker process model \
# with a bug-details form that collects summary, description, and priority"
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py
index c30081cd..b4095d2b 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py
@@ -72,7 +72,8 @@ PATH_SEGMENTS_FOR_PERMISSION_ALL = [
{"path": "/process-instance-terminate", "relevant_permissions": ["create"]},
{"path": "/process-model-natural-language", "relevant_permissions": ["create"]},
{"path": "/process-model-publish", "relevant_permissions": ["create"]},
- {"path": "/process-model-tests", "relevant_permissions": ["create"]},
+ {"path": "/process-model-tests/create", "relevant_permissions": ["create"]},
+ {"path": "/process-model-tests/run", "relevant_permissions": ["create"]},
{"path": "/task-assign", "relevant_permissions": ["create"]},
{"path": "/task-data", "relevant_permissions": ["read", "update"]},
]
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 51062bb3..97ecdd3c 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py
@@ -63,6 +63,7 @@ from spiffworkflow_backend.data_stores.kkv import KKVDataStoreConverter
from spiffworkflow_backend.data_stores.typeahead import TypeaheadDataStore
from spiffworkflow_backend.data_stores.typeahead import TypeaheadDataStoreConverter
from spiffworkflow_backend.exceptions.api_error import ApiError
+from spiffworkflow_backend.exceptions.error import TaskMismatchError
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
from spiffworkflow_backend.models.bpmn_process_definition_relationship import BpmnProcessDefinitionRelationshipModel
@@ -408,7 +409,6 @@ class ProcessInstanceProcessor:
_serializer = BpmnWorkflowSerializer(wf_spec_converter, version=SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION)
PROCESS_INSTANCE_ID_KEY = "process_instance_id"
- VALIDATION_PROCESS_KEY = "validate_only"
# __init__ calls these helpers:
# * get_spec, which returns a spec and any subprocesses (as IdToBpmnProcessSpecMapping dict)
@@ -416,7 +416,6 @@ class ProcessInstanceProcessor:
def __init__(
self,
process_instance_model: ProcessInstanceModel,
- validate_only: bool = False,
script_engine: PythonScriptEngine | None = None,
workflow_completed_handler: WorkflowCompletedHandler | None = None,
process_id_to_run: str | None = None,
@@ -429,7 +428,6 @@ class ProcessInstanceProcessor:
self.additional_processing_identifier = additional_processing_identifier
self.setup_processor_with_process_instance(
process_instance_model=process_instance_model,
- validate_only=validate_only,
process_id_to_run=process_id_to_run,
include_task_data_for_completed_tasks=include_task_data_for_completed_tasks,
)
@@ -437,7 +435,6 @@ class ProcessInstanceProcessor:
def setup_processor_with_process_instance(
self,
process_instance_model: ProcessInstanceModel,
- validate_only: bool = False,
process_id_to_run: str | None = None,
include_task_data_for_completed_tasks: bool = False,
) -> None:
@@ -482,7 +479,6 @@ class ProcessInstanceProcessor:
) = self.__get_bpmn_process_instance(
process_instance_model,
bpmn_process_spec,
- validate_only,
subprocesses=subprocesses,
)
self.set_script_engine(self.bpmn_process_instance, self._script_engine)
@@ -819,7 +815,6 @@ class ProcessInstanceProcessor:
def __get_bpmn_process_instance(
process_instance_model: ProcessInstanceModel,
spec: BpmnProcessSpec | None = None,
- validate_only: bool = False,
subprocesses: IdToBpmnProcessSpecMapping | None = None,
include_task_data_for_completed_tasks: bool = False,
) -> tuple[BpmnWorkflow, dict, dict]:
@@ -840,13 +835,13 @@ class ProcessInstanceProcessor:
# FIXME: the from_dict entrypoint in spiff will one day do this copy instead
process_copy = copy.deepcopy(full_bpmn_process_dict)
bpmn_process_instance = ProcessInstanceProcessor._serializer.from_dict(process_copy)
+ bpmn_process_instance.get_tasks()
except Exception as err:
raise err
finally:
spiff_logger.setLevel(original_spiff_logger_log_level)
else:
bpmn_process_instance = ProcessInstanceProcessor.get_bpmn_process_instance_from_workflow_spec(spec, subprocesses)
- bpmn_process_instance.data[ProcessInstanceProcessor.VALIDATION_PROCESS_KEY] = validate_only
return (
bpmn_process_instance,
@@ -1663,6 +1658,11 @@ class ProcessInstanceProcessor:
return task_json
def complete_task(self, spiff_task: SpiffTask, human_task: HumanTaskModel, user: UserModel) -> None:
+ if str(spiff_task.id) != human_task.task_guid:
+ raise TaskMismatchError(
+ f"Given spiff task ({spiff_task.task_spec.bpmn_id} - {spiff_task.id}) and human task ({human_task.task_name} -"
+ f" {human_task.task_guid}) must match"
+ )
task_model = TaskModel.query.filter_by(guid=human_task.task_id).first()
if task_model is None:
raise TaskNotFoundError(
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py
index ee27c779..15d0f76b 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py
@@ -114,6 +114,17 @@ class ProcessModelService(FileSystemService):
configured_prefix = current_app.config["SPIFFWORKFLOW_BACKEND_EXTENSIONS_PROCESS_MODEL_PREFIX"]
return process_model.id.startswith(f"{configured_prefix}/")
+ @classmethod
+ def add_json_data_to_json_file(cls, process_model: ProcessModelInfo, file_name: str, json_data: dict) -> None:
+ full_json_data = json_data
+ process_model_path = os.path.abspath(os.path.join(FileSystemService.root_path(), process_model.id_for_file_path()))
+ json_path = os.path.abspath(os.path.join(process_model_path, file_name))
+ if os.path.exists(json_path):
+ with open(json_path) as f:
+ existing_json = json.loads(f.read())
+ full_json_data = {**existing_json, **json_data}
+ cls.write_json_file(json_path, full_json_data)
+
@classmethod
def save_process_model(cls, process_model: ProcessModelInfo) -> None:
process_model_path = os.path.abspath(os.path.join(FileSystemService.root_path(), process_model.id_for_file_path()))
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_test_generator_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_test_generator_service.py
new file mode 100644
index 00000000..538c1c11
--- /dev/null
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_test_generator_service.py
@@ -0,0 +1,46 @@
+import copy
+
+from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore
+from SpiffWorkflow.bpmn.specs.mixins import ServiceTaskMixin # type: ignore
+from SpiffWorkflow.spiff.serializer.config import SPIFF_CONFIG # type: ignore
+
+
+class ProcessModelTestGeneratorService:
+ @classmethod
+ def generate_test_from_process_instance_dict(
+ cls, process_instance_dict: dict, test_case_identifier: str = "auto_generated_test_case"
+ ) -> dict:
+ wf_spec_converter = BpmnWorkflowSerializer.configure(SPIFF_CONFIG)
+ serializer = BpmnWorkflowSerializer(wf_spec_converter)
+ process_instance_dict_copy = copy.deepcopy(process_instance_dict)
+ bpmn_process_instance = serializer.from_dict(process_instance_dict_copy)
+
+ human_tasks = bpmn_process_instance.get_tasks(manual=True)
+ service_tasks = bpmn_process_instance.get_tasks(spec_class=ServiceTaskMixin)
+ all_spiff_tasks = human_tasks + service_tasks
+ bpmn_unit_test_specification = {"tasks": {}, "expected_output_json": bpmn_process_instance.data}
+ for spiff_task in all_spiff_tasks:
+ process_id = spiff_task.workflow.spec.name
+ bpmn_task_identifier = f"{process_id}:{spiff_task.task_spec.bpmn_id}"
+ if bpmn_task_identifier not in bpmn_unit_test_specification["tasks"]:
+ bpmn_unit_test_specification["tasks"][bpmn_task_identifier] = {}
+ if "data" not in bpmn_unit_test_specification["tasks"][bpmn_task_identifier]:
+ bpmn_unit_test_specification["tasks"][bpmn_task_identifier]["data"] = []
+
+ previous_task_data = spiff_task.parent.data
+ current_task_data = spiff_task.data
+ cls.remove_duplicates(previous_task_data, current_task_data)
+ bpmn_unit_test_specification["tasks"][bpmn_task_identifier]["data"].append(current_task_data)
+
+ return {test_case_identifier: bpmn_unit_test_specification}
+
+ @classmethod
+ def remove_duplicates(cls, dict_one: dict, dict_two: dict) -> None:
+ for key in list(dict_two.keys()):
+ if key in dict_one:
+ if isinstance(dict_one[key], dict) and isinstance(dict_two[key], dict):
+ cls.remove_duplicates(dict_one[key], dict_two[key])
+ if not dict_two[key]:
+ del dict_two[key]
+ else:
+ del dict_two[key]
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_test_runner_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_test_runner_service.py
index cb0fb374..97f8ff05 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_test_runner_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_test_runner_service.py
@@ -30,6 +30,44 @@ from spiffworkflow_backend.services.custom_parser import MyCustomParser
from spiffworkflow_backend.services.jinja_service import JinjaHelpers
from spiffworkflow_backend.services.process_instance_processor import CustomScriptEngineEnvironment
+DEFAULT_NSMAP = {
+ "bpmn": "http://www.omg.org/spec/BPMN/20100524/MODEL",
+ "bpmndi": "http://www.omg.org/spec/BPMN/20100524/DI",
+ "dc": "http://www.omg.org/spec/DD/20100524/DC",
+}
+
+
+"""
+JSON file name:
+ The name should be in format "test_BPMN_FILE_NAME.json".
+
+BPMN_TASK_IDENTIIFER:
+ can be either task bpmn identifier or in format:
+ [BPMN_PROCESS_ID]:[TASK_BPMN_IDENTIFIER]
+ example: 'BasicServiceTaskProcess:service_task_one'
+ this allows for tasks to share bpmn identifiers across models
+ which is useful for call activities
+
+DATA for tasks:
+ This is an array of task data. This allows for the task to
+ be called multiple times and given different data each time.
+ This is useful for testing loops where each iteration needs
+ different input. The test will fail if the task is called
+ multiple times without task data input for each call.
+
+JSON file format:
+{
+ TEST_CASE_NAME: {
+ "tasks": {
+ BPMN_TASK_IDENTIIFER: {
+ "data": [DATA]
+ }
+ },
+ "expected_output_json": DATA
+ }
+}
+"""
+
class UnrunnableTestCaseError(Exception):
pass
@@ -55,6 +93,29 @@ class BpmnFileMissingExecutableProcessError(Exception):
pass
+@dataclass
+class TestCaseErrorDetails:
+ error_messages: list[str]
+ task_error_line: str | None = None
+ task_trace: list[str] | None = None
+ task_bpmn_identifier: str | None = None
+ task_bpmn_type: str | None = None
+ task_bpmn_name: str | None = None
+ task_line_number: int | None = None
+ stacktrace: list[str] | None = None
+
+ output_data: dict | None = None
+ expected_data: dict | None = None
+
+
+@dataclass
+class TestCaseResult:
+ passed: bool
+ bpmn_file: str
+ test_case_identifier: str
+ test_case_error_details: TestCaseErrorDetails | None = None
+
+
def _import(name: str, glbls: dict[str, Any], *args: Any) -> None:
if name not in glbls:
raise ImportError(f"Import not allowed: {name}", name=name)
@@ -137,25 +198,6 @@ class ProcessModelTestRunnerScriptEngine(PythonScriptEngine): # type: ignore
raise Exception("please override this service task in your bpmn unit test json")
-@dataclass
-class TestCaseErrorDetails:
- error_messages: list[str]
- task_error_line: str | None = None
- task_trace: list[str] | None = None
- task_bpmn_identifier: str | None = None
- task_bpmn_name: str | None = None
- task_line_number: int | None = None
- stacktrace: list[str] | None = None
-
-
-@dataclass
-class TestCaseResult:
- passed: bool
- bpmn_file: str
- test_case_identifier: str
- test_case_error_details: TestCaseErrorDetails | None = None
-
-
class ProcessModelTestRunnerDelegate:
"""Abstract class for the process model test runner delegate.
@@ -288,45 +330,6 @@ class ProcessModelTestRunnerMostlyPureSpiffDelegate(ProcessModelTestRunnerDelega
self.bpmn_processes_to_file_mappings[bpmn_process_identifier] = file_norm
-DEFAULT_NSMAP = {
- "bpmn": "http://www.omg.org/spec/BPMN/20100524/MODEL",
- "bpmndi": "http://www.omg.org/spec/BPMN/20100524/DI",
- "dc": "http://www.omg.org/spec/DD/20100524/DC",
-}
-
-
-"""
-JSON file name:
- The name should be in format "test_BPMN_FILE_NAME_IT_TESTS.json".
-
-BPMN_TASK_IDENTIIFER:
- can be either task bpmn identifier or in format:
- [BPMN_PROCESS_ID]:[TASK_BPMN_IDENTIFIER]
- example: 'BasicServiceTaskProcess:service_task_one'
- this allows for tasks to share bpmn identifiers across models
- which is useful for call activities
-
-DATA for tasks:
- This is an array of task data. This allows for the task to
- be called multiple times and given different data each time.
- This is useful for testing loops where each iteration needs
- different input. The test will fail if the task is called
- multiple times without task data input for each call.
-
-JSON file format:
-{
- TEST_CASE_NAME: {
- "tasks": {
- BPMN_TASK_IDENTIIFER: {
- "data": [DATA]
- }
- },
- "expected_output_json": DATA
- }
-}
-"""
-
-
class ProcessModelTestRunner:
"""Runs the test case json files for a given process model directory.
@@ -401,6 +404,7 @@ class ProcessModelTestRunner:
def run_test_case(self, bpmn_file: str, test_case_identifier: str, test_case_contents: dict) -> None:
bpmn_process_instance = self._instantiate_executer(bpmn_file)
method_overrides = {}
+ # mocking python functions within script tasks
if "mocks" in test_case_contents:
for method_name, mock_return_value in test_case_contents["mocks"].items():
method_overrides[method_name] = lambda value=mock_return_value: value
@@ -430,23 +434,23 @@ class ProcessModelTestRunner:
error_message = None
if bpmn_process_instance.is_completed() is False:
- error_message = [
- "Expected process instance to complete but it did not.",
- f"Final data was: {bpmn_process_instance.last_task.data}",
- f"Last task bpmn id: {bpmn_process_instance.last_task.task_spec.bpmn_id}",
- f"Last task type: {bpmn_process_instance.last_task.task_spec.__class__.__name__}",
- ]
+ error_message = {
+ "error_messages": ["Expected process instance to complete but it did not."],
+ "output_data": bpmn_process_instance.last_task.data,
+ "task_bpmn_identifier": bpmn_process_instance.last_task.task_spec.bpmn_id,
+ "task_bpmn_type": bpmn_process_instance.last_task.task_spec.__class__.__name__,
+ }
elif bpmn_process_instance.success is False:
- error_message = [
- "Expected process instance to succeed but it did not.",
- f"Final data was: {bpmn_process_instance.data}",
- ]
+ error_message = {
+ "error_messages": ["Expected process instance to succeed but it did not."],
+ "output_data": bpmn_process_instance.data,
+ }
elif test_case_contents["expected_output_json"] != bpmn_process_instance.data:
- error_message = [
- "Expected output did not match actual output:",
- f"expected: {test_case_contents['expected_output_json']}",
- f"actual: {bpmn_process_instance.data}",
- ]
+ error_message = {
+ "error_messages": ["Expected output did not match actual output."],
+ "expected_data": test_case_contents["expected_output_json"],
+ "output_data": bpmn_process_instance.data,
+ }
self._add_test_result(error_message is None, bpmn_file, test_case_identifier, error_message)
def _execute_task(
@@ -510,14 +514,14 @@ class ProcessModelTestRunner:
passed: bool,
bpmn_file: str,
test_case_identifier: str,
- error_messages: list[str] | None = None,
+ error_messages: dict | None = None,
exception: Exception | None = None,
) -> None:
test_case_error_details = None
if exception is not None:
test_case_error_details = self._exception_to_test_case_error_details(exception)
elif error_messages:
- test_case_error_details = TestCaseErrorDetails(error_messages=error_messages)
+ test_case_error_details = TestCaseErrorDetails(**error_messages)
bpmn_file_relative = self._get_relative_path_of_bpmn_file(bpmn_file)
test_result = TestCaseResult(
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py
index c0905807..b5ce5c54 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py
@@ -12,6 +12,7 @@ from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.util.task import TaskState # type: ignore
from sqlalchemy import asc
+from spiffworkflow_backend.exceptions.error import TaskMismatchError
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.bpmn_process import BpmnProcessNotFoundError
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
@@ -280,6 +281,10 @@ class TaskService:
This will NOT update start_in_seconds or end_in_seconds.
It also returns the relating json_data object so they can be imported later.
"""
+ if str(spiff_task.id) != task_model.guid:
+ raise TaskMismatchError(
+ f"Given spiff task ({spiff_task.task_spec.bpmn_id} - {spiff_task.id}) and task ({task_model.guid}) must match"
+ )
new_properties_json = self.serializer.to_dict(spiff_task)
diff --git a/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/main.bpmn b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/main.bpmn
new file mode 100644
index 00000000..077aa269
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/main.bpmn
@@ -0,0 +1,105 @@
+
+
+
+
+ Flow_0j3unyw
+
+
+
+ Flow_1dbi4yf
+
+
+
+ Flow_0z2u0gc
+ Flow_1dbi4yf
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Flow_0j3unyw
+ Flow_0uoa0st
+
+ DataObjectReference_0qfbjfj
+
+ top_level_data_object = "a"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Flow_0uoa0st
+ Flow_0z2u0gc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-exampledata.json b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-exampledata.json
new file mode 100644
index 00000000..cad75d00
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-exampledata.json
@@ -0,0 +1,3 @@
+{
+ "firstName": "Chuck"
+}
diff --git a/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-schema.json b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-schema.json
new file mode 100644
index 00000000..d5598803
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-schema.json
@@ -0,0 +1,15 @@
+{
+ "title": "Text Field",
+ "description": "A simple text field that is required, has a default value, sets a placeholder, includes a description. (field name will be 'firstname')",
+ "type": "object",
+ "required": [
+ "firstName"
+ ],
+ "properties": {
+ "firstName": {
+ "type": "string",
+ "title": "First name",
+ "default": "Chuck"
+ }
+ }
+}
diff --git a/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-uischema.json b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-uischema.json
new file mode 100644
index 00000000..9d334096
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/sub-level-sub-process-user-form-uischema.json
@@ -0,0 +1,10 @@
+{
+ "firstName": {
+ "ui:autofocus": true,
+ "ui:emptyValue": "",
+ "ui:placeholder": "ui:emptyValue causes this field to always be valid despite being required",
+ "ui:autocomplete": "family-name",
+ "ui:enableMarkdownInDescription": true,
+ "ui:description": "Make text **bold** or *italic*. Take a look at other options [here](https://probablyup.com/markdown-to-jsx/)."
+ }
+}
diff --git a/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/without-service-task.bpmn b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/without-service-task.bpmn
new file mode 100644
index 00000000..5538eeae
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/with-service-task-call-activity-sub-process/without-service-task.bpmn
@@ -0,0 +1,182 @@
+
+
+
+
+ Flow_18zaszw
+
+
+
+ The process instance completed successfully.
+
+ Flow_02mnp5n
+
+
+
+ Flow_18zaszw
+ Flow_1dbpkjb
+
+ DataObjectReference_11gskr0
+
+ sub_level_data_object = "b"
+
+
+
+
+
+
+ Flow_1dbpkjb
+ Flow_15y3o98
+
+
+
+ Flow_0zr28rt
+ Flow_02mnp5n
+
+ DataObjectReference_06uyy2z
+
+ sub_level_data_object_two = 'c'
+
+
+
+
+
+ Flow_15y3o98
+ Flow_0zr28rt
+
+ Flow_1ekcyuv
+
+
+
+ Flow_1p3g9vw
+
+
+
+
+
+ Flow_157qtq3
+ Flow_1p3g9vw
+
+ DataObjectReference_1v83y6h
+
+ sub_level_data_object_three = 'd'
+
+
+
+
+
+
+
+
+
+ Flow_1ekcyuv
+ Flow_157qtq3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spiffworkflow-backend/tests/files/bpmn_process_instance_data_objects_version_3.json b/spiffworkflow-backend/tests/files/bpmn_process_instance_data_objects_version_3.json
index ee23bf44..b40308d7 100644
--- a/spiffworkflow-backend/tests/files/bpmn_process_instance_data_objects_version_3.json
+++ b/spiffworkflow-backend/tests/files/bpmn_process_instance_data_objects_version_3.json
@@ -1,6 +1,5 @@
{
"data": {
- "validate_only": false,
"top_level_data_object": "a",
"sub_level_data_object_two": "c",
"sub_level_data_object_three": "d"
@@ -12,9 +11,7 @@
"098e4fc2-a399-4325-b0a9-76d6c330fbf4": {
"id": "098e4fc2-a399-4325-b0a9-76d6c330fbf4",
"parent": "142bb43d-7d87-4341-acb5-f7762e48d8d3",
- "children": [
- "aa3991dd-2e91-4210-89e1-594245a0cf15"
- ],
+ "children": ["aa3991dd-2e91-4210-89e1-594245a0cf15"],
"last_state_change": 1710947480.2315426,
"state": 64,
"task_spec": "StartEvent_1",
@@ -28,9 +25,7 @@
"142bb43d-7d87-4341-acb5-f7762e48d8d3": {
"id": "142bb43d-7d87-4341-acb5-f7762e48d8d3",
"parent": null,
- "children": [
- "098e4fc2-a399-4325-b0a9-76d6c330fbf4"
- ],
+ "children": ["098e4fc2-a399-4325-b0a9-76d6c330fbf4"],
"last_state_change": 1710947480.210416,
"state": 64,
"task_spec": "Start",
@@ -42,9 +37,7 @@
"270d76e0-c1fe-4add-b58e-d5a51214a37b": {
"id": "270d76e0-c1fe-4add-b58e-d5a51214a37b",
"parent": "aa3991dd-2e91-4210-89e1-594245a0cf15",
- "children": [
- "e1188a09-95be-4b79-9a10-f7c376fa04a0"
- ],
+ "children": ["e1188a09-95be-4b79-9a10-f7c376fa04a0"],
"last_state_change": 1710950132.28626,
"state": 64,
"task_spec": "top_call_activity",
@@ -56,9 +49,7 @@
"973db925-12b3-4f45-95fe-53215db8929d": {
"id": "973db925-12b3-4f45-95fe-53215db8929d",
"parent": "e1188a09-95be-4b79-9a10-f7c376fa04a0",
- "children": [
- "a8052b4d-65ed-4e55-8233-062113ebe18f"
- ],
+ "children": ["a8052b4d-65ed-4e55-8233-062113ebe18f"],
"last_state_change": 1710950132.2983754,
"state": 64,
"task_spec": "Process_top_level.EndJoin",
@@ -82,9 +73,7 @@
"aa3991dd-2e91-4210-89e1-594245a0cf15": {
"id": "aa3991dd-2e91-4210-89e1-594245a0cf15",
"parent": "098e4fc2-a399-4325-b0a9-76d6c330fbf4",
- "children": [
- "270d76e0-c1fe-4add-b58e-d5a51214a37b"
- ],
+ "children": ["270d76e0-c1fe-4add-b58e-d5a51214a37b"],
"last_state_change": 1710947480.247028,
"state": 64,
"task_spec": "top_script_task",
@@ -96,9 +85,7 @@
"e1188a09-95be-4b79-9a10-f7c376fa04a0": {
"id": "e1188a09-95be-4b79-9a10-f7c376fa04a0",
"parent": "270d76e0-c1fe-4add-b58e-d5a51214a37b",
- "children": [
- "973db925-12b3-4f45-95fe-53215db8929d"
- ],
+ "children": ["973db925-12b3-4f45-95fe-53215db8929d"],
"last_state_change": 1710950132.2913136,
"state": 64,
"task_spec": "Event_1swh8gs",
@@ -119,9 +106,7 @@
"description": "BPMN Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Process_top_level.EndJoin"
- ],
+ "inputs": ["Process_top_level.EndJoin"],
"outputs": [],
"bpmn_id": null,
"bpmn_name": null,
@@ -137,12 +122,8 @@
"description": "Default End Event",
"manual": false,
"lookahead": 2,
- "inputs": [
- "top_call_activity"
- ],
- "outputs": [
- "Process_top_level.EndJoin"
- ],
+ "inputs": ["top_call_activity"],
+ "outputs": ["Process_top_level.EndJoin"],
"bpmn_id": "Event_1swh8gs",
"bpmn_name": null,
"lane": null,
@@ -163,12 +144,8 @@
"description": "BPMN Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Event_1swh8gs"
- ],
- "outputs": [
- "End"
- ],
+ "inputs": ["Event_1swh8gs"],
+ "outputs": ["End"],
"bpmn_id": null,
"bpmn_name": null,
"lane": null,
@@ -184,9 +161,7 @@
"manual": false,
"lookahead": 2,
"inputs": [],
- "outputs": [
- "StartEvent_1"
- ],
+ "outputs": ["StartEvent_1"],
"bpmn_id": null,
"bpmn_name": null,
"lane": null,
@@ -201,12 +176,8 @@
"description": "Default Start Event",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Start"
- ],
- "outputs": [
- "top_script_task"
- ],
+ "inputs": ["Start"],
+ "outputs": ["top_script_task"],
"bpmn_id": "StartEvent_1",
"bpmn_name": null,
"lane": null,
@@ -227,12 +198,8 @@
"description": "Call Activity",
"manual": false,
"lookahead": 2,
- "inputs": [
- "top_script_task"
- ],
- "outputs": [
- "Event_1swh8gs"
- ],
+ "inputs": ["top_script_task"],
+ "outputs": ["Event_1swh8gs"],
"bpmn_id": "top_call_activity",
"bpmn_name": "Top Call Activity",
"lane": null,
@@ -251,12 +218,8 @@
"description": "Script Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "StartEvent_1"
- ],
- "outputs": [
- "top_call_activity"
- ],
+ "inputs": ["StartEvent_1"],
+ "outputs": ["top_call_activity"],
"bpmn_id": "top_script_task",
"bpmn_name": "Top Script Task",
"lane": null,
@@ -312,12 +275,8 @@
"description": "Subprocess",
"manual": false,
"lookahead": 2,
- "inputs": [
- "sub_manual_task"
- ],
- "outputs": [
- "sub_script_task_two"
- ],
+ "inputs": ["sub_manual_task"],
+ "outputs": ["sub_script_task_two"],
"bpmn_id": "call_activity_sub_process",
"bpmn_name": "Call Activity Sub Process",
"lane": null,
@@ -336,9 +295,7 @@
"description": "BPMN Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Process_sub_level.EndJoin"
- ],
+ "inputs": ["Process_sub_level.EndJoin"],
"outputs": [],
"bpmn_id": null,
"bpmn_name": null,
@@ -354,12 +311,8 @@
"description": "Default End Event",
"manual": false,
"lookahead": 2,
- "inputs": [
- "sub_script_task_two"
- ],
- "outputs": [
- "Process_sub_level.EndJoin"
- ],
+ "inputs": ["sub_script_task_two"],
+ "outputs": ["Process_sub_level.EndJoin"],
"bpmn_id": "EndEvent_1",
"bpmn_name": null,
"lane": null,
@@ -382,12 +335,8 @@
"description": "BPMN Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "EndEvent_1"
- ],
- "outputs": [
- "End"
- ],
+ "inputs": ["EndEvent_1"],
+ "outputs": ["End"],
"bpmn_id": null,
"bpmn_name": null,
"lane": null,
@@ -403,9 +352,7 @@
"manual": false,
"lookahead": 2,
"inputs": [],
- "outputs": [
- "StartEvent_1"
- ],
+ "outputs": ["StartEvent_1"],
"bpmn_id": null,
"bpmn_name": null,
"lane": null,
@@ -420,12 +367,8 @@
"description": "Default Start Event",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Start"
- ],
- "outputs": [
- "sub_script_task"
- ],
+ "inputs": ["Start"],
+ "outputs": ["sub_script_task"],
"bpmn_id": "StartEvent_1",
"bpmn_name": null,
"lane": null,
@@ -446,12 +389,8 @@
"description": "Manual Task",
"manual": true,
"lookahead": 2,
- "inputs": [
- "sub_script_task"
- ],
- "outputs": [
- "call_activity_sub_process"
- ],
+ "inputs": ["sub_script_task"],
+ "outputs": ["call_activity_sub_process"],
"bpmn_id": "sub_manual_task",
"bpmn_name": "Sub Manual Task",
"lane": null,
@@ -469,12 +408,8 @@
"description": "Script Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "StartEvent_1"
- ],
- "outputs": [
- "sub_manual_task"
- ],
+ "inputs": ["StartEvent_1"],
+ "outputs": ["sub_manual_task"],
"bpmn_id": "sub_script_task",
"bpmn_name": "Sub Script Task",
"lane": null,
@@ -500,12 +435,8 @@
"description": "Script Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "call_activity_sub_process"
- ],
- "outputs": [
- "EndEvent_1"
- ],
+ "inputs": ["call_activity_sub_process"],
+ "outputs": ["EndEvent_1"],
"bpmn_id": "sub_script_task_two",
"bpmn_name": "Sub Script Task Two",
"lane": null,
@@ -555,12 +486,8 @@
"description": "BPMN Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Event_00vjfmy"
- ],
- "outputs": [
- "End"
- ],
+ "inputs": ["Event_00vjfmy"],
+ "outputs": ["End"],
"bpmn_id": null,
"bpmn_name": null,
"lane": null,
@@ -575,9 +502,7 @@
"description": "BPMN Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "call_activity_sub_process.EndJoin"
- ],
+ "inputs": ["call_activity_sub_process.EndJoin"],
"outputs": [],
"bpmn_id": null,
"bpmn_name": null,
@@ -593,12 +518,8 @@
"description": "Default End Event",
"manual": false,
"lookahead": 2,
- "inputs": [
- "sub_level_sub_process_script_task"
- ],
- "outputs": [
- "call_activity_sub_process.EndJoin"
- ],
+ "inputs": ["sub_level_sub_process_script_task"],
+ "outputs": ["call_activity_sub_process.EndJoin"],
"bpmn_id": "Event_00vjfmy",
"bpmn_name": null,
"lane": null,
@@ -619,12 +540,8 @@
"description": "Default Start Event",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Start"
- ],
- "outputs": [
- "sub_level_sub_process_script_task"
- ],
+ "inputs": ["Start"],
+ "outputs": ["sub_level_sub_process_script_task"],
"bpmn_id": "Event_0yfq3gm",
"bpmn_name": null,
"lane": null,
@@ -646,9 +563,7 @@
"manual": false,
"lookahead": 2,
"inputs": [],
- "outputs": [
- "Event_0yfq3gm"
- ],
+ "outputs": ["Event_0yfq3gm"],
"bpmn_id": null,
"bpmn_name": null,
"lane": null,
@@ -663,12 +578,8 @@
"description": "Script Task",
"manual": false,
"lookahead": 2,
- "inputs": [
- "Event_0yfq3gm"
- ],
- "outputs": [
- "Event_00vjfmy"
- ],
+ "inputs": ["Event_0yfq3gm"],
+ "outputs": ["Event_00vjfmy"],
"bpmn_id": "sub_level_sub_process_script_task",
"bpmn_name": "Sub Level Sub Process Script Task",
"lane": null,
@@ -718,7 +629,6 @@
"subprocesses": {
"270d76e0-c1fe-4add-b58e-d5a51214a37b": {
"data": {
- "validate_only": false,
"top_level_data_object": "a",
"sub_level_data_object_two": "c",
"sub_level_data_object_three": "d"
@@ -730,9 +640,7 @@
"0315382d-fdf6-4c27-8d7d-63dddf0b05fb": {
"id": "0315382d-fdf6-4c27-8d7d-63dddf0b05fb",
"parent": "8efb7c04-82d1-459f-b0f8-778782dd7f0e",
- "children": [
- "5eb9e777-cfbf-4ef9-8ba8-79fa5d172b7e"
- ],
+ "children": ["5eb9e777-cfbf-4ef9-8ba8-79fa5d172b7e"],
"last_state_change": 1710950132.2467537,
"state": 64,
"task_spec": "Process_sub_level.EndJoin",
@@ -744,9 +652,7 @@
"186869f9-6992-4a79-86dc-365c3906dd64": {
"id": "186869f9-6992-4a79-86dc-365c3906dd64",
"parent": null,
- "children": [
- "b9762626-24e2-48d8-939a-ce1b17757781"
- ],
+ "children": ["b9762626-24e2-48d8-939a-ce1b17757781"],
"last_state_change": 1710947480.247221,
"state": 64,
"task_spec": "Start",
@@ -770,9 +676,7 @@
"688506fc-ab27-4eb2-a1fa-b435dd958561": {
"id": "688506fc-ab27-4eb2-a1fa-b435dd958561",
"parent": "8b51d215-15ab-4e0a-8dfc-e335e685fb52",
- "children": [
- "d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed"
- ],
+ "children": ["d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed"],
"last_state_change": 1710950131.6774,
"state": 64,
"task_spec": "sub_manual_task",
@@ -784,9 +688,7 @@
"8b51d215-15ab-4e0a-8dfc-e335e685fb52": {
"id": "8b51d215-15ab-4e0a-8dfc-e335e685fb52",
"parent": "b9762626-24e2-48d8-939a-ce1b17757781",
- "children": [
- "688506fc-ab27-4eb2-a1fa-b435dd958561"
- ],
+ "children": ["688506fc-ab27-4eb2-a1fa-b435dd958561"],
"last_state_change": 1710947480.280872,
"state": 64,
"task_spec": "sub_script_task",
@@ -798,9 +700,7 @@
"8efb7c04-82d1-459f-b0f8-778782dd7f0e": {
"id": "8efb7c04-82d1-459f-b0f8-778782dd7f0e",
"parent": "b482d5b3-a8e0-4903-9d48-0dbce70bd682",
- "children": [
- "0315382d-fdf6-4c27-8d7d-63dddf0b05fb"
- ],
+ "children": ["0315382d-fdf6-4c27-8d7d-63dddf0b05fb"],
"last_state_change": 1710950132.1689236,
"state": 64,
"task_spec": "EndEvent_1",
@@ -812,9 +712,7 @@
"b482d5b3-a8e0-4903-9d48-0dbce70bd682": {
"id": "b482d5b3-a8e0-4903-9d48-0dbce70bd682",
"parent": "d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed",
- "children": [
- "8efb7c04-82d1-459f-b0f8-778782dd7f0e"
- ],
+ "children": ["8efb7c04-82d1-459f-b0f8-778782dd7f0e"],
"last_state_change": 1710950131.849423,
"state": 64,
"task_spec": "sub_script_task_two",
@@ -826,9 +724,7 @@
"b9762626-24e2-48d8-939a-ce1b17757781": {
"id": "b9762626-24e2-48d8-939a-ce1b17757781",
"parent": "186869f9-6992-4a79-86dc-365c3906dd64",
- "children": [
- "8b51d215-15ab-4e0a-8dfc-e335e685fb52"
- ],
+ "children": ["8b51d215-15ab-4e0a-8dfc-e335e685fb52"],
"last_state_change": 1710947480.255076,
"state": 64,
"task_spec": "StartEvent_1",
@@ -842,9 +738,7 @@
"d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed": {
"id": "d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed",
"parent": "688506fc-ab27-4eb2-a1fa-b435dd958561",
- "children": [
- "b482d5b3-a8e0-4903-9d48-0dbce70bd682"
- ],
+ "children": ["b482d5b3-a8e0-4903-9d48-0dbce70bd682"],
"last_state_change": 1710950131.8426125,
"state": 64,
"task_spec": "call_activity_sub_process",
@@ -861,7 +755,6 @@
},
"d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed": {
"data": {
- "validate_only": false,
"top_level_data_object": "a",
"sub_level_data_object_two": "c",
"sub_level_data_object_three": "d"
@@ -873,9 +766,7 @@
"6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1": {
"id": "6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1",
"parent": "b346574d-c50c-4b4b-864c-685803ebf14e",
- "children": [
- "af12522c-811b-4258-a569-65890838677f"
- ],
+ "children": ["af12522c-811b-4258-a569-65890838677f"],
"last_state_change": 1710950131.829411,
"state": 64,
"task_spec": "call_activity_sub_process.EndJoin",
@@ -887,9 +778,7 @@
"95974b26-58b8-4fc3-a6d1-2158c1ab6de8": {
"id": "95974b26-58b8-4fc3-a6d1-2158c1ab6de8",
"parent": "b22dae80-ce20-4565-983e-e86b98625554",
- "children": [
- "b346574d-c50c-4b4b-864c-685803ebf14e"
- ],
+ "children": ["b346574d-c50c-4b4b-864c-685803ebf14e"],
"last_state_change": 1710950131.8059175,
"state": 64,
"task_spec": "sub_level_sub_process_script_task",
@@ -913,9 +802,7 @@
"b22dae80-ce20-4565-983e-e86b98625554": {
"id": "b22dae80-ce20-4565-983e-e86b98625554",
"parent": "ca86a501-34f8-48fc-b284-cce8e8af058d",
- "children": [
- "95974b26-58b8-4fc3-a6d1-2158c1ab6de8"
- ],
+ "children": ["95974b26-58b8-4fc3-a6d1-2158c1ab6de8"],
"last_state_change": 1710950131.7874112,
"state": 64,
"task_spec": "Event_0yfq3gm",
@@ -929,9 +816,7 @@
"b346574d-c50c-4b4b-864c-685803ebf14e": {
"id": "b346574d-c50c-4b4b-864c-685803ebf14e",
"parent": "95974b26-58b8-4fc3-a6d1-2158c1ab6de8",
- "children": [
- "6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1"
- ],
+ "children": ["6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1"],
"last_state_change": 1710950131.817766,
"state": 64,
"task_spec": "Event_00vjfmy",
@@ -943,9 +828,7 @@
"ca86a501-34f8-48fc-b284-cce8e8af058d": {
"id": "ca86a501-34f8-48fc-b284-cce8e8af058d",
"parent": null,
- "children": [
- "b22dae80-ce20-4565-983e-e86b98625554"
- ],
+ "children": ["b22dae80-ce20-4565-983e-e86b98625554"],
"last_state_change": 1710950131.677685,
"state": 64,
"task_spec": "Start",
diff --git a/spiffworkflow-backend/tests/files/process_model_test_importer.json b/spiffworkflow-backend/tests/files/process_model_test_importer.json
new file mode 100644
index 00000000..5680c972
--- /dev/null
+++ b/spiffworkflow-backend/tests/files/process_model_test_importer.json
@@ -0,0 +1,1106 @@
+{
+ "data": {
+ "firstName": "Chuck",
+ "data_objects": {
+ "top_level_data_object": "a"
+ },
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "correlations": {},
+ "last_task": "911a8306-343c-4a83-953c-fc114b606b59",
+ "success": true,
+ "tasks": {
+ "1b83893b-8c5e-4cbf-94cc-09fc0200eca2": {
+ "id": "1b83893b-8c5e-4cbf-94cc-09fc0200eca2",
+ "parent": "88c85404-2bf6-4235-a27a-c06bfe49c5fe",
+ "children": ["db5ebc6c-7696-4705-9cf4-fb0441ab6f62"],
+ "last_state_change": 1711048490.8429425,
+ "state": 64,
+ "task_spec": "top_service_task",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "7044e74a-3b02-43c6-90ff-5c69e23662e2": {
+ "id": "7044e74a-3b02-43c6-90ff-5c69e23662e2",
+ "parent": null,
+ "children": ["babfccea-16b1-438e-83ee-33ffeade1c4e"],
+ "last_state_change": 1711048490.7585742,
+ "state": 64,
+ "task_spec": "Start",
+ "triggered": false,
+ "internal_data": {},
+ "data": {},
+ "typename": "Task"
+ },
+ "88c85404-2bf6-4235-a27a-c06bfe49c5fe": {
+ "id": "88c85404-2bf6-4235-a27a-c06bfe49c5fe",
+ "parent": "babfccea-16b1-438e-83ee-33ffeade1c4e",
+ "children": ["1b83893b-8c5e-4cbf-94cc-09fc0200eca2"],
+ "last_state_change": 1711048490.806986,
+ "state": 64,
+ "task_spec": "top_script_task",
+ "triggered": false,
+ "internal_data": {},
+ "data": {},
+ "typename": "Task"
+ },
+ "911a8306-343c-4a83-953c-fc114b606b59": {
+ "id": "911a8306-343c-4a83-953c-fc114b606b59",
+ "parent": "e95c0ea2-43b0-4bed-89a1-cfc28c8287c3",
+ "children": [],
+ "last_state_change": 1711048497.0260544,
+ "state": 64,
+ "task_spec": "End",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "b18bd98e-224f-462c-980e-7cfa71394e73": {
+ "id": "b18bd98e-224f-462c-980e-7cfa71394e73",
+ "parent": "db5ebc6c-7696-4705-9cf4-fb0441ab6f62",
+ "children": ["e95c0ea2-43b0-4bed-89a1-cfc28c8287c3"],
+ "last_state_change": 1711048497.008846,
+ "state": 64,
+ "task_spec": "Event_1swh8gs",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "babfccea-16b1-438e-83ee-33ffeade1c4e": {
+ "id": "babfccea-16b1-438e-83ee-33ffeade1c4e",
+ "parent": "7044e74a-3b02-43c6-90ff-5c69e23662e2",
+ "children": ["88c85404-2bf6-4235-a27a-c06bfe49c5fe"],
+ "last_state_change": 1711048490.7823734,
+ "state": 64,
+ "task_spec": "StartEvent_1",
+ "triggered": false,
+ "internal_data": {
+ "event_fired": true
+ },
+ "data": {},
+ "typename": "Task"
+ },
+ "db5ebc6c-7696-4705-9cf4-fb0441ab6f62": {
+ "id": "db5ebc6c-7696-4705-9cf4-fb0441ab6f62",
+ "parent": "1b83893b-8c5e-4cbf-94cc-09fc0200eca2",
+ "children": ["b18bd98e-224f-462c-980e-7cfa71394e73"],
+ "last_state_change": 1711048497.003491,
+ "state": 64,
+ "task_spec": "top_call_activity",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "e95c0ea2-43b0-4bed-89a1-cfc28c8287c3": {
+ "id": "e95c0ea2-43b0-4bed-89a1-cfc28c8287c3",
+ "parent": "b18bd98e-224f-462c-980e-7cfa71394e73",
+ "children": ["911a8306-343c-4a83-953c-fc114b606b59"],
+ "last_state_change": 1711048497.0182238,
+ "state": 64,
+ "task_spec": "Process_top_level.EndJoin",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ }
+ },
+ "root": "7044e74a-3b02-43c6-90ff-5c69e23662e2",
+ "spec": {
+ "name": "Process_top_level",
+ "description": "Top Level Process",
+ "file": "main.bpmn",
+ "task_specs": {
+ "End": {
+ "name": "End",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["Process_top_level.EndJoin"],
+ "outputs": [],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "SimpleBpmnTask"
+ },
+ "Event_1swh8gs": {
+ "name": "Event_1swh8gs",
+ "description": "Default End Event",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["top_call_activity"],
+ "outputs": ["Process_top_level.EndJoin"],
+ "bpmn_id": "Event_1swh8gs",
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "event_definition": {
+ "description": "Default",
+ "name": null,
+ "typename": "NoneEventDefinition"
+ },
+ "typename": "EndEvent",
+ "extensions": {}
+ },
+ "Process_top_level.EndJoin": {
+ "name": "Process_top_level.EndJoin",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["Event_1swh8gs"],
+ "outputs": ["End"],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "_EndJoin"
+ },
+ "Start": {
+ "name": "Start",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": [],
+ "outputs": ["StartEvent_1"],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "BpmnStartTask"
+ },
+ "StartEvent_1": {
+ "name": "StartEvent_1",
+ "description": "Default Start Event",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["Start"],
+ "outputs": ["top_script_task"],
+ "bpmn_id": "StartEvent_1",
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "event_definition": {
+ "description": "Default",
+ "name": null,
+ "typename": "NoneEventDefinition"
+ },
+ "typename": "StartEvent",
+ "extensions": {}
+ },
+ "top_call_activity": {
+ "name": "top_call_activity",
+ "description": "Call Activity",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["top_service_task"],
+ "outputs": ["Event_1swh8gs"],
+ "bpmn_id": "top_call_activity",
+ "bpmn_name": "Top Call Activity",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "spec": "Process_sub_level",
+ "typename": "CallActivity",
+ "extensions": {}
+ },
+ "top_script_task": {
+ "name": "top_script_task",
+ "description": "Script Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["StartEvent_1"],
+ "outputs": ["top_service_task"],
+ "bpmn_id": "top_script_task",
+ "bpmn_name": "Top Script Task",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [
+ {
+ "bpmn_id": "top_level_data_object",
+ "bpmn_name": "top_level_data_object",
+ "category": null,
+ "typename": "DataObject"
+ }
+ ],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "script": "top_level_data_object = \"a\"",
+ "typename": "ScriptTask",
+ "extensions": {
+ "serviceTaskOperator": {
+ "name": "http/GetRequestV2",
+ "parameters": {
+ "url": {
+ "type": "str",
+ "value": "\"http://localhost:7000/v1.0/status\""
+ }
+ },
+ "resultVariable": "the_response"
+ }
+ }
+ },
+ "top_service_task": {
+ "name": "top_service_task",
+ "description": "Service Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["top_script_task"],
+ "outputs": ["top_call_activity"],
+ "bpmn_id": "top_service_task",
+ "bpmn_name": "Top Service Task",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "operation_name": "http/GetRequestV2",
+ "operation_params": {
+ "url": {
+ "type": "str",
+ "value": "\"http://localhost:7000/v1.0/status\""
+ }
+ },
+ "result_variable": "backend_status_response",
+ "typename": "ServiceTask",
+ "extensions": {
+ "serviceTaskOperator": {
+ "name": "http/GetRequestV2",
+ "parameters": {
+ "url": {
+ "type": "str",
+ "value": "\"http://localhost:7000/v1.0/status\""
+ }
+ },
+ "resultVariable": "backend_status_response"
+ }
+ }
+ }
+ },
+ "io_specification": null,
+ "data_objects": {
+ "top_level_data_object": {
+ "bpmn_id": "top_level_data_object",
+ "bpmn_name": "top_level_data_object",
+ "category": null,
+ "typename": "DataObject"
+ }
+ },
+ "correlation_keys": {},
+ "typename": "BpmnProcessSpec"
+ },
+ "subprocess_specs": {
+ "Process_sub_level": {
+ "name": "Process_sub_level",
+ "description": "Process Sub Level",
+ "file": "without-service-task.bpmn",
+ "task_specs": {
+ "call_activity_sub_process": {
+ "name": "call_activity_sub_process",
+ "description": "Subprocess",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["sub_manual_task"],
+ "outputs": ["sub_script_task_two"],
+ "bpmn_id": "call_activity_sub_process",
+ "bpmn_name": "Call Activity Sub Process",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "spec": "call_activity_sub_process",
+ "typename": "SubWorkflowTask",
+ "extensions": {}
+ },
+ "End": {
+ "name": "End",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["Process_sub_level.EndJoin"],
+ "outputs": [],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "SimpleBpmnTask"
+ },
+ "EndEvent_1": {
+ "name": "EndEvent_1",
+ "description": "Default End Event",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["sub_script_task_two"],
+ "outputs": ["Process_sub_level.EndJoin"],
+ "bpmn_id": "EndEvent_1",
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "event_definition": {
+ "description": "Default",
+ "name": null,
+ "typename": "NoneEventDefinition"
+ },
+ "typename": "EndEvent",
+ "extensions": {
+ "instructionsForEndUser": "The process instance completed successfully."
+ }
+ },
+ "Process_sub_level.EndJoin": {
+ "name": "Process_sub_level.EndJoin",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["EndEvent_1"],
+ "outputs": ["End"],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "_EndJoin"
+ },
+ "Start": {
+ "name": "Start",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": [],
+ "outputs": ["StartEvent_1"],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "BpmnStartTask"
+ },
+ "StartEvent_1": {
+ "name": "StartEvent_1",
+ "description": "Default Start Event",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["Start"],
+ "outputs": ["sub_script_task"],
+ "bpmn_id": "StartEvent_1",
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "event_definition": {
+ "description": "Default",
+ "name": null,
+ "typename": "NoneEventDefinition"
+ },
+ "typename": "StartEvent",
+ "extensions": {}
+ },
+ "sub_manual_task": {
+ "name": "sub_manual_task",
+ "description": "Manual Task",
+ "manual": true,
+ "lookahead": 2,
+ "inputs": ["sub_script_task"],
+ "outputs": ["call_activity_sub_process"],
+ "bpmn_id": "sub_manual_task",
+ "bpmn_name": "Sub Manual Task",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "typename": "ManualTask",
+ "extensions": {}
+ },
+ "sub_script_task": {
+ "name": "sub_script_task",
+ "description": "Script Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["StartEvent_1"],
+ "outputs": ["sub_manual_task"],
+ "bpmn_id": "sub_script_task",
+ "bpmn_name": "Sub Script Task",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [
+ {
+ "bpmn_id": "sub_level_data_object",
+ "bpmn_name": "sub_level_data_object",
+ "category": null,
+ "typename": "DataObject"
+ }
+ ],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "script": "sub_level_data_object = \"b\"",
+ "typename": "ScriptTask",
+ "extensions": {}
+ },
+ "sub_script_task_two": {
+ "name": "sub_script_task_two",
+ "description": "Script Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["call_activity_sub_process"],
+ "outputs": ["EndEvent_1"],
+ "bpmn_id": "sub_script_task_two",
+ "bpmn_name": "Sub Script Task Two",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [
+ {
+ "bpmn_id": "sub_level_data_object_two",
+ "bpmn_name": "sub_level_data_object_two",
+ "category": null,
+ "typename": "DataObject"
+ }
+ ],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "script": "sub_level_data_object_two = 'c'",
+ "typename": "ScriptTask",
+ "extensions": {}
+ }
+ },
+ "io_specification": null,
+ "data_objects": {
+ "sub_level_data_object": {
+ "bpmn_id": "sub_level_data_object",
+ "bpmn_name": "sub_level_data_object",
+ "category": null,
+ "typename": "DataObject"
+ },
+ "sub_level_data_object_two": {
+ "bpmn_id": "sub_level_data_object_two",
+ "bpmn_name": "sub_level_data_object_two",
+ "category": null,
+ "typename": "DataObject"
+ }
+ },
+ "correlation_keys": {},
+ "typename": "BpmnProcessSpec"
+ },
+ "call_activity_sub_process": {
+ "name": "call_activity_sub_process",
+ "description": "Call Activity Sub Process",
+ "file": "without-service-task.bpmn",
+ "task_specs": {
+ "call_activity_sub_process.EndJoin": {
+ "name": "call_activity_sub_process.EndJoin",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["Event_00vjfmy"],
+ "outputs": ["End"],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "_EndJoin"
+ },
+ "End": {
+ "name": "End",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["call_activity_sub_process.EndJoin"],
+ "outputs": [],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "SimpleBpmnTask"
+ },
+ "Event_00vjfmy": {
+ "name": "Event_00vjfmy",
+ "description": "Default End Event",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["sub_level_sub_process_script_task"],
+ "outputs": ["call_activity_sub_process.EndJoin"],
+ "bpmn_id": "Event_00vjfmy",
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "event_definition": {
+ "description": "Default",
+ "name": null,
+ "typename": "NoneEventDefinition"
+ },
+ "typename": "EndEvent",
+ "extensions": {}
+ },
+ "Event_0yfq3gm": {
+ "name": "Event_0yfq3gm",
+ "description": "Default Start Event",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["Start"],
+ "outputs": ["sub_level_sub_process_user_task"],
+ "bpmn_id": "Event_0yfq3gm",
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "event_definition": {
+ "description": "Default",
+ "name": null,
+ "typename": "NoneEventDefinition"
+ },
+ "typename": "StartEvent",
+ "extensions": {}
+ },
+ "Start": {
+ "name": "Start",
+ "description": "BPMN Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": [],
+ "outputs": ["Event_0yfq3gm"],
+ "bpmn_id": null,
+ "bpmn_name": null,
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "typename": "BpmnStartTask"
+ },
+ "sub_level_sub_process_script_task": {
+ "name": "sub_level_sub_process_script_task",
+ "description": "Script Task",
+ "manual": false,
+ "lookahead": 2,
+ "inputs": ["sub_level_sub_process_user_task"],
+ "outputs": ["Event_00vjfmy"],
+ "bpmn_id": "sub_level_sub_process_script_task",
+ "bpmn_name": "Sub Level Sub Process Script Task",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [
+ {
+ "bpmn_id": "sub_level_data_object_three",
+ "bpmn_name": "sub_level_data_object_three",
+ "category": null,
+ "typename": "DataObject"
+ }
+ ],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "script": "sub_level_data_object_three = 'd'",
+ "typename": "ScriptTask",
+ "extensions": {}
+ },
+ "sub_level_sub_process_user_task": {
+ "name": "sub_level_sub_process_user_task",
+ "description": "User Task",
+ "manual": true,
+ "lookahead": 2,
+ "inputs": ["Event_0yfq3gm"],
+ "outputs": ["sub_level_sub_process_script_task"],
+ "bpmn_id": "sub_level_sub_process_user_task",
+ "bpmn_name": "Sub Level Sub Process User Task",
+ "lane": null,
+ "documentation": null,
+ "data_input_associations": [],
+ "data_output_associations": [],
+ "io_specification": null,
+ "prescript": null,
+ "postscript": null,
+ "typename": "UserTask",
+ "extensions": {
+ "properties": {
+ "formUiSchemaFilename": "sub-level-sub-process-user-form-uischema.json",
+ "formJsonSchemaFilename": "sub-level-sub-process-user-form-schema.json"
+ }
+ }
+ }
+ },
+ "io_specification": null,
+ "data_objects": {
+ "sub_level_data_object_three": {
+ "bpmn_id": "sub_level_data_object_three",
+ "bpmn_name": "sub_level_data_object_three",
+ "category": null,
+ "typename": "DataObject"
+ }
+ },
+ "correlation_keys": {},
+ "typename": "BpmnProcessSpec"
+ }
+ },
+ "subprocesses": {
+ "db5ebc6c-7696-4705-9cf4-fb0441ab6f62": {
+ "data": {
+ "firstName": "Chuck",
+ "data_objects": {
+ "sub_level_data_object": "b",
+ "sub_level_data_object_two": "c"
+ },
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "correlations": {},
+ "last_task": "4b903b29-1c46-4b2c-bf22-eabac3fb235f",
+ "success": true,
+ "tasks": {
+ "09eb3d41-eca6-425b-bacf-38e79790cf28": {
+ "id": "09eb3d41-eca6-425b-bacf-38e79790cf28",
+ "parent": "f3c8cc7f-302b-4699-b6ff-4196dcfed42c",
+ "children": ["4b903b29-1c46-4b2c-bf22-eabac3fb235f"],
+ "last_state_change": 1711048496.9747708,
+ "state": 64,
+ "task_spec": "Process_sub_level.EndJoin",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "10f09dfb-9afa-4b0e-9cd1-3e65da8ab380": {
+ "id": "10f09dfb-9afa-4b0e-9cd1-3e65da8ab380",
+ "parent": "2976389b-3748-4561-a6ff-daa445c8bb53",
+ "children": ["86ce80d7-e8fa-4184-8871-25c3711f64c9"],
+ "last_state_change": 1711048494.2738447,
+ "state": 64,
+ "task_spec": "sub_manual_task",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "2976389b-3748-4561-a6ff-daa445c8bb53": {
+ "id": "2976389b-3748-4561-a6ff-daa445c8bb53",
+ "parent": "81b8c283-696f-44df-b27d-4d5e0fd101ba",
+ "children": ["10f09dfb-9afa-4b0e-9cd1-3e65da8ab380"],
+ "last_state_change": 1711048490.8815038,
+ "state": 64,
+ "task_spec": "sub_script_task",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "4b903b29-1c46-4b2c-bf22-eabac3fb235f": {
+ "id": "4b903b29-1c46-4b2c-bf22-eabac3fb235f",
+ "parent": "09eb3d41-eca6-425b-bacf-38e79790cf28",
+ "children": [],
+ "last_state_change": 1711048496.9838288,
+ "state": 64,
+ "task_spec": "End",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "6279f995-89a1-42b6-8cee-6521a7bbb525": {
+ "id": "6279f995-89a1-42b6-8cee-6521a7bbb525",
+ "parent": "86ce80d7-e8fa-4184-8871-25c3711f64c9",
+ "children": ["f3c8cc7f-302b-4699-b6ff-4196dcfed42c"],
+ "last_state_change": 1711048496.624915,
+ "state": 64,
+ "task_spec": "sub_script_task_two",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "81b8c283-696f-44df-b27d-4d5e0fd101ba": {
+ "id": "81b8c283-696f-44df-b27d-4d5e0fd101ba",
+ "parent": "b6df484c-5d4b-4967-a169-50ac5b68ce7c",
+ "children": ["2976389b-3748-4561-a6ff-daa445c8bb53"],
+ "last_state_change": 1711048490.8500838,
+ "state": 64,
+ "task_spec": "StartEvent_1",
+ "triggered": false,
+ "internal_data": {
+ "event_fired": true
+ },
+ "data": {
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "86ce80d7-e8fa-4184-8871-25c3711f64c9": {
+ "id": "86ce80d7-e8fa-4184-8871-25c3711f64c9",
+ "parent": "10f09dfb-9afa-4b0e-9cd1-3e65da8ab380",
+ "children": ["6279f995-89a1-42b6-8cee-6521a7bbb525"],
+ "last_state_change": 1711048496.6147397,
+ "state": 64,
+ "task_spec": "call_activity_sub_process",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "b6df484c-5d4b-4967-a169-50ac5b68ce7c": {
+ "id": "b6df484c-5d4b-4967-a169-50ac5b68ce7c",
+ "parent": null,
+ "children": ["81b8c283-696f-44df-b27d-4d5e0fd101ba"],
+ "last_state_change": 1711048490.8432417,
+ "state": 64,
+ "task_spec": "Start",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "f3c8cc7f-302b-4699-b6ff-4196dcfed42c": {
+ "id": "f3c8cc7f-302b-4699-b6ff-4196dcfed42c",
+ "parent": "6279f995-89a1-42b6-8cee-6521a7bbb525",
+ "children": ["09eb3d41-eca6-425b-bacf-38e79790cf28"],
+ "last_state_change": 1711048496.905861,
+ "state": 64,
+ "task_spec": "EndEvent_1",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ }
+ },
+ "root": "b6df484c-5d4b-4967-a169-50ac5b68ce7c",
+ "parent_task_id": "db5ebc6c-7696-4705-9cf4-fb0441ab6f62",
+ "spec": "Process_sub_level",
+ "typename": "BpmnSubWorkflow"
+ },
+ "86ce80d7-e8fa-4184-8871-25c3711f64c9": {
+ "data": {
+ "firstName": "Chuck",
+ "data_objects": {
+ "sub_level_data_object_three": "d"
+ },
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "correlations": {},
+ "last_task": "f040826c-a6fd-4df3-94a3-cb52ee69363f",
+ "success": true,
+ "tasks": {
+ "1b5305a7-19f5-4ddc-8d9b-4264677b1e1d": {
+ "id": "1b5305a7-19f5-4ddc-8d9b-4264677b1e1d",
+ "parent": "30f001e6-bde6-4b14-9b42-ebaba70c01df",
+ "children": ["5039396b-ceae-4037-8eba-afa657de7610"],
+ "last_state_change": 1711048496.5584755,
+ "state": 64,
+ "task_spec": "Event_00vjfmy",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "30f001e6-bde6-4b14-9b42-ebaba70c01df": {
+ "id": "30f001e6-bde6-4b14-9b42-ebaba70c01df",
+ "parent": "aa169404-fecc-4b39-b3cc-6f715f715eb1",
+ "children": ["1b5305a7-19f5-4ddc-8d9b-4264677b1e1d"],
+ "last_state_change": 1711048496.541719,
+ "state": 64,
+ "task_spec": "sub_level_sub_process_script_task",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "5039396b-ceae-4037-8eba-afa657de7610": {
+ "id": "5039396b-ceae-4037-8eba-afa657de7610",
+ "parent": "1b5305a7-19f5-4ddc-8d9b-4264677b1e1d",
+ "children": ["f040826c-a6fd-4df3-94a3-cb52ee69363f"],
+ "last_state_change": 1711048496.5812914,
+ "state": 64,
+ "task_spec": "call_activity_sub_process.EndJoin",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "8232668f-39c6-4932-a236-711cd455ef3a": {
+ "id": "8232668f-39c6-4932-a236-711cd455ef3a",
+ "parent": "d2b07903-d018-43a6-9927-a9d0c99ce1c9",
+ "children": ["aa169404-fecc-4b39-b3cc-6f715f715eb1"],
+ "last_state_change": 1711048494.403072,
+ "state": 64,
+ "task_spec": "Event_0yfq3gm",
+ "triggered": false,
+ "internal_data": {
+ "event_fired": true
+ },
+ "data": {
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "aa169404-fecc-4b39-b3cc-6f715f715eb1": {
+ "id": "aa169404-fecc-4b39-b3cc-6f715f715eb1",
+ "parent": "8232668f-39c6-4932-a236-711cd455ef3a",
+ "children": ["30f001e6-bde6-4b14-9b42-ebaba70c01df"],
+ "last_state_change": 1711048496.483813,
+ "state": 64,
+ "task_spec": "sub_level_sub_process_user_task",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "d2b07903-d018-43a6-9927-a9d0c99ce1c9": {
+ "id": "d2b07903-d018-43a6-9927-a9d0c99ce1c9",
+ "parent": null,
+ "children": ["8232668f-39c6-4932-a236-711cd455ef3a"],
+ "last_state_change": 1711048494.2741008,
+ "state": 64,
+ "task_spec": "Start",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ },
+ "f040826c-a6fd-4df3-94a3-cb52ee69363f": {
+ "id": "f040826c-a6fd-4df3-94a3-cb52ee69363f",
+ "parent": "5039396b-ceae-4037-8eba-afa657de7610",
+ "children": [],
+ "last_state_change": 1711048496.5936687,
+ "state": 64,
+ "task_spec": "End",
+ "triggered": false,
+ "internal_data": {},
+ "data": {
+ "firstName": "Chuck",
+ "backend_status_response": {
+ "body": "{\"ok\": true}",
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2"
+ }
+ },
+ "typename": "Task"
+ }
+ },
+ "root": "d2b07903-d018-43a6-9927-a9d0c99ce1c9",
+ "parent_task_id": "86ce80d7-e8fa-4184-8871-25c3711f64c9",
+ "spec": "call_activity_sub_process",
+ "typename": "BpmnSubWorkflow"
+ }
+ },
+ "bpmn_events": [],
+ "typename": "BpmnWorkflow"
+}
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py
index 6d20643c..1fc160e4 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py
@@ -11,6 +11,7 @@ from flask.app import Flask
from flask.testing import FlaskClient
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
+from spiffworkflow_backend.models.human_task import HumanTaskModel
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
from spiffworkflow_backend.models.permission_assignment import Permission
from spiffworkflow_backend.models.permission_target import PermissionTargetModel
@@ -551,11 +552,21 @@ class BaseTest:
db.session.delete(process_instance_report)
db.session.commit()
- def complete_next_manual_task(self, processor: ProcessInstanceProcessor) -> None:
+ def complete_next_manual_task(
+ self,
+ processor: ProcessInstanceProcessor,
+ execution_mode: str | None = None,
+ data: dict | None = None,
+ ) -> None:
user_task = processor.get_ready_user_tasks()[0]
- human_task = processor.process_instance_model.human_tasks[0]
+ human_task = HumanTaskModel.query.filter_by(task_guid=str(user_task.id)).first()
ProcessInstanceService.complete_form_task(
- processor, user_task, {}, processor.process_instance_model.process_initiator, human_task
+ processor=processor,
+ spiff_task=user_task,
+ data=data or {},
+ user=processor.process_instance_model.process_initiator,
+ human_task=human_task,
+ execution_mode=execution_mode,
)
@contextmanager
@@ -583,3 +594,6 @@ class BaseTest:
elif isinstance(bpmn_process_dict, list):
for item in bpmn_process_dict:
self.round_last_state_change(item)
+
+ def get_test_file(self, *args: str) -> str:
+ return os.path.join(current_app.instance_path, "..", "..", "tests", "files", *args)
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_extensions_controller.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_extensions_controller.py
index 293483b0..f3af446e 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_extensions_controller.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_extensions_controller.py
@@ -37,7 +37,6 @@ class TestExtensionsController(BaseTest):
"Mike": "Awesome",
"my_var": "Hello World",
"person": "Kevin",
- "validate_only": False,
"wonderfulness": "Very wonderful",
"OUR_AWESOME_INPUT": "the awesome value",
}
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_models_controller.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_models_controller.py
index e6973278..5391457a 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_models_controller.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_models_controller.py
@@ -1,9 +1,14 @@
import io
+import json
from hashlib import sha256
+from unittest.mock import patch
from flask.app import Flask
from flask.testing import FlaskClient
+from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
+from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.user import UserModel
+from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor
from spiffworkflow_backend.services.spec_file_service import SpecFileService
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
@@ -103,6 +108,78 @@ class TestProcessModelsController(BaseTest):
)
assert json["error_code"] == "process_model_cannot_be_found"
+ def test_process_model_test_generate(
+ self,
+ app: Flask,
+ client: FlaskClient,
+ with_db_and_bpmn_file_cleanup: None,
+ with_super_admin_user: UserModel,
+ ) -> None:
+ process_model = load_test_spec(
+ process_model_id="test_group/with-service-task-call-activity-sub-process",
+ process_model_source_directory="with-service-task-call-activity-sub-process",
+ )
+ process_instance = self.create_process_instance_from_process_model(process_model, user=with_super_admin_user)
+ process_instance_id = process_instance.id
+ processor = ProcessInstanceProcessor(process_instance)
+ 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"})
+ process_instance = ProcessInstanceModel.query.filter_by(id=process_instance_id).first()
+ assert process_instance.status == ProcessInstanceStatus.complete.value
+
+ url = f"/v1.0/process-model-tests/create/{process_model.modified_process_model_identifier()}"
+ response = client.post(
+ url,
+ headers=self.logged_in_headers(with_super_admin_user),
+ content_type="application/json",
+ data=json.dumps({"process_instance_id": process_instance_id}),
+ )
+ assert response.status_code == 200
+
+ test_case_identifier = f"test_case_for_process_instance_{process_instance_id}"
+ expected_specification = {
+ test_case_identifier: {
+ "tasks": {
+ "Process_sub_level:sub_manual_task": {"data": [{}]},
+ "call_activity_sub_process:sub_level_sub_process_user_task": {"data": [{"firstName": "Chuck"}]},
+ "Process_top_level:top_service_task": {
+ "data": [
+ {
+ "backend_status_response": {
+ "body": '{"ok": true}',
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2",
+ }
+ }
+ ]
+ },
+ },
+ "expected_output_json": {
+ "firstName": "Chuck",
+ "data_objects": {"top_level_data_object": "a"},
+ "backend_status_response": {
+ "body": '{"ok": true}',
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2",
+ },
+ },
+ }
+ }
+ assert expected_specification == response.json
+
def _get_process_show_show_response(
self, client: FlaskClient, user: UserModel, process_model_id: str, expected_response: int = 200
) -> dict:
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py
index b5e3d083..38b850f2 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py
@@ -127,7 +127,8 @@ class TestAuthorizationService(BaseTest):
("/process-instances/some-process-group:some-process-model:*", "read"),
("/process-model-natural-language/some-process-group:some-process-model:*", "create"),
("/process-model-publish/some-process-group:some-process-model:*", "create"),
- ("/process-model-tests/some-process-group:some-process-model:*", "create"),
+ ("/process-model-tests/create/some-process-group:some-process-model:*", "create"),
+ ("/process-model-tests/run/some-process-group:some-process-model:*", "create"),
("/process-models/some-process-group:some-process-model:*", "create"),
("/process-models/some-process-group:some-process-model:*", "delete"),
("/process-models/some-process-group:some-process-model:*", "read"),
@@ -209,7 +210,8 @@ class TestAuthorizationService(BaseTest):
("/process-instances/some-process-group:some-process-model/*", "read"),
("/process-model-natural-language/some-process-group:some-process-model/*", "create"),
("/process-model-publish/some-process-group:some-process-model/*", "create"),
- ("/process-model-tests/some-process-group:some-process-model/*", "create"),
+ ("/process-model-tests/create/some-process-group:some-process-model/*", "create"),
+ ("/process-model-tests/run/some-process-group:some-process-model/*", "create"),
("/process-models/some-process-group:some-process-model/*", "create"),
("/process-models/some-process-group:some-process-model/*", "delete"),
("/process-models/some-process-group:some-process-model/*", "read"),
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py
index 0a3962ef..f58228b7 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py
@@ -52,5 +52,4 @@ class TestDotNotation(BaseTest):
processor.do_engine_steps(save=True)
actual_data = processor.get_data()
- del actual_data["validate_only"]
assert actual_data == expected
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py
index 7ec15a29..5eb8fd8c 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py
@@ -6,6 +6,7 @@ from flask.app import Flask
from flask.testing import FlaskClient
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.util.task import TaskState # type: ignore
+from spiffworkflow_backend.exceptions.error import TaskMismatchError
from spiffworkflow_backend.exceptions.error import UserDoesNotHaveAccessToTaskError
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.db import db
@@ -551,7 +552,6 @@ class TestProcessInstanceProcessor(BaseTest):
}
data_set_5 = {**data_set_4, **{"a": 1, "we_move_on": True}}
data_set_6 = {**data_set_5, **{"set_top_level_process_script_after_gate": 1}}
- data_set_7 = {**data_set_6, **{"validate_only": False, "set_top_level_process_script_after_gate": 1}}
expected_task_data = {
"top_level_script": {"data": data_set_1, "bpmn_process_identifier": "top_level_process"},
"top_level_manual_task_one": {"data": data_set_1, "bpmn_process_identifier": "top_level_process"},
@@ -694,7 +694,7 @@ class TestProcessInstanceProcessor(BaseTest):
.count()
)
assert task_models_that_are_predicted_count == 4
- assert processor_final.get_data() == data_set_7
+ assert processor_final.get_data() == data_set_6
def test_does_not_recreate_human_tasks_on_multiple_saves(
self,
@@ -971,3 +971,25 @@ class TestProcessInstanceProcessor(BaseTest):
self.round_last_state_change(bpmn_process_dict_initial)
assert bpmn_process_dict_after == bpmn_process_dict_initial
+
+ def test_returns_error_if_spiff_task_and_human_task_are_different(
+ self,
+ app: Flask,
+ client: FlaskClient,
+ with_db_and_bpmn_file_cleanup: None,
+ ) -> None:
+ process_model = load_test_spec(
+ process_model_id="group/call_activity_with_manual_task",
+ process_model_source_directory="call_activity_with_manual_task",
+ )
+ process_instance = self.create_process_instance_from_process_model(process_model=process_model)
+ processor = ProcessInstanceProcessor(process_instance)
+ processor.do_engine_steps(save=True)
+
+ process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
+ processor = ProcessInstanceProcessor(process_instance)
+ human_task_one = process_instance.active_human_tasks[0]
+ non_manual_spiff_task = processor.bpmn_process_instance.get_tasks(manual=False)[0]
+ assert human_task_one.task_guid != str(non_manual_spiff_task.id)
+ with pytest.raises(TaskMismatchError):
+ processor.complete_task(non_manual_spiff_task, human_task_one, user=process_instance.process_initiator)
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model_test_generator_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model_test_generator_service.py
new file mode 100644
index 00000000..c21c5a68
--- /dev/null
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model_test_generator_service.py
@@ -0,0 +1,74 @@
+import os
+
+from flask import json
+from flask.app import Flask
+from spiffworkflow_backend.services.file_system_service import FileSystemService
+from spiffworkflow_backend.services.process_model_test_generator_service import ProcessModelTestGeneratorService
+from spiffworkflow_backend.services.process_model_test_runner_service import ProcessModelTestRunner
+
+from tests.spiffworkflow_backend.helpers.base_test import BaseTest
+from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
+
+
+class TestProcessModelTestGeneratorService(BaseTest):
+ def test_can_generate_bpmn_unit_test_from_process_instance_json_and_run_it(
+ self, app: Flask, with_db_and_bpmn_file_cleanup: None
+ ) -> None:
+ process_instance_json_file = self.get_test_file("process_model_test_importer.json")
+ with open(process_instance_json_file) as f:
+ process_instance_json = f.read()
+ process_instance_dict = json.loads(process_instance_json)
+ test_case_identifier = "our_test_case"
+
+ bpmn_unit_test_specification = ProcessModelTestGeneratorService.generate_test_from_process_instance_dict(
+ process_instance_dict, test_case_identifier=test_case_identifier
+ )
+ expected_specification = {
+ test_case_identifier: {
+ "tasks": {
+ "Process_sub_level:sub_manual_task": {"data": [{}]},
+ "call_activity_sub_process:sub_level_sub_process_user_task": {"data": [{"firstName": "Chuck"}]},
+ "Process_top_level:top_service_task": {
+ "data": [
+ {
+ "backend_status_response": {
+ "body": '{"ok": true}',
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2",
+ }
+ }
+ ]
+ },
+ },
+ "expected_output_json": {
+ "firstName": "Chuck",
+ "data_objects": {"top_level_data_object": "a"},
+ "backend_status_response": {
+ "body": '{"ok": true}',
+ "mimetype": "application/json",
+ "http_status": 200,
+ "operator_identifier": "http/GetRequestV2",
+ },
+ },
+ }
+ }
+ assert expected_specification == bpmn_unit_test_specification
+
+ process_model = load_test_spec(
+ process_model_id="test_group/with-service-task-call-activity-sub-process",
+ process_model_source_directory="with-service-task-call-activity-sub-process",
+ )
+ process_model_path = os.path.abspath(os.path.join(FileSystemService.root_path(), process_model.id_for_file_path()))
+ with open(os.path.join(process_model_path, "test_main.json"), "w") as f:
+ f.write(json.dumps(bpmn_unit_test_specification, indent=2))
+ process_model_test_runner = ProcessModelTestRunner(
+ process_model_directory_path=process_model_path,
+ process_model_directory_for_test_discovery=process_model_path,
+ test_case_file="test_main.json",
+ test_case_identifier=test_case_identifier,
+ )
+ process_model_test_runner.run()
+
+ results = process_model_test_runner.test_case_results[0]
+ assert results.passed is True
diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json
index fcf05931..5c376493 100644
--- a/spiffworkflow-frontend/package-lock.json
+++ b/spiffworkflow-frontend/package-lock.json
@@ -5535,9 +5535,9 @@
"dev": true
},
"node_modules/@tanstack/query-core": {
- "version": "5.28.6",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.28.6.tgz",
- "integrity": "sha512-hnhotV+DnQtvtR3jPvbQMPNMW4KEK0J4k7c609zJ8muiNknm+yoDyMHmxTWM5ZnlZpsz0zOxYFr+mzRJNHWJsA==",
+ "version": "5.28.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.28.8.tgz",
+ "integrity": "sha512-cx64XHeB0kvKxFt22ibvegPeOxnaWVFUbAuhXoIrb7+XePEexHWoB9Kq5n9qroNPkRwQZwgFAP9HNbQz5ohoIg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
@@ -5553,11 +5553,11 @@
}
},
"node_modules/@tanstack/react-query": {
- "version": "5.28.6",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.28.6.tgz",
- "integrity": "sha512-/DdYuDBSsA21Qbcder1R8Cr/3Nx0ZnA2lgtqKsLMvov8wL4+g0HBz/gWYZPlIsof7iyfQafyhg4wUVUsS3vWZw==",
+ "version": "5.28.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.28.8.tgz",
+ "integrity": "sha512-4XYhoRmcThqziB32HsyiBLNXJcukaeGfYwAQ+fZqUUE3ZP4oB/Zy41UJdql+TUg98+vsezfbixxAwAbGHfc5Hg==",
"dependencies": {
- "@tanstack/query-core": "5.28.6"
+ "@tanstack/query-core": "5.28.8"
},
"funding": {
"type": "github",
@@ -5568,9 +5568,9 @@
}
},
"node_modules/@tanstack/react-query-devtools": {
- "version": "5.28.6",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.28.6.tgz",
- "integrity": "sha512-xSfskHlM2JkP7WpN89UqhJV2RbFxg8YnOMzQz+EEzWSsgxMI5Crce8HO9pcUAcJce8gSmw93RQwuKNdG3FbT6w==",
+ "version": "5.28.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.28.8.tgz",
+ "integrity": "sha512-NorR2ueGlGdB5PTvt1WynzjfNI/OJwiisB1r0UAwgi0Em2UalZpMltwvoIrGhJ0T2V+8b0MV5wD+cmf0PPdHGA==",
"dependencies": {
"@tanstack/query-devtools": "5.28.6"
},
@@ -5579,7 +5579,7 @@
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
- "@tanstack/react-query": "^5.28.6",
+ "@tanstack/react-query": "^5.28.8",
"react": "^18.0.0"
}
},
@@ -38168,9 +38168,9 @@
}
},
"@tanstack/query-core": {
- "version": "5.28.6",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.28.6.tgz",
- "integrity": "sha512-hnhotV+DnQtvtR3jPvbQMPNMW4KEK0J4k7c609zJ8muiNknm+yoDyMHmxTWM5ZnlZpsz0zOxYFr+mzRJNHWJsA=="
+ "version": "5.28.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.28.8.tgz",
+ "integrity": "sha512-cx64XHeB0kvKxFt22ibvegPeOxnaWVFUbAuhXoIrb7+XePEexHWoB9Kq5n9qroNPkRwQZwgFAP9HNbQz5ohoIg=="
},
"@tanstack/query-devtools": {
"version": "5.28.6",
@@ -38178,17 +38178,17 @@
"integrity": "sha512-DXJGqbrsteWU9XehDf6s3k3QxwQqGUlNXpitsF1xbwkYBcDaAakiC6hjJSMfPBHOrbZCnWfAGCVf4vh2D75/xw=="
},
"@tanstack/react-query": {
- "version": "5.28.6",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.28.6.tgz",
- "integrity": "sha512-/DdYuDBSsA21Qbcder1R8Cr/3Nx0ZnA2lgtqKsLMvov8wL4+g0HBz/gWYZPlIsof7iyfQafyhg4wUVUsS3vWZw==",
+ "version": "5.28.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.28.8.tgz",
+ "integrity": "sha512-4XYhoRmcThqziB32HsyiBLNXJcukaeGfYwAQ+fZqUUE3ZP4oB/Zy41UJdql+TUg98+vsezfbixxAwAbGHfc5Hg==",
"requires": {
- "@tanstack/query-core": "5.28.6"
+ "@tanstack/query-core": "5.28.8"
}
},
"@tanstack/react-query-devtools": {
- "version": "5.28.6",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.28.6.tgz",
- "integrity": "sha512-xSfskHlM2JkP7WpN89UqhJV2RbFxg8YnOMzQz+EEzWSsgxMI5Crce8HO9pcUAcJce8gSmw93RQwuKNdG3FbT6w==",
+ "version": "5.28.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.28.8.tgz",
+ "integrity": "sha512-NorR2ueGlGdB5PTvt1WynzjfNI/OJwiisB1r0UAwgi0Em2UalZpMltwvoIrGhJ0T2V+8b0MV5wD+cmf0PPdHGA==",
"requires": {
"@tanstack/query-devtools": "5.28.6"
}
diff --git a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx
index 05fbdb48..441aa377 100644
--- a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx
+++ b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx
@@ -15,11 +15,15 @@ function errorDetailDisplay(
title: string
) {
// Creates a bit of html for displaying a single error property if it exists.
- if (propertyName in errorObject && errorObject[propertyName]) {
+ let value = errorObject[propertyName];
+ if (propertyName in errorObject && value) {
+ if (typeof value === 'object') {
+ value = JSON.stringify(value);
+ }
return (
{title}:
- {errorObject[propertyName]}
+ {value}
);
}
@@ -63,6 +67,10 @@ export const errorForDisplayFromTestCaseErrorDetails = (
error_line: testCaseErrorDetails.task_line_contents,
task_trace: testCaseErrorDetails.task_trace,
stacktrace: testCaseErrorDetails.stacktrace,
+
+ task_type: testCaseErrorDetails.task_bpmn_type,
+ output_data: testCaseErrorDetails.output_data,
+ expected_data: testCaseErrorDetails.expected_data,
};
return errorForDisplay;
};
@@ -94,6 +102,17 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
'Line Number'
);
const errorLine = errorDetailDisplay(errorObject, 'error_line', 'Context');
+ const taskType = errorDetailDisplay(errorObject, 'task_type', 'Task Type');
+ const outputData = errorDetailDisplay(
+ errorObject,
+ 'output_data',
+ 'Output Data'
+ );
+ const expectedData = errorDetailDisplay(
+ errorObject,
+ 'expected_data',
+ 'Expected Data'
+ );
let codeTrace = null;
if (errorObject.task_trace && errorObject.task_trace.length > 0) {
codeTrace = (
@@ -126,6 +145,9 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
lineNumber,
errorLine,
codeTrace,
+ taskType,
+ outputData,
+ expectedData,
];
};
diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx
index 52e238b6..14bbd459 100644
--- a/spiffworkflow-frontend/src/components/NavigationBar.tsx
+++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx
@@ -226,9 +226,11 @@ export default function NavigationBar({ extensionUxElements }: OwnProps) {
element={Link}
to={navItemPage}
isCurrentPage={isActivePage(navItemPage)}
- data-qa={`extension-${slugifyString(uxElement.label)}`}
+ data-qa={`extension-${slugifyString(
+ uxElement.label || uxElement.page
+ )}`}
>
- {uxElement.label}
+ {uxElement.label || uxElement.page}
);
diff --git a/spiffworkflow-frontend/src/extension_ui_schema_interfaces.ts b/spiffworkflow-frontend/src/extension_ui_schema_interfaces.ts
index bbb4e166..628f83bb 100644
--- a/spiffworkflow-frontend/src/extension_ui_schema_interfaces.ts
+++ b/spiffworkflow-frontend/src/extension_ui_schema_interfaces.ts
@@ -81,6 +81,7 @@ export interface UiSchemaAction {
* The api_path to call.
* This will normally just be the colon delimited process model identifier for the extension minus the extension process group.
* For example: extensions/path/to/extension -> path:to:extension
+ * This will interpolate patterns like "{task_data_var}" if found in the task data.
*/
api_path: string;
@@ -105,6 +106,13 @@ export interface UiSchemaAction {
*/
results_markdown_filename?: string;
+ /**
+ * By default the extension data comes a key called "task_data" in the api result.
+ * This will instead allow the extension data to be set by the full result.
+ * this is useful when making api calls to non-extension apis.
+ */
+ set_extension_data_from_full_api_result?: string;
+
/**
* Parameters to grab from the search params of the url.
* This is useful when linking from one extension to another so params can be grabbed and given to the process model when running.
diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx
index 881a0b66..45780f91 100644
--- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx
+++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx
@@ -35,7 +35,7 @@ export const useUriListForPermissions = () => {
processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`,
processModelPublishPath: `/v1.0/process-model-publish/${params.process_model_id}`,
processModelShowPath: `/v1.0/process-models/${params.process_model_id}`,
- processModelTestsPath: `/v1.0/process-model-tests/${params.process_model_id}`,
+ processModelTestsPath: `/v1.0/process-model-tests/run/${params.process_model_id}`,
secretListPath: `/v1.0/secrets`,
userSearch: `/v1.0/users/search`,
userExists: `/v1.0/users/exists/by-username`,
diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts
index 3bd181b8..289b9b97 100644
--- a/spiffworkflow-frontend/src/interfaces.ts
+++ b/spiffworkflow-frontend/src/interfaces.ts
@@ -326,6 +326,10 @@ export interface ErrorForDisplay {
task_id?: string;
task_name?: string;
task_trace?: string[];
+
+ task_type?: string;
+ output_data?: any;
+ expected_data?: any;
}
export interface AuthenticationParam {
@@ -420,6 +424,10 @@ export interface TestCaseErrorDetails {
task_line_contents?: string;
task_line_number?: number;
task_trace?: string[];
+
+ task_bpmn_type?: string;
+ output_data?: any;
+ expected_data?: any;
}
export interface TestCaseResult {
diff --git a/spiffworkflow-frontend/src/routes/Extension.tsx b/spiffworkflow-frontend/src/routes/Extension.tsx
index 8f40fb21..75e8fe3a 100644
--- a/spiffworkflow-frontend/src/routes/Extension.tsx
+++ b/spiffworkflow-frontend/src/routes/Extension.tsx
@@ -73,6 +73,7 @@ export default function Extension({
const interpolateNavigationString = useCallback(
(navigationString: string, baseData: any) => {
+ // This will interpolate patterns like "{task_data_var}" if found in the task data.
let isValid = true;
const data = { backend_base_url: BACKEND_BASE_URL, ...baseData };
const optionString = navigationString.replace(/{(\w+)}/g, (_, k) => {
@@ -224,16 +225,20 @@ export default function Extension({
pageComponent: UiSchemaPageComponent,
result: ExtensionApiResponse
) => {
+ let taskData = result.task_data;
+ if (pageComponent.on_form_submit?.set_extension_data_from_full_api_result) {
+ taskData = result;
+ }
if (pageComponent && pageComponent.navigate_to_on_form_submit) {
const optionString = interpolateNavigationString(
pageComponent.navigate_to_on_form_submit,
- result.task_data
+ taskData
);
if (optionString !== null) {
window.location.href = optionString;
}
} else {
- setProcessedTaskData(result.task_data);
+ setProcessedTaskData(taskData);
if (result.rendered_results_markdown) {
const newMarkdown = FormattingService.checkForSpiffFormats(
result.rendered_results_markdown
@@ -277,16 +282,18 @@ export default function Extension({
}
} else {
let postBody: ExtensionPostBody = { extension_input: dataToSubmit };
- let apiPath = targetUris.extensionPath;
+ let apiPathRaw = targetUris.extensionPath;
if (pageComponent && pageComponent.on_form_submit) {
- if (pageComponent.on_form_submit.is_full_api_path) {
- apiPath = `/${pageComponent.on_form_submit.api_path}`;
- postBody = dataToSubmit;
+ apiPathRaw = pageComponent.on_form_submit.api_path.replace(/^\/?/, '/');
+ if (!pageComponent.on_form_submit.is_full_api_path) {
+ apiPathRaw = `${targetUris.extensionListPath}/${apiPathRaw}`;
} else {
- apiPath = `${targetUris.extensionListPath}/${pageComponent.on_form_submit.api_path}`;
+ postBody = dataToSubmit;
}
postBody.ui_schema_action = pageComponent.on_form_submit;
}
+ const apiPath =
+ interpolateNavigationString(apiPathRaw, dataToSubmit) || apiPathRaw;
// NOTE: rjsf sets blanks values to undefined and JSON.stringify removes keys with undefined values
// so we convert undefined values to null recursively so that we can unset values in form fields