Skip to content

Commit 0f7d305

Browse files
fix(platform): support entity resource overwrites with folder resolution
1 parent d0fdafd commit 0f7d305

File tree

9 files changed

+303
-204
lines changed

9 files changed

+303
-204
lines changed

packages/uipath-platform/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-platform"
3-
version = "0.1.21"
3+
version = "0.1.22"
44
description = "HTTP client library for programmatic access to UiPath Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

packages/uipath-platform/src/uipath/platform/common/_bindings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ def _normalize_value(resource_type: str, value: dict[str, Any]) -> dict[str, Any
155155

156156
normalized = dict(value)
157157
if "folderId" in normalized:
158-
normalized["folder_id"] = normalized["folderId"]
158+
normalized["folder_id"] = normalized.pop("folderId")
159159
if "folderPath" in normalized:
160-
normalized["folder_path"] = normalized["folderPath"]
160+
normalized["folder_path"] = normalized.pop("folderPath")
161161

162162
return normalized
163163

packages/uipath-platform/src/uipath/platform/entities/_entities_service.py

Lines changed: 13 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88
from uipath.core.tracing import traced
99

1010
from ..common._base_service import BaseService
11-
from ..common._bindings import EntityResourceOverwrite, _resource_overwrites
11+
from ..common._bindings import _resource_overwrites
1212
from ..common._config import UiPathApiConfig
1313
from ..common._execution_context import UiPathExecutionContext
1414
from ..common._models import Endpoint, RequestSpec
1515
from ..common.constants import HEADER_FOLDER_KEY
1616
from ..orchestrator._folder_service import FolderService
1717
from ._entity_resolution import (
18-
build_resolution_routing_context,
18+
RoutingStrategy,
1919
build_resolution_service,
2020
create_resolution_plan,
2121
create_resolution_plan_async,
22+
create_routing_strategy,
2223
fetch_resolved_entities,
2324
fetch_resolved_entities_async,
2425
)
@@ -27,7 +28,6 @@
2728
Entity,
2829
EntityRecord,
2930
EntityRecordsBatchResponse,
30-
EntityRouting,
3131
EntitySetResolution,
3232
QueryRoutingOverrideContext,
3333
)
@@ -74,32 +74,11 @@ def __init__(
7474
) -> None:
7575
super().__init__(config=config, execution_context=execution_context)
7676
self._folders_service = folders_service
77-
self._folders_map = folders_map or {}
78-
self._effective_entity_names = entity_name_overrides or {}
79-
self._routing_context = routing_context
80-
81-
def with_folders_map(
82-
self,
83-
folders_map: Dict[str, str],
84-
entity_name_overrides: Optional[Dict[str, str]] = None,
85-
) -> "EntitiesService":
86-
"""Return a new EntitiesService configured with the given folders map.
87-
88-
The map is used to build a routing context automatically when
89-
``query_entity_records`` is called without an explicit routing context.
90-
Folder paths in the map are resolved to folder keys via ``FolderService``.
91-
92-
Args:
93-
folders_map: Mapping of entity name to folder path.
94-
entity_name_overrides: Mapping of original entity name to
95-
overridden entity name.
96-
"""
97-
return EntitiesService(
98-
config=self._config,
99-
execution_context=self._execution_context,
100-
folders_service=self._folders_service,
77+
self._routing_strategy: RoutingStrategy = create_routing_strategy(
10178
folders_map=folders_map,
102-
entity_name_overrides=entity_name_overrides,
79+
effective_entity_names=entity_name_overrides,
80+
routing_context=routing_context,
81+
folders_service=folders_service,
10382
)
10483

10584
@traced(name="entity_retrieve", run_type="uipath")
@@ -566,7 +545,7 @@ def resolve_entity_set(
566545
items,
567546
_resource_overwrites.get() or {},
568547
lambda folder_path: (
569-
self._folders_service.retrieve_folder_key(folder_path)
548+
self._folders_service.retrieve_key(folder_path=folder_path)
570549
if self._folders_service is not None
571550
else None
572551
),
@@ -599,7 +578,9 @@ async def resolve_entity_set_async(
599578
async def _resolve_folder_path(folder_path: str) -> Optional[str]:
600579
if self._folders_service is None:
601580
return None
602-
return await self._folders_service.retrieve_folder_key_async(folder_path)
581+
return await self._folders_service.retrieve_key_async(
582+
folder_path=folder_path
583+
)
603584

604585
plan = await create_resolution_plan_async(
605586
items,
@@ -629,7 +610,7 @@ def _query_entities_for_records(
629610
sql_query: str,
630611
) -> List[Dict[str, Any]]:
631612
self._validate_sql_query(sql_query)
632-
routing_context = self._build_routing_context_from_map()
613+
routing_context = self._routing_strategy.resolve()
633614
spec = self._query_entity_records_spec(sql_query, routing_context)
634615
response = self.request(spec.method, spec.endpoint, json=spec.json)
635616
return response.json().get("results", [])
@@ -639,7 +620,7 @@ async def _query_entities_for_records_async(
639620
sql_query: str,
640621
) -> List[Dict[str, Any]]:
641622
self._validate_sql_query(sql_query)
642-
routing_context = await self._build_routing_context_from_map_async()
623+
routing_context = await self._routing_strategy.resolve_async()
643624
spec = self._query_entity_records_spec(sql_query, routing_context)
644625
response = await self.request_async(spec.method, spec.endpoint, json=spec.json)
645626
return response.json().get("results", [])
@@ -1158,152 +1139,6 @@ def _query_entity_records_spec(
11581139
json=body,
11591140
)
11601141

1161-
def _build_routing_context_from_map(
1162-
self,
1163-
) -> Optional[QueryRoutingOverrideContext]:
1164-
"""Build a routing context from the configured folders_map and context overwrites.
1165-
1166-
If a pre-built routing context was provided (e.g. by
1167-
``resolve_entity_set_async``), it is returned directly without
1168-
re-resolving folder paths.
1169-
1170-
Otherwise, folder paths in the map are resolved to folder keys via
1171-
FolderService and entity overwrites from the active
1172-
``ResourceOverwritesContext`` are merged in.
1173-
1174-
Returns:
1175-
A QueryRoutingOverrideContext if routing entries exist,
1176-
None otherwise.
1177-
"""
1178-
if self._routing_context is not None:
1179-
return self._routing_context
1180-
resolved = self._resolve_folder_paths_to_ids()
1181-
return self._build_routing_context_from_resolved_map(resolved)
1182-
1183-
async def _build_routing_context_from_map_async(
1184-
self,
1185-
) -> Optional[QueryRoutingOverrideContext]:
1186-
"""Async version of _build_routing_context_from_map."""
1187-
if self._routing_context is not None:
1188-
return self._routing_context
1189-
resolved = await self._resolve_folder_paths_to_ids_async()
1190-
return self._build_routing_context_from_resolved_map(resolved)
1191-
1192-
def _resolve_folder_paths_to_ids(self) -> Optional[dict[str, str]]:
1193-
entity_overwrites = self._get_entity_overwrites_from_context()
1194-
folder_paths = set(self._folders_map.values())
1195-
for overwrite in entity_overwrites.values():
1196-
if overwrite.folder_path:
1197-
folder_paths.add(overwrite.folder_path)
1198-
1199-
if not folder_paths:
1200-
return None
1201-
1202-
resolved: dict[str, str] = {}
1203-
for folder_path in folder_paths:
1204-
if self._folders_service is not None:
1205-
folder_key = self._folders_service.retrieve_folder_key(folder_path)
1206-
if folder_key is not None:
1207-
resolved[folder_path] = folder_key
1208-
continue
1209-
resolved[folder_path] = folder_path
1210-
1211-
return resolved
1212-
1213-
async def _resolve_folder_paths_to_ids_async(self) -> Optional[dict[str, str]]:
1214-
entity_overwrites = self._get_entity_overwrites_from_context()
1215-
folder_paths = set(self._folders_map.values())
1216-
for overwrite in entity_overwrites.values():
1217-
if overwrite.folder_path:
1218-
folder_paths.add(overwrite.folder_path)
1219-
1220-
if not folder_paths:
1221-
return None
1222-
1223-
resolved: dict[str, str] = {}
1224-
for folder_path in folder_paths:
1225-
if self._folders_service is not None:
1226-
folder_key = await self._folders_service.retrieve_folder_key_async(
1227-
folder_path
1228-
)
1229-
if folder_key is not None:
1230-
resolved[folder_path] = folder_key
1231-
continue
1232-
resolved[folder_path] = folder_path
1233-
1234-
return resolved
1235-
1236-
@staticmethod
1237-
def _get_entity_overwrites_from_context() -> Dict[str, EntityResourceOverwrite]:
1238-
"""Extract entity overwrites from the active ResourceOverwritesContext.
1239-
1240-
Returns:
1241-
A dict mapping original entity name to its EntityResourceOverwrite.
1242-
"""
1243-
context_overwrites = _resource_overwrites.get()
1244-
if not context_overwrites:
1245-
return {}
1246-
1247-
result: Dict[str, EntityResourceOverwrite] = {}
1248-
for key, overwrite in context_overwrites.items():
1249-
if isinstance(overwrite, EntityResourceOverwrite):
1250-
# Key format is "entity.<original_id>"
1251-
original_name = key.split(".", 1)[1] if "." in key else key
1252-
result[original_name] = overwrite
1253-
return result
1254-
1255-
def _build_routing_context_from_resolved_map(
1256-
self,
1257-
resolved: Optional[dict[str, str]],
1258-
) -> Optional[QueryRoutingOverrideContext]:
1259-
if self._folders_map:
1260-
return build_resolution_routing_context(
1261-
{
1262-
name: (resolved or {}).get(folder_path, folder_path)
1263-
for name, folder_path in self._folders_map.items()
1264-
},
1265-
self._effective_entity_names,
1266-
)
1267-
1268-
routings: List[EntityRouting] = []
1269-
if not self._folders_map:
1270-
# Fallback for direct SDK usage (no folders_map)
1271-
entity_overwrites = self._get_entity_overwrites_from_context()
1272-
for original_name, overwrite in entity_overwrites.items():
1273-
override_name = (
1274-
overwrite.resource_identifier
1275-
if overwrite.resource_identifier != original_name
1276-
else None
1277-
)
1278-
routings.append(
1279-
EntityRouting(
1280-
entity_name=original_name,
1281-
folder_id=self._resolve_overwrite_folder(overwrite, resolved),
1282-
override_entity_name=override_name,
1283-
)
1284-
)
1285-
1286-
if not routings:
1287-
return None
1288-
1289-
return QueryRoutingOverrideContext(entity_routings=routings)
1290-
1291-
@staticmethod
1292-
def _resolve_overwrite_folder(
1293-
overwrite: EntityResourceOverwrite,
1294-
resolved: Optional[dict[str, str]],
1295-
) -> str:
1296-
"""Return the folder key for an entity overwrite.
1297-
1298-
Uses folder_id directly when present (already a key).
1299-
Falls back to resolving folder_path through the resolved map.
1300-
"""
1301-
if overwrite.folder_id:
1302-
return overwrite.folder_id
1303-
if overwrite.folder_path and resolved:
1304-
return resolved.get(overwrite.folder_path, overwrite.folder_path)
1305-
return overwrite.folder_identifier
1306-
13071142
def _insert_batch_spec(self, entity_key: str, records: List[Any]) -> RequestSpec:
13081143
return RequestSpec(
13091144
method="POST",

0 commit comments

Comments
 (0)