diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index 97528d9be..b11fb5086 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-platform" -version = "0.2.19" +version = "0.2.20" description = "HTTP client library for programmatic access to UiPath Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/packages/uipath-platform/src/uipath/platform/orchestrator/_processes_service.py b/packages/uipath-platform/src/uipath/platform/orchestrator/_processes_service.py index 433793389..6c006aa9b 100644 --- a/packages/uipath-platform/src/uipath/platform/orchestrator/_processes_service.py +++ b/packages/uipath-platform/src/uipath/platform/orchestrator/_processes_service.py @@ -52,6 +52,7 @@ def invoke( attachments: Optional[list[Attachment]] = None, parent_operation_id: Optional[str] = None, run_as_me: Optional[bool] = None, + entry_point_path: Optional[str] = None, **kwargs: Any, ) -> Job: """Start execution of a process by its name. @@ -104,6 +105,7 @@ def invoke( parent_span_id=kwargs.get("parent_span_id"), parent_operation_id=parent_operation_id, run_as_me=run_as_me, + entry_point_path=entry_point_path, ) response = self.request( spec.method, @@ -128,6 +130,7 @@ async def invoke_async( attachments: Optional[list[Attachment]] = None, parent_operation_id: Optional[str] = None, run_as_me: Optional[bool] = None, + entry_point_path: Optional[str] = None, **kwargs: Any, ) -> Job: """Asynchronously start execution of a process by its name. @@ -175,6 +178,7 @@ async def main(): parent_span_id=kwargs.get("parent_span_id"), parent_operation_id=parent_operation_id, run_as_me=run_as_me, + entry_point_path=entry_point_path, ) response = await self.request_async( @@ -321,6 +325,7 @@ def _invoke_spec( parent_span_id: Optional[str] = None, parent_operation_id: Optional[str] = None, run_as_me: Optional[bool] = None, + entry_point_path: Optional[str] = None, ) -> RequestSpec: payload: Dict[str, Any] = { "ReleaseName": name, @@ -335,6 +340,11 @@ def _invoke_spec( if run_as_me is not None: payload["RunAsMe"] = run_as_me + # Omitted rather than sent empty: Orchestrator resolves the release's configured entry + # point when the key is absent, and rejects a path that does not exist in the package. + if entry_point_path: + payload["EntryPointPath"] = entry_point_path + request_spec = RequestSpec( method="POST", endpoint=Endpoint( diff --git a/packages/uipath-platform/tests/services/test_processes_service.py b/packages/uipath-platform/tests/services/test_processes_service.py index 054313a1c..9e1c18888 100644 --- a/packages/uipath-platform/tests/services/test_processes_service.py +++ b/packages/uipath-platform/tests/services/test_processes_service.py @@ -91,6 +91,63 @@ def test_invoke( == f"UiPath.Python.Sdk/UiPath.Python.Sdk.Activities.ProcessesService.invoke/{version}" ) + def test_invoke_includes_entry_point_path( + self, + httpx_mock: HTTPXMock, + service: ProcessesService, + base_url: str, + org: str, + tenant: str, + ) -> None: + process_name = "test-process" + httpx_mock.add_response( + url=f"{base_url}{org}{tenant}/orchestrator_/odata/Jobs/UiPath.Server.Configuration.OData.StartJobs", + status_code=200, + json={"value": [{"Key": "k", "State": "Running", "Id": 1}]}, + ) + + service.invoke(process_name, entry_point_path="Workflows/Main.xaml") + + sent_request = httpx_mock.get_request() + if sent_request is None: + raise Exception("No request was sent") + + assert sent_request.content.decode("utf-8") == json.dumps( + { + "startInfo": { + "ReleaseName": process_name, + "InputArguments": "{}", + "Source": "AgentService", + "EntryPointPath": "Workflows/Main.xaml", + } + }, + separators=(",", ":"), + ) + + def test_invoke_omits_an_empty_entry_point_path( + self, + httpx_mock: HTTPXMock, + service: ProcessesService, + base_url: str, + org: str, + tenant: str, + ) -> None: + # Sending "" would make Orchestrator resolve an entry point named "", which fails the + # release lookup; omitting the key is what selects the configured default. + httpx_mock.add_response( + url=f"{base_url}{org}{tenant}/orchestrator_/odata/Jobs/UiPath.Server.Configuration.OData.StartJobs", + status_code=200, + json={"value": [{"Key": "k", "State": "Running", "Id": 1}]}, + ) + + service.invoke("test-process", entry_point_path="") + + sent_request = httpx_mock.get_request() + if sent_request is None: + raise Exception("No request was sent") + + assert "EntryPointPath" not in sent_request.content.decode("utf-8") + def test_invoke_without_input_arguments( self, httpx_mock: HTTPXMock, diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index 68123d499..5b933953d 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1095,7 +1095,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.19" +version = "0.2.20" source = { editable = "." } dependencies = [ { name = "anyio" }, diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index 2ac4f3d35..a05a22a38 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,13 +1,13 @@ [project] name = "uipath" -version = "2.14.5" +version = "2.14.6" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" dependencies = [ "uipath-core>=0.5.30, <0.6.0", "uipath-runtime>=0.13.1, <0.14.0", - "uipath-platform>=0.2.14, <0.3.0", + "uipath-platform>=0.2.20, <0.3.0", "click>=8.3.1", "httpx>=0.28.1", "pyjwt>=2.10.1", diff --git a/packages/uipath/src/uipath/agent/models/agent.py b/packages/uipath/src/uipath/agent/models/agent.py index 86a0de739..530633d3d 100644 --- a/packages/uipath/src/uipath/agent/models/agent.py +++ b/packages/uipath/src/uipath/agent/models/agent.py @@ -948,6 +948,9 @@ class AgentProcessToolProperties(BaseResourceProperties): folder_path: Optional[str] = Field(None, alias="folderPath") process_name: Optional[str] = Field(None, alias="processName") + # Absent is a real mode, not a blank: Orchestrator resolves the process's configured entry + # point when StartJobs omits EntryPointPath. + entry_point_path: Optional[str] = Field(None, alias="entryPointPath") class AgentProcessToolResourceConfig(BaseAgentToolResourceConfig): diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 55a090fca..c09483f89 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2599,7 +2599,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.5" +version = "2.14.6" source = { editable = "." } dependencies = [ { name = "applicationinsights" }, @@ -2760,7 +2760,7 @@ wheels = [ [[package]] name = "uipath-platform" -version = "0.2.19" +version = "0.2.20" source = { editable = "../uipath-platform" } dependencies = [ { name = "anyio" },