Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/uipath-platform/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-platform"
version = "0.1.1"
version = "0.1.2"
description = "HTTP client library for programmatic access to UiPath Platform"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
from ._orchestrator_setup_service import OrchestratorSetupService
from ._processes_service import ProcessesService
from ._queues_service import QueuesService
from ._server_version import get_server_version, get_server_version_async
from ._server_version import (
get_server_info_async,
get_server_version,
get_server_version_async,
)
from .assets import Asset, UserAsset
from .attachment import Attachment
from .buckets import Bucket, BucketFile
Expand All @@ -37,6 +41,7 @@
"ProcessesService",
"QueuesService",
"OrchestratorSetupService",
"get_server_info_async",
"get_server_version",
"get_server_version_async",
"Asset",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Utility for retrieving the Orchestrator server version."""

from typing import Any

import httpx

from ..common._http_config import get_httpx_client_kwargs
Expand Down Expand Up @@ -28,6 +30,28 @@ def get_server_version(domain: str) -> str | None:
return None


async def get_server_info_async(domain: str) -> dict[Any, Any] | None:
"""Get the full Orchestrator server status info.

Args:
domain: The base URL of the UiPath platform (e.g., "https://cloud.uipath.com").

Returns:
The full server status JSON as a dict, or None if the request fails for any reason.
"""
url = f"{domain}/orchestrator_/api/status/version"

try:
client_kwargs = get_httpx_client_kwargs()
client_kwargs["timeout"] = 5.0
async with httpx.AsyncClient(**client_kwargs) as client:
response = await client.get(url)
response.raise_for_status()
return response.json()
except Exception:
return None


async def get_server_version_async(domain: str) -> str | None:
"""Get the Orchestrator server version.

Expand Down
2 changes: 1 addition & 1 deletion packages/uipath-platform/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/uipath/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[project]
name = "uipath"
version = "2.10.22"
version = "2.10.23"
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.2, <0.6.0",
"uipath-runtime>=0.9.1, <0.10.0",
"uipath-platform>=0.1.1, <0.2.0",
"uipath-platform>=0.1.2, <0.2.0",
"click>=8.3.1",
"httpx>=0.28.1",
"pyjwt>=2.10.1",
Expand Down
31 changes: 19 additions & 12 deletions packages/uipath/src/uipath/_cli/_auth/_oidc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import os
from urllib.parse import urlencode, urlparse

from uipath.platform.orchestrator import get_server_version_async
from uipath.platform.orchestrator import get_server_info_async

from .._utils._console import ConsoleLogger
from ._models import AuthConfig
Expand Down Expand Up @@ -50,10 +50,11 @@ async def _select_config_file(domain: str) -> str:

Logic:
1. If domain is alpha/staging/cloud.uipath.com -> use auth_config_cloud.json
2. Otherwise, try to get version from API
3. If version starts with '25.10' -> use auth_config_25_10.json
4. If version can't be determined -> fallback to auth_config_cloud.json
5. Otherwise -> fallback to auth_config_cloud.json
2. Otherwise, fetch server info from API
3. If deployment is not 'ServiceFabric' (cloud deployment) -> use auth_config_cloud.json
4. If version starts with '25.10' (AS release 25.10) -> use auth_config_25_10.json
5. If version starts with '26.3' (AS release 25.10.2) -> use auth_config_25_10_2.json
6. Otherwise (unknown version) -> use auth_config_25_10_2.json

Args:
domain: The UiPath domain
Expand All @@ -65,19 +66,25 @@ async def _select_config_file(domain: str) -> str:
if _is_cloud_domain(domain):
return "auth_config_cloud.json"

# Try to get version from API
version = await get_server_version_async(domain)
# Fetch full server info
info = await get_server_info_async(domain)

# If we can't determine version, fallback to cloud config
if version is None:
# Non-ServiceFabric deployments are cloud
if info is None or info.get("deployment") != "ServiceFabric":
return "auth_config_cloud.json"

# Check if version is 25.10.*
version = info.get("version") or ""

# Check if version is 25.10.* (AS release 25.10)
if version.startswith("25.10"):
return "auth_config_25_10.json"

# Default fallback to cloud config
return "auth_config_cloud.json"
# Check if version is 26.3.* (AS release 25.10.2)
if version.startswith("26.3"):
return "auth_config_25_10_2.json"

# Default fallback to latest AS release config
return "auth_config_25_10_2.json"


