From d5d7f21c5c25ca79334a3074c86184113b3ecba9 Mon Sep 17 00:00:00 2001 From: Harry White Date: Thu, 24 Jan 2019 15:00:47 +1100 Subject: [PATCH 1/5] Add test for related fields exclude --- report_builder/api/tests.py | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 report_builder/api/tests.py diff --git a/report_builder/api/tests.py b/report_builder/api/tests.py new file mode 100644 index 00000000..a03a7dab --- /dev/null +++ b/report_builder/api/tests.py @@ -0,0 +1,39 @@ +from django.test import TestCase, override_settings +from django.urls import reverse +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth import get_user_model +from rest_framework.test import APIClient +import json + + +class ApiTestCase(TestCase): + + def setUp(self): + self.su = get_user_model().objects.create_superuser('su', 'su@example.com', 'su') + self.client = APIClient() + + def get_child_related_fields(self): + self.client.login(username='su', password='su') + ct = ContentType.objects.get_by_natural_key('demo_models', 'child') + response = self.client.post( + reverse('related_fields'), + {'field': '', 'model': ct.id, 'path': ''}, + format='json' + ) + self.assertEqual(response.status_code, 200) + return response + + def test_related_fields(self): + response = self.get_child_related_fields() + related_fields = json.loads(response.content) + self.assertEqual(len(related_fields), 1) + self.assertEqual(related_fields[0]['field_name'], 'parent') + self.assertTrue(related_fields[0]['included_model']) + + @override_settings(REPORT_BUILDER_EXCLUDE=['demo_models.parent']) + def test_related_fields_exclude(self): + response = self.get_child_related_fields() + related_fields = json.loads(response.content) + self.assertEqual(len(related_fields), 1) + self.assertEqual(related_fields[0]['field_name'], 'parent') + self.assertFalse(related_fields[0]['included_model']) From c6262aa4c75e58f0d52c255438781f2a07a8f75a Mon Sep 17 00:00:00 2001 From: Harry White Date: Thu, 24 Jan 2019 15:33:30 +1100 Subject: [PATCH 2/5] Correctly get model label through field.related_model --- report_builder/api/tests.py | 2 +- report_builder/api/views.py | 25 +++++++------------------ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/report_builder/api/tests.py b/report_builder/api/tests.py index a03a7dab..89ad18da 100644 --- a/report_builder/api/tests.py +++ b/report_builder/api/tests.py @@ -30,7 +30,7 @@ def test_related_fields(self): self.assertEqual(related_fields[0]['field_name'], 'parent') self.assertTrue(related_fields[0]['included_model']) - @override_settings(REPORT_BUILDER_EXCLUDE=['demo_models.parent']) + @override_settings(REPORT_BUILDER_EXCLUDE=['demo_models.person']) def test_related_fields_exclude(self): response = self.get_child_related_fields() related_fields = json.loads(response.content) diff --git a/report_builder/api/views.py b/report_builder/api/views.py index 331a03c6..64c70059 100644 --- a/report_builder/api/views.py +++ b/report_builder/api/views.py @@ -4,18 +4,16 @@ from django.http import JsonResponse from django.utils.functional import cached_property from django.conf import settings -from rest_framework import viewsets, status +from rest_framework import viewsets from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.decorators import action -from rest_framework.response import Response from rest_framework.permissions import IsAdminUser from ..models import Report, Format, FilterField from .serializers import ( ReportNestedSerializer, ReportSerializer, FormatSerializer, FilterFieldSerializer, ContentTypeSerializer) from ..mixins import GetFieldsMixin, DataExportMixin -from django.core import serializers from ..utils import duplicate @@ -35,6 +33,7 @@ class ReportBuilderViewMixin: permission_classes = (IsAdminUser,) pagination_class = None + class ConfigView(ReportBuilderViewMixin, APIView): def get(self, request): data = { @@ -101,8 +100,6 @@ def copy_report(self, request, pk=None): serializer = ReportNestedSerializer(new_report) return JsonResponse(serializer.data) - - class RelatedFieldsView(ReportBuilderViewMixin, GetFieldsMixin, APIView): """ Get related fields from an ORM model """ @@ -123,26 +120,18 @@ def post(self, request): result = [] for new_field in new_fields: included_model = True - split_name = new_field.name.split(':') - if len(split_name) == 1: - split_name.append('') - split_name[1] = split_name[0] - split_name[0] = False - model_information = split_name[1] - else: - model_information = split_name[0] + "." + split_name[1] - app_label = split_name[0] - model_name = split_name[1] + related_model = new_field.related_model + label = related_model._meta.label_lower + app_label, model_name = label.split('.') if getattr(settings, 'REPORT_BUILDER_INCLUDE', False): includes = getattr(settings, 'REPORT_BUILDER_INCLUDE') # If it is not included as 'foo' and not as 'demo_models.foo' - if (model_name not in includes and - model_information not in includes): + if (model_name not in includes and label not in includes): included_model = False if getattr(settings, 'REPORT_BUILDER_EXCLUDE', False): excludes = getattr(settings, 'REPORT_BUILDER_EXCLUDE') # If it is excluded as 'foo' and as 'demo_models.foo' - if (model_name in excludes or model_information in excludes): + if (model_name in excludes or label in excludes): included_model = False verbose_name = getattr(new_field, 'verbose_name', None) if verbose_name is None: From ef3100498562d9eb4138e1a8ceb02f352f94984c Mon Sep 17 00:00:00 2001 From: Harry White Date: Thu, 24 Jan 2019 16:00:31 +1100 Subject: [PATCH 3/5] Added test for hiding content types --- report_builder/api/tests.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/report_builder/api/tests.py b/report_builder/api/tests.py index 89ad18da..6d59a8e5 100644 --- a/report_builder/api/tests.py +++ b/report_builder/api/tests.py @@ -21,19 +21,36 @@ def get_child_related_fields(self): format='json' ) self.assertEqual(response.status_code, 200) - return response + content = json.loads(response.content) + return content def test_related_fields(self): - response = self.get_child_related_fields() - related_fields = json.loads(response.content) + related_fields = self.get_child_related_fields() self.assertEqual(len(related_fields), 1) self.assertEqual(related_fields[0]['field_name'], 'parent') self.assertTrue(related_fields[0]['included_model']) @override_settings(REPORT_BUILDER_EXCLUDE=['demo_models.person']) def test_related_fields_exclude(self): - response = self.get_child_related_fields() - related_fields = json.loads(response.content) + related_fields = self.get_child_related_fields() self.assertEqual(len(related_fields), 1) self.assertEqual(related_fields[0]['field_name'], 'parent') self.assertFalse(related_fields[0]['included_model']) + + def get_content_types(self): + self.client.login(username='su', password='su') + response = self.client.get('/report_builder/api/contenttypes/') + self.assertEqual(response.status_code, 200) + content = json.loads(response.content) + return content + + def test_get_all_content_types(self): + num_content_types = ContentType.objects.count() + response = self.get_content_types() + self.assertEqual(len(response), num_content_types) + + @override_settings(REPORT_BUILDER_EXCLUDE=['demo_models.person']) + def test_get_content_types_with_exclude(self): + num_content_types = ContentType.objects.count() + response = self.get_content_types() + self.assertEqual(len(response), num_content_types - 1) From e37c2c3c49dcbcf008f009932e7eaa56343e60dc Mon Sep 17 00:00:00 2001 From: Harry White Date: Thu, 24 Jan 2019 16:35:01 +1100 Subject: [PATCH 4/5] Fixes content types appearing when exclude or include is set --- report_builder/api/views.py | 6 ++++-- report_builder/urls.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/report_builder/api/views.py b/report_builder/api/views.py index 64c70059..3a0cb992 100644 --- a/report_builder/api/views.py +++ b/report_builder/api/views.py @@ -9,7 +9,7 @@ from rest_framework.response import Response from rest_framework.decorators import action from rest_framework.permissions import IsAdminUser -from ..models import Report, Format, FilterField +from ..models import Report, Format, FilterField, get_allowed_models from .serializers import ( ReportNestedSerializer, ReportSerializer, FormatSerializer, FilterFieldSerializer, ContentTypeSerializer) @@ -57,9 +57,11 @@ class ContentTypeViewSet(ReportBuilderViewMixin, viewsets.ReadOnlyModelViewSet): """ Read only view of content types. Used to populate choices for new report root model. """ - queryset = ContentType.objects.all() serializer_class = ContentTypeSerializer + def get_queryset(self): + return get_allowed_models() + class ReportViewSet(ReportBuilderViewMixin, viewsets.ModelViewSet): queryset = Report.objects.all() diff --git a/report_builder/urls.py b/report_builder/urls.py index c76787e3..55785d23 100644 --- a/report_builder/urls.py +++ b/report_builder/urls.py @@ -10,7 +10,7 @@ router.register(r'report', api_views.ReportNestedViewSet) router.register(r'formats', api_views.FormatViewSet) router.register(r'filterfields', api_views.FilterFieldViewSet) -router.register(r'contenttypes', api_views.ContentTypeViewSet) +router.register(r'contenttypes', api_views.ContentTypeViewSet, base_name='contenttypes') urlpatterns = [ url(r'^report/(?P\d+)/download_file/$', views.DownloadFileView.as_view(), name="report_download_file"), From 61ee75eb0a2d534d6730925f4b426aa379b795e4 Mon Sep 17 00:00:00 2001 From: Harry White Date: Thu, 24 Jan 2019 16:47:25 +1100 Subject: [PATCH 5/5] Restructure api test case, add permissions check test --- report_builder/api/tests.py | 46 ++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/report_builder/api/tests.py b/report_builder/api/tests.py index 6d59a8e5..4e7e84aa 100644 --- a/report_builder/api/tests.py +++ b/report_builder/api/tests.py @@ -9,48 +9,62 @@ class ApiTestCase(TestCase): def setUp(self): - self.su = get_user_model().objects.create_superuser('su', 'su@example.com', 'su') + um = get_user_model() + self.superuser = um.objects.create_superuser('su', 'su@example.com', 'su') + self.regularuser = um.objects.create_user('user', 'user@example.com', 'user') self.client = APIClient() - def get_child_related_fields(self): + def get_json(self, url): self.client.login(username='su', password='su') - ct = ContentType.objects.get_by_natural_key('demo_models', 'child') + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + content = json.loads(response.content) + return content + + def post_json(self, url, data): response = self.client.post( - reverse('related_fields'), - {'field': '', 'model': ct.id, 'path': ''}, + url, + data, format='json' ) self.assertEqual(response.status_code, 200) content = json.loads(response.content) return content + def test_endpoint_not_accessable_by_regular_user(self): + self.client.login(username='user', password='user') + response = self.client.get('/report_builder/api/contenttypes/') + self.assertEqual(response.status_code, 403) + + def get_related_fields_for_ct(self, app_label, model_name): + self.client.login(username='su', password='su') + ct = ContentType.objects.get_by_natural_key(app_label, model_name) + content = self.post_json( + reverse('related_fields'), + {'field': '', 'model': ct.id, 'path': ''}, + ) + return content + def test_related_fields(self): - related_fields = self.get_child_related_fields() + related_fields = self.get_related_fields_for_ct('demo_models', 'child') self.assertEqual(len(related_fields), 1) self.assertEqual(related_fields[0]['field_name'], 'parent') self.assertTrue(related_fields[0]['included_model']) @override_settings(REPORT_BUILDER_EXCLUDE=['demo_models.person']) def test_related_fields_exclude(self): - related_fields = self.get_child_related_fields() + related_fields = self.get_related_fields_for_ct('demo_models', 'child') self.assertEqual(len(related_fields), 1) self.assertEqual(related_fields[0]['field_name'], 'parent') self.assertFalse(related_fields[0]['included_model']) - def get_content_types(self): - self.client.login(username='su', password='su') - response = self.client.get('/report_builder/api/contenttypes/') - self.assertEqual(response.status_code, 200) - content = json.loads(response.content) - return content - def test_get_all_content_types(self): num_content_types = ContentType.objects.count() - response = self.get_content_types() + response = self.get_json('/report_builder/api/contenttypes/') self.assertEqual(len(response), num_content_types) @override_settings(REPORT_BUILDER_EXCLUDE=['demo_models.person']) def test_get_content_types_with_exclude(self): num_content_types = ContentType.objects.count() - response = self.get_content_types() + response = self.get_json('/report_builder/api/contenttypes/') self.assertEqual(len(response), num_content_types - 1)