Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/firestore_v1/async_document.py: 45%
44 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"""Classes for representing documents for the Google Cloud Firestore API."""
16import datetime
17import logging
19from google.api_core import gapic_v1
20from google.api_core import retry as retries
21from google.cloud._helpers import _datetime_to_pb_timestamp # type: ignore
23from google.cloud.firestore_v1.base_document import (
24 BaseDocumentReference,
25 DocumentSnapshot,
26 _first_write_result,
27)
28from google.cloud.firestore_v1 import _helpers
29from google.cloud.firestore_v1.types import write
30from google.protobuf.timestamp_pb2 import Timestamp
31from typing import AsyncGenerator, Iterable
34logger = logging.getLogger(__name__)
37class AsyncDocumentReference(BaseDocumentReference):
38 """A reference to a document in a Firestore database.
40 The document may already exist or can be created by this class.
42 Args:
43 path (Tuple[str, ...]): The components in the document path.
44 This is a series of strings representing each collection and
45 sub-collection ID, as well as the document IDs for any documents
46 that contain a sub-collection (as well as the base document).
47 kwargs (dict): The keyword arguments for the constructor. The only
48 supported keyword is ``client`` and it must be a
49 :class:`~google.cloud.firestore_v1.client.Client`. It represents
50 the client that created this document reference.
52 Raises:
53 ValueError: if
55 * the ``path`` is empty
56 * there are an even number of elements
57 * a collection ID in ``path`` is not a string
58 * a document ID in ``path`` is not a string
59 TypeError: If a keyword other than ``client`` is used.
60 """
62 def __init__(self, *path, **kwargs) -> None:
63 super(AsyncDocumentReference, self).__init__(*path, **kwargs)
65 async def create(
66 self,
67 document_data: dict,
68 retry: retries.Retry = gapic_v1.method.DEFAULT,
69 timeout: float = None,
70 ) -> write.WriteResult:
71 """Create the current document in the Firestore database.
73 Args:
74 document_data (dict): Property names and values to use for
75 creating a document.
76 retry (google.api_core.retry.Retry): Designation of what errors, if any,
77 should be retried. Defaults to a system-specified policy.
78 timeout (float): The timeout for this request. Defaults to a
79 system-specified value.
81 Returns:
82 :class:`~google.cloud.firestore_v1.types.WriteResult`:
83 The write result corresponding to the committed document.
84 A write result contains an ``update_time`` field.
86 Raises:
87 :class:`google.cloud.exceptions.Conflict`:
88 If the document already exists.
89 """
90 batch, kwargs = self._prep_create(document_data, retry, timeout)
91 write_results = await batch.commit(**kwargs)
92 return _first_write_result(write_results)
94 async def set(
95 self,
96 document_data: dict,
97 merge: bool = False,
98 retry: retries.Retry = gapic_v1.method.DEFAULT,
99 timeout: float = None,
100 ) -> write.WriteResult:
101 """Replace the current document in the Firestore database.
103 A write ``option`` can be specified to indicate preconditions of
104 the "set" operation. If no ``option`` is specified and this document
105 doesn't exist yet, this method will create it.
107 Overwrites all content for the document with the fields in
108 ``document_data``. This method performs almost the same functionality
109 as :meth:`create`. The only difference is that this method doesn't
110 make any requirements on the existence of the document (unless
111 ``option`` is used), whereas as :meth:`create` will fail if the
112 document already exists.
114 Args:
115 document_data (dict): Property names and values to use for
116 replacing a document.
117 merge (Optional[bool] or Optional[List<apispec>]):
118 If True, apply merging instead of overwriting the state
119 of the document.
120 retry (google.api_core.retry.Retry): Designation of what errors, if any,
121 should be retried. Defaults to a system-specified policy.
122 timeout (float): The timeout for this request. Defaults to a
123 system-specified value.
125 Returns:
126 :class:`~google.cloud.firestore_v1.types.WriteResult`:
127 The write result corresponding to the committed document. A write
128 result contains an ``update_time`` field.
129 """
130 batch, kwargs = self._prep_set(document_data, merge, retry, timeout)
131 write_results = await batch.commit(**kwargs)
132 return _first_write_result(write_results)
134 async def update(
135 self,
136 field_updates: dict,
137 option: _helpers.WriteOption = None,
138 retry: retries.Retry = gapic_v1.method.DEFAULT,
139 timeout: float = None,
140 ) -> write.WriteResult:
141 """Update an existing document in the Firestore database.
143 By default, this method verifies that the document exists on the
144 server before making updates. A write ``option`` can be specified to
145 override these preconditions.
147 Each key in ``field_updates`` can either be a field name or a
148 **field path** (For more information on **field paths**, see
149 :meth:`~google.cloud.firestore_v1.client.Client.field_path`.) To
150 illustrate this, consider a document with
152 .. code-block:: python
154 >>> snapshot = await document.get()
155 >>> snapshot.to_dict()
156 {
157 'foo': {
158 'bar': 'baz',
159 },
160 'other': True,
161 }
163 stored on the server. If the field name is used in the update:
165 .. code-block:: python
167 >>> field_updates = {
168 ... 'foo': {
169 ... 'quux': 800,
170 ... },
171 ... }
172 >>> await document.update(field_updates)
174 then all of ``foo`` will be overwritten on the server and the new
175 value will be
177 .. code-block:: python
179 >>> snapshot = await document.get()
180 >>> snapshot.to_dict()
181 {
182 'foo': {
183 'quux': 800,
184 },
185 'other': True,
186 }
188 On the other hand, if a ``.``-delimited **field path** is used in the
189 update:
191 .. code-block:: python
193 >>> field_updates = {
194 ... 'foo.quux': 800,
195 ... }
196 >>> await document.update(field_updates)
198 then only ``foo.quux`` will be updated on the server and the
199 field ``foo.bar`` will remain intact:
201 .. code-block:: python
203 >>> snapshot = await document.get()
204 >>> snapshot.to_dict()
205 {
206 'foo': {
207 'bar': 'baz',
208 'quux': 800,
209 },
210 'other': True,
211 }
213 .. warning::
215 A **field path** can only be used as a top-level key in
216 ``field_updates``.
218 To delete / remove a field from an existing document, use the
219 :attr:`~google.cloud.firestore_v1.transforms.DELETE_FIELD` sentinel.
220 So with the example above, sending
222 .. code-block:: python
224 >>> field_updates = {
225 ... 'other': firestore.DELETE_FIELD,
226 ... }
227 >>> await document.update(field_updates)
229 would update the value on the server to:
231 .. code-block:: python
233 >>> snapshot = await document.get()
234 >>> snapshot.to_dict()
235 {
236 'foo': {
237 'bar': 'baz',
238 },
239 }
241 To set a field to the current time on the server when the
242 update is received, use the
243 :attr:`~google.cloud.firestore_v1.transforms.SERVER_TIMESTAMP`
244 sentinel.
245 Sending
247 .. code-block:: python
249 >>> field_updates = {
250 ... 'foo.now': firestore.SERVER_TIMESTAMP,
251 ... }
252 >>> await document.update(field_updates)
254 would update the value on the server to:
256 .. code-block:: python
258 >>> snapshot = await document.get()
259 >>> snapshot.to_dict()
260 {
261 'foo': {
262 'bar': 'baz',
263 'now': datetime.datetime(2012, ...),
264 },
265 'other': True,
266 }
268 Args:
269 field_updates (dict): Field names or paths to update and values
270 to update with.
271 option (Optional[:class:`~google.cloud.firestore_v1.client.WriteOption`]):
272 A write option to make assertions / preconditions on the server
273 state of the document before applying changes.
274 retry (google.api_core.retry.Retry): Designation of what errors, if any,
275 should be retried. Defaults to a system-specified policy.
276 timeout (float): The timeout for this request. Defaults to a
277 system-specified value.
279 Returns:
280 :class:`~google.cloud.firestore_v1.types.WriteResult`:
281 The write result corresponding to the updated document. A write
282 result contains an ``update_time`` field.
284 Raises:
285 :class:`google.cloud.exceptions.NotFound`:
286 If the document does not exist.
287 """
288 batch, kwargs = self._prep_update(field_updates, option, retry, timeout)
289 write_results = await batch.commit(**kwargs)
290 return _first_write_result(write_results)
292 async def delete(
293 self,
294 option: _helpers.WriteOption = None,
295 retry: retries.Retry = gapic_v1.method.DEFAULT,
296 timeout: float = None,
297 ) -> Timestamp:
298 """Delete the current document in the Firestore database.
300 Args:
301 option (Optional[:class:`~google.cloud.firestore_v1.client.WriteOption`]):
302 A write option to make assertions / preconditions on the server
303 state of the document before applying changes.
304 retry (google.api_core.retry.Retry): Designation of what errors, if any,
305 should be retried. Defaults to a system-specified policy.
306 timeout (float): The timeout for this request. Defaults to a
307 system-specified value.
309 Returns:
310 :class:`google.protobuf.timestamp_pb2.Timestamp`:
311 The time that the delete request was received by the server.
312 If the document did not exist when the delete was sent (i.e.
313 nothing was deleted), this method will still succeed and will
314 still return the time that the request was received by the server.
315 """
316 request, kwargs = self._prep_delete(option, retry, timeout)
318 commit_response = await self._client._firestore_api.commit(
319 request=request,
320 metadata=self._client._rpc_metadata,
321 **kwargs,
322 )
324 return commit_response.commit_time
326 async def get(
327 self,
328 field_paths: Iterable[str] = None,
329 transaction=None,
330 retry: retries.Retry = gapic_v1.method.DEFAULT,
331 timeout: float = None,
332 ) -> DocumentSnapshot:
333 """Retrieve a snapshot of the current document.
335 See :meth:`~google.cloud.firestore_v1.base_client.BaseClient.field_path` for
336 more information on **field paths**.
338 If a ``transaction`` is used and it already has write operations
339 added, this method cannot be used (i.e. read-after-write is not
340 allowed).
342 Args:
343 field_paths (Optional[Iterable[str, ...]]): An iterable of field
344 paths (``.``-delimited list of field names) to use as a
345 projection of document fields in the returned results. If
346 no value is provided, all fields will be returned.
347 transaction (Optional[:class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction`]):
348 An existing transaction that this reference
349 will be retrieved in.
350 retry (google.api_core.retry.Retry): Designation of what errors, if any,
351 should be retried. Defaults to a system-specified policy.
352 timeout (float): The timeout for this request. Defaults to a
353 system-specified value.
355 Returns:
356 :class:`~google.cloud.firestore_v1.base_document.DocumentSnapshot`:
357 A snapshot of the current document. If the document does not
358 exist at the time of the snapshot is taken, the snapshot's
359 :attr:`reference`, :attr:`data`, :attr:`update_time`, and
360 :attr:`create_time` attributes will all be ``None`` and
361 its :attr:`exists` attribute will be ``False``.
362 """
363 from google.cloud.firestore_v1.base_client import _parse_batch_get
365 request, kwargs = self._prep_batch_get(field_paths, transaction, retry, timeout)
367 response_iter = await self._client._firestore_api.batch_get_documents(
368 request=request,
369 metadata=self._client._rpc_metadata,
370 **kwargs,
371 )
373 async for resp in response_iter:
374 # Immediate return as the iterator should only ever have one item.
375 return _parse_batch_get(
376 get_doc_response=resp,
377 reference_map={self._document_path: self},
378 client=self._client,
379 )
381 logger.warning(
382 "`batch_get_documents` unexpectedly returned empty "
383 "stream. Expected one object.",
384 )
386 return DocumentSnapshot(
387 self,
388 None,
389 exists=False,
390 read_time=_datetime_to_pb_timestamp(datetime.datetime.now()),
391 create_time=None,
392 update_time=None,
393 )
395 async def collections(
396 self,
397 page_size: int = None,
398 retry: retries.Retry = gapic_v1.method.DEFAULT,
399 timeout: float = None,
400 ) -> AsyncGenerator:
401 """List subcollections of the current document.
403 Args:
404 page_size (Optional[int]]): The maximum number of collections
405 in each page of results from this request. Non-positive values
406 are ignored. Defaults to a sensible value set by the API.
407 retry (google.api_core.retry.Retry): Designation of what errors, if any,
408 should be retried. Defaults to a system-specified policy.
409 timeout (float): The timeout for this request. Defaults to a
410 system-specified value.
412 Returns:
413 Sequence[:class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`]:
414 iterator of subcollections of the current document. If the
415 document does not exist at the time of `snapshot`, the
416 iterator will be empty
417 """
418 request, kwargs = self._prep_collections(page_size, retry, timeout)
420 iterator = await self._client._firestore_api.list_collection_ids(
421 request=request,
422 metadata=self._client._rpc_metadata,
423 **kwargs,
424 )
426 async for collection_id in iterator:
427 yield self.collection(collection_id)