|
7 | 7 | - ``POST /{org}/agenticgovernance_/api/v1/runtime/govern`` — compensating |
8 | 8 | governance call fired when a ``guardrail_fallback`` rule matches |
9 | 9 | (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`). |
10 | 12 |
|
11 | 13 | A third backend endpoint — |
12 | 14 | ``POST /{org}/agenticgovernance_/api/v1/runtime/log`` — emits custom |
|
23 | 25 |
|
24 | 26 | from uipath.core import traced |
25 | 27 | from uipath.core.governance import ( |
| 28 | + AllPoliciesResponse, |
26 | 29 | FiredRule, |
27 | 30 | GovernRequest, |
28 | 31 | PolicyContext, |
|
44 | 47 | POLICY_API_PATH = "api/v1/runtime/policy" |
45 | 48 | GOVERN_API_PATH = "api/v1/runtime/govern" |
46 | 49 | LOG_API_PATH = "api/v1/runtime/log" |
| 50 | +ALL_POLICIES_API_PATH = "api/v1/all-policies" |
47 | 51 | AGENT_TYPE_PARAM = "agentType" |
48 | 52 |
|
49 | 53 | # Caller-set correlation id that becomes the App Insights ``operation_Id`` |
@@ -144,6 +148,104 @@ async def get_policy_async(self, context: PolicyContext) -> PolicyResponse: |
144 | 148 | is_conversational=context.is_conversational |
145 | 149 | ) |
146 | 150 |
|
| 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 | + |
147 | 249 | # ── Compensating governance call ───────────────────────────────── |
148 | 250 |
|
149 | 251 | def compensate( |
|
0 commit comments