# HG changeset patch # User Marcin Kuzminski # Date 2020-07-13 09:18:28 # Node ID 912182ba879faa1ed72ba1b96be555b2510c5217 # Parent d2ba385231e2100fe59609407873aeebc1340e83 comments: added new events for comment editing to handle them in integrations. diff --git a/rhodecode/api/views/repo_api.py b/rhodecode/api/views/repo_api.py --- a/rhodecode/api/views/repo_api.py +++ b/rhodecode/api/views/repo_api.py @@ -48,6 +48,7 @@ from rhodecode.model.db import ( Session, ChangesetStatus, RepositoryField, Repository, RepoGroup, ChangesetComment) from rhodecode.model.permission import PermissionModel +from rhodecode.model.pull_request import PullRequestModel from rhodecode.model.repo import RepoModel from rhodecode.model.scm import ScmModel, RepoList from rhodecode.model.settings import SettingsModel, VcsSettingsModel @@ -1869,6 +1870,20 @@ def edit_comment(request, apiuser, messa raise JSONRPCError( "comment ({}) can't be changed with empty string".format(comment_id) ) + + if comment.pull_request: + pull_request = comment.pull_request + PullRequestModel().trigger_pull_request_hook( + pull_request, apiuser, 'comment_edit', + data={'comment': comment}) + else: + db_repo = comment.repo + commit_id = comment.revision + commit = db_repo.get_commit(commit_id) + CommentsModel().trigger_commit_comment_hook( + db_repo, apiuser, 'edit', + data={'comment': comment, 'commit': commit}) + data = { 'comment': comment, 'version': comment_history.version if comment_history else None, diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py --- a/rhodecode/apps/repository/views/repo_commits.py +++ b/rhodecode/apps/repository/views/repo_commits.py @@ -649,6 +649,12 @@ class RepoCommitsView(RepoAppView): if not comment_history: raise HTTPNotFound() + commit_id = self.request.matchdict['commit_id'] + commit = self.db_repo.get_commit(commit_id) + CommentsModel().trigger_commit_comment_hook( + self.db_repo, self._rhodecode_user, 'edit', + data={'comment': comment, 'commit': commit}) + Session().commit() return { 'comment_history_id': comment_history.comment_history_id, diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -1611,6 +1611,11 @@ class RepoPullRequestsView(RepoAppView, raise HTTPNotFound() Session().commit() + + PullRequestModel().trigger_pull_request_hook( + pull_request, self._rhodecode_user, 'comment_edit', + data={'comment': comment}) + return { 'comment_history_id': comment_history.comment_history_id, 'comment_id': comment.comment_id, diff --git a/rhodecode/events/__init__.py b/rhodecode/events/__init__.py --- a/rhodecode/events/__init__.py +++ b/rhodecode/events/__init__.py @@ -53,7 +53,8 @@ from rhodecode.events.user import ( # p ) from rhodecode.events.repo import ( # pragma: no cover - RepoEvent, RepoCommitCommentEvent, + RepoEvent, + RepoCommitCommentEvent, RepoCommitCommentEditEvent, RepoPreCreateEvent, RepoCreateEvent, RepoPreDeleteEvent, RepoDeleteEvent, RepoPrePushEvent, RepoPushEvent, @@ -72,8 +73,8 @@ from rhodecode.events.pullrequest import PullRequestCreateEvent, PullRequestUpdateEvent, PullRequestCommentEvent, + PullRequestCommentEditEvent, PullRequestReviewEvent, PullRequestMergeEvent, PullRequestCloseEvent, - PullRequestCommentEvent, ) diff --git a/rhodecode/events/pullrequest.py b/rhodecode/events/pullrequest.py --- a/rhodecode/events/pullrequest.py +++ b/rhodecode/events/pullrequest.py @@ -19,8 +19,7 @@ import logging from rhodecode.translation import lazy_ugettext -from rhodecode.events.repo import ( - RepoEvent, _commits_as_dict, _issues_as_dict) +from rhodecode.events.repo import (RepoEvent, _commits_as_dict, _issues_as_dict) log = logging.getLogger(__name__) @@ -155,6 +154,7 @@ class PullRequestCommentEvent(PullReques 'type': self.comment.comment_type, 'file': self.comment.f_path, 'line': self.comment.line_no, + 'version': self.comment.last_version, 'url': CommentsModel().get_url( self.comment, request=self.request), 'permalink_url': CommentsModel().get_url( @@ -162,3 +162,42 @@ class PullRequestCommentEvent(PullReques } }) return data + + +class PullRequestCommentEditEvent(PullRequestEvent): + """ + An instance of this class is emitted as an :term:`event` after a pull + request comment is edited. + """ + name = 'pullrequest-comment-edit' + display_name = lazy_ugettext('pullrequest comment edited') + description = lazy_ugettext('Event triggered after a comment was edited on a code ' + 'in the pull request') + + def __init__(self, pullrequest, comment): + super(PullRequestCommentEditEvent, self).__init__(pullrequest) + self.comment = comment + + def as_dict(self): + from rhodecode.model.comment import CommentsModel + data = super(PullRequestCommentEditEvent, self).as_dict() + + status = None + if self.comment.status_change: + status = self.comment.status_change[0].status + + data.update({ + 'comment': { + 'status': status, + 'text': self.comment.text, + 'type': self.comment.comment_type, + 'file': self.comment.f_path, + 'line': self.comment.line_no, + 'version': self.comment.last_version, + 'url': CommentsModel().get_url( + self.comment, request=self.request), + 'permalink_url': CommentsModel().get_url( + self.comment, request=self.request, permalink=True), + } + }) + return data diff --git a/rhodecode/events/repo.py b/rhodecode/events/repo.py --- a/rhodecode/events/repo.py +++ b/rhodecode/events/repo.py @@ -211,6 +211,42 @@ class RepoCommitCommentEvent(RepoEvent): 'comment_type': self.comment.comment_type, 'comment_f_path': self.comment.f_path, 'comment_line_no': self.comment.line_no, + 'comment_version': self.comment.last_version, + } + return data + + +class RepoCommitCommentEditEvent(RepoEvent): + """ + An instance of this class is emitted as an :term:`event` after a comment is edited + on repository commit. + """ + + name = 'repo-commit-edit-comment' + display_name = lazy_ugettext('repository commit edit comment') + description = lazy_ugettext('Event triggered after a comment was edited ' + 'on commit inside a repository') + + def __init__(self, repo, commit, comment): + super(RepoCommitCommentEditEvent, self).__init__(repo) + self.commit = commit + self.comment = comment + + def as_dict(self): + data = super(RepoCommitCommentEditEvent, self).as_dict() + data['commit'] = { + 'commit_id': self.commit.raw_id, + 'commit_message': self.commit.message, + 'commit_branch': self.commit.branch, + } + + data['comment'] = { + 'comment_id': self.comment.comment_id, + 'comment_text': self.comment.text, + 'comment_type': self.comment.comment_type, + 'comment_f_path': self.comment.f_path, + 'comment_line_no': self.comment.line_no, + 'comment_version': self.comment.last_version, } return data diff --git a/rhodecode/integrations/types/base.py b/rhodecode/integrations/types/base.py --- a/rhodecode/integrations/types/base.py +++ b/rhodecode/integrations/types/base.py @@ -331,6 +331,26 @@ class WebhookDataHandler(CommitParsingDa return [(url, self.headers, data)] + def repo_commit_comment_edit_handler(self, event, data): + url = self.get_base_parsed_template(data) + log.debug('register %s call(%s) to url %s', self.name, event, url) + comment_vars = [ + ('commit_comment_id', data['comment']['comment_id']), + ('commit_comment_text', data['comment']['comment_text']), + ('commit_comment_type', data['comment']['comment_type']), + + ('commit_comment_f_path', data['comment']['comment_f_path']), + ('commit_comment_line_no', data['comment']['comment_line_no']), + + ('commit_comment_commit_id', data['commit']['commit_id']), + ('commit_comment_commit_branch', data['commit']['commit_branch']), + ('commit_comment_commit_message', data['commit']['commit_message']), + ] + for k, v in comment_vars: + url = UrlTmpl(url).safe_substitute(**{k: v}) + + return [(url, self.headers, data)] + def repo_create_event_handler(self, event, data): url = self.get_base_parsed_template(data) log.debug('register %s call(%s) to url %s', self.name, event, url) @@ -360,6 +380,8 @@ class WebhookDataHandler(CommitParsingDa return self.repo_create_event_handler(event, data) elif isinstance(event, events.RepoCommitCommentEvent): return self.repo_commit_comment_handler(event, data) + elif isinstance(event, events.RepoCommitCommentEditEvent): + return self.repo_commit_comment_edit_handler(event, data) elif isinstance(event, events.PullRequestEvent): return self.pull_request_event_handler(event, data) else: diff --git a/rhodecode/integrations/types/hipchat.py b/rhodecode/integrations/types/hipchat.py --- a/rhodecode/integrations/types/hipchat.py +++ b/rhodecode/integrations/types/hipchat.py @@ -133,6 +133,8 @@ class HipchatIntegrationType(Integration if isinstance(event, events.PullRequestCommentEvent): text = self.format_pull_request_comment_event(event, data) + elif isinstance(event, events.PullRequestCommentEditEvent): + text = self.format_pull_request_comment_event(event, data) elif isinstance(event, events.PullRequestReviewEvent): text = self.format_pull_request_review_event(event, data) elif isinstance(event, events.PullRequestEvent): diff --git a/rhodecode/integrations/types/slack.py b/rhodecode/integrations/types/slack.py --- a/rhodecode/integrations/types/slack.py +++ b/rhodecode/integrations/types/slack.py @@ -157,6 +157,9 @@ class SlackIntegrationType(IntegrationTy if isinstance(event, events.PullRequestCommentEvent): (title, text, fields, overrides) \ = self.format_pull_request_comment_event(event, data) + elif isinstance(event, events.PullRequestCommentEditEvent): + (title, text, fields, overrides) \ + = self.format_pull_request_comment_event(event, data) elif isinstance(event, events.PullRequestReviewEvent): title, text = self.format_pull_request_review_event(event, data) elif isinstance(event, events.PullRequestEvent): diff --git a/rhodecode/integrations/types/webhook.py b/rhodecode/integrations/types/webhook.py --- a/rhodecode/integrations/types/webhook.py +++ b/rhodecode/integrations/types/webhook.py @@ -144,11 +144,13 @@ class WebhookIntegrationType(Integration events.PullRequestMergeEvent, events.PullRequestUpdateEvent, events.PullRequestCommentEvent, + events.PullRequestCommentEditEvent, events.PullRequestReviewEvent, events.PullRequestCreateEvent, events.RepoPushEvent, events.RepoCreateEvent, events.RepoCommitCommentEvent, + events.RepoCommitCommentEditEvent, ] def settings_schema(self): diff --git a/rhodecode/lib/hooks_utils.py b/rhodecode/lib/hooks_utils.py --- a/rhodecode/lib/hooks_utils.py +++ b/rhodecode/lib/hooks_utils.py @@ -97,6 +97,34 @@ def trigger_comment_commit_hooks(usernam hooks_base.log_comment_commit_repository(**extras) +def trigger_comment_commit_edit_hooks(username, repo_name, repo_type, repo, data=None): + """ + Triggers when a comment is edited on a commit + + :param username: username who edits the comment + :param repo_name: name of target repo + :param repo_type: the type of SCM target repo + :param repo: the repo object we trigger the event for + :param data: extra data for specific events e.g {'comment': comment_obj, 'commit': commit_obj} + """ + if not _supports_repo_type(repo_type): + return + + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'comment_commit') + + comment = data['comment'] + commit = data['commit'] + + events.trigger(events.RepoCommitCommentEditEvent(repo, commit, comment)) + extras.update(repo.get_dict()) + + extras.commit = commit.serialize() + extras.comment = comment.get_api_data() + extras.created_by = username + # TODO(marcink): rcextensions handlers ?? + hooks_base.log_comment_commit_repository(**extras) + + def trigger_create_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): """ Triggers create pull request action hooks @@ -196,6 +224,29 @@ def trigger_comment_pull_request_hook(us hooks_base.log_comment_pull_request(**extras) +def trigger_comment_pull_request_edit_hook(username, repo_name, repo_type, pull_request, data=None): + """ + Triggers when a comment was edited on a pull request + + :param username: username who made the edit + :param repo_name: name of target repo + :param repo_type: the type of SCM target repo + :param pull_request: the pull request that comment was made on + :param data: extra data for specific events e.g {'comment': comment_obj} + """ + if not _supports_repo_type(repo_type): + return + + extras = _get_vcs_operation_context(username, repo_name, repo_type, 'comment_pull_request') + + comment = data['comment'] + events.trigger(events.PullRequestCommentEditEvent(pull_request, comment)) + extras.update(pull_request.get_api_data()) + extras.comment = comment.get_api_data() + # TODO(marcink): handle rcextensions... + hooks_base.log_comment_pull_request(**extras) + + def trigger_update_pull_request_hook(username, repo_name, repo_type, pull_request, data=None): """ Triggers update pull request action hooks diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -789,8 +789,7 @@ class CommentsModel(BaseModel): if action == 'create': trigger_hook = hooks_utils.trigger_comment_commit_hooks elif action == 'edit': - # TODO(dan): when this is supported we trigger edit hook too - return + trigger_hook = hooks_utils.trigger_comment_commit_edit_hooks else: return diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -703,6 +703,8 @@ class PullRequestModel(BaseModel): trigger_hook = hooks_utils.trigger_update_pull_request_hook elif action == 'comment': trigger_hook = hooks_utils.trigger_comment_pull_request_hook + elif action == 'comment_edit': + trigger_hook = hooks_utils.trigger_comment_pull_request_edit_hook else: return diff --git a/rhodecode/tests/events/test_pullrequest.py b/rhodecode/tests/events/test_pullrequest.py --- a/rhodecode/tests/events/test_pullrequest.py +++ b/rhodecode/tests/events/test_pullrequest.py @@ -28,6 +28,7 @@ from rhodecode.events import ( PullRequestCreateEvent, PullRequestUpdateEvent, PullRequestCommentEvent, + PullRequestCommentEditEvent, PullRequestReviewEvent, PullRequestMergeEvent, PullRequestCloseEvent, @@ -80,6 +81,21 @@ def test_pullrequest_comment_events_seri @pytest.mark.backends("git", "hg") +def test_pullrequest_comment_edit_events_serialized(pr_util, config_stub): + pr = pr_util.create_pull_request() + comment = CommentsModel().get_comments( + pr.target_repo.repo_id, pull_request=pr)[0] + event = PullRequestCommentEditEvent(pr, comment) + data = event.as_dict() + assert data['name'] == PullRequestCommentEditEvent.name + assert data['repo']['repo_name'] == pr.target_repo.repo_name + assert data['pullrequest']['pull_request_id'] == pr.pull_request_id + assert data['pullrequest']['url'] + assert data['pullrequest']['permalink_url'] + assert data['comment']['text'] == comment.text + + +@pytest.mark.backends("git", "hg") def test_close_pull_request_events(pr_util, user_admin, config_stub): pr = pr_util.create_pull_request() diff --git a/rhodecode/tests/events/test_repo.py b/rhodecode/tests/events/test_repo.py --- a/rhodecode/tests/events/test_repo.py +++ b/rhodecode/tests/events/test_repo.py @@ -29,7 +29,8 @@ from rhodecode.events.repo import ( RepoPrePullEvent, RepoPullEvent, RepoPrePushEvent, RepoPushEvent, RepoPreCreateEvent, RepoCreateEvent, - RepoPreDeleteEvent, RepoDeleteEvent, RepoCommitCommentEvent, + RepoPreDeleteEvent, RepoDeleteEvent, + RepoCommitCommentEvent, RepoCommitCommentEditEvent ) @@ -138,8 +139,32 @@ def test_repo_commit_event(config_stub, 'comment_type': 'comment_type', 'f_path': 'f_path', 'line_no': 'line_no', + 'last_version': 0, }) event = EventClass(repo=repo_stub, commit=commit, comment=comment) data = event.as_dict() assert data['commit']['commit_id'] assert data['comment']['comment_id'] + + +@pytest.mark.parametrize('EventClass', [RepoCommitCommentEditEvent]) +def test_repo_commit_edit_event(config_stub, repo_stub, EventClass): + + commit = StrictAttributeDict({ + 'raw_id': 'raw_id', + 'message': 'message', + 'branch': 'branch', + }) + + comment = StrictAttributeDict({ + 'comment_id': 'comment_id', + 'text': 'text', + 'comment_type': 'comment_type', + 'f_path': 'f_path', + 'line_no': 'line_no', + 'last_version': 0, + }) + event = EventClass(repo=repo_stub, commit=commit, comment=comment) + data = event.as_dict() + assert data['commit']['commit_id'] + assert data['comment']['comment_id']