diff --git a/pagure/api/issue.py b/pagure/api/issue.py index 66f6b1c..87520b7 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -21,7 +21,7 @@ import pagure.exceptions import pagure.lib from pagure.api import ( API, api_method, api_login_required, api_login_optional, APIERROR, - get_authorized_api_project, get_request_data + get_authorized_api_project, get_request_data, get_page, get_per_page ) from pagure.config import config as pagure_config from pagure.utils import ( @@ -406,6 +406,14 @@ def api_view_issues(repo, username=None, namespace=None): | | | | ``asc`` or ``desc``. | | | | | Default: ``desc`` | +---------------+---------+--------------+---------------------------+ + | ``page`` | int | Optional | | Specifies which | + | | | | page to return | + | | | | (defaults to: 1) | + +---------------+----------+-------------+---------------------------+ + | ``per_page`` | int | Optional | | The number of projects | + | | | | to return per page. | + | | | | The maximum is 100. | + +---------------+----------+-------------+---------------------------+ Sample response ^^^^^^^^^^^^^^^ @@ -447,7 +455,16 @@ def api_view_issues(repo, username=None, namespace=None): "name": "pingou" } } - ] + ], + u'pagination': { + 'first': 'http://localhost/api/0/test/issues?per_page=20&page=1', + 'last': 'http://localhost/api/0/test/issues?per_page=20&page=1', + 'next': None, + 'page': 1, + 'pages': 1, + 'per_page': 20, + 'prev': None + }, } """ @@ -536,7 +553,21 @@ def api_view_issues(repo, username=None, namespace=None): 400, error_code=APIERROR.EDATETIME) params.update({'updated_after': updated_after}) + + page = get_page() + per_page = get_per_page() + params['count'] = True + issue_cnt = pagure.lib.search_issues(**params) + pagination_metadata = pagure.lib.get_pagination_metadata( + flask.request, page, per_page, issue_cnt) + query_start = (page - 1) * per_page + query_limit = per_page + + params['count'] = False + params['limit'] = query_limit + params['offset'] = query_start issues = pagure.lib.search_issues(**params) + jsonout = flask.jsonify({ 'total_issues': len(issues), 'issues': [issue.to_json(public=True) for issue in issues], @@ -550,7 +581,8 @@ def api_view_issues(repo, username=None, namespace=None): 'since': since, 'status': status, 'tags': tags, - } + }, + 'pagination': pagination_metadata, }) return jsonout diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 988daf0..4ba980c 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -5215,6 +5215,8 @@ def get_pagination_metadata(flask_request, page, per_page, total): if key.startswith('_'): request_args_wo_page.pop(key) + request_args_wo_page.update(flask_request.view_args) + next_page = None if page < pages: next_page = url_for( diff --git a/tests/test_pagure_flask_api_issue.py b/tests/test_pagure_flask_api_issue.py index e21e56a..040a2d9 100644 --- a/tests/test_pagure_flask_api_issue.py +++ b/tests/test_pagure_flask_api_issue.py @@ -1159,6 +1159,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": [FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1201,6 +1210,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1246,6 +1264,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1275,6 +1302,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1320,6 +1356,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1348,6 +1393,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1373,6 +1427,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[0]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?status=Closed&page=1&per_page=20', + u'last': u'http://localhost/api/0/test/issues?status=Closed&page=1&per_page=20', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1, } ) @@ -1395,8 +1458,17 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "status": "Invalid", "tags": [] }, + "issues": [], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?status=Invalid&page=1&per_page=20', + u'last': u'http://localhost/api/0/test/issues?status=Invalid&page=0&per_page=20', + u'next': None, + u'page': 1, + u'pages': 0, + u'per_page': 20, + u'prev': None + }, "total_issues": 0, - "issues": [] } ) @@ -1422,6 +1494,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[0], FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?status=All&page=1&per_page=20', + u'last': u'http://localhost/api/0/test/issues?status=All&page=1&per_page=20', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } ) @@ -1487,6 +1568,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [FULL_ISSUE_LIST[8]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1&order=asc', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1&order=asc', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } self.assertDictEqual(data, expected) @@ -1554,6 +1644,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": lcl_issues, + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } ) @@ -1580,6 +1679,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": [lcl_issues[0]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&milestones=v1.0&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&milestones=v1.0&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1652,6 +1760,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": lcl_issues, + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } ) @@ -1678,6 +1795,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": [lcl_issues[0]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?priority=high&page=1&per_page=20', + u'last': u'http://localhost/api/0/test/issues?priority=high&page=1&per_page=20', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1703,6 +1829,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": [lcl_issues[0]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?priority=1&page=1&per_page=20', + u'last': u'http://localhost/api/0/test/issues?priority=1&page=1&per_page=20', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1792,6 +1927,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": lcl_issues, + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } ) @@ -1818,6 +1962,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": [lcl_issues[1]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1&no_stones=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1&no_stones=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1844,6 +1997,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [], }, "issues": [lcl_issues[0]], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1&no_stones=0', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1&no_stones=0', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -1939,6 +2101,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": LCL_ISSUES, + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } ) @@ -1968,6 +2139,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": LCL_ISSUES, + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=1' % start, + u'last': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=1' % start, + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } ) @@ -1994,6 +2174,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": LCL_ISSUES[:1], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=1' % middle, + u'last': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=1' % middle, + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) @@ -2020,6 +2209,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "tags": [] }, "issues": [], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=1' % final, + u'last': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=0' % final, + u'next': None, + u'page': 1, + u'pages': 0, + u'per_page': 20, + u'prev': None + }, "total_issues": 0 } ) @@ -2068,6 +2266,15 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): 'title': 'Issue #3', 'user': {'fullname': 'PY C', 'name': 'pingou'}} ], + u'pagination': { + u'first': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=1' % final, + u'last': u'http://localhost/api/0/test/issues?per_page=20&since=%s&page=1' % final, + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 1 } ) diff --git a/tests/test_pagure_flask_api_ui_private_repo.py b/tests/test_pagure_flask_api_ui_private_repo.py index df5e43d..3fd2b08 100644 --- a/tests/test_pagure_flask_api_ui_private_repo.py +++ b/tests/test_pagure_flask_api_ui_private_repo.py @@ -2382,7 +2382,16 @@ class PagurePrivateRepotest(tests.Modeltests): } } - ] + ], + 'pagination': { + u'first': u'http://localhost/api/0/test4/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test4/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, } ) @@ -2472,6 +2481,15 @@ class PagurePrivateRepotest(tests.Modeltests): } } ], + 'pagination': { + u'first': u'http://localhost/api/0/test4/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test4/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } @@ -2554,6 +2572,15 @@ class PagurePrivateRepotest(tests.Modeltests): } } ], + 'pagination': { + u'first': u'http://localhost/api/0/test4/issues?per_page=20&page=1', + u'last': u'http://localhost/api/0/test4/issues?per_page=20&page=1', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 } @@ -2578,8 +2605,17 @@ class PagurePrivateRepotest(tests.Modeltests): "since": None, "tags": [] }, + "issues": [], + 'pagination': { + u'first': u'http://localhost/api/0/test4/issues?status=Closed&page=1&per_page=20', + u'last': u'http://localhost/api/0/test4/issues?status=Closed&page=0&per_page=20', + u'next': None, + u'page': 1, + u'pages': 0, + u'per_page': 20, + u'prev': None + }, "total_issues": 0, - "issues": [] } ) @@ -2602,8 +2638,17 @@ class PagurePrivateRepotest(tests.Modeltests): "since": None, "tags": [] }, + "issues": [], + 'pagination': { + u'first': u'http://localhost/api/0/test4/issues?status=Invalid&page=1&per_page=20', + u'last': u'http://localhost/api/0/test4/issues?status=Invalid&page=0&per_page=20', + u'next': None, + u'page': 1, + u'pages': 0, + u'per_page': 20, + u'prev': None + }, "total_issues": 0, - "issues": [] } ) @@ -2678,6 +2723,15 @@ class PagurePrivateRepotest(tests.Modeltests): } } ], + 'pagination': { + u'first': u'http://localhost/api/0/test4/issues?status=All&page=1&per_page=20', + u'last': u'http://localhost/api/0/test4/issues?status=All&page=1&per_page=20', + u'next': None, + u'page': 1, + u'pages': 1, + u'per_page': 20, + u'prev': None + }, "total_issues": 2 }