Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • arkindex/backend
1 result
Show changes
Commits on Source (2)
......@@ -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'
}
]
})