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.
Files changed (68) hide show
  1. {processcube_etw_library-2026.1.22.131318b0/src/processcube_etw_library.egg-info → processcube_etw_library-2026.1.29.71849b0}/PKG-INFO +13 -11
  2. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/README.md +10 -9
  3. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/pyproject.toml +5 -2
  4. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/__init__.py +7 -5
  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
  6. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/etw_app.py +18 -5
  7. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/built_in.py +3 -4
  8. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/check.py +14 -4
  9. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/identity_provider.py +0 -2
  10. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/__init__.py +22 -0
  11. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/app_info/__init__.py +1 -0
  12. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/app_info/app_info_client.py +36 -0
  13. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/client_factory.py +37 -0
  14. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/__init__.py +2 -0
  15. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/__init__.py +13 -0
  16. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/base_client.py +235 -0
  17. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/client.py +816 -0
  18. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/__init__.py +0 -0
  19. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/application_info.py +34 -0
  20. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/data_object_instances.py +61 -0
  21. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/empty_tasks.py +86 -0
  22. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/events.py +39 -0
  23. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/external_tasks.py +142 -0
  24. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/flow_node_instances.py +80 -0
  25. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/manual_tasks.py +87 -0
  26. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/process_definitions.py +46 -0
  27. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/process_instances.py +96 -0
  28. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/process_models.py +51 -0
  29. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/api/helpers/user_tasks.py +130 -0
  30. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/base_client.py +175 -0
  31. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/core/loop_helper.py +200 -0
  32. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/event/__init__.py +1 -0
  33. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/event/event_client.py +43 -0
  34. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/__init__.py +3 -0
  35. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/client_wrapper.py +28 -0
  36. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/external_task_client.py +195 -0
  37. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/external_task_worker.py +205 -0
  38. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/external_task/functional_error.py +17 -0
  39. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/flow_node_instance/__init__.py +1 -0
  40. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/flow_node_instance/flow_node_instance_client.py +43 -0
  41. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/notification/__init__.py +1 -0
  42. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/notification/notification_client.py +103 -0
  43. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_definition/__init__.py +2 -0
  44. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_definition/process_definition_client.py +94 -0
  45. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_definition/start_callback_type.py +6 -0
  46. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_instance/__init__.py +1 -0
  47. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/process_instance/process_instance_client.py +32 -0
  48. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/user_task/__init__.py +1 -0
  49. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library/processcube_client/user_task/user_task_client.py +63 -0
  50. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/settings.py +35 -9
  51. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library.egg-info}/PKG-INFO +13 -11
  52. processcube_etw_library-2026.1.29.71849b0/src/processcube_etw_library.egg-info/SOURCES.txt +65 -0
  53. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library.egg-info/requires.txt +2 -1
  54. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/uv.lock +11 -506
  55. processcube_etw_library-2026.1.22.131318b0/src/processcube_etw_library.egg-info/SOURCES.txt +0 -25
  56. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/.github/workflows/build_and_publish.yml +0 -0
  57. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/.gitignore +0 -0
  58. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/.python-version +0 -0
  59. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/setup.cfg +0 -0
  60. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/__init__.py +0 -0
  61. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/handlers.py +0 -0
  62. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/models.py +0 -0
  63. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/registry.py +0 -0
  64. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/health/routes.py +0 -0
  65. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/server_config.py +0 -0
  66. {processcube_etw_library-2026.1.22.131318b0 → processcube_etw_library-2026.1.29.71849b0}/src/processcube_etw_library/typed_handler.py +0 -0
  67. {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
  68. {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.22.131318b0
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 | Default | Description |
31
- | ------------------------------------ | -------------------------------------- | ------------------------------------------------ |
32
- | `PROCESSCUBE_ENGINE_URL` | `http://localhost:56000` | URL of the ProcessCube Engine |
33
- | `PROCESSCUBE_AUTHORITY_URL` | Auto-discovered from engine | URL of the ProcessCube Authority (OAuth server) |
34
- | `PROCESSCUBE_ETW_CLIENT_ID` | `test_etw` | OAuth client ID for the External Task Worker |
35
- | `PROCESSCUBE_ETW_CLIENT_SECRET` | `3ef62eb3-fe49-4c2c-ba6f-73e4d234321b` | OAuth client secret for the External Task Worker |
36
- | `PROCESSCUBE_ETW_CLIENT_SCOPES` | `engine_etw` | OAuth scopes for the External Task Worker |
37
- | `MAX_GET_OAUTH_ACCESS_TOKEN_RETRIES` | `10` | Maximum retries for obtaining OAuth access token |
38
- | `ENVIRONMENT` | `development` | Environment mode (`development` or `production`) |
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
 
@@ -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 | 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
- | `MAX_GET_OAUTH_ACCESS_TOKEN_RETRIES` | `10` | Maximum retries for obtaining OAuth access token |
25
- | `ENVIRONMENT` | `development` | Environment mode (`development` or `production`) |
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
- from .health import (
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, 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 = settings.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
- self._etw_client.subscribe_to_external_task_topic(topic, handler, **options)
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(topic, wrapper, **options)
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(url: str, timeout: float = 5.0) -> HealthCheckCondition:
40
- async def check():
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 True if response.status_code == 200 else False
53
+ return response.status_code == 200
44
54
 
45
- return check
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),
@@ -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,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,2 @@
1
+ from .base_client import BaseClient
2
+ from .loop_helper import LoopHelper, get_or_create_loop, ensure_has_loop
@@ -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"]