diff --git a/openwisp_users/api/mixins.py b/openwisp_users/api/mixins.py index 09850ebe..98aeebec 100644 --- a/openwisp_users/api/mixins.py +++ b/openwisp_users/api/mixins.py @@ -262,7 +262,9 @@ def filter_for_field(cls, field, name, lookup_expr='exact'): field_name=field.name, user_attr=cls._user_attr, ) - if isinstance(field, ManyToManyField) and field.name != 'user': + if ( + isinstance(field, ManyToManyField) and field.name != 'user' + ): # pragma: no cover return DjangoOrganizationM2MFilter( queryset=field.remote_field.model.objects.all(), label=field.verbose_name.capitalize(), diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py index 4cb3b28d..0cf0f208 100644 --- a/tests/openwisp2/settings.py +++ b/tests/openwisp2/settings.py @@ -47,6 +47,7 @@ 'drf_yasg', 'testapp', 'reversion', + 'django_filters', ] AUTH_USER_MODEL = 'openwisp_users.User' diff --git a/tests/testapp/tests/test_filter_classes.py b/tests/testapp/tests/test_filter_classes.py index 1381a23d..b5f56402 100644 --- a/tests/testapp/tests/test_filter_classes.py +++ b/tests/testapp/tests/test_filter_classes.py @@ -11,6 +11,7 @@ OrganizationUser = load_model('openwisp_users', 'OrganizationUser') OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') +Organization = load_model('openwisp_users', 'Organization') User = get_user_model() @@ -35,6 +36,19 @@ def setUp(self): name='book2', organization=self._get_org('org_a'), shelf=self.shelf_b ) + def _assert_django_filters_options(self, response, shelf_a, shelf_b): + self.assertEqual(response.data[0]['id'], str(shelf_a.id)) + self.assertNotContains(response, str(shelf_b.id)) + # make sure only correct organization is + # visible in the django filters select options + self.assertContains(response, 'org_a') + self.assertNotContains(response, 'org_b') + self.assertNotContains(response, 'org_b') + self.assertNotContains(response, 'default') + self.assertNotContains(response, 'test org') + self.assertNotContains(response, 'test-shelf-a') + self.assertNotContains(response, 'test-shelf-b') + def test_browsable_api_filter_manager(self): operator = self._get_operator() # First user is automatically owner, so created dummy @@ -282,3 +296,41 @@ def test_shelf_with_read_only_org_field(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data[0]['organization'], org1.pk) self.assertNotContains(response, 'org1') + + def test_django_filter_by_org_membership(self): + operator = self._get_operator() + self._create_org_user(user=operator, organization=self._get_org('org_a')) + token = self._obtain_auth_token(operator) + url = reverse('test_shelf_list_member_view') + response = self.client.get( + url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + ) + + self._assert_django_filters_options(response, self.shelf_a, self.shelf_b) + + def test_django_filter_by_org_managed(self): + operator = self._get_operator() + self._create_org_user( + user=self._get_user(), is_admin=True, organization=self._get_org('org_a') + ) + self._create_org_user( + user=operator, is_admin=True, organization=self._get_org('org_a') + ) + token = self._obtain_auth_token(operator) + url = reverse('test_shelf_list_manager_view') + response = self.client.get( + url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + ) + self._assert_django_filters_options(response, self.shelf_a, self.shelf_b) + + def test_django_filter_by_org_owned(self): + operator = self._get_operator() + self._create_org_user( + user=operator, is_admin=True, organization=self._get_org('org_a') + ) + token = self._obtain_auth_token() + url = reverse('test_shelf_list_owner_view') + response = self.client.get( + url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + ) + self._assert_django_filters_options(response, self.shelf_a, self.shelf_b) diff --git a/tests/testapp/views.py b/tests/testapp/views.py index 691ea4d6..a80dc358 100644 --- a/tests/testapp/views.py +++ b/tests/testapp/views.py @@ -1,4 +1,5 @@ import swapper +from django_filters import rest_framework as filters from rest_framework.generics import ( ListAPIView, ListCreateAPIView, @@ -15,6 +16,9 @@ FilterByParentManaged, FilterByParentMembership, FilterByParentOwned, + FilterDjangoByOrgManaged, + FilterDjangoByOrgMembership, + FilterDjangoByOrgOwned, ) from openwisp_users.api.permissions import ( BaseOrganizationPermission, @@ -59,6 +63,12 @@ def get(self, request, *args, **kwargs): return Response({}) +class BaseShelfListFilter: + class Meta: + model = Shelf + fields = ('organization',) + + class ApiMemberView(BaseGetApiView): authentication_classes = (BearerAuthentication,) permission_classes = (IsOrganizationMember,) @@ -99,11 +109,21 @@ class ErrorOrganizationFieldView(BaseGetApiView): organization_field = 'error__organization' +class ShelfListMemberFilter(BaseShelfListFilter, FilterDjangoByOrgMembership): + pass + + class ShelfListMemberView(FilterByOrganizationMembership, ListAPIView): authentication_classes = (BearerAuthentication,) permission_classes = (IsOrganizationMember,) serializer_class = ShelfSerializer queryset = Shelf.objects.all() + filter_backends = (filters.DjangoFilterBackend,) + filterset_class = ShelfListMemberFilter + + +class ShelfListManagerFilter(BaseShelfListFilter, FilterDjangoByOrgManaged): + pass class ShelfListManagerView(FilterByOrganizationManaged, ListAPIView): @@ -111,6 +131,12 @@ class ShelfListManagerView(FilterByOrganizationManaged, ListAPIView): permission_classes = (IsOrganizationManager,) serializer_class = ShelfSerializer queryset = Shelf.objects.all() + filter_backends = (filters.DjangoFilterBackend,) + filterset_class = ShelfListManagerFilter + + +class ShelfListOwnerFilter(BaseShelfListFilter, FilterDjangoByOrgOwned): + pass class ShelfListOwnerView(FilterByOrganizationOwned, ListAPIView): @@ -118,6 +144,8 @@ class ShelfListOwnerView(FilterByOrganizationOwned, ListAPIView): permission_classes = (IsOrganizationOwner,) serializer_class = ShelfSerializer queryset = Shelf.objects.all() + filter_backends = (filters.DjangoFilterBackend,) + filterset_class = ShelfListOwnerFilter class BooksListMemberView(BookOrgMixin, FilterByParentMembership, ListCreateAPIView):