# HG changeset patch # User Bartłomiej Wołyńczyk # Date 2020-06-28 21:37:16 # Node ID d44504983605200ccc7d1e064772c2138cf2e6c6 # Parent 61666194d16389942f801c3a43bd5c39af27df49 api: added edit comment api diff --git a/rhodecode/api/tests/test_api.py b/rhodecode/api/tests/test_api.py --- a/rhodecode/api/tests/test_api.py +++ b/rhodecode/api/tests/test_api.py @@ -87,7 +87,7 @@ class TestApi(object): id_, params = build_data(self.apikey, 'comment', args='xx') response = api_call(self.app, params) expected = 'No such method: comment. ' \ - 'Similar methods: changeset_comment, comment_pull_request, ' \ + 'Similar methods: changeset_comment, comment_pull_request, edit_comment, ' \ 'get_pull_request_comments, comment_commit, get_repo_comments' assert_error(id_, expected, given=response.body) diff --git a/rhodecode/api/tests/test_comment_pull_request.py b/rhodecode/api/tests/test_comment_pull_request.py --- a/rhodecode/api/tests/test_comment_pull_request.py +++ b/rhodecode/api/tests/test_comment_pull_request.py @@ -21,7 +21,7 @@ import pytest from rhodecode.model.comment import CommentsModel -from rhodecode.model.db import UserLog, User +from rhodecode.model.db import UserLog, User, ChangesetComment from rhodecode.model.pull_request import PullRequestModel from rhodecode.tests import TEST_USER_ADMIN_LOGIN from rhodecode.api.tests.utils import ( @@ -218,8 +218,20 @@ class TestCommentPullRequest(object): assert_error(id_, expected, given=response.body) @pytest.mark.backends("git", "hg") - def test_api_comment_pull_request_non_admin_with_userid_error( - self, pr_util): + def test_api_comment_pull_request_non_admin_with_userid_error(self, pr_util): + pull_request = pr_util.create_pull_request() + id_, params = build_data( + self.apikey_regular, 'comment_pull_request', + repoid=pull_request.target_repo.repo_name, + pullrequestid=pull_request.pull_request_id, + userid=TEST_USER_ADMIN_LOGIN) + response = api_call(self.app, params) + + expected = 'userid is not the same as your user' + assert_error(id_, expected, given=response.body) + + @pytest.mark.backends("git", "hg") + def test_api_comment_pull_request_non_admin_with_userid_error(self, pr_util): pull_request = pr_util.create_pull_request() id_, params = build_data( self.apikey_regular, 'comment_pull_request', @@ -244,3 +256,135 @@ class TestCommentPullRequest(object): expected = 'Invalid commit_id `XXX` for this pull request.' assert_error(id_, expected, given=response.body) + + @pytest.mark.backends("git", "hg") + def test_api_edit_comment(self, pr_util): + pull_request = pr_util.create_pull_request() + + id_, params = build_data( + self.apikey, + 'comment_pull_request', + repoid=pull_request.target_repo.repo_name, + pullrequestid=pull_request.pull_request_id, + message='test message', + ) + response = api_call(self.app, params) + json_response = response.json + comment_id = json_response['result']['comment_id'] + + message_after_edit = 'just message' + id_, params = build_data( + self.apikey, + 'edit_comment', + comment_id=comment_id, + message=message_after_edit, + version=0, + ) + response = api_call(self.app, params) + json_response = response.json + assert json_response['result']['version'] == 1 + + text_form_db = ChangesetComment.get(comment_id).text + assert message_after_edit == text_form_db + + @pytest.mark.backends("git", "hg") + def test_api_edit_comment_wrong_version(self, pr_util): + pull_request = pr_util.create_pull_request() + + id_, params = build_data( + self.apikey, 'comment_pull_request', + repoid=pull_request.target_repo.repo_name, + pullrequestid=pull_request.pull_request_id, + message='test message') + response = api_call(self.app, params) + json_response = response.json + comment_id = json_response['result']['comment_id'] + + message_after_edit = 'just message' + id_, params = build_data( + self.apikey_regular, + 'edit_comment', + comment_id=comment_id, + message=message_after_edit, + version=1, + ) + response = api_call(self.app, params) + expected = 'comment ({}) version ({}) mismatch'.format(comment_id, 1) + assert_error(id_, expected, given=response.body) + + @pytest.mark.backends("git", "hg") + def test_api_edit_comment_wrong_version(self, pr_util): + pull_request = pr_util.create_pull_request() + + id_, params = build_data( + self.apikey, 'comment_pull_request', + repoid=pull_request.target_repo.repo_name, + pullrequestid=pull_request.pull_request_id, + message='test message') + response = api_call(self.app, params) + json_response = response.json + comment_id = json_response['result']['comment_id'] + + id_, params = build_data( + self.apikey, + 'edit_comment', + comment_id=comment_id, + message='', + version=0, + ) + response = api_call(self.app, params) + expected = "comment ({}) can't be changed with empty string".format(comment_id, 1) + assert_error(id_, expected, given=response.body) + + @pytest.mark.backends("git", "hg") + def test_api_edit_comment_wrong_user_set_by_non_admin(self, pr_util): + pull_request = pr_util.create_pull_request() + pull_request_id = pull_request.pull_request_id + id_, params = build_data( + self.apikey, + 'comment_pull_request', + repoid=pull_request.target_repo.repo_name, + pullrequestid=pull_request_id, + message='test message' + ) + response = api_call(self.app, params) + json_response = response.json + comment_id = json_response['result']['comment_id'] + + id_, params = build_data( + self.apikey_regular, + 'edit_comment', + comment_id=comment_id, + message='just message', + version=0, + userid=TEST_USER_ADMIN_LOGIN + ) + response = api_call(self.app, params) + expected = 'userid is not the same as your user' + assert_error(id_, expected, given=response.body) + + @pytest.mark.backends("git", "hg") + def test_api_edit_comment_wrong_user_with_permissions_to_edit_comment(self, pr_util): + pull_request = pr_util.create_pull_request() + pull_request_id = pull_request.pull_request_id + id_, params = build_data( + self.apikey, + 'comment_pull_request', + repoid=pull_request.target_repo.repo_name, + pullrequestid=pull_request_id, + message='test message' + ) + response = api_call(self.app, params) + json_response = response.json + comment_id = json_response['result']['comment_id'] + + id_, params = build_data( + self.apikey_regular, + 'edit_comment', + comment_id=comment_id, + message='just message', + version=0, + ) + response = api_call(self.app, params) + expected = "you don't have access to edit this comment" + assert_error(id_, expected, given=response.body) diff --git a/rhodecode/api/tests/test_get_method.py b/rhodecode/api/tests/test_get_method.py --- a/rhodecode/api/tests/test_get_method.py +++ b/rhodecode/api/tests/test_get_method.py @@ -37,7 +37,7 @@ class TestGetMethod(object): id_, params = build_data(self.apikey, 'get_method', pattern='*comment*') response = api_call(self.app, params) - expected = ['changeset_comment', 'comment_pull_request', + expected = ['changeset_comment', 'comment_pull_request', 'edit_comment', 'get_pull_request_comments', 'comment_commit', 'get_repo_comments'] assert_ok(id_, expected, given=response.body) diff --git a/rhodecode/api/views/pull_request_api.py b/rhodecode/api/views/pull_request_api.py --- a/rhodecode/api/views/pull_request_api.py +++ b/rhodecode/api/views/pull_request_api.py @@ -21,13 +21,13 @@ import logging -from rhodecode import events from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCValidationError from rhodecode.api.utils import ( has_superadmin_permission, Optional, OAttr, get_repo_or_error, get_pull_request_or_error, get_commit_or_error, get_user_or_error, validate_repo_permissions, resolve_ref_or_error, validate_set_owner_permissions) from rhodecode.lib.auth import (HasRepoPermissionAnyApi) +from rhodecode.lib.exceptions import CommentVersionMismatch from rhodecode.lib.base import vcs_operation_context from rhodecode.lib.utils2 import str2bool from rhodecode.model.changeset_status import ChangesetStatusModel @@ -292,10 +292,11 @@ def merge_pull_request( else: repo = pull_request.target_repo auth_user = apiuser + if not isinstance(userid, Optional): - if (has_superadmin_permission(apiuser) or - HasRepoPermissionAnyApi('repository.admin')( - user=apiuser, repo_name=repo.repo_name)): + is_repo_admin = HasRepoPermissionAnyApi('repository.admin')( + user=apiuser, repo_name=repo.repo_name) + if has_superadmin_permission(apiuser) or is_repo_admin: apiuser = get_user_or_error(userid) auth_user = apiuser.AuthUser() else: @@ -510,9 +511,9 @@ def comment_pull_request( auth_user = apiuser if not isinstance(userid, Optional): - if (has_superadmin_permission(apiuser) or - HasRepoPermissionAnyApi('repository.admin')( - user=apiuser, repo_name=repo.repo_name)): + is_repo_admin = HasRepoPermissionAnyApi('repository.admin')( + user=apiuser, repo_name=repo.repo_name) + if has_superadmin_permission(apiuser) or is_repo_admin: apiuser = get_user_or_error(userid) auth_user = apiuser.AuthUser() else: @@ -632,6 +633,79 @@ def comment_pull_request( @jsonrpc_method() +def edit_comment( + request, apiuser, message, comment_id, version, + userid=Optional(OAttr('apiuser')), +): + """ + Edit comment on the pull request or commit, + specified by the `comment_id` and version. Initially version should be 0 + + :param apiuser: This is filled automatically from the |authtoken|. + :type apiuser: AuthUser + :param comment_id: Specify the comment_id for editing + :type comment_id: int + :param version: version of the comment that will be created, starts from 0 + :type version: int + :param message: The text content of the comment. + :type message: str + :param userid: Comment on the pull request as this user + :type userid: Optional(str or int) + + Example output: + + .. code-block:: bash + + id : + result : { + "comment_history_id": "", + "version": "", + }, + error : null + """ + + auth_user = apiuser + comment = ChangesetComment.get(comment_id) + + is_super_admin = has_superadmin_permission(apiuser) + is_repo_admin = HasRepoPermissionAnyApi('repository.admin') \ + (user=apiuser, repo_name=comment.repo.repo_name) + + if not isinstance(userid, Optional): + if is_super_admin or is_repo_admin: + apiuser = get_user_or_error(userid) + auth_user = apiuser.AuthUser() + else: + raise JSONRPCError('userid is not the same as your user') + + comment_author = comment.author.user_id == auth_user.user_id + if not (comment.immutable is False and (is_super_admin or is_repo_admin) or comment_author): + raise JSONRPCError("you don't have access to edit this comment") + + try: + comment_history = CommentsModel().edit( + comment_id=comment_id, + text=message, + auth_user=auth_user, + version=version, + ) + Session().commit() + except CommentVersionMismatch: + raise JSONRPCError( + 'comment ({}) version ({}) mismatch'.format(comment_id, version) + ) + if not comment_history and not message: + raise JSONRPCError( + "comment ({}) can't be changed with empty string".format(comment_id) + ) + data = { + 'comment_history_id': comment_history.comment_history_id if comment_history else None, + 'version': comment_history.version if comment_history else None, + } + return data + + +@jsonrpc_method() def create_pull_request( request, apiuser, source_repo, target_repo, source_ref, target_ref, owner=Optional(OAttr('apiuser')), title=Optional(''), description=Optional(''), @@ -979,10 +1053,10 @@ def close_pull_request( else: repo = pull_request.target_repo + is_repo_admin = HasRepoPermissionAnyApi('repository.admin')( + user=apiuser, repo_name=repo.repo_name) if not isinstance(userid, Optional): - if (has_superadmin_permission(apiuser) or - HasRepoPermissionAnyApi('repository.admin')( - user=apiuser, repo_name=repo.repo_name)): + if has_superadmin_permission(apiuser) or is_repo_admin: apiuser = get_user_or_error(userid) else: raise JSONRPCError('userid is not the same as your user')