add Previous link to Project > Stacks table

Stack list API supports a sort direction and sort key which we can
use to paginate back

Following same convention as merged patch:
https://review.openstack.org/#/c/91111/

Change-Id: I511d7d27a6234e1510c7fa3c7f84c007599f3721
Partially-implements: blueprint pagination-add-prev-link
This commit is contained in:
Cindy Lu 2014-07-15 10:10:42 -07:00
parent 26f51f864e
commit 1bde43d1c7
4 changed files with 237 additions and 19 deletions

View File

@ -52,7 +52,8 @@ def heatclient(request, password=None):
return client
def stacks_list(request, marker=None, paginate=False):
def stacks_list(request, marker=None, sort_dir='desc', sort_key='created_at',
paginate=False):
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
page_size = utils.get_page_size(request)
@ -61,20 +62,28 @@ def stacks_list(request, marker=None, paginate=False):
else:
request_size = limit
kwargs = {}
kwargs = {'sort_dir': sort_dir, 'sort_key': sort_key}
if marker:
kwargs['marker'] = marker
stacks_iter = heatclient(request).stacks.list(limit=request_size,
**kwargs)
has_prev_data = False
has_more_data = False
stacks = list(stacks_iter)
if paginate:
if len(stacks) > page_size:
stacks.pop()
has_more_data = True
return (stacks, has_more_data)
if marker is not None:
has_prev_data = True
elif sort_dir == 'asc' and marker is not None:
has_more_data = True
elif marker is not None:
has_prev_data = True
return (stacks, has_more_data, has_prev_data)
def stack_delete(request, stack_id):

View File

@ -103,28 +103,31 @@ class StackTests(test.TestCase):
@test.create_stubs({api.heat: ('stacks_list',)})
def test_index_paginated(self):
stacks = self.stacks.list()[:5]
# import pdb; pdb.set_trace()
api.heat.stacks_list(IsA(http.HttpRequest),
marker=None,
paginate=True) \
paginate=True,
sort_dir='desc') \
.AndReturn([stacks,
True])
True, True])
api.heat.stacks_list(IsA(http.HttpRequest),
marker=None,
paginate=True) \
paginate=True,
sort_dir='desc') \
.AndReturn([stacks[:2],
True])
True, True])
api.heat.stacks_list(IsA(http.HttpRequest),
marker=stacks[2].id,
paginate=True) \
paginate=True,
sort_dir='desc') \
.AndReturn([stacks[2:4],
True])
True, True])
api.heat.stacks_list(IsA(http.HttpRequest),
marker=stacks[4].id,
paginate=True) \
paginate=True,
sort_dir='desc') \
.AndReturn([stacks[4:],
True])
True, True])
self.mox.ReplayAll()
url = reverse('horizon:project:stacks:index')
@ -153,6 +156,62 @@ class StackTests(test.TestCase):
self.assertEqual(len(res.context['stacks_table'].data),
1)
@override_settings(API_RESULT_PAGE_SIZE=2)
@test.create_stubs({api.heat: ('stacks_list',)})
def test_index_prev_paginated(self):
stacks = self.stacks.list()[:3]
api.heat.stacks_list(IsA(http.HttpRequest),
marker=None,
paginate=True,
sort_dir='desc') \
.AndReturn([stacks,
True, False])
api.heat.stacks_list(IsA(http.HttpRequest),
marker=None,
paginate=True,
sort_dir='desc') \
.AndReturn([stacks[:2],
True, True])
api.heat.stacks_list(IsA(http.HttpRequest),
marker=stacks[2].id,
paginate=True,
sort_dir='desc') \
.AndReturn([stacks[2:],
True, True])
api.heat.stacks_list(IsA(http.HttpRequest),
marker=stacks[2].id,
paginate=True,
sort_dir='asc') \
.AndReturn([stacks[:2],
True, True])
self.mox.ReplayAll()
url = reverse('horizon:project:stacks:index')
res = self.client.get(url)
# get all
self.assertEqual(len(res.context['stacks_table'].data),
len(stacks))
self.assertTemplateUsed(res, 'project/stacks/index.html')
res = self.client.get(url)
# get first page with 2 items
self.assertEqual(len(res.context['stacks_table'].data),
settings.API_RESULT_PAGE_SIZE)
url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'),
tables.StacksTable._meta.pagination_param, stacks[2].id)
res = self.client.get(url)
# get second page (item 3)
self.assertEqual(len(res.context['stacks_table'].data), 1)
url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'),
tables.StacksTable._meta.prev_pagination_param, stacks[2].id)
res = self.client.get(url)
# prev back to get first page with 2 pages
self.assertEqual(len(res.context['stacks_table'].data),
settings.API_RESULT_PAGE_SIZE)
@test.create_stubs({api.heat: ('stack_create', 'template_validate')})
def test_launch_stack(self):
template = self.stack_templates.first()

View File

@ -12,6 +12,7 @@
import json
import logging
from operator import attrgetter
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
@ -46,18 +47,34 @@ class IndexView(tables.DataTableView):
super(IndexView, self).__init__(*args, **kwargs)
self._more = None
def has_prev_data(self, table):
return self._prev
def has_more_data(self, table):
return self._more
def get_data(self):
stacks = []
marker = self.request.GET.get(
project_tables.StacksTable._meta.pagination_param)
prev_marker = self.request.GET.get(
project_tables.StacksTable._meta.prev_pagination_param)
if prev_marker is not None:
sort_dir = 'asc'
marker = prev_marker
else:
sort_dir = 'desc'
marker = self.request.GET.get(
project_tables.StacksTable._meta.pagination_param)
try:
stacks, self._more = api.heat.stacks_list(self.request,
marker=marker,
paginate=True)
stacks, self._more, self._prev = api.heat.stacks_list(
self.request,
marker=marker,
paginate=True,
sort_dir=sort_dir)
if prev_marker is not None:
stacks = sorted(stacks, key=attrgetter('creation_time'),
reverse=True)
except Exception:
self._prev = False
self._more = False
msg = _('Unable to retrieve stack list.')
exceptions.handle(self.request, msg)

