diff --git a/mpt_api_client/mpt_client.py b/mpt_api_client/mpt_client.py index 0a569f05..ac10c60b 100644 --- a/mpt_api_client/mpt_client.py +++ b/mpt_api_client/mpt_client.py @@ -13,6 +13,7 @@ AsyncIntegration, AsyncNotifications, AsyncProgram, + AsyncSpotlight, Audit, Billing, Catalog, @@ -22,6 +23,7 @@ Integration, Notifications, Program, + Spotlight, ) @@ -104,6 +106,11 @@ def program(self) -> AsyncProgram: """Program MPT API Client.""" return AsyncProgram(http_client=self.http_client) + @property + def spotlight(self) -> AsyncSpotlight: + """Spotlight MPT API Client.""" + return AsyncSpotlight(http_client=self.http_client) + class MPTClient: """MPT API Client.""" @@ -188,3 +195,8 @@ def integration(self) -> Integration: def program(self) -> Program: """Program MPT API Client.""" return Program(http_client=self.http_client) + + @property + def spotlight(self) -> Spotlight: + """Spotlight MPT API Client.""" + return Spotlight(http_client=self.http_client) diff --git a/mpt_api_client/resources/__init__.py b/mpt_api_client/resources/__init__.py index dd686aa6..e4e9114b 100644 --- a/mpt_api_client/resources/__init__.py +++ b/mpt_api_client/resources/__init__.py @@ -8,6 +8,7 @@ from mpt_api_client.resources.integration import AsyncIntegration, Integration from mpt_api_client.resources.notifications import AsyncNotifications, Notifications from mpt_api_client.resources.program import AsyncProgram, Program +from mpt_api_client.resources.spotlight import AsyncSpotlight, Spotlight __all__ = [ # noqa: WPS410 "Accounts", @@ -21,6 +22,7 @@ "AsyncIntegration", "AsyncNotifications", "AsyncProgram", + "AsyncSpotlight", "Audit", "Billing", "Catalog", @@ -30,4 +32,5 @@ "Integration", "Notifications", "Program", + "Spotlight", ] diff --git a/mpt_api_client/resources/spotlight/__init__.py b/mpt_api_client/resources/spotlight/__init__.py new file mode 100644 index 00000000..c2f4f36f --- /dev/null +++ b/mpt_api_client/resources/spotlight/__init__.py @@ -0,0 +1,6 @@ +from mpt_api_client.resources.spotlight.spotlight import AsyncSpotlight, Spotlight + +__all__ = [ # noqa: WPS410 + "AsyncSpotlight", + "Spotlight", +] diff --git a/mpt_api_client/resources/spotlight/objects.py b/mpt_api_client/resources/spotlight/objects.py new file mode 100644 index 00000000..20e78ab4 --- /dev/null +++ b/mpt_api_client/resources/spotlight/objects.py @@ -0,0 +1,71 @@ +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.http.mixins import ( + AsyncCollectionMixin, + AsyncGetMixin, + CollectionMixin, + GetMixin, +) +from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel + + +class SpotlightObject(Model): + """Spotlight Object resource. + + Attributes: + id: Unique identifier for the spotlight object. + total: Total number of spotlight objects. + top: Top spotlight objects. + query: Spotlight queries. + """ + + id: str = "" + total: int | None + top: BaseModel | None + query: BaseModel | None + + +class SpotlightObjectsServiceConfig: + """Configuration for Spotlight Objects Service. + + Attributes: + endpoint: API endpoint for spotlight objects. + """ + + _endpoint: str = "/public/v1/spotlight/objects" + _model_class = SpotlightObject + _collection_key = "data" + + +class SpotlightObjectsService( + GetMixin[SpotlightObject], + CollectionMixin[SpotlightObject], + Service[SpotlightObject], + SpotlightObjectsServiceConfig, +): + """Service for managing spotlight objects.""" + + def refresh(self, object_id: str = "-") -> None: + """Refresh a spotlight object. + + Args: + object_id: The ID of the spotlight object to refresh. + """ + self._resource(object_id).do_request("POST", "refresh") + + +class AsyncSpotlightObjectsService( + AsyncGetMixin[SpotlightObject], + AsyncCollectionMixin[SpotlightObject], + AsyncService[SpotlightObject], + SpotlightObjectsServiceConfig, +): + """Asynchronous service for managing spotlight objects.""" + + async def refresh(self, object_id: str = "-") -> None: + """Refresh a spotlight object. + + Args: + object_id: The ID of the spotlight object to refresh. + """ + await self._resource(object_id).do_request("POST", "refresh") diff --git a/mpt_api_client/resources/spotlight/spotlight.py b/mpt_api_client/resources/spotlight/spotlight.py new file mode 100644 index 00000000..9d36aa84 --- /dev/null +++ b/mpt_api_client/resources/spotlight/spotlight.py @@ -0,0 +1,29 @@ +from mpt_api_client.http import AsyncHTTPClient, HTTPClient +from mpt_api_client.resources.spotlight.objects import ( + AsyncSpotlightObjectsService, + SpotlightObjectsService, +) + + +class Spotlight: + """Spotlight MPT API Module.""" + + def __init__(self, http_client: HTTPClient): + self.http_client = http_client + + @property + def objects(self) -> SpotlightObjectsService: # noqa: WPS110 + """Spotlight Objects service.""" + return SpotlightObjectsService(http_client=self.http_client) + + +class AsyncSpotlight: + """Spotlight MPT API Module.""" + + def __init__(self, http_client: AsyncHTTPClient): + self.http_client = http_client + + @property + def objects(self) -> AsyncSpotlightObjectsService: # noqa: WPS110 + """Spotlight Objects service.""" + return AsyncSpotlightObjectsService(http_client=self.http_client) diff --git a/tests/e2e/spotlight/test_async_spotlight.py b/tests/e2e/spotlight/test_async_spotlight.py new file mode 100644 index 00000000..ef524160 --- /dev/null +++ b/tests/e2e/spotlight/test_async_spotlight.py @@ -0,0 +1,35 @@ +import pytest + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +def spotlight_objects_service(async_mpt_vendor): + return async_mpt_vendor.spotlight.objects + + +@pytest.fixture +async def spotlight_objects_data(spotlight_objects_service): + select = ["-total", "-top", "-query"] + spotlight_objects = spotlight_objects_service.select(*select) + return [spotlight_object async for spotlight_object in spotlight_objects.iterate()] + + +def test_select_spotlight_objects(spotlight_objects_data): + result = spotlight_objects_data + + assert len(result) > 0 + + +async def test_refresh_spotlight_objects_all(spotlight_objects_service): + await spotlight_objects_service.refresh(object_id="-") + + +async def test_refresh_spotlight_objects_specific( + spotlight_objects_data, spotlight_objects_service +): + result = spotlight_objects_data[0] if spotlight_objects_data else None + + if result is not None: + await spotlight_objects_service.refresh(object_id=result.id) + assert result is not None diff --git a/tests/e2e/spotlight/test_sync_spotlight.py b/tests/e2e/spotlight/test_sync_spotlight.py new file mode 100644 index 00000000..819eed77 --- /dev/null +++ b/tests/e2e/spotlight/test_sync_spotlight.py @@ -0,0 +1,33 @@ +import pytest + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +def spotlight_objects_service(mpt_vendor): + return mpt_vendor.spotlight.objects + + +@pytest.fixture +def spotlight_objects_data(spotlight_objects_service): + select = ["-total", "-top", "-query"] + spotlight_objects = spotlight_objects_service.select(*select) + return list(spotlight_objects.iterate()) + + +def test_select_spotlight_objects(spotlight_objects_data): + result = spotlight_objects_data + + assert len(result) > 0 + + +def test_refresh_spotlight_objects_all(spotlight_objects_service): + spotlight_objects_service.refresh(object_id="-") # act + + +def test_refresh_spotlight_objects_specific(spotlight_objects_data, spotlight_objects_service): + result = spotlight_objects_data[0] if spotlight_objects_data else None + + if result is not None: + spotlight_objects_service.refresh(object_id=result.id) + assert result is not None diff --git a/tests/unit/resources/spotlight/test_objects.py b/tests/unit/resources/spotlight/test_objects.py new file mode 100644 index 00000000..d70f8faf --- /dev/null +++ b/tests/unit/resources/spotlight/test_objects.py @@ -0,0 +1,93 @@ +import pytest +import respx + +from mpt_api_client.resources.spotlight.objects import ( + AsyncSpotlightObjectsService, + SpotlightObjectsService, +) + + +@pytest.fixture +def spotlight_object_service(http_client): + return SpotlightObjectsService(http_client=http_client) + + +@pytest.fixture +def async_spotlight_object_service(async_http_client): + return AsyncSpotlightObjectsService(http_client=async_http_client) + + +@pytest.fixture +def spotlight_object_data(): + return { + "id": "SPO-123", + "total": 10, + "top": {"id": "BJO-123", "name": "Top Object"}, + "query": {"id": "SPQ-123", "name": "Spotlight Query"}, + } + + +@pytest.mark.parametrize( + "method", + [ + "get", + "refresh", + "iterate", + ], +) +def test_mixins_present(spotlight_object_service, method): + result = hasattr(spotlight_object_service, method) + + assert result is True + + +@pytest.mark.parametrize( + "method", + [ + "get", + "refresh", + "iterate", + ], +) +def test_mixins_present_async(async_spotlight_object_service, method): + result = hasattr(async_spotlight_object_service, method) + + assert result is True + + +@pytest.mark.parametrize( + ("object_id", "expected_url"), + [ + ("SPO-123", "/public/v1/spotlight/objects/SPO-123/refresh"), + ("-", "/public/v1/spotlight/objects/-/refresh"), + ], +) +def test_refresh_method(spotlight_object_service, object_id, expected_url): + with respx.mock(base_url=spotlight_object_service.http_client.httpx_client.base_url) as mock: + mock.post(expected_url).respond(status_code=204) + spotlight_object_service.refresh(object_id=object_id) + + result = mock.calls.last.request + + assert result.method == "POST" + assert result.url.path == expected_url + + +@pytest.mark.parametrize( + ("object_id", "expected_url"), + [ + ("SPO-123", "/public/v1/spotlight/objects/SPO-123/refresh"), + ("-", "/public/v1/spotlight/objects/-/refresh"), + ], +) +async def test_refresh_method_async(async_spotlight_object_service, object_id, expected_url): + with respx.mock( + base_url=async_spotlight_object_service.http_client.httpx_client.base_url + ) as mock: + mock.post(expected_url).respond(status_code=204) + await async_spotlight_object_service.refresh(object_id=object_id) + + result = mock.calls.last.request + + assert result.method == "POST" + assert result.url.path == expected_url diff --git a/tests/unit/resources/spotlight/test_spotlight.py b/tests/unit/resources/spotlight/test_spotlight.py new file mode 100644 index 00000000..bd8f3301 --- /dev/null +++ b/tests/unit/resources/spotlight/test_spotlight.py @@ -0,0 +1,57 @@ +import pytest + +from mpt_api_client.resources.spotlight.objects import ( + AsyncSpotlightObjectsService, + SpotlightObjectsService, +) +from mpt_api_client.resources.spotlight.spotlight import AsyncSpotlight, Spotlight + + +@pytest.fixture +def spotlight(http_client): + return Spotlight(http_client=http_client) + + +@pytest.fixture +def async_spotlight(async_http_client): + return AsyncSpotlight(http_client=async_http_client) + + +@pytest.mark.parametrize( + ("property_name", "expected_service_class"), + [ + ("objects", SpotlightObjectsService), + ], +) +def test_spotlight_properties(spotlight, property_name, expected_service_class): + result = getattr(spotlight, property_name) + + assert isinstance(result, expected_service_class) + assert result.http_client is spotlight.http_client + + +@pytest.mark.parametrize( + ("property_name", "expected_service_class"), + [ + ("objects", AsyncSpotlightObjectsService), + ], +) +def test_async_spotlight_properties(async_spotlight, property_name, expected_service_class): + result = getattr(async_spotlight, property_name) + + assert isinstance(result, expected_service_class) + assert result.http_client is async_spotlight.http_client + + +def test_spotlight_initialization(http_client): + result = Spotlight(http_client=http_client) + + assert result.http_client is http_client + assert isinstance(result, Spotlight) + + +def test_async_spotlight_initialization(async_http_client): + result = AsyncSpotlight(http_client=async_http_client) + + assert result.http_client is async_http_client + assert isinstance(result, AsyncSpotlight) diff --git a/tests/unit/test_mpt_client.py b/tests/unit/test_mpt_client.py index 161f437e..26812144 100644 --- a/tests/unit/test_mpt_client.py +++ b/tests/unit/test_mpt_client.py @@ -14,6 +14,7 @@ AsyncIntegration, AsyncNotifications, AsyncProgram, + AsyncSpotlight, Audit, Billing, Catalog, @@ -23,6 +24,7 @@ Integration, Notifications, Program, + Spotlight, ) from tests.unit.conftest import API_TOKEN, API_URL @@ -44,6 +46,7 @@ def get_mpt_client(): ("exchange", Exchange), ("integration", Integration), ("program", Program), + ("spotlight", Spotlight), ], ) def test_mpt_client(resource_name: str, expected_type: type) -> None: @@ -78,6 +81,7 @@ def test_mpt_client_env(monkeypatch: pytest.MonkeyPatch) -> None: ("exchange", AsyncExchange), ("integration", AsyncIntegration), ("program", AsyncProgram), + ("spotlight", AsyncSpotlight), ], ) def test_async_mpt_client(resource_name: str, expected_type: type) -> None: