Skip to content

Commit

Permalink
fix: correct url path checking and path append
Browse files Browse the repository at this point in the history
  • Loading branch information
tdstein committed Feb 21, 2024
1 parent 1861324 commit 2dee631
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 198 deletions.
37 changes: 29 additions & 8 deletions src/posit/connect/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
from requests import Response, Session
from typing import Optional

from . import hooks
from . import hooks, urls

from .auth import Auth
from .config import Config
from .users import Users, CachedUsers
from .resources import CachedResources
from .users import Users, User


class Client:
Expand All @@ -33,7 +34,7 @@ def __init__(
session.hooks["response"].append(hooks.handle_errors)

# Initialize the Users instance.
self.users: CachedUsers = Users(config=self.config, session=session)
self.users: CachedResources[User] = Users(config=self.config, session=session)
# Store the Session object.
self.session = session

Expand Down Expand Up @@ -62,6 +63,21 @@ def __exit__(self, exc_type, exc_value, exc_tb):
if hasattr(self, "session") and self.session is not None:
self.session.close()

def request(self, method: str, path: str, **kwargs) -> Response:
"""
Sends an HTTP request to the specified path using the given method.
Args:
method (str): The HTTP method to use for the request.
path (str): The path to send the request to.
**kwargs: Additional keyword arguments to pass to the underlying session's request method.
Returns:
Response: The response object containing the server's response to the request.
"""
url = urls.append_path(self.config.url, path)
return self.session.request(method, url, **kwargs)

def get(self, path: str, **kwargs) -> Response:
"""
Send a GET request to the specified path.
Expand All @@ -74,7 +90,8 @@ def get(self, path: str, **kwargs) -> Response:
Response: The response object.
"""
return self.session.get(self.config.url.append(path), **kwargs)
url = urls.append_path(self.config.url, path)
return self.session.get(url, **kwargs)

def post(self, path: str, **kwargs) -> Response:
"""
Expand All @@ -88,7 +105,8 @@ def post(self, path: str, **kwargs) -> Response:
Response: The response object.
"""
return self.session.post(self.config.url.append(path), **kwargs)
url = urls.append_path(self.config.url, path)
return self.session.post(url, **kwargs)

def put(self, path: str, **kwargs) -> Response:
"""
Expand All @@ -102,7 +120,8 @@ def put(self, path: str, **kwargs) -> Response:
Response: The response object.
"""
return self.session.put(self.config.url.append(path), **kwargs)
url = urls.append_path(self.config.url, path)
return self.session.put(url, **kwargs)

def patch(self, path: str, **kwargs) -> Response:
"""
Expand All @@ -116,7 +135,8 @@ def patch(self, path: str, **kwargs) -> Response:
Response: The response object.
"""
return self.session.patch(self.config.url.append(path), **kwargs)
url = urls.append_path(self.config.url, path)
return self.session.patch(url, **kwargs)

def delete(self, path: str, **kwargs) -> Response:
"""
Expand All @@ -130,4 +150,5 @@ def delete(self, path: str, **kwargs) -> Response:
Response: The response object.
"""
return self.session.delete(self.config.url.append(path), **kwargs)
url = urls.append_path(self.config.url, path)
return self.session.delete(url, **kwargs)
46 changes: 24 additions & 22 deletions src/posit/connect/client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ def MockAuth():
yield mock


@pytest.fixture
def MockClient():
with patch("posit.connect.client.Client") as mock:
yield mock


@pytest.fixture
def MockConfig():
with patch("posit.connect.client.Config") as mock:
Expand Down Expand Up @@ -43,7 +37,7 @@ def test_init(
MockUsers: MagicMock,
):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
Client(api_key=api_key, url=url)
MockAuth.assert_called_once_with(config=MockConfig.return_value)
MockConfig.assert_called_once_with(api_key=api_key, url=url)
Expand All @@ -52,59 +46,67 @@ def test_init(
config=MockConfig.return_value, session=MockSession.return_value
)

def test__del__(self, MockSession):
def test__del__(self, MockAuth, MockConfig, MockSession, MockUsers):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
client = Client(api_key=api_key, url=url)
del client
MockSession.return_value.close.assert_called_once()

def test__enter__(self):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
with Client(api_key=api_key, url=url) as client:
assert isinstance(client, Client)

def test__exit__(self, MockSession):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
with Client(api_key=api_key, url=url) as client:
assert isinstance(client, Client)
MockSession.return_value.close.assert_called_once()

def test_request(self, MockSession):
api_key = "foobar"
url = "http://foo.bar/__api__"
client = Client(api_key=api_key, url=url)
client.request("GET", "/foo")
MockSession.return_value.request.assert_called_once_with(
"GET", "http://foo.bar/__api__/foo"
)

def test_get(self, MockSession):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
client = Client(api_key=api_key, url=url)
client.get("/foo")
client.session.get.assert_called_once_with("http://foo.bar/foo")
client.session.get.assert_called_once_with("http://foo.bar/__api__/foo")

def test_post(self, MockSession):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
client = Client(api_key=api_key, url=url)
client.post("/foo")
client.session.post.assert_called_once_with("http://foo.bar/foo")
client.session.post.assert_called_once_with("http://foo.bar/__api__/foo")

def test_put(self, MockSession):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
client = Client(api_key=api_key, url=url)
client.put("/foo")
client.session.put.assert_called_once_with("http://foo.bar/foo")
client.session.put.assert_called_once_with("http://foo.bar/__api__/foo")

def test_patch(self, MockSession):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
client = Client(api_key=api_key, url=url)
client.patch("/foo")
client.session.patch.assert_called_once_with("http://foo.bar/foo")
client.session.patch.assert_called_once_with("http://foo.bar/__api__/foo")

def test_delete(self, MockSession):
api_key = "foobar"
url = "http://foo.bar"
url = "http://foo.bar/__api__"
client = Client(api_key=api_key, url=url)
client.delete("/foo")
client.session.delete.assert_called_once_with("http://foo.bar/foo")
4 changes: 1 addition & 3 deletions src/posit/connect/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import os
from typing import Optional

from .urls import Url


def _get_api_key() -> str:
"""Gets the API key from the environment variable 'CONNECT_API_KEY'.
Expand Down Expand Up @@ -47,4 +45,4 @@ def __init__(
self, api_key: Optional[str] = None, url: Optional[str] = None
) -> None:
self.api_key = api_key or _get_api_key()
self.url = Url(url or _get_url())
self.url = url or _get_url()
67 changes: 33 additions & 34 deletions src/posit/connect/config_test.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
import pytest

from unittest.mock import MagicMock, patch
from unittest.mock import patch

from .config import Config, _get_api_key, _get_url


class TestGetApiKey:
@patch.dict("os.environ", {"CONNECT_API_KEY": "foobar"})
def test_get_api_key(self):
api_key = _get_api_key()
assert api_key == "foobar"
@patch.dict("os.environ", {"CONNECT_API_KEY": "foobar"})
def test_get_api_key():
api_key = _get_api_key()
assert api_key == "foobar"

@patch.dict("os.environ", {"CONNECT_API_KEY": ""})
def test_get_api_key_empty(self):
with pytest.raises(ValueError):
_get_api_key()

def test_get_api_key_miss(self):
with pytest.raises(ValueError):
_get_api_key()
@patch.dict("os.environ", {"CONNECT_API_KEY": ""})
def test_get_api_key_empty():
with pytest.raises(ValueError):
_get_api_key()


class TestGetUrl:
@patch.dict("os.environ", {"CONNECT_SERVER": "http://foo.bar"})
def test_get_endpoint(self):
url = _get_url()
assert url == "http://foo.bar"
def test_get_api_key_miss():
with pytest.raises(ValueError):
_get_api_key()

@patch.dict("os.environ", {"CONNECT_SERVER": ""})
def test_get_endpoint_empty(self):
with pytest.raises(ValueError):
_get_url()

def test_get_endpoint_miss(self):
with pytest.raises(ValueError):
_get_url()
@patch.dict("os.environ", {"CONNECT_SERVER": "http://foo.bar"})
def test_get_url():
url = _get_url()
assert url == "http://foo.bar"


class TestConfig:
@patch("posit.connect.config.Url")
def test_init(self, Url: MagicMock):
api_key = "foobar"
url = "http://foo.bar"
config = Config(api_key=api_key, url=url)
assert config.api_key == api_key
assert config.url == Url.return_value
Url.assert_called_with(url)
@patch.dict("os.environ", {"CONNECT_SERVER": ""})
def test_get_url_empty():
with pytest.raises(ValueError):
_get_url()


def test_get_url_miss():
with pytest.raises(ValueError):
_get_url()


def test_init():
api_key = "foobar"
url = "http://foo.bar"
config = Config(api_key=api_key, url=url)
assert config.api_key == api_key
assert config.url == url
36 changes: 0 additions & 36 deletions src/posit/connect/endpoints.py

This file was deleted.

16 changes: 0 additions & 16 deletions src/posit/connect/endpoints_test.py

This file was deleted.

4 changes: 2 additions & 2 deletions src/posit/connect/resources.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations


from abc import ABC, abstractmethod
from typing import Generic, Iterator, Optional, TypeVar, List, TypedDict, Tuple

Expand Down Expand Up @@ -51,8 +50,9 @@ def to_pandas(self):


class Resources(CachedResources[T]):
def __init__(self, data: List[T] = []) -> None:
def __init__(self, url: str, data: List[T] = []) -> None:
super().__init__(data)
self.url = url
self.data = data
self.exhausted = False
self.index = 0
Expand Down
11 changes: 7 additions & 4 deletions src/posit/connect/resources_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,24 @@ def get(self, _: str) -> FakeResource:
return Mock(spec=FakeResource)


class TestResources:
def test(self):
class TestCachedResources:
def test_init(self):
resources = FakeCachedResources()
assert resources == resources.find()
assert resources.find_one()
assert resources.get(None)


class FakeResources(FakeCachedResources, Resources):
def __init__(self) -> None:
super().__init__("")

def fetch(self, index) -> Tuple[Optional[Iterator[FakeResource]], bool]:
return iter([FakeResource()]), len(self.data) > 0


class TestFakeLazyResources:
def test(self):
class TestFakeResources:
def test_init(self):
resources = FakeResources()
assert resources == resources.find()
assert resources.find_one()
Expand Down
Loading

0 comments on commit 2dee631

Please sign in to comment.