Skip to content

Commit ab68cce

Browse files
authored
Properly provide project keys to Jira.paginated_projects (#792)
I (wrongly) assumed that this Jira package was [using requests](https://docs.python-requests.org/en/latest/user/quickstart/#passing-parameters-in-urls) to do param parsing, but it's actually using [urllib.parse.urlencode](https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode) directly, which doesn't do the magic of turning `{"param": ["foo", "bar"]}` into `?param=foo&param=bar`
1 parent e960885 commit ab68cce

File tree

2 files changed

+98
-7
lines changed

2 files changed

+98
-7
lines changed

jbi/services/jira.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,17 +121,17 @@ def paginated_projects(
121121
"``projects_from_cloud`` method is only available for Jira Cloud platform"
122122
)
123123

124-
params = {}
124+
params = []
125125

126126
if keys is not None:
127127
if len(keys) > 50:
128128
raise ValueError("Up to 50 project keys can be provided.")
129-
params["keys"] = list(keys)
129+
params = [("keys", key) for key in keys]
130130

131131
if included_archived:
132-
params["includeArchived"] = included_archived
132+
params.append(("includeArchived", included_archived))
133133
if expand:
134-
params["expand"] = expand
134+
params.append(("expand", expand))
135135
page_url = url or self.resource_url("project/search")
136136
is_url_absolute = bool(page_url.lower().startswith("http"))
137137
return self.get(page_url, params=params, absolute=is_url_absolute)
@@ -267,9 +267,14 @@ def _check_project_components(self, action):
267267
return True
268268

269269
def _all_project_issue_types_exist(self, actions: Actions):
270-
paginated_project_response = self.client.paginated_projects(
271-
expand="issueTypes", keys=actions.configured_jira_projects_keys
272-
)
270+
try:
271+
paginated_project_response = self.client.paginated_projects(
272+
expand="issueTypes", keys=actions.configured_jira_projects_keys
273+
)
274+
except requests.RequestException:
275+
logger.exception("Couldn't fetch projects")
276+
return False
277+
273278
projects = paginated_project_response["values"]
274279
issue_types_by_project = {
275280
project["key"]: {issue_type["name"] for issue_type in project["issueTypes"]}

tests/unit/services/test_jira.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,89 @@ def test_create_jira_issue_returns_errors(
358358
assert after.message == f"Failed to create issue for Bug {context.bug.id}"
359359
assert after.levelno == logging.ERROR
360360
assert after.response == fake_error_data
361+
362+
363+
def test_paginated_projects_no_keys(mocked_responses):
364+
url = f"{get_settings().jira_base_url}rest/api/2/project/search"
365+
mocked_response_data = {"some": "data"}
366+
mocked_responses.add(
367+
responses.GET,
368+
url,
369+
status=200,
370+
match=[responses.matchers.query_string_matcher(None)],
371+
json=mocked_response_data,
372+
)
373+
resp = jira.get_service().client.paginated_projects()
374+
assert resp == mocked_response_data
375+
376+
377+
def test_paginated_projects_with_keys(mocked_responses, action_factory):
378+
action_factory()
379+
url = f"{get_settings().jira_base_url}rest/api/2/project/search"
380+
mocked_response_data = {"some": "data"}
381+
mocked_responses.add(
382+
responses.GET,
383+
url,
384+
status=200,
385+
match=[responses.matchers.query_string_matcher("keys=ABC&keys=DEF")],
386+
json=mocked_response_data,
387+
)
388+
resp = jira.get_service().client.paginated_projects(keys=["ABC", "DEF"])
389+
assert resp == mocked_response_data
390+
391+
392+
def test_paginated_projects_greater_than_50_keys(mocked_responses):
393+
keys = [str(i) for i in range(51)]
394+
with pytest.raises(ValueError):
395+
jira.get_service().client.paginated_projects(keys=keys)
396+
397+
398+
@pytest.mark.parametrize(
399+
"project_data, expected_result",
400+
[
401+
(
402+
[
403+
{"key": "ABC", "issueTypes": [{"name": "Task"}, {"name": "Bug"}]},
404+
{"key": "DEF", "issueTypes": [{"name": "Task"}, {"name": "Bug"}]},
405+
],
406+
True,
407+
),
408+
(
409+
[
410+
{"key": "ABC", "issueTypes": [{"name": "Task"}]},
411+
{"key": "DEF", "issueTypes": [{"name": "Task"}, {"name": "Bug"}]},
412+
],
413+
False,
414+
),
415+
(
416+
[
417+
{"key": "ABC", "issueTypes": [{"name": "Task"}, {"name": "Bug"}]},
418+
],
419+
False,
420+
),
421+
],
422+
)
423+
def test_all_project_issue_types_exist(
424+
mocked_responses, action_factory, project_data, expected_result
425+
):
426+
actions = Actions(
427+
root=[
428+
action_factory(whiteboard_tag="abc", parameters__jira_project_key="ABC"),
429+
action_factory(whiteboard_tag="def", parameters__jira_project_key="DEF"),
430+
]
431+
)
432+
433+
url = f"{get_settings().jira_base_url}rest/api/2/project/search"
434+
mocked_responses.add(
435+
responses.GET,
436+
url,
437+
status=200,
438+
match=[
439+
responses.matchers.query_string_matcher(
440+
"keys=ABC&keys=DEF&expand=issueTypes"
441+
)
442+
],
443+
json={"values": project_data},
444+
)
445+
446+
assert jira.get_service()._all_project_issue_types_exist(actions) == expected_result

0 commit comments

Comments
 (0)