Skip to content

Commit 885f807

Browse files
authored
fix(GitLab): Linked issue tag isn't updated when webhook uses the work_items URL (#7305)
1 parent 2a91d0c commit 885f807

4 files changed

Lines changed: 66 additions & 6 deletions

File tree

api/integrations/gitlab/mappers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,21 @@ def map_gitlab_resource_to_tag_label(
5656
return None
5757

5858

59+
def map_resource_url_to_filter_value(url: str) -> list[str]:
60+
"""Return a list of equivalent URL shapes for use as an ``__in`` filter
61+
value when looking up a linked GitLab resource.
62+
63+
GitLab delivers issue webhooks with ``/-/work_items/<iid>`` URLs even when
64+
the feature was linked via the legacy ``/-/issues/<iid>`` form, and vice
65+
versa. Both shapes refer to the same issue.
66+
"""
67+
if "/-/issues/" in url:
68+
return [url, url.replace("/-/issues/", "/-/work_items/", 1)]
69+
if "/-/work_items/" in url:
70+
return [url, url.replace("/-/work_items/", "/-/issues/", 1)]
71+
return [url]
72+
73+
5974
def map_gitlab_webhook_payload_to_tag_label(
6075
payload: GitLabWebhookPayload,
6176
) -> GitLabTagLabel | None:

api/integrations/gitlab/services.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from integrations.gitlab.mappers import (
2828
map_gitlab_resource_to_tag_label,
2929
map_gitlab_webhook_payload_to_tag_label,
30+
map_resource_url_to_filter_value,
3031
)
3132
from integrations.gitlab.models import GitLabConfiguration, GitLabWebhook
3233
from integrations.gitlab.types import GitLabWebhookPayload
@@ -215,7 +216,7 @@ def apply_tag_for_event(
215216
if not (
216217
feature := Feature.objects.filter(
217218
project=webhook.gitlab_configuration.project,
218-
external_resources__url=resource_url,
219+
external_resources__url__in=map_resource_url_to_filter_value(resource_url),
219220
external_resources__type__in=GITLAB_RESOURCE_TYPES,
220221
).first()
221222
):

api/tests/integration/features/test_gitlab_webhook.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,50 @@ def _link(
7171
return _link
7272

7373

74+
@pytest.mark.parametrize(
75+
"linked_url, payload_url",
76+
[
77+
(
78+
"https://gitlab.example.com/testorg/testrepo/-/issues/42",
79+
"https://gitlab.example.com/testorg/testrepo/-/work_items/42",
80+
),
81+
(
82+
"https://gitlab.example.com/testorg/testrepo/-/work_items/42",
83+
"https://gitlab.example.com/testorg/testrepo/-/issues/42",
84+
),
85+
],
86+
)
87+
def test_gitlab_webhook__issue_url_variants__still_matches_feature(
88+
api_client: APIClient,
89+
feature: int,
90+
webhook_url: str,
91+
link_feature: LinkFeatureFixture,
92+
linked_url: str,
93+
payload_url: str,
94+
) -> None:
95+
# Given — GitLab may deliver ``work_items`` URLs even when the link uses the
96+
# legacy ``issues`` path (or vice versa). Both shapes refer to the same issue.
97+
link_feature(linked_url, ResourceType.GITLAB_ISSUE, metadata={"state": "opened"})
98+
payload = {
99+
"object_kind": "issue",
100+
"object_attributes": {"url": payload_url, "state": "closed"},
101+
}
102+
103+
# When
104+
response = api_client.post(
105+
webhook_url,
106+
data=payload,
107+
format="json",
108+
HTTP_X_GITLAB_TOKEN=WEBHOOK_SECRET,
109+
)
110+
111+
# Then
112+
assert response.status_code == status.HTTP_200_OK
113+
labels = set(Feature.objects.get(id=feature).tags.values_list("label", flat=True))
114+
assert GitLabTagLabel.ISSUE_CLOSED.value in labels
115+
assert GitLabTagLabel.ISSUE_OPEN.value not in labels
116+
117+
74118
@pytest.mark.django_db()
75119
def test_gitlab_webhook__issue_close_event__switches_tag_to_issue_closed(
76120
api_client: APIClient,

docs/docs/deployment-self-hosting/observability/_events-catalogue.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Attributes:
9898
### `gitlab.feature.tagged`
9999

100100
Logged at `info` from:
101-
- `api/integrations/gitlab/services.py:229`
101+
- `api/integrations/gitlab/services.py:230`
102102

103103
Attributes:
104104
- `action`
@@ -111,7 +111,7 @@ Attributes:
111111
### `gitlab.webhook.deregistered`
112112

113113
Logged at `info` from:
114-
- `api/integrations/gitlab/services.py:159`
114+
- `api/integrations/gitlab/services.py:160`
115115

116116
Attributes:
117117
- `gitlab.hook.id`
@@ -122,7 +122,7 @@ Attributes:
122122
### `gitlab.webhook.deregistration_failed`
123123

124124
Logged at `warning` from:
125-
- `api/integrations/gitlab/services.py:152`
125+
- `api/integrations/gitlab/services.py:153`
126126

127127
Attributes:
128128
- `exc_info`
@@ -134,7 +134,7 @@ Attributes:
134134
### `gitlab.webhook.registered`
135135

136136
Logged at `info` from:
137-
- `api/integrations/gitlab/services.py:112`
137+
- `api/integrations/gitlab/services.py:113`
138138

139139
Attributes:
140140
- `gitlab.hook.id`
@@ -146,7 +146,7 @@ Attributes:
146146
### `gitlab.webhook.registration_failed`
147147

148148
Logged at `error` from:
149-
- `api/integrations/gitlab/services.py:97`
149+
- `api/integrations/gitlab/services.py:98`
150150

151151
Attributes:
152152
- `exc_info`

0 commit comments

Comments
 (0)