uipath 2.0.0.dev3__py3-none-any.whl → 2.0.1.dev1__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.

Potentially problematic release.


This version of uipath might be problematic. Click here for more details.

Files changed (76) hide show
  1. uipath/__init__.py +24 -0
  2. uipath/_cli/README.md +11 -0
  3. uipath/_cli/__init__.py +54 -0
  4. uipath/_cli/_auth/_auth_server.py +165 -0
  5. uipath/_cli/_auth/_models.py +51 -0
  6. uipath/_cli/_auth/_oidc_utils.py +69 -0
  7. uipath/_cli/_auth/_portal_service.py +163 -0
  8. uipath/_cli/_auth/_utils.py +51 -0
  9. uipath/_cli/_auth/auth_config.json +6 -0
  10. uipath/_cli/_auth/index.html +167 -0
  11. uipath/_cli/_auth/localhost.crt +25 -0
  12. uipath/_cli/_auth/localhost.key +27 -0
  13. uipath/_cli/_runtime/_contracts.py +429 -0
  14. uipath/_cli/_runtime/_logging.py +193 -0
  15. uipath/_cli/_runtime/_runtime.py +264 -0
  16. uipath/_cli/_templates/.psmdcp.template +9 -0
  17. uipath/_cli/_templates/.rels.template +5 -0
  18. uipath/_cli/_templates/[Content_Types].xml.template +9 -0
  19. uipath/_cli/_templates/main.py.template +25 -0
  20. uipath/_cli/_templates/package.nuspec.template +10 -0
  21. uipath/_cli/_utils/_common.py +24 -0
  22. uipath/_cli/_utils/_input_args.py +126 -0
  23. uipath/_cli/_utils/_parse_ast.py +542 -0
  24. uipath/_cli/cli_auth.py +97 -0
  25. uipath/_cli/cli_deploy.py +13 -0
  26. uipath/_cli/cli_init.py +113 -0
  27. uipath/_cli/cli_new.py +76 -0
  28. uipath/_cli/cli_pack.py +337 -0
  29. uipath/_cli/cli_publish.py +113 -0
  30. uipath/_cli/cli_run.py +133 -0
  31. uipath/_cli/middlewares.py +113 -0
  32. uipath/_config.py +6 -0
  33. uipath/_execution_context.py +83 -0
  34. uipath/_folder_context.py +62 -0
  35. uipath/_models/__init__.py +37 -0
  36. uipath/_models/action_schema.py +26 -0
  37. uipath/_models/actions.py +64 -0
  38. uipath/_models/assets.py +48 -0
  39. uipath/_models/connections.py +51 -0
  40. uipath/_models/context_grounding.py +18 -0
  41. uipath/_models/context_grounding_index.py +60 -0
  42. uipath/_models/exceptions.py +6 -0
  43. uipath/_models/interrupt_models.py +28 -0
  44. uipath/_models/job.py +66 -0
  45. uipath/_models/llm_gateway.py +101 -0
  46. uipath/_models/processes.py +48 -0
  47. uipath/_models/queues.py +167 -0
  48. uipath/_services/__init__.py +26 -0
  49. uipath/_services/_base_service.py +250 -0
  50. uipath/_services/actions_service.py +271 -0
  51. uipath/_services/api_client.py +89 -0
  52. uipath/_services/assets_service.py +257 -0
  53. uipath/_services/buckets_service.py +268 -0
  54. uipath/_services/connections_service.py +185 -0
  55. uipath/_services/connections_service.pyi +50 -0
  56. uipath/_services/context_grounding_service.py +402 -0
  57. uipath/_services/folder_service.py +49 -0
  58. uipath/_services/jobs_service.py +265 -0
  59. uipath/_services/llm_gateway_service.py +311 -0
  60. uipath/_services/processes_service.py +168 -0
  61. uipath/_services/queues_service.py +314 -0
  62. uipath/_uipath.py +98 -0
  63. uipath/_utils/__init__.py +17 -0
  64. uipath/_utils/_endpoint.py +79 -0
  65. uipath/_utils/_infer_bindings.py +30 -0
  66. uipath/_utils/_logs.py +15 -0
  67. uipath/_utils/_request_override.py +18 -0
  68. uipath/_utils/_request_spec.py +23 -0
  69. uipath/_utils/_user_agent.py +16 -0
  70. uipath/_utils/constants.py +25 -0
  71. uipath/py.typed +0 -0
  72. {uipath-2.0.0.dev3.dist-info → uipath-2.0.1.dev1.dist-info}/METADATA +2 -3
  73. uipath-2.0.1.dev1.dist-info/RECORD +75 -0
  74. uipath-2.0.0.dev3.dist-info/RECORD +0 -4
  75. {uipath-2.0.0.dev3.dist-info → uipath-2.0.1.dev1.dist-info}/WHEEL +0 -0
  76. {uipath-2.0.0.dev3.dist-info → uipath-2.0.1.dev1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,257 @@
1
+ from typing import Dict, Optional
2
+
3
+ from httpx import Response
4
+
5
+ from .._config import Config
6
+ from .._execution_context import ExecutionContext
7
+ from .._folder_context import FolderContext
8
+ from .._models import UserAsset
9
+ from .._utils import Endpoint, RequestSpec, header_folder, infer_bindings
10
+ from ._base_service import BaseService
11
+
12
+
13
+ class AssetsService(FolderContext, BaseService):
14
+ """Service for managing UiPath assets.
15
+
16
+ Assets are key-value pairs that can be used to store configuration data,
17
+ credentials, and other settings used by automation processes.
18
+ """
19
+
20
+ def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
21
+ super().__init__(config=config, execution_context=execution_context)
22
+
23
+ @infer_bindings()
24
+ def retrieve(
25
+ self,
26
+ name: str,
27
+ *,
28
+ folder_key: Optional[str] = None,
29
+ folder_path: Optional[str] = None,
30
+ ) -> UserAsset:
31
+ """Retrieve an asset by its name.
32
+
33
+ Related Activity: [Get Asset](https://docs.uipath.com/activities/other/latest/workflow/get-robot-asset)
34
+
35
+ Args:
36
+ name (str): The name of the asset.
37
+ folder_key (Optional[str]): The key of the folder to execute the process in. Override the default one set in the SDK config.
38
+ folder_path (Optional[str]): The path of the folder to execute the process in. Override the default one set in the SDK config.
39
+
40
+ Returns:
41
+ UserAsset: The asset data.
42
+
43
+ Examples:
44
+ ```python
45
+ from uipath import UiPath
46
+
47
+ client = UiPath()
48
+
49
+ client.assets.retrieve(name="MyAsset")
50
+ ```
51
+ """
52
+ spec = self._retrieve_spec(name, folder_key=folder_key, folder_path=folder_path)
53
+ response = self.request(
54
+ spec.method, url=spec.endpoint, content=spec.content, headers=spec.headers
55
+ )
56
+
57
+ return UserAsset.model_validate(response.json())
58
+
59
+ async def retrieve_async(
60
+ self,
61
+ name: str,
62
+ *,
63
+ folder_key: Optional[str] = None,
64
+ folder_path: Optional[str] = None,
65
+ ) -> UserAsset:
66
+ """Asynchronously retrieve an asset by its name.
67
+
68
+ Related Activity: [Get Asset](https://docs.uipath.com/activities/other/latest/workflow/get-robot-asset)
69
+
70
+ Args:
71
+ name (str): The name of the asset.
72
+ folder_key (Optional[str]): The key of the folder to execute the process in. Override the default one set in the SDK config.
73
+ folder_path (Optional[str]): The path of the folder to execute the process in. Override the default one set in the SDK config.
74
+
75
+ Returns:
76
+ UserAsset: The asset data.
77
+ """
78
+ spec = self._retrieve_spec(name, folder_key=folder_key, folder_path=folder_path)
79
+ response = await self.request_async(
80
+ spec.method, url=spec.endpoint, content=spec.content, headers=spec.headers
81
+ )
82
+
83
+ return UserAsset.model_validate(response.json())
84
+
85
+ @infer_bindings()
86
+ def retrieve_credential(
87
+ self,
88
+ name: str,
89
+ *,
90
+ folder_key: Optional[str] = None,
91
+ folder_path: Optional[str] = None,
92
+ ) -> Optional[str]:
93
+ """Gets a specified Orchestrator credential.
94
+
95
+ The robot id is retrieved from the execution context (`UIPATH_ROBOT_KEY` environment variable)
96
+
97
+ Related Activity: [Get Credential](https://docs.uipath.com/activities/other/latest/workflow/get-robot-credential)
98
+
99
+ Args:
100
+ name (str): The name of the credential asset.
101
+ folder_key (Optional[str]): The key of the folder to execute the process in. Override the default one set in the SDK config.
102
+ folder_path (Optional[str]): The path of the folder to execute the process in. Override the default one set in the SDK config.
103
+
104
+ Returns:
105
+ Optional[str]: The decrypted credential password.
106
+ """
107
+ spec = self._retrieve_spec(name, folder_key=folder_key, folder_path=folder_path)
108
+
109
+ response = self.request(
110
+ spec.method,
111
+ url=spec.endpoint,
112
+ content=spec.content,
113
+ headers=spec.headers,
114
+ )
115
+
116
+ user_asset = UserAsset.model_validate(response.json())
117
+
118
+ return user_asset.credential_password
119
+
120
+ @infer_bindings()
121
+ async def retrieve_credential_async(
122
+ self,
123
+ name: str,
124
+ *,
125
+ folder_key: Optional[str] = None,
126
+ folder_path: Optional[str] = None,
127
+ ) -> Optional[str]:
128
+ """Asynchronously gets a specified Orchestrator credential.
129
+
130
+ The robot id is retrieved from the execution context (`UIPATH_ROBOT_KEY` environment variable)
131
+
132
+ Related Activity: [Get Credential](https://docs.uipath.com/activities/other/latest/workflow/get-robot-credential)
133
+
134
+ Args:
135
+ name (str): The name of the credential asset.
136
+ folder_key (Optional[str]): The key of the folder to execute the process in. Override the default one set in the SDK config.
137
+ folder_path (Optional[str]): The path of the folder to execute the process in. Override the default one set in the SDK config.
138
+
139
+ Returns:
140
+ Optional[str]: The decrypted credential password.
141
+
142
+ """
143
+ spec = self._retrieve_spec(name, folder_key=folder_key, folder_path=folder_path)
144
+
145
+ response = await self.request_async(
146
+ spec.method,
147
+ url=spec.endpoint,
148
+ content=spec.content,
149
+ headers=spec.headers,
150
+ )
151
+
152
+ user_asset = UserAsset.model_validate(response.json())
153
+
154
+ return user_asset.credential_password
155
+
156
+ def update(
157
+ self,
158
+ robot_asset: UserAsset,
159
+ *,
160
+ folder_key: Optional[str] = None,
161
+ folder_path: Optional[str] = None,
162
+ ) -> Response:
163
+ """Update an asset's value.
164
+
165
+ Related Activity: [Set Asset](https://docs.uipath.com/activities/other/latest/workflow/set-asset)
166
+
167
+ Args:
168
+ robot_asset (UserAsset): The asset object containing the updated values.
169
+
170
+ Returns:
171
+ Response: The HTTP response confirming the update.
172
+ """
173
+ spec = self._update_spec(
174
+ robot_asset, folder_key=folder_key, folder_path=folder_path
175
+ )
176
+
177
+ return self.request(
178
+ spec.method,
179
+ url=spec.endpoint,
180
+ content=spec.content,
181
+ headers=spec.headers,
182
+ )
183
+
184
+ async def update_async(
185
+ self,
186
+ robot_asset: UserAsset,
187
+ *,
188
+ folder_key: Optional[str] = None,
189
+ folder_path: Optional[str] = None,
190
+ ) -> Response:
191
+ """Asynchronously update an asset's value.
192
+
193
+ Related Activity: [Set Asset](https://docs.uipath.com/activities/other/latest/workflow/set-asset)
194
+
195
+ Args:
196
+ robot_asset (UserAsset): The asset object containing the updated values.
197
+
198
+ Returns:
199
+ Response: The HTTP response confirming the update.
200
+ """
201
+ spec = self._update_spec(
202
+ robot_asset, folder_key=folder_key, folder_path=folder_path
203
+ )
204
+
205
+ return await self.request_async(
206
+ spec.method,
207
+ url=spec.endpoint,
208
+ content=spec.content,
209
+ headers=spec.headers,
210
+ )
211
+
212
+ @property
213
+ def custom_headers(self) -> Dict[str, str]:
214
+ return self.folder_headers
215
+
216
+ def _retrieve_spec(
217
+ self,
218
+ name: str,
219
+ *,
220
+ folder_key: Optional[str] = None,
221
+ folder_path: Optional[str] = None,
222
+ ) -> RequestSpec:
223
+ return RequestSpec(
224
+ method="POST",
225
+ endpoint=Endpoint(
226
+ "/orchestrator_/odata/Assets/UiPath.Server.Configuration.OData.GetRobotAssetByNameForRobotKey"
227
+ ),
228
+ content=str(
229
+ {"assetName": name, "robotKey": self._execution_context.robot_key}
230
+ ),
231
+ headers={
232
+ **header_folder(folder_key, folder_path),
233
+ },
234
+ )
235
+
236
+ def _update_spec(
237
+ self,
238
+ robot_asset: UserAsset,
239
+ *,
240
+ folder_key: Optional[str] = None,
241
+ folder_path: Optional[str] = None,
242
+ ) -> RequestSpec:
243
+ return RequestSpec(
244
+ method="POST",
245
+ endpoint=Endpoint(
246
+ "/orchestrator_/odata/Assets/UiPath.Server.Configuration.OData.SetRobotAssetByRobotKey"
247
+ ),
248
+ content=str(
249
+ {
250
+ "robotKey": self._execution_context.robot_key,
251
+ "robotAsset": robot_asset,
252
+ }
253
+ ),
254
+ headers={
255
+ **header_folder(folder_key, folder_path),
256
+ },
257
+ )
@@ -0,0 +1,268 @@
1
+ from typing import Any, Dict, Optional, Union
2
+
3
+ from httpx import request
4
+
5
+ from .._config import Config
6
+ from .._execution_context import ExecutionContext
7
+ from .._folder_context import FolderContext
8
+ from .._utils import Endpoint, RequestSpec, infer_bindings
9
+ from ._base_service import BaseService
10
+
11
+
12
+ class BucketsService(FolderContext, BaseService):
13
+ """Service for managing UiPath storage buckets.
14
+
15
+ Buckets are cloud storage containers that can be used to store and manage files
16
+ used by automation processes.
17
+ """
18
+
19
+ def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
20
+ super().__init__(config=config, execution_context=execution_context)
21
+
22
+ def download(
23
+ self,
24
+ bucket_key: str,
25
+ blob_file_path: str,
26
+ destination_path: str,
27
+ ) -> None:
28
+ """Download a file from a bucket.
29
+
30
+ Args:
31
+ bucket_key: The key of the bucket
32
+ blob_file_path: The path to the file in the bucket
33
+ destination_path: The local path where the file will be saved
34
+ """
35
+ bucket = self.retrieve_by_key(bucket_key)
36
+ bucket_id = bucket["Id"]
37
+
38
+ endpoint = Endpoint(
39
+ f"/orchestrator_/odata/Buckets({bucket_id})/UiPath.Server.Configuration.OData.GetReadUri"
40
+ )
41
+
42
+ result = self.request("GET", endpoint, params={"path": blob_file_path}).json()
43
+ read_uri = result["Uri"]
44
+
45
+ headers = {
46
+ key: value
47
+ for key, value in zip(
48
+ result["Headers"]["Keys"], result["Headers"]["Values"], strict=False
49
+ )
50
+ }
51
+
52
+ with open(destination_path, "wb") as file:
53
+ # the self.request adds auth bearer token
54
+ if result["RequiresAuth"]:
55
+ file_content = self.request("GET", read_uri, headers=headers).content
56
+ else:
57
+ file_content = request("GET", read_uri, headers=headers).content
58
+ file.write(file_content)
59
+
60
+ def upload(
61
+ self,
62
+ *,
63
+ bucket_key: Optional[str] = None,
64
+ bucket_name: Optional[str] = None,
65
+ blob_file_path: str,
66
+ content_type: str,
67
+ source_path: str,
68
+ ) -> None:
69
+ """Upload a file to a bucket.
70
+
71
+ Args:
72
+ bucket_key: The key of the bucket
73
+ bucket_name: The name of the bucket
74
+ blob_file_path: The path where the file will be stored in the bucket
75
+ content_type: The MIME type of the file
76
+ source_path: The local path of the file to upload
77
+ """
78
+ if bucket_key:
79
+ bucket = self.retrieve_by_key(bucket_key)
80
+ elif bucket_name:
81
+ bucket = self.retrieve(bucket_name)
82
+ else:
83
+ raise ValueError("Must specify a bucket name or bucket key")
84
+
85
+ bucket_id = bucket["Id"]
86
+
87
+ endpoint = Endpoint(
88
+ f"/orchestrator_/odata/Buckets({bucket_id})/UiPath.Server.Configuration.OData.GetWriteUri"
89
+ )
90
+
91
+ result = self.request(
92
+ "GET",
93
+ endpoint,
94
+ params={"path": blob_file_path, "contentType": content_type},
95
+ ).json()
96
+ write_uri = result["Uri"]
97
+
98
+ headers = {
99
+ key: value
100
+ for key, value in zip(
101
+ result["Headers"]["Keys"], result["Headers"]["Values"], strict=False
102
+ )
103
+ }
104
+
105
+ with open(source_path, "rb") as file:
106
+ if result["RequiresAuth"]:
107
+ self.request("PUT", write_uri, headers=headers, files={"file": file})
108
+ else:
109
+ request("PUT", write_uri, headers=headers, files={"file": file})
110
+
111
+ def upload_from_memory(
112
+ self,
113
+ *,
114
+ bucket_key: Optional[str] = None,
115
+ bucket_name: Optional[str] = None,
116
+ blob_file_path: str,
117
+ content_type: str,
118
+ content: Union[str, bytes],
119
+ ) -> None:
120
+ """Upload content from memory to a bucket.
121
+
122
+ Args:
123
+ bucket_key: The key of the bucket
124
+ bucket_name: The name of the bucket
125
+ blob_file_path: The path where the content will be stored in the bucket
126
+ content_type: The MIME type of the content
127
+ content: The content to upload (string or bytes)
128
+ """
129
+ if bucket_key:
130
+ bucket = self.retrieve_by_key(bucket_key)
131
+ elif bucket_name:
132
+ bucket = self.retrieve(bucket_name)
133
+ else:
134
+ raise ValueError("Must specify a bucket name or bucket key")
135
+
136
+ bucket_id = bucket["Id"]
137
+
138
+ endpoint = Endpoint(
139
+ f"/orchestrator_/odata/Buckets({bucket_id})/UiPath.Server.Configuration.OData.GetWriteUri"
140
+ )
141
+
142
+ result = self.request(
143
+ "GET",
144
+ endpoint,
145
+ params={"path": blob_file_path, "contentType": content_type},
146
+ ).json()
147
+ write_uri = result["Uri"]
148
+
149
+ headers = {
150
+ key: value
151
+ for key, value in zip(
152
+ result["Headers"]["Keys"], result["Headers"]["Values"], strict=False
153
+ )
154
+ }
155
+
156
+ # Convert string to bytes if needed
157
+ if isinstance(content, str):
158
+ content = content.encode("utf-8")
159
+
160
+ if result["RequiresAuth"]:
161
+ self.request("PUT", write_uri, headers=headers, content=content)
162
+ else:
163
+ request("PUT", write_uri, headers=headers, content=content)
164
+
165
+ @infer_bindings()
166
+ def retrieve(self, name: str) -> Any:
167
+ """Retrieve bucket information by its name.
168
+
169
+ Args:
170
+ name (str): The name of the bucket to retrieve.
171
+
172
+ Returns:
173
+ Response: The HTTP response containing the bucket details, including
174
+ its ID, name, and configuration.
175
+ """
176
+ spec = self._retrieve_spec(name)
177
+
178
+ try:
179
+ response = self.request(
180
+ spec.method,
181
+ url=spec.endpoint,
182
+ params=spec.params,
183
+ )
184
+ except Exception as e:
185
+ raise Exception(f"Bucket with name {name} not found") from e
186
+
187
+ return response.json()["value"][0]
188
+
189
+ @infer_bindings()
190
+ async def retrieve_async(self, name: str) -> Any:
191
+ """Asynchronously retrieve bucket information by its name.
192
+
193
+ Args:
194
+ name (str): The name of the bucket to retrieve.
195
+
196
+ Returns:
197
+ Response: The HTTP response containing the bucket details, including
198
+ its ID, name, and configuration.
199
+ """
200
+ spec = self._retrieve_spec(name)
201
+
202
+ try:
203
+ response = await self.request_async(
204
+ spec.method,
205
+ url=spec.endpoint,
206
+ params=spec.params,
207
+ )
208
+ except Exception as e:
209
+ raise Exception(f"Bucket with name {name} not found") from e
210
+
211
+ return response.json()["value"][0]
212
+
213
+ def retrieve_by_key(self, key: str) -> Any:
214
+ """Retrieve bucket information by its key.
215
+
216
+ Args:
217
+ key (str): The key of the bucket
218
+
219
+ Returns:
220
+ Response: The HTTP response containing the bucket details, including
221
+ its ID, name, and configuration.
222
+ """
223
+ spec = self._retrieve_by_key_spec(key)
224
+
225
+ try:
226
+ response = self.request(spec.method, url=spec.endpoint)
227
+ except Exception as e:
228
+ raise Exception(f"Bucket with key {key} not found") from e
229
+
230
+ return response.json()
231
+
232
+ async def retrieve_by_key_async(self, key: str) -> Any:
233
+ """Asynchronously retrieve bucket information by its key.
234
+
235
+ Args:
236
+ key (str): The key of the bucket
237
+
238
+ Returns:
239
+ Response: The HTTP response containing the bucket details, including
240
+ its ID, name, and configuration.
241
+ """
242
+ spec = self._retrieve_by_key_spec(key)
243
+
244
+ try:
245
+ response = await self.request_async(spec.method, url=spec.endpoint)
246
+ except Exception as e:
247
+ raise Exception(f"Bucket with key {key} not found") from e
248
+
249
+ return response.json()
250
+
251
+ @property
252
+ def custom_headers(self) -> Dict[str, str]:
253
+ return self.folder_headers
254
+
255
+ def _retrieve_spec(self, name: str) -> RequestSpec:
256
+ return RequestSpec(
257
+ method="GET",
258
+ endpoint=Endpoint("/orchestrator_/odata/Buckets"),
259
+ params={"$filter": f"Name eq '{name}'", "$top": 1},
260
+ )
261
+
262
+ def _retrieve_by_key_spec(self, key: str) -> RequestSpec:
263
+ return RequestSpec(
264
+ method="GET",
265
+ endpoint=Endpoint(
266
+ f"/orchestrator_/odata/Buckets/UiPath.Server.Configuration.OData.GetByKey(identifier={key})"
267
+ ),
268
+ )
@@ -0,0 +1,185 @@
1
+ import importlib
2
+ import logging
3
+ from typing import Any, Dict, Protocol, TypeVar, Union
4
+
5
+ from .._config import Config
6
+ from .._execution_context import ExecutionContext
7
+ from .._models import Connection, ConnectionToken
8
+ from .._utils import Endpoint, RequestSpec
9
+ from .._utils.constants import ENTRYPOINT
10
+ from ._base_service import BaseService
11
+
12
+ T_co = TypeVar("T_co", covariant=True)
13
+
14
+ logger: logging.Logger = logging.getLogger("uipath")
15
+
16
+
17
+ class PluginNotFoundError(AttributeError):
18
+ """Raised when a plugin is not installed or failed to load."""
19
+
20
+ pass
21
+
22
+
23
+ class Connector(Protocol[T_co]):
24
+ def __call__(self, *, client: Any, instance_id: Union[str, int]) -> T_co: ...
25
+
26
+
27
+ class ConnectionsService(BaseService):
28
+ """Service for managing UiPath external service connections.
29
+
30
+ This service provides methods to retrieve and manage connections to external
31
+ systems and services that your automation processes interact with. It supports
32
+ both direct connection information retrieval and secure token management.
33
+
34
+ The service implements a flexible connector system that allows for type-safe
35
+ instantiation of specific service connectors, making it easier to interact
36
+ with different types of external services.
37
+ """
38
+
39
+ def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
40
+ super().__init__(config=config, execution_context=execution_context)
41
+ self._plugins: Dict[str, Any] = {}
42
+ self._plugins_loaded = False
43
+ self._load_connectors()
44
+
45
+ def __call__(self, connector: Connector[T_co], key: str) -> T_co:
46
+ connection = self.retrieve(key)
47
+ return connector(client=self.client, instance_id=connection.element_instance_id)
48
+
49
+ def __getattr__(self, name: str) -> Any:
50
+ """Get a plugin by name.
51
+
52
+ Args:
53
+ name: The name of the plugin to get
54
+
55
+ Returns:
56
+ The plugin instance
57
+
58
+ Raises:
59
+ PluginNotFoundError: If the plugin is not installed
60
+ ImportError: If the plugin fails to load
61
+ """
62
+ if not self._plugins_loaded:
63
+ self._load_connectors()
64
+
65
+ if name in self._plugins:
66
+ return self._plugins[name]
67
+
68
+ try:
69
+ plugin: Any = getattr(self.client, name)
70
+ self._plugins[name] = plugin
71
+ return plugin
72
+ except AttributeError as e:
73
+ raise PluginNotFoundError(f"Plugin '{name}' is not installed") from e
74
+
75
+ def retrieve(self, key: str) -> Connection:
76
+ """Retrieve connection details by its key.
77
+
78
+ This method fetches the configuration and metadata for a connection,
79
+ which can be used to establish communication with an external service.
80
+
81
+ Args:
82
+ key (str): The unique identifier of the connection to retrieve.
83
+
84
+ Returns:
85
+ Connection: The connection details, including configuration parameters
86
+ and authentication information.
87
+ """
88
+ spec = self._retrieve_spec(key)
89
+ response = self.request(spec.method, url=spec.endpoint)
90
+ return Connection.model_validate(response.json())
91
+
92
+ async def retrieve_async(self, key: str) -> Connection:
93
+ """Asynchronously retrieve connection details by its key.
94
+
95
+ This method fetches the configuration and metadata for a connection,
96
+ which can be used to establish communication with an external service.
97
+
98
+ Args:
99
+ key (str): The unique identifier of the connection to retrieve.
100
+
101
+ Returns:
102
+ Connection: The connection details, including configuration parameters
103
+ and authentication information.
104
+ """
105
+ spec = self._retrieve_spec(key)
106
+ response = await self.request_async(spec.method, url=spec.endpoint)
107
+ return Connection.model_validate(response.json())
108
+
109
+ def retrieve_token(self, key: str) -> ConnectionToken:
110
+ """Retrieve an authentication token for a connection.
111
+
112
+ This method obtains a fresh authentication token that can be used to
113
+ communicate with the external service. This is particularly useful for
114
+ services that use token-based authentication.
115
+
116
+ Args:
117
+ key (str): The unique identifier of the connection.
118
+
119
+ Returns:
120
+ ConnectionToken: The authentication token details, including the token
121
+ value and any associated metadata.
122
+ """
123
+ spec = self._retrieve_token_spec(key)
124
+ response = self.request(spec.method, url=spec.endpoint, params=spec.params)
125
+ return ConnectionToken.model_validate(response.json())
126
+
127
+ async def retrieve_token_async(self, key: str) -> ConnectionToken:
128
+ """Asynchronously retrieve an authentication token for a connection.
129
+
130
+ This method obtains a fresh authentication token that can be used to
131
+ communicate with the external service. This is particularly useful for
132
+ services that use token-based authentication.
133
+
134
+ Args:
135
+ key (str): The unique identifier of the connection.
136
+
137
+ Returns:
138
+ ConnectionToken: The authentication token details, including the token
139
+ value and any associated metadata.
140
+ """
141
+ spec = self._retrieve_token_spec(key)
142
+ response = await self.request_async(
143
+ spec.method, url=spec.endpoint, params=spec.params
144
+ )
145
+ return ConnectionToken.model_validate(response.json())
146
+
147
+ def _retrieve_spec(self, key: str) -> RequestSpec:
148
+ return RequestSpec(
149
+ method="GET",
150
+ endpoint=Endpoint(f"/connections_/api/v1/Connections/{key}"),
151
+ )
152
+
153
+ def _retrieve_token_spec(self, key: str) -> RequestSpec:
154
+ return RequestSpec(
155
+ method="GET",
156
+ endpoint=Endpoint(f"/connections_/api/v1/Connections/{key}/token"),
157
+ params={"type": "direct"},
158
+ )
159
+
160
+ def _load_connectors(self) -> None:
161
+ """Load all available connector plugins.
162
+
163
+ Raises:
164
+ ImportError: If a plugin fails to load
165
+ """
166
+ try:
167
+ entry_points: Any = importlib.metadata.entry_points()
168
+ if hasattr(entry_points, "select"):
169
+ connectors = list(entry_points.select(group=ENTRYPOINT))
170
+ else:
171
+ connectors = list(entry_points.get(ENTRYPOINT, []))
172
+
173
+ for entry_point in connectors:
174
+ try:
175
+ register_func = entry_point.load()
176
+ register_func(self)
177
+ except Exception as e:
178
+ logger.error(
179
+ f"[ERROR] Failed to load plugin {entry_point.name}: {str(e)}"
180
+ )
181
+
182
+ self._plugins_loaded = True
183
+ except Exception as e:
184
+ self._plugins_loaded = False
185
+ raise ImportError(f"Failed to load plugins: {str(e)}") from e