Skip to content

Commit ee4e54f

Browse files
evanbijoy251claude
andcommitted
feat: add retrieve_all_policies and download_bundle to GovernanceService
Adds retrieve_all_policies() / retrieve_all_policies_async() to fetch WASM bundle metadata from /all-policies/{tenant_id}, and download_bundle() / download_bundle_async() for CDN downloads (no platform auth — pre-signed URLs). Re-exports HookBundle and AllPoliciesResponse from uipath.platform.governance. Bumps uipath-platform to 0.2.7. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8718453 commit ee4e54f

5 files changed

Lines changed: 398 additions & 4 deletions

File tree

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.2.6"
3+
version = "0.2.7"
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/governance/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ._governance_provider import UiPathPlatformGovernanceProvider
99
from ._governance_service import GovernanceService
1010
from .compensate import FiredRule, GovernRequest
11-
from .policy import PolicyContext, PolicyResponse
11+
from .policy import AllPoliciesResponse, HookBundle, PolicyContext, PolicyResponse
1212

1313
# ``_live_track_event_dispatcher.LiveTrackEventDispatcher`` is intentionally
1414
# **not** re-exported. It is host-wiring glue (the runtime sink's
@@ -20,9 +20,11 @@
2020
# )
2121

2222
__all__ = [
23+
"AllPoliciesResponse",
2324
"FiredRule",
2425
"GovernRequest",
2526
"GovernanceService",
27+
"HookBundle",
2628
"PolicyContext",
2729
"PolicyResponse",
2830
"UiPathPlatformGovernanceProvider",

packages/uipath-platform/src/uipath/platform/governance/_governance_service.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
- ``POST /{org}/agenticgovernance_/api/v1/runtime/govern`` — compensating
88
governance call fired when a ``guardrail_fallback`` rule matches
99
(see :meth:`GovernanceService.compensate`).
10+
- ``GET /{org}/agenticgovernance_/api/v1/all-policies/{tenant_id}`` — fetch
11+
WASM bundle metadata for all hooks (see :meth:`GovernanceService.retrieve_all_policies`).
1012
1113
A third backend endpoint —
1214
``POST /{org}/agenticgovernance_/api/v1/runtime/log`` — emits custom
@@ -23,6 +25,7 @@
2325

2426
from uipath.core import traced
2527
from uipath.core.governance import (
28+
AllPoliciesResponse,
2629
FiredRule,
2730
GovernRequest,
2831
PolicyContext,
@@ -44,6 +47,7 @@
4447
POLICY_API_PATH = "api/v1/runtime/policy"
4548
GOVERN_API_PATH = "api/v1/runtime/govern"
4649
LOG_API_PATH = "api/v1/runtime/log"
50+
ALL_POLICIES_API_PATH = "api/v1/all-policies"
4751
AGENT_TYPE_PARAM = "agentType"
4852

4953
# Caller-set correlation id that becomes the App Insights ``operation_Id``
@@ -144,6 +148,104 @@ async def get_policy_async(self, context: PolicyContext) -> PolicyResponse:
144148
is_conversational=context.is_conversational
145149
)
146150

151+
# ── Rego WASM bundle fetch ────────────────────────────────────────
152+
153+
@traced(name="governance_retrieve_all_policies", run_type="uipath")
154+
def retrieve_all_policies(self) -> AllPoliciesResponse:
155+
"""Fetch WASM bundle metadata for all hooks for the active tenant.
156+
157+
Calls ``GET /{org}/agenticgovernance_/api/v1/all-policies/{tenant_id}``
158+
and returns the list of :class:`HookBundle` objects — one per
159+
lifecycle hook that has a compiled WASM policy bundle. Download
160+
each bundle's bytes separately with :meth:`download_bundle`.
161+
162+
Returns:
163+
AllPoliciesResponse: List of hook bundles with pre-signed
164+
URLs and ETags for cache-conditional re-fetches. The list
165+
is empty when no Rego policies are configured for the
166+
tenant.
167+
168+
Raises:
169+
ValueError: If ``UiPathConfig.organization_id`` or
170+
``UiPathConfig.tenant_id`` is not set.
171+
EnrichedException: If the backend returns a non-2xx response.
172+
173+
Examples:
174+
```python
175+
from uipath.platform import UiPath
176+
177+
client = UiPath()
178+
resp = client.governance.retrieve_all_policies()
179+
for bundle in resp.hook_bundles:
180+
data = client.governance.download_bundle(bundle.bundle_url)
181+
```
182+
"""
183+
url, headers = self._build_org_scoped_request(
184+
f"{ALL_POLICIES_API_PATH}/{UiPathConfig.tenant_id}"
185+
)
186+
response = self.request("GET", url=url, headers=headers)
187+
return AllPoliciesResponse.model_validate(response.json())
188+
189+
@traced(name="governance_retrieve_all_policies", run_type="uipath")
190+
async def retrieve_all_policies_async(self) -> AllPoliciesResponse:
191+
"""Asynchronously fetch WASM bundle metadata for all hooks.
192+
193+
See :meth:`retrieve_all_policies` for parameter and return semantics.
194+
"""
195+
url, headers = self._build_org_scoped_request(
196+
f"{ALL_POLICIES_API_PATH}/{UiPathConfig.tenant_id}"
197+
)
198+
response = await self.request_async("GET", url=url, headers=headers)
199+
return AllPoliciesResponse.model_validate(response.json())
200+
201+
def download_bundle(self, bundle_url: str) -> bytes:
202+
"""Download a WASM bundle from a pre-signed URL.
203+
204+
The URL returned by :meth:`retrieve_all_policies` is pre-signed
205+
and carries its own credentials — no platform Bearer token is
206+
sent. Callers should cache the result on disk; use the
207+
:attr:`~HookBundle.etag` from :class:`AllPoliciesResponse` to
208+
skip unchanged bundles on subsequent fetches.
209+
210+
Args:
211+
bundle_url: Pre-signed URL for the WASM ``.tar.gz`` bundle,
212+
as returned in :attr:`HookBundle.bundle_url`.
213+
214+
Returns:
215+
Raw bytes of the ``.tar.gz`` bundle file.
216+
217+
Raises:
218+
httpx.HTTPStatusError: If the returns a non-2xx response.
219+
220+
Examples:
221+
```python
222+
from uipath.platform import UiPath
223+
224+
client = UiPath()
225+
resp = client.governance.retrieve_all_policies()
226+
for bundle in resp.hook_bundles:
227+
data = client.governance.download_bundle(bundle.bundle_url)
228+
# write data to disk ...
229+
```
230+
"""
231+
import httpx
232+
233+
response = httpx.get(bundle_url, follow_redirects=True)
234+
response.raise_for_status()
235+
return response.content
236+
237+
async def download_bundle_async(self, bundle_url: str) -> bytes:
238+
"""Asynchronously download a WASM bundle from a pre-signed URL.
239+
240+
See :meth:`download_bundle` for parameter and return semantics.
241+
"""
242+
import httpx
243+
244+
async with httpx.AsyncClient() as client:
245+
response = await client.get(bundle_url, follow_redirects=True)
246+
response.raise_for_status()
247+
return response.content
248+
147249
# ── Compensating governance call ─────────────────────────────────
148250

149251
def compensate(

packages/uipath-platform/src/uipath/platform/governance/policy.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
keeps the existing ``uipath.platform.governance`` import paths working.
66
"""
77

8-
from uipath.core.governance import PolicyContext, PolicyResponse
8+
from uipath.core.governance import (
9+
AllPoliciesResponse,
10+
HookBundle,
11+
PolicyContext,
12+
PolicyResponse,
13+
)
914

10-
__all__ = ["PolicyContext", "PolicyResponse"]
15+
__all__ = ["AllPoliciesResponse", "HookBundle", "PolicyContext", "PolicyResponse"]

0 commit comments

Comments
 (0)