diff --git a/zerver/lib/webhooks/git.py b/zerver/lib/webhooks/git.py index f7056f92a9..f126eb3acf 100644 --- a/zerver/lib/webhooks/git.py +++ b/zerver/lib/webhooks/git.py @@ -206,9 +206,14 @@ def get_pull_request_event_message( type: str = "PR", title: str | None = None, ) -> str: + action_messages = { + "approval": "added their approval for", + "unapproval": "removed their approval for", + } + kwargs = { "user_name": user_name, - "action": action, + "action": action_messages.get(action, action), "type": type, "url": url, "id": f" #{number}" if number is not None else "", diff --git a/zerver/webhooks/gitlab/fixtures/merge_request_hook__merge_request_approval.json b/zerver/webhooks/gitlab/fixtures/merge_request_hook__merge_request_approval.json new file mode 100644 index 0000000000..9a3ece9191 --- /dev/null +++ b/zerver/webhooks/gitlab/fixtures/merge_request_hook__merge_request_approval.json @@ -0,0 +1,142 @@ +{ + "object_kind": "merge_request", + "event_type": "merge_request", + "user": { + "id": 587951, + "name": "Sumit Bhanushali", + "username": "sumitbhanu", + "avatar_url": "https://secure.gravatar.com/avatar/432182d26b23e4b0b2f89bf21263bf321215f562258f2f31dfcd6372789a10a2?s=80&d=identicon", + "email": "[REDACTED]" + }, + "project": { + "id": 68500871, + "name": "my-awesome-project", + "description": null, + "web_url": "https://gitlab.com/sumitb16/my-awesome-project", + "avatar_url": null, + "git_ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "git_http_url": "https://gitlab.com/sumitb16/my-awesome-project.git", + "namespace": "sumitb16", + "visibility_level": 0, + "path_with_namespace": "sumitb16/my-awesome-project", + "default_branch": "main", + "ci_config_path": "", + "homepage": "https://gitlab.com/sumitb16/my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "http_url": "https://gitlab.com/sumitb16/my-awesome-project.git" + }, + "object_attributes": { + "assignee_id": null, + "author_id": 26939388, + "created_at": "2025-03-30 07:38:58 UTC", + "description": "", + "draft": false, + "head_pipeline_id": null, + "id": 373098278, + "iid": 1, + "last_edited_at": null, + "last_edited_by_id": null, + "merge_commit_sha": null, + "merge_error": null, + "merge_params": { + "force_remove_source_branch": "1" + }, + "merge_status": "can_be_merged", + "merge_user_id": null, + "merge_when_pipeline_succeeds": false, + "milestone_id": null, + "source_branch": "t3", + "source_project_id": 68500871, + "state_id": 1, + "target_branch": "main", + "target_project_id": 68500871, + "time_estimate": 0, + "title": "Update the README", + "updated_at": "2025-03-30 08:13:27 UTC", + "updated_by_id": 26939388, + "prepared_at": "2025-03-30 07:38:59 UTC", + "assignee_ids": [], + "blocking_discussions_resolved": true, + "detailed_merge_status": "not_approved", + "first_contribution": false, + "human_time_change": null, + "human_time_estimate": null, + "human_total_time_spent": null, + "labels": [], + "last_commit": { + "id": "f61e5631699e3b869180fea166fd0968ec2c3992", + "message": "Update file README.md", + "title": "Update file README.md", + "timestamp": "2025-03-30T07:38:04+00:00", + "url": "https://gitlab.com/sumitb16/my-awesome-project/-/commit/f61e5631699e3b869180fea166fd0968ec2c3992", + "author": { + "name": "Sumit Bhanushali", + "email": "[REDACTED]" + } + }, + "reviewer_ids": [ + 26939388 + ], + "source": { + "id": 68500871, + "name": "my-awesome-project", + "description": null, + "web_url": "https://gitlab.com/sumitb16/my-awesome-project", + "avatar_url": null, + "git_ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "git_http_url": "https://gitlab.com/sumitb16/my-awesome-project.git", + "namespace": "sumitb16", + "visibility_level": 0, + "path_with_namespace": "sumitb16/my-awesome-project", + "default_branch": "main", + "ci_config_path": "", + "homepage": "https://gitlab.com/sumitb16/my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "http_url": "https://gitlab.com/sumitb16/my-awesome-project.git" + }, + "state": "opened", + "target": { + "id": 68500871, + "name": "my-awesome-project", + "description": null, + "web_url": "https://gitlab.com/sumitb16/my-awesome-project", + "avatar_url": null, + "git_ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "git_http_url": "https://gitlab.com/sumitb16/my-awesome-project.git", + "namespace": "sumitb16", + "visibility_level": 0, + "path_with_namespace": "sumitb16/my-awesome-project", + "default_branch": "main", + "ci_config_path": "", + "homepage": "https://gitlab.com/sumitb16/my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "http_url": "https://gitlab.com/sumitb16/my-awesome-project.git" + }, + "time_change": 0, + "total_time_spent": 0, + "url": "https://gitlab.com/sumitb16/my-awesome-project/merge_requests/1", + "work_in_progress": false, + "approval_rules": [], + "action": "approval" + }, + "labels": [], + "changes": {}, + "repository": { + "name": "my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "description": null, + "homepage": "https://gitlab.com/sumitb16/my-awesome-project" + }, + "reviewers": [ + { + "id": 26939388, + "name": "Sumit Bhanushali", + "username": "logancodes16", + "avatar_url": "https://secure.gravatar.com/avatar/83f4e0baf47f363b6064bf8376d12baabf2ab6b38c1f41a6b1fa41b6d038f254?s=80&d=identicon", + "email": "[REDACTED]" + } + ] +} diff --git a/zerver/webhooks/gitlab/fixtures/merge_request_hook__merge_request_unapproval.json b/zerver/webhooks/gitlab/fixtures/merge_request_hook__merge_request_unapproval.json new file mode 100644 index 0000000000..051975d069 --- /dev/null +++ b/zerver/webhooks/gitlab/fixtures/merge_request_hook__merge_request_unapproval.json @@ -0,0 +1,142 @@ +{ + "object_kind": "merge_request", + "event_type": "merge_request", + "user": { + "id": 26939734, + "name": "Sumit Bhanushali", + "username": "gunner.sumit", + "avatar_url": "https://secure.gravatar.com/avatar/62375142d03eb8b07a511a443f8d2af36497e8f36f24b71b6ffabecadda41598?s=80&d=identicon", + "email": "[REDACTED]" + }, + "project": { + "id": 68500871, + "name": "my-awesome-project", + "description": null, + "web_url": "https://gitlab.com/sumitb16/my-awesome-project", + "avatar_url": null, + "git_ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "git_http_url": "https://gitlab.com/sumitb16/my-awesome-project.git", + "namespace": "sumitb16", + "visibility_level": 0, + "path_with_namespace": "sumitb16/my-awesome-project", + "default_branch": "main", + "ci_config_path": "", + "homepage": "https://gitlab.com/sumitb16/my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "http_url": "https://gitlab.com/sumitb16/my-awesome-project.git" + }, + "object_attributes": { + "assignee_id": null, + "author_id": 26939388, + "created_at": "2025-03-30 07:38:58 UTC", + "description": "", + "draft": false, + "head_pipeline_id": null, + "id": 373098278, + "iid": 1, + "last_edited_at": null, + "last_edited_by_id": null, + "merge_commit_sha": null, + "merge_error": null, + "merge_params": { + "force_remove_source_branch": "1" + }, + "merge_status": "can_be_merged", + "merge_user_id": null, + "merge_when_pipeline_succeeds": false, + "milestone_id": null, + "source_branch": "t3", + "source_project_id": 68500871, + "state_id": 1, + "target_branch": "main", + "target_project_id": 68500871, + "time_estimate": 0, + "title": "Update the README", + "updated_at": "2025-03-30 08:22:36 UTC", + "updated_by_id": 26939388, + "prepared_at": "2025-03-30 07:38:59 UTC", + "assignee_ids": [], + "blocking_discussions_resolved": true, + "detailed_merge_status": "not_approved", + "first_contribution": false, + "human_time_change": null, + "human_time_estimate": null, + "human_total_time_spent": null, + "labels": [], + "last_commit": { + "id": "f61e5631699e3b869180fea166fd0968ec2c3992", + "message": "Update file README.md", + "title": "Update file README.md", + "timestamp": "2025-03-30T07:38:04+00:00", + "url": "https://gitlab.com/sumitb16/my-awesome-project/-/commit/f61e5631699e3b869180fea166fd0968ec2c3992", + "author": { + "name": "Sumit Bhanushali", + "email": "[REDACTED]" + } + }, + "reviewer_ids": [ + 26939388 + ], + "source": { + "id": 68500871, + "name": "my-awesome-project", + "description": null, + "web_url": "https://gitlab.com/sumitb16/my-awesome-project", + "avatar_url": null, + "git_ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "git_http_url": "https://gitlab.com/sumitb16/my-awesome-project.git", + "namespace": "sumitb16", + "visibility_level": 0, + "path_with_namespace": "sumitb16/my-awesome-project", + "default_branch": "main", + "ci_config_path": "", + "homepage": "https://gitlab.com/sumitb16/my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "http_url": "https://gitlab.com/sumitb16/my-awesome-project.git" + }, + "state": "opened", + "target": { + "id": 68500871, + "name": "my-awesome-project", + "description": null, + "web_url": "https://gitlab.com/sumitb16/my-awesome-project", + "avatar_url": null, + "git_ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "git_http_url": "https://gitlab.com/sumitb16/my-awesome-project.git", + "namespace": "sumitb16", + "visibility_level": 0, + "path_with_namespace": "sumitb16/my-awesome-project", + "default_branch": "main", + "ci_config_path": "", + "homepage": "https://gitlab.com/sumitb16/my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "ssh_url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "http_url": "https://gitlab.com/sumitb16/my-awesome-project.git" + }, + "time_change": 0, + "total_time_spent": 0, + "url": "https://gitlab.com/sumitb16/my-awesome-project/merge_requests/1", + "work_in_progress": false, + "approval_rules": [], + "action": "unapproval" + }, + "labels": [], + "changes": {}, + "repository": { + "name": "my-awesome-project", + "url": "git@gitlab.com:sumitb16/my-awesome-project.git", + "description": null, + "homepage": "https://gitlab.com/sumitb16/my-awesome-project" + }, + "reviewers": [ + { + "id": 26939388, + "name": "Sumit Bhanushali", + "username": "logancodes16", + "avatar_url": "https://secure.gravatar.com/avatar/83f4e0baf47f363b6064bf8376d12baabf2ab6b38c1f41a6b1fa41b6d038f254?s=80&d=identicon", + "email": "[REDACTED]" + } + ] +} diff --git a/zerver/webhooks/gitlab/tests.py b/zerver/webhooks/gitlab/tests.py index 004bb9008c..7e04d1df7d 100644 --- a/zerver/webhooks/gitlab/tests.py +++ b/zerver/webhooks/gitlab/tests.py @@ -388,6 +388,22 @@ A trivial change that should probably be ignored. "merge_request_hook__merge_request_approved", expected_topic_name, expected_message ) + def test_merge_request_approval_event_message(self) -> None: + expected_topic_name = "my-awesome-project / MR #1 Update the README" + expected_message = "Sumit Bhanushali added their approval for [MR #1](https://gitlab.com/sumitb16/my-awesome-project/merge_requests/1)." + + self.check_webhook( + "merge_request_hook__merge_request_approval", expected_topic_name, expected_message + ) + + def test_merge_request_unapproval_event_message(self) -> None: + expected_topic_name = "my-awesome-project / MR #1 Update the README" + expected_message = "Sumit Bhanushali removed their approval for [MR #1](https://gitlab.com/sumitb16/my-awesome-project/merge_requests/1)." + + self.check_webhook( + "merge_request_hook__merge_request_unapproval", expected_topic_name, expected_message + ) + def test_merge_request_updated_event_message(self) -> None: expected_topic_name = "my-awesome-project / MR #3 New Merge Request" expected_message = "Tomasz Kolek updated [MR #3](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/3) (assigned to Tomasz Kolek):\n\n~~~ quote\nupdated desc\n~~~" diff --git a/zerver/webhooks/gitlab/view.py b/zerver/webhooks/gitlab/view.py index 2cf18470d7..84b267f5b7 100644 --- a/zerver/webhooks/gitlab/view.py +++ b/zerver/webhooks/gitlab/view.py @@ -480,6 +480,10 @@ EVENT_FUNCTION_MAPPER: dict[str, EventFunction] = { "Note Hook Snippet": get_commented_snippet_event_body, "Merge Request Hook approved": partial(get_merge_request_event_body, "approved"), "Merge Request Hook unapproved": partial(get_merge_request_event_body, "unapproved"), + # approval and unapproval events are triggered only if there's more than one required approver + # ref: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8742 + "Merge Request Hook approval": partial(get_merge_request_event_body, "approval"), + "Merge Request Hook unapproval": partial(get_merge_request_event_body, "unapproval"), "Merge Request Hook open": partial(get_merge_request_open_or_updated_body, "created"), "Merge Request Hook update": get_merge_request_updated_event_body, "Merge Request Hook merge": partial(get_merge_request_event_body, "merged"),