From 9dfc965607b36d7b23934e30197b5aa9db8cb8cc Mon Sep 17 00:00:00 2001 From: Taylor Steinberg Date: Fri, 16 Feb 2024 13:27:35 -0500 Subject: [PATCH] fix: adds page size validation (#26) --- src/posit/connect/client.py | 4 ++-- src/posit/connect/client_test.py | 6 +++--- src/posit/connect/resources.py | 6 +++--- src/posit/connect/resources_test.py | 12 ++++++------ src/posit/connect/users.py | 16 +++++++++++----- src/posit/connect/users_test.py | 23 +++++++++++++++++++++++ 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/posit/connect/client.py b/src/posit/connect/client.py index 59ac246c..07007cad 100644 --- a/src/posit/connect/client.py +++ b/src/posit/connect/client.py @@ -8,7 +8,7 @@ from .auth import Auth from .config import Config -from .users import LazyUsers, Users +from .users import Users, CachedUsers @contextmanager @@ -54,7 +54,7 @@ def __init__( session.hooks["response"].append(hooks.handle_errors) # Initialize the Users instance. - self.users: Users = LazyUsers(config=config, session=session) + self.users: CachedUsers = Users(config=config, session=session) # Store the Session object. self._session = session diff --git a/src/posit/connect/client_test.py b/src/posit/connect/client_test.py index 490ab898..fe5f3906 100644 --- a/src/posit/connect/client_test.py +++ b/src/posit/connect/client_test.py @@ -13,7 +13,7 @@ def test(self, Client: MagicMock): class TestClient: - @patch("posit.connect.client.LazyUsers") + @patch("posit.connect.client.Users") @patch("posit.connect.client.Session") @patch("posit.connect.client.Config") @patch("posit.connect.client.Auth") @@ -22,7 +22,7 @@ def test_init( Auth: MagicMock, Config: MagicMock, Session: MagicMock, - LazyUsers: MagicMock, + Users: MagicMock, ): api_key = "foobar" endpoint = "http://foo.bar" @@ -31,7 +31,7 @@ def test_init( Auth.assert_called_once_with(config=config) Config.assert_called_once_with(api_key=api_key, endpoint=endpoint) Session.assert_called_once() - LazyUsers.assert_called_once_with(config=config, session=Session.return_value) + Users.assert_called_once_with(config=config, session=Session.return_value) @patch("posit.connect.client.Session") @patch("posit.connect.client.Auth") diff --git a/src/posit/connect/resources.py b/src/posit/connect/resources.py index 9427b95a..d14be0e0 100644 --- a/src/posit/connect/resources.py +++ b/src/posit/connect/resources.py @@ -12,13 +12,13 @@ class Resource(TypedDict): T = TypeVar("T", bound=Resource) -class Resources(ABC, Generic[T], Iterator[T]): +class CachedResources(ABC, Generic[T], Iterator[T]): def __init__(self, data: List[T] = []) -> None: super().__init__() self.data = data @abstractmethod - def find(self, *args, **kwargs) -> Resources[T]: + def find(self, *args, **kwargs) -> CachedResources[T]: raise NotImplementedError() @abstractmethod @@ -50,7 +50,7 @@ def to_pandas(self): return None -class LazyResources(Resources[T]): +class Resources(CachedResources[T]): def __init__(self, data: List[T] = []) -> None: super().__init__(data) self.data = data diff --git a/src/posit/connect/resources_test.py b/src/posit/connect/resources_test.py index 2720690a..78acacf0 100644 --- a/src/posit/connect/resources_test.py +++ b/src/posit/connect/resources_test.py @@ -1,15 +1,15 @@ from typing import Iterator, Tuple, Optional from unittest.mock import Mock -from .resources import Resource, Resources, LazyResources +from .resources import Resource, CachedResources, Resources class FakeResource(Resource): pass -class FakeResources(Resources[FakeResource]): - def find(self) -> Resources[FakeResource]: +class FakeCachedResources(CachedResources[FakeResource]): + def find(self) -> CachedResources[FakeResource]: return self def find_one(self) -> Optional[FakeResource]: @@ -21,20 +21,20 @@ def get(self, _: str) -> FakeResource: class TestResources: def test(self): - resources = FakeResources() + resources = FakeCachedResources() assert resources == resources.find() assert resources.find_one() assert resources.get(None) -class FakeLazyResources(FakeResources, LazyResources): +class FakeResources(FakeCachedResources, Resources): def fetch(self, index) -> Tuple[Optional[Iterator[FakeResource]], bool]: return iter([FakeResource()]), len(self.data) > 0 class TestFakeLazyResources: def test(self): - resources = FakeLazyResources() + resources = FakeResources() assert resources == resources.find() assert resources.find_one() assert resources.get(None) diff --git a/src/posit/connect/users.py b/src/posit/connect/users.py index 248debeb..ae100646 100644 --- a/src/posit/connect/users.py +++ b/src/posit/connect/users.py @@ -8,8 +8,9 @@ from requests import Session from .config import Config -from .resources import LazyResources, Resource, Resources +from .resources import Resources, Resource, CachedResources +# The maximum page size supported by the API. _MAX_PAGE_SIZE = 500 @@ -27,9 +28,9 @@ class User(Resource, total=False): locked: bool -class Users(Resources[User]): - def find(self, filter: Callable[[User], bool] = lambda _: True) -> Users: - return Users([user for user in self if filter(user)]) +class CachedUsers(CachedResources[User]): + def find(self, filter: Callable[[User], bool] = lambda _: True) -> CachedUsers: + return CachedUsers([user for user in self if filter(user)]) def find_one(self, filter: Callable[[User], bool] = lambda _: True) -> User | None: return next((user for user in self if filter(user)), None) @@ -41,10 +42,15 @@ def get(self, id: str) -> User: return user -class LazyUsers(Users, LazyResources[User]): +class Users(CachedUsers, Resources[User]): def __init__( self, config: Config, session: Session, *, page_size=_MAX_PAGE_SIZE ) -> None: + if page_size > _MAX_PAGE_SIZE: + raise ValueError( + f"page_size must be less than or equal to {_MAX_PAGE_SIZE}" + ) + super().__init__() self.config = config self.session = session diff --git a/src/posit/connect/users_test.py b/src/posit/connect/users_test.py index e69de29b..e2304214 100644 --- a/src/posit/connect/users_test.py +++ b/src/posit/connect/users_test.py @@ -0,0 +1,23 @@ +import pytest + +from unittest.mock import patch + +from .users import Users + + +@pytest.fixture +def mock_config(): + with patch("posit.connect.users.Config") as mock: + yield mock.return_value + + +@pytest.fixture +def mock_session(): + with patch("posit.connect.users.Session") as mock: + yield mock.return_value + + +class TestUsers: + def test_init(self, mock_config, mock_session): + with pytest.raises(ValueError): + Users(mock_config, mock_session, page_size=9999)