diff --git a/.github/workflows/docker_image_for_main_builds.yml b/.github/workflows/docker_image_for_main_builds.yml
index 79b15eb89..5b8d44c63 100644
--- a/.github/workflows/docker_image_for_main_builds.yml
+++ b/.github/workflows/docker_image_for_main_builds.yml
@@ -31,7 +31,7 @@ on:
branches:
- main
- spiffdemo
- - feature/update-extension-docs
+ - bugfix/data-object-management
jobs:
create_frontend_docker_image:
diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock
index d3f6a4f8e..469dfdce2 100644
--- a/spiffworkflow-backend/poetry.lock
+++ b/spiffworkflow-backend/poetry.lock
@@ -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"
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/constants.py b/spiffworkflow-backend/src/spiffworkflow_backend/constants.py
new file mode 100644
index 000000000..ac05988ee
--- /dev/null
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/constants.py
@@ -0,0 +1 @@
+SPIFFWORKFLOW_BACKEND_SERIALIZER_VERSION = "4"
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/process_instance_migrator.py b/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/process_instance_migrator.py
index f476b85e8..a2b940310 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/process_instance_migrator.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/process_instance_migrator.py
@@ -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
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/version_4.py b/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/version_4.py
new file mode 100644
index 000000000..cf449fbc4
--- /dev/null
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/version_4.py
@@ -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)
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py
index 77aa6703d..0bbaf180a 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py
@@ -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"]
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py
index aee1c7ee9..a9cf7dfbf 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py
@@ -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(
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
index 1427d5003..e60abb8d8 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
@@ -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):
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 e8d3c376b..51062bb3c 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py
@@ -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,
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py
index 88542c672..c09058071 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py
@@ -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(
diff --git a/spiffworkflow-backend/tests/data/service-task-with-data-obj/main.bpmn b/spiffworkflow-backend/tests/data/service-task-with-data-obj/main.bpmn
new file mode 100644
index 000000000..c2f1cf629
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/service-task-with-data-obj/main.bpmn
@@ -0,0 +1,80 @@
+
+