Skip to content
Snippets Groups Projects
Commit bc9bb877 authored by Bastien Abadie's avatar Bastien Abadie
Browse files

Merge branch 'list-user-groups' into 'master'

ListMemberships endpoint

See merge request !1099
parents 17e80cdc c3cd8678
No related branches found
No related tags found
1 merge request!1099ListMemberships endpoint
......@@ -101,6 +101,7 @@ from arkindex.users.api import (
UserCreate,
UserEmailLogin,
UserEmailVerification,
UserMemberships,
UserRetrieve,
)
......@@ -252,6 +253,7 @@ api = [
path('groups/', GroupsList.as_view(), name='groups-list'),
path('group/<uuid:pk>/', GroupDetails.as_view(), name='group-details'),
path('group/<uuid:pk>/members/', GroupMembershipsList.as_view(), name='group-members'),
path('user/memberships/', UserMemberships.as_view(), name='user-memberships'),
# Asynchronous jobs
path('jobs/', JobList.as_view(), name='jobs-list'),
......
......@@ -7,7 +7,7 @@ from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.auth.tokens import default_token_generator
from django.core.mail import send_mail
from django.db.models import Count, Q
from django.db.models import Count, Prefetch, Q
from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string
from django.urls import reverse
......@@ -37,9 +37,10 @@ from arkindex.users.models import Group, LevelRight, OAuthStatus, Scope, User, U
from arkindex.users.providers import get_provider, oauth_providers
from arkindex.users.serializers import (
EmailLoginSerializer,
GroupMembershipsSerializer,
GroupSerializer,
JobSerializer,
MemberGroupSerializer,
MemberSerializer,
NewUserSerializer,
OAuthCredentialsSerializer,
OAuthProviderClassSerializer,
......@@ -510,7 +511,7 @@ class GroupMembershipsList(ListAPIView):
"""
List members of a group to which user belongs with their privileges
"""
serializer_class = GroupMembershipsSerializer
serializer_class = MemberSerializer
permission_classes = (IsVerified, )
openapi_overrides = {
'security': [],
......@@ -559,3 +560,25 @@ class GroupDetails(RetrieveUpdateDestroyAPIView):
return Group.objects\
.annotate(members_count=Count('memberships')) \
.filter(Q(users__in=[self.request.user]))
class UserMemberships(ListAPIView):
"""
List groups to which the request user belongs with its privileges on each group
"""
serializer_class = MemberGroupSerializer
permission_classes = (IsVerified, )
openapi_overrides = {
'security': [],
'tags': ['users'],
}
def get_queryset(self):
return self.request.user.memberships \
.annotate(group_members_count=Count('group__users')) \
.prefetch_related(Prefetch(
'group',
# Annotate each group with their members count for serialization
queryset=Group.objects.annotate(members_count=Count('users'))
)) \
.order_by('group__name', 'group__id')
......@@ -220,7 +220,7 @@ class GroupSerializer(serializers.ModelSerializer):
"""
A light group serializer with properties and members count
"""
members_count = serializers.IntegerField(default=1, read_only=True)
members_count = serializers.IntegerField(default=None, read_only=True)
class Meta:
model = Group
......@@ -236,6 +236,8 @@ class GroupSerializer(serializers.ModelSerializer):
group = super().create(validated_data)
# Associate the creator to the group
Membership.objects.create(user=self.context['request'].user, group=group, level=100)
# Set members_count field manually after creation
group.members_count = 1
return group
......@@ -260,7 +262,10 @@ class JobSerializer(serializers.Serializer):
return instance.get_status(refresh=False)
class GroupMembershipsSerializer(serializers.ModelSerializer):
class MemberSerializer(serializers.ModelSerializer):
"""
Serialize a user member of a group with its privileges level
"""
id = serializers.StringRelatedField(source='user.id')
display_name = serializers.StringRelatedField(source='user.display_name')
email = serializers.StringRelatedField(source='user.email')
......@@ -278,3 +283,23 @@ class GroupMembershipsSerializer(serializers.ModelSerializer):
def get_level(self, obj):
right = LevelRight.from_level(obj.level)
return right and right.value
class MemberGroupSerializer(serializers.ModelSerializer):
"""
Serialize a group for a specific member with its privileges level
"""
group = GroupSerializer()
level = serializers.SerializerMethodField()
class Meta:
model = Membership
fields = (
'id',
'group',
'level',
)
def get_level(self, obj):
right = LevelRight.from_level(obj.level)
return right and right.value
......@@ -262,3 +262,52 @@ class TestMembership(FixtureAPITestCase):
with self.assertRaises(Group.DoesNotExist):
group.refresh_from_db()
self.assertTrue(User.objects.filter(id=user.id).exists())
def test_list_user_memberships_requires_login(self):
"""
User must be logged in to list its memberships
"""
with self.assertNumQueries(0):
response = self.client.delete(reverse('api:group-details', kwargs={'pk': str(self.group.id)}))
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(response.json(), {'detail': 'Authentication credentials were not provided.'})
def test_list_user_memberships(self):
"""
List groups for which the user is a member
"""
a_group = self.user.groups.create(name='Another group', through_defaults={'level': LevelRight.Read.level})
self.client.force_login(self.user)
with self.assertNumQueries(5):
response = self.client.get(reverse('api:user-memberships'))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(response.json(), {
'count': 2,
'number': 1,
'next': None,
'previous': None,
'results': [
{
'group':
{
'id': str(a_group.id),
'members_count': 1,
'name': 'Another group',
'public': False
},
'id': a_group.memberships.get(user=self.user).id,
'level': 'read'
},
{
'group':
{
'id': str(self.group.id),
'members_count': self.group.users.count(),
'name': self.group.name,
'public': self.group.public
},
'id': self.group.memberships.get(user=self.user).id,
'level': 'admin'
}
]
})
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment