From 6577dccfc14b700d83be65613de1373223604cc0 Mon Sep 17 00:00:00 2001 From: Alessandro De Noia Date: Fri, 6 Mar 2020 12:52:33 +0000 Subject: [PATCH 1/7] remove code climate --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 8a245cf5..b998ddf5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # directory-cms -[![code-climate-image]][code-climate] [![circle-ci-image]][circle-ci] [![coverage-image]][coverage] [![gitflow-image]][gitflow] @@ -122,9 +121,6 @@ https://github.com/uktrade?q=directory https://github.com/uktrade?q=great -[code-climate-image]: https://codeclimate.com/github/uktrade/directory-cms/badges/issue_count.svg -[code-climate]: https://codeclimate.com/github/uktrade/directory-cms - [circle-ci-image]: https://circleci.com/gh/uktrade/directory-cms/tree/develop.svg?style=svg [circle-ci]: https://circleci.com/gh/uktrade/directory-cms/tree/develop From d64225cb930ce4ae64bbcc7692d6b7595049ac06 Mon Sep 17 00:00:00 2001 From: Alessandro De Noia Date: Fri, 6 Mar 2020 15:03:56 +0000 Subject: [PATCH 2/7] Changelog for release 2020.03.06 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47308cf9..cab09495 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Pre-release +## [2020.03.06](https://github.com/uktrade/directory-cms/releases/tag/2020.03.06) +[Full Changelog](https://github.com/uktrade/directory-cms/compare/2020.03.04...2020.03.06) ### Implemented enhancements - CMS-1859 - Simplify caching system From 2585dcfa143e764fe451a82645d6afdb7f86a9c4 Mon Sep 17 00:00:00 2001 From: richtier Date: Tue, 10 Mar 2020 09:42:19 +0000 Subject: [PATCH 3/7] Cache /api/pages// --- CHANGELOG.md | 3 ++ core/views.py | 98 ++++++++++++++++++++++++--------------------------- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47308cf9..a2cfdc60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Implemented enhancements - CMS-1859 - Simplify caching system +- no ticket - return only pks on /api/pages/ +- no ticket - Add caching to /api/pages// +- no ticket - Remove region support ## [2020.03.04](https://github.com/uktrade/directory-cms/releases/tag/2020.03.04) [Full Changelog](https://github.com/uktrade/directory-cms/compare/2020.03.02...2020.03.04) diff --git a/core/views.py b/core/views.py index 72118c8f..92bb2db6 100644 --- a/core/views.py +++ b/core/views.py @@ -33,7 +33,7 @@ class APIEndpointBase(PagesAdminAPIEndpoint): meta_fields = [] known_query_parameters = ( PagesAdminAPIEndpoint.known_query_parameters.union( - ['lang', 'draft_token', 'service_name', 'region'] + ['lang', 'draft_token', 'service_name'] ) ) @@ -48,13 +48,6 @@ def get_serializer_class(self): model_class = self.get_model_class() return MODELS_SERIALIZERS_MAPPING[model_class] - def get_serializer_context(self): - context = super().get_serializer_context() - region = self.request.GET.get('region') - if region: - context['region'] = region - return context - @property def permission_classes(self): permission_classes = [SignatureCheckPermission] @@ -80,14 +73,56 @@ def get_object(self): self.handle_activate_language(instance) return instance + def check_parameter_validity(self): + """ + Called by `detail_view()` early in the response cycle to give + the endpoint an opportunity to raise exceptions due to invalid + parameters values being supplied. + """ + self.object_id + + def detail_view(self, request, **kwargs): + # Exit early if there are any issues + self.check_parameter_validity() + + if helpers.is_draft_requested(request): + return super().detail_view(request, pk=None) + + # Return a cached response if one is available + cached_data = cache.PageCache.get( + page_id=self.object_id, + lang=translation.get_language(), + ) + if cached_data: + cached_response = helpers.CachedResponse(cached_data) + cached_response['etag'] = cached_data.get('etag', None) + return get_conditional_response( + request=request, + etag=cached_response['etag'], + response=cached_response, + ) + + # No cached response available + response = super().detail_view(request, pk=None) + if response.status_code == 200: + # Reuse the already-fetched object to populate the cache + cache.CachePopulator.populate_async(self.get_object()) + + # No etag is set for this response because creating one is expensive. + # If API caching is enabled, one will be added to the cached version + # created above. + return response + + +class PagesOptionalDraftAPIEndpoint(APIEndpointBase): def listing_view(self, request): queryset = self.filter_queryset(self.get_queryset()) data = queryset.values_list('pk', flat=True) return Response(data) - -class PagesOptionalDraftAPIEndpoint(APIEndpointBase): - pass + @cached_property + def object_id(self): + return self.kwargs['pk'] class DetailViewEndpointBase(APIEndpointBase): @@ -116,14 +151,6 @@ def object_id(self): """ raise NotImplementedError # pragma: no cover - def check_parameter_validity(self): - """ - Called by `detail_view()` early in the response cycle to give - the endpoint an opportunity to raise exceptions due to invalid - parameters values being supplied. - """ - self.object_id - def get_object(self): if hasattr(self, 'object'): return self.object @@ -143,39 +170,6 @@ def get_object(self): self.object = instance return instance - def detail_view(self, request, **kwargs): - # Exit early if there are any issues - self.check_parameter_validity() - - if helpers.is_draft_requested(request): - return super().detail_view(request, pk=None) - - # Return a cached response if one is available - cached_data = cache.PageCache.get( - page_id=self.object_id, - lang=translation.get_language(), - region=request.GET.get('region'), - ) - if cached_data: - cached_response = helpers.CachedResponse(cached_data) - cached_response['etag'] = cached_data.get('etag', None) - return get_conditional_response( - request=request, - etag=cached_response['etag'], - response=cached_response, - ) - - # No cached response available - response = super().detail_view(request, pk=None) - if response.status_code == 200: - # Reuse the already-fetched object to populate the cache - cache.CachePopulator.populate_async(self.get_object()) - - # No etag is set for this response because creating one is expensive. - # If API caching is enabled, one will be added to the cached version - # created above. - return response - class PageLookupBySlugAPIEndpoint(DetailViewEndpointBase): From f99c283f2c3e70054af6840039ccdc3a49e43206 Mon Sep 17 00:00:00 2001 From: richtier Date: Tue, 10 Mar 2020 13:29:05 +0000 Subject: [PATCH 4/7] Expose only live pages on /api/pages/ --- core/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/views.py b/core/views.py index 92bb2db6..d2b0fcf8 100644 --- a/core/views.py +++ b/core/views.py @@ -117,7 +117,7 @@ def detail_view(self, request, **kwargs): class PagesOptionalDraftAPIEndpoint(APIEndpointBase): def listing_view(self, request): queryset = self.filter_queryset(self.get_queryset()) - data = queryset.values_list('pk', flat=True) + data = queryset.live().values_list('pk', flat=True) return Response(data) @cached_property From fc51153277bd7ff6b4d43b3e143f2392c63f8b7b Mon Sep 17 00:00:00 2001 From: richtier Date: Fri, 20 Mar 2020 15:37:58 +0000 Subject: [PATCH 5/7] Handle non-serializable page requested via /api/pages// --- CHANGELOG.md | 2 + core/views.py | 16 ++++++ requirements.in | 2 +- requirements.txt | 76 ++++++++++++++-------------- requirements_test.txt | 106 +++++++++++++++++++-------------------- tests/core/test_views.py | 16 ++++++ 6 files changed, 126 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d39afae9..a829ef8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## Pre-release +- no ticket - Handle non-serializable page requested via /api/pages// +- no ticket - Upgrade bleach to fix vulnerability ## [2020.03.06](https://github.com/uktrade/directory-cms/releases/tag/2020.03.06) [Full Changelog](https://github.com/uktrade/directory-cms/compare/2020.03.04...2020.03.06) diff --git a/core/views.py b/core/views.py index d2b0fcf8..542442b8 100644 --- a/core/views.py +++ b/core/views.py @@ -22,6 +22,10 @@ from core.serializer_mapping import MODELS_SERIALIZERS_MAPPING +class PageNotSerializableError(NotImplementedError): + pass + + class APIEndpointBase(PagesAdminAPIEndpoint): """At the very deep core this is a DRF GenericViewSet, with a few wagtail layers on top. @@ -124,6 +128,18 @@ def listing_view(self, request): def object_id(self): return self.kwargs['pk'] + def get_serializer_class(self): + model_class = self.get_model_class() + if model_class not in MODELS_SERIALIZERS_MAPPING: + raise PageNotSerializableError(model_class.__name__) + return super().get_serializer_class() + + def handle_exception(self, exc): + if isinstance(exc, PageNotSerializableError): + # page that exists has been requested, but it's not serializable. E.g, it's a folder page + return Response(status=201) + return super().handle_exception(exc) + class DetailViewEndpointBase(APIEndpointBase): detail_only_fields = ['id'] diff --git a/requirements.in b/requirements.in index 2710f9be..8a37d141 100644 --- a/requirements.in +++ b/requirements.in @@ -26,7 +26,7 @@ django-celery-beat==2.0.0 kombu==4.6.6 requests==2.21.0 markdown==2.6 -bleach==3.0.2 +bleach==3.1.1 bleach-whitelist==0.0.9 wagtail-modeltranslation==0.10.2 urllib3>=1.24.2<2.0.0 diff --git a/requirements.txt b/requirements.txt index e0bbca07..f5ed2c6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,82 +8,82 @@ amqp==2.5.2 # via kombu attrs==19.3.0 # via jsonschema beautifulsoup4==4.6.0 # via directory-components, wagtail billiard==3.6.1.0 # via celery -bleach-whitelist==0.0.9 -bleach==3.0.2 -boto3==1.6.3 +bleach-whitelist==0.0.9 # via -r requirements.in +bleach==3.1.1 # via -r requirements.in +boto3==1.6.3 # via -r requirements.in botocore==1.9.23 # via boto3, s3transfer -celery[redis]==4.3.0 +celery[redis]==4.3.0 # via -r requirements.in, django-celery-beat certifi==2019.11.28 # via requests, sentry-sdk chardet==3.0.4 # via requests -directory-components==20.0.0 -directory-constants==18.6.0 -directory-healthcheck==1.1.1 -dj-database-url==0.5.0 -django-admin-ip-restrictor==2.1.0 -django-celery-beat==2.0.0 -django-environ==0.4.5 -django-filter==2.2.0 +directory-components==20.0.0 # via -r requirements.in +directory-constants==18.6.0 # via -r requirements.in, directory-components +directory-healthcheck==1.1.1 # via -r requirements.in +dj-database-url==0.5.0 # via -r requirements.in +django-admin-ip-restrictor==2.1.0 # via -r requirements.in +django-celery-beat==2.0.0 # via -r requirements.in +django-environ==0.4.5 # via -r requirements.in +django-filter==2.2.0 # via -r requirements.in django-health-check==3.8.0 # via directory-healthcheck django-ipware==2.1.0 # via django-admin-ip-restrictor django-modelcluster==4.4 # via wagtail django-modeltranslation==0.14 # via wagtail-modeltranslation -django-pglocks==1.0.2 -django-redis==4.10.0 -django-staff-sso-client==1.0.0 +django-pglocks==1.0.2 # via -r requirements.in +django-redis==4.10.0 # via -r requirements.in +django-staff-sso-client==1.0.0 # via -r requirements.in django-taggit==0.24.0 # via wagtail django-timezone-field==4.0 # via django-celery-beat django-treebeard==4.3 # via wagtail -django==2.2.10 -django_storages==1.7.1 -djangorestframework==3.9.4 +django==2.2.10 # via -r requirements.in, directory-components, directory-constants, directory-healthcheck, django-admin-ip-restrictor, django-celery-beat, django-filter, django-redis, django-staff-sso-client, django-storages, django-taggit, django-timezone-field, django-treebeard, sigauth, wagtail +django_storages==1.7.1 # via -r requirements.in +djangorestframework==3.9.4 # via -r requirements.in, sigauth, wagtail docopt==0.6.2 # via notifications-python-client, num2words docutils==0.15.2 # via botocore draftjs-exporter==2.1.7 # via wagtail future==0.18.2 # via notifications-python-client -gevent==1.2.2 +gevent==1.2.2 # via -r requirements.in greenlet==0.4.15 # via gevent -gunicorn==19.5.0 -html2text==2018.1.9 +gunicorn==19.5.0 # via -r requirements.in +html2text==2018.1.9 # via -r requirements.in html5lib==1.0.1 # via wagtail idna==2.8 # via requests importlib-metadata==1.2.0 # via kombu jmespath==0.9.4 # via boto3, botocore jsonschema==3.0.1 # via directory-components -kombu==4.6.6 -markdown==2.6 +kombu==4.6.6 # via -r requirements.in, celery +markdown==2.6 # via -r requirements.in mohawk==0.3.4 # via sigauth monotonic==1.5 # via notifications-python-client more-itertools==8.0.0 # via zipp -notifications-python-client==5.3.0 -num2words==0.5.10 +notifications-python-client==5.3.0 # via -r requirements.in +num2words==0.5.10 # via -r requirements.in oauthlib==3.1.0 # via requests-oauthlib -pillow==6.2.1 -psycopg2==2.7.3.2 -pycountry==19.8.18 +pillow==6.2.1 # via -r requirements.in, wagtail +psycopg2==2.7.3.2 # via -r requirements.in +pycountry==19.8.18 # via -r requirements.in pyjwt==1.7.1 # via notifications-python-client pyrsistent==0.15.6 # via jsonschema python-crontab==2.4.0 # via django-celery-beat python-dateutil==2.6.1 # via botocore, python-crontab -pytube==9.2.2 +pytube==9.2.2 # via -r requirements.in pytz==2019.3 # via celery, django, django-modelcluster, django-timezone-field, wagtail raven==6.10.0 # via django-staff-sso-client redis==3.3.11 # via celery, django-redis requests-oauthlib==1.3.0 # via django-staff-sso-client -requests==2.21.0 +requests==2.21.0 # via -r requirements.in, notifications-python-client, requests-oauthlib, wagtail s3transfer==0.1.13 # via boto3 -sentry-sdk==0.13.4 -sigauth==4.1.0 +sentry-sdk==0.13.4 # via -r requirements.in +sigauth==4.1.0 # via -r requirements.in six==1.13.0 # via bleach, html5lib, jsonschema, mohawk, pyrsistent, python-dateutil, w3lib, wagtail sqlparse==0.3.0 # via django unidecode==1.1.1 # via wagtail -urllib3==1.24.3 +urllib3==1.24.3 # via -r requirements.in, requests, sentry-sdk vine==1.3.0 # via amqp, celery -w3lib==1.21.0 -wagtail-modeltranslation==0.10.2 -wagtail==2.6.3 -wagtailmedia==0.3.1 +w3lib==1.21.0 # via -r requirements.in +wagtail-modeltranslation==0.10.2 # via -r requirements.in +wagtail==2.6.3 # via -r requirements.in, wagtail-modeltranslation, wagtailmedia +wagtailmedia==0.3.1 # via -r requirements.in webencodings==0.5.1 # via bleach, html5lib -whitenoise==4.1.2 +whitenoise==4.1.2 # via -r requirements.in willow==1.1 # via wagtail zipp==0.6.0 # via importlib-metadata diff --git a/requirements_test.txt b/requirements_test.txt index c35d5328..f7921bdc 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -7,109 +7,109 @@ amqp==2.5.2 # via kombu atomicwrites==1.2.1 # via pytest attrs==18.2.0 # via jsonschema, pytest -beautifulsoup4==4.6.0 +beautifulsoup4==4.6.0 # via -r requirements_test.in, directory-components, wagtail billiard==3.5.0.5 # via celery -bleach-whitelist==0.0.9 -bleach==3.0.2 -boto3==1.6.3 +bleach-whitelist==0.0.9 # via -r requirements.in +bleach==3.1.1 # via -r requirements.in +boto3==1.6.3 # via -r requirements.in botocore==1.9.23 # via boto3, s3transfer -celery[redis]==4.2.1 +celery[redis]==4.2.1 # via -r requirements.in, django-celery-beat certifi==2018.11.29 # via requests, sentry-sdk chardet==3.0.4 # via requests click==7.0 # via pip-tools coverage==4.5.2 # via coveralls, pytest-cov -coveralls==1.8.2 -directory-components==20.0.0 -directory-constants==18.6.0 -directory-healthcheck==1.1.1 -dj-database-url==0.5.0 -django-admin-ip-restrictor==2.1.0 -django-celery-beat==2.0.0 -django-debug-toolbar==2.0 -django-environ==0.4.5 -django-filter==2.2.0 +coveralls==1.8.2 # via -r requirements_test.in +directory-components==20.0.0 # via -r requirements.in +directory-constants==18.6.0 # via -r requirements.in, directory-components +directory-healthcheck==1.1.1 # via -r requirements.in +dj-database-url==0.5.0 # via -r requirements.in +django-admin-ip-restrictor==2.1.0 # via -r requirements.in +django-celery-beat==2.0.0 # via -r requirements.in +django-debug-toolbar==2.0 # via -r requirements_test.in +django-environ==0.4.5 # via -r requirements.in +django-filter==2.2.0 # via -r requirements.in django-health-check==3.8.0 # via directory-healthcheck django-ipware==2.1.0 # via django-admin-ip-restrictor django-modelcluster==4.3 # via wagtail django-modeltranslation==0.13.1 # via wagtail-modeltranslation -django-pglocks==1.0.2 -django-redis==4.10.0 -django-staff-sso-client==1.0.0 +django-pglocks==1.0.2 # via -r requirements.in +django-redis==4.10.0 # via -r requirements.in +django-staff-sso-client==1.0.0 # via -r requirements.in django-taggit==0.23.0 # via wagtail django-timezone-field==4.0 # via django-celery-beat django-treebeard==4.3 # via wagtail -django==2.2.10 -django_storages==1.7.1 -djangorestframework==3.9.4 +django==2.2.10 # via -r requirements.in, directory-components, directory-constants, directory-healthcheck, django-admin-ip-restrictor, django-celery-beat, django-debug-toolbar, django-filter, django-redis, django-staff-sso-client, django-storages, django-taggit, django-timezone-field, django-treebeard, sigauth, wagtail +django_storages==1.7.1 # via -r requirements.in +djangorestframework==3.9.4 # via -r requirements.in, sigauth, wagtail docopt==0.6.2 # via coveralls, notifications-python-client, num2words docutils==0.14 # via botocore draftjs-exporter==2.1.5 # via wagtail -factory-boy==2.8.1 +factory-boy==2.8.1 # via -r requirements_test.in, wagtail-factories faker==1.0.1 # via factory-boy -flake8==3.6.0 -freezegun==0.3.11 +flake8==3.6.0 # via -r requirements_test.in +freezegun==0.3.11 # via -r requirements_test.in future==0.17.1 # via notifications-python-client -gevent==1.2.2 +gevent==1.2.2 # via -r requirements.in greenlet==0.4.15 # via gevent -gunicorn==19.5.0 -html2text==2018.1.9 +gunicorn==19.5.0 # via -r requirements.in +html2text==2018.1.9 # via -r requirements.in html5lib==1.0.1 # via wagtail idna==2.7 # via requests importlib-metadata==0.20 # via kombu, pluggy, pytest jmespath==0.9.3 # via boto3, botocore jsonschema==3.0.1 # via directory-components -kombu==4.6.6 -markdown==2.6 +kombu==4.6.6 # via -r requirements.in, celery +markdown==2.6 # via -r requirements.in mccabe==0.6.1 # via flake8 mohawk==0.3.4 # via sigauth monotonic==1.5 # via notifications-python-client -more-itertools==4.3.0 # via pytest, zipp -notifications-python-client==5.3.0 -num2words==0.5.10 +more-itertools==4.3.0 # via pytest +notifications-python-client==5.3.0 # via -r requirements.in +num2words==0.5.10 # via -r requirements.in oauthlib==3.0.1 # via requests-oauthlib packaging==18.0 # via pytest, pytest-sugar -pillow==6.2.1 -pip-tools==4.3.0 +pillow==6.2.1 # via -r requirements.in, wagtail +pip-tools==4.3.0 # via -r requirements_test.in pluggy==0.12.0 # via pytest -psycopg2==2.7.3.2 +psycopg2==2.7.3.2 # via -r requirements.in py==1.7.0 # via pytest pycodestyle==2.4.0 # via flake8 -pycountry==19.8.18 +pycountry==19.8.18 # via -r requirements.in pyflakes==2.0.0 # via flake8 pyjwt==1.7.1 # via notifications-python-client pyparsing==2.3.0 # via packaging pyrsistent==0.15.2 # via jsonschema -pytest-cov==2.7.1 -pytest-django==3.5.1 -pytest-sugar==0.9.2 -pytest==5.1.2 +pytest-cov==2.7.1 # via -r requirements_test.in +pytest-django==3.5.1 # via -r requirements_test.in +pytest-sugar==0.9.2 # via -r requirements_test.in +pytest==5.1.2 # via -r requirements_test.in, pytest-cov, pytest-django, pytest-sugar python-crontab==2.4.0 # via django-celery-beat python-dateutil==2.6.1 # via botocore, faker, freezegun, python-crontab -pytube==9.2.2 +pytube==9.2.2 # via -r requirements.in pytz==2018.7 # via celery, django, django-modelcluster, django-timezone-field, wagtail raven==6.10.0 # via django-staff-sso-client redis==2.10.6 # via celery, django-redis -requests-mock==1.5.2 +requests-mock==1.5.2 # via -r requirements_test.in requests-oauthlib==1.2.0 # via django-staff-sso-client -requests==2.21.0 +requests==2.21.0 # via -r requirements.in, coveralls, notifications-python-client, requests-mock, requests-oauthlib, wagtail s3transfer==0.1.13 # via boto3 -sentry-sdk==0.13.4 -sigauth==4.1.0 -six==1.12.0 # via bleach, faker, freezegun, html5lib, jsonschema, mohawk, more-itertools, packaging, pip-tools, pyrsistent, python-dateutil, requests-mock, w3lib, wagtail +sentry-sdk==0.13.4 # via -r requirements.in +sigauth==4.1.0 # via -r requirements.in +six==1.12.0 # via bleach, faker, freezegun, html5lib, jsonschema, mohawk, more-itertools, packaging, pip-tools, python-dateutil, requests-mock, w3lib, wagtail sqlparse==0.2.4 # via django, django-debug-toolbar termcolor==1.1.0 # via pytest-sugar text-unidecode==1.2 # via faker unidecode==1.0.23 # via wagtail -urllib3==1.24.2 +urllib3==1.24.2 # via -r requirements.in, requests, sentry-sdk vine==1.1.4 # via amqp -w3lib==1.20.0 -wagtail-factories==2.0.0 -wagtail-modeltranslation==0.10.2 -wagtail==2.6.3 -wagtailmedia==0.3.1 +w3lib==1.20.0 # via -r requirements.in +wagtail-factories==2.0.0 # via -r requirements_test.in +wagtail-modeltranslation==0.10.2 # via -r requirements.in +wagtail==2.6.3 # via -r requirements.in, wagtail-factories, wagtail-modeltranslation, wagtailmedia +wagtailmedia==0.3.1 # via -r requirements.in wcwidth==0.1.7 # via pytest webencodings==0.5.1 # via bleach, html5lib -whitenoise==4.1.2 +whitenoise==4.1.2 # via -r requirements.in willow==1.1 # via wagtail zipp==0.6.0 # via importlib-metadata diff --git a/tests/core/test_views.py b/tests/core/test_views.py index e7b1dfb2..b8e1887a 100644 --- a/tests/core/test_views.py +++ b/tests/core/test_views.py @@ -10,6 +10,7 @@ from core import cache, helpers, permissions, views from core.helpers import CachedResponse from conf.signature import SignatureCheckPermission +from components.models import ComponentsApp from tests.great_international.factories import InternationalSectorPageFactory from .helpers import clean_post_data @@ -329,6 +330,21 @@ def test_translations_exposed(translated_page, settings, client): assert response.json()['meta']['languages'] == expected +@pytest.mark.django_db +def test_unserializable_page_requested(settings, client): + page = ComponentsApp.objects.create( + title_en_gb='the app', + depth=2, + path='/thing', + ) + + url = reverse('api:api:pages:detail', kwargs={'pk': page.pk}) + + response = client.get(url) + + assert response.status_code == 201 + + @pytest.mark.django_db def test_lookup_by_path(international_root_page, page, admin_client): # Creating a semi-realistic page structure and moving page into it From ae2eafbf37e3ff259813e679b87499e9f5feece7 Mon Sep 17 00:00:00 2001 From: Richard Tier Date: Fri, 20 Mar 2020 15:52:01 +0000 Subject: [PATCH 6/7] Update tests/core/test_views.py Co-Authored-By: Janusz Kowalczyk --- tests/core/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_views.py b/tests/core/test_views.py index b8e1887a..9808c2cc 100644 --- a/tests/core/test_views.py +++ b/tests/core/test_views.py @@ -342,7 +342,7 @@ def test_unserializable_page_requested(settings, client): response = client.get(url) - assert response.status_code == 201 + assert response.status_code == 204 @pytest.mark.django_db From 05c4dc9e67181d11892e33f06916a882f1908a46 Mon Sep 17 00:00:00 2001 From: Richard Tier Date: Fri, 20 Mar 2020 15:52:08 +0000 Subject: [PATCH 7/7] Update core/views.py Co-Authored-By: Janusz Kowalczyk --- core/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/views.py b/core/views.py index 542442b8..b02faa21 100644 --- a/core/views.py +++ b/core/views.py @@ -137,7 +137,7 @@ def get_serializer_class(self): def handle_exception(self, exc): if isinstance(exc, PageNotSerializableError): # page that exists has been requested, but it's not serializable. E.g, it's a folder page - return Response(status=201) + return Response(status=204) return super().handle_exception(exc)