Skip to content

Commit

Permalink
Merge pull request #817 from uktrade/release
Browse files Browse the repository at this point in the history
23rd March Prod deployment
  • Loading branch information
kowalcj0 authored Mar 23, 2020
2 parents 2ffe95f + e21d35e commit c50b5a4
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 148 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Changelog

## Pre-release
- no ticket - Handle non-serializable page requested via /api/pages/<pk>/
- 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)

### Implemented enhancements
- CMS-1859 - Simplify caching system
- no ticket - return only pks on /api/pages/
- no ticket - Add caching to /api/pages/<pk>/
- 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)
Expand Down
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# directory-cms

[![code-climate-image]][code-climate]
[![circle-ci-image]][circle-ci]
[![coverage-image]][coverage]
[![gitflow-image]][gitflow]
Expand Down Expand Up @@ -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

Expand Down
114 changes: 62 additions & 52 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -33,7 +37,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']
)
)

Expand All @@ -48,13 +52,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]
Expand All @@ -80,14 +77,68 @@ 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)
data = queryset.live().values_list('pk', flat=True)
return Response(data)

@cached_property
def object_id(self):
return self.kwargs['pk']

class PagesOptionalDraftAPIEndpoint(APIEndpointBase):
pass
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=204)
return super().handle_exception(exc)


class DetailViewEndpointBase(APIEndpointBase):
Expand Down Expand Up @@ -116,14 +167,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
Expand All @@ -143,39 +186,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):

Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 38 additions & 38 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading

0 comments on commit c50b5a4

Please sign in to comment.