diff --git a/pagure/api/issue.py b/pagure/api/issue.py index 051a420..474bbb7 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -232,6 +232,12 @@ def api_view_issues(repo, username=None, namespace=None): | ``priority`` | string | Optional | | Filter the issues | | | | | by priority | +---------------+---------+--------------+---------------------------+ + | ``no_stones`` | boolean | Optional | | If true returns only the| + | | | | issues having no | + | | | | milestone, if false | + | | | | returns only the issues | + | | | | having a milestone | + +---------------+---------+--------------+---------------------------+ | ``since`` | string | Optional | | Filter the issues | | | | | updated after this date.| | | | | The date can either be | @@ -249,6 +255,7 @@ def api_view_issues(repo, username=None, namespace=None): "assignee": null, "author": null, 'milestones': [], + 'no_stones': null, 'priority': null, "since": null, "status": "Closed", @@ -295,6 +302,12 @@ def api_view_issues(repo, username=None, namespace=None): assignee = flask.request.args.get('assignee', None) author = flask.request.args.get('author', None) milestone = flask.request.args.getlist('milestones', None) + no_stones = flask.request.args.get('no_stones', None) + if no_stones is not None: + if str(no_stones).lower() in ['1', 'true', 't']: + no_stones = True + else: + no_stones = False priority = flask.request.args.get('priority', None) since = flask.request.args.get('since', None) status = flask.request.args.get('status', None) @@ -322,6 +335,7 @@ def api_view_issues(repo, username=None, namespace=None): 'private': private, 'milestones': milestone, 'priority': priority, + 'no_milestones': no_stones, } if status is not None: @@ -359,6 +373,7 @@ def api_view_issues(repo, username=None, namespace=None): 'assignee': assignee, 'author': author, 'milestones': milestone, + 'no_stones': no_stones, 'priority': priority, 'since': since, 'status': status, diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 0c555f7..c1875c2 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -2262,6 +2262,10 @@ def search_issues( query = query.filter( model.Issue.milestone.in_(milestones) ) + elif no_milestones is False: + query = query.filter( + model.Issue.milestone.isnot(None) + ) if custom_search: constraints = [] diff --git a/tests/test_pagure_flask_api_issue.py b/tests/test_pagure_flask_api_issue.py index 323173d..944899d 100644 --- a/tests/test_pagure_flask_api_issue.py +++ b/tests/test_pagure_flask_api_issue.py @@ -827,6 +827,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -866,6 +867,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -907,6 +909,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -934,6 +937,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -975,6 +979,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -1001,6 +1006,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -1022,6 +1028,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": "Closed", @@ -1043,6 +1050,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": "Invalid", @@ -1067,6 +1075,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": "All", @@ -1132,6 +1141,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -1156,6 +1166,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': ['v1.0'], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -1221,6 +1232,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -1245,6 +1257,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': 'high', "since": None, "status": None, @@ -1255,6 +1268,122 @@ class PagureFlaskApiIssuetests(tests.Modeltests): } ) + def test_api_view_issues_no_stones(self): + """ Test the api_view_issues method of the flask api when filtering + with no_stones. + """ + tests.create_projects(self.session) + tests.create_projects_git( + os.path.join(self.path, 'tickets'), bare=True) + tests.create_tokens(self.session) + tests.create_tokens_acl(self.session) + + repo = pagure.lib.get_project(self.session, 'test') + + # Create 2 tickets but only 1 has a milestone + start = datetime.datetime.utcnow().strftime('%s') + issue = pagure.lib.model.Issue( + id=pagure.lib.get_next_id(self.session, repo.id), + project_id=repo.id, + title='Issue #1', + content='Description', + user_id=1, # pingou + uid='issue#1', + private=False, + ) + self.session.add(issue) + self.session.commit() + + issue = pagure.lib.model.Issue( + id=pagure.lib.get_next_id(self.session, repo.id), + project_id=repo.id, + title='Issue #2', + content='Description', + user_id=1, # pingou + uid='issue#2', + private=False, + milestone='v1.0', + ) + self.session.add(issue) + self.session.commit() + + # List all opened issues + output = self.app.get('/api/0/test/issues') + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + for idx in range(len(data['issues'])): + data['issues'][idx]['date_created'] = '1431414800' + data['issues'][idx]['last_updated'] = '1431414800' + lcl_issues = copy.deepcopy(LCL_ISSUES) + lcl_issues[0]['milestone'] = 'v1.0' + self.assertDictEqual( + data, + { + "args": { + "assignee": None, + "author": None, + 'milestones': [], + 'no_stones': None, + 'priority': None, + "since": None, + "status": None, + "tags": [], + }, + "issues": lcl_issues, + "total_issues": 2 + } + ) + + # List all issues with no milestone + output = self.app.get('/api/0/test/issues?no_stones=1') + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + for idx in range(len(data['issues'])): + data['issues'][idx]['date_created'] = '1431414800' + data['issues'][idx]['last_updated'] = '1431414800' + self.assertDictEqual( + data, + { + "args": { + "assignee": None, + "author": None, + 'milestones': [], + 'no_stones': True, + 'priority': None, + "since": None, + "status": None, + "tags": [], + }, + "issues": [lcl_issues[1]], + "total_issues": 1 + } + ) + + # List all issues with a milestone + output = self.app.get('/api/0/test/issues?no_stones=0') + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + for idx in range(len(data['issues'])): + data['issues'][idx]['date_created'] = '1431414800' + data['issues'][idx]['last_updated'] = '1431414800' + self.assertDictEqual( + data, + { + "args": { + "assignee": None, + "author": None, + 'milestones': [], + 'no_stones': False, + 'priority': None, + "since": None, + "status": None, + "tags": [], + }, + "issues": [lcl_issues[0]], + "total_issues": 1 + } + ) + def test_api_view_issues_since(self): """ Test the api_view_issues method of the flask api for since option """ @@ -1338,6 +1467,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": None, "status": None, @@ -1365,6 +1495,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": start, "status": None, @@ -1389,6 +1520,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": middle, "status": None, @@ -1413,6 +1545,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": final, "status": None, @@ -1440,6 +1573,7 @@ class PagureFlaskApiIssuetests(tests.Modeltests): "assignee": None, "author": None, 'milestones': [], + 'no_stones': None, 'priority': None, "since": final, "status": None,