diff --git a/openrag/services/orchestrators/partition_service.py b/openrag/services/orchestrators/partition_service.py index b0204a9e4..68a56c843 100644 --- a/openrag/services/orchestrators/partition_service.py +++ b/openrag/services/orchestrators/partition_service.py @@ -674,7 +674,11 @@ def _meta(row: dict[str, Any]) -> dict[str, Any]: async def list_members(self, partition: str) -> list[dict]: await self._ensure_partition(partition) - return await self._membership_repo.list_partition_members(partition) + members = await self._membership_repo.list_partition_members(partition) + users = await asyncio.gather(*(self._user_repo.get_user(m["user_id"]) for m in members)) + for member, user in zip(members, users, strict=True): + member["display_name"] = user.display_name if user else None + return members async def add_member(self, partition: str, user_id: int, role: str) -> None: await self._ensure_partition(partition) diff --git a/tests/unit/services/orchestrators/test_partition_service.py b/tests/unit/services/orchestrators/test_partition_service.py index ca2497aeb..ba26bc055 100644 --- a/tests/unit/services/orchestrators/test_partition_service.py +++ b/tests/unit/services/orchestrators/test_partition_service.py @@ -212,12 +212,18 @@ async def query_chunks_by_filter(self, collection, filters, output_fields=None): class FakeUserRepo: - def __init__(self, existing: set[int] | None = None): + def __init__(self, existing: set[int] | None = None, display_names: dict[int, str] | None = None): self._existing = existing if existing is not None else set() + self._display_names = display_names or {} async def user_exists(self, user_id: int) -> bool: return user_id in self._existing + async def get_user(self, user_id: int): + if user_id not in self._existing: + return None + return SimpleNamespace(display_name=self._display_names.get(user_id)) + def _svc( *, @@ -830,6 +836,24 @@ async def test_list_members_missing_partition_404(): await _svc(prepo=FakePartitionRepo(set())).list_members("x") +@pytest.mark.asyncio +async def test_list_members_enriches_with_display_name(): + mrepo = FakeMembershipRepo(members={(9, "p")}) + urepo = FakeUserRepo({9}, display_names={9: "Alice"}) + svc = _svc(prepo=FakePartitionRepo({"p"}), mrepo=mrepo, urepo=urepo) + members = await svc.list_members("p") + assert members == [{"user_id": 9, "role": "viewer", "display_name": "Alice"}] + + +@pytest.mark.asyncio +async def test_list_members_missing_user_display_name_is_none(): + mrepo = FakeMembershipRepo(members={(9, "p")}) + urepo = FakeUserRepo(set()) # user_id 9 no longer exists + svc = _svc(prepo=FakePartitionRepo({"p"}), mrepo=mrepo, urepo=urepo) + members = await svc.list_members("p") + assert members[0]["display_name"] is None + + @pytest.mark.asyncio async def test_add_member_checks_partition_and_user(): mrepo = FakeMembershipRepo() diff --git a/ui/src/lib/api/partitions.ts b/ui/src/lib/api/partitions.ts index 13743a141..99d011842 100644 --- a/ui/src/lib/api/partitions.ts +++ b/ui/src/lib/api/partitions.ts @@ -8,7 +8,7 @@ import { request } from "./client"; // POST /partition/{p} create (name in path, NO body; caller becomes owner) → 201 // PATCH /partition/{p} update config → PartitionDetailResponse // DELETE /partition/{p} delete → 204 -// GET /partition/{p}/users members → { members: [{ user_id, role, added_at }] } +// GET /partition/{p}/users members → { members: [{ user_id, display_name, role, added_at }] } // POST /partition/{p}/users add member (multipart: user_id, role) // PATCH /partition/{p}/users/{user_id} change role (multipart: role) // DELETE /partition/{p}/users/{user_id} remove member @@ -194,6 +194,7 @@ export function listPartitionFiles(name: string, limit?: number): Promise<{ file export interface PartitionMember { user_id: number; + display_name: string | null; role: PartitionRole; added_at: string | null; } diff --git a/ui/src/pages/admin/partitions/detail.tsx b/ui/src/pages/admin/partitions/detail.tsx index 704573d3f..636529aa5 100644 --- a/ui/src/pages/admin/partitions/detail.tsx +++ b/ui/src/pages/admin/partitions/detail.tsx @@ -467,7 +467,7 @@ function UsersTab({ partitionName }: { partitionName: string }) { - User ID + User Role Added {canManage && Actions} @@ -476,8 +476,10 @@ function UsersTab({ partitionName }: { partitionName: string }) { {usersQuery.data.members.map((user) => ( - - {user.user_id} + + {user.display_name || ( + {user.user_id} + )} {canManage ? ( @@ -508,7 +510,7 @@ function UsersTab({ partitionName }: { partitionName: string }) { removeMutation.mutate(user.user_id)} >