Skip to content

Commit cfcebf4

Browse files
authored
add unassign role to consistent updates (#275)
* Enhance Consistent Updates feature with new role unassignment endpoints - Updated `FactsClient` to support optional query parameters in `build_forward_request` and `send_forward_request` methods, improving flexibility in request handling. - Added new DELETE endpoints for unassigning user roles and deleting role assignments in the `facts_router`, allowing for better management of user roles with a `return_deleted` query parameter. - Refactored `forward_request_then_wait_for_update` to accommodate the new query parameters, ensuring consistent behavior across requests. * Refactor FactsClient methods to remove optional query parameters - Updated `build_forward_request` and `send_forward_request` methods in `FactsClient` to eliminate the optional `query_params` argument, simplifying the request handling process. - Adjusted related calls in `facts_router` to align with the new method signatures, ensuring consistent behavior across the application. * fix pre-commit * Enhance FactsClient and router with query parameter support - Updated `FactsClient` methods to accept optional `query_params`, improving flexibility in request handling. - Introduced a `pre_return_callback` parameter to `convert_response` for custom response processing. - Modified `facts_router` endpoints to utilize the new query parameter functionality, specifically for handling `return_deleted` logic in user role management. - Added a utility function to convert 200 responses to 204, enhancing response consistency. * Refactor FactsClient and router to remove pre_return_callback parameter - Eliminated the `pre_return_callback` parameter from `convert_response` in `FactsClient`, simplifying response handling. - Updated `unassign_user_role` and `delete_role_assignment` functions in `facts_router` to remove references to `pre_return_callback`, ensuring consistent response processing. - Adjusted `forward_request_then_wait_for_update` to align with the new method signature, enhancing clarity and maintainability. * Refactor query parameter handling in FactsClient - Updated the `FactsClient` to use a separate variable for merging query parameters, improving code clarity. - Simplified the request parameter construction by directly using the new variable, enhancing maintainability.
1 parent a7b4f2a commit cfcebf4

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

charts/pdp/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
apiVersion: v2
22
name: pdp
33
description: An official Helm chart for Permit.io PDP (Policy Decision Point)
4-
version: 0.0.4
4+
version: 0.0.4

horizon/facts/client.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Annotated
1+
from typing import Annotated, Any
22
from urllib.parse import urljoin
33

44
from fastapi import Depends, HTTPException
@@ -30,7 +30,13 @@ def client(self) -> AsyncClient:
3030
)
3131
return self._client
3232

33-
async def build_forward_request(self, request: FastApiRequest, path: str) -> HttpxRequest:
33+
async def build_forward_request(
34+
self,
35+
request: FastApiRequest,
36+
path: str,
37+
*,
38+
query_params: dict[str, Any] | None = None,
39+
) -> HttpxRequest:
3440
"""
3541
Build an HTTPX request from a FastAPI request to forward to the facts service.
3642
:param request: FastAPI request
@@ -50,10 +56,11 @@ async def build_forward_request(self, request: FastApiRequest, path: str) -> Htt
5056
)
5157

5258
full_path = urljoin(f"/v2/facts/{project_id}/{environment_id}/", path.removeprefix("/"))
59+
_query_params = {**request.query_params, **(query_params or {})}
5360
return self.client.build_request(
5461
method=request.method,
5562
url=full_path,
56-
params=request.query_params,
63+
params=_query_params,
5764
headers=forward_headers,
5865
content=request.stream(),
5966
)
@@ -62,18 +69,28 @@ async def send(self, request: HttpxRequest, *, stream: bool = False) -> HttpxRes
6269
logger.info(f"Forwarding facts request: {request.method} {request.url}")
6370
return await self.client.send(request, stream=stream)
6471

65-
async def send_forward_request(self, request: FastApiRequest, path: str) -> HttpxResponse:
72+
async def send_forward_request(
73+
self,
74+
request: FastApiRequest,
75+
path: str,
76+
*,
77+
query_params: dict[str, Any] | None = None,
78+
) -> HttpxResponse:
6679
"""
6780
Send a forward request to the facts service.
6881
:param request: FastAPI request
6982
:param path: Backend facts service path to forward to
7083
:return: HTTPX response
7184
"""
72-
forward_request = await self.build_forward_request(request, path)
85+
forward_request = await self.build_forward_request(request, path, query_params=query_params)
7386
return await self.send(forward_request)
7487

7588
@staticmethod
76-
def convert_response(response: HttpxResponse, *, stream: bool = False) -> FastApiResponse:
89+
def convert_response(
90+
response: HttpxResponse,
91+
*,
92+
stream: bool = False,
93+
) -> FastApiResponse:
7794
"""
7895
Convert an HTTPX response to a FastAPI response.
7996
:param response: HTTPX response
@@ -103,6 +120,8 @@ def extract_body(response: HttpxResponse):
103120
return None
104121