class OidcUtils:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"client_id": "36dea5b8-e8bb-423d-8e7b-c808df8f1c00",
"redirect_uri": "http://localhost:__PY_REPLACE_PORT__/oidc/login",
"scope": "offline_access ProcessMining OrchestratorApiUserAccess StudioWebBackend IdentityServerApi ConnectionService DataService DocumentUnderstanding Du.Digitization.Api Du.Classification.Api Du.Extraction.Api Du.Validation.Api EnterpriseContextService Directory JamJamApi LLMGateway LLMOps OMS RCS.FolderAuthorization TM.Projects TM.TestCases TM.Requirements TM.TestSets",
"port": 8104
}
71 changes: 54 additions & 17 deletions packages/uipath/tests/cli/test_oidc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,32 +121,66 @@ def test_is_cloud_domain(self, domain, expected):
assert _is_cloud_domain(domain) == expected

@pytest.mark.parametrize(
"domain,mock_version,expected_config",
"domain,mock_info,expected_config",
[
# Cloud domains should always use auth_config_cloud.json
# Cloud domains always use auth_config_cloud.json (no API call needed)
("https://alpha.uipath.com", None, "auth_config_cloud.json"),
("https://staging.uipath.com", None, "auth_config_cloud.json"),
("https://cloud.uipath.com", None, "auth_config_cloud.json"),
# Version 25.10.* should use auth_config_25_10.json
# Non-ServiceFabric deployments (cloud releases) use auth_config_cloud.json
(
"https://custom.domain.com",
"25.10.0-beta.415",
"auth_config_25_10.json",
{"version": "26.3.0-s188.574", "deployment": None},
"auth_config_cloud.json",
),
(
"https://custom.domain.com",
{"version": "25.10.0-s99", "deployment": "Kubernetes"},
"auth_config_cloud.json",
),
("https://custom.domain.com", "25.10.1", "auth_config_25_10.json"),
# Other versions should fallback to cloud config
("https://custom.domain.com", "24.10.0", "auth_config_cloud.json"),
("https://custom.domain.com", "26.1.0", "auth_config_cloud.json"),
# Unable to determine version should fallback to cloud config
# API unreachable uses auth_config_cloud.json
("https://custom.domain.com", None, "auth_config_cloud.json"),
# ServiceFabric + 25.10.* (AS release 25.10) uses auth_config_25_10.json
(
"https://custom.domain.com",
{"version": "25.10.0-beta.415", "deployment": "ServiceFabric"},
"auth_config_25_10.json",
),
(
"https://custom.domain.com",
{"version": "25.10.1", "deployment": "ServiceFabric"},
"auth_config_25_10.json",
),
# ServiceFabric + 26.3.* (AS release 25.10.2) uses auth_config_25_10_2.json
(
"https://custom.domain.com",
{"version": "26.3.0-beta.188", "deployment": "ServiceFabric"},
"auth_config_25_10_2.json",
),
(
"https://custom.domain.com",
{"version": "26.3.1", "deployment": "ServiceFabric"},
"auth_config_25_10_2.json",
),
# ServiceFabric + unknown version falls back to latest AS config
(
"https://custom.domain.com",
{"version": "24.10.0", "deployment": "ServiceFabric"},
"auth_config_25_10_2.json",
),
(
"https://custom.domain.com",
{"version": "26.1.0", "deployment": "ServiceFabric"},
"auth_config_25_10_2.json",
),
],
)
async def test_select_config_file(self, domain, mock_version, expected_config):
"""Test _select_config_file selects the correct config based on domain and version."""
async def test_select_config_file(self, domain, mock_info, expected_config):
"""Test _select_config_file selects the correct config based on domain and server info."""
with patch(
"uipath._cli._auth._oidc_utils.get_server_version_async",
"uipath._cli._auth._oidc_utils.get_server_info_async",
new_callable=AsyncMock,
return_value=mock_version,
return_value=mock_info,
):
config_file = await _select_config_file(domain)
assert config_file == expected_config
Expand All @@ -170,12 +204,15 @@ async def test_get_auth_config_with_cloud_domain(self):
assert config["port"] == 8104

async def test_get_auth_config_with_25_10_version(self):
"""Test get_auth_config with version 25.10 uses auth_config_25_10.json."""
"""Test get_auth_config with AS 25.10 ServiceFabric deployment uses auth_config_25_10.json."""
with (
patch(
"uipath._cli._auth._oidc_utils.get_server_version_async",
"uipath._cli._auth._oidc_utils.get_server_info_async",
new_callable=AsyncMock,
return_value="25.10.0-beta.415",
return_value={
"version": "25.10.0-beta.415",
"deployment": "ServiceFabric",
},
),
patch(
"uipath._cli._auth._oidc_utils.OidcUtils._find_free_port",
Expand Down
4 changes: 2 additions & 2 deletions packages/uipath/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading