Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the adding of headers rather than just overwriting all headers #1085

Merged
merged 4 commits into from
Aug 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
------

Expand Down
8 changes: 8 additions & 0 deletions jira/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down
76 changes: 76 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -119,3 +129,69 @@ 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"],
)
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 = 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
]

# 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 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