Skip to content

Commit 5a9d925

Browse files
committed
feat: add support for folder path in action service
1 parent cea847c commit 5a9d925

File tree

6 files changed

+111
-42
lines changed

6 files changed

+111
-42
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath"
3-
version = "2.0.6"
3+
version = "2.0.7"
44
description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools."
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.10"

src/uipath/_folder_context.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,12 @@ def folder_headers(self) -> dict[str, str]:
4646
4747
Returns:
4848
dict[str, str]: A dictionary containing the appropriate folder
49-
header (either folder key or folder path).
50-
51-
Raises:
52-
ValueError: If neither folder key nor folder path is set in
53-
the environment.
49+
header (either folder key or folder path). If no folder header is
50+
set as environment variable, the function returns an empty dictionary.
5451
"""
5552
if self._folder_key is not None:
5653
return {HEADER_FOLDER_KEY: self._folder_key}
5754
elif self._folder_path is not None:
5855
return {HEADER_FOLDER_PATH: self._folder_path}
5956
else:
60-
raise ValueError(
61-
f"Folder key or path is not set ({ENV_FOLDER_KEY} or {ENV_FOLDER_PATH})"
62-
)
57+
return {}

src/uipath/_services/actions_service.py

Lines changed: 97 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@
77
from .._execution_context import ExecutionContext
88
from .._folder_context import FolderContext
99
from .._utils import Endpoint, RequestSpec
10-
from .._utils.constants import ENV_TENANT_ID, HEADER_TENANT_ID
10+
from .._utils.constants import (
11+
ENV_TENANT_ID,
12+
HEADER_FOLDER_KEY,
13+
HEADER_FOLDER_PATH,
14+
HEADER_TENANT_ID,
15+
)
1116
from ..models import Action, ActionSchema
1217
from ._base_service import BaseService
1318

1419

1520
def _create_spec(
16-
title: str,
1721
data: Optional[Dict[str, Any]],
1822
action_schema: Optional[ActionSchema],
23+
title: str,
1924
app_key: str = "",
2025
app_version: int = -1,
26+
app_folder_key: str = "",
27+
app_folder_path: str = "",
2128
) -> RequestSpec:
2229
field_list = []
2330
outcome_list = []
@@ -97,14 +104,18 @@ def _create_spec(
97104
else {},
98105
}
99106
),
107+
headers=folder_headers(app_folder_key, app_folder_path),
100108
)
101109

102110

103-
def _retrieve_action_spec(action_key: str) -> RequestSpec:
111+
def _retrieve_action_spec(
112+
action_key: str, app_folder_key: str, app_folder_path: str
113+
) -> RequestSpec:
104114
return RequestSpec(
105115
method="GET",
106116
endpoint=Endpoint("/orchestrator_/tasks/GenericTasks/GetTaskDataByKey"),
107117
params={"taskKey": action_key},
118+
headers=folder_headers(app_folder_key, app_folder_path),
108119
)
109120

110121

@@ -132,6 +143,15 @@ def _retrieve_app_key_spec(app_name: str) -> RequestSpec:
132143
)
133144

134145

146+
def folder_headers(app_folder_key: str, app_folder_path: str) -> Dict[str, str]:
147+
headers = {}
148+
if app_folder_key:
149+
headers[HEADER_FOLDER_KEY] = app_folder_key
150+
elif app_folder_path:
151+
headers[HEADER_FOLDER_PATH] = app_folder_path
152+
return headers
153+
154+
135155
class ActionsService(FolderContext, BaseService):
136156
"""Service for managing UiPath Actions.
137157
@@ -162,6 +182,8 @@ async def create_async(
162182
*,
163183
app_name: str = "",
164184
app_key: str = "",
185+
app_folder_path: str = "",
186+
app_folder_key: str = "",
165187
app_version: int = -1,
166188
assignee: str = "",
167189
) -> Action:
@@ -175,6 +197,8 @@ async def create_async(
175197
data: Optional dictionary containing input data for the action
176198
app_name: The name of the application (if creating an app-specific action)
177199
app_key: The key of the application (if creating an app-specific action)
200+
app_folder_path: Optional folder path for the action
201+
app_folder_key: Optional folder key for the action
178202
app_version: The version of the application
179203
assignee: Optional username or email to assign the task to
180204
@@ -195,10 +219,12 @@ async def create_async(
195219
app_key=key,
196220
app_version=app_version,
197221
action_schema=action_schema,
222+
app_folder_key=app_folder_key,
223+
app_folder_path=app_folder_path,
198224
)
199225

200226
response = await self.request_async(
201-
spec.method, spec.endpoint, content=spec.content
227+
spec.method, spec.endpoint, content=spec.content, headers=spec.headers
202228
)
203229
json_response = response.json()
204230
if assignee:
@@ -213,6 +239,8 @@ def create(
213239
*,
214240
app_name: str = "",
215241
app_key: str = "",
242+
app_folder_path: str = "",
243+
app_folder_key: str = "",
216244
app_version: int = -1,
217245
assignee: str = "",
218246
) -> Action:
@@ -226,6 +254,8 @@ def create(
226254
data: Optional dictionary containing input data for the action
227255
app_name: The name of the application (if creating an app-specific action)
228256
app_key: The key of the application (if creating an app-specific action)
257+
app_folder_path: Optional folder path for the action
258+
app_folder_key: Optional folder key for the action
229259
app_version: The version of the application
230260
assignee: Optional username or email to assign the task to
231261
@@ -244,48 +274,63 @@ def create(
244274
app_key=key,
245275
app_version=app_version,
246276
action_schema=action_schema,
277+
app_folder_key=app_folder_key,
278+
app_folder_path=app_folder_path,
247279
)
248280

249-
response = self.request(spec.method, spec.endpoint, content=spec.content)
281+
response = self.request(
282+
spec.method, spec.endpoint, content=spec.content, headers=spec.headers
283+
)
250284
json_response = response.json()
251285
if assignee:
252286
spec = _assign_task_spec(json_response["id"], assignee)
253-
print(spec)
254287
self.request(spec.method, spec.endpoint, content=spec.content)
255288
return Action.model_validate(json_response)
256289

257290
def retrieve(
258-
self,
259-
action_key: str,
291+
self, action_key: str, app_folder_path: str = "", app_folder_key: str = ""
260292
) -> Action:
261293
"""Retrieves an action by its key synchronously.
262294
263295
Args:
264296
action_key: The unique identifier of the action to retrieve
297+
app_folder_path: Optional folder path for the action
298+
app_folder_key: Optional folder key for the action
265299
266300
Returns:
267301
Action: The retrieved action object
268302
"""
269-
spec = _retrieve_action_spec(action_key=action_key)
270-
response = self.request(spec.method, spec.endpoint, params=spec.params)
303+
spec = _retrieve_action_spec(
304+
action_key=action_key,
305+
app_folder_key=app_folder_key,
306+
app_folder_path=app_folder_path,
307+
)
308+
response = self.request(
309+
spec.method, spec.endpoint, params=spec.params, headers=spec.headers
310+
)
271311

272312
return Action.model_validate(response.json())
273313

274314
async def retrieve_async(
275-
self,
276-
action_key: str,
315+
self, action_key: str, app_folder_path: str = "", app_folder_key: str = ""
277316
) -> Action:
278317
"""Retrieves an action by its key asynchronously.
279318
280319
Args:
281320
action_key: The unique identifier of the action to retrieve
321+
app_folder_path: Optional folder path for the action
322+
app_folder_key: Optional folder key for the action
282323
283324
Returns:
284325
Action: The retrieved action object
285326
"""
286-
spec = _retrieve_action_spec(action_key=action_key)
327+
spec = _retrieve_action_spec(
328+
action_key=action_key,
329+
app_folder_key=app_folder_key,
330+
app_folder_path=app_folder_path,
331+
)
287332
response = await self.request_async(
288-
spec.method, spec.endpoint, params=spec.params
333+
spec.method, spec.endpoint, params=spec.params, headers=spec.headers
289334
)
290335

291336
return Action.model_validate(response.json())
@@ -311,8 +356,25 @@ async def __get_app_key_and_schema_async(
311356
response = await self.request_org_scope_async(
312357
spec.method, spec.endpoint, params=spec.params, headers=spec.headers
313358
)
314-
deployed_app = response.json()["deployed"][0]
315-
return (deployed_app["systemName"], deployed_app["actionSchema"])
359+
try:
360+
deployed_app = response.json()["deployed"][0]
361+
action_schema = deployed_app["actionSchema"]
362+
deployed_app_key = deployed_app["systemName"]
363+
except (KeyError, IndexError):
364+
raise Exception("Action app not found") from None
365+
try:
366+
return (
367+
deployed_app_key,
368+
ActionSchema(
369+
key=action_schema["key"],
370+
in_outs=action_schema["inOuts"],
371+
inputs=action_schema["inputs"],
372+
outputs=action_schema["outputs"],
373+
outcomes=action_schema["outcomes"],
374+
),
375+
)
376+
except KeyError:
377+
raise Exception("Failed to deserialize action schema") from KeyError
316378

317379
def __get_app_key_and_schema(
318380
self, app_name: str
@@ -326,18 +388,25 @@ def __get_app_key_and_schema(
326388
spec.method, spec.endpoint, params=spec.params, headers=spec.headers
327389
)
328390

329-
deployed_app = response.json()["deployed"][0]
330-
action_schema = deployed_app["actionSchema"]
331-
return (
332-
deployed_app["systemName"],
333-
ActionSchema(
334-
key=action_schema["key"],
335-
in_outs=action_schema["inOuts"],
336-
inputs=action_schema["inputs"],
337-
outputs=action_schema["outputs"],
338-
outcomes=action_schema["outcomes"],
339-
),
340-
)
391+
try:
392+
deployed_app = response.json()["deployed"][0]
393+
action_schema = deployed_app["actionSchema"]
394+
deployed_app_key = deployed_app["systemName"]
395+
except (KeyError, IndexError):
396+
raise Exception("Action app not found") from None
397+
try:
398+
return (
399+
deployed_app_key,
400+
ActionSchema(
401+
key=action_schema["key"],
402+
in_outs=action_schema["inOuts"],
403+
inputs=action_schema["inputs"],
404+
outputs=action_schema["outputs"],
405+
outcomes=action_schema["outcomes"],
406+
),
407+
)
408+
except KeyError:
409+
raise Exception("Failed to deserialize action schema") from KeyError
341410

342411
@property
343412
def custom_headers(self) -> Dict[str, str]:

src/uipath/_services/context_grounding_service.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .._utils import Endpoint, RequestSpec
1010
from .._utils.constants import (
1111
HEADER_FOLDER_KEY,
12+
HEADER_FOLDER_PATH,
1213
ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE,
1314
)
1415
from ..models import IngestionInProgressException
@@ -326,7 +327,9 @@ def custom_headers(self) -> Dict[str, str]:
326327
)
327328

328329
if self._folder_key is None:
329-
raise ValueError(f"Folder key is not set ({HEADER_FOLDER_KEY})")
330+
raise ValueError(
331+
f"Neither the folder key nor the folder path is set ({HEADER_FOLDER_KEY}, {HEADER_FOLDER_PATH})"
332+
)
330333

331334
return self.folder_headers
332335

src/uipath/models/interrupt_models.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ class WaitJob(BaseModel):
1616

1717

1818
class CreateAction(BaseModel):
19-
name: Optional[str] = None
20-
key: Optional[str] = None
2119
title: str
2220
data: Optional[Dict[str, Any]] = None
23-
app_version: Optional[int] = 1
2421
assignee: Optional[str] = ""
22+
app_name: Optional[str] = None
23+
app_folder_path: Optional[str] = None
24+
app_folder_key: Optional[str] = None
25+
app_key: Optional[str] = None
26+
app_version: Optional[int] = 1
2527

2628

2729
class WaitAction(BaseModel):

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)