From 708a2768b382d5dd1e3a5019e05fe18f9a40b3d1 Mon Sep 17 00:00:00 2001
From: Nikita Melkozerov <nikita@some.engineering>
Date: Mon, 18 Mar 2024 12:30:30 +0000
Subject: [PATCH] Return a better workspace user roles json

---
 fixbackend/permissions/router.py           |  4 ++-
 fixbackend/workspaces/router.py            |  2 +-
 fixbackend/workspaces/schemas.py           | 42 +++++++++++++++++++---
 tests/fixbackend/workspaces/router_test.py | 32 +++++++----------
 4 files changed, 54 insertions(+), 26 deletions(-)

diff --git a/fixbackend/permissions/router.py b/fixbackend/permissions/router.py
index 4db0aa9a..82b1bacb 100644
--- a/fixbackend/permissions/router.py
+++ b/fixbackend/permissions/router.py
@@ -43,7 +43,9 @@ async def list_roles(
 
         roles = no_assigned_roles + roles
 
-        return [UserRolesRead.from_model(role) for role in roles]
+        only_workspace_roles = [role for role in roles if role.workspace_id == workspace.id]
+
+        return [UserRolesRead.from_model(role) for role in only_workspace_roles]
 
     @router.put("/{workspace_id}/roles/{user_id}")
     async def update_user_role(
diff --git a/fixbackend/workspaces/router.py b/fixbackend/workspaces/router.py
index f3205765..c5091392 100644
--- a/fixbackend/workspaces/router.py
+++ b/fixbackend/workspaces/router.py
@@ -135,7 +135,7 @@ async def list_users(
     ) -> List[WorkspaceUserRead]:
         user_ids = workspace.all_users()
         users = await user_repository.get_by_ids(user_ids)
-        return [WorkspaceUserRead.from_model(user) for user in users]
+        return [WorkspaceUserRead.from_model(user, workspace.id) for user in users]
 
     @router.post("/{workspace_id}/invites/")
     async def invite_to_organization(
diff --git a/fixbackend/workspaces/schemas.py b/fixbackend/workspaces/schemas.py
index 45c5e686..3a413375 100644
--- a/fixbackend/workspaces/schemas.py
+++ b/fixbackend/workspaces/schemas.py
@@ -13,14 +13,15 @@
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from datetime import datetime
+from functools import reduce
 from typing import List, Literal, Optional, Union
 from fixbackend.auth.models import User
 from fixbackend.ids import InvitationId, WorkspaceId, UserId, ExternalId
 
 from pydantic import BaseModel, EmailStr, Field
 
+from fixbackend.permissions.models import Roles, UserRole
 from fixbackend.workspaces.models import Workspace, WorkspaceInvitation
-from fixbackend.permissions.schemas import UserRolesRead
 
 
 class WorkspaceRead(BaseModel):
@@ -179,22 +180,55 @@ class FixUserSource(BaseModel):
 UserSource = Union[FixUserSource]
 
 
+class WorkspaceUserRoleRead(BaseModel):
+    member: bool = Field(description="if user has member role")
+    admin: bool = Field(description="if user has admin role")
+    owner: bool = Field(description="if user has owner role")
+    billing_admin: bool = Field(description="if user has billing role")
+
+    @staticmethod
+    def from_model(model: List[UserRole]) -> "WorkspaceUserRoleRead":
+        role_names = reduce(lambda x, y: x | y, [role.role_names for role in model], Roles(0))
+
+        return WorkspaceUserRoleRead(
+            member=Roles.workspace_member in role_names,
+            admin=Roles.workspace_admin in role_names,
+            owner=Roles.workspace_owner in role_names,
+            billing_admin=Roles.workspace_billing_admin in role_names,
+        )
+
+    model_config = {
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "user_id": "00000000-0000-0000-0000-000000000000",
+                    "user_email": "foo@example.com",
+                    "member": True,
+                    "owner": True,
+                    "admin": False,
+                    "billing_admin": False,
+                }
+            ]
+        }
+    }
+
+
 class WorkspaceUserRead(BaseModel):
     id: UserId = Field(description="The user's unique identifier")
     sources: List[UserSource] = Field(description="Where the user is found")
     name: str = Field(description="The user's name")
     email: str = Field(description="The user's email")
-    roles: List[UserRolesRead] = Field(description="The user's roles")
+    roles: WorkspaceUserRoleRead = Field(description="The user's roles")
     last_login: Optional[datetime] = Field(description="The user's last login time, if any")
 
     @staticmethod
-    def from_model(user: User) -> "WorkspaceUserRead":
+    def from_model(user: User, workspace_id: WorkspaceId) -> "WorkspaceUserRead":
         return WorkspaceUserRead(
             id=user.id,
             sources=[FixUserSource()],
             name=user.email,
             email=user.email,
-            roles=[UserRolesRead.from_model(role) for role in user.roles],
+            roles=WorkspaceUserRoleRead.from_model([role for role in user.roles if role.workspace_id == workspace_id]),
             last_login=None,
         )
 
diff --git a/tests/fixbackend/workspaces/router_test.py b/tests/fixbackend/workspaces/router_test.py
index 97e20f82..5309f6d2 100644
--- a/tests/fixbackend/workspaces/router_test.py
+++ b/tests/fixbackend/workspaces/router_test.py
@@ -110,27 +110,19 @@ async def test_list_workspace_users(
     assert user_json.get("id") == str(user.id)
     assert user_json.get("email") == user.email
     assert user_json.get("name") == user.email
-    assert user_json.get("roles") == [
-        {
-            "user_id": str(user.id),
-            "workspace_id": str(workspace.id),
-            "member": False,
-            "admin": False,
-            "owner": True,
-            "billing_admin": False,
-        }
-    ]
+    assert user_json.get("roles") == {
+        "member": False,
+        "admin": False,
+        "owner": True,
+        "billing_admin": False,
+    }
 
     await role_repository.add_roles(user.id, workspace.id, Roles.workspace_admin)
     response = await client.get(f"/api/workspaces/{workspace.id}/users/")
     user_json = response.json()[0]
-    assert user_json.get("roles") == [
-        {
-            "user_id": str(user.id),
-            "workspace_id": str(workspace.id),
-            "member": False,
-            "admin": True,
-            "owner": True,
-            "billing_admin": False,
-        }
-    ]
+    assert user_json.get("roles") == {
+        "member": False,
+        "admin": True,
+        "owner": True,
+        "billing_admin": False,
+    }