-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add ?tags and ?content filters to wagtail pages API
- Loading branch information
1 parent
cf31375
commit aae4710
Showing
8 changed files
with
237 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from django.db.models import Q | ||
from rest_framework.filters import BaseFilterBackend | ||
|
||
from mycarehub.content.models import ContentItem | ||
|
||
|
||
class TagFilter(BaseFilterBackend): | ||
""" | ||
Implements the ?tag filter which returns only pages that match a tag. | ||
This filter accepts a single tag. | ||
""" | ||
|
||
def filter_queryset(self, request, queryset, view): | ||
query_params = request.query_params | ||
tag = query_params.get("tag", "") | ||
|
||
if tag and queryset.model is ContentItem: | ||
queryset = queryset.filter(Q(tags__name=tag) | Q(tags__slug=tag)) | ||
|
||
return queryset | ||
|
||
|
||
class CategoryFilter(BaseFilterBackend): | ||
""" | ||
Implements the ?category filter which returns only pages that match a | ||
category ID. | ||
This filter accepts a single category ID. | ||
""" | ||
|
||
def filter_queryset(self, request, queryset, view): | ||
query_params = request.query_params | ||
category_id = query_params.get("category", "") | ||
if category_id and queryset.model is ContentItem: | ||
queryset = queryset.filter(categories__id=category_id) | ||
|
||
return queryset |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import pytest | ||
from django.urls import reverse | ||
from django.utils import timezone | ||
from model_bakery import baker | ||
from rest_framework import status | ||
from taggit.models import Tag | ||
from wagtail.core.models import Page, Site | ||
|
||
from mycarehub.content.models import Author, ContentItem, ContentItemCategory, ContentItemIndexPage | ||
from mycarehub.home.models import HomePage | ||
|
||
pytestmark = pytest.mark.django_db | ||
|
||
|
||
@pytest.fixture | ||
def content_item_with_tag_and_category(request_with_user): | ||
# get the root page | ||
site = Site.find_for_request(request_with_user) | ||
assert site is not None | ||
root = site.root_page | ||
assert root is not None | ||
|
||
# set up a home page | ||
home = HomePage( | ||
title="Home", | ||
slug="index", | ||
) | ||
root.add_child(instance=home) | ||
root.save_revision().publish() | ||
|
||
# set up a content item index page | ||
content_item_index = ContentItemIndexPage( | ||
title="Content Item Index", | ||
slug="articles", | ||
intro="content", | ||
) | ||
home.add_child(instance=content_item_index) | ||
home.save_revision().publish() | ||
|
||
# set up a content item | ||
author = baker.make(Author) | ||
content_item = ContentItem( | ||
title="An article", | ||
slug="article-1", | ||
intro="intro", | ||
body="body", | ||
item_type="ARTICLE", | ||
date=timezone.now().date(), | ||
author=author, | ||
) | ||
content_item_index.add_child(instance=content_item) | ||
content_item_index.save_revision().publish() | ||
|
||
# add a category | ||
icon = baker.make("wagtailimages.Image", _create_files=True) | ||
cat = baker.make(ContentItemCategory, id=999_999, name="a valid category", icon=icon) | ||
content_item.categories.add(cat) | ||
content_item.save() | ||
assert ContentItem.objects.filter(categories__id=cat.pk).count() == 1 | ||
|
||
# add a tag | ||
tag = baker.make(Tag, name="a-valid-tag") # expect slug a-valid-tag | ||
content_item.tags.add(tag) | ||
content_item.save() | ||
assert ContentItem.objects.filter(tags__name="a-valid-tag").count() == 1 | ||
|
||
# sanity checks | ||
assert ( | ||
Page.objects.all().public().live().count() >= 4 | ||
) # root, home, content index, content item | ||
assert ContentItem.objects.all().public().live().count() == 1 | ||
|
||
# return the initialized content item | ||
content_item.save_revision().publish() | ||
return content_item | ||
|
||
|
||
def test_tag_filter_found_tags( | ||
content_item_with_tag_and_category, | ||
request_with_user, | ||
client, | ||
): | ||
assert content_item_with_tag_and_category is not None | ||
assert content_item_with_tag_and_category.tags.count() == 1 | ||
assert content_item_with_tag_and_category.tags.filter(name="a-valid-tag").count() == 1 | ||
|
||
client.force_login(request_with_user.user) | ||
url = ( | ||
reverse("wagtailapi:pages:listing") | ||
+ "?type=content.ContentItem&fields=*&order=-first_published_at&tag=a-valid-tag" | ||
) | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
response_data = response.json() | ||
assert response_data["meta"]["total_count"] == 1 | ||
|
||
|
||
def test_tag_filter_absent_tags( | ||
content_item_with_tag_and_category, | ||
request_with_user, | ||
client, | ||
): | ||
assert content_item_with_tag_and_category is not None | ||
assert content_item_with_tag_and_category.tags.count() == 1 | ||
assert content_item_with_tag_and_category.tags.filter(name="not-a-valid-tag").count() == 0 | ||
|
||
client.force_login(request_with_user.user) | ||
url = ( | ||
reverse("wagtailapi:pages:listing") | ||
+ "?type=content.ContentItem&fields=*&order=-first_published_at&tag=not-a-valid-tag" | ||
) | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
response_data = response.json() | ||
assert response_data["meta"]["total_count"] == 0 | ||
|
||
|
||
def test_category_filter_found_categories( | ||
content_item_with_tag_and_category, | ||
request_with_user, | ||
client, | ||
): | ||
assert content_item_with_tag_and_category is not None | ||
assert content_item_with_tag_and_category.categories.count() == 1 | ||
assert content_item_with_tag_and_category.categories.filter(id=999_999).count() == 1 | ||
|
||
client.force_login(request_with_user.user) | ||
url = ( | ||
reverse("wagtailapi:pages:listing") | ||
+ "?type=content.ContentItem&fields=*&order=-first_published_at&category=999999" | ||
) | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
response_data = response.json() | ||
assert response_data["meta"]["total_count"] == 1 | ||
|
||
|
||
def test_category_filter_absent_categories( | ||
content_item_with_tag_and_category, | ||
request_with_user, | ||
client, | ||
): | ||
assert content_item_with_tag_and_category is not None | ||
assert content_item_with_tag_and_category.categories.count() == 1 | ||
assert content_item_with_tag_and_category.categories.filter(id=999_999).count() == 1 | ||
|
||
client.force_login(request_with_user.user) | ||
url = ( | ||
reverse("wagtailapi:pages:listing") | ||
+ "?type=content.ContentItem&fields=*&order=-first_published_at&category=87654321" | ||
) | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
response_data = response.json() | ||
assert response_data["meta"]["total_count"] == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from wagtail.api.v2.filters import ( | ||
AncestorOfFilter, | ||
ChildOfFilter, | ||
DescendantOfFilter, | ||
FieldsFilter, | ||
LocaleFilter, | ||
OrderingFilter, | ||
SearchFilter, | ||
TranslationOfFilter, | ||
) | ||
from wagtail.api.v2.views import PagesAPIViewSet | ||
|
||
from .filters import CategoryFilter, TagFilter | ||
|
||
|
||
class CustomPageAPIViewset(PagesAPIViewSet): | ||
# the order is important...wagtail filters come last | ||
filter_backends = [ | ||
TagFilter, | ||
CategoryFilter, | ||
FieldsFilter, | ||
ChildOfFilter, | ||
AncestorOfFilter, | ||
DescendantOfFilter, | ||
OrderingFilter, | ||
TranslationOfFilter, | ||
LocaleFilter, | ||
SearchFilter, # must be last | ||
] | ||
known_query_parameters = PagesAPIViewSet.known_query_parameters.union(["tag", "category"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters