From cde2012ff64c28212f18a95fd0abedc89aa0ebed Mon Sep 17 00:00:00 2001 From: adehad <26027314+adehad@users.noreply.github.com> Date: Sat, 26 Jun 2021 14:02:51 +0100 Subject: [PATCH 1/4] Allow the adding of headers rather than just overwriting all headers Co-Authored-By: Peter Radcliffe <51170007+pradcliffe-ns@users.noreply.github.com> --- jira/client.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jira/client.py b/jira/client.py index 6f281f2ac..400387084 100644 --- a/jira/client.py +++ b/jira/client.py @@ -353,6 +353,7 @@ def __init__( * verify -- Verify SSL certs. Defaults to ``True``. * client_cert -- a tuple of (cert,key) for the requests library for client side SSL * check_update -- Check whether using the newest python-jira library version. + * headers -- a dict to update the default headers the session uses for all API requests. basic_auth (Union[None, Tuple[str, str]]): A tuple of username and password to use when establishing a session via HTTP BASIC authentication. @@ -420,7 +421,14 @@ def __init__( self._options: Dict[str, Any] = copy.copy(JIRA.DEFAULT_OPTIONS) + if "headers" in options: + headers = copy.copy(options["headers"]) + del options["headers"] + else: + headers = {} + self._options.update(options) + self._options["headers"].update(headers) self._rank = None From 3195c83015a0c5c243d892a71606d8ef91863703 Mon Sep 17 00:00:00 2001 From: adehad <26027314+adehad@users.noreply.github.com> Date: Sat, 26 Jun 2021 17:00:43 +0100 Subject: [PATCH 2/4] add tests for unclobbered headers update asda --- tests/test_client.py | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index a717d190b..b84c89659 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -55,6 +55,16 @@ def remove_by_slug(): return slug +@pytest.fixture() +def no_fields(monkeypatch): + """When we want to test the __init__ method of the jira.client.JIRA + we don't need any external calls to get the fields. + + We don't need the features of a MagicMock, hence we don't use it here. + """ + monkeypatch.setattr(jira.client.JIRA, "fields", lambda *args, **kwargs: []) + + def test_delete_project(cl_admin, cl_normal, slug): assert cl_admin.delete_project(slug) @@ -119,3 +129,53 @@ def test_result_list_if_empty(): with pytest.raises(StopIteration): next(results) + + +@pytest.mark.parametrize( + "options_arg", + [ + {"headers": {"Content-Type": "application/json;charset=UTF-8"}}, + {"headers": {"random-header": "nice random"}}, + {}, + ], + ids=["overwrite", "new", "none"], +) +def test_headers_unclobbered_update(options_arg, no_fields): + + # GIVEN: the headers and the expected value + header_to_check: str + expected_header_value: str + if options_arg: + header_to_check = list(options_arg["headers"].keys())[0] + expected_header_value = options_arg["headers"][header_to_check] + elif "headers" not in options_arg: + # when we are checking not proving any headers + header_to_check = "Content-Type" + expected_header_value = jira.client.JIRA.DEFAULT_OPTIONS["headers"][ + header_to_check + ] + + invariant_header_name: str = "X-Atlassian-Token" + invariant_header_value: str = jira.client.JIRA.DEFAULT_OPTIONS["headers"][ + invariant_header_name + ] + if options_arg: + # We arbitrarily chose a header to check it remains unchanged/unclobbered + # so should not be overwritten by a test case + assert ( + invariant_header_name not in options_arg["headers"] + ), f"{invariant_header_name} is checked as not being overwritten in this test" + + # WHEN: we initialise the JIRA class and get the headers + jira_client = jira.client.JIRA( + server="https://jira.atlasian.com", + get_server_info=False, + validate=False, + options=options_arg, + ) + + session_headers = jira_client._session.headers + + # THEN: we have set the right headers and not affect the defaults + assert session_headers[header_to_check] == expected_header_value + assert session_headers[invariant_header_name] == invariant_header_value From b68b3794bee3526f6e3f03ea78ac9700d8d3eec3 Mon Sep 17 00:00:00 2001 From: adehad <26027314+adehad@users.noreply.github.com> Date: Fri, 23 Jul 2021 19:30:20 +0100 Subject: [PATCH 3/4] update tests to be less dependent on input test case --- tests/test_client.py | 56 ++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index b84c89659..ee37ab6fd 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -136,35 +136,27 @@ def test_result_list_if_empty(): [ {"headers": {"Content-Type": "application/json;charset=UTF-8"}}, {"headers": {"random-header": "nice random"}}, - {}, ], - ids=["overwrite", "new", "none"], + ids=["overwrite", "new"], ) def test_headers_unclobbered_update(options_arg, no_fields): + assert "headers" in options_arg, "test case options must contain headers" + # GIVEN: the headers and the expected value - header_to_check: str - expected_header_value: str - if options_arg: - header_to_check = list(options_arg["headers"].keys())[0] - expected_header_value = options_arg["headers"][header_to_check] - elif "headers" not in options_arg: - # when we are checking not proving any headers - header_to_check = "Content-Type" - expected_header_value = jira.client.JIRA.DEFAULT_OPTIONS["headers"][ - header_to_check - ] + header_to_check: str = list(options_arg["headers"].keys())[0] + expected_header_value: str = options_arg["headers"][header_to_check] invariant_header_name: str = "X-Atlassian-Token" invariant_header_value: str = jira.client.JIRA.DEFAULT_OPTIONS["headers"][ invariant_header_name ] - if options_arg: - # We arbitrarily chose a header to check it remains unchanged/unclobbered - # so should not be overwritten by a test case - assert ( - invariant_header_name not in options_arg["headers"] - ), f"{invariant_header_name} is checked as not being overwritten in this test" + + # We arbitrarily chose a header to check it remains unchanged/unclobbered + # so should not be overwritten by a test case + assert ( + invariant_header_name not in options_arg["headers"] + ), f"{invariant_header_name} is checked as not being overwritten in this test" # WHEN: we initialise the JIRA class and get the headers jira_client = jira.client.JIRA( @@ -176,6 +168,30 @@ def test_headers_unclobbered_update(options_arg, no_fields): session_headers = jira_client._session.headers - # THEN: we have set the right headers and not affect the defaults + # THEN: we have set the right headers and not affect the other headers' defaults assert session_headers[header_to_check] == expected_header_value assert session_headers[invariant_header_name] == invariant_header_value + + +def test_headers_unclobbered_update_with_no_provided_headers(no_fields): + + options_arg = {} # a dict with "headers" not set + + # GIVEN:the headers and the expected value + invariant_header_name: str = "X-Atlassian-Token" + invariant_header_value: str = jira.client.JIRA.DEFAULT_OPTIONS["headers"][ + invariant_header_name + ] + + # WHEN: we initialise the JIRA class with no provided headers and get the headers + jira_client = jira.client.JIRA( + server="https://jira.atlasian.com", + get_server_info=False, + validate=False, + options=options_arg, + ) + + session_headers = jira_client._session.headers + + # THEN: we have not affected the other headers' defaults + assert session_headers[invariant_header_name] == invariant_header_value From 2ed15c57f5e02dc1b18e551b25ddb567519e6f9d Mon Sep 17 00:00:00 2001 From: adehad <26027314+adehad@users.noreply.github.com> Date: Fri, 23 Jul 2021 19:58:50 +0100 Subject: [PATCH 4/4] add to examples how this may be used --- docs/examples.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/examples.rst b/docs/examples.rst index d49350dd2..37014e4d6 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -125,6 +125,21 @@ To pass additional options to Kerberos auth use dict ``kerberos_options``, e.g.: .. _jirashell-label: +Headers +------- + +Headers can be provided to the internally used ``requests.Session``. +If the user provides a header that the :py:class:`jira.client.JIRA` also attempts to set, the user provided header will take preference. + +Perhaps you want to use a custom User Agent:: + + from requests_toolbelt import user_agent + + jira = JIRA( + basic_auth=("email", "API token"), + options={"headers": {"User-Agent": user_agent("my_package", "0.0.1")}}, + ) + Issues ------