-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adds get_user and get_current_user methods. (#7)
Adds a new property to the Client class called 'users', which is an instance of a User class. The User class is responsible for managing requests to the /v1/user and /v1/users endpoints. Additionally, a hook is added to check and parse client errors from Connect.
- Loading branch information
Showing
12 changed files
with
206 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,36 @@ | ||
from requests import Session | ||
from typing import Optional | ||
|
||
from . import hooks | ||
|
||
from .auth import Auth | ||
from .config import ConfigBuilder | ||
from .users import Users | ||
|
||
|
||
class Client: | ||
users: Users | ||
|
||
def __init__( | ||
self, endpoint: Optional[str] = None, api_key: Optional[str] = None | ||
self, | ||
api_key: Optional[str] = None, | ||
endpoint: Optional[str] = None, | ||
) -> None: | ||
builder = ConfigBuilder() | ||
builder.set_api_key(api_key) | ||
builder.set_endpoint(endpoint) | ||
if api_key: | ||
builder.set_api_key(api_key) | ||
if endpoint: | ||
builder.set_endpoint(endpoint) | ||
self._config = builder.build() | ||
|
||
if self._config.api_key is None: | ||
raise ValueError("Invalid value for 'api_key': Must be a non-empty string.") | ||
if self._config.endpoint is None: | ||
raise ValueError( | ||
"Invalid value for 'endpoint': Must be a non-empty string." | ||
) | ||
|
||
self._session = Session() | ||
self._session.hooks["response"].append(hooks.handle_errors) | ||
self._session.auth = Auth(self._config.api_key) | ||
|
||
def get(self, endpoint: str, *args, **kwargs): # pragma: no cover | ||
return self._session.request( | ||
"GET", f"{self._config.endpoint}/{endpoint}", *args, **kwargs | ||
) | ||
self.users = Users(self._config.endpoint, self._session) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class ClientError(Exception): | ||
def __init__( | ||
self, error_code: int, error_message: str, http_status: int, http_message: str | ||
): | ||
self.error_code = error_code | ||
self.error_message = error_message | ||
self.http_status = http_status | ||
self.http_message = http_message | ||
super().__init__( | ||
f"{error_message} (Error Code: {error_code}, HTTP Status: {http_status} {http_message})" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import pytest | ||
|
||
from .errors import ClientError | ||
|
||
|
||
class TestClientError: | ||
def test(self): | ||
error_code = 0 | ||
error_message = "foo" | ||
http_status = 404 | ||
http_message = "Foo Bar" | ||
with pytest.raises( | ||
ClientError, match=r"foo \(Error Code: 0, HTTP Status: 404 Foo Bar\)" | ||
): | ||
raise ClientError( | ||
error_code=error_code, | ||
error_message=error_message, | ||
http_status=http_status, | ||
http_message=http_message, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from http.client import responses | ||
from requests import Response | ||
|
||
from .errors import ClientError | ||
|
||
|
||
def handle_errors(response: Response, *args, **kwargs) -> Response: | ||
if response.status_code >= 400 and response.status_code < 500: | ||
data = response.json() | ||
error_code = data["code"] | ||
message = data["error"] | ||
http_status = response.status_code | ||
http_status_message = responses[http_status] | ||
raise ClientError(error_code, message, http_status, http_status_message) | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import pytest | ||
|
||
from unittest.mock import Mock | ||
|
||
from .hooks import handle_errors | ||
|
||
|
||
class TestHandleErrors: | ||
def test(self): | ||
response = Mock() | ||
response.status_code = 200 | ||
assert handle_errors(response) == response | ||
|
||
def test_client_error(self): | ||
response = Mock() | ||
response.status_code = 400 | ||
response.json = Mock() | ||
response.json.return_value = {"code": 0, "error": "foobar"} | ||
with pytest.raises(Exception): | ||
handle_errors(response) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import os | ||
|
||
from requests import Session, Response | ||
|
||
|
||
class Users: | ||
def __init__(self, endpoint: str, session: Session) -> None: | ||
self._endpoint = endpoint | ||
self._session = session | ||
|
||
def get_user(self, user_id: str) -> Response: | ||
endpoint = os.path.join(self._endpoint, "v1/users", user_id) | ||
return self._session.get(endpoint) | ||
|
||
def get_current_user(self) -> Response: | ||
endpoint = os.path.join(self._endpoint, "v1/user") | ||
return self._session.get(endpoint) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from unittest.mock import Mock | ||
|
||
from .users import Users | ||
|
||
|
||
class TestUsers: | ||
def test_get_user(self): | ||
session = Mock() | ||
session.get = Mock(return_value={}) | ||
users = Users(endpoint="http://foo.bar/", session=session) | ||
response = users.get_user(user_id="foo") | ||
assert response == {} | ||
session.get.assert_called_once_with("http://foo.bar/v1/users/foo") | ||
|
||
def test_get_current_user(self): | ||
session = Mock() | ||
session.get = Mock(return_value={}) | ||
users = Users(endpoint="http://foo.bar/", session=session) | ||
response = users.get_current_user() | ||
assert response == {} | ||
session.get.assert_called_once_with("http://foo.bar/v1/user") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from posit.client import Client | ||
|
||
client = Client() | ||
res = client.users.get_current_user() | ||
print(res.json()) |