From 67bb8e25e28efbe140b74a95a61d2415233b7489 Mon Sep 17 00:00:00 2001 From: Prashant Makwana Date: Tue, 13 Aug 2024 11:29:12 -0400 Subject: [PATCH] feat: Updating enterprise-customer-support endpoint (#2199) * feat: adding search and ranking criteria for enterprise-customer-support endpoint * feat: adding search and ranking criteria for enterprise-customer-support endpoint * feat: updating ordering criteria * style: lint fix * chore: updating ordering logic * style: lint fix * feat: updating secondary sorting criteria to include first name * chore: bumping up version num --- CHANGELOG.rst | 4 + enterprise/__init__.py | 2 +- .../v1/views/enterprise_customer_support.py | 94 ++++++++++++++++--- 3 files changed, 87 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cfa01066c..5b6fd718c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,10 @@ Unreleased ---------- * nothing unreleased +[4.23.8] +---------- +* feat: updating enterprise-customer-support endpoint + [4.23.7] --------- * feat: add migration for model updateroleassignmentswithcustomersconfig diff --git a/enterprise/__init__.py b/enterprise/__init__.py index 9561ae010..70734e28c 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.23.7" +__version__ = "4.23.8" diff --git a/enterprise/api/v1/views/enterprise_customer_support.py b/enterprise/api/v1/views/enterprise_customer_support.py index bf980037a..389864ef2 100644 --- a/enterprise/api/v1/views/enterprise_customer_support.py +++ b/enterprise/api/v1/views/enterprise_customer_support.py @@ -6,16 +6,20 @@ from collections import OrderedDict from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import permissions, response, status +from rest_framework import filters, permissions, response, status from rest_framework.pagination import PageNumberPagination +from django.contrib import auth from django.core.exceptions import ValidationError +from django.db.models import Q from enterprise import models from enterprise.api.v1 import serializers from enterprise.api.v1.views.base_views import EnterpriseReadOnlyModelViewSet from enterprise.logging import getEnterpriseLogger +User = auth.get_user_model() + LOGGER = getEnterpriseLogger(__name__) @@ -40,9 +44,6 @@ def paginate_queryset(self, queryset, request, view=None): Paginate a queryset if required, either returning a page object, or `None` if pagination is not configured for this view. - This method is a modified version of the original `paginate_queryset` method - from the `PageNumberPagination` class. The original method was modified to - handle the case where the `queryset` is a `filter` object. """ if isinstance(queryset, filter): queryset = list(queryset) @@ -52,47 +53,116 @@ def paginate_queryset(self, queryset, request, view=None): class EnterpriseCustomerSupportViewSet(EnterpriseReadOnlyModelViewSet): """ - API views for the ``enterprise-user`` API endpoint. + API views for the ``enterprise-customer-support`` API endpoint. """ - queryset = models.EnterpriseCustomerUser.objects.all() - filter_backends = (DjangoFilterBackend,) + queryset = models.PendingEnterpriseCustomerUser.objects.all() + filter_backends = (DjangoFilterBackend, filters.OrderingFilter) permission_classes = (permissions.IsAuthenticated,) paginator = EnterpriseCustomerSupportPaginator() - USER_ID_FILTER = 'enterprise_customer_users__user_id' + ordering_fields = ['id'] + filterset_fields = ['user_email'] + + def filter_queryset_by_email(self, queryset, is_pending_user=False): + """ + Filter queryset based on user provided email address + """ + filter_email = self.request.query_params.get('user_email', None) + + if filter_email: + if not is_pending_user: + queryset = queryset.filter( + user_id__in=User.objects.filter(Q(email__icontains=filter_email)) + ) + else: + queryset = queryset.filter(user_email=filter_email) + + return queryset def retrieve(self, request, *args, **kwargs): """ - - Filter down the queryset of groups available to the requesting uuid. + Filter down the queryset of groups available to the requesting uuid. """ enterprise_uuid = kwargs.get('enterprise_uuid', None) users = [] + try: enterprise_customer_queryset = models.EnterpriseCustomerUser.objects.filter( enterprise_customer__uuid=enterprise_uuid, ) + enterprise_customer_queryset = self.filter_queryset_by_email(enterprise_customer_queryset) users.extend(enterprise_customer_queryset) + pending_enterprise_customer_queryset = models.PendingEnterpriseCustomerUser.objects.filter( enterprise_customer__uuid=enterprise_uuid ).order_by('user_email') + pending_enterprise_customer_queryset = self.filter_queryset_by_email( + pending_enterprise_customer_queryset, + is_pending_user=True + ) users.extend(pending_enterprise_customer_queryset) + except ValidationError: - # did not find UUID match in either EnterpriseCustomerUser or PendingEnterpriseCustomerUser + # did not find UUID match in either EnterpriseCustomerUser or PendingEnterpriseCustomerUser return response.Response( {'detail': 'Could not find enterprise uuid {}'.format(enterprise_uuid)}, status=status.HTTP_404_NOT_FOUND ) + # default sort criteria + is_reversed = False + sort_field = 'first_name' + + ordering_criteria = self.request.query_params.get('ordering', None) + + # apply pre-serialization ordering by user criteria before the users + # get divvied up by pagination + if ordering_criteria: + is_reversed = '-' in ordering_criteria + sort_field = 'user_id' + + # sort the users by default or specified criteria since the queryset will get + # split up during pagination and the post-serialization sort operations + # will be only applied to a single page of results + users = sorted( + users, + key=( + lambda k: + getattr(k, sort_field) + if hasattr(k, sort_field) + else k.id + ), + reverse=is_reversed + ) + + # paginate the queryset users_page = self.paginator.paginate_queryset( users, request, view=self ) + + # serialize the paged dataset serializer = serializers.EnterpriseUserSerializer( users_page, many=True ) - serializer_data = sorted( - serializer.data, key=lambda k: k['is_admin'], reverse=True) + serializer_data = serializer.data + + # Apply post-serialization default ordering criteria (first by is_admin, + # then first name) only if user does not specify ordering criteria; + # Process this after the data has been serialized since the is_admin + # field is computed/available only after serialization step + if not ordering_criteria: + serializer_data = sorted( + serializer_data, + key=lambda k: ( + # sort by is_admin = True first (i.e. -1), + # then sort by first_name lexicographically + (-1 * k['is_admin'], k['enterprise_customer_user']['first_name']) + if k['enterprise_customer_user'] is not None + else -1 * k['is_admin'] + ) + ) return self.paginator.get_paginated_response(serializer_data)