From 8fc057118e984344a12914cbcf80ae9974637c64 Mon Sep 17 00:00:00 2001 From: Nishad Manerikar Date: Thu, 2 Apr 2026 10:54:42 +0200 Subject: [PATCH 1/2] Add the new ownership parameter to the storage listing methods --- .../_resource_clients/dataset_collection.py | 16 ++- .../key_value_store_collection.py | 16 ++- .../request_queue_collection.py | 20 +++- tests/unit/test_storage_collection_listing.py | 112 ++++++++++++++++++ 4 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 tests/unit/test_storage_collection_listing.py diff --git a/src/apify_client/_resource_clients/dataset_collection.py b/src/apify_client/_resource_clients/dataset_collection.py index 038b1d15..e8deeb3d 100644 --- a/src/apify_client/_resource_clients/dataset_collection.py +++ b/src/apify_client/_resource_clients/dataset_collection.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal from apify_client._docs import docs_group from apify_client._models import Dataset, DatasetResponse, ListOfDatasets, ListOfDatasetsResponse @@ -36,6 +36,7 @@ def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, + ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, timeout: Timeout = 'medium', ) -> ListOfDatasets: """List the available datasets. @@ -47,12 +48,16 @@ def list( limit: How many datasets to retrieve. offset: What dataset to include as first when retrieving the list. desc: Whether to sort the datasets in descending order based on their modification date. + ownership: Filter by ownership. 'ownedByMe' returns only user's own datasets, + 'sharedWithMe' returns only datasets shared with the user. timeout: Timeout for the API HTTP request. Returns: The list of available datasets matching the specified filters. """ - result = self._list(timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc) + result = self._list( + timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc, ownership=ownership + ) return ListOfDatasetsResponse.model_validate(result).data def get_or_create( @@ -104,6 +109,7 @@ async def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, + ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, timeout: Timeout = 'medium', ) -> ListOfDatasets: """List the available datasets. @@ -115,12 +121,16 @@ async def list( limit: How many datasets to retrieve. offset: What dataset to include as first when retrieving the list. desc: Whether to sort the datasets in descending order based on their modification date. + ownership: Filter by ownership. 'ownedByMe' returns only user's own datasets, + 'sharedWithMe' returns only datasets shared with the user. timeout: Timeout for the API HTTP request. Returns: The list of available datasets matching the specified filters. """ - result = await self._list(timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc) + result = await self._list( + timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc, ownership=ownership + ) return ListOfDatasetsResponse.model_validate(result).data async def get_or_create( diff --git a/src/apify_client/_resource_clients/key_value_store_collection.py b/src/apify_client/_resource_clients/key_value_store_collection.py index 76609cc0..dc08b294 100644 --- a/src/apify_client/_resource_clients/key_value_store_collection.py +++ b/src/apify_client/_resource_clients/key_value_store_collection.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal from apify_client._docs import docs_group from apify_client._models import ( @@ -41,6 +41,7 @@ def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, + ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, timeout: Timeout = 'medium', ) -> ListOfKeyValueStores: """List the available key-value stores. @@ -52,12 +53,16 @@ def list( limit: How many key-value stores to retrieve. offset: What key-value store to include as first when retrieving the list. desc: Whether to sort the key-value stores in descending order based on their modification date. + ownership: Filter by ownership. 'ownedByMe' returns only user's own key-value stores, + 'sharedWithMe' returns only key-value stores shared with the user. timeout: Timeout for the API HTTP request. Returns: The list of available key-value stores matching the specified filters. """ - result = self._list(timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc) + result = self._list( + timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc, ownership=ownership + ) return ListOfKeyValueStoresResponse.model_validate(result).data def get_or_create( @@ -109,6 +114,7 @@ async def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, + ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, timeout: Timeout = 'medium', ) -> ListOfKeyValueStores: """List the available key-value stores. @@ -120,12 +126,16 @@ async def list( limit: How many key-value stores to retrieve. offset: What key-value store to include as first when retrieving the list. desc: Whether to sort the key-value stores in descending order based on their modification date. + ownership: Filter by ownership. 'ownedByMe' returns only user's own key-value stores, + 'sharedWithMe' returns only key-value stores shared with the user. timeout: Timeout for the API HTTP request. Returns: The list of available key-value stores matching the specified filters. """ - result = await self._list(timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc) + result = await self._list( + timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc, ownership=ownership + ) return ListOfKeyValueStoresResponse.model_validate(result).data async def get_or_create( diff --git a/src/apify_client/_resource_clients/request_queue_collection.py b/src/apify_client/_resource_clients/request_queue_collection.py index b1fdf36b..7bf8ad11 100644 --- a/src/apify_client/_resource_clients/request_queue_collection.py +++ b/src/apify_client/_resource_clients/request_queue_collection.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal from apify_client._docs import docs_group from apify_client._models import ( @@ -41,6 +41,7 @@ def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, + ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, timeout: Timeout = 'medium', ) -> ListOfRequestQueues: """List the available request queues. @@ -51,13 +52,17 @@ def list( unnamed: Whether to include unnamed request queues in the list. limit: How many request queues to retrieve. offset: What request queue to include as first when retrieving the list. - desc: Whether to sort therequest queues in descending order based on their modification date. + desc: Whether to sort the request queues in descending order based on their modification date. + ownership: Filter by ownership. 'ownedByMe' returns only user's own request queues, + 'sharedWithMe' returns only request queues shared with the user. timeout: Timeout for the API HTTP request. Returns: The list of available request queues matching the specified filters. """ - result = self._list(timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc) + result = self._list( + timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc, ownership=ownership + ) return ListOfRequestQueuesResponse.model_validate(result).data def get_or_create( @@ -107,6 +112,7 @@ async def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, + ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, timeout: Timeout = 'medium', ) -> ListOfRequestQueues: """List the available request queues. @@ -117,13 +123,17 @@ async def list( unnamed: Whether to include unnamed request queues in the list. limit: How many request queues to retrieve. offset: What request queue to include as first when retrieving the list. - desc: Whether to sort therequest queues in descending order based on their modification date. + desc: Whether to sort the request queues in descending order based on their modification date. + ownership: Filter by ownership. 'ownedByMe' returns only user's own request queues, + 'sharedWithMe' returns only request queues shared with the user. timeout: Timeout for the API HTTP request. Returns: The list of available request queues matching the specified filters. """ - result = await self._list(timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc) + result = await self._list( + timeout=timeout, unnamed=unnamed, limit=limit, offset=offset, desc=desc, ownership=ownership + ) return ListOfRequestQueuesResponse.model_validate(result).data async def get_or_create( diff --git a/tests/unit/test_storage_collection_listing.py b/tests/unit/test_storage_collection_listing.py new file mode 100644 index 00000000..feaf483f --- /dev/null +++ b/tests/unit/test_storage_collection_listing.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +import json +from typing import TYPE_CHECKING + +import pytest +from werkzeug.wrappers import Response + +from apify_client import ApifyClient, ApifyClientAsync + +if TYPE_CHECKING: + from collections.abc import Callable + + from pytest_httpserver import HTTPServer + from werkzeug.wrappers import Request + +_MOCK_LIST_RESPONSE = json.dumps( + { + 'data': { + 'items': [], + 'count': 0, + 'offset': 0, + 'limit': 100, + 'total': 0, + 'desc': False, + } + } +) + + +def _make_handler(captured: dict) -> Callable[[Request], Response]: + def handler(request: Request) -> Response: + captured['args'] = dict(request.args) + return Response(_MOCK_LIST_RESPONSE, content_type='application/json') + + return handler + + +@pytest.fixture +def client_urls(httpserver: HTTPServer) -> dict: + server_url = httpserver.url_for('/').removesuffix('/') + return {'api_url': server_url, 'api_public_url': server_url} + + +def test_dataset_collection_list_ownership_sync(httpserver: HTTPServer, client_urls: dict) -> None: + captured: dict = {} + httpserver.expect_oneshot_request('/v2/datasets', method='GET').respond_with_handler(_make_handler(captured)) + + client = ApifyClient(token='placeholder_token', **client_urls) + result = client.datasets().list(ownership='ownedByMe') + + assert result.total == 0 + assert captured['args']['ownership'] == 'ownedByMe' + + +async def test_dataset_collection_list_ownership_async(httpserver: HTTPServer, client_urls: dict) -> None: + captured: dict = {} + httpserver.expect_oneshot_request('/v2/datasets', method='GET').respond_with_handler(_make_handler(captured)) + + client = ApifyClientAsync(token='placeholder_token', **client_urls) + result = await client.datasets().list(ownership='sharedWithMe') + + assert result.total == 0 + assert captured['args']['ownership'] == 'sharedWithMe' + + +def test_key_value_store_collection_list_ownership_sync(httpserver: HTTPServer, client_urls: dict) -> None: + captured: dict = {} + httpserver.expect_oneshot_request('/v2/key-value-stores', method='GET').respond_with_handler( + _make_handler(captured) + ) + + client = ApifyClient(token='placeholder_token', **client_urls) + result = client.key_value_stores().list(ownership='ownedByMe') + + assert result.total == 0 + assert captured['args']['ownership'] == 'ownedByMe' + + +async def test_key_value_store_collection_list_ownership_async(httpserver: HTTPServer, client_urls: dict) -> None: + captured: dict = {} + httpserver.expect_oneshot_request('/v2/key-value-stores', method='GET').respond_with_handler( + _make_handler(captured) + ) + + client = ApifyClientAsync(token='placeholder_token', **client_urls) + result = await client.key_value_stores().list(ownership='sharedWithMe') + + assert result.total == 0 + assert captured['args']['ownership'] == 'sharedWithMe' + + +def test_request_queue_collection_list_ownership_sync(httpserver: HTTPServer, client_urls: dict) -> None: + captured: dict = {} + httpserver.expect_oneshot_request('/v2/request-queues', method='GET').respond_with_handler(_make_handler(captured)) + + client = ApifyClient(token='placeholder_token', **client_urls) + result = client.request_queues().list(ownership='ownedByMe') + + assert result.total == 0 + assert captured['args']['ownership'] == 'ownedByMe' + + +async def test_request_queue_collection_list_ownership_async(httpserver: HTTPServer, client_urls: dict) -> None: + captured: dict = {} + httpserver.expect_oneshot_request('/v2/request-queues', method='GET').respond_with_handler(_make_handler(captured)) + + client = ApifyClientAsync(token='placeholder_token', **client_urls) + result = await client.request_queues().list(ownership='sharedWithMe') + + assert result.total == 0 + assert captured['args']['ownership'] == 'sharedWithMe' From 3328587d5af57bd49a4793593174f33bd019ab25 Mon Sep 17 00:00:00 2001 From: Nishad Manerikar Date: Thu, 2 Apr 2026 14:33:08 +0200 Subject: [PATCH 2/2] Create a type for StorageOwnership --- src/apify_client/_resource_clients/dataset_collection.py | 8 ++++---- .../_resource_clients/key_value_store_collection.py | 8 ++++---- .../_resource_clients/request_queue_collection.py | 8 ++++---- src/apify_client/_types.py | 3 +++ 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/apify_client/_resource_clients/dataset_collection.py b/src/apify_client/_resource_clients/dataset_collection.py index e8deeb3d..31c9c687 100644 --- a/src/apify_client/_resource_clients/dataset_collection.py +++ b/src/apify_client/_resource_clients/dataset_collection.py @@ -1,13 +1,13 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any from apify_client._docs import docs_group from apify_client._models import Dataset, DatasetResponse, ListOfDatasets, ListOfDatasetsResponse from apify_client._resource_clients._resource_client import ResourceClient, ResourceClientAsync if TYPE_CHECKING: - from apify_client._types import Timeout + from apify_client._types import StorageOwnership, Timeout @docs_group('Resource clients') @@ -36,7 +36,7 @@ def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, + ownership: StorageOwnership | None = None, timeout: Timeout = 'medium', ) -> ListOfDatasets: """List the available datasets. @@ -109,7 +109,7 @@ async def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, + ownership: StorageOwnership | None = None, timeout: Timeout = 'medium', ) -> ListOfDatasets: """List the available datasets. diff --git a/src/apify_client/_resource_clients/key_value_store_collection.py b/src/apify_client/_resource_clients/key_value_store_collection.py index dc08b294..0f0e7e81 100644 --- a/src/apify_client/_resource_clients/key_value_store_collection.py +++ b/src/apify_client/_resource_clients/key_value_store_collection.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any from apify_client._docs import docs_group from apify_client._models import ( @@ -12,7 +12,7 @@ from apify_client._resource_clients._resource_client import ResourceClient, ResourceClientAsync if TYPE_CHECKING: - from apify_client._types import Timeout + from apify_client._types import StorageOwnership, Timeout @docs_group('Resource clients') @@ -41,7 +41,7 @@ def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, + ownership: StorageOwnership | None = None, timeout: Timeout = 'medium', ) -> ListOfKeyValueStores: """List the available key-value stores. @@ -114,7 +114,7 @@ async def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, + ownership: StorageOwnership | None = None, timeout: Timeout = 'medium', ) -> ListOfKeyValueStores: """List the available key-value stores. diff --git a/src/apify_client/_resource_clients/request_queue_collection.py b/src/apify_client/_resource_clients/request_queue_collection.py index 7bf8ad11..c0ab28d0 100644 --- a/src/apify_client/_resource_clients/request_queue_collection.py +++ b/src/apify_client/_resource_clients/request_queue_collection.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any from apify_client._docs import docs_group from apify_client._models import ( @@ -12,7 +12,7 @@ from apify_client._resource_clients._resource_client import ResourceClient, ResourceClientAsync if TYPE_CHECKING: - from apify_client._types import Timeout + from apify_client._types import StorageOwnership, Timeout @docs_group('Resource clients') @@ -41,7 +41,7 @@ def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, + ownership: StorageOwnership | None = None, timeout: Timeout = 'medium', ) -> ListOfRequestQueues: """List the available request queues. @@ -112,7 +112,7 @@ async def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - ownership: Literal['ownedByMe', 'sharedWithMe'] | None = None, + ownership: StorageOwnership | None = None, timeout: Timeout = 'medium', ) -> ListOfRequestQueues: """List the available request queues. diff --git a/src/apify_client/_types.py b/src/apify_client/_types.py index d3af93d0..18cd8595 100644 --- a/src/apify_client/_types.py +++ b/src/apify_client/_types.py @@ -9,6 +9,9 @@ from apify_client._models import ActorJobStatus, WebhookCreate # noqa: TC001 +StorageOwnership = Literal['ownedByMe', 'sharedWithMe'] +"""Filter for storage listing methods to return only storages owned by the user or shared with the user.""" + Timeout = timedelta | Literal['no_timeout', 'short', 'medium', 'long'] """Type for the `timeout` parameter on resource client methods.