diff --git a/spiffworkflow-backend/bin/get_bpmn_json_for_process_instance.py b/spiffworkflow-backend/bin/get_bpmn_json_for_process_instance.py index 406047561..6d540c559 100755 --- a/spiffworkflow-backend/bin/get_bpmn_json_for_process_instance.py +++ b/spiffworkflow-backend/bin/get_bpmn_json_for_process_instance.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + import sys from spiffworkflow_backend import create_app diff --git a/spiffworkflow-backend/bin/query_tasks b/spiffworkflow-backend/bin/query_tasks index 6f57bd7a4..5545a2475 100755 --- a/spiffworkflow-backend/bin/query_tasks +++ b/spiffworkflow-backend/bin/query_tasks @@ -13,15 +13,16 @@ db_name=spiffworkflow_backend_local_development mysql -uroot "$db_name" -e ' select * from process_instance; - select t.guid as task_guid, t.state as task_state, td.bpmn_identifier as task_id, t.properties_json from task t + select t.guid as task_guid, t.state as task_state, td.bpmn_identifier as task_id, td.typename, t.properties_json from task t join task_definition td on td.id = t.task_definition_id - where process_instance_id=(select max(id) from process_instance); + where process_instance_id=(select max(id) from process_instance) + order by td.bpmn_identifier; - select bp.guid as bp_guid, bpd.bpmn_identifier as bp_identifier, bp.properties_json from bpmn_process bp - join bpmn_process_definition bpd on bpd.id = bp.bpmn_process_definition_id - join bpmn_process bpb on bpb.id = bp.direct_parent_process_id - join process_instance pi on bpb.id = pi.bpmn_process_id - where pi.id = (select max(id) from process_instance); + # select bp.guid as bp_guid, bpd.bpmn_identifier as bp_identifier, bp.properties_json from bpmn_process bp + # join bpmn_process_definition bpd on bpd.id = bp.bpmn_process_definition_id + # join bpmn_process bpb on bpb.id = bp.direct_parent_process_id + # join process_instance pi on bpb.id = pi.bpmn_process_id + # where pi.id = (select max(id) from process_instance); ' # mysql -uroot spiffworkflow_backend_local_development -e '\ diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index 066346cc6..56d4d61b8 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alembic" @@ -1900,8 +1900,6 @@ files = [ {file = "psycopg2-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:426f9f29bde126913a20a96ff8ce7d73fd8a216cfb323b1f04da402d452853c3"}, {file = "psycopg2-2.9.9-cp311-cp311-win32.whl", hash = "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372"}, {file = "psycopg2-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981"}, - {file = "psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024"}, - {file = "psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693"}, {file = "psycopg2-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:5e0d98cade4f0e0304d7d6f25bbfbc5bd186e07b38eac65379309c4ca3193efa"}, {file = "psycopg2-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:7e2dacf8b009a1c1e843b5213a87f7c544b2b042476ed7755be813eaf4e8347a"}, {file = "psycopg2-2.9.9-cp38-cp38-win32.whl", hash = "sha256:ff432630e510709564c01dafdbe996cb552e0b9f3f065eb89bdce5bd31fabf4c"}, @@ -2226,7 +2224,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -2603,51 +2600,37 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, @@ -2956,7 +2939,7 @@ doc = ["sphinx", "sphinx_rtd_theme"] type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "d9cd100dc3473adb63a0948011e5ca0ebb9b34bd" +resolved_reference = "faf859fefcb21eecd6038f32efb48eb19d09ae28" [[package]] name = "spiffworkflow-connector-command" 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 a2b940310..35b80fab9 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 @@ -6,6 +6,7 @@ from spiffworkflow_backend.data_migrations.data_migration_base import DataMigrat 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.data_migrations.version_5 import Version5 from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @@ -55,8 +56,11 @@ class ProcessInstanceMigrator: elif process_instance.spiff_serializer_version < Version3.version(): cls.run_version(Version3, process_instance) cls.run_version(Version4, process_instance) - else: + elif process_instance.spiff_serializer_version < Version4.version(): cls.run_version(Version4, process_instance) + cls.run_version(Version5, process_instance) + else: + cls.run_version(Version5, process_instance) @classmethod @benchmark_log_func diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/version_5.py b/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/version_5.py new file mode 100644 index 000000000..93249a8c4 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/data_migrations/version_5.py @@ -0,0 +1,38 @@ +import copy + +from flask import current_app +from SpiffWorkflow.util.task import TaskState # type: ignore +from spiffworkflow_backend.data_migrations.data_migration_base import DataMigrationBase +from spiffworkflow_backend.models.db import db +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.task import TaskModel +from spiffworkflow_backend.models.task_definition import TaskDefinitionModel + + +class Version5(DataMigrationBase): + @classmethod + def version(cls) -> str: + return "5" + + @classmethod + def run(cls, process_instance: ProcessInstanceModel) -> None: + try: + tasks = ( + TaskModel.query.filter_by(process_instance_id=process_instance.id, state="WAITING") + .join(TaskDefinitionModel, TaskDefinitionModel.id == TaskModel.task_definition_id) + .filter( + TaskDefinitionModel.typename.in_( # type: ignore + ["SequentialMultiInstanceTask", "ParallelMultiInstanceTask", "StandardLoopTask"] + ) + ) + .all() + ) + + for task in tasks: + task.state = "STARTED" + new_properties_json = copy.copy(task.properties_json) + new_properties_json["state"] = TaskState.STARTED + task.properties_json = new_properties_json + db.session.add(task) + except Exception as ex: + current_app.logger.warning(f"Failed to migrate process_instance '{process_instance.id}'. The error was {str(ex)}") diff --git a/spiffworkflow-backend/tests/files/bpmn_multi_instance_task_version_4.json b/spiffworkflow-backend/tests/files/bpmn_multi_instance_task_version_4.json new file mode 100644 index 000000000..9cb88a436 --- /dev/null +++ b/spiffworkflow-backend/tests/files/bpmn_multi_instance_task_version_4.json @@ -0,0 +1,354 @@ +{ + "data": {}, + "correlations": {}, + "last_task": "f67c1c06-454c-40d1-b26c-74172139e729", + "success": true, + "tasks": { + "1d7372b6-c4f0-4e00-ada9-2c47380d8489": { + "id": "1d7372b6-c4f0-4e00-ada9-2c47380d8489", + "parent": "3490076c-5a0b-4792-be37-5ca944eb8ae9", + "children": [], + "last_state_change": 1717785780.4789772, + "state": 16, + "task_spec": "manual_task [child]", + "triggered": true, + "internal_data": { + "key_or_index": 1 + }, + "data": { + "z": [], + "the_input": ["a", "b", "c"], + "the_input_var": "b" + }, + "typename": "Task" + }, + "3490076c-5a0b-4792-be37-5ca944eb8ae9": { + "id": "3490076c-5a0b-4792-be37-5ca944eb8ae9", + "parent": "7268fc55-7783-49de-b7a9-ee473ff5f49f", + "children": [ + "a98802ad-4197-46c7-a21a-e4e8764c209a", + "d73ca5a4-6d3d-4fdd-88f9-a1be3ab568c4", + "1d7372b6-c4f0-4e00-ada9-2c47380d8489", + "f67c1c06-454c-40d1-b26c-74172139e729" + ], + "last_state_change": 1717785780.4788644, + "state": 8, + "task_spec": "manual_task", + "triggered": false, + "internal_data": { + "merged": ["f67c1c06-454c-40d1-b26c-74172139e729"], + "started": true + }, + "data": { + "z": ["c"], + "the_input": ["a", "b", "c"] + }, + "typename": "Task" + }, + "7268fc55-7783-49de-b7a9-ee473ff5f49f": { + "id": "7268fc55-7783-49de-b7a9-ee473ff5f49f", + "parent": "98b34430-cd3b-40c4-89d3-3cf5218faef4", + "children": ["3490076c-5a0b-4792-be37-5ca944eb8ae9"], + "last_state_change": 1717785780.4787302, + "state": 64, + "task_spec": "Activity_0wg6zvw", + "triggered": false, + "internal_data": {}, + "data": { + "the_input": ["a", "b", "c"] + }, + "typename": "Task" + }, + "98b34430-cd3b-40c4-89d3-3cf5218faef4": { + "id": "98b34430-cd3b-40c4-89d3-3cf5218faef4", + "parent": "a2aba954-d531-4f46-bad3-8b28b46be4c9", + "children": ["7268fc55-7783-49de-b7a9-ee473ff5f49f"], + "last_state_change": 1717785780.4604492, + "state": 64, + "task_spec": "StartEvent_1", + "triggered": false, + "internal_data": { + "event_fired": true + }, + "data": {}, + "typename": "Task" + }, + "a2aba954-d531-4f46-bad3-8b28b46be4c9": { + "id": "a2aba954-d531-4f46-bad3-8b28b46be4c9", + "parent": null, + "children": ["98b34430-cd3b-40c4-89d3-3cf5218faef4"], + "last_state_change": 1717785780.4322698, + "state": 64, + "task_spec": "Start", + "triggered": false, + "internal_data": {}, + "data": {}, + "typename": "Task" + }, + "a8efe64f-ef02-4aa3-9a53-00ae3e2fc9bb": { + "id": "a8efe64f-ef02-4aa3-9a53-00ae3e2fc9bb", + "parent": "b32bb0c0-e72a-4b8e-b858-63b263a8e5a5", + "children": [], + "last_state_change": 1717785780.3915155, + "state": 4, + "task_spec": "End", + "triggered": false, + "internal_data": {}, + "data": {}, + "typename": "Task" + }, + "a98802ad-4197-46c7-a21a-e4e8764c209a": { + "id": "a98802ad-4197-46c7-a21a-e4e8764c209a", + "parent": "3490076c-5a0b-4792-be37-5ca944eb8ae9", + "children": ["b32bb0c0-e72a-4b8e-b858-63b263a8e5a5"], + "last_state_change": 1717785780.391497, + "state": 4, + "task_spec": "EndEvent_1", + "triggered": false, + "internal_data": {}, + "data": {}, + "typename": "Task" + }, + "b32bb0c0-e72a-4b8e-b858-63b263a8e5a5": { + "id": "b32bb0c0-e72a-4b8e-b858-63b263a8e5a5", + "parent": "a98802ad-4197-46c7-a21a-e4e8764c209a", + "children": ["a8efe64f-ef02-4aa3-9a53-00ae3e2fc9bb"], + "last_state_change": 1717785780.391503, + "state": 4, + "task_spec": "Process_multiinstance_manual_task_eayacuw.EndJoin", + "triggered": false, + "internal_data": {}, + "data": {}, + "typename": "Task" + }, + "d73ca5a4-6d3d-4fdd-88f9-a1be3ab568c4": { + "id": "d73ca5a4-6d3d-4fdd-88f9-a1be3ab568c4", + "parent": "3490076c-5a0b-4792-be37-5ca944eb8ae9", + "children": [], + "last_state_change": 1717785780.4789374, + "state": 16, + "task_spec": "manual_task [child]", + "triggered": true, + "internal_data": { + "key_or_index": 0 + }, + "data": { + "z": [], + "the_input": ["a", "b", "c"], + "the_input_var": "a" + }, + "typename": "Task" + }, + "f67c1c06-454c-40d1-b26c-74172139e729": { + "id": "f67c1c06-454c-40d1-b26c-74172139e729", + "parent": "3490076c-5a0b-4792-be37-5ca944eb8ae9", + "children": [], + "last_state_change": 1717785782.4456985, + "state": 64, + "task_spec": "manual_task [child]", + "triggered": true, + "internal_data": { + "key_or_index": 2 + }, + "data": { + "z": [], + "the_input": ["a", "b", "c"], + "the_input_var": "c", + "the_output_var": "c" + }, + "typename": "Task" + } + }, + "root": "a2aba954-d531-4f46-bad3-8b28b46be4c9", + "spec": { + "name": "Process_multiinstance_manual_task_eayacuw", + "description": "Process_multiinstance_manual_task_eayacuw", + "file": "testmulti.bpmn", + "task_specs": { + "Activity_0wg6zvw": { + "name": "Activity_0wg6zvw", + "description": "Script Task", + "manual": false, + "lookahead": 2, + "inputs": ["StartEvent_1"], + "outputs": ["manual_task"], + "bpmn_id": "Activity_0wg6zvw", + "bpmn_name": null, + "lane": null, + "documentation": null, + "data_input_associations": [], + "data_output_associations": [], + "io_specification": null, + "prescript": null, + "postscript": null, + "script": "the_input = ['a', 'b', 'c']", + "typename": "ScriptTask", + "extensions": {} + }, + "End": { + "name": "End", + "description": "BPMN Task", + "manual": false, + "lookahead": 2, + "inputs": ["Process_multiinstance_manual_task_eayacuw.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": ["manual_task"], + "outputs": ["Process_multiinstance_manual_task_eayacuw.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": {} + }, + "manual_task": { + "name": "manual_task", + "description": "Parallel MultiInstance", + "manual": false, + "lookahead": 2, + "inputs": ["Activity_0wg6zvw"], + "outputs": ["EndEvent_1"], + "bpmn_id": "manual_task", + "bpmn_name": null, + "lane": null, + "documentation": null, + "data_input_associations": [], + "data_output_associations": [], + "io_specification": null, + "task_spec": "manual_task [child]", + "cardinality": null, + "data_input": { + "bpmn_id": "the_input", + "bpmn_name": null, + "typename": "TaskDataReference" + }, + "data_output": { + "bpmn_id": "z", + "bpmn_name": null, + "typename": "TaskDataReference" + }, + "input_item": { + "bpmn_id": "the_input_var", + "bpmn_name": "the_input_var", + "typename": "TaskDataReference" + }, + "output_item": { + "bpmn_id": "the_output_var", + "bpmn_name": "the_output_var", + "typename": "TaskDataReference" + }, + "condition": null, + "prescript": null, + "postscript": null, + "typename": "ParallelMultiInstanceTask" + }, + "manual_task [child]": { + "name": "manual_task [child]", + "description": "Manual Task", + "manual": true, + "lookahead": 2, + "inputs": ["manual_task"], + "outputs": [], + "bpmn_id": "manual_task", + "bpmn_name": "Manual Task", + "lane": null, + "documentation": null, + "data_input_associations": [], + "data_output_associations": [], + "io_specification": null, + "prescript": null, + "postscript": "the_output_var = the_input_var", + "typename": "ManualTask", + "extensions": { + "preScript": null, + "postScript": "the_output_var = the_input_var", + "instructionsForEndUser": "{{ the_input_var }}" + } + }, + "Process_multiinstance_manual_task_eayacuw.EndJoin": { + "name": "Process_multiinstance_manual_task_eayacuw.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": ["Activity_0wg6zvw"], + "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": {} + } + }, + "io_specification": null, + "data_objects": {}, + "correlation_keys": {}, + "typename": "BpmnProcessSpec" + }, + "subprocess_specs": {}, + "subprocesses": {}, + "bpmn_events": [], + "typename": "BpmnWorkflow" +} diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_migrator.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_migrator.py index 219481446..16dfc42ae 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_migrator.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_migrator.py @@ -8,10 +8,13 @@ from SpiffWorkflow.bpmn.serializer.migration.version_1_3 import update_data_obje 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.data_migrations.version_5 import Version5 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.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel +from spiffworkflow_backend.models.process_model import ProcessModelInfo 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 @@ -88,35 +91,13 @@ class TestProcessInstanceMigrator(BaseTest): 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, bpmn_process_dict_version_4_from_spiff) = self._import_bpmn_json_for_test( + app, "bpmn_process_instance_data_objects_version_3.json", process_model ) - 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, include_task_data_for_completed_tasks=True, include_completed_subprocesses=True - ) - 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, @@ -163,3 +144,72 @@ class TestProcessInstanceMigrator(BaseTest): process_instance_events = ProcessInstanceEventModel.query.filter_by(process_instance_id=process_instance.id).all() pi_events_count_after = len(process_instance_events) assert pi_events_count_before == pi_events_count_after + + def test_can_run_version_5_migration( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + process_model = load_test_spec( + process_model_id="test_group/multiinstance_manual_task", + process_model_source_directory="multiinstance_manual_task", + ) + (process_instance, _bpmn_process_dict_before_import) = self._import_bpmn_json_for_test( + app, "bpmn_multi_instance_task_version_4.json", process_model + ) + tasks = ( + TaskModel.query.filter_by(process_instance_id=process_instance.id) + .join(TaskDefinitionModel, TaskDefinitionModel.id == TaskModel.task_definition_id) + .filter(TaskDefinitionModel.bpmn_identifier == "manual_task") + .all() + ) + assert len(tasks) == 1 + assert tasks[0].state == "WAITING" + Version5.run(process_instance) + db.session.commit() + tasks = ( + TaskModel.query.filter_by(process_instance_id=process_instance.id) + .join(TaskDefinitionModel, TaskDefinitionModel.id == TaskModel.task_definition_id) + .filter(TaskDefinitionModel.bpmn_identifier == "manual_task") + .all() + ) + assert len(tasks) == 1 + assert tasks[0].state == "STARTED" + + process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first() + processor = ProcessInstanceProcessor(process_instance) + # save the processor so it creates the human tasks + processor.save() + self.complete_next_manual_task(processor, execution_mode="synchronous") + self.complete_next_manual_task(processor, execution_mode="synchronous") + assert process_instance.status == ProcessInstanceStatus.complete.value + + def _import_bpmn_json_for_test(self, app: Flask, bpmn_json_file_name: str, process_model: ProcessModelInfo) -> tuple: + bpmn_json_file = os.path.join( + app.instance_path, + "..", + "..", + "tests", + "files", + bpmn_json_file_name, + ) + with open(bpmn_json_file) as f: + bpmn_process_dict_before_import = json.loads(f.read()) + process_instance = self.create_process_instance_from_process_model(process_model=process_model) + ProcessInstanceProcessor.persist_bpmn_process_dict( + bpmn_process_dict_before_import, + 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, include_task_data_for_completed_tasks=True, include_completed_subprocesses=True + ) + bpmn_process_dict_version_3_after_import = processor.serialize() + self.round_last_state_change(bpmn_process_dict_before_import) + self.round_last_state_change(bpmn_process_dict_version_3_after_import) + assert bpmn_process_dict_version_3_after_import == bpmn_process_dict_before_import + return (process_instance, bpmn_process_dict_before_import)