From b7266e5ea2d3d804c2883e378bfee86a9339d2d3 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Tue, 6 Feb 2024 16:41:40 +0600 Subject: [PATCH 1/6] Bump zeebe-grpc to 8.4.0, add tenant_id and variables --- .github/workflows/test.yml | 2 +- poetry.lock | 73 +++++++++++-------- pyproject.toml | 6 +- pyzeebe/client/client.py | 40 ++++++++-- pyzeebe/client/sync_client.py | 23 ++++-- pyzeebe/grpc_internals/zeebe_job_adapter.py | 28 +++++-- .../grpc_internals/zeebe_message_adapter.py | 2 + .../grpc_internals/zeebe_process_adapter.py | 53 ++++++++++++-- pyzeebe/job/job.py | 33 ++++++++- pyzeebe/worker/job_poller.py | 4 + pyzeebe/worker/worker.py | 4 + tests/integration/run_process_test.py | 1 - tests/unit/client/client_test.py | 8 ++ tests/unit/client/sync_client_test.py | 10 +++ .../grpc_internals/zeebe_job_adapter_test.py | 19 ++--- .../zeebe_process_adapter_test.py | 37 +++++++++- tests/unit/job/job_test.py | 12 ++- tests/unit/utils/gateway_mock.py | 19 +++++ tests/unit/worker/job_poller_test.py | 2 +- 19 files changed, 299 insertions(+), 77 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e0ae77f..9c84df47 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - zeebe-version: ["1.3.14", "8.2.10"] + zeebe-version: ["1.3.14", "8.1.22", "8.2.21", "8.3.5", "8.4.1"] container: python:3.11 diff --git a/poetry.lock b/poetry.lock index 856c2a5b..46013d74 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiofiles" @@ -591,6 +591,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -777,33 +787,22 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "3.20.3" -description = "Protocol Buffers" +version = "4.25.2" +description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, - {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, - {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, - {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, - {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, - {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, - {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, - {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, - {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, - {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, - {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, - {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, - {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, - {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, - {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, - {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, + {file = "protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6"}, + {file = "protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9"}, + {file = "protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d"}, + {file = "protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62"}, + {file = "protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020"}, + {file = "protobuf-4.25.2-cp38-cp38-win32.whl", hash = "sha256:33a1aeef4b1927431d1be780e87b641e322b88d654203a9e9d93f218ee359e61"}, + {file = "protobuf-4.25.2-cp38-cp38-win_amd64.whl", hash = "sha256:47f3de503fe7c1245f6f03bea7e8d3ec11c6c4a2ea9ef910e3221c8a15516d62"}, + {file = "protobuf-4.25.2-cp39-cp39-win32.whl", hash = "sha256:5e5c933b4c30a988b52e0b7c02641760a5ba046edc5e43d3b94a74c9fc57c1b3"}, + {file = "protobuf-4.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:d66a769b8d687df9024f2985d5137a337f957a0916cf5464d1513eee96a63ff0"}, + {file = "protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830"}, + {file = "protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e"}, ] [[package]] @@ -943,6 +942,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -950,8 +950,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {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_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"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -968,6 +975,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -975,6 +983,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1354,18 +1363,18 @@ files = [ [[package]] name = "zeebe-grpc" -version = "8.0.4.post1" +version = "8.4.0" description = "zeebe Python gRPC Gateway" optional = false python-versions = "*" files = [ - {file = "zeebe_grpc-8.0.4.post1-py3-none-any.whl", hash = "sha256:ac8db92760d6d5180ba4575c1e742a8ed6e77624b97ac539b76947d990ed33dc"}, - {file = "zeebe_grpc-8.0.4.post1.tar.gz", hash = "sha256:a2f9c3b36ae0d6c11b6b1c5cb4e8b7225065fa6e2e111a039c445417cd88aa4f"}, + {file = "zeebe_grpc-8.4.0-py3-none-any.whl", hash = "sha256:d83e2f76a3eafa10298200b0c1c4f37ee9eca84902a973516c6293d88bd16a23"}, + {file = "zeebe_grpc-8.4.0.tar.gz", hash = "sha256:ff84f11df4c519937d877a9b253b8b9e7492b5d1661f2dd7b9a62f0dc131ac7c"}, ] [package.dependencies] -grpcio = ">=1,<2" -protobuf = ">=3.19.0,<4" +grpcio = ">=1.49,<2.0" +protobuf = ">=4.21,<5.0" [[package]] name = "zipp" @@ -1385,4 +1394,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "5e436f5565485bf6710de8cc72e3afd2e680bf11debc3c31c21d3caf2ace68e8" +content-hash = "cba2d839f329f76fef50e96fa154e5b5fb1ede0f9ad7b72363bd4aa5ccad7664" diff --git a/pyproject.toml b/pyproject.toml index 9033c444..995284c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,8 @@ python = "^3.8" oauthlib = "~=3.1.0" requests-oauthlib = "~=1.3.0" aiofiles = ">=0.7,<0.9" -zeebe-grpc = "^8.0.4.post1" +zeebe-grpc = "8.4.0" +typing-extensions = "^4.5.0" [tool.poetry.group.dev.dependencies] pytest = "^7.4.0" @@ -41,6 +42,9 @@ sphinx-rtd-theme = "^1.2.2" sphinx = "^6" importlib-metadata = "^6.8.0" +[tool.mypy] +python_version = "3.8" + [[tool.mypy.overrides]] module = [ "grpc", diff --git a/pyzeebe/client/client.py b/pyzeebe/client/client.py index 89f43426..70e8be3d 100644 --- a/pyzeebe/client/client.py +++ b/pyzeebe/client/client.py @@ -1,6 +1,7 @@ -from typing import Dict, List, Optional, Tuple +from typing import Dict, Optional, Tuple import grpc +from typing_extensions import deprecated from pyzeebe.grpc_internals.zeebe_adapter import ZeebeAdapter @@ -17,7 +18,9 @@ def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = self.zeebe_adapter = ZeebeAdapter(grpc_channel, max_connection_retries) - async def run_process(self, bpmn_process_id: str, variables: Optional[Dict] = None, version: int = -1) -> int: + async def run_process( + self, bpmn_process_id: str, variables: Optional[Dict] = None, version: int = -1, tenant_id: Optional[str] = None + ) -> int: """ Run process @@ -25,6 +28,7 @@ async def run_process(self, bpmn_process_id: str, variables: Optional[Dict] = No bpmn_process_id (str): The unique process id of the process. variables (dict): A dictionary containing all the starting variables the process needs. Must be JSONable. version (int): The version of the process. Default: -1 (latest) + tenant_id (str): The tenant ID of the process definition. New in Zeebe 8.3. Returns: int: process_instance_key, the unique id of the running process generated by Zeebe. @@ -40,7 +44,7 @@ async def run_process(self, bpmn_process_id: str, variables: Optional[Dict] = No """ return await self.zeebe_adapter.create_process_instance( - bpmn_process_id=bpmn_process_id, variables=variables or {}, version=version + bpmn_process_id=bpmn_process_id, variables=variables or {}, version=version, tenant_id=tenant_id ) async def run_process_with_result( @@ -49,7 +53,7 @@ async def run_process_with_result( variables: Optional[Dict] = None, version: int = -1, timeout: int = 0, - variables_to_fetch: Optional[List[str]] = None, + tenant_id: Optional[str] = None, ) -> Tuple[int, Dict]: """ Run process and wait for the result. @@ -59,7 +63,7 @@ async def run_process_with_result( variables (dict): A dictionary containing all the starting variables the process needs. Must be JSONable. version (int): The version of the process. Default: -1 (latest) timeout (int): How long to wait until a timeout occurs. Default: 0 (Zeebe default timeout) - variables_to_fetch (List[str]): Which variables to get from the finished process + tenant_id (str): The tenant ID of the process definition. New in Zeebe 8.3. Returns: tuple: (The process instance key, A dictionary of the end state of the process instance) @@ -80,7 +84,7 @@ async def run_process_with_result( variables=variables or {}, version=version, timeout=timeout, - variables_to_fetch=variables_to_fetch or [], + tenant_id=tenant_id, ) async def cancel_process_instance(self, process_instance_key: int) -> int: @@ -104,6 +108,7 @@ async def cancel_process_instance(self, process_instance_key: int) -> int: await self.zeebe_adapter.cancel_process_instance(process_instance_key=process_instance_key) return process_instance_key + @deprecated("Deprecated since Zeebe 8.0. Use deploy_resource instead") async def deploy_process(self, *process_file_path: str) -> None: """ Deploy one or more processes @@ -121,6 +126,26 @@ async def deploy_process(self, *process_file_path: str) -> None: """ await self.zeebe_adapter.deploy_process(*process_file_path) + async def deploy_resource(self, *resource_file_path: str, tenant_id: Optional[str] = None) -> None: + """ + Deploy one or more processes + + New in Zeebe 8.0. + + Args: + resource_file_path (str): The file path to a resource definition file (bpmn/dmn/form) + tenant_id (str): The tenant ID of the resources to deploy. New in Zeebe 8.3. + + Raises: + ProcessInvalidError: If one of the process file definitions is invalid + ZeebeBackPressureError: If Zeebe is currently in back pressure (too many requests) + ZeebeGatewayUnavailableError: If the Zeebe gateway is unavailable + ZeebeInternalError: If Zeebe experiences an internal error + UnkownGrpcStatusCodeError: If Zeebe returns an unexpected status code + + """ + await self.zeebe_adapter.deploy_resource(*resource_file_path, tenant_id=tenant_id) + async def publish_message( self, name: str, @@ -128,6 +153,7 @@ async def publish_message( variables: Optional[Dict] = None, time_to_live_in_milliseconds: int = 60000, message_id: Optional[str] = None, + tenant_id: Optional[str] = None, ) -> None: """ Publish a message @@ -139,6 +165,7 @@ async def publish_message( time_to_live_in_milliseconds (int): How long this message should stay active. Default: 60000 ms (60 seconds) message_id (str): A unique message id. Useful for avoiding duplication. If a message with this id is still active, a MessageAlreadyExists will be raised. + tenant_id (str): The tenant ID of the message. New in Zeebe 8.3. Raises: MessageAlreadyExistError: If a message with message_id already exists @@ -154,4 +181,5 @@ async def publish_message( time_to_live_in_milliseconds=time_to_live_in_milliseconds, variables=variables or {}, message_id=message_id, + tenant_id=tenant_id, ) diff --git a/pyzeebe/client/sync_client.py b/pyzeebe/client/sync_client.py index 9de02edd..e203fa45 100644 --- a/pyzeebe/client/sync_client.py +++ b/pyzeebe/client/sync_client.py @@ -1,7 +1,8 @@ import asyncio -from typing import Dict, List, Optional, Tuple +from typing import Dict, Optional, Tuple import grpc +from typing_extensions import deprecated from pyzeebe import ZeebeClient @@ -11,8 +12,14 @@ def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = self.loop = asyncio.get_event_loop() self.client = ZeebeClient(grpc_channel, max_connection_retries) - def run_process(self, bpmn_process_id: str, variables: Optional[Dict] = None, version: int = -1) -> int: - return self.loop.run_until_complete(self.client.run_process(bpmn_process_id, variables, version)) + def run_process( + self, + bpmn_process_id: str, + variables: Optional[Dict] = None, + version: int = -1, + tenant_id: Optional[str] = None, + ) -> int: + return self.loop.run_until_complete(self.client.run_process(bpmn_process_id, variables, version, tenant_id)) def run_process_with_result( self, @@ -20,18 +27,22 @@ def run_process_with_result( variables: Optional[Dict] = None, version: int = -1, timeout: int = 0, - variables_to_fetch: Optional[List[str]] = None, + tenant_id: Optional[str] = None, ) -> Tuple[int, Dict]: return self.loop.run_until_complete( - self.client.run_process_with_result(bpmn_process_id, variables, version, timeout, variables_to_fetch) + self.client.run_process_with_result(bpmn_process_id, variables, version, timeout, tenant_id) ) def cancel_process_instance(self, process_instance_key: int) -> int: return self.loop.run_until_complete(self.client.cancel_process_instance(process_instance_key)) + @deprecated("Deprecated since Zeebe 8.0. Use deploy_resource instead") def deploy_process(self, *process_file_path: str) -> None: return self.loop.run_until_complete(self.client.deploy_process(*process_file_path)) + def deploy_resource(self, *resource_file_path: str, tenant_id: Optional[str] = None) -> None: + return self.loop.run_until_complete(self.client.deploy_resource(*resource_file_path, tenant_id=tenant_id)) + def publish_message( self, name: str, @@ -39,6 +50,7 @@ def publish_message( variables: Optional[Dict] = None, time_to_live_in_milliseconds: int = 60000, message_id: Optional[str] = None, + tenant_id: Optional[str] = None, ) -> None: return self.loop.run_until_complete( self.client.publish_message( @@ -47,5 +59,6 @@ def publish_message( variables, time_to_live_in_milliseconds, message_id, + tenant_id, ) ) diff --git a/pyzeebe/grpc_internals/zeebe_job_adapter.py b/pyzeebe/grpc_internals/zeebe_job_adapter.py index 10b6d280..67d03ea2 100644 --- a/pyzeebe/grpc_internals/zeebe_job_adapter.py +++ b/pyzeebe/grpc_internals/zeebe_job_adapter.py @@ -1,6 +1,6 @@ import json import logging -from typing import AsyncGenerator, Dict, List +from typing import AsyncGenerator, Dict, List, Optional import grpc from zeebe_grpc.gateway_pb2 import ( @@ -34,6 +34,7 @@ async def activate_jobs( max_jobs_to_activate: int, variables_to_fetch: List[str], request_timeout: int, + tenant_ids: Optional[List[str]] = None, ) -> AsyncGenerator[Job, None]: try: async for response in self._gateway_stub.ActivateJobs( @@ -44,6 +45,7 @@ async def activate_jobs( maxJobsToActivate=max_jobs_to_activate, fetchVariable=variables_to_fetch, requestTimeout=request_timeout, + tenantIds=tenant_ids, ) ): for raw_job in response.jobs: @@ -70,6 +72,7 @@ def _create_job_from_raw_job(self, response) -> Job: retries=response.retries, deadline=response.deadline, variables=json.loads(response.variables), + tenant_id=response.tenantId, zeebe_adapter=self, ) @@ -85,10 +88,18 @@ async def complete_job(self, job_key: int, variables: Dict) -> CompleteJobRespon raise JobAlreadyDeactivatedError(job_key=job_key) from grpc_error await self._handle_grpc_error(grpc_error) - async def fail_job(self, job_key: int, retries: int, message: str) -> FailJobResponse: + async def fail_job( + self, job_key: int, retries: int, message: str, retry_back_off_ms: int, variables: Dict + ) -> FailJobResponse: try: return await self._gateway_stub.FailJob( - FailJobRequest(jobKey=job_key, retries=retries, errorMessage=message) + FailJobRequest( + jobKey=job_key, + retries=retries, + errorMessage=message, + retryBackOff=retry_back_off_ms, + variables=json.dumps(variables), + ) ) except grpc.aio.AioRpcError as grpc_error: if is_error_status(grpc_error, grpc.StatusCode.NOT_FOUND): @@ -97,10 +108,17 @@ async def fail_job(self, job_key: int, retries: int, message: str) -> FailJobRes raise JobAlreadyDeactivatedError(job_key=job_key) from grpc_error await self._handle_grpc_error(grpc_error) - async def throw_error(self, job_key: int, message: str, error_code: str = "") -> ThrowErrorResponse: + async def throw_error( + self, job_key: int, message: str, variables: Dict, error_code: str = "" + ) -> ThrowErrorResponse: try: return await self._gateway_stub.ThrowError( - ThrowErrorRequest(jobKey=job_key, errorMessage=message, errorCode=error_code) + ThrowErrorRequest( + jobKey=job_key, + errorMessage=message, + errorCode=error_code, + variables=json.dumps(variables), + ) ) except grpc.aio.AioRpcError as grpc_error: if is_error_status(grpc_error, grpc.StatusCode.NOT_FOUND): diff --git a/pyzeebe/grpc_internals/zeebe_message_adapter.py b/pyzeebe/grpc_internals/zeebe_message_adapter.py index f4623bc8..e20a5e2d 100644 --- a/pyzeebe/grpc_internals/zeebe_message_adapter.py +++ b/pyzeebe/grpc_internals/zeebe_message_adapter.py @@ -17,6 +17,7 @@ async def publish_message( time_to_live_in_milliseconds: int, variables: Dict, message_id: Optional[str] = None, + tenant_id: Optional[str] = None, ) -> PublishMessageResponse: try: return await self._gateway_stub.PublishMessage( @@ -26,6 +27,7 @@ async def publish_message( messageId=message_id, timeToLive=time_to_live_in_milliseconds, variables=json.dumps(variables), + tenantId=tenant_id, ) ) except grpc.aio.AioRpcError as grpc_error: diff --git a/pyzeebe/grpc_internals/zeebe_process_adapter.py b/pyzeebe/grpc_internals/zeebe_process_adapter.py index eb0f25e1..29fa9ca0 100644 --- a/pyzeebe/grpc_internals/zeebe_process_adapter.py +++ b/pyzeebe/grpc_internals/zeebe_process_adapter.py @@ -1,16 +1,20 @@ import json import os -from typing import Dict, Tuple +from typing import Dict, Optional, Tuple import aiofiles import grpc +from typing_extensions import deprecated from zeebe_grpc.gateway_pb2 import ( CancelProcessInstanceRequest, CreateProcessInstanceRequest, CreateProcessInstanceWithResultRequest, DeployProcessRequest, DeployProcessResponse, + DeployResourceRequest, + DeployResourceResponse, ProcessRequestObject, + Resource, ) from pyzeebe.errors import ( @@ -26,11 +30,20 @@ class ZeebeProcessAdapter(ZeebeAdapterBase): - async def create_process_instance(self, bpmn_process_id: str, version: int, variables: Dict) -> int: + async def create_process_instance( + self, + bpmn_process_id: str, + version: int, + variables: Dict, + tenant_id: Optional[str] = None, + ) -> int: try: response = await self._gateway_stub.CreateProcessInstance( CreateProcessInstanceRequest( - bpmnProcessId=bpmn_process_id, version=version, variables=json.dumps(variables) + bpmnProcessId=bpmn_process_id, + version=version, + variables=json.dumps(variables), + tenantId=tenant_id, ) ) except grpc.aio.AioRpcError as grpc_error: @@ -38,16 +51,23 @@ async def create_process_instance(self, bpmn_process_id: str, version: int, vari return response.processInstanceKey async def create_process_instance_with_result( - self, bpmn_process_id: str, version: int, variables: Dict, timeout: int, variables_to_fetch + self, + bpmn_process_id: str, + version: int, + variables: Dict, + timeout: int, + tenant_id: Optional[str] = None, ) -> Tuple[int, Dict]: try: response = await self._gateway_stub.CreateProcessInstanceWithResult( CreateProcessInstanceWithResultRequest( request=CreateProcessInstanceRequest( - bpmnProcessId=bpmn_process_id, version=version, variables=json.dumps(variables) + bpmnProcessId=bpmn_process_id, + version=version, + variables=json.dumps(variables), + tenantId=tenant_id, ), requestTimeout=timeout, - fetchVariables=variables_to_fetch, ) ) except grpc.aio.AioRpcError as grpc_error: @@ -79,6 +99,7 @@ async def cancel_process_instance(self, process_instance_key: int) -> None: raise ProcessInstanceNotFoundError(process_instance_key=process_instance_key) from grpc_error await self._handle_grpc_error(grpc_error) + @deprecated("Deprecated since Zeebe 8.0. Use deploy_resource instead") async def deploy_process(self, *process_file_path: str) -> DeployProcessResponse: try: return await self._gateway_stub.DeployProcess( @@ -91,7 +112,27 @@ async def deploy_process(self, *process_file_path: str) -> DeployProcessResponse raise ProcessInvalidError() from grpc_error await self._handle_grpc_error(grpc_error) + async def deploy_resource( + self, *resource_file_path: str, tenant_id: Optional[str] = None + ) -> DeployResourceResponse: + try: + return await self._gateway_stub.DeployResource( + DeployResourceRequest( + resources=[await result for result in map(_create_resource_request, resource_file_path)], + tenantId=tenant_id, + ) + ) + except grpc.aio.AioRpcError as grpc_error: + if is_error_status(grpc_error, grpc.StatusCode.INVALID_ARGUMENT): + raise ProcessInvalidError() from grpc_error + await self._handle_grpc_error(grpc_error) + async def _create_process_request(process_file_path: str) -> ProcessRequestObject: async with aiofiles.open(process_file_path, "rb") as file: return ProcessRequestObject(name=os.path.basename(process_file_path), definition=await file.read()) + + +async def _create_resource_request(resource_file_path: str) -> Resource: + async with aiofiles.open(resource_file_path, "rb") as file: + return Resource(name=os.path.basename(resource_file_path), content=await file.read()) diff --git a/pyzeebe/job/job.py b/pyzeebe/job/job.py index c5da74c1..8410026b 100644 --- a/pyzeebe/job/job.py +++ b/pyzeebe/job/job.py @@ -21,6 +21,7 @@ class Job: retries: int deadline: int variables: Dict + tenant_id: Optional[str] = None status: JobStatus = JobStatus.Running zeebe_adapter: Optional["ZeebeAdapter"] = None # type: ignore @@ -57,13 +58,21 @@ async def set_success_status(self) -> None: else: raise NoZeebeAdapterError() - async def set_failure_status(self, message: str) -> None: + async def set_failure_status( + self, + message: str, + retry_back_off_ms: int = 0, + variables: Optional[Dict] = None, + ) -> None: """ Failure status means a technical error has occurred. If retried the job may succeed. For example: connection to DB lost Args: message (str): The failure message that Zeebe will receive + retry_back_off_ms (int): The backoff timeout (in ms) for the next retry. New in Zeebe 8.1. + variables (dict): A dictionary containing variables that will instantiate the variables at + the local scope of the job's associated task. Must be JSONable. New in Zeebe 8.2. Raises: NoZeebeAdapterError: If the job does not have a configured ZeebeAdapter @@ -74,11 +83,22 @@ async def set_failure_status(self, message: str) -> None: """ if self.zeebe_adapter: self.status = JobStatus.Failed - await self.zeebe_adapter.fail_job(job_key=self.key, retries=self.retries - 1, message=message) + await self.zeebe_adapter.fail_job( + job_key=self.key, + retries=self.retries - 1, + message=message, + retry_back_off_ms=retry_back_off_ms, + variables=variables or {}, + ) else: raise NoZeebeAdapterError() - async def set_error_status(self, message: str, error_code: str = "") -> None: + async def set_error_status( + self, + message: str, + error_code: str = "", + variables: Optional[Dict] = None, + ) -> None: """ Error status means that the job could not be completed because of a business error and won't ever be able to be completed. For example: a required parameter was not given @@ -87,6 +107,8 @@ async def set_error_status(self, message: str, error_code: str = "") -> None: Args: message (str): The error message error_code (str): The error code that Zeebe will receive + variables (dict): A dictionary containing variables that will instantiate the variables at + the local scope of the job's associated task. Must be JSONable. New in Zeebe 8.2. Raises: NoZeebeAdapterError: If the job does not have a configured ZeebeAdapter @@ -97,7 +119,9 @@ async def set_error_status(self, message: str, error_code: str = "") -> None: """ if self.zeebe_adapter: self.status = JobStatus.ErrorThrown - await self.zeebe_adapter.throw_error(job_key=self.key, message=message, error_code=error_code) + await self.zeebe_adapter.throw_error( + job_key=self.key, message=message, error_code=error_code, variables=variables or {} + ) else: raise NoZeebeAdapterError() @@ -122,6 +146,7 @@ def create_copy(job: Job) -> Job: job.retries, job.deadline, copy.deepcopy(job.variables), + job.tenant_id, job.status, job.zeebe_adapter, ) diff --git a/pyzeebe/worker/job_poller.py b/pyzeebe/worker/job_poller.py index c0364b12..fbb634b3 100644 --- a/pyzeebe/worker/job_poller.py +++ b/pyzeebe/worker/job_poller.py @@ -1,5 +1,6 @@ import asyncio import logging +from typing import List, Optional from pyzeebe.errors import ( ActivateJobsRequestInvalidError, @@ -24,6 +25,7 @@ def __init__( request_timeout: int, task_state: TaskState, poll_retry_delay: int, + tenant_ids: Optional[List[str]], ): self.zeebe_adapter = zeebe_adapter self.task = task @@ -32,6 +34,7 @@ def __init__( self.request_timeout = request_timeout self.task_state = task_state self.poll_retry_delay = poll_retry_delay + self.tenant_ids = tenant_ids self.stop_event = asyncio.Event() async def poll(self): @@ -58,6 +61,7 @@ async def poll_once(self): max_jobs_to_activate=self.calculate_max_jobs_to_activate(), variables_to_fetch=self.task.config.variables_to_fetch, request_timeout=self.request_timeout, + tenant_ids=self.tenant_ids, ) async for job in jobs: self.task_state.add(job) diff --git a/pyzeebe/worker/worker.py b/pyzeebe/worker/worker.py index 48d93c58..76a0e4b8 100644 --- a/pyzeebe/worker/worker.py +++ b/pyzeebe/worker/worker.py @@ -29,6 +29,7 @@ def __init__( max_connection_retries: int = 10, watcher_max_errors_factor: int = 3, poll_retry_delay: int = 5, + tenant_ids: Optional[List[str]] = None, ): """ Args: @@ -40,6 +41,7 @@ def __init__( max_connection_retries (int): Amount of connection retries before worker gives up on connecting to zeebe. To setup with infinite retries use -1 watcher_max_errors_factor (int): Number of consecutive errors for a task watcher will accept before raising MaxConsecutiveTaskThreadError poll_retry_delay (int): The number of seconds to wait before attempting to poll again when reaching max amount of running jobs + tenant_ids (List[str]): A list of tenant IDs for which to activate jobs. New in Zeebe 8.3. """ super().__init__(before, after) self.zeebe_adapter = ZeebeAdapter(grpc_channel, max_connection_retries) @@ -48,6 +50,7 @@ def __init__( self.watcher_max_errors_factor = watcher_max_errors_factor self._watcher_thread = None self.poll_retry_delay = poll_retry_delay + self.tenant_ids = tenant_ids self._work_task: Optional[asyncio.Future] = None self._job_pollers: List[JobPoller] = [] self._job_executors: List[JobExecutor] = [] @@ -78,6 +81,7 @@ async def work(self) -> None: self.request_timeout, task_state, self.poll_retry_delay, + self.tenant_ids, ) executor = JobExecutor(task, jobs_queue, task_state) self._job_pollers.append(poller) diff --git a/tests/integration/run_process_test.py b/tests/integration/run_process_test.py index ac2603a0..845ed68c 100644 --- a/tests/integration/run_process_test.py +++ b/tests/integration/run_process_test.py @@ -7,7 +7,6 @@ from pyzeebe.errors import ProcessDefinitionNotFoundError from tests.integration.utils import ProcessStats, wait_for_process - PROCESS_TIMEOUT_IN_MS = 60_000 diff --git a/tests/unit/client/client_test.py b/tests/unit/client/client_test.py index 1e854b08..10f2738a 100644 --- a/tests/unit/client/client_test.py +++ b/tests/unit/client/client_test.py @@ -43,6 +43,14 @@ async def test_deploy_process(zeebe_client): zeebe_client.zeebe_adapter.deploy_process.assert_called_with(file_path) +@pytest.mark.asyncio +async def test_deploy_resource(zeebe_client): + zeebe_client.zeebe_adapter.deploy_resource = AsyncMock() + file_path = str(uuid4()) + await zeebe_client.deploy_resource(file_path) + zeebe_client.zeebe_adapter.deploy_resource.assert_called_with(file_path, tenant_id=None) + + @pytest.mark.asyncio async def test_run_non_existent_process(zeebe_client): with pytest.raises(ProcessDefinitionNotFoundError): diff --git a/tests/unit/client/sync_client_test.py b/tests/unit/client/sync_client_test.py index 5f21d31e..e5e2b11e 100644 --- a/tests/unit/client/sync_client_test.py +++ b/tests/unit/client/sync_client_test.py @@ -76,6 +76,16 @@ def test_calls_deploy_process_of_zeebe_client(self, sync_zeebe_client: SyncZeebe sync_zeebe_client.client.deploy_process.assert_called_with(file_path) +class TestDeployResource: + def test_calls_deploy_resource_of_zeebe_client(self, sync_zeebe_client: SyncZeebeClient): + sync_zeebe_client.client.deploy_resource = AsyncMock() + file_path = str(uuid4()) + + sync_zeebe_client.deploy_resource(file_path) + + sync_zeebe_client.client.deploy_resource.assert_called_with(file_path, tenant_id=None) + + class TestPublishMessage: def test_calls_publish_message_of_zeebe_client(self, sync_zeebe_client: SyncZeebeClient): sync_zeebe_client.client.publish_message = AsyncMock() diff --git a/tests/unit/grpc_internals/zeebe_job_adapter_test.py b/tests/unit/grpc_internals/zeebe_job_adapter_test.py index 8893caa4..799d53ae 100644 --- a/tests/unit/grpc_internals/zeebe_job_adapter_test.py +++ b/tests/unit/grpc_internals/zeebe_job_adapter_test.py @@ -44,9 +44,10 @@ def activate_jobs( request_timeout=100, max_jobs_to_activate=1, variables_to_fetch=[], + tenant_ids=None, ): return self.zeebe_job_adapter.activate_jobs( - task_type, worker, timeout, max_jobs_to_activate, variables_to_fetch, request_timeout + task_type, worker, timeout, max_jobs_to_activate, variables_to_fetch, request_timeout, tenant_ids ) async def test_returns_correct_amount_of_jobs(self, grpc_servicer: GatewayMock, task: Task): @@ -101,34 +102,34 @@ async def test_raises_on_already_completed_job(self, zeebe_adapter: ZeebeJobAdap @pytest.mark.asyncio class TestFailJob: async def test_response_is_of_correct_type(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - response = await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message()) + response = await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message(), 0, {}) assert isinstance(response, FailJobResponse) async def test_raises_on_fake_job(self, zeebe_adapter: ZeebeJobAdapter): with pytest.raises(JobNotFoundError): - await zeebe_adapter.fail_job(random_job_key(), 1, random_message()) + await zeebe_adapter.fail_job(random_job_key(), 1, random_message(), 0, {}) async def test_raises_on_deactivated_job(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message()) + await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message(), 0, {}) with pytest.raises(JobAlreadyDeactivatedError): - await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message()) + await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message(), 0, {}) @pytest.mark.asyncio class TestThrowError: async def test_response_is_of_correct_type(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - response = await zeebe_adapter.throw_error(first_active_job.key, random_message()) + response = await zeebe_adapter.throw_error(first_active_job.key, random_message(), {}) assert isinstance(response, ThrowErrorResponse) async def test_raises_on_fake_job(self, zeebe_adapter: ZeebeJobAdapter): with pytest.raises(JobNotFoundError): - await zeebe_adapter.throw_error(random_job_key(), random_message()) + await zeebe_adapter.throw_error(random_job_key(), random_message(), {}) async def test_raises_on_deactivated_job(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - await zeebe_adapter.throw_error(first_active_job.key, random_message()) + await zeebe_adapter.throw_error(first_active_job.key, random_message(), {}) with pytest.raises(JobAlreadyDeactivatedError): - await zeebe_adapter.throw_error(first_active_job.key, random_message()) + await zeebe_adapter.throw_error(first_active_job.key, random_message(), {}) diff --git a/tests/unit/grpc_internals/zeebe_process_adapter_test.py b/tests/unit/grpc_internals/zeebe_process_adapter_test.py index f8abd2f0..368f3554 100644 --- a/tests/unit/grpc_internals/zeebe_process_adapter_test.py +++ b/tests/unit/grpc_internals/zeebe_process_adapter_test.py @@ -66,7 +66,7 @@ async def test_process_instance_key_type_is_int( grpc_servicer.mock_deploy_process(bpmn_process_id, version, []) process_instance_key, _ = await zeebe_adapter.create_process_instance_with_result( - bpmn_process_id=bpmn_process_id, variables={}, version=version, timeout=0, variables_to_fetch=[] + bpmn_process_id=bpmn_process_id, variables={}, version=version, timeout=0, tenant_id=None ) assert isinstance(process_instance_key, int) @@ -77,7 +77,7 @@ async def test_variables_type_is_dict(self, zeebe_adapter: ZeebeProcessAdapter, grpc_servicer.mock_deploy_process(bpmn_process_id, version, []) _, response = await zeebe_adapter.create_process_instance_with_result( - bpmn_process_id=bpmn_process_id, variables={}, version=version, timeout=0, variables_to_fetch=[] + bpmn_process_id=bpmn_process_id, variables={}, version=version, timeout=0, tenant_id=None ) assert isinstance(response, dict) @@ -96,7 +96,7 @@ async def test_raises_on_process_timeout(self, zeebe_adapter: ZeebeProcessAdapte variables={}, version=version, timeout=0, - variables_to_fetch=[], + tenant_id=None, ) @@ -154,3 +154,34 @@ async def test_calls_open_in_rb_mode(self, zeebe_adapter: ZeebeProcessAdapter): await zeebe_adapter.deploy_process(file_path) self.open_mock.assert_called_with(file_path, "rb") + + +@pytest.mark.asyncio +class TestDeployResource: + open_mock: MagicMock + + @pytest.fixture(autouse=True) + def mocked_aiofiles_open(self): + read_mock = AsyncMock(return_value=bytes()) + + file_mock = AsyncMock() + file_mock.__aenter__.return_value.read = read_mock + + with patch("pyzeebe.grpc_internals.zeebe_process_adapter.aiofiles.open", return_value=file_mock) as open_mock: + self.open_mock = open_mock + yield + + async def test_raises_on_invalid_process(self, zeebe_adapter: ZeebeProcessAdapter): + error = grpc.aio.AioRpcError(grpc.StatusCode.INVALID_ARGUMENT, None, None) + + zeebe_adapter._gateway_stub.DeployResource = AsyncMock(side_effect=error) + + with pytest.raises(ProcessInvalidError): + await zeebe_adapter.deploy_resource() + + async def test_calls_open_in_rb_mode(self, zeebe_adapter: ZeebeProcessAdapter): + file_path = str(uuid4()) + + await zeebe_adapter.deploy_resource(file_path) + + self.open_mock.assert_called_with(file_path, "rb") diff --git a/tests/unit/job/job_test.py b/tests/unit/job/job_test.py index f6ac8564..c96ae74d 100644 --- a/tests/unit/job/job_test.py +++ b/tests/unit/job/job_test.py @@ -39,7 +39,7 @@ async def test_updates_job_in_zeebe(self, job_with_adapter): await job_with_adapter.set_error_status(message) - throw_error_mock.assert_called_with(job_key=job_with_adapter.key, message=message, error_code="") + throw_error_mock.assert_called_with(job_key=job_with_adapter.key, message=message, error_code="", variables={}) async def test_updates_job_in_zeebe_with_code(self, job_with_adapter): throw_error_mock = AsyncMock() @@ -49,7 +49,9 @@ async def test_updates_job_in_zeebe_with_code(self, job_with_adapter): await job_with_adapter.set_error_status(message, error_code) - throw_error_mock.assert_called_with(job_key=job_with_adapter.key, message=message, error_code=error_code) + throw_error_mock.assert_called_with( + job_key=job_with_adapter.key, message=message, error_code=error_code, variables={} + ) async def test_status_is_set(self, job_with_adapter): throw_error_mock = AsyncMock() @@ -76,7 +78,11 @@ async def test_updates_job_in_zeebe(self, job_with_adapter): await job_with_adapter.set_failure_status(message) fail_job_mock.assert_called_with( - job_key=job_with_adapter.key, retries=job_with_adapter.retries - 1, message=message + job_key=job_with_adapter.key, + retries=job_with_adapter.retries - 1, + message=message, + retry_back_off_ms=0, + variables={}, ) async def test_status_is_set(self, job_with_adapter): diff --git a/tests/unit/utils/gateway_mock.py b/tests/unit/utils/gateway_mock.py index 9b0e2c21..0b2c7a59 100644 --- a/tests/unit/utils/gateway_mock.py +++ b/tests/unit/utils/gateway_mock.py @@ -63,6 +63,7 @@ def ActivateJobs(self, request, context): retries=active_job.retries, deadline=active_job.deadline, variables=json.dumps(active_job.variables), + tenantId=active_job.tenant_id, ) ) yield ActivateJobsResponse(jobs=jobs) @@ -115,6 +116,7 @@ def CreateProcessInstance(self, request, context): bpmnProcessId=request.bpmnProcessId, version=request.version, processInstanceKey=process_instance_key, + tenantId=request.tenantId, ) else: context.set_code(grpc.StatusCode.NOT_FOUND) @@ -131,6 +133,7 @@ def CreateProcessInstanceWithResult(self, request, context): bpmnProcessId=request.request.bpmnProcessId, version=randint(0, 10), variables=request.request.variables, + tenantId=request.request.tenantId, ) else: context.set_code(grpc.StatusCode.NOT_FOUND) @@ -157,6 +160,22 @@ def DeployProcess(self, request, context): return DeployProcessResponse(key=randint(0, RANDOM_RANGE), processes=processes) + def DeployResource(self, request, context): + resources = [] + for resource in request.resources: + process_metadata = Deployment( + process=ProcessMetadata( + bpmnProcessId=str(uuid4()), + version=randint(0, 10), + processDefinitionKey=randint(0, RANDOM_RANGE), + resourceName=resource.name, + tenantId=request.tenantId, + ) + ) + resources.append(process_metadata) + + return DeployResourceResponse(key=randint(0, RANDOM_RANGE), deployments=resources, tenantId=request.tenantId) + def PublishMessage(self, request, context): if request.messageId in self.messages.keys(): context.set_code(grpc.StatusCode.ALREADY_EXISTS) diff --git a/tests/unit/worker/job_poller_test.py b/tests/unit/worker/job_poller_test.py index d01d1986..7d8cf69e 100644 --- a/tests/unit/worker/job_poller_test.py +++ b/tests/unit/worker/job_poller_test.py @@ -14,7 +14,7 @@ @pytest.fixture def job_poller(zeebe_adapter: ZeebeAdapter, task: Task, queue: asyncio.Queue, task_state: TaskState) -> JobPoller: - return JobPoller(zeebe_adapter, task, queue, "test_worker", 100, task_state, 0) + return JobPoller(zeebe_adapter, task, queue, "test_worker", 100, task_state, 0, None) @pytest.mark.asyncio From 12c1d49cdec6b0b7c38b5498acb644cfc38ebf2a Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Thu, 22 Feb 2024 10:45:00 +0600 Subject: [PATCH 2/6] revert "remove CreateProcessInstanceWithResultRequest.fetchVariables" --- pyzeebe/client/client.py | 5 ++++- pyzeebe/client/sync_client.py | 7 +++++-- pyzeebe/grpc_internals/zeebe_process_adapter.py | 2 ++ .../grpc_internals/zeebe_process_adapter_test.py | 15 +++++++++++++-- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pyzeebe/client/client.py b/pyzeebe/client/client.py index 70e8be3d..af775608 100644 --- a/pyzeebe/client/client.py +++ b/pyzeebe/client/client.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Tuple +from typing import Dict, List, Optional, Tuple import grpc from typing_extensions import deprecated @@ -53,6 +53,7 @@ async def run_process_with_result( variables: Optional[Dict] = None, version: int = -1, timeout: int = 0, + variables_to_fetch: Optional[List[str]] = None, tenant_id: Optional[str] = None, ) -> Tuple[int, Dict]: """ @@ -63,6 +64,7 @@ async def run_process_with_result( variables (dict): A dictionary containing all the starting variables the process needs. Must be JSONable. version (int): The version of the process. Default: -1 (latest) timeout (int): How long to wait until a timeout occurs. Default: 0 (Zeebe default timeout) + variables_to_fetch (List[str]): Which variables to get from the finished process tenant_id (str): The tenant ID of the process definition. New in Zeebe 8.3. Returns: @@ -84,6 +86,7 @@ async def run_process_with_result( variables=variables or {}, version=version, timeout=timeout, + variables_to_fetch=variables_to_fetch or [], tenant_id=tenant_id, ) diff --git a/pyzeebe/client/sync_client.py b/pyzeebe/client/sync_client.py index e203fa45..1dc6ad22 100644 --- a/pyzeebe/client/sync_client.py +++ b/pyzeebe/client/sync_client.py @@ -1,5 +1,5 @@ import asyncio -from typing import Dict, Optional, Tuple +from typing import Dict, List, Optional, Tuple import grpc from typing_extensions import deprecated @@ -27,10 +27,13 @@ def run_process_with_result( variables: Optional[Dict] = None, version: int = -1, timeout: int = 0, + variables_to_fetch: Optional[List[str]] = None, tenant_id: Optional[str] = None, ) -> Tuple[int, Dict]: return self.loop.run_until_complete( - self.client.run_process_with_result(bpmn_process_id, variables, version, timeout, tenant_id) + self.client.run_process_with_result( + bpmn_process_id, variables, version, timeout, variables_to_fetch, tenant_id + ) ) def cancel_process_instance(self, process_instance_key: int) -> int: diff --git a/pyzeebe/grpc_internals/zeebe_process_adapter.py b/pyzeebe/grpc_internals/zeebe_process_adapter.py index 29fa9ca0..bfe8b42c 100644 --- a/pyzeebe/grpc_internals/zeebe_process_adapter.py +++ b/pyzeebe/grpc_internals/zeebe_process_adapter.py @@ -56,6 +56,7 @@ async def create_process_instance_with_result( version: int, variables: Dict, timeout: int, + variables_to_fetch, tenant_id: Optional[str] = None, ) -> Tuple[int, Dict]: try: @@ -68,6 +69,7 @@ async def create_process_instance_with_result( tenantId=tenant_id, ), requestTimeout=timeout, + fetchVariables=variables_to_fetch, ) ) except grpc.aio.AioRpcError as grpc_error: diff --git a/tests/unit/grpc_internals/zeebe_process_adapter_test.py b/tests/unit/grpc_internals/zeebe_process_adapter_test.py index 368f3554..13c78b12 100644 --- a/tests/unit/grpc_internals/zeebe_process_adapter_test.py +++ b/tests/unit/grpc_internals/zeebe_process_adapter_test.py @@ -66,7 +66,12 @@ async def test_process_instance_key_type_is_int( grpc_servicer.mock_deploy_process(bpmn_process_id, version, []) process_instance_key, _ = await zeebe_adapter.create_process_instance_with_result( - bpmn_process_id=bpmn_process_id, variables={}, version=version, timeout=0, tenant_id=None + bpmn_process_id=bpmn_process_id, + variables={}, + version=version, + timeout=0, + variables_to_fetch=[], + tenant_id=None, ) assert isinstance(process_instance_key, int) @@ -77,7 +82,12 @@ async def test_variables_type_is_dict(self, zeebe_adapter: ZeebeProcessAdapter, grpc_servicer.mock_deploy_process(bpmn_process_id, version, []) _, response = await zeebe_adapter.create_process_instance_with_result( - bpmn_process_id=bpmn_process_id, variables={}, version=version, timeout=0, tenant_id=None + bpmn_process_id=bpmn_process_id, + variables={}, + version=version, + timeout=0, + variables_to_fetch=[], + tenant_id=None, ) assert isinstance(response, dict) @@ -96,6 +106,7 @@ async def test_raises_on_process_timeout(self, zeebe_adapter: ZeebeProcessAdapte variables={}, version=version, timeout=0, + variables_to_fetch=[], tenant_id=None, ) From 34f656f12b7c1e8dc4e4130218bfb7444d320e59 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Thu, 22 Feb 2024 10:45:22 +0600 Subject: [PATCH 3/6] zeebe-grpc: 8.4.0 -> ^8.4.0 --- poetry.lock | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 46013d74..4eaf0d69 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1394,4 +1394,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "cba2d839f329f76fef50e96fa154e5b5fb1ede0f9ad7b72363bd4aa5ccad7664" +content-hash = "fbcb08afddd0dd83a77c7e078e925f5c2252f240bce50be0746f66c4f299c1d7" diff --git a/pyproject.toml b/pyproject.toml index 995284c2..f43bf6e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ python = "^3.8" oauthlib = "~=3.1.0" requests-oauthlib = "~=1.3.0" aiofiles = ">=0.7,<0.9" -zeebe-grpc = "8.4.0" +zeebe-grpc = "^8.4.0" typing-extensions = "^4.5.0" [tool.poetry.group.dev.dependencies] From 5ba2a00e092417f4b6e50c23c30f620d4978583a Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Thu, 22 Feb 2024 11:13:08 +0600 Subject: [PATCH 4/6] tests: de duplicate code --- .../zeebe_process_adapter_test.py | 47 +++++++------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/tests/unit/grpc_internals/zeebe_process_adapter_test.py b/tests/unit/grpc_internals/zeebe_process_adapter_test.py index 13c78b12..659c90cb 100644 --- a/tests/unit/grpc_internals/zeebe_process_adapter_test.py +++ b/tests/unit/grpc_internals/zeebe_process_adapter_test.py @@ -3,7 +3,7 @@ import grpc import pytest -from mock import AsyncMock, MagicMock, patch +from mock import AsyncMock, patch from pyzeebe.errors import ( InvalidJSONError, @@ -18,6 +18,17 @@ from tests.unit.utils.random_utils import RANDOM_RANGE +@pytest.fixture() +def mocked_aiofiles_open(): + read_mock = AsyncMock(return_value=bytes()) + + file_mock = AsyncMock() + file_mock.__aenter__.return_value.read = read_mock + + with patch("pyzeebe.grpc_internals.zeebe_process_adapter.aiofiles.open", return_value=file_mock) as open_mock: + yield open_mock + + @pytest.mark.asyncio class TestCreateProcessInstance: async def test_response_is_of_correct_type(self, zeebe_adapter: ZeebeProcessAdapter, grpc_servicer: GatewayMock): @@ -138,19 +149,6 @@ async def test_raises_on_already_cancelled_process( @pytest.mark.asyncio class TestDeployProcess: - open_mock: MagicMock - - @pytest.fixture(autouse=True) - def mocked_aiofiles_open(self): - read_mock = AsyncMock(return_value=bytes()) - - file_mock = AsyncMock() - file_mock.__aenter__.return_value.read = read_mock - - with patch("pyzeebe.grpc_internals.zeebe_process_adapter.aiofiles.open", return_value=file_mock) as open_mock: - self.open_mock = open_mock - yield - async def test_raises_on_invalid_process(self, zeebe_adapter: ZeebeProcessAdapter): error = grpc.aio.AioRpcError(grpc.StatusCode.INVALID_ARGUMENT, None, None) @@ -159,29 +157,16 @@ async def test_raises_on_invalid_process(self, zeebe_adapter: ZeebeProcessAdapte with pytest.raises(ProcessInvalidError): await zeebe_adapter.deploy_process() - async def test_calls_open_in_rb_mode(self, zeebe_adapter: ZeebeProcessAdapter): + async def test_calls_open_in_rb_mode(self, zeebe_adapter: ZeebeProcessAdapter, mocked_aiofiles_open): file_path = str(uuid4()) await zeebe_adapter.deploy_process(file_path) - self.open_mock.assert_called_with(file_path, "rb") + mocked_aiofiles_open.assert_called_with(file_path, "rb") @pytest.mark.asyncio class TestDeployResource: - open_mock: MagicMock - - @pytest.fixture(autouse=True) - def mocked_aiofiles_open(self): - read_mock = AsyncMock(return_value=bytes()) - - file_mock = AsyncMock() - file_mock.__aenter__.return_value.read = read_mock - - with patch("pyzeebe.grpc_internals.zeebe_process_adapter.aiofiles.open", return_value=file_mock) as open_mock: - self.open_mock = open_mock - yield - async def test_raises_on_invalid_process(self, zeebe_adapter: ZeebeProcessAdapter): error = grpc.aio.AioRpcError(grpc.StatusCode.INVALID_ARGUMENT, None, None) @@ -190,9 +175,9 @@ async def test_raises_on_invalid_process(self, zeebe_adapter: ZeebeProcessAdapte with pytest.raises(ProcessInvalidError): await zeebe_adapter.deploy_resource() - async def test_calls_open_in_rb_mode(self, zeebe_adapter: ZeebeProcessAdapter): + async def test_calls_open_in_rb_mode(self, zeebe_adapter: ZeebeProcessAdapter, mocked_aiofiles_open): file_path = str(uuid4()) await zeebe_adapter.deploy_resource(file_path) - self.open_mock.assert_called_with(file_path, "rb") + mocked_aiofiles_open.assert_called_with(file_path, "rb") From 59b76f3bf047fb01407d7652152fe3c5f2db7987 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Thu, 22 Feb 2024 11:14:26 +0600 Subject: [PATCH 5/6] tests: make more readable --- .../grpc_internals/zeebe_job_adapter_test.py | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/tests/unit/grpc_internals/zeebe_job_adapter_test.py b/tests/unit/grpc_internals/zeebe_job_adapter_test.py index 799d53ae..604cd320 100644 --- a/tests/unit/grpc_internals/zeebe_job_adapter_test.py +++ b/tests/unit/grpc_internals/zeebe_job_adapter_test.py @@ -1,4 +1,5 @@ from random import randint +from typing import Dict from uuid import uuid4 import pytest @@ -28,6 +29,14 @@ def random_message() -> str: return str(uuid4()) +def random_variables() -> Dict: + return {str(uuid4()): str(uuid4())} + + +def random_retry_back_off_ms() -> int: + return randint(0, RANDOM_RANGE) + + @pytest.mark.asyncio class TestActivateJobs: zeebe_job_adapter: ZeebeJobAdapter @@ -102,34 +111,54 @@ async def test_raises_on_already_completed_job(self, zeebe_adapter: ZeebeJobAdap @pytest.mark.asyncio class TestFailJob: async def test_response_is_of_correct_type(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - response = await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message(), 0, {}) + response = await zeebe_adapter.fail_job( + first_active_job.key, + first_active_job.retries, + random_message(), + random_retry_back_off_ms(), + random_variables(), + ) assert isinstance(response, FailJobResponse) async def test_raises_on_fake_job(self, zeebe_adapter: ZeebeJobAdapter): with pytest.raises(JobNotFoundError): - await zeebe_adapter.fail_job(random_job_key(), 1, random_message(), 0, {}) + await zeebe_adapter.fail_job( + random_job_key(), 1, random_message(), random_retry_back_off_ms(), random_variables() + ) async def test_raises_on_deactivated_job(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message(), 0, {}) + await zeebe_adapter.fail_job( + first_active_job.key, + first_active_job.retries, + random_message(), + random_retry_back_off_ms(), + random_variables(), + ) with pytest.raises(JobAlreadyDeactivatedError): - await zeebe_adapter.fail_job(first_active_job.key, first_active_job.retries, random_message(), 0, {}) + await zeebe_adapter.fail_job( + first_active_job.key, + first_active_job.retries, + random_message(), + random_retry_back_off_ms(), + random_variables(), + ) @pytest.mark.asyncio class TestThrowError: async def test_response_is_of_correct_type(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - response = await zeebe_adapter.throw_error(first_active_job.key, random_message(), {}) + response = await zeebe_adapter.throw_error(first_active_job.key, random_message(), random_variables()) assert isinstance(response, ThrowErrorResponse) async def test_raises_on_fake_job(self, zeebe_adapter: ZeebeJobAdapter): with pytest.raises(JobNotFoundError): - await zeebe_adapter.throw_error(random_job_key(), random_message(), {}) + await zeebe_adapter.throw_error(random_job_key(), random_message(), random_variables()) async def test_raises_on_deactivated_job(self, zeebe_adapter: ZeebeJobAdapter, first_active_job: Job): - await zeebe_adapter.throw_error(first_active_job.key, random_message(), {}) + await zeebe_adapter.throw_error(first_active_job.key, random_message(), random_variables()) with pytest.raises(JobAlreadyDeactivatedError): - await zeebe_adapter.throw_error(first_active_job.key, random_message(), {}) + await zeebe_adapter.throw_error(first_active_job.key, random_message(), random_variables()) From 5294412bdb749a2afc2f488975224361e2c91404 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Sat, 24 Feb 2024 16:25:31 +0600 Subject: [PATCH 6/6] tests: autouse=True for mocked_aiofiles_open Co-authored-by: Jonatan Martens <40060128+JonatanMartens@users.noreply.github.com> --- tests/unit/grpc_internals/zeebe_process_adapter_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/grpc_internals/zeebe_process_adapter_test.py b/tests/unit/grpc_internals/zeebe_process_adapter_test.py index 659c90cb..08676be6 100644 --- a/tests/unit/grpc_internals/zeebe_process_adapter_test.py +++ b/tests/unit/grpc_internals/zeebe_process_adapter_test.py @@ -18,7 +18,7 @@ from tests.unit.utils.random_utils import RANDOM_RANGE -@pytest.fixture() +@pytest.fixture(autouse=True) def mocked_aiofiles_open(): read_mock = AsyncMock(return_value=bytes())