105122
try:
123+
if response.status_code == 204:
124+
return None
106125
body = response.json()
107126
except Exception: # noqa: BLE001
108127
logger.exception("Failed to parse response body as JSON, skipping wait for update.")

horizon/facts/router.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,27 @@ async def assign_user_role(
192192
)
193193

194194

195+
@facts_router.delete("/users/{user_id}/roles")
196+
async def unassign_user_role(
197+
request: FastApiRequest,
198+
client: FactsClientDependency,
199+
update_subscriber: DataUpdateSubscriberDependency,
200+
wait_timeout: WaitTimeoutDependency,
201+
timeout_policy: TimeoutPolicyDependency,
202+
user_id: str,
203+
):
204+
return await forward_request_then_wait_for_update(
205+
client,
206+
request,
207+
update_subscriber,
208+
wait_timeout,
209+
path=f"/users/{user_id}/roles",
210+
query_params={"return_deleted": True},
211+
entries_callback=create_role_assignment_data_entries,
212+
timeout_policy=timeout_policy,
213+
)
214+
215+
195216
@facts_router.post("/role_assignments")
196217
async def create_role_assignment(
197218
request: FastApiRequest,
@@ -211,6 +232,26 @@ async def create_role_assignment(
211232
)
212233

213234

235+
@facts_router.delete("/role_assignments")
236+
async def delete_role_assignment(
237+
request: FastApiRequest,
238+
client: FactsClientDependency,
239+
update_subscriber: DataUpdateSubscriberDependency,
240+
wait_timeout: WaitTimeoutDependency,
241+
timeout_policy: TimeoutPolicyDependency,
242+
):
243+
return await forward_request_then_wait_for_update(
244+
client,
245+
request,
246+
update_subscriber,
247+
wait_timeout,
248+
path="/role_assignments",
249+
entries_callback=create_role_assignment_data_entries,
250+
timeout_policy=timeout_policy,
251+
query_params={"return_deleted": True},
252+
)
253+
254+
214255
@facts_router.post("/resource_instances")
215256
async def create_resource_instance(
216257
request: FastApiRequest,
@@ -293,6 +334,12 @@ async def create_relationship_tuple(
293334
)
294335

295336

337+
def cast_delete_200_to_204(response: Response) -> Response:
338+
if response.status_code == 200:
339+
return Response(status_code=204)
340+
return response
341+
342+
296343
async def forward_request_then_wait_for_update(
297344
client: FactsClient,
298345
request: FastApiRequest,
@@ -303,9 +350,10 @@ async def forward_request_then_wait_for_update(
303350
update_id: UUID | None = None,
304351
entries_callback: Callable[[FastApiRequest, dict[str, Any], UUID | None], Iterable[DataSourceEntry]],
305352
timeout_policy: TimeoutPolicy = TimeoutPolicy.IGNORE,
353+
query_params: dict[str, Any] | None = None,
306354
) -> Response:
307355
_update_id = update_id or uuid4()
308-
response = await client.send_forward_request(request, path)
356+
response = await client.send_forward_request(request, path, query_params=query_params)
309357
body = client.extract_body(response)
310358
if body is None:
311359
return client.convert_response(response)

0 commit comments

Comments
 (0)