From e17d868acb6bbaa31166fc7c2e8724d70e56c1f2 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 7 Oct 2022 09:13:51 +0200 Subject: [PATCH 3/6] api: refactor user synchronization API endpoint (#67901) --- src/authentic2/api_views.py | 65 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/authentic2/api_views.py b/src/authentic2/api_views.py index b466f2db..fc23fff9 100644 --- a/src/authentic2/api_views.py +++ b/src/authentic2/api_views.py @@ -52,6 +52,7 @@ from rest_framework.validators import UniqueTogetherValidator from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet, ViewSet +from authentic2.apps.journal.models import reference_integer from authentic2.compat.drf import action from . import api_mixins, app_settings, decorators, hooks @@ -796,31 +797,27 @@ class UsersAPI(api_mixins.GetOrCreateMixinView, HookMixin, ExceptionHandlerMixin full_known_users = serializers.BooleanField(required=False) timestamp = serializers.DateTimeField(required=False) - def check_uuids(self, uuids, timestamp=None): - User = get_user_model() - known_uuids = User.objects.filter(uuid__in=uuids).values_list('uuid', flat=True) - unknown_uuids = set(uuids) - set(known_uuids) - modified_users_uuids = [] - if timestamp: - user_ct = ContentType.objects.get_for_model(get_user_model()) - user_events = Event.objects.filter( - reference_ct_ids__contains=[user_ct.id], timestamp__gt=timestamp - ) - for user_event in user_events: - for reference in user_event.references: - if ( - reference is not None # xxx None references in journal?! - and ContentType.objects.get_for_model(reference) == user_ct - and reference.uuid not in modified_users_uuids - and reference.uuid not in unknown_uuids - ): - modified_users_uuids.append(reference.uuid) - return (known_uuids, unknown_uuids, modified_users_uuids) - - def get_users_from_uuids(self, known_uuids): - User = get_user_model() - known_users = User.objects.filter(uuid__in=known_uuids) - return [BaseUserSerializer(user).data for user in known_users] + def check_unknown_uuids(self, remote_uuids, users): + return set(remote_uuids) - {user.uuid for user in users} + + def check_modified_uuids(self, timestamp, users, unknown_uuids): + modified_users_uuids = set() + user_ct = ContentType.objects.get_for_model(get_user_model()) + reference_ids = [reference_integer(user) for user in users] + user_events = Event.objects.filter( + models.Q(reference_ids__overlap=reference_ids) | models.Q(user__in=users), + timestamp__gt=timestamp, + ) + users_pks = {user.pk: user for user in users} + for user_event in user_events: + for ct_id, instance_pk in user_event.get_reference_ids(): + if ( + ct_id == user_ct.pk + and instance_pk in users_pks + and users_pks[instance_pk].uuid not in modified_users_uuids + ): + modified_users_uuids.add(users_pks[instance_pk].uuid) + return modified_users_uuids @action(detail=False, methods=['post'], permission_classes=(DjangoPermission('custom_user.search_user'),)) def synchronization(self, request): @@ -829,19 +826,23 @@ class UsersAPI(api_mixins.GetOrCreateMixinView, HookMixin, ExceptionHandlerMixin response = {'result': 0, 'errors': serializer.errors} return Response(response, status.HTTP_400_BAD_REQUEST) hooks.call_hooks('api_modify_serializer_after_validation', self, serializer) - known_uuids = serializer.validated_data.get('known_uuids', []) - timestamp = serializer.validated_data.get('timestamp', None) - known_uuids, unknown_uuids, modified_users_uuids = self.check_uuids(known_uuids, timestamp) + remote_uuids = serializer.validated_data.get('known_uuids', []) + users = User.objects.filter(uuid__in=remote_uuids).only('id', 'uuid') + unknown_uuids = self.check_unknown_uuids(remote_uuids, users) data = { 'result': 1, 'unknown_uuids': unknown_uuids, - 'modified_users_uuids': modified_users_uuids, } + + timestamp = serializer.validated_data.get('timestamp', None) + if timestamp: + data['modified_users_uuids'] = self.check_modified_uuids(timestamp, users, unknown_uuids) + full_known_users = serializer.validated_data.get('full_known_users', None) if full_known_users: - if len(known_uuids) > 1000: - known_uuids = known_uuids[:1000] - data['known_users'] = self.get_users_from_uuids(known_uuids) + # reload users to get all fields + known_users = User.objects.filter(pk__in=[user.pk for user in users[:1000]]) + data['known_users'] = [BaseUserSerializer(user).data for user in known_users] hooks.call_hooks('api_modify_response', self, 'synchronization', data) return Response(data) -- 2.37.2