From 378d3b697050869882f086aecb65c682f30264c9 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Jul 18 2022 09:09:12 +0000 Subject: Add API endpoint for reopening pull requests Signed-off-by: Matej Focko --- diff --git a/pagure/api/__init__.py b/pagure/api/__init__.py index eef1a10..f1580e7 100644 --- a/pagure/api/__init__.py +++ b/pagure/api/__init__.py @@ -589,6 +589,7 @@ def api(): fork.api_pull_request_merge, fork.api_pull_request_rebase, fork.api_pull_request_close, + fork.api_pull_request_reopen, fork.api_pull_request_add_comment, fork.api_pull_request_add_flag, fork.api_pull_request_get_flag, diff --git a/pagure/api/fork.py b/pagure/api/fork.py index 547e93b..5312c57 100644 --- a/pagure/api/fork.py +++ b/pagure/api/fork.py @@ -797,6 +797,74 @@ def api_pull_request_close(repo, requestid, username=None, namespace=None): return jsonout +@API.route("//pull-request//reopen", methods=["POST"]) +@API.route( + "///pull-request//reopen", methods=["POST"] +) +@API.route( + "/fork///pull-request//reopen", + methods=["POST"], +) +@API.route( + "/fork////pull-request//reopen", + methods=["POST"], +) +@api_login_required(acls=["pull_request_close", "pull_request_update"]) +@api_method +def api_pull_request_reopen(repo, requestid, username=None, namespace=None): + """ + Reopen a pull-request + -------------------- + Instruct Pagure to reopen a pull request. + + :: + + POST /api/0//pull-request//reopen + POST /api/0///pull-request//reopen + + :: + + POST /api/0/fork///pull-request//reopen + POST /api/0/fork////pull-request//reopen + + Sample response + ^^^^^^^^^^^^^^^ + + :: + + { + "message": "Pull-request reopened!" + } + + """ # noqa + output = {} + + repo = _get_repo(repo, username, namespace) + _check_pull_request(repo) + _check_token(repo, project_token=False) + request = _get_request(repo, requestid) + + if ( + not is_repo_committer(repo) + and not flask.g.fas_user.username == request.user.username + ): + raise pagure.exceptions.APIError(403, error_code=APIERROR.ENOPRCLOSE) + + try: + pagure.lib.query.reopen_pull_request( + flask.g.session, request, flask.g.fas_user.username + ) + flask.g.session.commit() + output["message"] = "Pull-request reopened!" + except SQLAlchemyError as err: # pragma: no cover + flask.g.session.rollback() + _log.exception(err) + raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) + + jsonout = flask.jsonify(output) + return jsonout + + @API.route("//pull-request//comment", methods=["POST"]) @API.route( "///pull-request//comment", diff --git a/tests/test_pagure_flask_api_fork.py b/tests/test_pagure_flask_api_fork.py index 232a384..456942d 100644 --- a/tests/test_pagure_flask_api_fork.py +++ b/tests/test_pagure_flask_api_fork.py @@ -1284,6 +1284,123 @@ class PagureFlaskApiForktests(tests.Modeltests): self.assertDictEqual(data, {"message": "Pull-request closed!"}) @patch("pagure.lib.notify.send_email") + def test_api_pull_request_reopen(self, send_email): + """Test the api_pull_request_reopen method of the flask api.""" + send_email.return_value = True + + tests.create_projects(self.session) + tests.create_tokens(self.session) + tests.create_tokens_acl(self.session) + + # Create the pull-request to close and reopen + repo = pagure.lib.query.get_authorized_project(self.session, "test") + forked_repo = pagure.lib.query.get_authorized_project( + self.session, "test" + ) + req = pagure.lib.query.new_pull_request( + session=self.session, + repo_from=forked_repo, + branch_from="master", + repo_to=repo, + branch_to="master", + title="test pull-request", + user="pingou", + ) + self.session.commit() + self.assertEqual(req.id, 1) + self.assertEqual(req.title, "test pull-request") + + headers = {"Authorization": "token aaabbbcccddd"} + + # Invalid project + output = self.app.post( + "/api/0/foo/pull-request/1/close", headers=headers + ) + self.assertEqual(output.status_code, 404) + data = json.loads(output.get_data(as_text=True)) + self.assertDictEqual( + data, {"error": "Project not found", "error_code": "ENOPROJECT"} + ) + + # Valid token, wrong project + output = self.app.post( + "/api/0/test2/pull-request/1/close", headers=headers + ) + self.assertEqual(output.status_code, 401) + data = json.loads(output.get_data(as_text=True)) + self.assertEqual( + pagure.api.APIERROR.EINVALIDTOK.name, data["error_code"] + ) + self.assertEqual(pagure.api.APIERROR.EINVALIDTOK.value, data["error"]) + + # Invalid PR + output = self.app.post( + "/api/0/test/pull-request/2/close", headers=headers + ) + self.assertEqual(output.status_code, 404) + data = json.loads(output.get_data(as_text=True)) + self.assertDictEqual( + data, {"error": "Pull-Request not found", "error_code": "ENOREQ"} + ) + + # Create a token for foo for this project + item = pagure.lib.model.Token( + id="foobar_token", + user_id=2, + project_id=1, + expiration=datetime.datetime.utcnow() + + datetime.timedelta(days=30), + ) + self.session.add(item) + self.session.commit() + + # Allow the token to close and reopen PR + acls = pagure.lib.query.get_acls(self.session) + for acl in acls: + if acl.name == "pull_request_close": + break + item = pagure.lib.model.TokenAcl( + token_id="foobar_token", acl_id=acl.id + ) + self.session.add(item) + self.session.commit() + + headers = {"Authorization": "token foobar_token"} + + # User not admin + output = self.app.post( + "/api/0/test/pull-request/1/close", headers=headers + ) + self.assertEqual(output.status_code, 403) + data = json.loads(output.get_data(as_text=True)) + self.assertDictEqual( + data, + { + "error": "You are not allowed to merge/close pull-request " + "for this project", + "error_code": "ENOPRCLOSE", + }, + ) + + headers = {"Authorization": "token aaabbbcccddd"} + + # Close PR + output = self.app.post( + "/api/0/test/pull-request/1/close", headers=headers + ) + self.assertEqual(output.status_code, 200) + data = json.loads(output.get_data(as_text=True)) + self.assertDictEqual(data, {"message": "Pull-request closed!"}) + + # Reopen PR + output = self.app.post( + "/api/0/test/pull-request/1/reopen", headers=headers + ) + self.assertEqual(output.status_code, 200) + data = json.loads(output.get_data(as_text=True)) + self.assertDictEqual(data, {"message": "Pull-request reopened!"}) + + @patch("pagure.lib.notify.send_email") def test_api_pull_request_merge_pr_disabled(self, send_email): """Test the api_pull_request_merge method of the flask api when PR are disabled."""