From 391b37773d69d44d5fa904aaac1fb5029657a2b2 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 30 Oct 2024 08:29:51 +0000 Subject: [PATCH] feat: add group admin to list groups (#4779) --- api/tests/unit/users/test_unit_users_views.py | 65 ++++++++++++++++++- api/users/serializers.py | 23 +++++-- api/users/views.py | 10 +++ 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/api/tests/unit/users/test_unit_users_views.py b/api/tests/unit/users/test_unit_users_views.py index 2a65de5b43ae..f66f8cf01c71 100644 --- a/api/tests/unit/users/test_unit_users_views.py +++ b/api/tests/unit/users/test_unit_users_views.py @@ -19,7 +19,12 @@ from integrations.lead_tracking.hubspot.constants import HUBSPOT_COOKIE_NAME from organisations.invites.models import Invite, InviteLink from organisations.models import Organisation, OrganisationRole -from users.models import FFAdminUser, HubspotTracker, UserPermissionGroup +from users.models import ( + FFAdminUser, + HubspotTracker, + UserPermissionGroup, + UserPermissionGroupMembership, +) def test_join_organisation( @@ -867,3 +872,61 @@ def test_send_reset_password_emails_rate_limit_resets_after_password_reset( # Then - we should receive another email assert len(mail.outbox) == 1 + + +def test_list_user_groups( + organisation: Organisation, + admin_client: APIClient, + django_assert_num_queries: DjangoAssertNumQueries, +) -> None: + # Given + user1 = FFAdminUser.objects.create(email="user1@example.com") + user2 = FFAdminUser.objects.create(email="user2@example.com") + + user1.add_organisation(organisation) + user2.add_organisation(organisation) + + user_permission_group_1 = UserPermissionGroup.objects.create( + organisation=organisation, name="group1" + ) + user_permission_group_2 = UserPermissionGroup.objects.create( + organisation=organisation, name="group2" + ) + + UserPermissionGroupMembership.objects.create( + ffadminuser=user1, userpermissiongroup=user_permission_group_1, group_admin=True + ) + UserPermissionGroupMembership.objects.create( + ffadminuser=user2, userpermissiongroup=user_permission_group_2, group_admin=True + ) + UserPermissionGroupMembership.objects.create( + ffadminuser=user1, userpermissiongroup=user_permission_group_2 + ) + + url = reverse( + "api-v1:organisations:organisation-groups-list", args=[organisation.id] + ) + + # When + with django_assert_num_queries(7): + response = admin_client.get(url) + + # Then + assert response.status_code == status.HTTP_200_OK + + response_json = response.json() + assert response_json["count"] == 2 + + group_1 = response_json["results"][0] + group_1_users = group_1["users"] + assert len(group_1_users) == 1 + assert group_1_users[0]["id"] == user1.pk + assert group_1_users[0]["group_admin"] is True + + group_2 = response_json["results"][1] + group_2_users = group_2["users"] + assert len(group_2_users) == 2 + assert tuple((user["id"], user["group_admin"]) for user in group_2_users) == ( + (user1.pk, False), + (user2.pk, True), + ) diff --git a/api/users/serializers.py b/api/users/serializers.py index 858ae491a2ba..4dc19a6cc0a3 100644 --- a/api/users/serializers.py +++ b/api/users/serializers.py @@ -5,7 +5,11 @@ from organisations.models import Organisation from organisations.serializers import UserOrganisationSerializer -from .models import FFAdminUser, UserPermissionGroup +from .models import ( + FFAdminUser, + UserPermissionGroup, + UserPermissionGroupMembership, +) class UserIdSerializer(serializers.Serializer): @@ -106,13 +110,24 @@ class Meta: class ListUserPermissionGroupMembershipSerializer(serializers.ModelSerializer): + # Note that in order to add the group_admin attribute, we use the UserPermissionGroupMembership + # object instead of the FFAdminUser object. As such, we need to manually define the fields + # and sources here. + id = serializers.IntegerField(source="ffadminuser.id") + email = serializers.EmailField(source="ffadminuser.email") + first_name = serializers.CharField(source="ffadminuser.first_name") + last_name = serializers.CharField(source="ffadminuser.last_name") + last_login = serializers.CharField(source="ffadminuser.last_login") + class Meta: - model = FFAdminUser - fields = ("id", "email", "first_name", "last_name", "last_login") + model = UserPermissionGroupMembership + fields = ("id", "email", "first_name", "last_name", "last_login", "group_admin") class ListUserPermissionGroupSerializer(UserPermissionGroupSerializer): - users = ListUserPermissionGroupMembershipSerializer(many=True, read_only=True) + users = ListUserPermissionGroupMembershipSerializer( + many=True, read_only=True, source="userpermissiongroupmembership_set" + ) class UserPermissionGroupMembershipSerializer(serializers.ModelSerializer): diff --git a/api/users/views.py b/api/users/views.py index 82ee6a41a9b5..684b794bb0a8 100644 --- a/api/users/views.py +++ b/api/users/views.py @@ -189,6 +189,16 @@ def get_queryset(self): q = q & Q(userpermissiongroupmembership__group_admin=True) qs = qs.filter(q) + if self.action == "list": + qs = qs.prefetch_related( + Prefetch( + "userpermissiongroupmembership_set", + queryset=UserPermissionGroupMembership.objects.select_related( + "ffadminuser" + ), + ) + ) + return qs def paginate_queryset(self, queryset: QuerySet) -> list[UserPermissionGroup] | None: