Skip to content

Commit

Permalink
Add extra_headers option to get methods (#192)
Browse files Browse the repository at this point in the history
Co-authored-by: Mariatta Wijaya <Mariatta@users.noreply.github.com>
  • Loading branch information
basnijholt and Mariatta committed May 26, 2023
1 parent 901f057 commit b9fe3c6
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 15 deletions.
9 changes: 6 additions & 3 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ version: 2
sphinx:
configuration: docs/conf.py

# Optionally set the version of Python and requirements required to build your docs
build:
os: ubuntu-22.04
tools:
python: "3.11"

python:
version: 3.7
install:
- requirements: docs_requirements.txt
- requirements: docs_requirements.txt
16 changes: 9 additions & 7 deletions docs/abc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ experimental APIs without issue.
methods that send data to GitHub, there is a *data* argument which
accepts an object which can be serialized to JSON (because
``None`` is a legitimate JSON value, ``""`` is used to represent
no data).
no data). The *extra_headers* argument optionally is ``dict[str, str]``,
and allows passing extra headers to the request specifying extra
options that the GitHub API allows.

The returned value for GitHub requests is the decoded body of the
response according to :func:`gidgethub.sansio.decipher_response`.
Expand Down Expand Up @@ -119,7 +121,7 @@ experimental APIs without issue.
Renamed from ``_sleep()``.


.. py:method:: getitem(url, url_vars={}, *, accept=sansio.accept_format(), jwt=None, oauth_token=None)
.. py:method:: getitem(url, url_vars={}, *, accept=sansio.accept_format(), jwt=None, oauth_token=None, extra_headers=None)
:async:

Get a single item from GitHub.
Expand Down Expand Up @@ -162,7 +164,7 @@ experimental APIs without issue.
on API endpoints like /orgs/{org}/members/{username} where the
HTTP response code is the relevant answer.

.. py:method:: getiter(url, url_vars={}, *, accept=sansio.accept_format(), jwt=None, oauth_token=None, iterable_key="items")
.. py:method:: getiter(url, url_vars={}, *, accept=sansio.accept_format(), jwt=None, oauth_token=None, iterable_key="items", extra_headers=None)
:async:

Get all items from a GitHub API endpoint.
Expand Down Expand Up @@ -203,7 +205,7 @@ experimental APIs without issue.
:meth:`getitem`.


.. py:method:: post(url, url_vars={}, *, data, accept=sansio.accept_format(), jwt=None, oauth_token=None, content_type="application/json")
.. py:method:: post(url, url_vars={}, *, data, accept=sansio.accept_format(), jwt=None, oauth_token=None, content_type="application/json", extra_headers=None)
:async:

Send a ``POST`` request to GitHub.
Expand Down Expand Up @@ -237,7 +239,7 @@ experimental APIs without issue.
Added *jwt* and *oauth_token*.


.. py:method:: patch(url, url_vars={}, *, data, accept=sansio.accept_format(), jwt=None, oauth_token=None)
.. py:method:: patch(url, url_vars={}, *, data, accept=sansio.accept_format(), jwt=None, oauth_token=None, extra_headers=None)
:async:

Send a ``PATCH`` request to GitHub.
Expand All @@ -257,7 +259,7 @@ experimental APIs without issue.
Added *jwt* and *oauth_token*.


.. py:method:: put(url, url_vars={}, *, data=b"", accept=sansio.accept_format(), jwt=None, oauth_token=None)
.. py:method:: put(url, url_vars={}, *, data=b"", accept=sansio.accept_format(), jwt=None, oauth_token=None, extra_headers=None)
:async:

Send a ``PUT`` request to GitHub.
Expand All @@ -281,7 +283,7 @@ experimental APIs without issue.
Added *jwt* and *oauth_token*.


.. py:method:: delete(url, url_vars={}, *, data=b"", accept=sansio.accept_format(), jwt=None, oauth_token=None)
.. py:method:: delete(url, url_vars={}, *, data=b"", accept=sansio.accept_format(), jwt=None, oauth_token=None, extra_headers=None)
:async:

Send a ``DELETE`` request to GitHub.
Expand Down
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Changelog
5.3.0
-----

- Add extra_headers option to HTTP methods in GitHubAPI
(`Issue #193 <https://github.com/brettcannon/gidgethub/pull/193>_`)

- Add support passing ``extra_headers`` when making requests
(`PR #192 <https://github.com/brettcannon/gidgethub/pull/192>_`)

- Add a getstatus() method for APIs that do not return content.
(`PR #194 <https://github.com/brettcannon/gidgethub/pull/194>_`)

