Skip to content

Commit

Permalink
Add headers option to Client constructor
Browse files Browse the repository at this point in the history
Co-Authored-By: Ethan Glasser-Camp <ethan@betacantrips.com>
  • Loading branch information
leplatrem and glasserc committed Jan 9, 2020
1 parent ad604ba commit d9fc420
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 48 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.rst
Expand Up @@ -7,7 +7,9 @@ This document describes changes between each past release.
10.7.0 (unreleased)
===================

- Nothing changed yet.
**New features**

- Add ability to specify headers from Client constructor


10.6.1 (2019-11-13)
Expand Down
15 changes: 15 additions & 0 deletions README.rst
Expand Up @@ -88,6 +88,21 @@ is `customized on the server <https://kinto.readthedocs.io/en/stable/configurati
the client must specify the expected type: ``kinto_http.BearerTokenAuth("XYPJTNsFKV2" type="Bearer+OIDC")``


Custom headers
--------------

Custom headers can be specified in the Client constructor, and will be sent in every request:

.. code-block:: python
import kinto_http
client = kinto_http.Client(server_url="http://server/v1", headers={
"Allow-Access": "CDN",
"User-Agent": "blocklist-updater"
})
Getting server information
--------------------------

Expand Down
29 changes: 17 additions & 12 deletions kinto_http/__init__.py
Expand Up @@ -93,14 +93,19 @@ def __init__(
retry=0,
retry_after=None,
timeout=None,
ignore_batch_4xx=False
ignore_batch_4xx=False,
headers=None,
):
self.endpoints = Endpoints()

session_kwargs = dict(
server_url=server_url, auth=auth, session=session,
retry=retry, retry_after=retry_after,
server_url=server_url,
auth=auth,
session=session,
retry=retry,
retry_after=retry_after,
timeout=timeout,
headers=headers,
)
self.session = create_session(**session_kwargs)
self._bucket_name = bucket
Expand Down Expand Up @@ -323,7 +328,7 @@ def patch_bucket(
original=None,
permissions=None,
safe=True,
if_match=None
if_match=None,
):
"""Issue a PATCH request on a bucket.
Expand Down Expand Up @@ -464,7 +469,7 @@ def patch_group(
original=None,
permissions=None,
safe=True,
if_match=None
if_match=None,
):
"""Issue a PATCH request on a bucket.
Expand Down Expand Up @@ -592,7 +597,7 @@ def patch_collection(
original=None,
permissions=None,
safe=True,
if_match=None
if_match=None,
):
"""Issue a PATCH request on a collection.
Expand Down Expand Up @@ -724,7 +729,7 @@ def create_record(
data=None,
permissions=None,
safe=True,
if_not_exists=False
if_not_exists=False,
):

id = id or data.get("id", None)
Expand Down Expand Up @@ -774,7 +779,7 @@ def update_record(
data=None,
permissions=None,
safe=True,
if_match=None
if_match=None,
):
id = id or data.get("id")
if id is None:
Expand Down Expand Up @@ -803,7 +808,7 @@ def patch_record(
original=None,
permissions=None,
safe=True,
if_match=None
if_match=None,
):
"""Issue a PATCH request on a record.
Expand Down Expand Up @@ -872,11 +877,11 @@ def get_history(self, *, bucket=None, **kwargs):
return self._paginated(endpoint, **kwargs)

def purge_history(self, *, bucket=None, safe=True, if_match=None):
endpoint = self.get_endpoint('history', bucket=bucket)
endpoint = self.get_endpoint("history", bucket=bucket)
headers = self._get_cache_headers(safe, if_match=if_match)
logger.info("Purge History of bucket %r" % bucket or self._bucket_name)
resp, _ = self.session.request('delete', endpoint, headers=headers)
return resp['data']
resp, _ = self.session.request("delete", endpoint, headers=headers)
return resp["data"]

def __repr__(self):
if self._collection_name:
Expand Down
15 changes: 6 additions & 9 deletions kinto_http/session.py
Expand Up @@ -49,13 +49,16 @@ class Session(object):
"""Handles all the interactions with the network.
"""

def __init__(self, server_url, auth=None, timeout=False, retry=0, retry_after=None):
def __init__(
self, server_url, auth=None, timeout=False, headers=None, retry=0, retry_after=None
):
self.backoff = None
self.server_url = server_url
self.auth = auth
self.nb_retry = retry
self.retry_after = retry_after
self.timeout = timeout
self.headers = headers or {}

def request(self, method, endpoint, data=None, permissions=None, payload=None, **kwargs):
current_time = time.time()
Expand All @@ -75,9 +78,6 @@ def request(self, method, endpoint, data=None, permissions=None, payload=None, *
if self.auth is not None:
kwargs.setdefault("auth", self.auth)

if kwargs.get("headers") is None:
kwargs["headers"] = dict()

if kwargs.get("params") is not None:
params = dict()
for key, value in kwargs["params"].items():
Expand All @@ -89,13 +89,10 @@ def request(self, method, endpoint, data=None, permissions=None, payload=None, *
params[key] = json.dumps(value)
kwargs["params"] = params

if not isinstance(kwargs["headers"], dict):
raise TypeError("headers must be a dict (got {})".format(kwargs["headers"]))
overridden_headers = kwargs.get("headers") or {}

# Set the default User-Agent if not already defined.
# In the meantime, clone the header dict to avoid changing the
# user header dict when adding information.
kwargs["headers"] = {"User-Agent": USER_AGENT, **kwargs["headers"]}
kwargs["headers"] = {"User-Agent": USER_AGENT, **self.headers, **overridden_headers}

payload = payload or {}
if data is not None:
Expand Down
54 changes: 32 additions & 22 deletions kinto_http/tests/test_client.py
Expand Up @@ -225,6 +225,16 @@ def test_client_uses_passed_bucket_if_specified(self):
client = Client(server_url="https://kinto.notmyidea.org/v1", bucket="buck")
assert client._bucket_name == "buck"

def test_client_can_receive_default_headers(self):
r = mock.MagicMock()
r.status_code = 200
client = Client(server_url="https://kinto.io/v1", headers={"Allow-Access": "CDN"})
with mock.patch("kinto_http.session.requests") as mocked:
mocked.request.return_value = r
client.server_info()

assert "Allow-Access" in mocked.request.call_args_list[0][1]["headers"]

def test_client_clone_with_auth(self):
client_clone = self.client.clone(auth=("reviewer", ""))
assert client_clone.session.auth == ("reviewer", "")
Expand Down Expand Up @@ -1191,34 +1201,34 @@ def setUp(self):

def test_basic_retrivial_of_bucket_history(self):
mock_response(self.session)
self.client.get_history(bucket='mybucket')
url = '/buckets/mybucket/history'
self.session.request.assert_called_with('get', url, headers={}, params={})
self.client.get_history(bucket="mybucket")
url = "/buckets/mybucket/history"
self.session.request.assert_called_with("get", url, headers={}, params={})

def test_filter_sorting_operations_on_bucket_history(self):
mock_response(self.session)
self.client.get_history(bucket='mybucket',
_limit=2,
_sort='-last_modified',
_since='1533762576015')

url = '/buckets/mybucket/history'
self.session.request.assert_called_with('get', url, headers={},
params={'_limit': 2,
'_sort': '-last_modified',
'_since': '1533762576015'}
)
self.client.get_history(
bucket="mybucket", _limit=2, _sort="-last_modified", _since="1533762576015"
)

url = "/buckets/mybucket/history"
self.session.request.assert_called_with(
"get",
url,
headers={},
params={"_limit": 2, "_sort": "-last_modified", "_since": "1533762576015"},
)

def test_filtering_by_resource_name(self):
mock_response(self.session)
self.client.get_history(bucket='mybucket', resource_name='collection')
url = '/buckets/mybucket/history'
self.session.request.assert_called_with('get', url, headers={},
params={'resource_name': 'collection'}
)
self.client.get_history(bucket="mybucket", resource_name="collection")
url = "/buckets/mybucket/history"
self.session.request.assert_called_with(
"get", url, headers={}, params={"resource_name": "collection"}
)

def test_purging_of_history(self):
mock_response(self.session)
self.client.purge_history(bucket='mybucket')
url = '/buckets/mybucket/history'
self.session.request.assert_called_with('delete', url, headers=None)
self.client.purge_history(bucket="mybucket")
url = "/buckets/mybucket/history"
self.session.request.assert_called_with("delete", url, headers=None)
12 changes: 8 additions & 4 deletions kinto_http/tests/test_session.py
Expand Up @@ -40,8 +40,10 @@ def test_timeout_can_be_set_to_none(self):
self.assertEqual(session.auth, None)
session.request("get", "/test")
self.requests_mock.request.assert_called_with(
"get", "https://example.org/test", timeout=None,
headers=self.requests_mock.request.headers
"get",
"https://example.org/test",
timeout=None,
headers=self.requests_mock.request.headers,
)

def test_timeout_can_be_set_to_value(self):
Expand All @@ -51,8 +53,10 @@ def test_timeout_can_be_set_to_value(self):
self.assertEqual(session.auth, None)
session.request("get", "/test")
self.requests_mock.request.assert_called_with(
"get", "https://example.org/test", timeout=4,
headers=self.requests_mock.request.headers
"get",
"https://example.org/test",
timeout=4,
headers=self.requests_mock.request.headers,
)

def test_no_auth_is_used_by_default(self):
Expand Down

0 comments on commit d9fc420

Please sign in to comment.