Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/firestore_v1/async_client.py: 44%
68 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 06:27 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 06:27 +0000
1# Copyright 2020 Google LLC All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15"""Client for interacting with the Google Cloud Firestore API.
17This is the base from which all interactions with the API occur.
19In the hierarchy of API concepts
21* a :class:`~google.cloud.firestore_v1.client.Client` owns a
22 :class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`
23* a :class:`~google.cloud.firestore_v1.client.Client` owns a
24 :class:`~google.cloud.firestore_v1.async_document.AsyncDocumentReference`
25"""
27from google.api_core import gapic_v1
28from google.api_core import retry as retries
30from google.cloud.firestore_v1.base_client import (
31 BaseClient,
32 _CLIENT_INFO,
33 _parse_batch_get, # type: ignore
34 _path_helper,
35)
37from google.cloud.firestore_v1.async_query import AsyncCollectionGroup
38from google.cloud.firestore_v1.async_batch import AsyncWriteBatch
39from google.cloud.firestore_v1.async_collection import AsyncCollectionReference
40from google.cloud.firestore_v1.async_document import (
41 AsyncDocumentReference,
42 DocumentSnapshot,
43)
44from google.cloud.firestore_v1.async_transaction import AsyncTransaction
45from google.cloud.firestore_v1.field_path import FieldPath
46from google.cloud.firestore_v1.services.firestore import (
47 async_client as firestore_client,
48)
49from google.cloud.firestore_v1.services.firestore.transports import (
50 grpc_asyncio as firestore_grpc_transport,
51)
52from typing import Any, AsyncGenerator, Iterable, List, Optional, Union, TYPE_CHECKING
54if TYPE_CHECKING:
55 from google.cloud.firestore_v1.bulk_writer import BulkWriter # pragma: NO COVER
58class AsyncClient(BaseClient):
59 """Client for interacting with Google Cloud Firestore API.
61 .. note::
63 Since the Cloud Firestore API requires the gRPC transport, no
64 ``_http`` argument is accepted by this class.
66 Args:
67 project (Optional[str]): The project which the client acts on behalf
68 of. If not passed, falls back to the default inferred
69 from the environment.
70 credentials (Optional[~google.auth.credentials.Credentials]): The
71 OAuth2 Credentials to use for this client. If not passed, falls
72 back to the default inferred from the environment.
73 database (Optional[str]): The database name that the client targets.
74 For now, :attr:`DEFAULT_DATABASE` (the default value) is the
75 only valid database.
76 client_info (Optional[google.api_core.gapic_v1.client_info.ClientInfo]):
77 The client info used to send a user-agent string along with API
78 requests. If ``None``, then default info will be used. Generally,
79 you only need to set this if you're developing your own library
80 or partner tool.
81 client_options (Union[dict, google.api_core.client_options.ClientOptions]):
82 Client options used to set user options on the client. API Endpoint
83 should be set through client_options.
84 """
86 def __init__(
87 self,
88 project=None,
89 credentials=None,
90 database=None,
91 client_info=_CLIENT_INFO,
92 client_options=None,
93 ) -> None:
94 super(AsyncClient, self).__init__(
95 project=project,
96 credentials=credentials,
97 database=database,
98 client_info=client_info,
99 client_options=client_options,
100 )
102 def _to_sync_copy(self):
103 from google.cloud.firestore_v1.client import Client
105 if not getattr(self, "_sync_copy", None):
106 self._sync_copy = Client(
107 project=self.project,
108 credentials=self._credentials,
109 database=self._database,
110 client_info=self._client_info,
111 client_options=self._client_options,
112 )
113 return self._sync_copy
115 @property
116 def _firestore_api(self):
117 """Lazy-loading getter GAPIC Firestore API.
118 Returns:
119 :class:`~google.cloud.gapic.firestore.v1`.async_firestore_client.FirestoreAsyncClient:
120 The GAPIC client with the credentials of the current client.
121 """
122 return self._firestore_api_helper(
123 firestore_grpc_transport.FirestoreGrpcAsyncIOTransport,
124 firestore_client.FirestoreAsyncClient,
125 firestore_client,
126 )
128 @property
129 def _target(self):
130 """Return the target (where the API is).
131 Eg. "firestore.googleapis.com"
133 Returns:
134 str: The location of the API.
135 """
136 return self._target_helper(firestore_client.FirestoreAsyncClient)
138 def collection(self, *collection_path: str) -> AsyncCollectionReference:
139 """Get a reference to a collection.
141 For a top-level collection:
143 .. code-block:: python
145 >>> client.collection('top')
147 For a sub-collection:
149 .. code-block:: python
151 >>> client.collection('mydocs/doc/subcol')
152 >>> # is the same as
153 >>> client.collection('mydocs', 'doc', 'subcol')
155 Sub-collections can be nested deeper in a similar fashion.
157 Args:
158 collection_path: Can either be
160 * A single ``/``-delimited path to a collection
161 * A tuple of collection path segments
163 Returns:
164 :class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`:
165 A reference to a collection in the Firestore database.
166 """
167 return AsyncCollectionReference(*_path_helper(collection_path), client=self)
169 def collection_group(self, collection_id: str) -> AsyncCollectionGroup:
170 """
171 Creates and returns a new AsyncQuery that includes all documents in the
172 database that are contained in a collection or subcollection with the
173 given collection_id.
175 .. code-block:: python
177 >>> query = client.collection_group('mygroup')
179 Args:
180 collection_id (str) Identifies the collections to query over.
182 Every collection or subcollection with this ID as the last segment of its
183 path will be included. Cannot contain a slash.
185 Returns:
186 :class:`~google.cloud.firestore_v1.async_query.AsyncCollectionGroup`:
187 The created AsyncQuery.
188 """
189 return AsyncCollectionGroup(self._get_collection_reference(collection_id))
191 def document(self, *document_path: str) -> AsyncDocumentReference:
192 """Get a reference to a document in a collection.
194 For a top-level document:
196 .. code-block:: python
198 >>> client.document('collek/shun')
199 >>> # is the same as
200 >>> client.document('collek', 'shun')
202 For a document in a sub-collection:
204 .. code-block:: python
206 >>> client.document('mydocs/doc/subcol/child')
207 >>> # is the same as
208 >>> client.document('mydocs', 'doc', 'subcol', 'child')
210 Documents in sub-collections can be nested deeper in a similar fashion.
212 Args:
213 document_path: Can either be
215 * A single ``/``-delimited path to a document
216 * A tuple of document path segments
218 Returns:
219 :class:`~google.cloud.firestore_v1.document.AsyncDocumentReference`:
220 A reference to a document in a collection.
221 """
222 return AsyncDocumentReference(
223 *self._document_path_helper(*document_path), client=self
224 )
226 async def get_all(
227 self,
228 references: List[AsyncDocumentReference],
229 field_paths: Iterable[str] = None,
230 transaction=None,
231 retry: retries.Retry = gapic_v1.method.DEFAULT,
232 timeout: float = None,
233 ) -> AsyncGenerator[DocumentSnapshot, Any]:
234 """Retrieve a batch of documents.
236 .. note::
238 Documents returned by this method are not guaranteed to be
239 returned in the same order that they are given in ``references``.
241 .. note::
243 If multiple ``references`` refer to the same document, the server
244 will only return one result.
246 See :meth:`~google.cloud.firestore_v1.client.Client.field_path` for
247 more information on **field paths**.
249 If a ``transaction`` is used and it already has write operations
250 added, this method cannot be used (i.e. read-after-write is not
251 allowed).
253 Args:
254 references (List[.AsyncDocumentReference, ...]): Iterable of document
255 references to be retrieved.
256 field_paths (Optional[Iterable[str, ...]]): An iterable of field
257 paths (``.``-delimited list of field names) to use as a
258 projection of document fields in the returned results. If
259 no value is provided, all fields will be returned.
260 transaction (Optional[:class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction`]):
261 An existing transaction that these ``references`` will be
262 retrieved in.
263 retry (google.api_core.retry.Retry): Designation of what errors, if any,
264 should be retried. Defaults to a system-specified policy.
265 timeout (float): The timeout for this request. Defaults to a
266 system-specified value.
268 Yields:
269 .DocumentSnapshot: The next document snapshot that fulfills the
270 query, or :data:`None` if the document does not exist.
271 """
272 request, reference_map, kwargs = self._prep_get_all(
273 references, field_paths, transaction, retry, timeout
274 )
276 response_iterator = await self._firestore_api.batch_get_documents(
277 request=request,
278 metadata=self._rpc_metadata,
279 **kwargs,
280 )
282 async for get_doc_response in response_iterator:
283 yield _parse_batch_get(get_doc_response, reference_map, self)
285 async def collections(
286 self,
287 retry: retries.Retry = gapic_v1.method.DEFAULT,
288 timeout: float = None,
289 ) -> AsyncGenerator[AsyncCollectionReference, Any]:
290 """List top-level collections of the client's database.
292 Args:
293 retry (google.api_core.retry.Retry): Designation of what errors, if any,
294 should be retried. Defaults to a system-specified policy.
295 timeout (float): The timeout for this request. Defaults to a
296 system-specified value.
298 Returns:
299 Sequence[:class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`]:
300 iterator of subcollections of the current document.
301 """
302 request, kwargs = self._prep_collections(retry, timeout)
303 iterator = await self._firestore_api.list_collection_ids(
304 request=request,
305 metadata=self._rpc_metadata,
306 **kwargs,
307 )
309 async for collection_id in iterator:
310 yield self.collection(collection_id)
312 async def recursive_delete(
313 self,
314 reference: Union[AsyncCollectionReference, AsyncDocumentReference],
315 *,
316 bulk_writer: Optional["BulkWriter"] = None,
317 chunk_size: Optional[int] = 5000,
318 ):
319 """Deletes documents and their subcollections, regardless of collection
320 name.
322 Passing an AsyncCollectionReference leads to each document in the
323 collection getting deleted, as well as all of their descendents.
325 Passing an AsyncDocumentReference deletes that one document and all of
326 its descendents.
328 Args:
329 reference (Union[
330 :class:`@google.cloud.firestore_v1.async_collection.CollectionReference`,
331 :class:`@google.cloud.firestore_v1.async_document.DocumentReference`,
332 ])
333 The reference to be deleted.
335 bulk_writer (Optional[:class:`@google.cloud.firestore_v1.bulk_writer.BulkWriter`])
336 The BulkWriter used to delete all matching documents. Supply this
337 if you want to override the default throttling behavior.
338 """
339 if bulk_writer is None:
340 bulk_writer = self.bulk_writer()
342 return await self._recursive_delete(
343 reference,
344 bulk_writer=bulk_writer,
345 chunk_size=chunk_size,
346 )
348 async def _recursive_delete(
349 self,
350 reference: Union[AsyncCollectionReference, AsyncDocumentReference],
351 bulk_writer: "BulkWriter",
352 *,
353 chunk_size: Optional[int] = 5000,
354 depth: Optional[int] = 0,
355 ) -> int:
356 """Recursion helper for `recursive_delete."""
358 num_deleted: int = 0
360 if isinstance(reference, AsyncCollectionReference):
361 chunk: List[DocumentSnapshot]
362 async for chunk in reference.recursive().select(
363 [FieldPath.document_id()]
364 )._chunkify(chunk_size):
365 doc_snap: DocumentSnapshot
366 for doc_snap in chunk:
367 num_deleted += 1
368 bulk_writer.delete(doc_snap.reference)
370 elif isinstance(reference, AsyncDocumentReference):
371 col_ref: AsyncCollectionReference
372 async for col_ref in reference.collections():
373 num_deleted += await self._recursive_delete(
374 col_ref,
375 bulk_writer=bulk_writer,
376 depth=depth + 1,
377 chunk_size=chunk_size,
378 )
379 num_deleted += 1
380 bulk_writer.delete(reference)
382 else:
383 raise TypeError(
384 f"Unexpected type for reference: {reference.__class__.__name__}"
385 )
387 if depth == 0:
388 bulk_writer.close()
390 return num_deleted
392 def batch(self) -> AsyncWriteBatch:
393 """Get a batch instance from this client.
395 Returns:
396 :class:`~google.cloud.firestore_v1.async_batch.AsyncWriteBatch`:
397 A "write" batch to be used for accumulating document changes and
398 sending the changes all at once.
399 """
400 return AsyncWriteBatch(self)
402 def transaction(self, **kwargs) -> AsyncTransaction:
403 """Get a transaction that uses this client.
405 See :class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction` for
406 more information on transactions and the constructor arguments.
408 Args:
409 kwargs (Dict[str, Any]): The keyword arguments (other than
410 ``client``) to pass along to the
411 :class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction`
412 constructor.
414 Returns:
415 :class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction`:
416 A transaction attached to this client.
417 """
418 return AsyncTransaction(self, **kwargs)