Skip to content

Commit 8589053

Browse files
authored
Always use the bug retrieved through the bugzilla API for field changes (#72)
Webhook requests for private bug creation and modification do not include the normal set of bug fields (for some reason requests for new comments do). So to make sure you have the full bug info you must retrieve the bug through the API and then use that to generate the correct field changes to make in Jira. This does drop one behaviour, it looks like JBI attempts to modify the reporter when the reporter changes for a bug. But to my knowledge it isn't possible to change the reporter for a bug.
1 parent 859acdc commit 8589053

File tree

3 files changed

+283
-18
lines changed

3 files changed

+283
-18
lines changed

src/jbi/bugzilla.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import json
88
import logging
99
import traceback
10-
from typing import Dict, List, Optional, Tuple
10+
from typing import Dict, List, Optional
1111
from urllib.parse import ParseResult, urlparse
1212

1313
from pydantic import BaseModel # pylint: disable=no-name-in-module
@@ -146,13 +146,16 @@ def convert_whiteboard_to_tag(self, whiteboard): # pylint: disable=no-self-use
146146
return ""
147147
return whiteboard.split(sep="-", maxsplit=1)[0].lower()
148148

149+
def issue_type(self) -> str:
150+
"""Get the Jira issue type for this bug"""
151+
type_map: dict = {"enhancement": "Task", "task": "Task", "defect": "Bug"}
152+
return type_map.get(self.type, "Task")
153+
149154
def map_as_jira_issue(self) -> Dict:
150155
"""Extract bug info as jira issue dictionary"""
151-
type_map: dict = {"enhancement": "Task", "task": "Task", "defect": "Bug"}
152156
return {
153157
"summary": self.summary,
154158
"labels": self.get_jira_labels(),
155-
"issuetype": {"name": type_map.get(self.type, "Task")},
156159
}
157160

158161
def extract_from_see_also(self):
@@ -199,20 +202,16 @@ def map_as_jira_description(self):
199202
body = f"*(description)*: \n{{quote}}{comment_body}{{quote}}"
200203
return body
201204

202-
def map_as_tuple_of_field_dict_and_comments(
205+
def map_as_comments(
203206
self,
204207
status_log_enabled: bool = True,
205208
assignee_log_enabled: bool = True,
206-
) -> Tuple[Dict, List[str]]:
209+
) -> List[str]:
207210
"""Extract update dict and comment list from Webhook Event"""
208211

209212
comments: List = []
210213
bug: BugzillaBug = self.bug # type: ignore
211214

212-
update_fields: dict = {
213-
"summary": bug.summary,
214-
"labels": bug.get_jira_labels(),
215-
}
216215
if self.event.changes:
217216
user = self.event.user.login if self.event.user else "unknown"
218217
for change in self.event.changes:
@@ -229,13 +228,7 @@ def map_as_tuple_of_field_dict_and_comments(
229228
if assignee_log_enabled and change.field in ["assigned_to", "assignee"]:
230229
comments.append({"assignee": bug.assigned_to})
231230

232-
if change.field == "reporter":
233-
update_fields[change.field] = change.added
234-
235-
comments_as_str: List[str] = [
236-
json.dumps(comment, indent=4) for comment in comments
237-
]
238-
return update_fields, comments_as_str
231+
return [json.dumps(comment, indent=4) for comment in comments]
239232

240233

241234
class BugzillaApiResponse(BaseModel):

src/jbi/whiteboard_actions/default.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ def bug_create_or_update(
6464
linked_issue_key = bug_obj.extract_from_see_also() # type: ignore
6565
if linked_issue_key:
6666
# update
67-
fields, comments = payload.map_as_tuple_of_field_dict_and_comments()
67+
comments = payload.map_as_comments()
6868
jira_response_update = self.jira_client.update_issue_field(
69-
key=linked_issue_key, fields=fields
69+
key=linked_issue_key, fields=bug_obj.map_as_jira_issue()
7070
)
7171
# comment
7272
jira_response_comments = []
@@ -88,6 +88,7 @@ def create_and_link_issue(self, payload, bug_obj):
8888
comment_list = self.bugzilla_client.get_comments(idlist=[bug_obj.id])
8989
fields = {
9090
**bug_obj.map_as_jira_issue(), # type: ignore
91+
"issuetype": {"name": bug_obj.issue_type()},
9192
"description": comment_list["bugs"][str(bug_obj.id)]["comments"][0]["text"],
9293
"project": {"key": self.jira_project_key},
9394
}

tests/unit/jbi/whiteboard_actions/test_default.py

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from unittest.mock import MagicMock
77

88
import pytest
9+
from src.jbi.bugzilla import BugzillaBug, BugzillaWebhookRequest
910

1011
from src.jbi.whiteboard_actions import default
1112

@@ -35,3 +36,273 @@ def test_default_returns_callable_with_data(webhook_request_example):
3536
value = callable_object(payload=webhook_request_example)
3637

3738
assert value["status"] == "create"
39+
40+
41+
def test_created_public():
42+
public_bug = BugzillaBug.parse_obj(
43+
{
44+
"assigned_to": "[email protected]",
45+
"comment": None,
46+
"component": "General",
47+
"creator": "[email protected]",
48+
"flags": [],
49+
"id": 654321,
50+
"is_private": False,
51+
"keywords": [],
52+
"priority": "",
53+
"product": "JBI",
54+
"resolution": "",
55+
"see_also": [],
56+
"severity": "--",
57+
"status": "NEW",
58+
"summary": "JBI Test",
59+
"type": "defect",
60+
"whiteboard": "[foo]",
61+
}
62+
)
63+
64+
public_webhook = BugzillaWebhookRequest.parse_obj(
65+
{
66+
"bug": public_bug,
67+
"event": {
68+
"action": "create",
69+
"routing_key": "bug.create",
70+
"target": "bug",
71+
"time": "2022-06-30T14:03:02",
72+
"user": {
73+
"id": 123456,
74+
"login": "[email protected]",
75+
"real_name": "Nobody [ :nobody ]",
76+
},
77+
},
78+
"webhook_id": 34,
79+
"webhook_name": "local-test",
80+
}
81+
)
82+
83+
mock_jira_client = MagicMock()
84+
mock_bugzilla_client = MagicMock()
85+
mock_bugzilla_client.get_comments.return_value = {
86+
"bugs": {"654321": {"comments": [{"text": "Initial comment"}]}}
87+
}
88+
89+
with mock.patch("src.jbi.whiteboard_actions.default.get_jira") as mocked_jira:
90+
mocked_jira.return_value = mock_jira_client
91+
with mock.patch("src.jbi.whiteboard_actions.default.get_bugzilla") as mocked_bz:
92+
mocked_bz.return_value = mock_bugzilla_client
93+
with mock.patch(
94+
"src.jbi.whiteboard_actions.default.getbug_as_bugzilla_object"
95+
) as mocked_bz_func:
96+
mocked_bz_func.return_value = public_bug
97+
callable_object = default.init(
98+
whiteboard_tag="", jira_project_key="JBI"
99+
)
100+
assert callable_object
101+
value = callable_object(payload=public_webhook)
102+
103+
mock_jira_client.create_issue.assert_called_once_with(
104+
fields={
105+
"summary": "JBI Test",
106+
"labels": ["bugzilla", "foo", "[foo]"],
107+
"issuetype": {"name": "Bug"},
108+
"description": "Initial comment",
109+
"project": {"key": "JBI"},
110+
},
111+
)
112+
assert value["status"] == "create"
113+
114+
115+
def test_created_private():
116+
private_bug = BugzillaBug.parse_obj(
117+
{
118+
"assigned_to": "[email protected]",
119+
"comment": None,
120+
"component": "General",
121+
"creator": "[email protected]",
122+
"flags": [],
123+
"id": 654321,
124+
"is_private": True,
125+
"keywords": [],
126+
"priority": "",
127+
"product": "JBI",
128+
"resolution": "",
129+
"see_also": [],
130+
"severity": "--",
131+
"status": "NEW",
132+
"summary": "JBI Test",
133+
"type": "defect",
134+
"whiteboard": "[foo]",
135+
}
136+
)
137+
138+
private_webhook = BugzillaWebhookRequest.parse_obj(
139+
{
140+
"bug": {"id": 654321, "is_private": True},
141+
"event": {
142+
"action": "create",
143+
"routing_key": "bug.create",
144+
"target": "bug",
145+
"time": "2022-06-30T14:03:02",
146+
"user": {
147+
"id": 123456,
148+
"login": "[email protected]",
149+
"real_name": "Nobody [ :nobody ]",
150+
},
151+
},
152+
"webhook_id": 34,
153+
"webhook_name": "local-test",
154+
}
155+
)
156+
157+
mock_jira_client = MagicMock()
158+
mock_bugzilla_client = MagicMock()
159+
mock_bugzilla_client.get_comments.return_value = {
160+
"bugs": {"654321": {"comments": [{"text": "Initial comment"}]}}
161+
}
162+
163+
with mock.patch("src.jbi.whiteboard_actions.default.get_jira") as mocked_jira:
164+
mocked_jira.return_value = mock_jira_client
165+
with mock.patch("src.jbi.whiteboard_actions.default.get_bugzilla") as mocked_bz:
166+
mocked_bz.return_value = mock_bugzilla_client
167+
with mock.patch(
168+
"src.jbi.whiteboard_actions.default.getbug_as_bugzilla_object"
169+
) as mocked_bz_func:
170+
mocked_bz_func.return_value = private_bug
171+
callable_object = default.init(
172+
whiteboard_tag="", jira_project_key="JBI"
173+
)
174+
assert callable_object
175+
value = callable_object(payload=private_webhook)
176+
177+
mock_jira_client.create_issue.assert_called_once_with(
178+
fields={
179+
"summary": "JBI Test",
180+
"labels": ["bugzilla", "foo", "[foo]"],
181+
"issuetype": {"name": "Bug"},
182+
"description": "Initial comment",
183+
"project": {"key": "JBI"},
184+
},
185+
)
186+
assert value["status"] == "create"
187+
188+
189+
def test_modified_public():
190+
public_bug = BugzillaBug.parse_obj(
191+
{
192+
"assigned_to": "[email protected]",
193+
"comment": None,
194+
"component": "General",
195+
"creator": "[email protected]",
196+
"flags": [],
197+
"id": 654321,
198+
"is_private": False,
199+
"keywords": [],
200+
"priority": "",
201+
"product": "JBI",
202+
"resolution": "",
203+
"see_also": ["https://mozilla.atlassian.net/browse/JBI-234"],
204+
"severity": "--",
205+
"status": "NEW",
206+
"summary": "JBI Test",
207+
"type": "defect",
208+
"whiteboard": "[foo]",
209+
}
210+
)
211+
212+
public_webhook = BugzillaWebhookRequest.parse_obj(
213+
{
214+
"bug": public_bug,
215+
"event": {
216+
"action": "modify",
217+
"routing_key": "bug.modify:status",
218+
"target": "bug",
219+
"time": "2022-06-30T14:03:02",
220+
"user": {
221+
"id": 123456,
222+
"login": "[email protected]",
223+
"real_name": "Nobody [ :nobody ]",
224+
},
225+
},
226+
"webhook_id": 34,
227+
"webhook_name": "local-test",
228+
}
229+
)
230+
231+
mock_jira_client = MagicMock()
232+
with mock.patch("src.jbi.whiteboard_actions.default.get_jira") as mocked_jira:
233+
mocked_jira.return_value = mock_jira_client
234+
with mock.patch("src.jbi.whiteboard_actions.default.get_bugzilla") as mocked_bz:
235+
with mock.patch(
236+
"src.jbi.whiteboard_actions.default.getbug_as_bugzilla_object"
237+
) as mocked_bz_func:
238+
mocked_bz_func.return_value = public_bug
239+
callable_object = default.init(whiteboard_tag="", jira_project_key="")
240+
assert callable_object
241+
value = callable_object(payload=public_webhook)
242+
243+
mock_jira_client.update_issue_field.assert_called_once_with(
244+
key="JBI-234",
245+
fields={"summary": "JBI Test", "labels": ["bugzilla", "foo", "[foo]"]},
246+
)
247+
assert value["status"] == "update"
248+
249+
250+
def test_modified_private():
251+
private_bug = BugzillaBug.parse_obj(
252+
{
253+
"assigned_to": "[email protected]",
254+
"comment": None,
255+
"component": "General",
256+
"creator": "[email protected]",
257+
"flags": [],
258+
"id": 654321,
259+
"is_private": True,
260+
"keywords": [],
261+
"priority": "",
262+
"product": "JBI",
263+
"resolution": "",
264+
"see_also": ["https://mozilla.atlassian.net/browse/JBI-234"],
265+
"severity": "--",
266+
"status": "NEW",
267+
"summary": "JBI Test",
268+
"type": "defect",
269+
"whiteboard": "[foo]",
270+
}
271+
)
272+
273+
private_webhook = BugzillaWebhookRequest.parse_obj(
274+
{
275+
"bug": {"id": 654321, "is_private": True},
276+
"event": {
277+
"action": "modify",
278+
"routing_key": "bug.modify:status",
279+
"target": "bug",
280+
"time": "2022-06-30T14:03:02",
281+
"user": {
282+
"id": 123456,
283+
"login": "[email protected]",
284+
"real_name": "Nobody [ :nobody ]",
285+
},
286+
},
287+
"webhook_id": 34,
288+
"webhook_name": "local-test",
289+
}
290+
)
291+
292+
mock_jira_client = MagicMock()
293+
with mock.patch("src.jbi.whiteboard_actions.default.get_jira") as mocked_jira:
294+
mocked_jira.return_value = mock_jira_client
295+
with mock.patch("src.jbi.whiteboard_actions.default.get_bugzilla") as mocked_bz:
296+
with mock.patch(
297+
"src.jbi.whiteboard_actions.default.getbug_as_bugzilla_object"
298+
) as mocked_bz_func:
299+
mocked_bz_func.return_value = private_bug
300+
callable_object = default.init(whiteboard_tag="", jira_project_key="")
301+
assert callable_object
302+
value = callable_object(payload=private_webhook)
303+
304+
mock_jira_client.update_issue_field.assert_called_once_with(
305+
key="JBI-234",
306+
fields={"summary": "JBI Test", "labels": ["bugzilla", "foo", "[foo]"]},
307+
)
308+
assert value["status"] == "update"

0 commit comments

Comments
 (0)