Expand Down
56 changes: 51 additions & 5 deletions gidgethub/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ async def _make_request(
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
content_type: str = JSON_CONTENT_TYPE,
extra_headers: Optional[Dict[str, str]] = None,
) -> Tuple[bytes, Opt[str], int]:
"""Construct and make an HTTP request."""
if oauth_token is not None and jwt is not None:
Expand All @@ -84,6 +85,8 @@ async def _make_request(
request_headers = sansio.create_headers(
self.requester, accept=accept, oauth_token=self.oauth_token
)
if extra_headers is not None:
request_headers.update(extra_headers)
cached = cacheable = False
# Can't use None as a "no body" sentinel as it's a legitimate JSON type.
if data == b"":
Expand Down Expand Up @@ -131,11 +134,19 @@ async def getitem(
accept: str = sansio.accept_format(),
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
extra_headers: Optional[Dict[str, str]] = None,
) -> Any:
"""Send a GET request for a single item to the specified endpoint."""

data, _, _ = await self._make_request(
"GET", url, url_vars, b"", accept, jwt=jwt, oauth_token=oauth_token
"GET",
url,
url_vars,
b"",
accept,
jwt=jwt,
oauth_token=oauth_token,
extra_headers=extra_headers,
)
return data

Expand Down Expand Up @@ -167,11 +178,19 @@ async def getiter(
accept: str = sansio.accept_format(),
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
extra_headers: Optional[Dict[str, str]] = None,
iterable_key: Opt[str] = ITERABLE_KEY,
) -> AsyncGenerator[Any, None]:
"""Return an async iterable for all the items at a specified endpoint."""
data, more, _ = await self._make_request(
"GET", url, url_vars, b"", accept, jwt=jwt, oauth_token=oauth_token
"GET",
url,
url_vars,
b"",
accept,
jwt=jwt,
oauth_token=oauth_token,
extra_headers=extra_headers,
)

if isinstance(data, dict) and iterable_key in data:
Expand All @@ -187,6 +206,7 @@ async def getiter(
jwt=jwt,
oauth_token=oauth_token,
iterable_key=iterable_key,
extra_headers=extra_headers,
):
yield item # pragma: nocover

Expand All @@ -199,6 +219,7 @@ async def post(
accept: str = sansio.accept_format(),
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
extra_headers: Optional[Dict[str, str]] = None,
content_type: str = JSON_CONTENT_TYPE,
) -> Any:
data, _, _ = await self._make_request(
Expand All @@ -210,6 +231,7 @@ async def post(
jwt=jwt,
oauth_token=oauth_token,
content_type=content_type,
extra_headers=extra_headers,
)
return data

Expand All @@ -222,9 +244,17 @@ async def patch(
accept: str = sansio.accept_format(),
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
extra_headers: Optional[Dict[str, str]] = None,
) -> Any:
data, _, _ = await self._make_request(
"PATCH", url, url_vars, data, accept, jwt=jwt, oauth_token=oauth_token
"PATCH",
url,
url_vars,
data,
accept,
jwt=jwt,
oauth_token=oauth_token,
extra_headers=extra_headers,
)
return data

Expand All @@ -237,9 +267,17 @@ async def put(
accept: str = sansio.accept_format(),
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
extra_headers: Optional[Dict[str, str]] = None,
) -> Any:
data, _, _ = await self._make_request(
"PUT", url, url_vars, data, accept, jwt=jwt, oauth_token=oauth_token
"PUT",
url,
url_vars,
data,
accept,
jwt=jwt,
oauth_token=oauth_token,
extra_headers=extra_headers,
)
return data

Expand All @@ -252,9 +290,17 @@ async def delete(
accept: str = sansio.accept_format(),
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
extra_headers: Optional[Dict[str, str]] = None,
) -> None:
await self._make_request(
"DELETE", url, url_vars, data, accept, jwt=jwt, oauth_token=oauth_token
"DELETE",
url,
url_vars,
data,
accept,
jwt=jwt,
oauth_token=oauth_token,
extra_headers=extra_headers,
)

async def graphql(
Expand Down
39 changes: 39 additions & 0 deletions tests/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,31 @@ async def test_getiter(self):
assert data[2] == 1
assert data[3] == 2

@pytest.mark.asyncio
async def test_getiter_with_extra_headers(self):
"""Test that getiter() sends extra headers correctly."""
original_data = [1, 2]
next_url = "https://api.github.com/fake{/extra}?page=2"
headers = MockGitHubAPI.DEFAULT_HEADERS.copy()
headers["content-type"] = "application/json; charset=UTF-8"
headers["link"] = f'<{next_url}>; rel="next"'
gh = MockGitHubAPI(
headers=headers, body=json.dumps(original_data).encode("utf8")
)
extra_headers = {"X-Custom-Header": "custom_value"}
data = []
async for item in gh.getiter(
"/fake", {"extra": "stuff"}, extra_headers=extra_headers
):
data.append(item)
assert gh.method == "GET"
assert gh.headers["X-Custom-Header"] == "custom_value"
assert len(data) == 4
assert data[0] == 1
assert data[1] == 2
assert data[2] == 1
assert data[3] == 2

@pytest.mark.asyncio
async def test_with_passed_jwt(self):
original_data = [1, 2]
Expand Down Expand Up @@ -401,6 +426,20 @@ async def test_checks_api(self):
assert data[2] == 1
assert data[3] == 2

@pytest.mark.asyncio
async def test_extra_headers(self):
"""Test that extra headers are passed correctly."""
accept = sansio.accept_format()
extra_headers = {"X-Custom-Header": "custom_value"}
gh = MockGitHubAPI()
await gh._make_request(
"GET", "/rate_limit", {}, "", accept, extra_headers=extra_headers
)
assert gh.headers["user-agent"] == "test_abc"
assert gh.headers["accept"] == accept
assert "X-Custom-Header" in gh.headers
assert gh.headers["X-Custom-Header"] == "custom_value"


class TestGitHubAPIPost:
@pytest.mark.asyncio
Expand Down

0 comments on commit b9fe3c6

Please sign in to comment.