From d97a332d23067f45cb663ef81417409a7a290310 Mon Sep 17 00:00:00 2001 From: rr- Date: Sun, 10 Apr 2016 22:13:38 +0200 Subject: [PATCH] server/api: let users control page size --- API.md | 2 +- server/szurubooru/api/user_api.py | 6 +- server/szurubooru/search/search_executor.py | 5 +- server/szurubooru/tests/api/util.py | 8 +- .../tests/search/test_user_search_config.py | 93 ++++++++++--------- 5 files changed, 64 insertions(+), 50 deletions(-) diff --git a/API.md b/API.md index fe9e4b0..c9da36d 100644 --- a/API.md +++ b/API.md @@ -77,7 +77,7 @@ data. ### Listing users -Request: `GET /users/?page=&query=` +Request: `GET /users/?page=&pageSize=&query=` Output: ```json5 { diff --git a/server/szurubooru/api/user_api.py b/server/szurubooru/api/user_api.py index 42fbade..697f940 100644 --- a/server/szurubooru/api/user_api.py +++ b/server/szurubooru/api/user_api.py @@ -38,11 +38,13 @@ class UserListApi(BaseApi): auth.verify_privilege(context.user, 'users:list') query = context.get_param_as_string('query') page = context.get_param_as_int('page', 1) - count, user_list = self._search_executor.execute(context.session, query, page) + page_size = min(100, context.get_param_as_int('pageSize', required=False) or 100) + count, user_list = self._search_executor.execute( + context.session, query, page, page_size) return { 'query': query, 'page': page, - 'pageSize': self._search_executor.page_size, + 'pageSize': page_size, 'total': count, 'users': [_serialize_user(context.user, user) for user in user_list], } diff --git a/server/szurubooru/search/search_executor.py b/server/szurubooru/search/search_executor.py index 52d3c71..1f58007 100644 --- a/server/szurubooru/search/search_executor.py +++ b/server/szurubooru/search/search_executor.py @@ -13,17 +13,16 @@ class SearchExecutor(object): ORDER_ASC = 2 def __init__(self, search_config): - self.page_size = 100 self._search_config = search_config - def execute(self, session, query_text, page): + def execute(self, session, query_text, page, page_size): ''' Parse input and return tuple containing total record count and filtered entities. ''' filter_query = self._prepare(session, query_text) entities = filter_query \ - .offset((page - 1) * self.page_size).limit(self.page_size).all() + .offset((page - 1) * page_size).limit(page_size).all() count_query = filter_query.statement \ .with_only_columns([sqlalchemy.func.count()]).order_by(None) count = filter_query.session.execute(count_query).scalar() diff --git a/server/szurubooru/tests/api/util.py b/server/szurubooru/tests/api/util.py index a076110..8b252e7 100644 --- a/server/szurubooru/tests/api/util.py +++ b/server/szurubooru/tests/api/util.py @@ -25,12 +25,16 @@ def mock_context(parent): parent.context = context def mock_params(context, params): - def get_param_as_string(key, default=None): + def get_param_as_string(key, default=None, required=False): if key not in params: + if required: + raise RuntimeError('Param is missing!') return default return params[key] - def get_param_as_int(key, default=None): + def get_param_as_int(key, default=None, required=False): if key not in params: + if required: + raise RuntimeError('Param is missing!') return default return int(params[key]) context.get_param_as_string = get_param_as_string diff --git a/server/szurubooru/tests/search/test_user_search_config.py b/server/szurubooru/tests/search/test_user_search_config.py index 0eefbc0..9f3d472 100644 --- a/server/szurubooru/tests/search/test_user_search_config.py +++ b/server/szurubooru/tests/search/test_user_search_config.py @@ -9,11 +9,20 @@ class TestUserSearchExecutor(DatabaseTestCase): self.search_config = search.UserSearchConfig() self.executor = search.SearchExecutor(self.search_config) - def _test(self, query, page, expected_count, expected_user_names): - count, users = self.executor.execute(self.session, query, page) + def _test(self, query, page, page_size, expected_count, expected_user_names): + count, users = self.executor.execute(self.session, query, page, page_size) self.assertEqual(count, expected_count) self.assertEqual([u.name for u in users], expected_user_names) + def _test_raises(self, query, page, page_size): + self.assertRaises( + errors.SearchError, + self.executor.execute, + self.session, + query, + page, + page_size) + def test_filter_by_creation_time(self): user1 = util.mock_user('u1') user2 = util.mock_user('u2') @@ -21,7 +30,7 @@ class TestUserSearchExecutor(DatabaseTestCase): user2.creation_time = datetime(2015, 1, 1) self.session.add_all([user1, user2]) for alias in ['creation-time', 'creation-date']: - self._test('%s:2014' % alias, 1, 1, ['u1']) + self._test('%s:2014' % alias, 1, 100, 1, ['u1']) def test_filter_by_negated_creation_time(self): user1 = util.mock_user('u1') @@ -30,7 +39,7 @@ class TestUserSearchExecutor(DatabaseTestCase): user2.creation_time = datetime(2015, 1, 1) self.session.add_all([user1, user2]) for alias in ['creation-time', 'creation-date']: - self._test('-%s:2014' % alias, 1, 1, ['u2']) + self._test('-%s:2014' % alias, 1, 100, 1, ['u2']) def test_filter_by_ranged_creation_time(self): user1 = util.mock_user('u1') @@ -41,12 +50,11 @@ class TestUserSearchExecutor(DatabaseTestCase): user3.creation_time = datetime(2015, 1, 1) self.session.add_all([user1, user2, user3]) for alias in ['creation-time', 'creation-date']: - self._test('%s:2014..2014-06' % alias, 1, 2, ['u1', 'u2']) - self._test('%s:2014-06..2015-01-01' % alias, 1, 2, ['u2', 'u3']) - self._test('%s:2014-06..' % alias, 1, 2, ['u2', 'u3']) - self._test('%s:..2014-06' % alias, 1, 2, ['u1', 'u2']) - self.assertRaises( - errors.SearchError, self.executor.execute, self.session, '%s:..', 1) + self._test('%s:2014..2014-06' % alias, 1, 100, 2, ['u1', 'u2']) + self._test('%s:2014-06..2015-01-01' % alias, 1, 100, 2, ['u2', 'u3']) + self._test('%s:2014-06..' % alias, 1, 100, 2, ['u2', 'u3']) + self._test('%s:..2014-06' % alias, 1, 100, 2, ['u1', 'u2']) + self._test_raises('%s:..' % alias, 1, 100) def test_filter_by_negated_ranged_creation_time(self): user1 = util.mock_user('u1') @@ -57,8 +65,8 @@ class TestUserSearchExecutor(DatabaseTestCase): user3.creation_time = datetime(2015, 1, 1) self.session.add_all([user1, user2, user3]) for alias in ['creation-time', 'creation-date']: - self._test('-%s:2014..2014-06' % alias, 1, 1, ['u3']) - self._test('-%s:2014-06..2015-01-01' % alias, 1, 1, ['u1']) + self._test('-%s:2014..2014-06' % alias, 1, 100, 1, ['u3']) + self._test('-%s:2014-06..2015-01-01' % alias, 1, 100, 1, ['u1']) def test_filter_by_composite_creation_time(self): user1 = util.mock_user('u1') @@ -69,7 +77,7 @@ class TestUserSearchExecutor(DatabaseTestCase): user3.creation_time = datetime(2015, 1, 1) self.session.add_all([user1, user2, user3]) for alias in ['creation-time', 'creation-date']: - self._test('%s:2014-01,2015' % alias, 1, 2, ['u1', 'u3']) + self._test('%s:2014-01,2015' % alias, 1, 100, 2, ['u1', 'u3']) def test_filter_by_negated_composite_creation_time(self): user1 = util.mock_user('u1') @@ -80,52 +88,50 @@ class TestUserSearchExecutor(DatabaseTestCase): user3.creation_time = datetime(2015, 1, 1) self.session.add_all([user1, user2, user3]) for alias in ['creation-time', 'creation-date']: - self._test('-%s:2014-01,2015' % alias, 1, 1, ['u2']) + self._test('-%s:2014-01,2015' % alias, 1, 100, 1, ['u2']) def test_filter_by_name(self): self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) - self._test('name:u1', 1, 1, ['u1']) - self._test('name:u2', 1, 1, ['u2']) + self._test('name:u1', 1, 100, 1, ['u1']) + self._test('name:u2', 1, 100, 1, ['u2']) def test_filter_by_negated_name(self): self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) - self._test('-name:u1', 1, 1, ['u2']) - self._test('-name:u2', 1, 1, ['u1']) + self._test('-name:u1', 1, 100, 1, ['u2']) + self._test('-name:u2', 1, 100, 1, ['u1']) def test_filter_by_composite_name(self): self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) self.session.add(util.mock_user('u3')) - self._test('name:u1,u2', 1, 2, ['u1', 'u2']) + self._test('name:u1,u2', 1, 100, 2, ['u1', 'u2']) def test_filter_by_negated_composite_name(self): self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) self.session.add(util.mock_user('u3')) - self._test('-name:u1,u3', 1, 1, ['u2']) + self._test('-name:u1,u3', 1, 100, 1, ['u2']) def test_filter_by_ranged_name(self): - self.assertRaises( - errors.SearchError, self.executor.execute, self.session, 'name:u1..u2', 1) + self._test_raises('name:u1..u2', 1, 100) def test_paging(self): - self.executor.page_size = 1 self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) - self._test('', 1, 2, ['u1']) - self._test('', 2, 2, ['u2']) + self._test('', 1, 1, 2, ['u1']) + self._test('', 2, 1, 2, ['u2']) def test_order_by_name(self): self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) - self._test('order:name', 1, 2, ['u1', 'u2']) - self._test('-order:name', 1, 2, ['u2', 'u1']) - self._test('order:name,asc', 1, 2, ['u1', 'u2']) - self._test('order:name,desc', 1, 2, ['u2', 'u1']) - self._test('-order:name,asc', 1, 2, ['u2', 'u1']) - self._test('-order:name,desc', 1, 2, ['u1', 'u2']) + self._test('order:name', 1, 100, 2, ['u1', 'u2']) + self._test('-order:name', 1, 100, 2, ['u2', 'u1']) + self._test('order:name,asc', 1, 100, 2, ['u1', 'u2']) + self._test('order:name,desc', 1, 100, 2, ['u2', 'u1']) + self._test('-order:name,asc', 1, 100, 2, ['u2', 'u1']) + self._test('-order:name,desc', 1, 100, 2, ['u1', 'u2']) def test_invalid_tokens(self): for query in [ @@ -135,20 +141,24 @@ class TestUserSearchExecutor(DatabaseTestCase): 'order:name,asc,desc', 'bad:x', 'special:unsupported']: - self.assertRaises( - errors.SearchError, self.executor.execute, self.session, query, 1) + self._test_raises(query, 1, 100) def test_anonymous(self): self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) - self._test('u1', 1, 1, ['u1']) - self._test('u2', 1, 1, ['u2']) + self._test('u1', 1, 100, 1, ['u1']) + self._test('u2', 1, 100, 1, ['u2']) + + def test_empty_search(self): + self.session.add(util.mock_user('u1')) + self.session.add(util.mock_user('u2')) + self._test('', 1, 100, 2, ['u1', 'u2']) def test_negated_anonymous(self): self.session.add(util.mock_user('u1')) self.session.add(util.mock_user('u2')) - self._test('-u1', 1, 1, ['u2']) - self._test('-u2', 1, 1, ['u1']) + self._test('-u1', 1, 100, 1, ['u2']) + self._test('-u2', 1, 100, 1, ['u1']) def test_combining(self): user1 = util.mock_user('u1') @@ -158,10 +168,9 @@ class TestUserSearchExecutor(DatabaseTestCase): user2.creation_time = datetime(2014, 6, 1) user3.creation_time = datetime(2015, 1, 1) self.session.add_all([user1, user2, user3]) - self._test('creation-time:2014 u1', 1, 1, ['u1']) - self._test('creation-time:2014 u2', 1, 1, ['u2']) - self._test('creation-time:2016 u2', 1, 0, []) + self._test('creation-time:2014 u1', 1, 100, 1, ['u1']) + self._test('creation-time:2014 u2', 1, 100, 1, ['u2']) + self._test('creation-time:2016 u2', 1, 100, 0, []) def test_special(self): - self.assertRaises( - errors.SearchError, self.executor.execute, self.session, 'special:-', 1) + self._test_raises('special:-', 1, 100)