Fix SQL error with pagination around Repositories

Fixes #1591
This commit is contained in:
Joseph Schorr 2016-06-30 17:31:35 -04:00
parent 36fa93a0fb
commit 1eec6f53b2
4 changed files with 27 additions and 14 deletions

View file

@ -1,4 +1,4 @@
def paginate(query, model, descending=False, page_token=None, limit=50): def paginate(query, model, descending=False, page_token=None, limit=50, id_field='id'):
""" Paginates the given query using an ID range, starting at the optional page_token. """ Paginates the given query using an ID range, starting at the optional page_token.
Returns a *list* of matching results along with an unencrypted page_token for the Returns a *list* of matching results along with an unencrypted page_token for the
next page, if any. If descending is set to True, orders by the ID descending rather next page, if any. If descending is set to True, orders by the ID descending rather
@ -7,9 +7,9 @@ def paginate(query, model, descending=False, page_token=None, limit=50):
query = query.limit(limit + 1) query = query.limit(limit + 1)
if descending: if descending:
query = query.order_by(model.id.desc()) query = query.order_by(getattr(model, id_field).desc())
else: else:
query = query.order_by(model.id) query = query.order_by(getattr(model, id_field))
if page_token is not None: if page_token is not None:
start_id = page_token.get('start_id') start_id = page_token.get('start_id')

View file

@ -231,10 +231,10 @@ def get_visible_repositories(username, namespace=None, include_public=False):
""" Returns the repositories visible to the given user (if any). """ Returns the repositories visible to the given user (if any).
""" """
if not include_public and not username: if not include_public and not username:
return [] return Repository.select().where(Repository.id == -1)
query = (Repository query = (Repository
.select(Repository.name, Repository.id.alias('id'), Repository.description, Namespace.username, .select(Repository.name, Repository.id.alias('rid'), Repository.description, Namespace.username,
Repository.visibility) Repository.visibility)
.distinct() .distinct()
.switch(Repository) .switch(Repository)

View file

@ -166,7 +166,8 @@ class RepositoryList(ApiResource):
# result in far smaller queries. # result in far smaller queries.
if not parsed_args['namespace'] and not parsed_args['starred']: if not parsed_args['namespace'] and not parsed_args['starred']:
repos, next_page_token = model.modelutil.paginate(repo_query, repo_query.c, repos, next_page_token = model.modelutil.paginate(repo_query, repo_query.c,
page_token=page_token, limit=REPOS_PER_PAGE) page_token=page_token, limit=REPOS_PER_PAGE,
id_field='rid')
else: else:
repos = list(repo_query) repos = list(repo_query)
next_page_token = None next_page_token = None

View file

@ -55,7 +55,8 @@ from endpoints.api.organization import (OrganizationList, OrganizationMember,
Organization, ApplicationInformation, Organization, ApplicationInformation,
OrganizationApplications, OrganizationApplicationResource, OrganizationApplications, OrganizationApplicationResource,
OrganizationApplicationResetClientSecret, Organization) OrganizationApplicationResetClientSecret, Organization)
from endpoints.api.repository import RepositoryList, RepositoryVisibility, Repository from endpoints.api.repository import (RepositoryList, RepositoryVisibility, Repository,
REPOS_PER_PAGE)
from endpoints.api.permission import (RepositoryUserPermission, RepositoryTeamPermission, from endpoints.api.permission import (RepositoryUserPermission, RepositoryTeamPermission,
RepositoryTeamPermissionList, RepositoryUserPermissionList) RepositoryTeamPermissionList, RepositoryUserPermissionList)
from endpoints.api.superuser import (SuperUserLogs, SuperUserList, SuperUserManagement, from endpoints.api.superuser import (SuperUserLogs, SuperUserList, SuperUserManagement,
@ -1446,13 +1447,24 @@ class TestListRepos(ApiTestCase):
self.assertEquals(len(json['repositories']), 1) self.assertEquals(len(json['repositories']), 1)
def test_listrepos_asguest_withpages(self):
# Add public repos until we have enough for over 1 page.
public_user = model.user.get_user('public')
for i in range(0, REPOS_PER_PAGE):
model.repository.create_repository('public', 'publicrepo%s' % i, public_user,
visibility='public')
# Request the first page of results.
json = self.getJsonResponse(RepositoryList, params=dict(public=True))
self.assertEquals(len(json['repositories']), REPOS_PER_PAGE)
# Request the second page of results.
json = self.getJsonResponse(RepositoryList, params=dict(public=True,
next_page=json['next_page']))
self.assertGreater(len(json['repositories']), 0)
def test_listrepos_asorgmember(self): def test_listrepos_asorgmember(self):
self.login(READ_ACCESS_USER) self.login(READ_ACCESS_USER)
json = self.getJsonResponse(RepositoryList, params=dict(public=True))
# Queries: Base + the list query
with assert_query_count(BASE_LOGGEDIN_QUERY_COUNT + 2):
json = self.getJsonResponse(RepositoryList, params=dict(public=True))
self.assertGreater(len(json['repositories']), 0) self.assertGreater(len(json['repositories']), 0)
def test_listrepos_filter(self): def test_listrepos_filter(self):
@ -1461,7 +1473,7 @@ class TestListRepos(ApiTestCase):
params=dict(namespace=ORGANIZATION, params=dict(namespace=ORGANIZATION,
public=False)) public=False))
self.assertTrue(len(json['repositories']) > 0) self.assertGreater(len(json['repositories']), 0)
for repo in json['repositories']: for repo in json['repositories']:
self.assertEquals(ORGANIZATION, repo['namespace']) self.assertEquals(ORGANIZATION, repo['namespace'])
@ -1477,7 +1489,7 @@ class TestListRepos(ApiTestCase):
last_modified=True, last_modified=True,
popularity=True)) popularity=True))
self.assertTrue(len(json['repositories']) > 0) self.assertGreater(len(json['repositories']), 0)
for repo in json['repositories']: for repo in json['repositories']:
self.assertEquals(ORGANIZATION, repo['namespace']) self.assertEquals(ORGANIZATION, repo['namespace'])