Skip to content

Commit fe78ddd

Browse files
Allow the adding of headers rather than just overwriting all headers (#1085)
* Allow the adding of headers rather than just overwriting all headers Co-Authored-By: Peter Radcliffe <[email protected]> * add tests for unclobbered headers update asda * update tests to be less dependent on input test case * add to examples how this may be used Co-authored-by: Peter Radcliffe <[email protected]>
1 parent a8bae1f commit fe78ddd

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

docs/examples.rst

+15
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ To pass additional options to Kerberos auth use dict ``kerberos_options``, e.g.:
125125

126126
.. _jirashell-label:
127127

128+
Headers
129+
-------
130+
131+
Headers can be provided to the internally used ``requests.Session``.
132+
If the user provides a header that the :py:class:`jira.client.JIRA` also attempts to set, the user provided header will take preference.
133+
134+
Perhaps you want to use a custom User Agent::
135+
136+
from requests_toolbelt import user_agent
137+
138+
jira = JIRA(
139+
basic_auth=("email", "API token"),
140+
options={"headers": {"User-Agent": user_agent("my_package", "0.0.1")}},
141+
)
142+
128143
Issues
129144
------
130145

jira/client.py

+8
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ def __init__(
354354
* verify -- Verify SSL certs. Defaults to ``True``.
355355
* client_cert -- a tuple of (cert,key) for the requests library for client side SSL
356356
* check_update -- Check whether using the newest python-jira library version.
357+
* headers -- a dict to update the default headers the session uses for all API requests.
357358
358359
basic_auth (Union[None, Tuple[str, str]]): A tuple of username and password to use when
359360
establishing a session via HTTP BASIC authentication.
@@ -421,7 +422,14 @@ def __init__(
421422

422423
self._options: Dict[str, Any] = copy.copy(JIRA.DEFAULT_OPTIONS)
423424

425+
if "headers" in options:
426+
headers = copy.copy(options["headers"])
427+
del options["headers"]
428+
else:
429+
headers = {}
430+
424431
self._options.update(options)
432+
self._options["headers"].update(headers)
425433

426434
self._rank = None
427435

tests/test_client.py

+76
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ def remove_by_slug():
5555
return slug
5656

5757

58+
@pytest.fixture()
59+
def no_fields(monkeypatch):
60+
"""When we want to test the __init__ method of the jira.client.JIRA
61+
we don't need any external calls to get the fields.
62+
63+
We don't need the features of a MagicMock, hence we don't use it here.
64+
"""
65+
monkeypatch.setattr(jira.client.JIRA, "fields", lambda *args, **kwargs: [])
66+
67+
5868
def test_delete_project(cl_admin, cl_normal, slug):
5969

6070
assert cl_admin.delete_project(slug)
@@ -119,3 +129,69 @@ def test_result_list_if_empty():
119129

120130
with pytest.raises(StopIteration):
121131
next(results)
132+
133+
134+
@pytest.mark.parametrize(
135+
"options_arg",
136+
[
137+
{"headers": {"Content-Type": "application/json;charset=UTF-8"}},
138+
{"headers": {"random-header": "nice random"}},
139+
],
140+
ids=["overwrite", "new"],
141+
)
142+
def test_headers_unclobbered_update(options_arg, no_fields):
143+
144+
assert "headers" in options_arg, "test case options must contain headers"
145+
146+
# GIVEN: the headers and the expected value
147+
header_to_check: str = list(options_arg["headers"].keys())[0]
148+
expected_header_value: str = options_arg["headers"][header_to_check]
149+
150+
invariant_header_name: str = "X-Atlassian-Token"
151+
invariant_header_value: str = jira.client.JIRA.DEFAULT_OPTIONS["headers"][
152+
invariant_header_name
153+
]
154+
155+
# We arbitrarily chose a header to check it remains unchanged/unclobbered
156+
# so should not be overwritten by a test case
157+
assert (
158+
invariant_header_name not in options_arg["headers"]
159+
), f"{invariant_header_name} is checked as not being overwritten in this test"
160+
161+
# WHEN: we initialise the JIRA class and get the headers
162+
jira_client = jira.client.JIRA(
163+
server="https://jira.atlasian.com",
164+
get_server_info=False,
165+
validate=False,
166+
options=options_arg,
167+
)
168+
169+
session_headers = jira_client._session.headers
170+
171+
# THEN: we have set the right headers and not affect the other headers' defaults
172+
assert session_headers[header_to_check] == expected_header_value
173+
assert session_headers[invariant_header_name] == invariant_header_value
174+
175+
176+
def test_headers_unclobbered_update_with_no_provided_headers(no_fields):
177+
178+
options_arg = {} # a dict with "headers" not set
179+
180+
# GIVEN:the headers and the expected value
181+
invariant_header_name: str = "X-Atlassian-Token"
182+
invariant_header_value: str = jira.client.JIRA.DEFAULT_OPTIONS["headers"][
183+
invariant_header_name
184+
]
185+
186+
# WHEN: we initialise the JIRA class with no provided headers and get the headers
187+
jira_client = jira.client.JIRA(
188+
server="https://jira.atlasian.com",
189+
get_server_info=False,
190+
validate=False,
191+
options=options_arg,
192+
)
193+
194+
session_headers = jira_client._session.headers
195+
196+
# THEN: we have not affected the other headers' defaults
197+
assert session_headers[invariant_header_name] == invariant_header_value

0 commit comments

Comments
 (0)