processcube-etw-library 2026.1.22.145211__py3-none-any.whl → 2026.1.29.103633__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 (52) hide show
  1. processcube_etw_library/__init__.py +7 -5
  2. processcube_etw_library/create_external_task_client.py +4 -2
  3. processcube_etw_library/etw_app.py +18 -5
  4. processcube_etw_library/health/built_in.py +3 -4
  5. processcube_etw_library/health/check.py +14 -4
  6. processcube_etw_library/identity_provider.py +0 -2
  7. processcube_etw_library/processcube_client/__init__.py +22 -0
  8. processcube_etw_library/processcube_client/app_info/__init__.py +1 -0
  9. processcube_etw_library/processcube_client/app_info/app_info_client.py +36 -0
  10. processcube_etw_library/processcube_client/client_factory.py +37 -0
  11. processcube_etw_library/processcube_client/core/__init__.py +2 -0
  12. processcube_etw_library/processcube_client/core/api/__init__.py +13 -0
  13. processcube_etw_library/processcube_client/core/api/base_client.py +235 -0
  14. processcube_etw_library/processcube_client/core/api/client.py +816 -0
  15. processcube_etw_library/processcube_client/core/api/helpers/__init__.py +0 -0
  16. processcube_etw_library/processcube_client/core/api/helpers/application_info.py +34 -0
  17. processcube_etw_library/processcube_client/core/api/helpers/data_object_instances.py +61 -0
  18. processcube_etw_library/processcube_client/core/api/helpers/empty_tasks.py +86 -0
  19. processcube_etw_library/processcube_client/core/api/helpers/events.py +39 -0
  20. processcube_etw_library/processcube_client/core/api/helpers/external_tasks.py +142 -0
  21. processcube_etw_library/processcube_client/core/api/helpers/flow_node_instances.py +80 -0
  22. processcube_etw_library/processcube_client/core/api/helpers/manual_tasks.py +87 -0
  23. processcube_etw_library/processcube_client/core/api/helpers/process_definitions.py +46 -0
  24. processcube_etw_library/processcube_client/core/api/helpers/process_instances.py +96 -0
  25. processcube_etw_library/processcube_client/core/api/helpers/process_models.py +51 -0
  26. processcube_etw_library/processcube_client/core/api/helpers/user_tasks.py +130 -0
  27. processcube_etw_library/processcube_client/core/base_client.py +175 -0
  28. processcube_etw_library/processcube_client/core/loop_helper.py +200 -0
  29. processcube_etw_library/processcube_client/event/__init__.py +1 -0
  30. processcube_etw_library/processcube_client/event/event_client.py +43 -0
  31. processcube_etw_library/processcube_client/external_task/__init__.py +3 -0
  32. processcube_etw_library/processcube_client/external_task/client_wrapper.py +28 -0
  33. processcube_etw_library/processcube_client/external_task/external_task_client.py +195 -0
  34. processcube_etw_library/processcube_client/external_task/external_task_worker.py +205 -0
  35. processcube_etw_library/processcube_client/external_task/functional_error.py +17 -0
  36. processcube_etw_library/processcube_client/flow_node_instance/__init__.py +1 -0
  37. processcube_etw_library/processcube_client/flow_node_instance/flow_node_instance_client.py +43 -0
  38. processcube_etw_library/processcube_client/notification/__init__.py +1 -0
  39. processcube_etw_library/processcube_client/notification/notification_client.py +103 -0
  40. processcube_etw_library/processcube_client/process_definition/__init__.py +2 -0
  41. processcube_etw_library/processcube_client/process_definition/process_definition_client.py +94 -0
  42. processcube_etw_library/processcube_client/process_definition/start_callback_type.py +6 -0
  43. processcube_etw_library/processcube_client/process_instance/__init__.py +1 -0
  44. processcube_etw_library/processcube_client/process_instance/process_instance_client.py +32 -0
  45. processcube_etw_library/processcube_client/user_task/__init__.py +1 -0
  46. processcube_etw_library/processcube_client/user_task/user_task_client.py +63 -0
  47. processcube_etw_library/settings.py +35 -9
  48. {processcube_etw_library-2026.1.22.145211.dist-info → processcube_etw_library-2026.1.29.103633.dist-info}/METADATA +13 -11
  49. processcube_etw_library-2026.1.29.103633.dist-info/RECORD +58 -0
  50. {processcube_etw_library-2026.1.22.145211.dist-info → processcube_etw_library-2026.1.29.103633.dist-info}/WHEEL +1 -1
  51. processcube_etw_library-2026.1.22.145211.dist-info/RECORD +0 -18
  52. {processcube_etw_library-2026.1.22.145211.dist-info → processcube_etw_library-2026.1.29.103633.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,195 @@
1
+ import asyncio
2
+ from concurrent import futures
3
+ import errno
4
+ import logging
5
+ import uuid
6
+ import warnings
7
+
8
+ from ..core import BaseClient, LoopHelper, get_or_create_loop
9
+ from .external_task_worker import ExternalTaskWorker
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ExternalTaskClient(BaseClient):
15
+ def __init__(self, url, session=None, identity=None, loop=None, **kwargs):
16
+ super(ExternalTaskClient, self).__init__(url, session, identity)
17
+
18
+ new_kwargs = kwargs.copy()
19
+ new_kwargs["on_shutdown"] = self.__on_shutdown
20
+
21
+ if loop is None:
22
+ loop = get_or_create_loop()
23
+ else:
24
+ if "install_signals" not in new_kwargs:
25
+ new_kwargs["install_signals"] = False
26
+
27
+ self.__loop_helper = LoopHelper(loop, **new_kwargs)
28
+ self.__worker_id = kwargs.get("worker_id", str(uuid.uuid4()))
29
+
30
+ self._topic_for_handler = {}
31
+
32
+ def subscribe_to_external_task_for_topic(self, topic, handler, **options):
33
+ warnings.warn(
34
+ "Please use 'subscribe_to_external_task_topic' instead of 'subscribe_to_external_task_for_topic'.",
35
+ DeprecationWarning,
36
+ )
37
+ self.subscribe_to_external_task_topic(topic, handler, **options)
38
+
39
+ def subscribe_to_external_task_topic(self, topic, handler, **options):
40
+ """
41
+ task_options:
42
+ - max_tasks
43
+ - long_polling_timeout -> long_polling_timeout_in_ms
44
+ - lock_duration -> lock_duration_in_ms
45
+ #- additional_duration
46
+ #- extend_lock_timeout
47
+ - payload_filter
48
+ """
49
+
50
+ task_options = options.copy()
51
+
52
+ if task_options.get("long_polling_timeout", None) is not None:
53
+ logger.warning(
54
+ "deprecated long_polling_timeout, please use long_polling_timeout_in_ms"
55
+ )
56
+ task_options["long_polling_timeout_in_ms"] = options["long_polling_timeout"]
57
+ del task_options["long_polling_timeout"]
58
+
59
+ if task_options.get("lock_duration", None) is not None:
60
+ logger.warning("deprecated lock_duration, please use lock_duration_in_ms")
61
+ task_options["lock_duration_in_ms"] = options["lock_duration"]
62
+ del task_options["lock_duration"]
63
+
64
+ if task_options.get("additional_lock_duration", None) is not None:
65
+ logger.warning(
66
+ "additional_lock_duration is ignored, but lock_duration_in_ms is used"
67
+ )
68
+
69
+ if task_options.get("extend_lock_timeout", None) is not None:
70
+ logger.warning(
71
+ "extend_lock_timeout is ignored, but 90 percent of lock_duration is used"
72
+ )
73
+ del task_options["extend_lock_timeout"]
74
+
75
+ if topic in self._topic_for_handler:
76
+ logger.warning(f"The topic '{topic}' skipped, is already registered.'")
77
+ else:
78
+ self.__start_subscription(topic, handler, task_options)
79
+
80
+ def __start_subscription(self, topic, handler, task_options):
81
+ async def bg_job_handle_external_tasks():
82
+ try:
83
+ await self.handle_external_tasks(topic, handler, task_options)
84
+ except Exception as e:
85
+ if type(e) is errno.EPIPE:
86
+ # Raised while long polling finished, that the reason for only log as info.
87
+ logger.info(f"Expected long polling error: {e}")
88
+ else:
89
+ import traceback
90
+ import sys
91
+
92
+ traceback.print_exc(file=sys.stdout)
93
+ logger.warn(f"Exception on handle_external_task: {e}")
94
+
95
+ async_bg_task = self.__loop_helper.register_background_task(
96
+ bg_job_handle_external_tasks
97
+ )
98
+ self._topic_for_handler[topic] = async_bg_task
99
+
100
+ def subscribe_to_external_tasks_for_topics(self, topics_for_handlers):
101
+ for topic in topics_for_handlers.keys():
102
+ handler = topics_for_handlers[
103
+ topic
104
+ ] # TODO: Options sollten auch hier zu setzen sein
105
+
106
+ self.subscribe_to_external_task_for_topic(topic, handler)
107
+
108
+ def start(self, run_forever=True):
109
+ logger.info(
110
+ f"Starting external task for topics '{', '.join(self._topic_for_handler.keys())}'."
111
+ )
112
+ logger.info(f"Connecting to process engine at url '{self._url}'.")
113
+ self.__loop_helper.start(run_forever=run_forever)
114
+
115
+ async def handle_external_tasks(self, topic, handler, task_options):
116
+ external_tasks = await self.__fetch_and_lock(topic, handler, task_options)
117
+
118
+ len_external_tasks = len(external_tasks)
119
+ if len_external_tasks >= 1:
120
+ logger.info(f"Receive {len_external_tasks} tasks for topic '{topic}'.")
121
+
122
+ external_task_tasks = []
123
+
124
+ external_task_options = {
125
+ "long_polling_timeout_in_ms": task_options.get(
126
+ "long_polling_timeout_in_ms"
127
+ ),
128
+ "lock_duration_in_ms": task_options.get("lock_duration_in_ms"),
129
+ }
130
+
131
+ for external_task in external_tasks:
132
+ external_task_worker = ExternalTaskWorker(
133
+ self._url,
134
+ self._session,
135
+ self._identity,
136
+ self.__loop_helper,
137
+ topic,
138
+ handler,
139
+ external_task,
140
+ external_task_options,
141
+ )
142
+ task = asyncio.create_task(external_task_worker.start())
143
+ external_task_tasks.append(task)
144
+
145
+ try:
146
+ done_tasks, pending = await asyncio.wait(
147
+ external_task_tasks, return_when=futures.ALL_COMPLETED
148
+ )
149
+ logger.debug(
150
+ f"Wait result while handle external task (done: {done_tasks}, pending {pending})."
151
+ )
152
+ except Exception as e:
153
+ logger.error(f"asyncio.wait({e} - {type(e)})")
154
+
155
+ async def __on_shutdown(self):
156
+ await self.close()
157
+
158
+ def stop(self):
159
+ logger.info(
160
+ f"Stopping external task for topics {self._topic_for_handler.keys()}"
161
+ )
162
+ self.__loop_helper.stop()
163
+
164
+ async def __fetch_and_lock(self, topic, handler, task_options={}):
165
+ logger.debug(
166
+ f"fetch and lock external task for topic {topic} and options {task_options}"
167
+ )
168
+
169
+ max_tasks = task_options.get("max_tasks", 10)
170
+ long_polling_timeout = task_options.get(
171
+ "long_polling_timeout_in_ms", (10 * 1000)
172
+ )
173
+ lock_duration = task_options.get("lock_duration_in_ms", (100 * 1000))
174
+ payload_filter = task_options.get("payload_filter", None)
175
+
176
+ request = {
177
+ "workerId": self.__worker_id,
178
+ "topicName": topic,
179
+ "maxTasks": max_tasks,
180
+ "longPollingTimeout": long_polling_timeout,
181
+ "lockDuration": lock_duration,
182
+ }
183
+
184
+ if payload_filter is not None:
185
+ request["payloadFilter"] = payload_filter
186
+
187
+ try:
188
+ external_tasks = await self.do_post(
189
+ "/atlas_engine/api/v1/external_tasks/fetch_and_lock", request
190
+ )
191
+ except Exception as e:
192
+ logger.error(f"fetch_and_lock({e} - {type(e)})")
193
+ external_tasks = []
194
+
195
+ return external_tasks
@@ -0,0 +1,205 @@
1
+ import asyncio
2
+ import inspect
3
+ import logging
4
+ import traceback
5
+ import concurrent.futures
6
+
7
+ from ..core import BaseClient, ensure_has_loop
8
+ from .functional_error import FunctionalError
9
+
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ExternalTaskWorker(BaseClient):
15
+
16
+ LOCK_DURATION_IN_MS = (60 * 1000)
17
+ # Class-level executor to be reused across all instances
18
+ _executor = None
19
+
20
+ def __init__(self, url, session, identity, loop_helper, topic, handler, external_task, options={}):
21
+ super(ExternalTaskWorker, self).__init__(url, session, identity)
22
+ self._loop_helper = loop_helper
23
+ self._topic = topic
24
+ self._handler = handler
25
+ self._external_task = external_task
26
+ self._payload = self._external_task.get('payload', {})
27
+ self._options = options
28
+
29
+ @classmethod
30
+ def _get_executor(cls):
31
+ """Get or create the class-level ThreadPoolExecutor"""
32
+ if cls._executor is None:
33
+ cls._executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
34
+ return cls._executor
35
+
36
+ def __get_lock_duration_in_ms(self):
37
+ lock_duration_in_ms = self._options.get("lock_duration_in_ms", ExternalTaskWorker.LOCK_DURATION_IN_MS)
38
+
39
+ if lock_duration_in_ms is None:
40
+ logger.info(f":ock_duration_in_ms is not given, set to default {ExternalTaskWorker.LOCK_DURATION_IN_MS}")
41
+ lock_duration_in_ms = ExternalTaskWorker.LOCK_DURATION_IN_MS
42
+
43
+ return lock_duration_in_ms
44
+
45
+ def __lock_timeout_in_ms(self):
46
+ lock_duration_in_ms = self.__get_lock_duration_in_ms()
47
+ lock_timeout_in_ms = lock_duration_in_ms * 0.9
48
+
49
+ return lock_timeout_in_ms
50
+
51
+ def __delay_in_seconds(self):
52
+ delay_in_seconds = self.__lock_timeout_in_ms() / 1000
53
+
54
+ return delay_in_seconds
55
+
56
+ def __with_external_task_param(self):
57
+
58
+ def is_handler_a_func(func):
59
+ spec = inspect.getfullargspec(func)
60
+ is_func = inspect.isroutine(func)
61
+
62
+ result = (len(spec.args) == 2 and is_func)
63
+
64
+ return result
65
+
66
+ def is_handler_callable(caller):
67
+ spec = inspect.getfullargspec(caller)
68
+ is_func = inspect.isroutine(caller)
69
+
70
+ result = (len(spec.args) == 3 and not is_func)
71
+
72
+ return result
73
+
74
+ return is_handler_a_func(self._handler) or is_handler_callable(self._handler)
75
+
76
+ async def start(self):
77
+
78
+ def is_async_handler(handler):
79
+ if asyncio.iscoroutinefunction(handler):
80
+ return True
81
+ elif hasattr(handler, '__call__') and inspect.iscoroutinefunction(handler.__call__):
82
+ return True
83
+ else:
84
+ return False
85
+
86
+ async def extend_lock_task_wrapper():
87
+ worker_id = self._external_task['workerId']
88
+ external_task_id = self._external_task['id']
89
+
90
+ lock_duration_in_ms = self.__get_lock_duration_in_ms()
91
+
92
+ await self.extend_lock(worker_id, external_task_id, lock_duration_in_ms)
93
+
94
+ delay_in_seconds = self.__delay_in_seconds()
95
+
96
+ options = {'delay_in_seconds': delay_in_seconds}
97
+ extend_lock = self._loop_helper.register_background_task(extend_lock_task_wrapper, **options)
98
+
99
+ external_task_id = self._external_task['id']
100
+ logger.info(f"Starting external task '{external_task_id}' for topic '{self._topic}'")
101
+
102
+ try:
103
+ result = None
104
+
105
+ if is_async_handler(self._handler):
106
+ def handler_wrapper():
107
+ async def async_handler():
108
+ if self.__with_external_task_param():
109
+ return await self._handler(self._payload, self._external_task)
110
+ else:
111
+ return await self._handler(self._payload)
112
+
113
+ #new_loop = asyncio.new_event_loop()
114
+ # see https://stackoverflow.com/questions/46727787/runtimeerror-there-is-no-current-event-loop-in-thread-in-async-apscheduler
115
+ #asyncio.set_event_loop(new_loop)
116
+
117
+ new_loop = ensure_has_loop()
118
+
119
+ result = new_loop.run_until_complete(async_handler())
120
+
121
+ return result
122
+
123
+ # Use class-level executor to avoid race condition
124
+ # The executor must not be closed before the async operation completes
125
+ executor = self._get_executor()
126
+ result = await self._loop_helper._loop.run_in_executor(executor, handler_wrapper)
127
+
128
+ else:
129
+ fkt = None
130
+ if self.__with_external_task_param():
131
+ def with_all_args():
132
+ ensure_has_loop()
133
+
134
+ result = self._handler(self._payload, self._external_task)
135
+ return result
136
+ fkt = with_all_args
137
+ else:
138
+ def with_some_args():
139
+ ensure_has_loop()
140
+
141
+ result = self._handler(self._payload)
142
+ return result
143
+ fkt = with_some_args
144
+
145
+ # Use class-level executor to avoid race condition
146
+ # The executor must not be closed before the async operation completes
147
+ executor = self._get_executor()
148
+ result = await self._loop_helper._loop.run_in_executor(executor, fkt)
149
+
150
+ await self.__finish_task_successfully(result)
151
+
152
+ except FunctionalError as fe:
153
+ logger.warning(f"Finish external task with functional error (code: {fe.get_code()}, message: {fe.get_message()})")
154
+ await self.__finish_task_with_errors(fe.get_code(), fe.get_message(), fe.get_details())
155
+
156
+ except Exception as e:
157
+ formatted_lines = traceback.format_exc().splitlines()
158
+ logger.error(f"Finish external task with technical error ({str(e)} -> {formatted_lines})")
159
+ await self.__finish_task_with_errors(str(e), str(formatted_lines))
160
+ finally:
161
+ self._loop_helper.unregister_background_task(extend_lock, "extend_lock background task")
162
+
163
+ async def extend_lock(self, worker_id, external_task_id, additional_duration):
164
+ path = f"/atlas_engine/api/v1/external_tasks/{external_task_id}/extend_lock"
165
+
166
+ logger.info(f"Extend lock for worker '{worker_id}' with external_task_id '{external_task_id}' for topic '{self._topic}' for another {additional_duration}ms")
167
+
168
+ request = {
169
+ "workerId": worker_id,
170
+ "additionalDuration": additional_duration
171
+ }
172
+ try:
173
+ await self.do_put(path, request)
174
+ except Exception as e:
175
+ logger.error(f"Error on extend lock for worker {worker_id} for {additional_duration}ms with error {e}")
176
+
177
+ async def __finish_task_successfully(self, result):
178
+ external_task_id = self._external_task['id']
179
+ worker_id = self._external_task['workerId']
180
+
181
+ logger.info(f"Finish external task '{external_task_id}' for worker '{worker_id}' and topic '{self._topic}' with result '{result}'")
182
+ path = f"/atlas_engine/api/v1/external_tasks/{external_task_id}/finish"
183
+
184
+ payload = {
185
+ "workerId": worker_id,
186
+ "result": result
187
+ }
188
+
189
+ result = await self.do_put(path, payload)
190
+ logger.info(f"Finished external task '{external_task_id}' successfully.")
191
+
192
+ async def __finish_task_with_errors(self, error_code, error_message, error_details=""):
193
+ logger.warning(f"Finished external task_with errors '{self._external_task}', '{error_code}', '{error_message}'.")
194
+ path = f"/atlas_engine/api/v1/external_tasks/{self._external_task['id']}/error"
195
+
196
+ payload = {
197
+ "workerId": self._external_task['workerId'],
198
+ "error": {
199
+ "errorCode": error_code,
200
+ "errorMessage": error_message,
201
+ "errorDetails": error_details
202
+ }
203
+ }
204
+
205
+ await self.do_put(path, payload)
@@ -0,0 +1,17 @@
1
+
2
+ class FunctionalError(Exception):
3
+
4
+ def __init__(self, code, message, details=""):
5
+ super(FunctionalError, self).__init__(message)
6
+ self._code = code
7
+ self._message = message
8
+ self._details = details
9
+
10
+ def get_code(self):
11
+ return self._code
12
+
13
+ def get_message(self):
14
+ return self._message
15
+
16
+ def get_details(self):
17
+ return self._details
@@ -0,0 +1 @@
1
+ from .flow_node_instance_client import FlowNodeInstanceClient
@@ -0,0 +1,43 @@
1
+ import logging
2
+
3
+ from ..core import base_client
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ class FlowNodeInstanceClient(base_client.BaseClient):
9
+ def __init__(self, url, session=None, identity=None):
10
+ super(FlowNodeInstanceClient, self).__init__(url, session, identity)
11
+
12
+ async def __trigger_message_event(self, event_name, process_instance_id=None):
13
+ url = f"/atlas_engine/api/v1/messages/{event_name}/trigger"
14
+
15
+ if process_instance_id is not None:
16
+ url = f"{url}?processInstanceId={process_instance_id}"
17
+
18
+ result = await self.do_post(url, {})
19
+
20
+ return result
21
+
22
+ def trigger_message_event(self, event_name, process_instance_id=None):
23
+ logger.info(f"Connection to process engine at url '{self._url}'.")
24
+ logger.info(f"Trigger message '{event_name}'.")
25
+
26
+ return base_client.run_async_in_sync_context(
27
+ self.__trigger_message_event(event_name, process_instance_id)
28
+ )
29
+
30
+ async def __trigger_signal_event(self, event_name):
31
+ url = f"/atlas_engine/api/v1/signals/{event_name}/trigger"
32
+
33
+ result = await self.do_post(url, {})
34
+
35
+ return result
36
+
37
+ def trigger_signal_event(self, event_name):
38
+ logger.info(f"Connection to process engine at url '{self._url}'.")
39
+ logger.info(f"Trigger signal '{event_name}'.")
40
+
41
+ return base_client.run_async_in_sync_context(
42
+ self.__trigger_signal_event(event_name)
43
+ )
@@ -0,0 +1 @@
1
+ from .notification_client import NotificationClient
@@ -0,0 +1,103 @@
1
+ import asyncio
2
+ import logging
3
+
4
+ from ..core import BaseClient, LoopHelper
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class NotificationClient(BaseClient):
10
+ def __init__(self, url, session=None, identity=None):
11
+ super(NotificationClient, self).__init__(url, session, identity)
12
+
13
+ self.__loop_helper = LoopHelper(on_shutdown=self.__on_shutdown)
14
+ self.__notification_callbacks = {}
15
+
16
+ async def __on_shutdown(self):
17
+ for task in self.__notification_callbacks.values():
18
+ self.__loop_helper.unregister_background_task(task)
19
+
20
+ await self.close()
21
+
22
+ def start(self):
23
+ logger.info(f"Connecting to process engine at url '{self._url}'.")
24
+ self.__loop_helper.start()
25
+
26
+ def __on_register_event(self, event_name, callback):
27
+ async def bg_on_event():
28
+ url = f"/atlas_engine/api/v1/notifications/long_polling/{event_name}"
29
+
30
+ result = await self.do_get(url)
31
+
32
+ logger.debug(f"handling event '{event_name}' with {result}")
33
+
34
+ if asyncio.iscoroutine(callback):
35
+ await callback(result)
36
+ else:
37
+ callback(result)
38
+
39
+ async_bg_task = self.__loop_helper.register_background_task(bg_on_event)
40
+ self.__notification_callbacks[event_name] = async_bg_task
41
+
42
+ def on_activity_reached(self, callback):
43
+ self.__on_register_event("activity_reached", callback)
44
+
45
+ def on_activity_finished(self, callback):
46
+ self.__on_register_event("activity_finished", callback)
47
+
48
+ def on_boundary_event_triggered(self, callback):
49
+ self.__on_register_event("boundary_event_triggered", callback)
50
+
51
+ def on_empty_activity_waiting(self, callback):
52
+ self.__on_register_event("empty_activity_waiting", callback)
53
+
54
+ def on_empty_activity_finished(self, callback):
55
+ self.__on_register_event("empty_activity_finished", callback)
56
+
57
+ def on_manual_task_waiting(self, callback):
58
+ self.__on_register_event("manual_task_waiting", callback)
59
+
60
+ def on_manual_task_finished(self, callback):
61
+ self.__on_register_event("manual_task_finished", callback)
62
+
63
+ def on_process_started(self, callback):
64
+ self.__on_register_event("process_started", callback)
65
+
66
+ def on_process_ended(self, callback):
67
+ self.__on_register_event("process_ended", callback)
68
+
69
+ def on_process_error(self, callback):
70
+ self.__on_register_event("process_error", callback)
71
+
72
+ def on_user_task_waiting(self, callback):
73
+ self.__on_register_event("user_task_waiting", callback)
74
+
75
+ def on_user_task_finished(self, callback):
76
+ self.__on_register_event("user_task_finished", callback)
77
+
78
+ def on_user_task_reserved(self, callback):
79
+ self.__on_register_event("user_task_reserved", callback)
80
+
81
+ def on_user_task_reservation_canceled(self, callback):
82
+ self.__on_register_event("user_task_reservation_canceled", callback)
83
+
84
+ def on_empty_activity_finished(self, callback):
85
+ self.__on_register_event("empty_activity_finished", callback)
86
+
87
+ def on_empty_activity_waiting(self, callback):
88
+ self.__on_register_event("empty_activity_waiting", callback)
89
+
90
+ def on_manual_task_finished(self, callback):
91
+ self.__on_register_event("manual_task_finished", callback)
92
+
93
+ def on_manual_task_waiting(self, callback):
94
+ self.__on_register_event("manual_task_waiting", callback)
95
+
96
+ def on_intermediate_throw_event_triggered(self, callback):
97
+ self.__on_register_event("intermediate_throw_event_triggered", callback)
98
+
99
+ def on_intermediate_catch_event_reached(self, callback):
100
+ self.__on_register_event("intermediate_catch_event_reached", callback)
101
+
102
+ def on_intermediate_catch_event_finished(self, callback):
103
+ self.__on_register_event("intermediate_catch_event_finished", callback)
@@ -0,0 +1,2 @@
1
+ from .process_definition_client import ProcessDefinitionClient
2
+ from .start_callback_type import StartCallbackType
@@ -0,0 +1,94 @@
1
+ import logging
2
+
3
+ from ..core import base_client
4
+ from .start_callback_type import StartCallbackType
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class ProcessDefinitionClient(base_client.BaseClient):
10
+
11
+ def __init__(self, url, session=None, identity=None):
12
+ super(ProcessDefinitionClient, self).__init__(url, session, identity)
13
+
14
+ async def __start_process_instance(self, process_model_id, **options):
15
+ start_callback_type = options.get('start_callback', StartCallbackType.ON_PROCESSINSTANCE_CREATED)
16
+ path = f"/atlas_engine/api/v1/process_models/{process_model_id}/start"
17
+
18
+ initial_token = options.get('inital_token', {})
19
+
20
+ payload = {
21
+ "returnOn": int(start_callback_type),
22
+ "processModelId": process_model_id,
23
+ "initialToken": initial_token
24
+ }
25
+
26
+ logger.info(f"Start process with uri '{path}' and payload '{payload}'")
27
+
28
+ result = await self.do_post(path, payload)
29
+
30
+ return result
31
+
32
+ async def __start_process_instance_by_start_event(self, process_model_id, start_event_id, end_event_id, **options):
33
+ start_callback_type = options.get('start_callback', StartCallbackType.ON_PROCESSINSTANCE_CREATED)
34
+ path = f"/atlas_engine/api/v1/process_models/{process_model_id}/start"
35
+
36
+ initial_token = options.get('initial_token', {})
37
+
38
+ payload = {
39
+ "returnOn": int(start_callback_type),
40
+ "processModelId": process_model_id,
41
+ "startEventId": start_event_id,
42
+ "initialToken": initial_token
43
+ }
44
+
45
+ if end_event_id is not None:
46
+ payload["endEventId"] = end_event_id
47
+
48
+ logger.info(f"Start process with uri '{path}' and payload '{payload}'")
49
+
50
+ result = await self.do_post(path, payload)
51
+
52
+ return result
53
+
54
+ def __start_process_instance_wrapper(self, process_model_id, start_event_id=None, end_event_id=None, **options):
55
+
56
+ async def _start():
57
+ if start_event_id is not None:
58
+ result = await self.__start_process_instance_by_start_event(process_model_id, start_event_id, end_event_id, **options)
59
+ else:
60
+ result = await self.__start_process_instance(process_model_id, **options)
61
+
62
+ await self.close()
63
+
64
+ return result
65
+
66
+ logger.info(f"Connection to process engine at url '{self._url}'.")
67
+ logger.info(f"Starting process instance process_model_id '{process_model_id}' and start_event_id '{start_event_id}'.")
68
+
69
+ return base_client.run_async_in_sync_context(_start())
70
+
71
+ def start_process_instance(self, process_model_id, start_event_id=None, **options):
72
+ options['start_callback'] = StartCallbackType.ON_PROCESSINSTANCE_CREATED
73
+
74
+ return self.__start_process_instance_wrapper(process_model_id, start_event_id, **options)
75
+
76
+ def start_process_instance_and_await_end_event(self, process_model_id, start_event_id=None, **options):
77
+ options['start_callback'] = StartCallbackType.ON_PROCESSINSTANCE_FINISHED
78
+
79
+ return self.__start_process_instance_wrapper(process_model_id, start_event_id, **options)
80
+
81
+ def start_process_instance_and_await_specific_end_event(self, process_model_id, start_event_id=None, end_event_id=None, **options):
82
+ options['start_callback'] = StartCallbackType.ON_ENDEVENT_REACHED
83
+
84
+ return self.__start_process_instance_wrapper(process_model_id, start_event_id, end_event_id, **options)
85
+
86
+ def get_process_definition(self, process_model_id):
87
+
88
+ async def _get_definition():
89
+ url = f"/atlas_engine/api/v1/process_models/{process_model_id}/process_definition"
90
+ return await self.do_get(url)
91
+
92
+ logger.info(f"Get the definition of process_model '{process_model_id}' from atlas engine at url '{self._url}'.")
93
+
94
+ return base_client.run_async_in_sync_context(_get_definition())
@@ -0,0 +1,6 @@
1
+ from enum import IntEnum
2
+
3
+ class StartCallbackType(IntEnum):
4
+ ON_PROCESSINSTANCE_CREATED = 1
5
+ ON_PROCESSINSTANCE_FINISHED = 2
6
+ ON_ENDEVENT_REACHED = 3
@@ -0,0 +1 @@
1
+ from .process_instance_client import ProcessInstanceClient