processcube-etw-library 2026.1.22.131318b0__tar.gz → 2026.1.29.71849b0__tar.gz
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.
- {processcube_etw_library-2026.1.22.131318b0/src/processcube_etw_library.egg-info → processcube_etw_library-2026.1.29.71849b0}/PKG-INFO +13 -11
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/README.md +10 -9
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/pyproject.toml +5 -2
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/__init__.py +7 -5
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/create_external_task_client.py +4 -2
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/etw_app.py +18 -5
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/built_in.py +3 -4
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/check.py +14 -4
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/identity_provider.py +0 -2
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/__init__.py +22 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/app_info/__init__.py +1 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/app_info/app_info_client.py +36 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/client_factory.py +37 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/__init__.py +2 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/__init__.py +13 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/base_client.py +235 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/client.py +816 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/__init__.py +0 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/application_info.py +34 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/data_object_instances.py +61 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/empty_tasks.py +86 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/events.py +39 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/external_tasks.py +142 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/flow_node_instances.py +80 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/manual_tasks.py +87 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/process_definitions.py +46 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/process_instances.py +96 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/process_models.py +51 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/user_tasks.py +130 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/base_client.py +175 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/loop_helper.py +200 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/event/__init__.py +1 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/event/event_client.py +43 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/__init__.py +3 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/client_wrapper.py +28 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/external_task_client.py +195 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/external_task_worker.py +205 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/functional_error.py +17 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/flow_node_instance/__init__.py +1 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/flow_node_instance/flow_node_instance_client.py +43 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/notification/__init__.py +1 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/notification/notification_client.py +103 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_definition/__init__.py +2 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_definition/process_definition_client.py +94 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_definition/start_callback_type.py +6 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_instance/__init__.py +1 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_instance/process_instance_client.py +32 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/user_task/__init__.py +1 -0
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/user_task/user_task_client.py +63 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/settings.py +35 -9
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library.egg-info}/PKG-INFO +13 -11
- processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library.egg-info/SOURCES.txt +65 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library.egg-info/requires.txt +2 -1
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/uv.lock +11 -506
- processcube_etw_library-2026.1.22.131318b0/src/processcube_etw_library.egg-info/SOURCES.txt +0 -25
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/.github/workflows/build_and_publish.yml +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/.gitignore +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/.python-version +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/setup.cfg +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/__init__.py +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/handlers.py +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/models.py +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/registry.py +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/routes.py +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/server_config.py +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/typed_handler.py +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library.egg-info/dependency_links.txt +0 -0
- {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library.egg-info/top_level.txt +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: processcube-etw-library
|
|
3
|
-
Version: 2026.1.
|
|
3
|
+
Version: 2026.1.29.71849b0
|
|
4
4
|
Summary: A library to create ETW apps with the ProcessCube platform.
|
|
5
5
|
Author-email: Jeremy Hill <jeremy.hill@profection.de>
|
|
6
6
|
License: MIT
|
|
7
7
|
Requires-Python: >=3.12
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: async-lru>=2.1.0
|
|
10
|
+
Requires-Dist: dataclasses-json>=0.6.7
|
|
9
11
|
Requires-Dist: fastapi[standard]>=0.128.0
|
|
10
12
|
Requires-Dist: oauth2-client>=1.4.2
|
|
11
|
-
Requires-Dist: processcube-client>=5.0.0
|
|
12
13
|
Requires-Dist: tenacity>=9.1.2
|
|
13
14
|
|
|
14
15
|
# ProcessCube ETW Library
|
|
@@ -27,15 +28,16 @@ uv add processcube-etw-library
|
|
|
27
28
|
|
|
28
29
|
The library uses environment variables for configuration. You can set these in your environment or in a `.env` file in your project root.
|
|
29
30
|
|
|
30
|
-
| Variable
|
|
31
|
-
|
|
|
32
|
-
| `PROCESSCUBE_ENGINE_URL`
|
|
33
|
-
| `PROCESSCUBE_AUTHORITY_URL`
|
|
34
|
-
| `PROCESSCUBE_ETW_CLIENT_ID`
|
|
35
|
-
| `PROCESSCUBE_ETW_CLIENT_SECRET`
|
|
36
|
-
| `PROCESSCUBE_ETW_CLIENT_SCOPES`
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
31
|
+
| Variable | Default | Description |
|
|
32
|
+
| ------------------------------------------------ | -------------------------------------- | ------------------------------------------------------- |
|
|
33
|
+
| `PROCESSCUBE_ENGINE_URL` | `http://localhost:56000` | URL of the ProcessCube Engine |
|
|
34
|
+
| `PROCESSCUBE_AUTHORITY_URL` | Auto-discovered from engine | URL of the ProcessCube Authority (OAuth server) |
|
|
35
|
+
| `PROCESSCUBE_ETW_CLIENT_ID` | `test_etw` | OAuth client ID for the External Task Worker |
|
|
36
|
+
| `PROCESSCUBE_ETW_CLIENT_SECRET` | `3ef62eb3-fe49-4c2c-ba6f-73e4d234321b` | OAuth client secret for the External Task Worker |
|
|
37
|
+
| `PROCESSCUBE_ETW_CLIENT_SCOPES` | `engine_etw` | OAuth scopes for the External Task Worker |
|
|
38
|
+
| `PROCESSCUBE_MAX_GET_OAUTH_ACCESS_TOKEN_RETRIES` | `10` | Maximum retries for obtaining OAuth access token |
|
|
39
|
+
| `PROCESSCUBE_ETW_LONG_POLLING_TIMEOUT_IN_MS` | `60000` | Long polling timeout in milliseconds for external tasks |
|
|
40
|
+
| `ENVIRONMENT` | `development` | Environment mode (`development` or `production`) |
|
|
39
41
|
|
|
40
42
|
#### Example `.env` File
|
|
41
43
|
|
{processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/README.md
RENAMED
|
@@ -14,15 +14,16 @@ uv add processcube-etw-library
|
|
|
14
14
|
|
|
15
15
|
The library uses environment variables for configuration. You can set these in your environment or in a `.env` file in your project root.
|
|
16
16
|
|
|
17
|
-
| Variable
|
|
18
|
-
|
|
|
19
|
-
| `PROCESSCUBE_ENGINE_URL`
|
|
20
|
-
| `PROCESSCUBE_AUTHORITY_URL`
|
|
21
|
-
| `PROCESSCUBE_ETW_CLIENT_ID`
|
|
22
|
-
| `PROCESSCUBE_ETW_CLIENT_SECRET`
|
|
23
|
-
| `PROCESSCUBE_ETW_CLIENT_SCOPES`
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
17
|
+
| Variable | Default | Description |
|
|
18
|
+
| ------------------------------------------------ | -------------------------------------- | ------------------------------------------------------- |
|
|
19
|
+
| `PROCESSCUBE_ENGINE_URL` | `http://localhost:56000` | URL of the ProcessCube Engine |
|
|
20
|
+
| `PROCESSCUBE_AUTHORITY_URL` | Auto-discovered from engine | URL of the ProcessCube Authority (OAuth server) |
|
|
21
|
+
| `PROCESSCUBE_ETW_CLIENT_ID` | `test_etw` | OAuth client ID for the External Task Worker |
|
|
22
|
+
| `PROCESSCUBE_ETW_CLIENT_SECRET` | `3ef62eb3-fe49-4c2c-ba6f-73e4d234321b` | OAuth client secret for the External Task Worker |
|
|
23
|
+
| `PROCESSCUBE_ETW_CLIENT_SCOPES` | `engine_etw` | OAuth scopes for the External Task Worker |
|
|
24
|
+
| `PROCESSCUBE_MAX_GET_OAUTH_ACCESS_TOKEN_RETRIES` | `10` | Maximum retries for obtaining OAuth access token |
|
|
25
|
+
| `PROCESSCUBE_ETW_LONG_POLLING_TIMEOUT_IN_MS` | `60000` | Long polling timeout in milliseconds for external tasks |
|
|
26
|
+
| `ENVIRONMENT` | `development` | Environment mode (`development` or `production`) |
|
|
26
27
|
|
|
27
28
|
#### Example `.env` File
|
|
28
29
|
|
|
@@ -11,9 +11,10 @@ authors = [{ name = "Jeremy Hill", email = "jeremy.hill@profection.de" }]
|
|
|
11
11
|
license = { text = "MIT" }
|
|
12
12
|
requires-python = ">=3.12"
|
|
13
13
|
dependencies = [
|
|
14
|
+
"async-lru>=2.1.0",
|
|
15
|
+
"dataclasses-json>=0.6.7",
|
|
14
16
|
"fastapi[standard]>=0.128.0",
|
|
15
17
|
"oauth2-client>=1.4.2",
|
|
16
|
-
"processcube-client>=5.0.0",
|
|
17
18
|
"tenacity>=9.1.2",
|
|
18
19
|
]
|
|
19
20
|
|
|
@@ -21,7 +22,9 @@ dependencies = [
|
|
|
21
22
|
override-dependencies = ["nest-asyncio>=1.6.0"]
|
|
22
23
|
|
|
23
24
|
[tool.setuptools]
|
|
24
|
-
packages = ["processcube_etw_library"]
|
|
25
25
|
package-dir = { "" = "src" }
|
|
26
26
|
|
|
27
|
+
[tool.setuptools.packages.find]
|
|
28
|
+
where = ["src"]
|
|
29
|
+
|
|
27
30
|
[tool.setuptools_scm]
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Prevent nest_asyncio from patching asyncio.run() in processcube_client - it breaks uvicorn's loop_factory
|
|
2
|
-
import nest_asyncio
|
|
3
|
-
nest_asyncio.apply = lambda *args, **kwargs: None
|
|
2
|
+
# import nest_asyncio
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
# nest_asyncio.apply = lambda *args, **kwargs: None
|
|
5
|
+
|
|
6
|
+
from .health import ( # noqa: E402
|
|
6
7
|
create_url_health_check,
|
|
7
8
|
HealthCheck,
|
|
8
9
|
HealthCheckModel,
|
|
@@ -10,8 +11,8 @@ from .health import (
|
|
|
10
11
|
HealthConditionInfo,
|
|
11
12
|
LivezResponse,
|
|
12
13
|
)
|
|
13
|
-
from .etw_app import new_external_task_worker_app
|
|
14
|
-
from .settings import load_settings,
|
|
14
|
+
from .etw_app import new_external_task_worker_app # noqa: E402
|
|
15
|
+
from .settings import load_settings, ETWSettings # noqa: E402
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
"create_url_health_check",
|
|
@@ -22,4 +23,5 @@ __all__ = [
|
|
|
22
23
|
"LivezResponse",
|
|
23
24
|
"new_external_task_worker_app",
|
|
24
25
|
"load_settings",
|
|
26
|
+
"ETWSettings",
|
|
25
27
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from processcube_client.external_task import ExternalTaskClient
|
|
1
|
+
from .processcube_client.external_task import ExternalTaskClient
|
|
2
2
|
|
|
3
3
|
from .identity_provider import IdentityProvider
|
|
4
4
|
from .settings import load_settings
|
|
@@ -11,7 +11,9 @@ def create_external_task_client() -> ExternalTaskClient:
|
|
|
11
11
|
client_name = settings.processcube_etw_client_id
|
|
12
12
|
client_secret = settings.processcube_etw_client_secret
|
|
13
13
|
client_scopes = settings.processcube_etw_client_scopes
|
|
14
|
-
max_get_oauth_access_token_retries =
|
|
14
|
+
max_get_oauth_access_token_retries = (
|
|
15
|
+
settings.processcube_max_get_oauth_access_token_retries
|
|
16
|
+
)
|
|
15
17
|
|
|
16
18
|
identity_provider = IdentityProvider(
|
|
17
19
|
authority_url,
|
|
@@ -6,16 +6,17 @@ from typing import Callable, Optional
|
|
|
6
6
|
from fastapi import FastAPI
|
|
7
7
|
import uvicorn
|
|
8
8
|
|
|
9
|
+
from .settings import load_settings
|
|
9
10
|
from .health import (
|
|
10
11
|
add_built_in_health_checks,
|
|
11
12
|
HealthCheck,
|
|
12
13
|
HealthCheckRegistry,
|
|
13
|
-
setup_health_routes
|
|
14
|
-
|
|
14
|
+
setup_health_routes,
|
|
15
|
+
)
|
|
15
16
|
from .create_external_task_client import create_external_task_client
|
|
16
17
|
from .server_config import get_server_config
|
|
17
18
|
from .typed_handler import create_typed_handler_wrapper
|
|
18
|
-
from processcube_client.external_task import ExternalTaskClient
|
|
19
|
+
from .processcube_client.external_task import ExternalTaskClient
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class ExternalTaskWorkerApp:
|
|
@@ -67,13 +68,25 @@ class ExternalTaskWorkerApp:
|
|
|
67
68
|
def subscribe_to_external_task_for_topic(
|
|
68
69
|
self, topic: str, handler: Callable, **options
|
|
69
70
|
) -> None:
|
|
70
|
-
|
|
71
|
+
settings = load_settings()
|
|
72
|
+
self._etw_client.subscribe_to_external_task_topic(
|
|
73
|
+
topic,
|
|
74
|
+
handler,
|
|
75
|
+
long_polling_timeout_in_ms=settings.processcube_etw_long_polling_timeout_in_ms,
|
|
76
|
+
**options,
|
|
77
|
+
)
|
|
71
78
|
|
|
72
79
|
def subscribe_to_external_task_for_topic_typed(
|
|
73
80
|
self, topic: str, handler: Callable, **options
|
|
74
81
|
) -> None:
|
|
82
|
+
settings = load_settings()
|
|
75
83
|
wrapper = create_typed_handler_wrapper(handler)
|
|
76
|
-
self._etw_client.subscribe_to_external_task_topic(
|
|
84
|
+
self._etw_client.subscribe_to_external_task_topic(
|
|
85
|
+
topic,
|
|
86
|
+
wrapper,
|
|
87
|
+
long_polling_timeout_in_ms=settings.processcube_etw_long_polling_timeout_in_ms,
|
|
88
|
+
**options,
|
|
89
|
+
)
|
|
77
90
|
|
|
78
91
|
def add_health_check(self, check: HealthCheck) -> None:
|
|
79
92
|
self._health_registry.register(check)
|
|
@@ -7,8 +7,7 @@ def add_built_in_health_checks(registry: HealthCheckRegistry) -> None:
|
|
|
7
7
|
settings = load_settings()
|
|
8
8
|
|
|
9
9
|
engine_url = (
|
|
10
|
-
settings.processcube_engine_url.strip("/")
|
|
11
|
-
+ "/atlas_engine/api/v1/info"
|
|
10
|
+
settings.processcube_engine_url.strip("/") + "/atlas_engine/api/v1/info"
|
|
12
11
|
)
|
|
13
12
|
|
|
14
13
|
authority_url = (
|
|
@@ -18,7 +17,7 @@ def add_built_in_health_checks(registry: HealthCheckRegistry) -> None:
|
|
|
18
17
|
|
|
19
18
|
registry.register(
|
|
20
19
|
HealthCheck(
|
|
21
|
-
create_url_health_check(engine_url),
|
|
20
|
+
create_url_health_check(engine_url, use_cache=True, ttl=300),
|
|
22
21
|
service_name="ProcessCube Engine",
|
|
23
22
|
tags=["core", "backend"],
|
|
24
23
|
comments=["Checks if the ProcessCube Engine is reachable"],
|
|
@@ -26,7 +25,7 @@ def add_built_in_health_checks(registry: HealthCheckRegistry) -> None:
|
|
|
26
25
|
)
|
|
27
26
|
registry.register(
|
|
28
27
|
HealthCheck(
|
|
29
|
-
create_url_health_check(authority_url),
|
|
28
|
+
create_url_health_check(authority_url, use_cache=True, ttl=300),
|
|
30
29
|
service_name="ProcessCube Authority",
|
|
31
30
|
tags=["core", "auth"],
|
|
32
31
|
comments=["Checks if the ProcessCube Authority is reachable"],
|
|
@@ -2,6 +2,7 @@ from collections.abc import Awaitable, Callable
|
|
|
2
2
|
from datetime import timedelta
|
|
3
3
|
from time import perf_counter
|
|
4
4
|
import inspect
|
|
5
|
+
from async_lru import alru_cache
|
|
5
6
|
|
|
6
7
|
import httpx
|
|
7
8
|
|
|
@@ -10,6 +11,7 @@ from .models import HealthCheckResult
|
|
|
10
11
|
|
|
11
12
|
type HealthCheckCondition = Callable[[], bool | Awaitable[bool]]
|
|
12
13
|
|
|
14
|
+
|
|
13
15
|
class HealthCheck:
|
|
14
16
|
def __init__(
|
|
15
17
|
self,
|
|
@@ -36,10 +38,18 @@ class HealthCheck:
|
|
|
36
38
|
return HealthCheckResult(healthy=healthy, time_taken=timedelta(seconds=elapsed))
|
|
37
39
|
|
|
38
40
|
|
|
39
|
-
def create_url_health_check(
|
|
40
|
-
|
|
41
|
+
def create_url_health_check(
|
|
42
|
+
url: str, timeout: float = 5.0, use_cache: bool = False, ttl: int = 60
|
|
43
|
+
) -> HealthCheckCondition:
|
|
44
|
+
@alru_cache(maxsize=128, ttl=ttl)
|
|
45
|
+
async def check_cached():
|
|
46
|
+
async with httpx.AsyncClient() as client:
|
|
47
|
+
response = await client.get(url, timeout=timeout)
|
|
48
|
+
return response.status_code == 200
|
|
49
|
+
|
|
50
|
+
async def check_no_cache():
|
|
41
51
|
async with httpx.AsyncClient() as client:
|
|
42
52
|
response = await client.get(url, timeout=timeout)
|
|
43
|
-
return
|
|
53
|
+
return response.status_code == 200
|
|
44
54
|
|
|
45
|
-
return
|
|
55
|
+
return check_cached if use_cache else check_no_cache
|
|
@@ -27,7 +27,6 @@ def log_failed_attempt(retry_state):
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class IdentityProvider:
|
|
30
|
-
|
|
31
30
|
def __init__(
|
|
32
31
|
self,
|
|
33
32
|
authority_url: str,
|
|
@@ -51,7 +50,6 @@ class IdentityProvider:
|
|
|
51
50
|
return self._access_token_caller()
|
|
52
51
|
|
|
53
52
|
def _prepare_get_access_token_caller(self):
|
|
54
|
-
|
|
55
53
|
@retry(
|
|
56
54
|
stop=stop_after_attempt(self._max_get_oauth_access_token_retries),
|
|
57
55
|
wait=wait_exponential(multiplier=1, min=2, max=30),
|
processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .client_factory import ClientFactory
|
|
2
|
+
from .app_info import AppInfoClient
|
|
3
|
+
from .event import EventClient
|
|
4
|
+
from .external_task import ClientWrapper, ExternalTaskClient
|
|
5
|
+
from .flow_node_instance import FlowNodeInstanceClient
|
|
6
|
+
from .notification import NotificationClient
|
|
7
|
+
from .process_instance import ProcessInstanceClient
|
|
8
|
+
from .process_definition import ProcessDefinitionClient
|
|
9
|
+
from .user_task import UserTaskClient
|
|
10
|
+
|
|
11
|
+
__all__ = ['ClientFactory',
|
|
12
|
+
'AppInfoClient',
|
|
13
|
+
'EventClient',
|
|
14
|
+
'ClientWrapper',
|
|
15
|
+
'ExternalTaskClient',
|
|
16
|
+
'FlowNodeInstanceClient',
|
|
17
|
+
'NotificationClient',
|
|
18
|
+
'ProcessInstanceClient',
|
|
19
|
+
'ProcessDefinitionClient',
|
|
20
|
+
'UserTaskClient',
|
|
21
|
+
]
|
|
22
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .app_info_client import AppInfoClient
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from ..core.base_client import BaseClient, run_async_in_sync_context
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AppInfoClient(BaseClient):
|
|
9
|
+
def __init__(self, url, session=None, identity=None):
|
|
10
|
+
super(AppInfoClient, self).__init__(url, session, identity)
|
|
11
|
+
|
|
12
|
+
async def __get_info(self):
|
|
13
|
+
url = f"/atlas_engine/api/v1/info"
|
|
14
|
+
|
|
15
|
+
result = await self.do_get(url)
|
|
16
|
+
|
|
17
|
+
return result
|
|
18
|
+
|
|
19
|
+
def get_info(self):
|
|
20
|
+
logger.info(f"Connection to atlas engine at url '{self._url}'.")
|
|
21
|
+
logger.info(f"Get info of the atlas engine.")
|
|
22
|
+
|
|
23
|
+
return run_async_in_sync_context(self.__get_info())
|
|
24
|
+
|
|
25
|
+
async def __get_authority(self):
|
|
26
|
+
url = f"/atlas_engine/api/v1/authority"
|
|
27
|
+
|
|
28
|
+
result = await self.do_get(url)
|
|
29
|
+
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
def get_authority(self):
|
|
33
|
+
logger.info(f"Connection to atlas engine at url '{self._url}'.")
|
|
34
|
+
logger.info(f"Get info of the authority of the atlas engine.")
|
|
35
|
+
|
|
36
|
+
return run_async_in_sync_context(self.__get_authority())
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from .app_info import AppInfoClient
|
|
2
|
+
from .event import EventClient
|
|
3
|
+
from .external_task import ClientWrapper
|
|
4
|
+
from .flow_node_instance import FlowNodeInstanceClient
|
|
5
|
+
from .notification import NotificationClient
|
|
6
|
+
from .process_instance import ProcessInstanceClient
|
|
7
|
+
from .process_definition import ProcessDefinitionClient
|
|
8
|
+
from .user_task import UserTaskClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ClientFactory:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def create_app_info_client(self, engine_url):
|
|
16
|
+
return AppInfoClient(engine_url)
|
|
17
|
+
|
|
18
|
+
def create_event_client(self, engine_url):
|
|
19
|
+
return EventClient(engine_url)
|
|
20
|
+
|
|
21
|
+
def create_external_task_client(self, engine_url):
|
|
22
|
+
return ClientWrapper(engine_url)
|
|
23
|
+
|
|
24
|
+
def create_flow_node_instance_client(self, engine_url):
|
|
25
|
+
return FlowNodeInstanceClient(engine_url)
|
|
26
|
+
|
|
27
|
+
def create_notification_client(self, engine_url):
|
|
28
|
+
return NotificationClient(engine_url)
|
|
29
|
+
|
|
30
|
+
def create_process_instance_client(self, engine_url):
|
|
31
|
+
return ProcessInstanceClient(engine_url)
|
|
32
|
+
|
|
33
|
+
def create_process_definition_client(self, engine_url):
|
|
34
|
+
return ProcessDefinitionClient(engine_url)
|
|
35
|
+
|
|
36
|
+
def create_user_task_client(self, engine_url):
|
|
37
|
+
return UserTaskClient(engine_url)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .client import Client
|
|
2
|
+
|
|
3
|
+
from .helpers.application_info import ApplicationInfo
|
|
4
|
+
from .helpers.data_object_instances import DataObjectInstanceHandler, DataObjectInstancesQuery, DataObjectInstanceResponse
|
|
5
|
+
from .helpers.empty_tasks import EmptyTaskHandler, EmptyTaskQuery, EmptyTaskResponse
|
|
6
|
+
from .helpers.events import EventsHandler, MessageTriggerRequest
|
|
7
|
+
from .helpers.process_definitions import ProcessDefinitionUploadPayload, ProcessDefinitionHandler
|
|
8
|
+
from .helpers.process_instances import ProcessInstanceHandler, ProcessInstanceQueryRequest, ProcessInstanceQueryResponse
|
|
9
|
+
from .helpers.process_models import ProcessStartResponse, ProcessStartRequest, StartCallbackType
|
|
10
|
+
from .helpers.external_tasks import FetchAndLockRequestPayload, ExternalTask, FinishExternalTaskRequestPayload
|
|
11
|
+
from .helpers.flow_node_instances import FlowNodeInstanceHandler, FlowNodeInstanceResponse, FlowNodeInstancesQuery
|
|
12
|
+
from .helpers.manual_tasks import ManualTaskHandler, ManualTaskQuery, ManualTaskResponse
|
|
13
|
+
from .helpers.user_tasks import UserTaskHandler, UserTaskQuery, UserTaskResponse, ReserveUserTaskRequest
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base client for ProcessCube® API handlers.
|
|
3
|
+
|
|
4
|
+
This module provides the base HTTP client functionality used by all handler classes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Callable, Dict, Optional, Protocol
|
|
10
|
+
from urllib.parse import urljoin
|
|
11
|
+
|
|
12
|
+
import requests
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IsDataclass(Protocol):
|
|
16
|
+
"""Protocol for dataclass type checking."""
|
|
17
|
+
|
|
18
|
+
__dataclass_fields__: Dict
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseClient:
|
|
22
|
+
"""
|
|
23
|
+
Base HTTP client for ProcessCube® API communication.
|
|
24
|
+
|
|
25
|
+
This class provides low-level HTTP methods (GET, POST, PUT, DELETE) with
|
|
26
|
+
automatic authentication header injection.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
url: Base URL of the ProcessCube® Engine
|
|
30
|
+
identity: Optional callable that returns identity dict with 'token' key
|
|
31
|
+
api_version: API version to use (default: "v1")
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
url: str,
|
|
37
|
+
identity: Optional[Callable[[], Dict[str, str]]] = None,
|
|
38
|
+
api_version: str = "v1",
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Initialize the BaseClient.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
url: Base URL of the ProcessCube® Engine (e.g., "http://localhost:56100")
|
|
45
|
+
identity: Optional callable returning dict with 'token' key.
|
|
46
|
+
If None, uses default dummy token: ZHVtbXlfdG9rZW4=
|
|
47
|
+
api_version: API version to use (default: "v1")
|
|
48
|
+
"""
|
|
49
|
+
self._base_url = url.rstrip("/")
|
|
50
|
+
self._api_version = api_version
|
|
51
|
+
self.logger = logging.getLogger(__name__)
|
|
52
|
+
|
|
53
|
+
if identity is not None:
|
|
54
|
+
self._identity = identity
|
|
55
|
+
else:
|
|
56
|
+
# Default identity with dummy token from Swagger spec
|
|
57
|
+
self._identity = lambda: {"token": "ZHVtbXlfdG9rZW4="}
|
|
58
|
+
|
|
59
|
+
def _build_url(self, path: str) -> str:
|
|
60
|
+
"""
|
|
61
|
+
Build the full API URL from a path.
|
|
62
|
+
|
|
63
|
+
According to Swagger spec, the API is at:
|
|
64
|
+
/atlas_engine/api/v1/...
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
path: API endpoint path (e.g., "process_models")
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Full URL to the API endpoint
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> client._build_url("process_models")
|
|
74
|
+
'http://localhost:56100/atlas_engine/api/v1/process_models'
|
|
75
|
+
"""
|
|
76
|
+
# Remove leading slash from path
|
|
77
|
+
path = path.lstrip("/")
|
|
78
|
+
|
|
79
|
+
# Build API path according to Swagger spec
|
|
80
|
+
api_path = f"atlas_engine/api/{self._api_version}/{path}"
|
|
81
|
+
|
|
82
|
+
# Use urljoin for proper URL construction
|
|
83
|
+
return urljoin(self._base_url + "/", api_path)
|
|
84
|
+
|
|
85
|
+
def do_get(self, path: str, options: Optional[Dict] = None) -> Any:
|
|
86
|
+
"""
|
|
87
|
+
Execute a GET request.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
path: API endpoint path
|
|
91
|
+
options: Optional request options (can contain 'headers')
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Parsed JSON response
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
requests.HTTPError: On HTTP errors
|
|
98
|
+
"""
|
|
99
|
+
options = options or {}
|
|
100
|
+
headers = self._get_default_headers()
|
|
101
|
+
headers.update(options.get("headers", {}))
|
|
102
|
+
headers.update(self._get_auth_headers())
|
|
103
|
+
|
|
104
|
+
request_url = self._build_url(path)
|
|
105
|
+
|
|
106
|
+
self.logger.debug(f"GET {request_url}")
|
|
107
|
+
response = requests.get(request_url, headers=headers)
|
|
108
|
+
response.raise_for_status()
|
|
109
|
+
|
|
110
|
+
return response.json()
|
|
111
|
+
|
|
112
|
+
def do_delete(self, path: str, options: Optional[Dict] = None) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Execute a DELETE request.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
path: API endpoint path
|
|
118
|
+
options: Optional request options (can contain 'headers')
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
requests.HTTPError: On HTTP errors
|
|
122
|
+
"""
|
|
123
|
+
options = options or {}
|
|
124
|
+
headers = self._get_default_headers()
|
|
125
|
+
headers.update(options.get("headers", {}))
|
|
126
|
+
headers.update(self._get_auth_headers())
|
|
127
|
+
|
|
128
|
+
request_url = self._build_url(path)
|
|
129
|
+
|
|
130
|
+
self.logger.debug(f"DELETE {request_url}")
|
|
131
|
+
response = requests.delete(request_url, headers=headers)
|
|
132
|
+
response.raise_for_status()
|
|
133
|
+
|
|
134
|
+
def do_post(
|
|
135
|
+
self, path: str, payload: IsDataclass, options: Optional[Dict] = None
|
|
136
|
+
) -> Any:
|
|
137
|
+
"""
|
|
138
|
+
Execute a POST request.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
path: API endpoint path
|
|
142
|
+
payload: Dataclass payload to send
|
|
143
|
+
options: Optional request options (can contain 'headers')
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Parsed JSON response if status is 200, empty dict otherwise
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
requests.HTTPError: On HTTP errors
|
|
150
|
+
"""
|
|
151
|
+
options = options or {}
|
|
152
|
+
headers = self._get_default_headers()
|
|
153
|
+
headers.update(options.get("headers", {}))
|
|
154
|
+
headers.update(self._get_auth_headers())
|
|
155
|
+
|
|
156
|
+
request_url = self._build_url(path)
|
|
157
|
+
json_payload = json.dumps(payload)
|
|
158
|
+
|
|
159
|
+
self.logger.debug(f"POST {request_url}")
|
|
160
|
+
response = requests.post(request_url, json_payload, headers=headers)
|
|
161
|
+
response.raise_for_status()
|
|
162
|
+
|
|
163
|
+
if response.status_code == 200:
|
|
164
|
+
return response.json()
|
|
165
|
+
return {}
|
|
166
|
+
|
|
167
|
+
def do_put(
|
|
168
|
+
self, path: str, payload: IsDataclass, options: Optional[Dict] = None
|
|
169
|
+
) -> Any:
|
|
170
|
+
"""
|
|
171
|
+
Execute a PUT request.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
path: API endpoint path
|
|
175
|
+
payload: Dataclass payload to send
|
|
176
|
+
options: Optional request options (can contain 'headers')
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Parsed JSON response if status is 200, empty dict otherwise
|
|
180
|
+
|
|
181
|
+
Raises:
|
|
182
|
+
requests.HTTPError: On HTTP errors
|
|
183
|
+
"""
|
|
184
|
+
options = options or {}
|
|
185
|
+
headers = self._get_default_headers()
|
|
186
|
+
headers.update(options.get("headers", {}))
|
|
187
|
+
headers.update(self._get_auth_headers())
|
|
188
|
+
|
|
189
|
+
request_url = self._build_url(path)
|
|
190
|
+
json_payload = json.dumps(payload)
|
|
191
|
+
|
|
192
|
+
self.logger.debug(f"PUT {request_url}")
|
|
193
|
+
response = requests.put(request_url, json_payload, headers=headers)
|
|
194
|
+
response.raise_for_status()
|
|
195
|
+
|
|
196
|
+
if response.status_code == 200:
|
|
197
|
+
return response.json()
|
|
198
|
+
return {}
|
|
199
|
+
|
|
200
|
+
def _get_auth_headers(self) -> Dict[str, str]:
|
|
201
|
+
"""
|
|
202
|
+
Build authentication headers.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Dict with Authorization header
|
|
206
|
+
"""
|
|
207
|
+
identity = self._get_identity()
|
|
208
|
+
token = identity["token"]
|
|
209
|
+
return {"Authorization": f"Bearer {token}"}
|
|
210
|
+
|
|
211
|
+
def _get_default_headers(self) -> Dict[str, str]:
|
|
212
|
+
"""
|
|
213
|
+
Get default headers for all requests.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
Dict with default headers
|
|
217
|
+
"""
|
|
218
|
+
return {"Content-Type": "application/json"}
|
|
219
|
+
|
|
220
|
+
def _get_identity(self) -> Dict[str, str]:
|
|
221
|
+
"""
|
|
222
|
+
Get the current identity.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Identity dict with 'token' key
|
|
226
|
+
"""
|
|
227
|
+
identity = self._identity
|
|
228
|
+
|
|
229
|
+
if callable(self._identity):
|
|
230
|
+
identity = self._identity()
|
|
231
|
+
|
|
232
|
+
return identity
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
__all__ = ["BaseClient", "IsDataclass"]
|