mirror of
https://github.com/zulip/zulip.git
synced 2025-11-01 12:33:40 +00:00
integrations: Add support for GitLab design comments.
Fixes #26199. Co-authored-by: Barış <barisunsalhn@users.noreply.github.com> Co-authored-by: Satyam Bansal <sbansal1999@gmail.com>
This commit is contained in:
94
zerver/webhooks/gitlab/fixtures/note_hook__design_note.json
Normal file
94
zerver/webhooks/gitlab/fixtures/note_hook__design_note.json
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"object_kind": "note",
|
||||
"event_type": "note",
|
||||
"user": {
|
||||
"id": 14613894,
|
||||
"name": "Satyam Bansal",
|
||||
"username": "sbansal1999",
|
||||
"avatar_url": "https://gitlab.com/uploads/-/system/user/avatar/14613894/avatar.png",
|
||||
"email": "[REDACTED]"
|
||||
},
|
||||
"project_id": 46186758,
|
||||
"project": {
|
||||
"id": 46186758,
|
||||
"name": "testing-zulip-gitlab-integration",
|
||||
"description": null,
|
||||
"web_url": "https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration",
|
||||
"avatar_url": null,
|
||||
"git_ssh_url": "git@gitlab.com:sbansal1999/testing-zulip-gitlab-integration.git",
|
||||
"git_http_url": "https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration.git",
|
||||
"namespace": "Satyam Bansal",
|
||||
"visibility_level": 0,
|
||||
"path_with_namespace": "sbansal1999/testing-zulip-gitlab-integration",
|
||||
"default_branch": "main",
|
||||
"ci_config_path": "",
|
||||
"homepage": "https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration",
|
||||
"url": "git@gitlab.com:sbansal1999/testing-zulip-gitlab-integration.git",
|
||||
"ssh_url": "git@gitlab.com:sbansal1999/testing-zulip-gitlab-integration.git",
|
||||
"http_url": "https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration.git"
|
||||
},
|
||||
"object_attributes": {
|
||||
"attachment": null,
|
||||
"author_id": 14613894,
|
||||
"change_position": {
|
||||
"base_sha": null,
|
||||
"start_sha": null,
|
||||
"head_sha": null,
|
||||
"old_path": null,
|
||||
"new_path": null,
|
||||
"position_type": "text",
|
||||
"old_line": null,
|
||||
"new_line": null,
|
||||
"line_range": null
|
||||
},
|
||||
"commit_id": null,
|
||||
"created_at": "2023-07-05 16:38:53 UTC",
|
||||
"discussion_id": "90c3bd62a51e132229eb2433a42af0707c1b7091",
|
||||
"id": 1458583152,
|
||||
"line_code": null,
|
||||
"note": "hello",
|
||||
"noteable_id": 601586,
|
||||
"noteable_type": "DesignManagement::Design",
|
||||
"original_position": {
|
||||
"base_sha": "0000000000000000000000000000000000000000",
|
||||
"start_sha": "0000000000000000000000000000000000000000",
|
||||
"head_sha": "3e8137cae02e78675cd220ae1dd2ced26feba62f",
|
||||
"old_path": null,
|
||||
"new_path": "designs/issue-1/Screenshot.png",
|
||||
"position_type": "image",
|
||||
"width": 740,
|
||||
"height": 499,
|
||||
"x": 413,
|
||||
"y": 315
|
||||
},
|
||||
"position": {
|
||||
"base_sha": "0000000000000000000000000000000000000000",
|
||||
"start_sha": "0000000000000000000000000000000000000000",
|
||||
"head_sha": "3e8137cae02e78675cd220ae1dd2ced26feba62f",
|
||||
"old_path": null,
|
||||
"new_path": "designs/issue-1/Screenshot.png",
|
||||
"position_type": "image",
|
||||
"width": 740,
|
||||
"height": 499,
|
||||
"x": 413,
|
||||
"y": 315
|
||||
},
|
||||
"project_id": 46186758,
|
||||
"resolved_at": null,
|
||||
"resolved_by_id": null,
|
||||
"resolved_by_push": null,
|
||||
"st_diff": null,
|
||||
"system": false,
|
||||
"type": "DiffNote",
|
||||
"updated_at": "2023-07-05 16:38:53 UTC",
|
||||
"updated_by_id": null,
|
||||
"description": "hello",
|
||||
"url": null
|
||||
},
|
||||
"repository": {
|
||||
"name": "testing-zulip-gitlab-integration",
|
||||
"url": "git@gitlab.com:sbansal1999/testing-zulip-gitlab-integration.git",
|
||||
"description": null,
|
||||
"homepage": "https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration"
|
||||
}
|
||||
}
|
||||
@@ -257,6 +257,12 @@ class GitlabHookTests(WebhookTestCase):
|
||||
|
||||
self.check_webhook("note_hook__issue_note", expected_topic_name, expected_message)
|
||||
|
||||
def test_note_design_event_message(self) -> None:
|
||||
expected_topic_name = "testing-zulip-gitlab-integration / design Screenshot.png"
|
||||
expected_message = "Satyam Bansal [commented](https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration/-/issues/1/designs/Screenshot.png#note_1458583152) on design [Screenshot.png](https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration/-/issues/1/designs/Screenshot.png):\n\n~~~ quote\nhello\n~~~"
|
||||
|
||||
self.check_webhook("note_hook__design_note", expected_topic_name, expected_message)
|
||||
|
||||
def test_note_confidential_issue_event_message(self) -> None:
|
||||
expected_subject = "testing-zulip-gitlab-integration / issue #1 Add more lines"
|
||||
expected_message = "Satyam Bansal [commented](https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration/-/issues/1#note_1406130881) on [issue #1](https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration/-/issues/1):\n\n~~~ quote\nSome more comments\n~~~"
|
||||
@@ -270,6 +276,13 @@ class GitlabHookTests(WebhookTestCase):
|
||||
|
||||
self.check_webhook("note_hook__issue_note", expected_topic_name, expected_message)
|
||||
|
||||
def test_note_design_with_custom_topic_in_url(self) -> None:
|
||||
self.url = self.build_webhook_url(topic="notifications")
|
||||
expected_topic_name = "notifications"
|
||||
expected_message = "[[testing-zulip-gitlab-integration](https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration)] Satyam Bansal [commented](https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration/-/issues/1/designs/Screenshot.png#note_1458583152) on design [Screenshot.png](https://gitlab.com/sbansal1999/testing-zulip-gitlab-integration/-/issues/1/designs/Screenshot.png):\n\n~~~ quote\nhello\n~~~"
|
||||
|
||||
self.check_webhook("note_hook__design_note", expected_topic_name, expected_message)
|
||||
|
||||
def test_note_snippet_old_event_message(self) -> None:
|
||||
expected_topic_name = "my-awesome-project / snippet #2 test"
|
||||
expected_message = "Tomasz Kolek [commented](https://gitlab.com/tomaszkolek0/my-awesome-project/snippets/2#note_14172058) on [snippet #2](https://gitlab.com/tomaszkolek0/my-awesome-project/-/snippets/2):\n\n~~~ quote\nNice snippet\n~~~"
|
||||
|
||||
@@ -31,6 +31,11 @@ from zerver.lib.webhooks.git import (
|
||||
)
|
||||
from zerver.models import UserProfile
|
||||
|
||||
TOPIC_WITH_DESIGN_INFO_TEMPLATE = "{repo} / {type} {design_name}"
|
||||
DESIGN_COMMENT_MESSAGE_TEMPLATE = (
|
||||
"{user_name} {action} on design [{design_name}]({design_url}):\n{content_message}"
|
||||
)
|
||||
|
||||
|
||||
def fixture_to_headers(fixture_name: str) -> dict[str, str]:
|
||||
if fixture_name.startswith("build"):
|
||||
@@ -209,6 +214,23 @@ def replace_assignees_username_with_name(
|
||||
return formatted_assignees
|
||||
|
||||
|
||||
def parse_design_comment(comment: WildValue, repository_url: str) -> tuple[str, str, str]:
|
||||
note_id = comment["id"].tame(check_int)
|
||||
|
||||
# As there is no issue field in the payloads related to designs,
|
||||
# we need to parse the issue number from the new_path field.
|
||||
design_path = comment["position"]["new_path"].tame(check_string)
|
||||
|
||||
# Sample design_path: "designs/issue-1/Screenshot_20250302_230445.png"
|
||||
_, issue_subpath, design_name = design_path.split("/")
|
||||
|
||||
issue_number = issue_subpath.split("-")[-1]
|
||||
design_url = f"{repository_url}/-/issues/{issue_number}/designs/{design_name}"
|
||||
comment_url = f"{design_url}#note_{note_id}"
|
||||
|
||||
return comment_url, design_url, design_name
|
||||
|
||||
|
||||
def get_commented_commit_event_body(payload: WildValue, include_title: bool) -> str:
|
||||
comment = payload["object_attributes"]
|
||||
action = "[commented]({})".format(comment["url"].tame(check_string))
|
||||
@@ -253,6 +275,23 @@ def get_commented_issue_event_body(payload: WildValue, include_title: bool) -> s
|
||||
)
|
||||
|
||||
|
||||
def get_commented_design_event_body(payload: WildValue, include_title: bool) -> str:
|
||||
comment = payload["object_attributes"]
|
||||
repository_url = payload["repository"]["homepage"].tame(check_string)
|
||||
|
||||
comment_url, design_url, design_name = parse_design_comment(comment, repository_url)
|
||||
action = f"[commented]({comment_url})"
|
||||
content_message = CONTENT_MESSAGE_TEMPLATE.format(message=comment["note"].tame(check_string))
|
||||
|
||||
return DESIGN_COMMENT_MESSAGE_TEMPLATE.format(
|
||||
user_name=get_issue_user_name(payload),
|
||||
action=action,
|
||||
design_name=design_name,
|
||||
design_url=design_url,
|
||||
content_message=content_message,
|
||||
)
|
||||
|
||||
|
||||
def get_commented_snippet_event_body(payload: WildValue, include_title: bool) -> str:
|
||||
comment = payload["object_attributes"]
|
||||
action = "[commented]({}) on".format(comment["url"].tame(check_string))
|
||||
@@ -405,6 +444,17 @@ def get_object_url(payload: WildValue) -> str:
|
||||
return payload["object_attributes"]["url"].tame(check_string)
|
||||
|
||||
|
||||
def skip_previews(event: str) -> bool:
|
||||
# Add event names to this array for which previews should be skipped.
|
||||
return event in [
|
||||
# Design events link to images, but the images cannot be
|
||||
# accessed without authentication, so trying to preview them
|
||||
# doesn't work.
|
||||
"Note Hook DesignManagement::Design",
|
||||
"Confidential Note Hook DesignManagement::Design",
|
||||
]
|
||||
|
||||
|
||||
class EventFunction(Protocol):
|
||||
def __call__(self, payload: WildValue, include_title: bool) -> str: ...
|
||||
|
||||
@@ -425,6 +475,8 @@ EVENT_FUNCTION_MAPPER: dict[str, EventFunction] = {
|
||||
"Note Hook MergeRequest": get_commented_merge_request_event_body,
|
||||
"Note Hook Issue": get_commented_issue_event_body,
|
||||
"Confidential Note Hook Issue": get_commented_issue_event_body,
|
||||
"Note Hook DesignManagement::Design": get_commented_design_event_body,
|
||||
"Confidential Note Hook DesignManagement::Design": get_commented_design_event_body,
|
||||
"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"),
|
||||
@@ -469,7 +521,9 @@ def api_gitlab_webhook(
|
||||
body = f"[{project_url}] {body}"
|
||||
|
||||
topic_name = get_topic_based_on_event(event, payload, use_merge_request_title)
|
||||
check_send_webhook_message(request, user_profile, topic_name, body, event)
|
||||
check_send_webhook_message(
|
||||
request, user_profile, topic_name, body, event, no_previews=skip_previews(event)
|
||||
)
|
||||
return json_success(request)
|
||||
|
||||
|
||||
@@ -514,6 +568,18 @@ def get_topic_based_on_event(event: str, payload: WildValue, use_merge_request_t
|
||||
id=payload["issue"]["iid"].tame(check_int),
|
||||
title=payload["issue"]["title"].tame(check_string),
|
||||
)
|
||||
elif event in (
|
||||
"Note Hook DesignManagement::Design",
|
||||
"Confidential Note Hook DesignManagement::Design",
|
||||
):
|
||||
design_path = payload["object_attributes"]["position"]["new_path"].tame(check_string)
|
||||
design_name = design_path.split("/")[-1]
|
||||
|
||||
return TOPIC_WITH_DESIGN_INFO_TEMPLATE.format(
|
||||
repo=get_repo_name(payload),
|
||||
type="design",
|
||||
design_name=design_name,
|
||||
)
|
||||
elif event == "Note Hook MergeRequest":
|
||||
return TOPIC_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format(
|
||||
repo=get_repo_name(payload),
|
||||
|
||||
Reference in New Issue
Block a user