operaton-external-task-client-python3 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. examples/__init__.py +0 -0
  2. examples/bpmn_error_example.py +75 -0
  3. examples/correlate_message.py +11 -0
  4. examples/event_subprocess_example.py +50 -0
  5. examples/examples_auth_basic/__init__.py +0 -0
  6. examples/examples_auth_basic/fetch_and_execute.py +31 -0
  7. examples/examples_auth_basic/get_process_instance.py +12 -0
  8. examples/examples_auth_basic/start_process.py +15 -0
  9. examples/examples_auth_basic/task_handler_example.py +44 -0
  10. examples/fetch_and_execute.py +30 -0
  11. examples/get_process_instance.py +12 -0
  12. examples/retry_task_example.py +58 -0
  13. examples/start_process.py +14 -0
  14. examples/task_handler_example.py +44 -0
  15. examples/tasks_example.py +36 -0
  16. operaton/__init__.py +0 -0
  17. operaton/client/__init__.py +0 -0
  18. operaton/client/async_external_task_client.py +171 -0
  19. operaton/client/engine_client.py +180 -0
  20. operaton/client/external_task_client.py +166 -0
  21. operaton/client/tests/__init__.py +0 -0
  22. operaton/client/tests/test_async_external_task_client.py +128 -0
  23. operaton/client/tests/test_async_external_task_client_auth.py +42 -0
  24. operaton/client/tests/test_async_external_task_client_bearer.py +43 -0
  25. operaton/client/tests/test_engine_client.py +228 -0
  26. operaton/client/tests/test_engine_client_auth.py +231 -0
  27. operaton/client/tests/test_engine_client_bearer.py +237 -0
  28. operaton/client/tests/test_external_task_client.py +17 -0
  29. operaton/client/tests/test_external_task_client_auth.py +19 -0
  30. operaton/client/tests/test_external_task_client_bearer.py +24 -0
  31. operaton/external_task/__init__.py +0 -0
  32. operaton/external_task/async_external_task_executor.py +91 -0
  33. operaton/external_task/async_external_task_worker.py +181 -0
  34. operaton/external_task/external_task.py +173 -0
  35. operaton/external_task/external_task_executor.py +88 -0
  36. operaton/external_task/external_task_worker.py +92 -0
  37. operaton/external_task/tests/__init__.py +0 -0
  38. operaton/external_task/tests/test_async_external_task_executor.py +139 -0
  39. operaton/external_task/tests/test_async_external_task_worker.py +129 -0
  40. operaton/external_task/tests/test_external_task.py +106 -0
  41. operaton/external_task/tests/test_external_task_executor.py +200 -0
  42. operaton/external_task/tests/test_external_task_worker.py +147 -0
  43. operaton/process_definition/__init__.py +0 -0
  44. operaton/process_definition/process_definition_client.py +123 -0
  45. operaton/process_definition/tests/__init__.py +0 -0
  46. operaton/process_definition/tests/test_process_definition_client.py +181 -0
  47. operaton/utils/__init__.py +0 -0
  48. operaton/utils/auth_basic.py +28 -0
  49. operaton/utils/auth_bearer.py +28 -0
  50. operaton/utils/log_utils.py +31 -0
  51. operaton/utils/response_utils.py +35 -0
  52. operaton/utils/tests/test_auth_basic.py +30 -0
  53. operaton/utils/tests/test_auth_bearer.py +27 -0
  54. operaton/utils/tests/test_response_utils.py +43 -0
  55. operaton/utils/tests/test_utils.py +21 -0
  56. operaton/utils/utils.py +14 -0
  57. operaton/variables/__init__.py +0 -0
  58. operaton/variables/properties.py +27 -0
  59. operaton/variables/tests/test_properties.py +20 -0
  60. operaton/variables/tests/test_variables.py +60 -0
  61. operaton/variables/variables.py +45 -0
  62. operaton_external_task_client_python3-1.0.0.dist-info/METADATA +258 -0
  63. operaton_external_task_client_python3-1.0.0.dist-info/RECORD +66 -0
  64. operaton_external_task_client_python3-1.0.0.dist-info/WHEEL +5 -0
  65. operaton_external_task_client_python3-1.0.0.dist-info/licenses/LICENSE +201 -0
  66. operaton_external_task_client_python3-1.0.0.dist-info/top_level.txt +2 -0
