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.
- uipath/__init__.py +24 -0
- uipath/_cli/README.md +11 -0
- uipath/_cli/__init__.py +54 -0
- uipath/_cli/_auth/_auth_server.py +165 -0
- uipath/_cli/_auth/_models.py +51 -0
- uipath/_cli/_auth/_oidc_utils.py +69 -0
- uipath/_cli/_auth/_portal_service.py +163 -0
- uipath/_cli/_auth/_utils.py +51 -0
- uipath/_cli/_auth/auth_config.json +6 -0
- uipath/_cli/_auth/index.html +167 -0
- uipath/_cli/_auth/localhost.crt +25 -0
- uipath/_cli/_auth/localhost.key +27 -0
- uipath/_cli/_runtime/_contracts.py +429 -0
- uipath/_cli/_runtime/_logging.py +193 -0
- uipath/_cli/_runtime/_runtime.py +264 -0
- uipath/_cli/_templates/.psmdcp.template +9 -0
- uipath/_cli/_templates/.rels.template +5 -0
- uipath/_cli/_templates/[Content_Types].xml.template +9 -0
- uipath/_cli/_templates/main.py.template +25 -0
- uipath/_cli/_templates/package.nuspec.template +10 -0
- uipath/_cli/_utils/_common.py +24 -0
- uipath/_cli/_utils/_input_args.py +126 -0
- uipath/_cli/_utils/_parse_ast.py +542 -0
- uipath/_cli/cli_auth.py +97 -0
- uipath/_cli/cli_deploy.py +13 -0
- uipath/_cli/cli_init.py +113 -0
- uipath/_cli/cli_new.py +76 -0
- uipath/_cli/cli_pack.py +337 -0
- uipath/_cli/cli_publish.py +113 -0
- uipath/_cli/cli_run.py +133 -0
- uipath/_cli/middlewares.py +113 -0
- uipath/_config.py +6 -0
- uipath/_execution_context.py +83 -0
- uipath/_folder_context.py +62 -0
- uipath/_models/__init__.py +37 -0
- uipath/_models/action_schema.py +26 -0
- uipath/_models/actions.py +64 -0
- uipath/_models/assets.py +48 -0
- uipath/_models/connections.py +51 -0
- uipath/_models/context_grounding.py +18 -0
- uipath/_models/context_grounding_index.py +60 -0
- uipath/_models/exceptions.py +6 -0
- uipath/_models/interrupt_models.py +28 -0
- uipath/_models/job.py +66 -0
- uipath/_models/llm_gateway.py +101 -0
- uipath/_models/processes.py +48 -0
- uipath/_models/queues.py +167 -0
- uipath/_services/__init__.py +26 -0
- uipath/_services/_base_service.py +250 -0
- uipath/_services/actions_service.py +271 -0
- uipath/_services/api_client.py +89 -0
- uipath/_services/assets_service.py +257 -0
- uipath/_services/buckets_service.py +268 -0
- uipath/_services/connections_service.py +185 -0
- uipath/_services/connections_service.pyi +50 -0
- uipath/_services/context_grounding_service.py +402 -0
- uipath/_services/folder_service.py +49 -0
- uipath/_services/jobs_service.py +265 -0
- uipath/_services/llm_gateway_service.py +311 -0
- uipath/_services/processes_service.py +168 -0
- uipath/_services/queues_service.py +314 -0
- uipath/_uipath.py +98 -0
- uipath/_utils/__init__.py +17 -0
- uipath/_utils/_endpoint.py +79 -0
- uipath/_utils/_infer_bindings.py +30 -0
- uipath/_utils/_logs.py +15 -0
- uipath/_utils/_request_override.py +18 -0
- uipath/_utils/_request_spec.py +23 -0
- uipath/_utils/_user_agent.py +16 -0
- uipath/_utils/constants.py +25 -0
- uipath/py.typed +0 -0
- {uipath-2.0.0.dev3.dist-info → uipath-2.0.1.dev1.dist-info}/METADATA +2 -3
- uipath-2.0.1.dev1.dist-info/RECORD +75 -0
- uipath-2.0.0.dev3.dist-info/RECORD +0 -4
- {uipath-2.0.0.dev3.dist-info → uipath-2.0.1.dev1.dist-info}/WHEEL +0 -0
- {uipath-2.0.0.dev3.dist-info → uipath-2.0.1.dev1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from logging import getLogger
|
|
3
|
+
from typing import Any, Union
|
|
4
|
+
|
|
5
|
+
from httpx import (
|
|
6
|
+
URL,
|
|
7
|
+
AsyncClient,
|
|
8
|
+
Client,
|
|
9
|
+
ConnectTimeout,
|
|
10
|
+
Headers,
|
|
11
|
+
Response,
|
|
12
|
+
TimeoutException,
|
|
13
|
+
)
|
|
14
|
+
from tenacity import (
|
|
15
|
+
retry,
|
|
16
|
+
retry_if_exception,
|
|
17
|
+
retry_if_result,
|
|
18
|
+
wait_exponential,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from .._config import Config
|
|
22
|
+
from .._execution_context import ExecutionContext
|
|
23
|
+
from .._utils import user_agent_value
|
|
24
|
+
from .._utils.constants import HEADER_USER_AGENT
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_retryable_exception(exception: BaseException) -> bool:
|
|
28
|
+
return isinstance(exception, (ConnectTimeout, TimeoutException))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_retryable_status_code(response: Response) -> bool:
|
|
32
|
+
return response.status_code >= 500 and response.status_code < 600
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BaseService:
|
|
36
|
+
def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
|
|
37
|
+
self._logger = getLogger("uipath")
|
|
38
|
+
self._config = config
|
|
39
|
+
self._execution_context = execution_context
|
|
40
|
+
self._tenant_scope_client = Client(
|
|
41
|
+
base_url=self._config.base_url, headers=Headers(self.default_headers)
|
|
42
|
+
)
|
|
43
|
+
self._tenant_scope_client_async = AsyncClient(
|
|
44
|
+
base_url=self._config.base_url, headers=Headers(self.default_headers)
|
|
45
|
+
)
|
|
46
|
+
org_scope_base_url = self.__get_org_scope_base_url()
|
|
47
|
+
self._org_scope_client = Client(
|
|
48
|
+
base_url=org_scope_base_url, headers=self.default_headers
|
|
49
|
+
)
|
|
50
|
+
self._org_scope_client_async = AsyncClient(
|
|
51
|
+
base_url=org_scope_base_url, headers=self.default_headers
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
self._logger.debug(f"HEADERS: {self.default_headers}")
|
|
55
|
+
|
|
56
|
+
super().__init__()
|
|
57
|
+
|
|
58
|
+
@retry(
|
|
59
|
+
retry=(
|
|
60
|
+
retry_if_exception(is_retryable_exception)
|
|
61
|
+
| retry_if_result(is_retryable_status_code)
|
|
62
|
+
),
|
|
63
|
+
wait=wait_exponential(multiplier=1, min=1, max=10),
|
|
64
|
+
)
|
|
65
|
+
def request(
|
|
66
|
+
self,
|
|
67
|
+
method: str,
|
|
68
|
+
url: Union[URL, str],
|
|
69
|
+
**kwargs: Any,
|
|
70
|
+
) -> Response:
|
|
71
|
+
self._logger.debug(f"Request: {method} {url}")
|
|
72
|
+
self._logger.debug(
|
|
73
|
+
f"HEADERS: {kwargs.get('headers', self._tenant_scope_client.headers)}"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
stack = inspect.stack()
|
|
78
|
+
|
|
79
|
+
# use the third frame because of the retry decorator
|
|
80
|
+
caller_frame = stack[3].frame
|
|
81
|
+
function_name = caller_frame.f_code.co_name
|
|
82
|
+
|
|
83
|
+
if "self" in caller_frame.f_locals:
|
|
84
|
+
module_name = type(caller_frame.f_locals["self"]).__name__
|
|
85
|
+
elif "cls" in caller_frame.f_locals:
|
|
86
|
+
module_name = caller_frame.f_locals["cls"].__name__
|
|
87
|
+
else:
|
|
88
|
+
module_name = ""
|
|
89
|
+
except Exception:
|
|
90
|
+
function_name = ""
|
|
91
|
+
module_name = ""
|
|
92
|
+
|
|
93
|
+
specific_component = (
|
|
94
|
+
f"{module_name}.{function_name}" if module_name and function_name else ""
|
|
95
|
+
)
|
|
96
|
+
headers = kwargs.get("headers", {})
|
|
97
|
+
headers[HEADER_USER_AGENT] = user_agent_value(specific_component)
|
|
98
|
+
|
|
99
|
+
response = self._tenant_scope_client.request(method, url, **kwargs)
|
|
100
|
+
response.raise_for_status()
|
|
101
|
+
|
|
102
|
+
return response
|
|
103
|
+
|
|
104
|
+
@retry(
|
|
105
|
+
retry=(
|
|
106
|
+
retry_if_exception(is_retryable_exception)
|
|
107
|
+
| retry_if_result(is_retryable_status_code)
|
|
108
|
+
),
|
|
109
|
+
wait=wait_exponential(multiplier=1, min=1, max=10),
|
|
110
|
+
)
|
|
111
|
+
async def request_async(
|
|
112
|
+
self,
|
|
113
|
+
method: str,
|
|
114
|
+
url: Union[URL, str],
|
|
115
|
+
**kwargs: Any,
|
|
116
|
+
) -> Response:
|
|
117
|
+
self._logger.debug(f"Request: {method} {url}")
|
|
118
|
+
self._logger.debug(
|
|
119
|
+
f"HEADERS: {kwargs.get('headers', self._tenant_scope_client.headers)}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
stack = inspect.stack()
|
|
124
|
+
|
|
125
|
+
# use the third frame because of the retry decorator
|
|
126
|
+
caller_frame = stack[3].frame
|
|
127
|
+
function_name = caller_frame.f_code.co_name
|
|
128
|
+
|
|
129
|
+
if "self" in caller_frame.f_locals:
|
|
130
|
+
module_name = type(caller_frame.f_locals["self"]).__name__
|
|
131
|
+
elif "cls" in caller_frame.f_locals:
|
|
132
|
+
module_name = caller_frame.f_locals["cls"].__name__
|
|
133
|
+
else:
|
|
134
|
+
module_name = ""
|
|
135
|
+
except Exception:
|
|
136
|
+
function_name = ""
|
|
137
|
+
module_name = ""
|
|
138
|
+
|
|
139
|
+
specific_component = (
|
|
140
|
+
f"{module_name}.{function_name}" if module_name and function_name else ""
|
|
141
|
+
)
|
|
142
|
+
headers = kwargs.get("headers", {})
|
|
143
|
+
headers[HEADER_USER_AGENT] = user_agent_value(specific_component)
|
|
144
|
+
|
|
145
|
+
response = await self._tenant_scope_client_async.request(method, url, **kwargs)
|
|
146
|
+
response.raise_for_status()
|
|
147
|
+
|
|
148
|
+
return response
|
|
149
|
+
|
|
150
|
+
def request_org_scope(
|
|
151
|
+
self,
|
|
152
|
+
method: str,
|
|
153
|
+
url: Union[URL, str],
|
|
154
|
+
**kwargs: Any,
|
|
155
|
+
) -> Response:
|
|
156
|
+
self._logger.debug(f"Request: {method} {url}")
|
|
157
|
+
self._logger.debug(
|
|
158
|
+
f"HEADERS: {kwargs.get('headers', self._tenant_scope_client.headers)}"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
stack = inspect.stack()
|
|
163
|
+
|
|
164
|
+
# use the third frame because of the retry decorator
|
|
165
|
+
caller_frame = stack[3].frame
|
|
166
|
+
function_name = caller_frame.f_code.co_name
|
|
167
|
+
|
|
168
|
+
if "self" in caller_frame.f_locals:
|
|
169
|
+
module_name = type(caller_frame.f_locals["self"]).__name__
|
|
170
|
+
elif "cls" in caller_frame.f_locals:
|
|
171
|
+
module_name = caller_frame.f_locals["cls"].__name__
|
|
172
|
+
else:
|
|
173
|
+
module_name = ""
|
|
174
|
+
except Exception:
|
|
175
|
+
function_name = ""
|
|
176
|
+
module_name = ""
|
|
177
|
+
|
|
178
|
+
specific_component = (
|
|
179
|
+
f"{module_name}.{function_name}" if module_name and function_name else ""
|
|
180
|
+
)
|
|
181
|
+
headers = kwargs.get("headers", {})
|
|
182
|
+
headers[HEADER_USER_AGENT] = user_agent_value(specific_component)
|
|
183
|
+
|
|
184
|
+
response = self._org_scope_client.request(method, url, **kwargs)
|
|
185
|
+
response.raise_for_status()
|
|
186
|
+
|
|
187
|
+
return response
|
|
188
|
+
|
|
189
|
+
async def request_org_scope_async(
|
|
190
|
+
self,
|
|
191
|
+
method: str,
|
|
192
|
+
url: Union[URL, str],
|
|
193
|
+
**kwargs: Any,
|
|
194
|
+
) -> Response:
|
|
195
|
+
self._logger.debug(f"Request: {method} {url}")
|
|
196
|
+
self._logger.debug(
|
|
197
|
+
f"HEADERS: {kwargs.get('headers', self._tenant_scope_client.headers)}"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
stack = inspect.stack()
|
|
202
|
+
|
|
203
|
+
# use the third frame because of the retry decorator
|
|
204
|
+
caller_frame = stack[3].frame
|
|
205
|
+
function_name = caller_frame.f_code.co_name
|
|
206
|
+
|
|
207
|
+
if "self" in caller_frame.f_locals:
|
|
208
|
+
module_name = type(caller_frame.f_locals["self"]).__name__
|
|
209
|
+
elif "cls" in caller_frame.f_locals:
|
|
210
|
+
module_name = caller_frame.f_locals["cls"].__name__
|
|
211
|
+
else:
|
|
212
|
+
module_name = ""
|
|
213
|
+
except Exception:
|
|
214
|
+
function_name = ""
|
|
215
|
+
module_name = ""
|
|
216
|
+
|
|
217
|
+
specific_component = (
|
|
218
|
+
f"{module_name}.{function_name}" if module_name and function_name else ""
|
|
219
|
+
)
|
|
220
|
+
headers = kwargs.get("headers", {})
|
|
221
|
+
headers[HEADER_USER_AGENT] = user_agent_value(specific_component)
|
|
222
|
+
|
|
223
|
+
response = await self._org_scope_client_async.request(method, url, **kwargs)
|
|
224
|
+
response.raise_for_status()
|
|
225
|
+
|
|
226
|
+
return response
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def default_headers(self) -> dict[str, str]:
|
|
230
|
+
return {
|
|
231
|
+
"Accept": "application/json",
|
|
232
|
+
"Content-Type": "application/json",
|
|
233
|
+
**self.auth_headers,
|
|
234
|
+
**self.custom_headers,
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def auth_headers(self) -> dict[str, str]:
|
|
239
|
+
header = f"Bearer {self._config.secret}"
|
|
240
|
+
return {"Authorization": header}
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def custom_headers(self) -> dict[str, str]:
|
|
244
|
+
return {}
|
|
245
|
+
|
|
246
|
+
def __get_org_scope_base_url(self) -> str:
|
|
247
|
+
base_url = str(self._config.base_url)
|
|
248
|
+
if base_url.endswith("/"):
|
|
249
|
+
base_url = base_url[:-1]
|
|
250
|
+
return base_url.rsplit("/", 1)[0] + "/"
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import uuid
|
|
3
|
+
from json import dumps
|
|
4
|
+
from typing import Any, Dict, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from .._config import Config
|
|
7
|
+
from .._execution_context import ExecutionContext
|
|
8
|
+
from .._folder_context import FolderContext
|
|
9
|
+
from .._models import Action, ActionSchema
|
|
10
|
+
from .._utils import Endpoint, RequestSpec
|
|
11
|
+
from .._utils.constants import ENV_TENANT_ID, HEADER_TENANT_ID
|
|
12
|
+
from ._base_service import BaseService
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _create_spec(
|
|
16
|
+
title: str,
|
|
17
|
+
data: Optional[Dict[str, Any]],
|
|
18
|
+
action_schema: Optional[ActionSchema],
|
|
19
|
+
app_key: str = "",
|
|
20
|
+
app_version: int = -1,
|
|
21
|
+
) -> RequestSpec:
|
|
22
|
+
field_list = []
|
|
23
|
+
outcome_list = []
|
|
24
|
+
if action_schema:
|
|
25
|
+
if action_schema.inputs:
|
|
26
|
+
for input_field in action_schema.inputs:
|
|
27
|
+
field_name = input_field.name
|
|
28
|
+
field_list.append(
|
|
29
|
+
{
|
|
30
|
+
"Id": input_field.key,
|
|
31
|
+
"Name": field_name,
|
|
32
|
+
"Title": field_name,
|
|
33
|
+
"Type": "Fact",
|
|
34
|
+
"Value": data.get(field_name, "") if data is not None else "",
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
if action_schema.outputs:
|
|
38
|
+
for output_field in action_schema.outputs:
|
|
39
|
+
field_name = output_field.name
|
|
40
|
+
field_list.append(
|
|
41
|
+
{
|
|
42
|
+
"Id": output_field.key,
|
|
43
|
+
"Name": field_name,
|
|
44
|
+
"Title": field_name,
|
|
45
|
+
"Type": "Fact",
|
|
46
|
+
"Value": "",
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
if action_schema.in_outs:
|
|
50
|
+
for inout_field in action_schema.in_outs:
|
|
51
|
+
field_name = inout_field.name
|
|
52
|
+
field_list.append(
|
|
53
|
+
{
|
|
54
|
+
"Id": inout_field.key,
|
|
55
|
+
"Name": field_name,
|
|
56
|
+
"Title": field_name,
|
|
57
|
+
"Type": "Fact",
|
|
58
|
+
"Value": data.get(field_name, "") if data is not None else "",
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
if action_schema.outcomes:
|
|
62
|
+
for outcome in action_schema.outcomes:
|
|
63
|
+
outcome_list.append(
|
|
64
|
+
{
|
|
65
|
+
"Id": action_schema.key,
|
|
66
|
+
"Name": outcome.name,
|
|
67
|
+
"Title": outcome.name,
|
|
68
|
+
"Type": "Action.Http",
|
|
69
|
+
"IsPrimary": True,
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return RequestSpec(
|
|
74
|
+
method="POST",
|
|
75
|
+
endpoint=Endpoint("/orchestrator_/tasks/AppTasks/CreateAppTask"),
|
|
76
|
+
content=dumps(
|
|
77
|
+
{
|
|
78
|
+
"appId": app_key,
|
|
79
|
+
"appVersion": app_version,
|
|
80
|
+
"title": title,
|
|
81
|
+
"data": data if data is not None else {},
|
|
82
|
+
"actionableMessageMetaData": {
|
|
83
|
+
"fieldSet": {
|
|
84
|
+
"id": str(uuid.uuid4()),
|
|
85
|
+
"fields": field_list,
|
|
86
|
+
}
|
|
87
|
+
if len(field_list) != 0
|
|
88
|
+
else {},
|
|
89
|
+
"actionSet": {
|
|
90
|
+
"id": str(uuid.uuid4()),
|
|
91
|
+
"actions": outcome_list,
|
|
92
|
+
}
|
|
93
|
+
if len(outcome_list) != 0
|
|
94
|
+
else {},
|
|
95
|
+
}
|
|
96
|
+
if action_schema is not None
|
|
97
|
+
else {},
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _retrieve_action_spec(action_key: str) -> RequestSpec:
|
|
104
|
+
return RequestSpec(
|
|
105
|
+
method="GET",
|
|
106
|
+
endpoint=Endpoint("/orchestrator_/tasks/GenericTasks/GetTaskDataByKey"),
|
|
107
|
+
params={"taskKey": action_key},
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _assign_task_spec(task_key: str, assignee: str) -> RequestSpec:
|
|
112
|
+
return RequestSpec(
|
|
113
|
+
method="POST",
|
|
114
|
+
endpoint=Endpoint(
|
|
115
|
+
"/orchestrator_/odata/Tasks/UiPath.Server.Configuration.OData.AssignTasks"
|
|
116
|
+
),
|
|
117
|
+
content=dumps(
|
|
118
|
+
{"taskAssignments": [{"taskId": task_key, "UserNameOrEmail": assignee}]}
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _retrieve_app_key_spec(app_name: str) -> RequestSpec:
|
|
124
|
+
tenant_id = os.getenv(ENV_TENANT_ID, None)
|
|
125
|
+
if not tenant_id:
|
|
126
|
+
raise Exception(f"{ENV_TENANT_ID} env var is not set")
|
|
127
|
+
return RequestSpec(
|
|
128
|
+
method="GET",
|
|
129
|
+
endpoint=Endpoint("/apps_/default/api/v1/default/deployed-action-apps-schemas"),
|
|
130
|
+
params={"search": app_name},
|
|
131
|
+
headers={HEADER_TENANT_ID: tenant_id},
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class ActionsService(FolderContext, BaseService):
|
|
136
|
+
"""Service for managing UiPath Actions.
|
|
137
|
+
|
|
138
|
+
Actions are task-based automation components that can be integrated into
|
|
139
|
+
applications and processes. They represent discrete units of work that can
|
|
140
|
+
be triggered and monitored through the UiPath API.
|
|
141
|
+
|
|
142
|
+
This service provides methods to create and retrieve actions, supporting
|
|
143
|
+
both app-specific and generic actions. It inherits folder context management
|
|
144
|
+
capabilities from FolderContext.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
|
|
148
|
+
super().__init__(config=config, execution_context=execution_context)
|
|
149
|
+
|
|
150
|
+
async def create_async(
|
|
151
|
+
self,
|
|
152
|
+
title: str,
|
|
153
|
+
data: Optional[Dict[str, Any]] = None,
|
|
154
|
+
*,
|
|
155
|
+
app_name: str = "",
|
|
156
|
+
app_key: str = "",
|
|
157
|
+
app_version: int = -1,
|
|
158
|
+
assignee: str = "",
|
|
159
|
+
) -> Action:
|
|
160
|
+
(key, action_schema) = (
|
|
161
|
+
(app_key, None)
|
|
162
|
+
if app_key
|
|
163
|
+
else await self.__get_app_key_and_schema_async(app_name)
|
|
164
|
+
)
|
|
165
|
+
spec = _create_spec(
|
|
166
|
+
title=title,
|
|
167
|
+
data=data,
|
|
168
|
+
app_key=key,
|
|
169
|
+
app_version=app_version,
|
|
170
|
+
action_schema=action_schema,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
response = await self.request_async(
|
|
174
|
+
spec.method, spec.endpoint, content=spec.content
|
|
175
|
+
)
|
|
176
|
+
json_response = response.json()
|
|
177
|
+
if assignee:
|
|
178
|
+
spec = _assign_task_spec(json_response["id"], assignee)
|
|
179
|
+
await self.request_async(spec.method, spec.endpoint, content=spec.content)
|
|
180
|
+
return Action.model_validate(json_response)
|
|
181
|
+
|
|
182
|
+
def create(
|
|
183
|
+
self,
|
|
184
|
+
title: str,
|
|
185
|
+
data: Optional[Dict[str, Any]] = None,
|
|
186
|
+
*,
|
|
187
|
+
app_name: str = "",
|
|
188
|
+
app_key: str = "",
|
|
189
|
+
app_version: int = -1,
|
|
190
|
+
assignee: str = "",
|
|
191
|
+
) -> Action:
|
|
192
|
+
(key, action_schema) = (
|
|
193
|
+
(app_key, None) if app_key else self.__get_app_key_and_schema(app_name)
|
|
194
|
+
)
|
|
195
|
+
spec = _create_spec(
|
|
196
|
+
title=title,
|
|
197
|
+
data=data,
|
|
198
|
+
app_key=key,
|
|
199
|
+
app_version=app_version,
|
|
200
|
+
action_schema=action_schema,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
response = self.request(spec.method, spec.endpoint, content=spec.content)
|
|
204
|
+
json_response = response.json()
|
|
205
|
+
if assignee:
|
|
206
|
+
spec = _assign_task_spec(json_response["id"], assignee)
|
|
207
|
+
print(spec)
|
|
208
|
+
self.request(spec.method, spec.endpoint, content=spec.content)
|
|
209
|
+
return Action.model_validate(json_response)
|
|
210
|
+
|
|
211
|
+
def retrieve(
|
|
212
|
+
self,
|
|
213
|
+
action_key: str,
|
|
214
|
+
) -> Action:
|
|
215
|
+
spec = _retrieve_action_spec(action_key=action_key)
|
|
216
|
+
response = self.request(spec.method, spec.endpoint, params=spec.params)
|
|
217
|
+
|
|
218
|
+
return Action.model_validate(response.json())
|
|
219
|
+
|
|
220
|
+
async def retrieve_async(
|
|
221
|
+
self,
|
|
222
|
+
action_key: str,
|
|
223
|
+
) -> Action:
|
|
224
|
+
spec = _retrieve_action_spec(action_key=action_key)
|
|
225
|
+
response = await self.request_async(
|
|
226
|
+
spec.method, spec.endpoint, params=spec.params
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return Action.model_validate(response.json())
|
|
230
|
+
|
|
231
|
+
async def __get_app_key_and_schema_async(
|
|
232
|
+
self, app_name: str
|
|
233
|
+
) -> Tuple[str, Optional[ActionSchema]]:
|
|
234
|
+
if not app_name:
|
|
235
|
+
raise Exception("appName or appKey is required")
|
|
236
|
+
spec = _retrieve_app_key_spec(app_name=app_name)
|
|
237
|
+
|
|
238
|
+
response = await self.request_org_scope_async(
|
|
239
|
+
spec.method, spec.endpoint, params=spec.params, headers=spec.headers
|
|
240
|
+
)
|
|
241
|
+
deployed_app = response.json()["deployed"][0]
|
|
242
|
+
return (deployed_app["systemName"], deployed_app["actionSchema"])
|
|
243
|
+
|
|
244
|
+
def __get_app_key_and_schema(
|
|
245
|
+
self, app_name: str
|
|
246
|
+
) -> Tuple[str, Optional[ActionSchema]]:
|
|
247
|
+
if not app_name:
|
|
248
|
+
raise Exception("appName or appKey is required")
|
|
249
|
+
|
|
250
|
+
spec = _retrieve_app_key_spec(app_name=app_name)
|
|
251
|
+
|
|
252
|
+
response = self.request_org_scope(
|
|
253
|
+
spec.method, spec.endpoint, params=spec.params, headers=spec.headers
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
deployed_app = response.json()["deployed"][0]
|
|
257
|
+
action_schema = deployed_app["actionSchema"]
|
|
258
|
+
return (
|
|
259
|
+
deployed_app["systemName"],
|
|
260
|
+
ActionSchema(
|
|
261
|
+
key=action_schema["key"],
|
|
262
|
+
in_outs=action_schema["inOuts"],
|
|
263
|
+
inputs=action_schema["inputs"],
|
|
264
|
+
outputs=action_schema["outputs"],
|
|
265
|
+
outcomes=action_schema["outcomes"],
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def custom_headers(self) -> Dict[str, str]:
|
|
271
|
+
return self.folder_headers
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from typing import Any, Union
|
|
2
|
+
|
|
3
|
+
from httpx import URL, Response
|
|
4
|
+
|
|
5
|
+
from .._config import Config
|
|
6
|
+
from .._execution_context import ExecutionContext
|
|
7
|
+
from .._folder_context import FolderContext
|
|
8
|
+
from ._base_service import BaseService
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ApiClient(FolderContext, BaseService):
|
|
12
|
+
"""Low-level client for making direct HTTP requests to the UiPath API.
|
|
13
|
+
|
|
14
|
+
This class provides a flexible way to interact with the UiPath API when the
|
|
15
|
+
higher-level service classes don't provide the needed functionality. It inherits
|
|
16
|
+
from both FolderContext and BaseService to provide folder-aware request capabilities
|
|
17
|
+
with automatic authentication and retry logic.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
|
|
21
|
+
super().__init__(config=config, execution_context=execution_context)
|
|
22
|
+
|
|
23
|
+
def request(
|
|
24
|
+
self,
|
|
25
|
+
method: str,
|
|
26
|
+
url: Union[URL, str],
|
|
27
|
+
**kwargs: Any,
|
|
28
|
+
) -> Response:
|
|
29
|
+
if kwargs.get("include_folder_headers", False):
|
|
30
|
+
kwargs["headers"] = {
|
|
31
|
+
**kwargs.get("headers", self._tenant_scope_client.headers),
|
|
32
|
+
**self.folder_headers,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if "include_folder_headers" in kwargs:
|
|
36
|
+
del kwargs["include_folder_headers"]
|
|
37
|
+
|
|
38
|
+
return super().request(method, url, **kwargs)
|
|
39
|
+
|
|
40
|
+
async def request_async(
|
|
41
|
+
self,
|
|
42
|
+
method: str,
|
|
43
|
+
url: Union[URL, str],
|
|
44
|
+
**kwargs: Any,
|
|
45
|
+
) -> Response:
|
|
46
|
+
if kwargs.get("include_folder_headers", False):
|
|
47
|
+
kwargs["headers"] = {
|
|
48
|
+
**kwargs.get("headers", self._tenant_scope_client_async.headers),
|
|
49
|
+
**self.folder_headers,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if "include_folder_headers" in kwargs:
|
|
53
|
+
del kwargs["include_folder_headers"]
|
|
54
|
+
|
|
55
|
+
return await super().request_async(method, url, **kwargs)
|
|
56
|
+
|
|
57
|
+
def request_org_scope(
|
|
58
|
+
self,
|
|
59
|
+
method: str,
|
|
60
|
+
url: Union[URL, str],
|
|
61
|
+
**kwargs: Any,
|
|
62
|
+
) -> Response:
|
|
63
|
+
if kwargs.get("include_folder_headers", False):
|
|
64
|
+
kwargs["headers"] = {
|
|
65
|
+
**kwargs.get("headers", self._org_scope_client.headers),
|
|
66
|
+
**self.folder_headers,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if "include_folder_headers" in kwargs:
|
|
70
|
+
del kwargs["include_folder_headers"]
|
|
71
|
+
|
|
72
|
+
return super().request_org_scope(method, url, **kwargs)
|
|
73
|
+
|
|
74
|
+
async def request_org_scope_async(
|
|
75
|
+
self,
|
|
76
|
+
method: str,
|
|
77
|
+
url: Union[URL, str],
|
|
78
|
+
**kwargs: Any,
|
|
79
|
+
) -> Response:
|
|
80
|
+
if kwargs.get("include_folder_headers", False):
|
|
81
|
+
kwargs["headers"] = {
|
|
82
|
+
**kwargs.get("headers", self._org_scope_client_async.headers),
|
|
83
|
+
**self.folder_headers,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if "include_folder_headers" in kwargs:
|
|
87
|
+
del kwargs["include_folder_headers"]
|
|
88
|
+
|
|
89
|
+
return await super().request_org_scope_async(method, url, **kwargs)
|