Skip to content

Commit 3574a3a

Browse files
authored
Fix #554: configurable issue type (#557)
* Fix #554: configurable issue type * Add missing tests as suggested by @grahamalama * Fix tests after merge
1 parent 3a34197 commit 3574a3a

File tree

7 files changed

+93
-16
lines changed

7 files changed

+93
-16
lines changed

.github/ISSUE_TEMPLATE/new-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ body:
5050
description: Select the data you'd like to sync to Jira issues
5151
options:
5252
- label: Component (from the Bug and as specified in config)
53+
- label: Issue Type (please provide mapping, eg. `enhancement` -> `Story`)
5354
- label: Status (please provide mapping, eg. `RESOLVED` -> `Done`)
5455
- label: Resolution (please provide mapping, eg. `WONTFIX` -> `Won't do`)
5556
- label: Whiteboard tags (as labels)

docs/actions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ to the Bugzilla ticket on the Jira issue.
7878
- `resolution_map` (optional)
7979
- mapping [str, str]
8080
- If defined, map the Bugzilla bug resolution to Jira issue resolution
81+
- `issue_type_map` (optional)
82+
- mapping [str, str]
83+
- If defined, map the Bugzilla type to Jira issue type (default: ``Bug`` if ``defect`` else ``Task``)
8184

8285
Minimal configuration:
8386
```yaml

jbi/actions/steps.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,20 @@ def create_issue(context: ActionContext, **parameters):
3535
"""Create the Jira issue with the first comment as the description."""
3636
bug = context.bug
3737

38+
# If not specified, issue type will be either 'Bug' or 'Task'
39+
issue_type_map = parameters.get(
40+
"issue_type_map", {"enhancement": "Task", "task": "Task", "defect": "Bug"}
41+
)
42+
if not isinstance(issue_type_map, dict):
43+
raise TypeError("The 'issue_type_map' parameter must be a dictionary.")
44+
issue_type = issue_type_map.get(bug.type, "Task")
45+
3846
# In the payload of a bug creation, the `comment` field is `null`.
3947
# We fetch the list of comments to use the first one as the Jira issue description.
4048
comment_list = bugzilla.get_client().get_comments(bug.id)
4149
description = comment_list[0].text if comment_list else ""
4250

43-
jira_create_response = jira.create_jira_issue(context, description)
51+
jira_create_response = jira.create_jira_issue(context, description, issue_type)
4452
issue_key = jira_create_response.get("key")
4553

4654
context = context.update(jira=context.jira.update(issue=issue_key))

jbi/models.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,6 @@ def is_assigned(self) -> bool:
234234
"""Return `true` if the bug is assigned to a user."""
235235
return self.assigned_to != "[email protected]"
236236

237-
def issue_type(self) -> str:
238-
"""Get the Jira issue type for this bug"""
239-
type_map: dict = {"enhancement": "Task", "task": "Task", "defect": "Bug"}
240-
return type_map.get(self.type, "Task")
241-
242237
def extract_from_see_also(self):
243238
"""Extract Jira Issue Key from see_also if jira url present"""
244239
if not self.see_also or len(self.see_also) == 0:

jbi/services/jira.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,7 @@ class JiraCreateError(Exception):
232232
"""Error raised on Jira issue creation."""
233233

234234

235-
def create_jira_issue(
236-
context: ActionContext,
237-
description: str,
238-
):
235+
def create_jira_issue(context: ActionContext, description: str, issue_type: str):
239236
"""Create a Jira issue with basic fields in the project and return its key."""
240237
bug = context.bug
241238
logger.debug(
@@ -245,7 +242,7 @@ def create_jira_issue(
245242
)
246243
fields: dict[str, Any] = {
247244
"summary": bug.summary,
248-
"issuetype": {"name": bug.issue_type()},
245+
"issuetype": {"name": issue_type},
249246
"description": description[:JIRA_DESCRIPTION_CHAR_LIMIT],
250247
"project": {"key": context.jira.project},
251248
}

tests/unit/actions/test_steps.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,80 @@ def test_created_public(
6161
)
6262

6363

64+
def test_misconfigured_issue_type(
65+
context_create_example: ActionContext, mocked_jira, mocked_bugzilla
66+
):
67+
context_create_example.bug.type = "enhancement"
68+
69+
callable_object = default.init(
70+
jira_project_key=context_create_example.jira.project,
71+
steps={"new": ["create_issue"]},
72+
issue_type_map=["Epic"], # list instead dict!
73+
)
74+
with pytest.raises(TypeError):
75+
callable_object(context=context_create_example)
76+
77+
78+
def test_created_with_custom_issue_type_and_fallback(
79+
context_create_example: ActionContext, mocked_jira, mocked_bugzilla
80+
):
81+
context_create_example.bug.type = "enhancement"
82+
mocked_jira.create_issue.return_value = {"key": "k"}
83+
mocked_bugzilla.get_bug.return_value = context_create_example.bug
84+
mocked_bugzilla.get_comments.return_value = [
85+
comment_factory(text="Initial comment")
86+
]
87+
88+
callable_object = default.init(
89+
jira_project_key=context_create_example.jira.project,
90+
steps={"new": ["create_issue"]},
91+
issue_type_map={
92+
"task": "Epic",
93+
},
94+
)
95+
96+
callable_object(context=context_create_example)
97+
98+
mocked_jira.create_issue.assert_called_once_with(
99+
fields={
100+
"summary": "JBI Test",
101+
"issuetype": {"name": "Task"},
102+
"description": "Initial comment",
103+
"project": {"key": "JBI"},
104+
},
105+
)
106+
107+
108+
def test_created_with_custom_issue_type(
109+
context_create_example: ActionContext, mocked_jira, mocked_bugzilla
110+
):
111+
context_create_example.bug.type = "task"
112+
mocked_jira.create_issue.return_value = {"key": "k"}
113+
mocked_bugzilla.get_bug.return_value = context_create_example.bug
114+
mocked_bugzilla.get_comments.return_value = [
115+
comment_factory(text="Initial comment")
116+
]
117+
118+
callable_object = default.init(
119+
jira_project_key=context_create_example.jira.project,
120+
steps={"new": ["create_issue"]},
121+
issue_type_map={
122+
"task": "Epic",
123+
},
124+
)
125+
126+
callable_object(context=context_create_example)
127+
128+
mocked_jira.create_issue.assert_called_once_with(
129+
fields={
130+
"summary": "JBI Test",
131+
"issuetype": {"name": "Epic"},
132+
"description": "Initial comment",
133+
"project": {"key": "JBI"},
134+
},
135+
)
136+
137+
64138
def test_modified_public(context_update_example: ActionContext, mocked_jira):
65139
context_update_example.event.changes = [
66140
webhook_event_change_factory(field="summary", removed="", added="JBI Test")

tests/unit/services/test_jira.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ def test_jira_create_issue_is_instrumented(
2525
},
2626
)
2727

28-
jira.create_jira_issue(
29-
context_create_example,
30-
"Description",
31-
)
28+
jira.create_jira_issue(context_create_example, "Description", issue_type="Task")
3229
jira_client = jira.get_client()
3330

3431
jira_client.create_issue({})
@@ -98,7 +95,9 @@ def test_jira_does_not_retry_4XX(mocked_responses, context_create_example):
9895
)
9996

10097
with pytest.raises(requests.HTTPError):
101-
jira.create_jira_issue(context=context_create_example, description="")
98+
jira.create_jira_issue(
99+
context=context_create_example, description="", issue_type="Task"
100+
)
102101

103102
assert len(mocked_responses.calls) == 1
104103

0 commit comments

Comments
 (0)