Bugfix/data object management (#1243)

* initial updates to prepare for data object data migration w/ burnettk

* added method to import bpmn_process_dict to the database w/ burnettk

* test to ensure we can import json is passing w/ burnettk

* added some more tests around importing bpmn process json w/ burnettk

* version 4 migration test is now passing w/ burnettk

* spiff lib back to main after its merge

* some coderabbit suggestions

* do not run version 3 and 4 migrations twice w/ burnettk

* build docker images for this branch w/ burnettk

---------

Co-authored-by: jasquat <jasquat@users.noreply.github.com>
Co-authored-by: burnettk <burnettk@users.noreply.github.com>
This commit is contained in:
jasquat 2024-03-21 16:00:03 +00:00 committed by GitHub
parent 26d8cfdf16
commit 0db1727a99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 1547 additions and 53 deletions

View File

@ -31,7 +31,7 @@ on:
branches:
- main
- spiffdemo
- feature/update-extension-docs
- bugfix/data-object-management
jobs:
create_frontend_docker_image:

View File

@ -1945,6 +1945,8 @@ 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"},
@ -2284,6 +2286,7 @@ 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"},
@ -2660,37 +2663,51 @@ 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-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"},
{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-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-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"},
{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-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-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"},
{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-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"},
@ -2975,7 +2992,7 @@ doc = ["sphinx", "sphinx_rtd_theme"]
type = "git"
url = "https://github.com/sartography/SpiffWorkflow"
reference = "main"
resolved_reference = "32c00e4a276b931aace9c9e9491cdb18011e6780"
resolved_reference = "86cb84d29cb25c1a5a407a702ef35cf7b469df6b"
[[package]]
name = "spiffworkflow-connector-command"

View File

@ -0,0 +1 @@
SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION = "4"

View File

@ -5,6 +5,7 @@ from flask import current_app
from spiffworkflow_backend.data_migrations.data_migration_base import DataMigrationBase
from spiffworkflow_backend.data_migrations.version_2 import Version2
from spiffworkflow_backend.data_migrations.version_3 import Version3
from spiffworkflow_backend.data_migrations.version_4 import Version4
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
@ -29,8 +30,6 @@ def benchmark_log_func(func: Any) -> Any:
class ProcessInstanceMigrator:
CURRENT_VERSION = "3"
@classmethod
def run(cls, process_instance: ProcessInstanceModel) -> None:
"""This updates the serialization of an instance to the current expected state.
@ -52,8 +51,12 @@ class ProcessInstanceMigrator:
if process_instance.spiff_serializer_version < Version2.version():
cls.run_version(Version3, process_instance)
cls.run_version(Version2, process_instance)
else:
cls.run_version(Version4, process_instance)
elif process_instance.spiff_serializer_version < Version3.version():
cls.run_version(Version3, process_instance)
cls.run_version(Version4, process_instance)
else:
cls.run_version(Version4, process_instance)
@classmethod
@benchmark_log_func

View File

@ -0,0 +1,33 @@
from flask import current_app
from SpiffWorkflow.bpmn.serializer.migration.version_1_3 import update_data_objects # type: ignore
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from spiffworkflow_backend.data_migrations.data_migration_base import DataMigrationBase
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor
from spiffworkflow_backend.services.task_service import TaskService
class Version4(DataMigrationBase):
@classmethod
def version(cls) -> str:
return "4"
@classmethod
def run(cls, process_instance: ProcessInstanceModel) -> None:
# return None
try:
processor = ProcessInstanceProcessor(process_instance)
bpmn_process_dict = processor.serialize()
update_data_objects(bpmn_process_dict)
ProcessInstanceProcessor.persist_bpmn_process_dict(
bpmn_process_dict, bpmn_definition_to_task_definitions_mappings={}, process_instance_model=process_instance
)
except Exception as ex:
current_app.logger.warning(f"Failed to migrate process_instance '{process_instance.id}'. The error was {str(ex)}")
@classmethod
def update_spiff_task_parents(cls, spiff_task: SpiffTask, task_service: TaskService) -> None:
task_service.update_task_model_with_spiff_task(spiff_task)
if spiff_task.parent is not None:
cls.update_spiff_task_parents(spiff_task.parent, task_service)

View File

@ -48,3 +48,7 @@ class BpmnProcessDefinitionModel(SpiffworkflowBaseDBModel):
updated_at_in_seconds: int = db.Column(db.Integer)
created_at_in_seconds: int = db.Column(db.Integer)
@classmethod
def keys_for_full_process_model_hash(cls) -> list[str]:
return ["spec", "subprocess_specs", "serializer_version"]

View File

@ -167,6 +167,7 @@ def _process_data_fetcher(
data_objects = bpmn_process_instance.spec.data_objects
data_object = data_objects.get(process_data_identifier)
if data_object is None:
raise ApiError(
error_code="data_object_not_found",
@ -185,7 +186,7 @@ def _process_data_fetcher(
status_code=400,
)
process_data_value = bpmn_process_data.get(process_data_identifier)
process_data_value = bpmn_process_data.get("data_objects", bpmn_process_data).get(process_data_identifier)
return make_response(
jsonify(

View File

@ -22,6 +22,7 @@ from sqlalchemy import func
from sqlalchemy.orm import aliased
from sqlalchemy.orm.util import AliasedClass
from spiffworkflow_backend.constants import SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION
from spiffworkflow_backend.data_migrations.process_instance_migrator import ProcessInstanceMigrator
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.exceptions.error import HumanTaskAlreadyCompletedError
@ -624,7 +625,7 @@ def _dequeued_interstitial_stream(
# attempt to run the migrator even for a readonly operation if the process instance is not newest
if (
process_instance.spiff_serializer_version is not None
and process_instance.spiff_serializer_version < ProcessInstanceMigrator.CURRENT_VERSION
and process_instance.spiff_serializer_version < SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION
):
try:
with ProcessInstanceQueueService.dequeued(process_instance):

View File

@ -53,6 +53,7 @@ from SpiffWorkflow.util.task import TaskIterator # type: ignore
from SpiffWorkflow.util.task import TaskState
from sqlalchemy import and_
from spiffworkflow_backend.constants import SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION
from spiffworkflow_backend.data_stores.json import JSONDataStore
from spiffworkflow_backend.data_stores.json import JSONDataStoreConverter
from spiffworkflow_backend.data_stores.json import JSONFileDataStore
@ -402,10 +403,9 @@ IdToBpmnProcessSpecMapping = NewType("IdToBpmnProcessSpecMapping", dict[str, Bpm
class ProcessInstanceProcessor:
_default_script_engine = CustomBpmnScriptEngine()
SERIALIZER_VERSION = "3"
wf_spec_converter = BpmnWorkflowSerializer.configure(SPIFF_CONFIG)
_serializer = BpmnWorkflowSerializer(wf_spec_converter, version=SERIALIZER_VERSION)
_serializer = BpmnWorkflowSerializer(wf_spec_converter, version=SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION)
PROCESS_INSTANCE_ID_KEY = "process_instance_id"
VALIDATION_PROCESS_KEY = "validate_only"
@ -496,6 +496,34 @@ class ProcessInstanceProcessor:
),
) from ke
@classmethod
def persist_bpmn_process_dict(
cls,
bpmn_process_dict: dict,
bpmn_definition_to_task_definitions_mappings: dict,
process_instance_model: ProcessInstanceModel,
) -> None:
cls._add_bpmn_process_definitions(
bpmn_process_dict,
bpmn_definition_to_task_definitions_mappings=bpmn_definition_to_task_definitions_mappings,
process_instance_model=process_instance_model,
force_update=True,
)
task_service = TaskService(
process_instance=process_instance_model,
serializer=cls._serializer,
bpmn_definition_to_task_definitions_mappings=bpmn_definition_to_task_definitions_mappings,
force_update_definitions=True,
)
process_copy = copy.deepcopy(bpmn_process_dict)
bpmn_process_instance = cls._serializer.from_dict(process_copy)
bpmn_process_instance.script_engine = cls._default_script_engine
for spiff_task in bpmn_process_instance.get_tasks():
task_service.update_task_model_with_spiff_task(spiff_task)
task_service.save_objects_to_database()
db.session.commit()
@classmethod
def get_process_model_and_subprocesses(
cls,
@ -913,9 +941,11 @@ class ProcessInstanceProcessor:
db.session.add(pim)
db.session.commit()
@classmethod
def _store_bpmn_process_definition(
self,
cls,
process_bpmn_properties: dict,
bpmn_definition_to_task_definitions_mappings: dict,
bpmn_process_definition_parent: BpmnProcessDefinitionModel | None = None,
store_bpmn_definition_mappings: bool = False,
full_bpmn_spec_dict: dict | None = None,
@ -943,9 +973,10 @@ class ProcessInstanceProcessor:
bpmn_name=process_bpmn_name,
properties_json=process_bpmn_properties,
)
process_bpmn_properties["task_specs"] = task_specs
db.session.add(bpmn_process_definition)
self._update_bpmn_definition_mappings(
self.bpmn_definition_to_task_definitions_mappings,
cls._update_bpmn_definition_mappings(
bpmn_definition_to_task_definitions_mappings,
bpmn_process_definition.bpmn_identifier,
bpmn_process_definition=bpmn_process_definition,
)
@ -960,23 +991,23 @@ class ProcessInstanceProcessor:
)
db.session.add(task_definition)
if store_bpmn_definition_mappings:
self._update_bpmn_definition_mappings(
self.bpmn_definition_to_task_definitions_mappings,
cls._update_bpmn_definition_mappings(
bpmn_definition_to_task_definitions_mappings,
process_bpmn_identifier,
task_definition=task_definition,
)
elif store_bpmn_definition_mappings:
# this should only ever happen when new process instances use a pre-existing bpmn process definitions
# otherwise this should get populated on processor initialization
self._update_bpmn_definition_mappings(
self.bpmn_definition_to_task_definitions_mappings,
cls._update_bpmn_definition_mappings(
bpmn_definition_to_task_definitions_mappings,
process_bpmn_identifier,
bpmn_process_definition=bpmn_process_definition,
)
task_definitions = TaskDefinitionModel.query.filter_by(bpmn_process_definition_id=bpmn_process_definition.id).all()
for task_definition in task_definitions:
self._update_bpmn_definition_mappings(
self.bpmn_definition_to_task_definitions_mappings,
cls._update_bpmn_definition_mappings(
bpmn_definition_to_task_definitions_mappings,
process_bpmn_identifier,
task_definition=task_definition,
)
@ -994,35 +1025,43 @@ class ProcessInstanceProcessor:
db.session.add(bpmn_process_definition_relationship)
return bpmn_process_definition
def _add_bpmn_process_definitions(self) -> None:
@classmethod
def _add_bpmn_process_definitions(
cls,
bpmn_process_dict: dict,
bpmn_definition_to_task_definitions_mappings: dict,
process_instance_model: ProcessInstanceModel,
force_update: bool = False,
) -> None:
"""Adds serialized_bpmn_definition records to the db session.
Expects the calling method to commit it.
"""
if self.process_instance_model.spiffworkflow_fully_initialized():
if force_update is False and process_instance_model.spiffworkflow_fully_initialized():
return None
bpmn_dict = self.serialize()
bpmn_dict_keys = ("spec", "subprocess_specs", "serializer_version")
bpmn_dict_keys = BpmnProcessDefinitionModel.keys_for_full_process_model_hash()
bpmn_spec_dict = {}
for bpmn_key in bpmn_dict.keys():
for bpmn_key in bpmn_process_dict.keys():
if bpmn_key in bpmn_dict_keys:
bpmn_spec_dict[bpmn_key] = bpmn_dict[bpmn_key]
bpmn_spec_dict[bpmn_key] = bpmn_process_dict[bpmn_key]
# store only if mappings is currently empty. this also would mean this is a new instance that has never saved before
store_bpmn_definition_mappings = not self.bpmn_definition_to_task_definitions_mappings
bpmn_process_definition_parent = self._store_bpmn_process_definition(
store_bpmn_definition_mappings = not bpmn_definition_to_task_definitions_mappings
bpmn_process_definition_parent = cls._store_bpmn_process_definition(
bpmn_spec_dict["spec"],
bpmn_definition_to_task_definitions_mappings=bpmn_definition_to_task_definitions_mappings,
store_bpmn_definition_mappings=store_bpmn_definition_mappings,
full_bpmn_spec_dict=bpmn_spec_dict,
)
for process_bpmn_properties in bpmn_spec_dict["subprocess_specs"].values():
self._store_bpmn_process_definition(
cls._store_bpmn_process_definition(
process_bpmn_properties,
bpmn_process_definition_parent,
bpmn_definition_to_task_definitions_mappings=bpmn_definition_to_task_definitions_mappings,
bpmn_process_definition_parent=bpmn_process_definition_parent,
store_bpmn_definition_mappings=store_bpmn_definition_mappings,
)
self.process_instance_model.bpmn_process_definition = bpmn_process_definition_parent
process_instance_model.bpmn_process_definition = bpmn_process_definition_parent
#
# builds and caches the element units for the parent bpmn process defintion. these
@ -1046,7 +1085,7 @@ class ProcessInstanceProcessor:
def save(self) -> None:
"""Saves the current state of this processor to the database."""
self.process_instance_model.spiff_serializer_version = self.SERIALIZER_VERSION
self.process_instance_model.spiff_serializer_version = SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION
self.process_instance_model.status = self.get_status().value
current_app.logger.debug(
f"the_status: {self.process_instance_model.status} for instance {self.process_instance_model.id}"
@ -1457,7 +1496,11 @@ class ProcessInstanceProcessor:
execution_strategy_name: str | None = None,
execution_strategy: ExecutionStrategy | None = None,
) -> TaskRunnability:
self._add_bpmn_process_definitions()
self._add_bpmn_process_definitions(
self.serialize(),
bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings,
process_instance_model=self.process_instance_model,
)
task_model_delegate = TaskModelSavingDelegate(
serializer=self._serializer,

View File

@ -110,11 +110,17 @@ class TaskService:
serializer: BpmnWorkflowSerializer,
bpmn_definition_to_task_definitions_mappings: dict,
run_started_at: float | None = None,
force_update_definitions: bool = False,
) -> None:
self.process_instance = process_instance
self.bpmn_definition_to_task_definitions_mappings = bpmn_definition_to_task_definitions_mappings
self.serializer = serializer
# this updates the definition ids for both tasks and bpmn_processes when they are updated
# in case definitions were changed for the same instances.
# this is currently only used when importing a process instance from bpmn json or when running data migrations.
self.force_update_definitions = force_update_definitions
self.bpmn_processes: dict[str, BpmnProcessModel] = {}
self.task_models: dict[str, TaskModel] = {}
self.json_data_dicts: dict[str, JsonDataDict] = {}
@ -229,6 +235,12 @@ class TaskService:
)
self.process_instance_events[task_model.guid] = process_instance_event
if self.force_update_definitions is True:
task_definition = self.bpmn_definition_to_task_definitions_mappings[spiff_task.workflow.spec.name][
spiff_task.task_spec.name
]
task_model.task_definition_id = task_definition.id
self.update_bpmn_process(spiff_task.workflow, bpmn_process)
return task_model
@ -252,6 +264,12 @@ class TaskService:
direct_parent_bpmn_process = BpmnProcessModel.query.filter_by(id=bpmn_process.direct_parent_process_id).first()
self.update_bpmn_process(spiff_workflow.parent_workflow, direct_parent_bpmn_process)
if self.force_update_definitions is True:
bpmn_process_definition = self.bpmn_definition_to_task_definitions_mappings[spiff_workflow.spec.name][
"bpmn_process_definition"
]
bpmn_process.bpmn_process_definition_id = bpmn_process_definition.id
def update_task_model(
self,
task_model: TaskModel,
@ -294,17 +312,16 @@ class TaskService:
bpmn_process = self.task_bpmn_process(
spiff_task,
)
task_model = TaskModel.query.filter_by(guid=spiff_task_guid).first()
if task_model is None:
task_definition = self.bpmn_definition_to_task_definitions_mappings[spiff_task.workflow.spec.name][
spiff_task.task_spec.name
]
task_model = TaskModel(
guid=spiff_task_guid,
bpmn_process_id=bpmn_process.id,
process_instance_id=self.process_instance.id,
task_definition_id=task_definition.id,
)
task_definition = self.bpmn_definition_to_task_definitions_mappings[spiff_task.workflow.spec.name][
spiff_task.task_spec.name
]
task_model = TaskModel(
guid=spiff_task_guid,
bpmn_process_id=bpmn_process.id,
process_instance_id=self.process_instance.id,
task_definition_id=task_definition.id,
)
return (bpmn_process, task_model)
def task_bpmn_process(

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="Process_top_level" name="Top Level Process" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0j3unyw</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0j3unyw" sourceRef="StartEvent_1" targetRef="top_script_task" />
<bpmn:endEvent id="Event_1swh8gs">
<bpmn:incoming>Flow_1dbi4yf</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1dbi4yf" sourceRef="top_call_activity" targetRef="Event_1swh8gs" />
<bpmn:callActivity id="top_call_activity" name="Top Call Activity" calledElement="Process_sub_level">
<bpmn:incoming>Flow_0uoa0st</bpmn:incoming>
<bpmn:outgoing>Flow_1dbi4yf</bpmn:outgoing>
</bpmn:callActivity>
<bpmn:sequenceFlow id="Flow_0uoa0st" sourceRef="top_script_task" targetRef="top_call_activity" />
<bpmn:dataObjectReference id="DataObjectReference_0qfbjfj" name="top_level_data_object" dataObjectRef="top_level_data_object" />
<bpmn:dataObject id="top_level_data_object" name="top_level_data_object" />
<bpmn:scriptTask id="top_script_task" name="Top Script Task">
<bpmn:extensionElements>
<spiffworkflow:serviceTaskOperator id="http/GetRequestV2" resultVariable="the_response">
<spiffworkflow:parameters>
<spiffworkflow:parameter id="url" type="str" value="&#34;http://localhost:7000/v1.0/status&#34;" />
<spiffworkflow:parameter id="headers" type="any" />
<spiffworkflow:parameter id="params" type="any" />
<spiffworkflow:parameter id="basic_auth_username" type="str" />
<spiffworkflow:parameter id="basic_auth_password" type="str" />
<spiffworkflow:parameter id="attempts" type="int" />
</spiffworkflow:parameters>
</spiffworkflow:serviceTaskOperator>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0j3unyw</bpmn:incoming>
<bpmn:outgoing>Flow_0uoa0st</bpmn:outgoing>
<bpmn:dataOutputAssociation id="DataOutputAssociation_1fxfphs">
<bpmn:targetRef>DataObjectReference_0qfbjfj</bpmn:targetRef>
</bpmn:dataOutputAssociation>
<bpmn:script>top_level_data_object = "a"</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_top_level">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="72" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1swh8gs_di" bpmnElement="Event_1swh8gs">
<dc:Bounds x="412" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_18m3q89_di" bpmnElement="top_call_activity">
<dc:Bounds x="270" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="DataObjectReference_0qfbjfj_di" bpmnElement="DataObjectReference_0qfbjfj">
<dc:Bounds x="172" y="55" width="36" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="145" y="31" width="90" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0dh9loj_di" bpmnElement="top_script_task">
<dc:Bounds x="140" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0j3unyw_di" bpmnElement="Flow_0j3unyw">
<di:waypoint x="108" y="177" />
<di:waypoint x="140" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1dbi4yf_di" bpmnElement="Flow_1dbi4yf">
<di:waypoint x="370" y="177" />
<di:waypoint x="412" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0uoa0st_di" bpmnElement="Flow_0uoa0st">
<di:waypoint x="240" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="DataOutputAssociation_1fxfphs_di" bpmnElement="DataOutputAssociation_1fxfphs">
<di:waypoint x="192" y="137" />
<di:waypoint x="193" y="105" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="Process_sub_level" name="Process Sub Level" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_18zaszw</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:endEvent id="EndEvent_1">
<bpmn:extensionElements>
<spiffworkflow:instructionsForEndUser>The process instance completed successfully.</spiffworkflow:instructionsForEndUser>
</bpmn:extensionElements>
<bpmn:incoming>Flow_02mnp5n</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1dbpkjb" sourceRef="sub_script_task" targetRef="sub_manual_task" />
<bpmn:scriptTask id="sub_script_task" name="Sub Script Task">
<bpmn:incoming>Flow_18zaszw</bpmn:incoming>
<bpmn:outgoing>Flow_1dbpkjb</bpmn:outgoing>
<bpmn:dataOutputAssociation id="DataOutputAssociation_0nq3jkj">
<bpmn:targetRef>DataObjectReference_11gskr0</bpmn:targetRef>
</bpmn:dataOutputAssociation>
<bpmn:script>sub_level_data_object = "b"</bpmn:script>
</bpmn:scriptTask>
<bpmn:dataObject id="sub_level_data_object" name="sub_level_data_object" />
<bpmn:dataObjectReference id="DataObjectReference_11gskr0" name="sub_level_data_object" dataObjectRef="sub_level_data_object" />
<bpmn:sequenceFlow id="Flow_18zaszw" sourceRef="StartEvent_1" targetRef="sub_script_task" />
<bpmn:sequenceFlow id="Flow_15y3o98" sourceRef="sub_manual_task" targetRef="call_activity_sub_process" />
<bpmn:manualTask id="sub_manual_task" name="Sub Manual Task">
<bpmn:incoming>Flow_1dbpkjb</bpmn:incoming>
<bpmn:outgoing>Flow_15y3o98</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:sequenceFlow id="Flow_02mnp5n" sourceRef="sub_script_task_two" targetRef="EndEvent_1" />
<bpmn:scriptTask id="sub_script_task_two" name="Sub Script Task Two">
<bpmn:incoming>Flow_0zr28rt</bpmn:incoming>
<bpmn:outgoing>Flow_02mnp5n</bpmn:outgoing>
<bpmn:dataOutputAssociation id="DataOutputAssociation_0quuwm4">
<bpmn:targetRef>DataObjectReference_06uyy2z</bpmn:targetRef>
</bpmn:dataOutputAssociation>
<bpmn:script>sub_level_data_object_two = 'c'</bpmn:script>
</bpmn:scriptTask>
<bpmn:dataObject id="sub_level_data_object_two" name="sub_level_data_object_two" />
<bpmn:dataObjectReference id="DataObjectReference_06uyy2z" name="sub_level_data_object_two" dataObjectRef="sub_level_data_object_two" />
<bpmn:sequenceFlow id="Flow_0zr28rt" sourceRef="call_activity_sub_process" targetRef="sub_script_task_two" />
<bpmn:subProcess id="call_activity_sub_process" name="Call Activity Sub Process">
<bpmn:incoming>Flow_15y3o98</bpmn:incoming>
<bpmn:outgoing>Flow_0zr28rt</bpmn:outgoing>
<bpmn:startEvent id="Event_0yfq3gm">
<bpmn:outgoing>Flow_1ekcyuv</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_1ekcyuv" sourceRef="Event_0yfq3gm" targetRef="sub_level_sub_process_script_task" />
<bpmn:endEvent id="Event_00vjfmy">
<bpmn:incoming>Flow_1p3g9vw</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1p3g9vw" sourceRef="sub_level_sub_process_script_task" targetRef="Event_00vjfmy" />
<bpmn:dataObjectReference id="DataObjectReference_1v83y6h" name="sub_level_data_object_three" dataObjectRef="sub_level_data_object_three" />
<bpmn:dataObject id="sub_level_data_object_three" name="sub_level_data_object_three" />
<bpmn:scriptTask id="sub_level_sub_process_script_task" name="Sub Level Sub Process Script Task">
<bpmn:incoming>Flow_1ekcyuv</bpmn:incoming>
<bpmn:outgoing>Flow_1p3g9vw</bpmn:outgoing>
<bpmn:dataOutputAssociation id="DataOutputAssociation_1xpueyl">
<bpmn:targetRef>DataObjectReference_1v83y6h</bpmn:targetRef>
</bpmn:dataOutputAssociation>
<bpmn:script>sub_level_data_object_three = 'd'</bpmn:script>
</bpmn:scriptTask>
</bpmn:subProcess>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_sub_level">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_14za570_di" bpmnElement="EndEvent_1">
<dc:Bounds x="762" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0stvzig_di" bpmnElement="sub_script_task">
<dc:Bounds x="260" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="DataObjectReference_11gskr0_di" bpmnElement="DataObjectReference_11gskr0">
<dc:Bounds x="292" y="45" width="36" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="266" y="21" width="87" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1roka0p_di" bpmnElement="sub_manual_task">
<dc:Bounds x="380" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ao5f38_di" bpmnElement="sub_script_task_two">
<dc:Bounds x="630" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="DataObjectReference_06uyy2z_di" bpmnElement="DataObjectReference_06uyy2z">
<dc:Bounds x="662" y="25" width="36" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="636" y="-12.5" width="87" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0bc52l5_di" bpmnElement="call_activity_sub_process">
<dc:Bounds x="510" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1dbpkjb_di" bpmnElement="Flow_1dbpkjb">
<di:waypoint x="360" y="177" />
<di:waypoint x="380" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="DataOutputAssociation_0nq3jkj_di" bpmnElement="DataOutputAssociation_0nq3jkj">
<di:waypoint x="311" y="137" />
<di:waypoint x="314" y="95" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_18zaszw_di" bpmnElement="Flow_18zaszw">
<di:waypoint x="215" y="177" />
<di:waypoint x="260" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_15y3o98_di" bpmnElement="Flow_15y3o98">
<di:waypoint x="480" y="177" />
<di:waypoint x="510" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_02mnp5n_di" bpmnElement="Flow_02mnp5n">
<di:waypoint x="730" y="177" />
<di:waypoint x="762" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="DataOutputAssociation_0quuwm4_di" bpmnElement="DataOutputAssociation_0quuwm4">
<di:waypoint x="680" y="137" />
<di:waypoint x="679" y="75" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0zr28rt_di" bpmnElement="Flow_0zr28rt">
<di:waypoint x="610" y="177" />
<di:waypoint x="630" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
<bpmndi:BPMNDiagram id="BPMNDiagram_0714uj7">
<bpmndi:BPMNPlane id="BPMNPlane_1xy36cs" bpmnElement="call_activity_sub_process">
<bpmndi:BPMNShape id="Event_0yfq3gm_di" bpmnElement="Event_0yfq3gm">
<dc:Bounds x="572" y="292" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_00vjfmy_di" bpmnElement="Event_00vjfmy">
<dc:Bounds x="812" y="292" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="DataObjectReference_1v83y6h_di" bpmnElement="DataObjectReference_1v83y6h">
<dc:Bounds x="692" y="135" width="36" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="666" y="98" width="87" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_165y1p1_di" bpmnElement="sub_level_sub_process_script_task">
<dc:Bounds x="660" y="270" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1ekcyuv_di" bpmnElement="Flow_1ekcyuv">
<di:waypoint x="608" y="310" />
<di:waypoint x="660" y="310" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1p3g9vw_di" bpmnElement="Flow_1p3g9vw">
<di:waypoint x="760" y="310" />
<di:waypoint x="812" y="310" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="DataOutputAssociation_1xpueyl_di" bpmnElement="DataOutputAssociation_1xpueyl">
<di:waypoint x="709" y="270" />
<di:waypoint x="708" y="185" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1,966 @@
{
"data": {
"validate_only": false,
"top_level_data_object": "a",
"sub_level_data_object_two": "c",
"sub_level_data_object_three": "d"
},
"correlations": {},
"last_task": "a8052b4d-65ed-4e55-8233-062113ebe18f",
"success": true,
"tasks": {
"098e4fc2-a399-4325-b0a9-76d6c330fbf4": {
"id": "098e4fc2-a399-4325-b0a9-76d6c330fbf4",
"parent": "142bb43d-7d87-4341-acb5-f7762e48d8d3",
"children": [
"aa3991dd-2e91-4210-89e1-594245a0cf15"
],
"last_state_change": 1710947480.2315426,
"state": 64,
"task_spec": "StartEvent_1",
"triggered": false,
"internal_data": {
"event_fired": true
},
"data": {},
"typename": "Task"
},
"142bb43d-7d87-4341-acb5-f7762e48d8d3": {
"id": "142bb43d-7d87-4341-acb5-f7762e48d8d3",
"parent": null,
"children": [
"098e4fc2-a399-4325-b0a9-76d6c330fbf4"
],
"last_state_change": 1710947480.210416,
"state": 64,
"task_spec": "Start",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"270d76e0-c1fe-4add-b58e-d5a51214a37b": {
"id": "270d76e0-c1fe-4add-b58e-d5a51214a37b",
"parent": "aa3991dd-2e91-4210-89e1-594245a0cf15",
"children": [
"e1188a09-95be-4b79-9a10-f7c376fa04a0"
],
"last_state_change": 1710950132.28626,
"state": 64,
"task_spec": "top_call_activity",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"973db925-12b3-4f45-95fe-53215db8929d": {
"id": "973db925-12b3-4f45-95fe-53215db8929d",
"parent": "e1188a09-95be-4b79-9a10-f7c376fa04a0",
"children": [
"a8052b4d-65ed-4e55-8233-062113ebe18f"
],
"last_state_change": 1710950132.2983754,
"state": 64,
"task_spec": "Process_top_level.EndJoin",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"a8052b4d-65ed-4e55-8233-062113ebe18f": {
"id": "a8052b4d-65ed-4e55-8233-062113ebe18f",
"parent": "973db925-12b3-4f45-95fe-53215db8929d",
"children": [],
"last_state_change": 1710950132.3121655,
"state": 64,
"task_spec": "End",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"aa3991dd-2e91-4210-89e1-594245a0cf15": {
"id": "aa3991dd-2e91-4210-89e1-594245a0cf15",
"parent": "098e4fc2-a399-4325-b0a9-76d6c330fbf4",
"children": [
"270d76e0-c1fe-4add-b58e-d5a51214a37b"
],
"last_state_change": 1710947480.247028,
"state": 64,
"task_spec": "top_script_task",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"e1188a09-95be-4b79-9a10-f7c376fa04a0": {
"id": "e1188a09-95be-4b79-9a10-f7c376fa04a0",
"parent": "270d76e0-c1fe-4add-b58e-d5a51214a37b",
"children": [
"973db925-12b3-4f45-95fe-53215db8929d"
],
"last_state_change": 1710950132.2913136,
"state": 64,
"task_spec": "Event_1swh8gs",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
}
},
"root": "142bb43d-7d87-4341-acb5-f7762e48d8d3",
"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_script_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_call_activity"
],
"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"
}
}
}
},
"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_script_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": [
"Event_0yfq3gm"
],
"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": {}
}
},
"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"
},
"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": {
"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"
},
"correlations": {},
"last_task": "5eb9e777-cfbf-4ef9-8ba8-79fa5d172b7e",
"success": true,
"tasks": {
"0315382d-fdf6-4c27-8d7d-63dddf0b05fb": {
"id": "0315382d-fdf6-4c27-8d7d-63dddf0b05fb",
"parent": "8efb7c04-82d1-459f-b0f8-778782dd7f0e",
"children": [
"5eb9e777-cfbf-4ef9-8ba8-79fa5d172b7e"
],
"last_state_change": 1710950132.2467537,
"state": 64,
"task_spec": "Process_sub_level.EndJoin",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"186869f9-6992-4a79-86dc-365c3906dd64": {
"id": "186869f9-6992-4a79-86dc-365c3906dd64",
"parent": null,
"children": [
"b9762626-24e2-48d8-939a-ce1b17757781"
],
"last_state_change": 1710947480.247221,
"state": 64,
"task_spec": "Start",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"5eb9e777-cfbf-4ef9-8ba8-79fa5d172b7e": {
"id": "5eb9e777-cfbf-4ef9-8ba8-79fa5d172b7e",
"parent": "0315382d-fdf6-4c27-8d7d-63dddf0b05fb",
"children": [],
"last_state_change": 1710950132.2639363,
"state": 64,
"task_spec": "End",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"688506fc-ab27-4eb2-a1fa-b435dd958561": {
"id": "688506fc-ab27-4eb2-a1fa-b435dd958561",
"parent": "8b51d215-15ab-4e0a-8dfc-e335e685fb52",
"children": [
"d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed"
],
"last_state_change": 1710950131.6774,
"state": 64,
"task_spec": "sub_manual_task",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"8b51d215-15ab-4e0a-8dfc-e335e685fb52": {
"id": "8b51d215-15ab-4e0a-8dfc-e335e685fb52",
"parent": "b9762626-24e2-48d8-939a-ce1b17757781",
"children": [
"688506fc-ab27-4eb2-a1fa-b435dd958561"
],
"last_state_change": 1710947480.280872,
"state": 64,
"task_spec": "sub_script_task",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"8efb7c04-82d1-459f-b0f8-778782dd7f0e": {
"id": "8efb7c04-82d1-459f-b0f8-778782dd7f0e",
"parent": "b482d5b3-a8e0-4903-9d48-0dbce70bd682",
"children": [
"0315382d-fdf6-4c27-8d7d-63dddf0b05fb"
],
"last_state_change": 1710950132.1689236,
"state": 64,
"task_spec": "EndEvent_1",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"b482d5b3-a8e0-4903-9d48-0dbce70bd682": {
"id": "b482d5b3-a8e0-4903-9d48-0dbce70bd682",
"parent": "d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed",
"children": [
"8efb7c04-82d1-459f-b0f8-778782dd7f0e"
],
"last_state_change": 1710950131.849423,
"state": 64,
"task_spec": "sub_script_task_two",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"b9762626-24e2-48d8-939a-ce1b17757781": {
"id": "b9762626-24e2-48d8-939a-ce1b17757781",
"parent": "186869f9-6992-4a79-86dc-365c3906dd64",
"children": [
"8b51d215-15ab-4e0a-8dfc-e335e685fb52"
],
"last_state_change": 1710947480.255076,
"state": 64,
"task_spec": "StartEvent_1",
"triggered": false,
"internal_data": {
"event_fired": true
},
"data": {},
"typename": "Task"
},
"d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed": {
"id": "d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed",
"parent": "688506fc-ab27-4eb2-a1fa-b435dd958561",
"children": [
"b482d5b3-a8e0-4903-9d48-0dbce70bd682"
],
"last_state_change": 1710950131.8426125,
"state": 64,
"task_spec": "call_activity_sub_process",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
}
},
"root": "186869f9-6992-4a79-86dc-365c3906dd64",
"parent_task_id": "270d76e0-c1fe-4add-b58e-d5a51214a37b",
"spec": "Process_sub_level",
"typename": "BpmnSubWorkflow"
},
"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"
},
"correlations": {},
"last_task": "af12522c-811b-4258-a569-65890838677f",
"success": true,
"tasks": {
"6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1": {
"id": "6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1",
"parent": "b346574d-c50c-4b4b-864c-685803ebf14e",
"children": [
"af12522c-811b-4258-a569-65890838677f"
],
"last_state_change": 1710950131.829411,
"state": 64,
"task_spec": "call_activity_sub_process.EndJoin",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"95974b26-58b8-4fc3-a6d1-2158c1ab6de8": {
"id": "95974b26-58b8-4fc3-a6d1-2158c1ab6de8",
"parent": "b22dae80-ce20-4565-983e-e86b98625554",
"children": [
"b346574d-c50c-4b4b-864c-685803ebf14e"
],
"last_state_change": 1710950131.8059175,
"state": 64,
"task_spec": "sub_level_sub_process_script_task",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"af12522c-811b-4258-a569-65890838677f": {
"id": "af12522c-811b-4258-a569-65890838677f",
"parent": "6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1",
"children": [],
"last_state_change": 1710950131.8363433,
"state": 64,
"task_spec": "End",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"b22dae80-ce20-4565-983e-e86b98625554": {
"id": "b22dae80-ce20-4565-983e-e86b98625554",
"parent": "ca86a501-34f8-48fc-b284-cce8e8af058d",
"children": [
"95974b26-58b8-4fc3-a6d1-2158c1ab6de8"
],
"last_state_change": 1710950131.7874112,
"state": 64,
"task_spec": "Event_0yfq3gm",
"triggered": false,
"internal_data": {
"event_fired": true
},
"data": {},
"typename": "Task"
},
"b346574d-c50c-4b4b-864c-685803ebf14e": {
"id": "b346574d-c50c-4b4b-864c-685803ebf14e",
"parent": "95974b26-58b8-4fc3-a6d1-2158c1ab6de8",
"children": [
"6e6ad5c3-e701-4b59-8a81-4ed2c63bd0e1"
],
"last_state_change": 1710950131.817766,
"state": 64,
"task_spec": "Event_00vjfmy",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
},
"ca86a501-34f8-48fc-b284-cce8e8af058d": {
"id": "ca86a501-34f8-48fc-b284-cce8e8af058d",
"parent": null,
"children": [
"b22dae80-ce20-4565-983e-e86b98625554"
],
"last_state_change": 1710950131.677685,
"state": 64,
"task_spec": "Start",
"triggered": false,
"internal_data": {},
"data": {},
"typename": "Task"
}
},
"root": "ca86a501-34f8-48fc-b284-cce8e8af058d",
"parent_task_id": "d0c6a2d9-9a43-4ccd-b4e3-ea62872f15ed",
"spec": "call_activity_sub_process",
"typename": "BpmnSubWorkflow"
}
},
"bpmn_events": [],
"typename": "BpmnWorkflow"
}

View File

@ -566,3 +566,20 @@ class BaseTest:
yield
finally:
app.config[config_identifier] = initial_value
def round_last_state_change(self, bpmn_process_dict: dict | list) -> None:
"""Round last state change to the nearest 4 significant digits.
Works around imprecise floating point values in mysql json columns.
The values between mysql and SpiffWorkflow seem to have minor differences on randomly and since
we do not care about such precision for this field, round it to a value that is more likely to match.
"""
if isinstance(bpmn_process_dict, dict):
for key, value in bpmn_process_dict.items():
if key == "last_state_change":
bpmn_process_dict[key] = round(value, 4)
elif isinstance(value, dict | list):
self.round_last_state_change(value)
elif isinstance(bpmn_process_dict, list):
for item in bpmn_process_dict:
self.round_last_state_change(item)

View File

@ -1,13 +1,20 @@
import copy
import json
import os
from flask.app import Flask
from flask.testing import FlaskClient
from SpiffWorkflow.bpmn.serializer.migration.version_1_3 import update_data_objects # type: ignore
from spiffworkflow_backend.data_migrations.process_instance_migrator import ProcessInstanceMigrator
from spiffworkflow_backend.data_migrations.version_1_3 import VersionOneThree
from spiffworkflow_backend.data_migrations.version_4 import Version4
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
from spiffworkflow_backend.models.task_definition import TaskDefinitionModel
from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor
from sqlalchemy import or_
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
@ -73,3 +80,75 @@ class TestProcessInstanceMigrator(BaseTest):
VersionOneThree().run()
task_model = TaskModel.query.filter_by(guid=task_model.guid).first()
assert task_model.properties_json["last_state_change"] is not None
def test_can_run_version_4_migration(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
version_3_json = os.path.join(
app.instance_path,
"..",
"..",
"tests",
"files",
"bpmn_process_instance_data_objects_version_3.json",
)
with open(version_3_json) as f:
bpmn_process_dict_version_3 = json.loads(f.read())
bpmn_process_dict_version_4_from_spiff = copy.deepcopy(bpmn_process_dict_version_3)
process_model = load_test_spec(
process_model_id="test_group/service-task-with-data-obj",
process_model_source_directory="service-task-with-data-obj",
)
process_instance = self.create_process_instance_from_process_model(process_model=process_model)
ProcessInstanceProcessor.persist_bpmn_process_dict(
bpmn_process_dict_version_3, process_instance_model=process_instance, bpmn_definition_to_task_definitions_mappings={}
)
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
# ensure data was imported correctly and is in expected state
processor = ProcessInstanceProcessor(process_instance)
bpmn_process_dict_version_3_after_import = processor.serialize()
self.round_last_state_change(bpmn_process_dict_version_3)
self.round_last_state_change(bpmn_process_dict_version_3_after_import)
assert bpmn_process_dict_version_3_after_import == bpmn_process_dict_version_3
bpmn_process_cache_version_3 = {
"bpmn_process_definition_id": process_instance.bpmn_process_definition_id,
"bpmn_process_id": process_instance.bpmn_process_id,
}
Version4.run(process_instance)
update_data_objects(bpmn_process_dict_version_4_from_spiff)
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
processor = ProcessInstanceProcessor(process_instance)
bpmn_process_dict_version_4 = processor.serialize()
self.round_last_state_change(bpmn_process_dict_version_4)
self.round_last_state_change(bpmn_process_dict_version_4_from_spiff)
assert bpmn_process_dict_version_4 == bpmn_process_dict_version_4_from_spiff
bpmn_process_cache_version_4 = {
"bpmn_process_definition_id": process_instance.bpmn_process_definition_id,
"bpmn_process_id": process_instance.bpmn_process_id,
}
assert (
bpmn_process_cache_version_4["bpmn_process_definition_id"]
!= bpmn_process_cache_version_3["bpmn_process_definition_id"]
)
assert bpmn_process_cache_version_4["bpmn_process_id"] == bpmn_process_cache_version_3["bpmn_process_id"]
assert (
process_instance.bpmn_process.bpmn_process_definition_id == bpmn_process_cache_version_4["bpmn_process_definition_id"]
)
bpmn_processes = BpmnProcessModel.query.filter(
or_(
BpmnProcessModel.id == process_instance.bpmn_process_id,
BpmnProcessModel.top_level_process_id == process_instance.bpmn_process_id,
)
).all()
assert len(bpmn_processes) == 3
for bpmn_process in bpmn_processes:
for task_model in bpmn_process.tasks:
assert task_model.task_definition.bpmn_process_definition_id == bpmn_process.bpmn_process_definition_id

View File

@ -10,6 +10,7 @@ from spiffworkflow_backend.exceptions.error import UserDoesNotHaveAccessToTaskEr
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.group import GroupModel
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel
@ -904,3 +905,69 @@ class TestProcessInstanceProcessor(BaseTest):
db.session.delete(process_instance)
db.session.commit()
def test_can_persist_given_bpmn_process_dict_when_imported_from_scratch(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
process_model = load_test_spec(
process_model_id="test_group/service-task-with-data-obj",
process_model_source_directory="service-task-with-data-obj",
)
process_instance = self.create_process_instance_from_process_model(process_model=process_model)
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps(save=True)
bpmn_process_dict_initial = processor.serialize()
# clear the database so we know the import is all new
meta = db.metadata
db.session.execute(db.update(BpmnProcessModel).values(top_level_process_id=None))
db.session.execute(db.update(BpmnProcessModel).values(direct_parent_process_id=None))
for table in reversed(meta.sorted_tables):
db.session.execute(table.delete())
db.session.commit()
# ensure everything is removed from the sqlalchemy cache when we clear the database
# otherwise it gets autoflush errors
db.session.expunge_all()
process_instance = self.create_process_instance_from_process_model(process_model=process_model)
assert process_instance.bpmn_process_definition_id is None
ProcessInstanceProcessor.persist_bpmn_process_dict(
bpmn_process_dict_initial, process_instance_model=process_instance, bpmn_definition_to_task_definitions_mappings={}
)
processor = ProcessInstanceProcessor(process_instance)
bpmn_process_dict_after = processor.serialize()
self.round_last_state_change(bpmn_process_dict_after)
self.round_last_state_change(bpmn_process_dict_initial)
assert bpmn_process_dict_after == bpmn_process_dict_initial
def test_can_persist_given_bpmn_process_dict_when_loaded_before(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
process_model = load_test_spec(
process_model_id="test_group/service-task-with-data-obj",
process_model_source_directory="service-task-with-data-obj",
)
process_instance = self.create_process_instance_from_process_model(process_model=process_model)
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps(save=True)
bpmn_process_dict_initial = processor.serialize()
ProcessInstanceProcessor.persist_bpmn_process_dict(
bpmn_process_dict_initial, process_instance_model=process_instance, bpmn_definition_to_task_definitions_mappings={}
)
processor = ProcessInstanceProcessor(process_instance)
bpmn_process_dict_after = processor.serialize()
self.round_last_state_change(bpmn_process_dict_after)
self.round_last_state_change(bpmn_process_dict_initial)
assert bpmn_process_dict_after == bpmn_process_dict_initial

View File

@ -75,10 +75,12 @@ export default function BaseRoutes({ extensionUxElements }: OwnProps) {
const style = { margin: '50px 0 50px 50px' };
return (
<Loading
description="Active loading indicator"
withOverlay={false}
style={style}
/>
<div className="fixed-width-container">
<Loading
description="Active loading indicator"
withOverlay={false}
style={style}
/>
</div>
);
}