Paginate results of Courses API list endpoint
* Catalog results are now paginated * Implements the new namespaced pagination described at https://openedx.atlassian.net/wiki/pages/viewpage.action?pageId=47481813 * API level code returns pythonic business objects * View layer performs serialize at the view layer * Convert views to use DRF generic views * Removes an unintentional authentication decorator that caused the detail endpoint to return a 401 for anonymous users MA-1724
This commit is contained in:
committed by
Nimisha Asthagiri
parent
11f34c1e8c
commit
f53de2c04a
@@ -29,6 +29,38 @@ class DefaultPagination(pagination.PageNumberPagination):
|
||||
})
|
||||
|
||||
|
||||
class NamespacedPageNumberPagination(pagination.PageNumberPagination):
|
||||
"""
|
||||
Pagination scheme that returns results with pagination metadata
|
||||
embedded in a "pagination" attribute. Can be used with data
|
||||
that comes as a list of items, or as a dict with a "results"
|
||||
attribute that contains a list of items.
|
||||
"""
|
||||
|
||||
page_size_query_param = "page_size"
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
"""
|
||||
Annotate the response with pagination information
|
||||
"""
|
||||
metadata = {
|
||||
'next': self.get_next_link(),
|
||||
'previous': self.get_previous_link(),
|
||||
'count': self.page.paginator.count,
|
||||
'num_pages': self.page.paginator.num_pages,
|
||||
}
|
||||
if isinstance(data, dict):
|
||||
if 'results' not in data:
|
||||
raise TypeError(u'Malformed result dict')
|
||||
data['pagination'] = metadata
|
||||
else:
|
||||
data = {
|
||||
'results': data,
|
||||
'pagination': metadata,
|
||||
}
|
||||
return Response(data)
|
||||
|
||||
|
||||
def paginate_search_results(object_class, search_results, page_size, page):
|
||||
"""
|
||||
Takes edx-search results and returns a Page object populated
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
""" Tests paginator methods """
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
import ddt
|
||||
from mock import Mock, MagicMock
|
||||
from unittest import TestCase
|
||||
from django.http import Http404
|
||||
from django.test import RequestFactory
|
||||
from rest_framework import serializers
|
||||
|
||||
from openedx.core.lib.api.paginators import paginate_search_results
|
||||
from openedx.core.lib.api.paginators import NamespacedPageNumberPagination, paginate_search_results
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -118,6 +123,50 @@ class PaginateSearchResultsTestCase(TestCase):
|
||||
paginate_search_results(self.mock_model, self.search_results, self.default_size, page_num)
|
||||
|
||||
|
||||
class NamespacedPaginationTestCase(TestCase):
|
||||
"""
|
||||
Test behavior of `NamespacedPageNumberPagination`
|
||||
"""
|
||||
|
||||
TestUser = namedtuple('TestUser', ['username', 'email'])
|
||||
|
||||
class TestUserSerializer(serializers.Serializer): # pylint: disable=abstract-method
|
||||
"""
|
||||
Simple serializer to paginate results from
|
||||
"""
|
||||
username = serializers.CharField()
|
||||
email = serializers.CharField()
|
||||
|
||||
expected_data = {
|
||||
'results': [
|
||||
{'username': 'user_5', 'email': 'user_5@example.com'},
|
||||
{'username': 'user_6', 'email': 'user_6@example.com'},
|
||||
{'username': 'user_7', 'email': 'user_7@example.com'},
|
||||
{'username': 'user_8', 'email': 'user_8@example.com'},
|
||||
{'username': 'user_9', 'email': 'user_9@example.com'},
|
||||
],
|
||||
'pagination': {
|
||||
'next': 'http://testserver/endpoint?page=3&page_size=5',
|
||||
'previous': 'http://testserver/endpoint?page_size=5',
|
||||
'count': 25,
|
||||
'num_pages': 5,
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(NamespacedPaginationTestCase, self).setUp()
|
||||
self.paginator = NamespacedPageNumberPagination()
|
||||
self.users = [self.TestUser('user_{}'.format(idx), 'user_{}@example.com'.format(idx)) for idx in xrange(25)]
|
||||
self.request_factory = RequestFactory()
|
||||
|
||||
def test_basic_pagination(self):
|
||||
request = self.request_factory.get('/endpoint', data={'page': 2, 'page_size': 5})
|
||||
request.query_params = {'page': 2, 'page_size': 5}
|
||||
paged_users = self.paginator.paginate_queryset(self.users, request)
|
||||
results = self.TestUserSerializer(paged_users, many=True).data
|
||||
self.assertEqual(self.expected_data, self.paginator.get_paginated_response(results).data)
|
||||
|
||||
|
||||
def build_mock_object(obj_id):
|
||||
""" Build a mock object with the passed id"""
|
||||
mock_object = Mock()
|
||||
|
||||
Reference in New Issue
Block a user