View File

@ -24,11 +24,144 @@ class HeatApiTests(test.APITestCase):
heatclient = self.stub_heatclient()
heatclient.stacks = self.mox.CreateMockAnything()
heatclient.stacks.list(limit=limit).AndReturn(iter(api_stacks))
heatclient.stacks.list(limit=limit,
sort_dir='desc',
sort_key='created_at',) \
.AndReturn(iter(api_stacks))
self.mox.ReplayAll()
stacks, has_more = api.heat.stacks_list(self.request)
stacks, has_more, has_prev = api.heat.stacks_list(self.request)
self.assertItemsEqual(stacks, api_stacks)
self.assertFalse(has_more)
self.assertFalse(has_prev)
@override_settings(API_RESULT_PAGE_SIZE=2)
def test_stack_list_sort_options(self):
# Verify that sort_dir and sort_key work
api_stacks = self.stacks.list()
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
sort_dir = 'asc'
sort_key = 'size'
heatclient = self.stub_heatclient()
heatclient.stacks = self.mox.CreateMockAnything()
heatclient.stacks.list(limit=limit,
sort_dir=sort_dir,
sort_key=sort_key,) \
.AndReturn(iter(api_stacks))
self.mox.ReplayAll()
stacks, has_more, has_prev = api.heat.stacks_list(self.request,
sort_dir=sort_dir,
sort_key=sort_key)
self.assertItemsEqual(stacks, api_stacks)
self.assertFalse(has_more)
self.assertFalse(has_prev)
@override_settings(API_RESULT_PAGE_SIZE=20)
def test_stack_list_pagination_less_page_size(self):
api_stacks = self.stacks.list()
page_size = settings.API_RESULT_PAGE_SIZE
sort_dir = 'desc'
sort_key = 'created_at'
heatclient = self.stub_heatclient()
heatclient.stacks = self.mox.CreateMockAnything()
heatclient.stacks.list(limit=page_size + 1,
sort_dir=sort_dir,
sort_key=sort_key,) \
.AndReturn(iter(api_stacks))
self.mox.ReplayAll()
stacks, has_more, has_prev = api.heat.stacks_list(self.request,
sort_dir=sort_dir,
sort_key=sort_key,
paginate=True)
expected_stacks = api_stacks[:page_size]
self.assertItemsEqual(stacks, expected_stacks)
self.assertFalse(has_more)
self.assertFalse(has_prev)
@override_settings(API_RESULT_PAGE_SIZE=10)
def test_stack_list_pagination_equal_page_size(self):
api_stacks = self.stacks.list()
page_size = settings.API_RESULT_PAGE_SIZE
sort_dir = 'desc'
sort_key = 'created_at'
heatclient = self.stub_heatclient()
heatclient.stacks = self.mox.CreateMockAnything()
heatclient.stacks.list(limit=page_size + 1,
sort_dir=sort_dir,
sort_key=sort_key,) \
.AndReturn(iter(api_stacks))
self.mox.ReplayAll()
stacks, has_more, has_prev = api.heat.stacks_list(self.request,
sort_dir=sort_dir,
sort_key=sort_key,
paginate=True)
expected_stacks = api_stacks[:page_size]
self.assertItemsEqual(stacks, expected_stacks)
self.assertFalse(has_more)
self.assertFalse(has_prev)
@override_settings(API_RESULT_PAGE_SIZE=2)
def test_stack_list_pagination_marker(self):
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20)
sort_dir = 'desc'
sort_key = 'created_at'
marker = 'nonsense'
api_stacks = self.stacks.list()
heatclient = self.stub_heatclient()
heatclient.stacks = self.mox.CreateMockAnything()
heatclient.stacks.list(limit=page_size + 1,
marker=marker,
sort_dir=sort_dir,
sort_key=sort_key,) \
.AndReturn(iter(api_stacks[:page_size + 1]))
self.mox.ReplayAll()
stacks, has_more, has_prev = api.heat.stacks_list(self.request,
marker=marker,
paginate=True,
sort_dir=sort_dir,
sort_key=sort_key,)
self.assertEqual(len(stacks), page_size)
self.assertItemsEqual(stacks, api_stacks[:page_size])
self.assertTrue(has_more)
self.assertTrue(has_prev)
@override_settings(API_RESULT_PAGE_SIZE=2)
def test_stack_list_pagination_marker_prev(self):
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20)
sort_dir = 'asc'
sort_key = 'created_at'
marker = 'nonsense'
api_stacks = self.stacks.list()
heatclient = self.stub_heatclient()
heatclient.stacks = self.mox.CreateMockAnything()
heatclient.stacks.list(limit=page_size + 1,
marker=marker,
sort_dir=sort_dir,
sort_key=sort_key,) \
.AndReturn(iter(api_stacks[:page_size + 1]))
self.mox.ReplayAll()
stacks, has_more, has_prev = api.heat.stacks_list(self.request,
marker=marker,
paginate=True,
sort_dir=sort_dir,
sort_key=sort_key,)
self.assertEqual(len(stacks), page_size)
self.assertItemsEqual(stacks, api_stacks[:page_size])
self.assertTrue(has_more)
self.assertTrue(has_prev)
def test_template_get(self):
api_stacks = self.stacks.list()