|
7 | 7 |
|
8 | 8 | from typing_extensions import override |
9 | 9 |
|
| 10 | +from apify_client.clients import ( |
| 11 | + KeyValueStoreClientAsync, |
| 12 | +) |
10 | 13 | from crawlee.storage_clients._base import KeyValueStoreClient |
11 | 14 | from crawlee.storage_clients.models import KeyValueStoreRecord, KeyValueStoreRecordMetadata |
12 | 15 | from crawlee.storages import KeyValueStore |
13 | 16 |
|
14 | 17 | from ._models import ApifyKeyValueStoreMetadata, KeyValueStoreListKeysPage |
15 | | -from ._utils import AliasResolver, create_apify_client |
| 18 | +from ._utils import ApiClientFactory |
16 | 19 |
|
17 | 20 | if TYPE_CHECKING: |
18 | 21 | from collections.abc import AsyncIterator |
19 | 22 |
|
20 | | - from apify_client.clients import KeyValueStoreClientAsync |
| 23 | + from apify_client.clients import ( |
| 24 | + KeyValueStoreCollectionClientAsync, |
| 25 | + ) |
21 | 26 |
|
22 | 27 | from apify import Configuration |
23 | 28 |
|
24 | 29 | logger = getLogger(__name__) |
25 | 30 |
|
26 | 31 |
|
| 32 | +class KvsApiClientFactory(ApiClientFactory[KeyValueStoreClientAsync, ApifyKeyValueStoreMetadata]): |
| 33 | + @property |
| 34 | + def _collection_client(self) -> KeyValueStoreCollectionClientAsync: |
| 35 | + return self._api_client.key_value_stores() |
| 36 | + |
| 37 | + def _get_resource_client(self, id: str) -> KeyValueStoreClientAsync: |
| 38 | + return self._api_client.key_value_store(key_value_store_id=id) |
| 39 | + |
| 40 | + @property |
| 41 | + def _default_id(self) -> str | None: |
| 42 | + return self._configuration.default_key_value_store_id |
| 43 | + |
| 44 | + @property |
| 45 | + def _storage_type(self) -> type[KeyValueStore]: |
| 46 | + return KeyValueStore |
| 47 | + |
| 48 | + @staticmethod |
| 49 | + def _get_metadata(raw_metadata: dict | None) -> ApifyKeyValueStoreMetadata: |
| 50 | + return ApifyKeyValueStoreMetadata.model_validate(raw_metadata) |
| 51 | + |
| 52 | + |
27 | 53 | class ApifyKeyValueStoreClient(KeyValueStoreClient): |
28 | 54 | """An Apify platform implementation of the key-value store client.""" |
29 | 55 |
|
@@ -90,91 +116,14 @@ async def open( |
90 | 116 | `id`, `name`, or `alias` is provided, or if none are provided and no default storage ID is available |
91 | 117 | in the configuration. |
92 | 118 | """ |
93 | | - if sum(1 for param in [id, name, alias] if param is not None) > 1: |
94 | | - raise ValueError('Only one of "id", "name", or "alias" can be specified, not multiple.') |
95 | | - |
96 | | - apify_client_async = create_apify_client(configuration) |
97 | | - apify_kvss_client = apify_client_async.key_value_stores() |
98 | | - |
99 | | - # Normalize unnamed default storage in cases where not defined in `configuration.default_key_value_store_id` to |
100 | | - # unnamed storage aliased as `__default__` |
101 | | - if not any([alias, name, id, configuration.default_key_value_store_id]): |
102 | | - alias = '__default__' |
103 | | - |
104 | | - if alias: |
105 | | - # Check if there is pre-existing alias mapping in the default KVS. |
106 | | - async with AliasResolver(storage_type=KeyValueStore, alias=alias, configuration=configuration) as _alias: |
107 | | - id = await _alias.resolve_id() |
108 | | - |
109 | | - if id: |
110 | | - # There was id, storage has to exist, fetch metadata to confirm it. |
111 | | - apify_kvs_client = apify_client_async.key_value_store(key_value_store_id=id) |
112 | | - raw_metadata = await apify_kvs_client.get() |
113 | | - if raw_metadata: |
114 | | - return cls( |
115 | | - api_client=apify_kvs_client, |
116 | | - api_public_base_url='', |
117 | | - # Remove in version 4.0, https://github.com/apify/apify-sdk-python/issues/635 |
118 | | - lock=asyncio.Lock(), |
119 | | - ) |
120 | | - |
121 | | - # There was no pre-existing alias in the mapping or the id did not point to existing storage. |
122 | | - # Create a new unnamed storage and store the alias mapping. |
123 | | - metadata = ApifyKeyValueStoreMetadata.model_validate( |
124 | | - await apify_kvss_client.get_or_create(), |
125 | | - ) |
126 | | - await _alias.store_mapping(storage_id=metadata.id) |
127 | | - |
128 | | - # Return the client for the newly created storage directly. |
129 | | - # It was just created, no need to refetch it. |
130 | | - return cls( |
131 | | - api_client=apify_client_async.key_value_store(key_value_store_id=metadata.id), |
132 | | - api_public_base_url='', # Remove in version 4.0, https://github.com/apify/apify-sdk-python/issues/635 |
133 | | - lock=asyncio.Lock(), |
134 | | - ) |
135 | | - |
136 | | - # If name is provided, get or create the storage by name. |
137 | | - elif name: |
138 | | - metadata = ApifyKeyValueStoreMetadata.model_validate(await apify_kvss_client.get_or_create(name=name)) |
139 | | - |
140 | | - # Freshly fetched named storage. No need to fetch it again. |
141 | | - return cls( |
142 | | - api_client=apify_client_async.key_value_store(key_value_store_id=metadata.id), |
143 | | - api_public_base_url='', # Remove in version 4.0, https://github.com/apify/apify-sdk-python/issues/635 |
144 | | - lock=asyncio.Lock(), |
145 | | - ) |
146 | | - # If id is provided, then storage has to exists. |
147 | | - elif id: |
148 | | - # Now create the client for the determined ID |
149 | | - apify_kvs_client = apify_client_async.key_value_store(key_value_store_id=id) |
150 | | - # Fetch its metadata. |
151 | | - raw_metadata = await apify_kvs_client.get() |
152 | | - # If metadata is None, it means the storage does not exist. |
153 | | - if raw_metadata is None: |
154 | | - raise ValueError(f'Opening key-value store with id={id} failed.') |
155 | | - return cls( |
156 | | - api_client=apify_kvs_client, |
157 | | - api_public_base_url='', # Remove in version 4.0, https://github.com/apify/apify-sdk-python/issues/635 |
158 | | - lock=asyncio.Lock(), |
159 | | - ) |
160 | | - # Default key-value store ID from configuration |
161 | | - elif configuration.default_key_value_store_id: |
162 | | - # Now create the client for the determined ID |
163 | | - apify_kvs_client = apify_client_async.key_value_store( |
164 | | - key_value_store_id=configuration.default_key_value_store_id |
165 | | - ) |
166 | | - # Fetch its metadata. |
167 | | - raw_metadata = await apify_kvs_client.get() |
168 | | - if not raw_metadata: |
169 | | - metadata = ApifyKeyValueStoreMetadata.model_validate(await apify_kvss_client.get_or_create(name=name)) |
170 | | - apify_kvs_client = apify_client_async.key_value_store(key_value_store_id=metadata.id) |
171 | | - |
172 | | - return cls( |
173 | | - api_client=apify_kvs_client, |
174 | | - api_public_base_url='', # Remove in version 4.0, https://github.com/apify/apify-sdk-python/issues/635 |
175 | | - lock=asyncio.Lock(), |
176 | | - ) |
177 | | - raise RuntimeError('Will never happen') |
| 119 | + api_client, _ =await KvsApiClientFactory( |
| 120 | + configuration=configuration, alias=alias, name=name, id=id |
| 121 | + ).get_client_with_metadata() |
| 122 | + return cls( |
| 123 | + api_client=api_client, |
| 124 | + api_public_base_url='', # Remove in version 4.0, https://github.com/apify/apify-sdk-python/issues/635 |
| 125 | + lock=asyncio.Lock(), |
| 126 | + ) |
178 | 127 |
|
179 | 128 | @override |
180 | 129 | async def purge(self) -> None: |
|
0 commit comments