examples/__init__.py ADDED
File without changes
@@ -0,0 +1,75 @@
1
+ import logging
2
+ from concurrent.futures.thread import ThreadPoolExecutor
3
+
4
+ from operaton.external_task.external_task import ExternalTask
5
+ from operaton.external_task.external_task_worker import ExternalTaskWorker
6
+ from operaton.utils.log_utils import log_with_context
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ default_config = {
11
+ "maxTasks": 1,
12
+ "lockDuration": 10000,
13
+ "asyncResponseTimeout": 30000,
14
+ "retries": 3,
15
+ "retryTimeout": 5000,
16
+ "sleepSeconds": 30,
17
+ "isDebug": True,
18
+ }
19
+
20
+
21
+ def validate_image(task: ExternalTask):
22
+ """
23
+ To simulate BPMN/Failure/Success, this handler uses image name variable (to be passed when launching the process)
24
+ """
25
+ log_context = {"WORKER_ID": task.get_worker_id(),
26
+ "TASK_ID": task.get_task_id(),
27
+ "TOPIC": task.get_topic_name()}
28
+
29
+ log_with_context("executing validate_image", log_context)
30
+ img_name = task.get_variable('imgName')
31
+
32
+ if "poor" in img_name:
33
+ return task.bpmn_error("POOR_QUALITY_IMAGE", "Image quality is bad",
34
+ {"img_rejection_code": "POOR_QUALITY_CODE_XX",
35
+ "img_rejection_reason": f"Image quality must be at least GOOD"})
36
+ elif "jpg" in img_name:
37
+ return task.complete({"img_approved": True})
38
+ elif "corrupt" in img_name:
39
+ return task.failure("Cannot validate image", "image is corrupted", 0, default_config.get("retryTimeout"))
40
+ else:
41
+ return task.bpmn_error("INVALID_IMAGE", "Image extension must be jpg",
42
+ {"img_rejection_code": "INVALID_IMG_NAME",
43
+ "img_rejection_reason": f"Image name {img_name} is invalid"})
44
+
45
+
46
+ def generic_task_handler(task: ExternalTask):
47
+ log_context = {"WORKER_ID": task.get_worker_id(),
48
+ "TASK_ID": task.get_task_id(),
49
+ "TOPIC": task.get_topic_name()}
50
+
51
+ log_with_context("executing generic task handler", log_context)
52
+ return task.complete()
53
+
54
+
55
+ def main():
56
+ configure_logging()
57
+ topics = [("VALIDATE_IMAGE", validate_image),
58
+ # ("APPROVE_IMAGE", generic_task_handler),
59
+ # ("REJECT_IMAGE", generic_task_handler),
60
+ # ("ENHANCE_IMAGE_QUALITY", generic_task_handler),
61
+ ]
62
+ executor = ThreadPoolExecutor(max_workers=len(topics))
63
+ for index, topic_handler in enumerate(topics):
64
+ topic = topic_handler[0]
65
+ handler_func = topic_handler[1]
66
+ executor.submit(ExternalTaskWorker(worker_id=index, config=default_config).subscribe, topic, handler_func)
67
+
68
+
69
+ def configure_logging():
70
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s",
71
+ handlers=[logging.StreamHandler()])
72
+
73
+
74
+ if __name__ == '__main__':
75
+ main()
@@ -0,0 +1,11 @@
1
+ from operaton.client.engine_client import EngineClient
2
+
3
+
4
+ def main():
5
+ client = EngineClient()
6
+ resp_json = client.correlate_message("CANCEL_MESSAGE", business_key="b4a6f392-12ab-11eb-80ef-acde48001122")
7
+ print(resp_json)
8
+
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,50 @@
1
+ import logging
2
+ from concurrent.futures.thread import ThreadPoolExecutor
3
+
4
+ from operaton.external_task.external_task import ExternalTask
5
+ from operaton.external_task.external_task_worker import ExternalTaskWorker
6
+ from operaton.utils.log_utils import log_with_context
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ default_config = {
11
+ "maxTasks": 1,
12
+ "lockDuration": 10000,
13
+ "asyncResponseTimeout": 30000,
14
+ "retries": 3,
15
+ "retryTimeout": 5000,
16
+ "sleepSeconds": 30,
17
+ "isDebug": True,
18
+ }
19
+
20
+
21
+ def generic_task_handler(task: ExternalTask):
22
+ log_context = {"WORKER_ID": task.get_worker_id(),
23
+ "TASK_ID": task.get_task_id(),
24
+ "TOPIC": task.get_topic_name()}
25
+
26
+ log_with_context("executing generic task handler", log_context)
27
+ return task.complete()
28
+
29
+
30
+ def main():
31
+ configure_logging()
32
+ topics = [
33
+ ("STEP_1", generic_task_handler),
34
+ # ("STEP_2", generic_task_handler),
35
+ # ("CLEAN_UP", generic_task_handler),
36
+ ]
37
+ executor = ThreadPoolExecutor(max_workers=len(topics))
38
+ for index, topic_handler in enumerate(topics):
39
+ topic = topic_handler[0]
40
+ handler_func = topic_handler[1]
41
+ executor.submit(ExternalTaskWorker(worker_id=index, config=default_config).subscribe, topic, handler_func)
42
+
43
+
44
+ def configure_logging():
45
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s",
46
+ handlers=[logging.StreamHandler()])
47
+
48
+
49
+ if __name__ == '__main__':
50
+ main()
File without changes
@@ -0,0 +1,31 @@
1
+ import logging
2
+
3
+ from operaton.external_task.external_task_worker import ExternalTaskWorker
4
+ from task_handler_example import handle_task
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ default_config = {
9
+ "auth_basic": {"username": "demo", "password": "demo"},
10
+ "maxTasks": 1,
11
+ "lockDuration": 10000,
12
+ "asyncResponseTimeout": 0,
13
+ "isDebug": True,
14
+ }
15
+
16
+
17
+ def main():
18
+ configure_logging()
19
+ topics = ["PARALLEL_STEP_1", "PARALLEL_STEP_2", "COMBINE_STEP"]
20
+ for index, topic in enumerate(topics):
21
+ ExternalTaskWorker(worker_id=index, config=default_config) \
22
+ .fetch_and_execute(topic_names=topic, action=handle_task, process_variables={"strVar": "hello"})
23
+
24
+
25
+ def configure_logging():
26
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s",
27
+ handlers=[logging.StreamHandler()])
28
+
29
+
30
+ if __name__ == '__main__':
31
+ main()
@@ -0,0 +1,12 @@
1
+ from operaton.client.engine_client import EngineClient
2
+
3
+
4
+ def main():
5
+ client = EngineClient(config={"auth_basic": {"username": "demo", "password": "demo"}})
6
+ resp_json = client.get_process_instance("PARALLEL_STEPS_EXAMPLE", ["intVar_eq_1", "strVar_eq_hello"],
7
+ ["6172cdf0-7b32-4460-9da0-ded5107aa977"])
8
+ print(resp_json)
9
+
10
+
11
+ if __name__ == '__main__':
12
+ main()
@@ -0,0 +1,15 @@
1
+ import uuid
2
+
3
+ from operaton.client.engine_client import EngineClient
4
+
5
+
6
+ def main():
7
+ client = EngineClient(config={"auth_basic": {"username": "demo", "password": "demo"}})
8
+ resp_json = client.start_process(
9
+ process_key="PARALLEL_STEPS_EXAMPLE", variables={"intVar": "1", "strVar": "hello"},
10
+ tenant_id="6172cdf0-7b32-4460-9da0-ded5107aa977", business_key=str(uuid.uuid1()))
11
+ print(resp_json)
12
+
13
+
14
+ if __name__ == '__main__':
15
+ main()
@@ -0,0 +1,44 @@
1
+ from datetime import datetime
2
+ from random import randint
3
+
4
+ import time
5
+
6
+ from operaton.external_task.external_task import ExternalTask
7
+ from operaton.utils.log_utils import log_with_context
8
+
9
+
10
+ def handle_task(task: ExternalTask):
11
+ log_context = {"WORKER_ID": task.get_worker_id(),
12
+ "TASK_ID": task.get_task_id(),
13
+ "TOPIC": task.get_topic_name()}
14
+
15
+ log_with_context(f"handle_task started: business key = {task.get_business_key()}", log_context)
16
+
17
+ # simulate task execution
18
+ execution_time = randint(0, 10)
19
+ log_with_context(f"handle_task - business logic execution started for task: "
20
+ f"it will execute for {execution_time} seconds", log_context)
21
+ time.sleep(execution_time)
22
+
23
+ # simulate that task results randomly into failure/BPMN error/complete
24
+ failure = random_true()
25
+ bpmn_error = False if failure else random_true()
26
+ # override the values to simulate success/failure/BPMN error explicitly (if needed)
27
+ failure, bpmn_error = False, False
28
+ log_with_context(f"handle_task - business logic executed: failure: {failure}, bpmn_error: {bpmn_error}",
29
+ log_context)
30
+
31
+ return __handle_task_result(task, failure, bpmn_error)
32
+
33
+
34
+ def __handle_task_result(task, failure, bpmn_error):
35
+ if failure:
36
+ return task.failure("task failed", "failed task details", 3, 5000)
37
+ elif bpmn_error:
38
+ return task.bpmn_error("BPMN_ERROR_CODE")
39
+ return task.complete({"success": True, "task_completed_on": str(datetime.now())})
40
+
41
+
42
+ def random_true():
43
+ current_milli_time = int(round(time.time() * 1000))
44
+ return current_milli_time % 2 == 0
@@ -0,0 +1,30 @@
1
+ import logging
2
+
3
+ from operaton.external_task.external_task_worker import ExternalTaskWorker
4
+ from examples.task_handler_example import handle_task
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ default_config = {
9
+ "maxTasks": 1,
10
+ "lockDuration": 10000,
11
+ "asyncResponseTimeout": 0,
12
+ "isDebug": True,
13
+ }
14
+
15
+
16
+ def main():
17
+ configure_logging()
18
+ topics = ["PARALLEL_STEP_1", "PARALLEL_STEP_2", "COMBINE_STEP"]
19
+ for index, topic in enumerate(topics):
20
+ ExternalTaskWorker(worker_id=index, config=default_config) \
21
+ .fetch_and_execute(topic_names=topic, action=handle_task, process_variables={"strVar": "hello"})
22
+
23
+
24
+ def configure_logging():
25
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s",
26
+ handlers=[logging.StreamHandler()])
27
+
28
+
29
+ if __name__ == '__main__':
30
+ main()
@@ -0,0 +1,12 @@
1
+ from operaton.client.engine_client import EngineClient
2
+
3
+
4
+ def main():
5
+ client = EngineClient()
6
+ resp_json = client.get_process_instance("PARALLEL_STEPS_EXAMPLE", ["intVar_eq_1", "strVar_eq_hello"],
7
+ ["6172cdf0-7b32-4460-9da0-ded5107aa977"])
8
+ print(resp_json)
9
+
10
+
11
+ if __name__ == '__main__':
12
+ main()
@@ -0,0 +1,58 @@
1
+ import logging
2
+ from concurrent.futures.thread import ThreadPoolExecutor
3
+
4
+ from operaton.external_task.external_task import ExternalTask
5
+ from operaton.external_task.external_task_worker import ExternalTaskWorker
6
+ from operaton.utils.log_utils import log_with_context
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ default_config = {
11
+ "maxTasks": 1,
12
+ "lockDuration": 10000,
13
+ "asyncResponseTimeout": 30000,
14
+ "retries": 3,
15
+ "retryTimeout": 5000,
16
+ "sleepSeconds": 30,
17
+ "isDebug": True,
18
+ }
19
+
20
+
21
+ def generic_task_handler(task: ExternalTask):
22
+ log_context = {"WORKER_ID": task.get_worker_id(),
23
+ "TASK_ID": task.get_task_id(),
24
+ "TOPIC": task.get_topic_name()}
25
+
26
+ log_with_context("executing generic task handler", log_context)
27
+ return task.complete()
28
+
29
+
30
+ def fail_task_handler(task: ExternalTask):
31
+ log_context = {"WORKER_ID": task.get_worker_id(),
32
+ "TASK_ID": task.get_task_id(),
33
+ "TOPIC": task.get_topic_name()}
34
+
35
+ log_with_context("executing fail_task_handler", log_context)
36
+ return task.failure("task failed", "task failed forced", 0, 10)
37
+
38
+
39
+ def main():
40
+ configure_logging()
41
+ topics = [
42
+ ("TASK_1", fail_task_handler),
43
+ ("TASK_2", fail_task_handler),
44
+ ]
45
+ executor = ThreadPoolExecutor(max_workers=len(topics))
46
+ for index, topic_handler in enumerate(topics):
47
+ topic = topic_handler[0]
48
+ handler_func = topic_handler[1]
49
+ executor.submit(ExternalTaskWorker(worker_id=index, config=default_config).subscribe, topic, handler_func)
50
+
51
+
52
+ def configure_logging():
53
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s",
54
+ handlers=[logging.StreamHandler()])
55
+
56
+
57
+ if __name__ == '__main__':
58
+ main()
@@ -0,0 +1,14 @@
1
+ import uuid
2
+
3
+ from operaton.client.engine_client import EngineClient
4
+
5
+
6
+ def main():
7
+ client = EngineClient()
8
+ resp_json = client.start_process(process_key="PARALLEL_STEPS_EXAMPLE", variables={"intVar": "1", "strVar": "hello"},
9
+ tenant_id="6172cdf0-7b32-4460-9da0-ded5107aa977", business_key=str(uuid.uuid1()))
10
+ print(resp_json)
11
+
12
+
13
+ if __name__ == '__main__':
14
+ main()
@@ -0,0 +1,44 @@
1
+ from datetime import datetime
2
+ from random import randint
3
+
4
+ import time
5
+
6
+ from operaton.external_task.external_task import ExternalTask
7
+ from operaton.utils.log_utils import log_with_context
8
+
9
+
10
+ def handle_task(task: ExternalTask):
11
+ log_context = {"WORKER_ID": task.get_worker_id(),
12
+ "TASK_ID": task.get_task_id(),
13
+ "TOPIC": task.get_topic_name()}
14
+
15
+ log_with_context(f"handle_task started: business key = {task.get_business_key()}", log_context)
16
+
17
+ # simulate task execution
18
+ execution_time = randint(0, 10)
19
+ log_with_context(f"handle_task - business logic execution started for task: "
20
+ f"it will execute for {execution_time} seconds", log_context)
21
+ time.sleep(execution_time)
22
+
23
+ # simulate that task results randomly into failure/BPMN error/complete
24
+ failure = random_true()
25
+ bpmn_error = False if failure else random_true()
26
+ # override the values to simulate success/failure/BPMN error explicitly (if needed)
27
+ failure, bpmn_error = False, False
28
+ log_with_context(f"handle_task - business logic executed: failure: {failure}, bpmn_error: {bpmn_error}",
29
+ log_context)
30
+
31
+ return __handle_task_result(task, failure, bpmn_error)
32
+
33
+
34
+ def __handle_task_result(task, failure, bpmn_error):
35
+ if failure:
36
+ return task.failure("task failed", "failed task details", 3, 5000)
37
+ elif bpmn_error:
38
+ return task.bpmn_error("BPMN_ERROR_CODE")
39
+ return task.complete({"success": True, "task_completed_on": str(datetime.now())})
40
+
41
+
42
+ def random_true():
43
+ current_milli_time = int(round(time.time() * 1000))
44
+ return current_milli_time % 2 == 0
@@ -0,0 +1,36 @@
1
+ import logging
2
+ from concurrent.futures.thread import ThreadPoolExecutor
3
+
4
+ from operaton.external_task.external_task_worker import ExternalTaskWorker
5
+ from examples.task_handler_example import handle_task
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ default_config = {
10
+ "maxTasks": 1,
11
+ "lockDuration": 10000,
12
+ "asyncResponseTimeout": 3000,
13
+ "retries": 3,
14
+ "retryTimeout": 5000,
15
+ "sleepSeconds": 30,
16
+ "isDebug": True,
17
+ "httpTimeoutMillis": 3000,
18
+ }
19
+
20
+
21
+ def main():
22
+ configure_logging()
23
+ topics = ["PARALLEL_STEP_1", "PARALLEL_STEP_2", "COMBINE_STEP"]
24
+ executor = ThreadPoolExecutor(max_workers=len(topics))
25
+ for index, topic in enumerate(topics):
26
+ executor.submit(ExternalTaskWorker(worker_id=index, config=default_config).subscribe, topic, handle_task,
27
+ {"strVar": "hello"})
28
+
29
+
30
+ def configure_logging():
31
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s",
32
+ handlers=[logging.StreamHandler()])
33
+
34
+
35
+ if __name__ == '__main__':
36
+ main()
operaton/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,171 @@
1
+ import logging
2
+ from http import HTTPStatus
3
+
4
+ import httpx
5
+
6
+ from operaton.client.engine_client import ENGINE_LOCAL_BASE_URL
7
+ from operaton.utils.log_utils import log_with_context
8
+ from operaton.utils.response_utils import raise_exception_if_not_ok
9
+ from operaton.utils.utils import str_to_list
10
+ from operaton.utils.auth_basic import AuthBasic, obfuscate_password
11
+ from operaton.utils.auth_bearer import AuthBearer
12
+ from operaton.variables.variables import Variables
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class AsyncExternalTaskClient:
18
+ default_config = {
19
+ "maxConcurrentTasks": 10, # Number of concurrent tasks you can process
20
+ "lockDuration": 300000, # in milliseconds
21
+ "asyncResponseTimeout": 30000,
22
+ "retries": 3,
23
+ "retryTimeout": 300000,
24
+ "httpTimeoutMillis": 30000,
25
+ "timeoutDeltaMillis": 5000,
26
+ "includeExtensionProperties": True, # enables Camunda Extension Properties
27
+ "deserializeValues": True, # deserialize values when fetch a task by default
28
+ "usePriority": False,
29
+ "sorting": None
30
+ }
31
+
32
+ def __init__(self, worker_id, engine_base_url=ENGINE_LOCAL_BASE_URL, config=None):
33
+ config = config if config is not None else {}
34
+ self.worker_id = worker_id
35
+ self.external_task_base_url = engine_base_url + "/external-task"
36
+ self.config = type(self).default_config.copy()
37
+ self.config.update(config)
38
+ self.is_debug = config.get('isDebug', False)
39
+ self.http_timeout_seconds = self.config.get('httpTimeoutMillis') / 1000
40
+ self._log_with_context(f"Created External Task client with config: {obfuscate_password(self.config)}")
41
+
42
+ def get_fetch_and_lock_url(self):
43
+ return f"{self.external_task_base_url}/fetchAndLock"
44
+
45
+ async def fetch_and_lock(self, topic_names, process_variables=None, variables=None):
46
+ url = self.get_fetch_and_lock_url()
47
+ body = {
48
+ "workerId": str(self.worker_id), # convert to string to make it JSON serializable
49
+ "maxTasks": 1,
50
+ "topics": self._get_topics(topic_names, process_variables, variables),
51
+ "asyncResponseTimeout": self.config["asyncResponseTimeout"],
52
+ "usePriority": self.config["usePriority"],
53
+ "sorting": self.config["sorting"]
54
+ }
55
+
56
+ if self.is_debug:
57
+ self._log_with_context(f"Trying to fetch and lock with request payload: {body}")
58
+ http_timeout_seconds = self.__get_fetch_and_lock_http_timeout_seconds()
59
+
60
+ async with httpx.AsyncClient() as client:
61
+ response = await client.post(url, headers=self._get_headers(), json=body, timeout=http_timeout_seconds)
62
+ raise_exception_if_not_ok(response)
63
+
64
+ resp_json = response.json()
65
+ if self.is_debug:
66
+ self._log_with_context(f"Fetch and lock response JSON: {resp_json} for request: {body}")
67
+ return resp_json
68
+
69
+ def __get_fetch_and_lock_http_timeout_seconds(self):
70
+ # Use HTTP timeout slightly more than async response / long polling timeout
71
+ return (self.config["timeoutDeltaMillis"] + self.config["asyncResponseTimeout"]) / 1000
72
+
73
+ def _get_topics(self, topic_names, process_variables, variables):
74
+ topics = []
75
+ for topic in str_to_list(topic_names):
76
+ topics.append({
77
+ "topicName": topic,
78
+ "lockDuration": self.config["lockDuration"],
79
+ "processVariables": process_variables if process_variables else {},
80
+ # Enables Camunda Extension Properties
81
+ "includeExtensionProperties": self.config.get("includeExtensionProperties") or False,
82
+ "deserializeValues": self.config["deserializeValues"],
83
+ "variables": variables
84
+ })
85
+ return topics
86
+
87
+ async def complete(self, task_id, global_variables, local_variables=None):
88
+ url = self.get_task_complete_url(task_id)
89
+
90
+ body = {
91
+ "workerId": self.worker_id,
92
+ "variables": Variables.format(global_variables),
93
+ "localVariables": Variables.format(local_variables)
94
+ }
95
+
96
+ async with httpx.AsyncClient() as client:
97
+ response = await client.post(url, headers=self._get_headers(), json=body, timeout=self.http_timeout_seconds)
98
+ raise_exception_if_not_ok(response)
99
+ return response.status_code == HTTPStatus.NO_CONTENT
100
+
101
+ def get_task_complete_url(self, task_id):
102
+ return f"{self.external_task_base_url}/{task_id}/complete"
103
+
104
+ async def failure(self, task_id, error_message, error_details, retries, retry_timeout):
105
+ url = self.get_task_failure_url(task_id)
106
+ logger.info(f"Setting retries to: {retries} for task: {task_id}")
107
+ body = {
108
+ "workerId": self.worker_id,
109
+ "errorMessage": error_message,
110
+ "retries": retries,
111
+ "retryTimeout": retry_timeout,
112
+ }
113
+ if error_details:
114
+ body["errorDetails"] = error_details
115
+
116
+ async with httpx.AsyncClient() as client:
117
+ response = await client.post(url, headers=self._get_headers(), json=body, timeout=self.http_timeout_seconds)
118
+ raise_exception_if_not_ok(response)
119
+ return response.status_code == HTTPStatus.NO_CONTENT
120
+
121
+ def get_task_failure_url(self, task_id):
122
+ return f"{self.external_task_base_url}/{task_id}/failure"
123
+
124
+ async def bpmn_failure(self, task_id, error_code, error_message, variables=None):
125
+ url = self.get_task_bpmn_error_url(task_id)
126
+
127
+ body = {
128
+ "workerId": self.worker_id,
129
+ "errorCode": error_code,
130
+ "errorMessage": error_message,
131
+ "variables": Variables.format(variables),
132
+ }
133
+
134
+ if self.is_debug:
135
+ self._log_with_context(f"Trying to report BPMN error with request payload: {body}")
136
+
137
+ async with httpx.AsyncClient() as client:
138
+ response = await client.post(url, headers=self._get_headers(), json=body, timeout=self.http_timeout_seconds)
139
+ response.raise_for_status()
140
+ return response.status_code == HTTPStatus.NO_CONTENT
141
+
142
+ def get_task_bpmn_error_url(self, task_id):
143
+ return f"{self.external_task_base_url}/{task_id}/bpmnError"
144
+
145
+ @property
146
+ def auth_basic(self) -> dict:
147
+ if not self.config.get("auth_basic") or not isinstance(self.config.get("auth_basic"), dict):
148
+ return {}
149
+ token = AuthBasic(**self.config.get("auth_basic").copy()).token
150
+ return {"Authorization": token}
151
+
152
+ @property
153
+ def auth_bearer(self) -> dict:
154
+ if not self.config.get("auth_bearer") or not isinstance(self.config.get("auth_bearer"), dict):
155
+ return {}
156
+ token = AuthBearer(access_token=self.config["auth_bearer"]).access_token
157
+ return {"Authorization": token}
158
+
159
+ def _get_headers(self):
160
+ headers = {
161
+ "Content-Type": "application/json"
162
+ }
163
+ if self.auth_basic:
164
+ headers.update(self.auth_basic)
165
+ if self.auth_bearer:
166
+ headers.update(self.auth_bearer)
167
+ return headers
168
+
169
+ def _log_with_context(self, msg, log_level='info', **kwargs):
170
+ context = {"WORKER_ID": self.worker_id}
171
+ log_with_context(msg, context=context, log_level=log_level, **kwargs)