From 93bdbd22f0f31ba20e04553c90d2f719645583b0 Mon Sep 17 00:00:00 2001 From: Michal Konečný Date: Nov 13 2020 10:14:44 +0000 Subject: Add pagination to group API This PR adds pagination for the group API call if projects are requested. Fixes issue https://pagure.io/fedora-infrastructure/issue/9399 Signed-off-by: Michal Konečný --- diff --git a/pagure/api/group.py b/pagure/api/group.py index 4c58d02..3f06db3 100644 --- a/pagure/api/group.py +++ b/pagure/api/group.py @@ -140,25 +140,36 @@ def api_view_group(group): GET /api/0/group/some_group_name?projects=1&acl=commit + :: + + GET /api/0/group/some_group_name?page=1&per_page=50 + Input ^^^^^ - +------------------+---------+--------------+-----------------------------+ - | Key | Type | Optionality | Description | - +==================+=========+==============+=============================+ - | ``group name`` | str | Mandatory | The name of the group to | - | | | | retrieve information about. | - +------------------+---------+--------------+-----------------------------+ - | ``projects`` | bool | Optional | Specifies whether to include| - | | | | projects in the data | - | | | | returned. | - +------------------+---------+--------------+-----------------------------+ - | ``acl`` | str | Optional | Filter the project returned | - | | | | (if any) to those where the | - | | | | has the specified ACL level.| - | | | | Can be any of: ``admin``, | - | | | | ``commit`` or ``ticket``. | - +------------------+---------+--------------+-----------------------------+ + +-----------------------+---------+--------------+-----------------------------+ + | Key | Type | Optionality | Description | + +=======================+=========+==============+=============================+ + | ``group name`` | str | Mandatory | The name of the group to | + | | | | retrieve information about. | + +-----------------------+---------+--------------+-----------------------------+ + | ``projects`` | bool | Optional | Specifies whether to include| + | | | | projects in the data | + | | | | returned. | + +-----------------------+---------+--------------+-----------------------------+ + | ``acl`` | str | Optional | Filter the project returned | + | | | | (if any) to those where the | + | | | | has the specified ACL level.| + | | | | Can be any of: ``admin``, | + | | | | ``commit`` or ``ticket``. | + +-----------------------+---------+--------------+-----------------------------+ + | ``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 @@ -179,6 +190,15 @@ def api_view_group(group): "description": "Some Group", "display_name": "Some Group", "group_type": "user", + "pagination": { + "first": "http://127.0.0.1:5000/api/0/group/some_group_name?per_page=2&page=1", + "last": "http://127.0.0.1:5000/api/0/group/some_group_name?per_page=2&page=2", + "next": "http://127.0.0.1:5000/api/0/group/some_group_name?per_page=2&page=2", + "page": 1, + "pages": 2, + "per_page": 2, + "prev": null + }, "members": [ "user1", "user2" @@ -206,6 +226,19 @@ def api_view_group(group): "user2" ], "name": "some_group_name", + "total_projects": 1000, + "pagination": { + "first": + "http://127.0.0.1:5000/api/0/group/some_group_name?per_page=2&projects=1&page=1", + "last": + "http://127.0.0.1:5000/api/0/group/some_group_name?per_page=2&projects=1&page=500", + "next": + "http://127.0.0.1:5000/api/0/group/some_group_name?per_page=2&projects=1&page=2", + "page": 1, + "pages": 500, + "per_page": 2, + "prev": null + }, "projects": [], } @@ -228,15 +261,30 @@ def api_view_group(group): raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOGROUP) output = group.to_json(public=(not pagure.utils.api_authenticated())) - if projects and not acl: - output["projects"] = [ - project.to_json(public=True) for project in group.projects - ] - elif projects and acl: + + if projects: + # Prepare pagination data for projects + if not acl: + group_projects = group.projects + elif acl: + group_projects = [ + pg.project for pg in group.projects_groups if pg.access in acl + ] + page = get_page() + per_page = get_per_page() + projects_cnt = len(group_projects) + pagination_metadata = pagure.lib.query.get_pagination_metadata( + flask.request, page, per_page, projects_cnt + ) + query_start = (page - 1) * per_page + query_limit = per_page + page_projects = group_projects[query_start:query_limit] + + output["total_projects"] = projects_cnt + output["pagination"] = pagination_metadata + output["projects"] = [ - pg.project.to_json(public=True) - for pg in group.projects_groups - if pg.access in acl + project.to_json(public=True) for project in page_projects ] jsonout = flask.jsonify(output) jsonout.status_code = 200 diff --git a/tests/test_pagure_flask_api_group.py b/tests/test_pagure_flask_api_group.py index 39c2b7f..59cf532 100644 --- a/tests/test_pagure_flask_api_group.py +++ b/tests/test_pagure_flask_api_group.py @@ -286,6 +286,15 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): "date_created": "1492020239", "group_type": "user", "name": "some_group", + "pagination": { + "first": "http://localhost...", + "last": "http://localhost...", + "next": None, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": None, + }, "projects": [ { "access_groups": { @@ -327,9 +336,14 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): }, } ], + "total_projects": 1, } data = json.loads(output.get_data(as_text=True)) data["date_created"] = "1492020239" + self.assertIsNotNone(data["pagination"]["first"]) + data["pagination"]["first"] = "http://localhost..." + self.assertIsNotNone(data["pagination"]["last"]) + data["pagination"]["last"] = "http://localhost..." projects = [] for p in data["projects"]: p["date_created"] = "1492020239" @@ -338,13 +352,22 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): data["projects"] = projects self.assertDictEqual(data, exp) - output2 = self.app.get( + output = self.app.get( "/api/0/group/some_group?projects=1&acl=admin", headers=headers ) - self.assertListEqual( - output.get_data(as_text=True).split("\n"), - output2.get_data(as_text=True).split("\n"), - ) + data = json.loads(output.get_data(as_text=True)) + data["date_created"] = "1492020239" + self.assertIsNotNone(data["pagination"]["first"]) + data["pagination"]["first"] = "http://localhost..." + self.assertIsNotNone(data["pagination"]["last"]) + data["pagination"]["last"] = "http://localhost..." + projects = [] + for p in data["projects"]: + p["date_created"] = "1492020239" + p["date_modified"] = "1492020239" + projects.append(p) + data["projects"] = projects + self.assertDictEqual(data, exp) def test_api_view_group_w_projects_and_acl_commit(self): """ @@ -366,6 +389,15 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): "date_created": "1492020239", "group_type": "user", "name": "some_group", + "pagination": { + "first": "http://localhost...", + "last": "http://localhost...", + "next": None, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": None, + }, "projects": [ { "access_groups": { @@ -407,9 +439,14 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): }, } ], + "total_projects": 1, } data = json.loads(output.get_data(as_text=True)) data["date_created"] = "1492020239" + self.assertIsNotNone(data["pagination"]["first"]) + data["pagination"]["first"] = "http://localhost..." + self.assertIsNotNone(data["pagination"]["last"]) + data["pagination"]["last"] = "http://localhost..." projects = [] for p in data["projects"]: p["date_created"] = "1492020239" @@ -438,6 +475,15 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): "date_created": "1492020239", "group_type": "user", "name": "some_group", + "pagination": { + "first": "http://localhost...", + "last": "http://localhost...", + "next": None, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": None, + }, "projects": [ { "access_groups": { @@ -479,9 +525,14 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): }, } ], + "total_projects": 1, } data = json.loads(output.get_data(as_text=True)) data["date_created"] = "1492020239" + self.assertIsNotNone(data["pagination"]["first"]) + data["pagination"]["first"] = "http://localhost..." + self.assertIsNotNone(data["pagination"]["last"]) + data["pagination"]["last"] = "http://localhost..." projects = [] for p in data["projects"]: p["date_created"] = "1492020239" @@ -522,10 +573,24 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): "date_created": "1492020239", "group_type": "user", "name": "some_group", + "pagination": { + "first": "http://localhost...", + "last": "http://localhost...", + "next": None, + "page": 1, + "pages": 0, + "per_page": 20, + "prev": None, + }, "projects": [], + "total_projects": 0, } data = json.loads(output.get_data(as_text=True)) data["date_created"] = "1492020239" + self.assertIsNotNone(data["pagination"]["first"]) + data["pagination"]["first"] = "http://localhost..." + self.assertIsNotNone(data["pagination"]["last"]) + data["pagination"]["last"] = "http://localhost..." self.assertDictEqual(data, exp) def test_api_view_group_w_projects_and_acl_commit_no_project(self): @@ -560,10 +625,24 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): "date_created": "1492020239", "group_type": "user", "name": "some_group", + "pagination": { + "first": "http://localhost...", + "last": "http://localhost...", + "next": None, + "page": 1, + "pages": 0, + "per_page": 20, + "prev": None, + }, "projects": [], + "total_projects": 0, } data = json.loads(output.get_data(as_text=True)) data["date_created"] = "1492020239" + self.assertIsNotNone(data["pagination"]["first"]) + data["pagination"]["first"] = "http://localhost..." + self.assertIsNotNone(data["pagination"]["last"]) + data["pagination"]["last"] = "http://localhost..." self.assertDictEqual(data, exp) def test_api_view_group_w_projects_and_acl_ticket_no_project(self): @@ -596,10 +675,24 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): "date_created": "1492020239", "group_type": "user", "name": "rel-eng", + "pagination": { + "first": "http://localhost...", + "last": "http://localhost...", + "next": None, + "page": 1, + "pages": 0, + "per_page": 20, + "prev": None, + }, "projects": [], + "total_projects": 0, } data = json.loads(output.get_data(as_text=True)) data["date_created"] = "1492020239" + self.assertIsNotNone(data["pagination"]["first"]) + data["pagination"]["first"] = "http://localhost..." + self.assertIsNotNone(data["pagination"]["last"]) + data["pagination"]["last"] = "http://localhost..." self.assertDictEqual(data, exp)