From 6fa1ec2b506ae9b12b3be5c36e017784d87ca280 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Nov 06 2017 08:10:08 +0000 Subject: Expand the group info API endpoint The API endpoint can now include the list of projects this group has access to and even filter this list by the access level the group has on the projects. Fixes https://pagure.io/pagure/issue/2741 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/api/group.py b/pagure/api/group.py index ea718a7..58edcf9 100644 --- a/pagure/api/group.py +++ b/pagure/api/group.py @@ -101,6 +101,32 @@ def api_view_group(group): GET /api/0/group/some_group_name + + :: + + GET /api/0/group/some_group_name?projects=1&acl=commit + + 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``. | + +------------------+---------+--------------+-----------------------------+ + + Sample response ^^^^^^^^^^^^^^^ @@ -125,12 +151,58 @@ def api_view_group(group): ], "name": "some_group_name" } + + :: + + { + "creator": { + "default_email": "user1@example.com", + "emails": [ + "user1@example.com" + ], + "fullname": "User1", + "name": "user1" + }, + "date_created": "1492011511", + "description": "Some Group", + "display_name": "Some Group", + "group_type": "user", + "members": [ + "user1", + "user2" + ], + "name": "some_group_name", + "projects": [], + } + + """ # noqa + projects = flask.request.values.get( + 'projects', '').strip().lower() in ['1', 'true'] + acl = flask.request.values.get('acl', '').strip().lower() or None + if acl == 'ticket': + acl = ['admin', 'commit', 'ticket'] + elif acl == 'commit': + acl = ['commit', 'admin'] + elif acl: + acl = [acl] + group = pagure.lib.search_groups(SESSION, group_name=group) if not group: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOGROUP) - jsonout = flask.jsonify(group.to_json( - public=(not pagure.api_authenticated()))) + output = group.to_json(public=(not pagure.api_authenticated())) + if projects and not acl: + output['projects'] = [ + project.to_json(public=True) + for project in group.projects + ] + elif projects and acl: + output['projects'] = [ + pg.project.to_json(public=True) + for pg in group.projects_groups + if pg.access in acl + ] + jsonout = flask.jsonify(output) jsonout.status_code = 200 return jsonout diff --git a/tests/test_pagure_flask_api_group.py b/tests/test_pagure_flask_api_group.py index 3e267fa..1c6eef8 100644 --- a/tests/test_pagure_flask_api_group.py +++ b/tests/test_pagure_flask_api_group.py @@ -25,6 +25,8 @@ import tests class PagureFlaskApiGroupTests(tests.SimplePagureTest): """ Tests for the flask API of pagure for issue """ + maxDiff = None + def setUp(self): """ Set up the environnment, ran before every tests. """ super(PagureFlaskApiGroupTests, self).setUp() @@ -50,6 +52,17 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): ) self.session.commit() + tests.create_projects(self.session) + + project = pagure.lib._get_project(self.session, 'test2') + msg = pagure.lib.add_group_to_project( + session=self.session, + project=project, + new_group='some_group', + user='pingou', + ) + self.session.commit() + self.assertEqual(msg, 'Group added') def test_api_groups(self): """ Test the api_groups function. """ @@ -113,21 +126,21 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): self.assertEqual( data, { - "groups": [ - { - "description": None, - "name": "some_group" - }, - { - "description": None, - "name": "group1" - }, - { - "description": None, - "name": "rel-eng" - } - ], - "total_groups": 3 + "groups": [ + { + "description": None, + "name": "some_group" + }, + { + "description": None, + "name": "group1" + }, + { + "description": None, + "name": "rel-eng" + } + ], + "total_groups": 3 } ) @@ -136,8 +149,8 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): Test the api_view_group method of the flask api with an authenticated user. The tested group has one member. """ - tests.create_projects(self.session) tests.create_tokens(self.session) + headers = {'Authorization': 'token aaabbbcccddd'} output = self.app.get('/api/0/group/some_group', headers=headers) self.assertEqual(output.status_code, 200) @@ -201,9 +214,10 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): group = pagure.lib.search_groups(self.session, group_name='some_group') result = pagure.lib.add_user_to_group( self.session, user.username, group, user.username, True) + self.assertEqual( + result, 'User `mprahl` added to the group `some_group`.') self.session.commit() - tests.create_projects(self.session) tests.create_tokens(self.session) headers = {'Authorization': 'token aaabbbcccddd'} @@ -229,8 +243,6 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): self.maxDiff = None data = json.loads(output.data) data['date_created'] = '1492020239' - from pprint import pprint - pprint(data) self.assertDictEqual(data, exp) def test_api_view_group_no_group_error(self): @@ -244,5 +256,387 @@ class PagureFlaskApiGroupTests(tests.SimplePagureTest): self.assertEqual(data['error'], 'Group not found') self.assertEqual(data['error_code'], 'ENOGROUP') + def test_api_view_group_w_projects_and_acl(self): + """ + Test the api_view_group method with project info and restricted + to the admin ACL + """ + tests.create_tokens(self.session) + + headers = {'Authorization': 'token aaabbbcccddd'} + output = self.app.get( + '/api/0/group/some_group?projects=1', headers=headers) + self.assertEqual(output.status_code, 200) + exp = { + "display_name": "Some Group", + "description": None, + "creator": { + "fullname": "PY C", + "default_email": "bar@pingou.com", + "emails": [ + "bar@pingou.com", + "foo@pingou.com" + ], + "name": "pingou" + }, + "members": ["pingou"], + "date_created": "1492020239", + "group_type": "user", + "name": "some_group", + "projects": [ + { + "access_groups": { + "admin": [ + "some_group" + ], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [], + "commit": [], + "owner": [ + "pingou" + ], + "ticket": [] + }, + "close_status": [ + "Invalid", + "Insufficient data", + "Fixed", + "Duplicate" + ], + "custom_keys": [], + "date_created": "1492020239", + "date_modified": "1492020239", + "description": "test project #2", + "fullname": "test2", + "id": 2, + "milestones": {}, + "name": "test2", + "namespace": None, + "parent": None, + "priorities": {}, + "settings": { + "Enforce_signed-off_commits_in_pull-request": False, + "Minimum_score_to_merge_pull-request": -1, + "Only_assignee_can_merge_pull-request": False, + "Web-hooks": None, + "always_merge": False, + "fedmsg_notifications": True, + "issue_tracker": True, + "issues_default_to_private": False, + "project_documentation": False, + "pull_request_access_only": False, + "pull_requests": True + }, + "tags": [], + "url_path": "test2", + "user": { + "fullname": "PY C", + "name": "pingou" + } + } + ] + } + data = json.loads(output.data) + data['date_created'] = '1492020239' + projects = [] + for p in data['projects']: + p['date_created'] = '1492020239' + p['date_modified'] = '1492020239' + projects.append(p) + data['projects'] = projects + self.assertDictEqual(data, exp) + + output2 = self.app.get( + '/api/0/group/some_group?projects=1&acl=admin', headers=headers) + self.assertEqual(output.data.split('\n'), output2.data.split('\n')) + + def test_api_view_group_w_projects_and_acl_commit(self): + """ + Test the api_view_group method with project info and restricted + to the commit ACL + """ + + output = self.app.get( + '/api/0/group/some_group?projects=1&acl=commit') + self.assertEqual(output.status_code, 200) + exp = { + "display_name": "Some Group", + "description": None, + "creator": { + "fullname": "PY C", + "name": "pingou" + }, + "members": ["pingou"], + "date_created": "1492020239", + "group_type": "user", + "name": "some_group", + "projects": [ + { + "access_groups": { + "admin": [ + "some_group" + ], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [], + "commit": [], + "owner": [ + "pingou" + ], + "ticket": [] + }, + "close_status": [ + "Invalid", + "Insufficient data", + "Fixed", + "Duplicate" + ], + "custom_keys": [], + "date_created": "1492020239", + "date_modified": "1492020239", + "description": "test project #2", + "fullname": "test2", + "id": 2, + "milestones": {}, + "name": "test2", + "namespace": None, + "parent": None, + "priorities": {}, + "settings": { + "Enforce_signed-off_commits_in_pull-request": False, + "Minimum_score_to_merge_pull-request": -1, + "Only_assignee_can_merge_pull-request": False, + "Web-hooks": None, + "always_merge": False, + "fedmsg_notifications": True, + "issue_tracker": True, + "issues_default_to_private": False, + "project_documentation": False, + "pull_request_access_only": False, + "pull_requests": True + }, + "tags": [], + "url_path": "test2", + "user": { + "fullname": "PY C", + "name": "pingou" + } + } + ] + } + data = json.loads(output.data) + data['date_created'] = '1492020239' + 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_ticket(self): + """ + Test the api_view_group method with project info and restricted + to the ticket ACL + """ + + output = self.app.get( + '/api/0/group/some_group?projects=1&acl=ticket') + self.assertEqual(output.status_code, 200) + exp = { + "display_name": "Some Group", + "description": None, + "creator": { + "fullname": "PY C", + "name": "pingou" + }, + "members": ["pingou"], + "date_created": "1492020239", + "group_type": "user", + "name": "some_group", + "projects": [ + { + "access_groups": { + "admin": [ + "some_group" + ], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [], + "commit": [], + "owner": [ + "pingou" + ], + "ticket": [] + }, + "close_status": [ + "Invalid", + "Insufficient data", + "Fixed", + "Duplicate" + ], + "custom_keys": [], + "date_created": "1492020239", + "date_modified": "1492020239", + "description": "test project #2", + "fullname": "test2", + "id": 2, + "milestones": {}, + "name": "test2", + "namespace": None, + "parent": None, + "priorities": {}, + "settings": { + "Enforce_signed-off_commits_in_pull-request": False, + "Minimum_score_to_merge_pull-request": -1, + "Only_assignee_can_merge_pull-request": False, + "Web-hooks": None, + "always_merge": False, + "fedmsg_notifications": True, + "issue_tracker": True, + "issues_default_to_private": False, + "project_documentation": False, + "pull_request_access_only": False, + "pull_requests": True + }, + "tags": [], + "url_path": "test2", + "user": { + "fullname": "PY C", + "name": "pingou" + } + } + ] + } + data = json.loads(output.data) + data['date_created'] = '1492020239' + 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_admin_no_project(self): + """ + Test the api_view_group method with project info and restricted + to the admin ACL + """ + + # Make the group having only commit access + project = pagure.lib._get_project(self.session, 'test2') + msg = pagure.lib.add_group_to_project( + session=self.session, + project=project, + new_group='some_group', + user='pingou', + access='commit', + ) + self.session.commit() + self.assertEqual(msg, 'Group access updated') + + output = self.app.get( + '/api/0/group/some_group?projects=1&acl=admin') + self.assertEqual(output.status_code, 200) + exp = { + "display_name": "Some Group", + "description": None, + "creator": { + "fullname": "PY C", + "name": "pingou" + }, + "members": ["pingou"], + "date_created": "1492020239", + "group_type": "user", + "name": "some_group", + "projects": [] + } + data = json.loads(output.data) + data['date_created'] = '1492020239' + self.assertDictEqual(data, exp) + + def test_api_view_group_w_projects_and_acl_commit_no_project(self): + """ + Test the api_view_group method with project info and restricted + to the commit ACL + """ + + # Make the group having only ticket access + project = pagure.lib._get_project(self.session, 'test2') + msg = pagure.lib.add_group_to_project( + session=self.session, + project=project, + new_group='some_group', + user='pingou', + access='ticket', + ) + self.session.commit() + self.assertEqual(msg, 'Group access updated') + + output = self.app.get( + '/api/0/group/some_group?projects=1&acl=commit') + self.assertEqual(output.status_code, 200) + exp = { + "display_name": "Some Group", + "description": None, + "creator": { + "fullname": "PY C", + "name": "pingou" + }, + "members": ["pingou"], + "date_created": "1492020239", + "group_type": "user", + "name": "some_group", + "projects": [] + } + data = json.loads(output.data) + data['date_created'] = '1492020239' + self.assertDictEqual(data, exp) + + def test_api_view_group_w_projects_and_acl_ticket_no_project(self): + """ + Test the api_view_group method with project info and restricted + to the ticket ACL + """ + + # Create a group not linked to any project + item = pagure.lib.model.PagureGroup( + group_name='rel-eng', + group_type='user', + display_name='Release engineering group', + user_id=1, # pingou + ) + self.session.add(item) + self.session.commit() + + output = self.app.get( + '/api/0/group/rel-eng?projects=1&acl=ticket') + self.assertEqual(output.status_code, 200) + exp = { + "display_name": "Release engineering group", + "description": None, + "creator": { + "fullname": "PY C", + "name": "pingou" + }, + "members": [], + "date_created": "1492020239", + "group_type": "user", + "name": "rel-eng", + "projects": [] + } + data = json.loads(output.data) + data['date_created'] = '1492020239' + self.assertDictEqual(data, exp) + + if __name__ == "__main__": unittest.main(verbosity=2)