Skip to content

Commit

Permalink
Add a getstatus() method to gidgethub.abc (#194)
Browse files Browse the repository at this point in the history
This now supports API methods whose responses are expressed in a status
code, such as `/orgs/{org}/members/{username}` to determine if a group
contains a specific user.

Closes #188

Co-authored-by: Mariatta Wijaya <Mariatta@users.noreply.github.com>
  • Loading branch information
offbyone and Mariatta committed May 4, 2023
1 parent 20e8612 commit 901f057
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 10 deletions.
19 changes: 19 additions & 0 deletions docs/abc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,25 @@ experimental APIs without issue.
For ``GET`` calls that can return multiple values and
potentially require pagination, see ``getiter()``.

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

Get a single item's *HTTP status* from GitHub.

*jwt* is the value of the JSON web token, for authenticating as a GitHub
App.

*oauth_token* is the value of the oauth token, for making an authenticated
API call.

Only one of *oauth_token* or *jwt* may be passed. A ``ValueError`` is
raised if both are passed. If neither was passed, it defaults to the
value of the *oauth_token* attribute.

.. note::
This method discards any returned content, and is only for use
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")
:async:
Expand Down
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
=========

5.3.0
-----

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

5.2.1
-----

Expand Down
2 changes: 1 addition & 1 deletion gidgethub/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""An async GitHub API library"""
__version__ = "5.2.1"
__version__ = "5.3.0"

import http
from typing import Any, Optional
Expand Down
35 changes: 28 additions & 7 deletions gidgethub/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
GitHubBroken,
GraphQLAuthorizationFailure,
GraphQLException,
HTTPException,
QueryError,
GraphQLResponseTypeError,
)
Expand Down Expand Up @@ -65,7 +66,7 @@ async def _make_request(
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
content_type: str = JSON_CONTENT_TYPE,
) -> Tuple[bytes, Opt[str]]:
) -> Tuple[bytes, Opt[str], int]:
"""Construct and make an HTTP request."""
if oauth_token is not None and jwt is not None:
raise ValueError("Cannot pass both oauth_token and jwt.")
Expand Down Expand Up @@ -120,7 +121,7 @@ async def _make_request(
etag = response[1].get("etag")
last_modified = response[1].get("last-modified")
self._cache[filled_url] = etag, last_modified, data, more
return data, more
return data, more, response[0]

async def getitem(
self,
Expand All @@ -133,11 +134,31 @@ async def getitem(
) -> Any:
"""Send a GET request for a single item to the specified endpoint."""

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

async def getstatus(
self,
url: str,
url_vars: Optional[variable.VariableValueDict] = {},
*,
accept: str = sansio.accept_format(),
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
) -> int:
"""Send a GET request for a single item to the specifie endpoint and return its status code."""

try:
_, _, status_code = await self._make_request(
"GET", url, url_vars, b"", accept, jwt=jwt, oauth_token=oauth_token
)
except HTTPException as e:
status_code = e.status_code

return status_code

async def getiter(
self,
url: str,
Expand All @@ -149,7 +170,7 @@ async def getiter(
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(
data, more, _ = await self._make_request(
"GET", url, url_vars, b"", accept, jwt=jwt, oauth_token=oauth_token
)

Expand Down Expand Up @@ -180,7 +201,7 @@ async def post(
oauth_token: Opt[str] = None,
content_type: str = JSON_CONTENT_TYPE,
) -> Any:
data, _ = await self._make_request(
data, _, _ = await self._make_request(
"POST",
url,
url_vars,
Expand All @@ -202,7 +223,7 @@ async def patch(
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
) -> Any:
data, _ = await self._make_request(
data, _, _ = await self._make_request(
"PATCH", url, url_vars, data, accept, jwt=jwt, oauth_token=oauth_token
)
return data
Expand All @@ -217,7 +238,7 @@ async def put(
jwt: Opt[str] = None,
oauth_token: Opt[str] = None,
) -> Any:
data, _ = await self._make_request(
data, _, _ = await self._make_request(
"PUT", url, url_vars, data, accept, jwt=jwt, oauth_token=oauth_token
)
return data
Expand Down
49 changes: 47 additions & 2 deletions tests/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ async def test_decoding(self):
gh = MockGitHubAPI(
headers=headers, body=json.dumps(original_data).encode("utf8")
)
data, _ = await gh._make_request(
data, _, _ = await gh._make_request(
"GET", "/rate_limit", {}, "", sansio.accept_format()
)
assert data == original_data
Expand All @@ -175,9 +175,22 @@ async def test_more(self):
headers = MockGitHubAPI.DEFAULT_HEADERS.copy()
headers["link"] = "<https://api.github.com/fake?page=2>; " 'rel="next"'
gh = MockGitHubAPI(headers=headers)
_, more = await gh._make_request("GET", "/fake", {}, "", sansio.accept_format())
_, more, _ = await gh._make_request(
"GET", "/fake", {}, "", sansio.accept_format()
)
assert more == "https://api.github.com/fake?page=2"

@pytest.mark.asyncio
async def test_status_code(self):
"""The status code is returned appropriately."""
headers = MockGitHubAPI.DEFAULT_HEADERS.copy()
headers["link"] = "<https://api.github.com/fake?page=2>; " 'rel="next"'
gh = MockGitHubAPI(headers=headers)
_, _, status_code = await gh._make_request(
"GET", "/fake", {}, "", sansio.accept_format()
)
assert status_code == 200


class TestGitHubAPIGetitem:
@pytest.mark.asyncio
Expand Down Expand Up @@ -232,6 +245,38 @@ async def test_cannot_pass_both_jwt_and_oauth_token(self):
assert str(exc_info.value) == "Cannot pass both oauth_token and jwt."


class TestGitHubAPIGetStatus:
@pytest.mark.asyncio
async def test_getstatus(self):
original_status = 204
headers = MockGitHubAPI.DEFAULT_HEADERS.copy()
headers["content-type"] = "application/json; charset=UTF-8"
gh = MockGitHubAPI(headers=headers, status_code=original_status)
data = await gh.getstatus("/fake")
assert gh.method == "GET"
assert data == original_status

@pytest.mark.asyncio
async def test_getstatus_4xx(self):
original_status = 404
headers = MockGitHubAPI.DEFAULT_HEADERS.copy()
headers["content-type"] = "application/json; charset=UTF-8"
gh = MockGitHubAPI(headers=headers, status_code=original_status)
data = await gh.getstatus("/fake")
assert gh.method == "GET"
assert data == original_status

@pytest.mark.asyncio
async def test_getstatus_5xx(self):
original_status = 504
headers = MockGitHubAPI.DEFAULT_HEADERS.copy()
headers["content-type"] = "application/json; charset=UTF-8"
gh = MockGitHubAPI(headers=headers, status_code=original_status)
data = await gh.getstatus("/fake")
assert gh.method == "GET"
assert data == original_status


class TestGitHubAPIGetiter:
@pytest.mark.asyncio
async def test_getiter(self):
Expand Down

0 comments on commit 901f057

Please sign in to comment.