def test_paths(self): from google.cloud.firestore_v1.types import common field_paths = ["a.b", "c"] result = self._call_fut(field_paths) expected = common.DocumentMask(field_paths=field_paths) self.assertEqual(result, expected)
def _prep_batch_get( self, field_paths: Iterable[str] = None, transaction=None, retry: retries.Retry = None, timeout: float = None, ) -> Tuple[dict, dict]: """Shared setup for async/sync :meth:`get`.""" if isinstance(field_paths, str): raise ValueError( "'field_paths' must be a sequence of paths, not a string.") if field_paths is not None: mask = common.DocumentMask(field_paths=sorted(field_paths)) else: mask = None request = { "database": self._client._database_string, "documents": [self._document_path], "mask": mask, "transaction": _helpers.get_transaction_id(transaction), } kwargs = _helpers.make_retry_timeout_kwargs(retry, timeout) return request, kwargs
def test_basewritebatch_update(): from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.types import document from google.cloud.firestore_v1.types import write client = _make_client() batch = _make_derived_write_batch(client) assert batch._write_pbs == [] reference = client.document("cats", "cradle") field_path = "head.foot" value = u"knees toes shoulders" field_updates = {field_path: value} ret_val = batch.update(reference, field_updates) assert ret_val is None map_pb = document.MapValue(fields={"foot": _value_pb(string_value=value)}) new_write_pb = write.Write( update=document.Document( name=reference._document_path, fields={"head": _value_pb(map_value=map_pb)}, ), update_mask=common.DocumentMask(field_paths=[field_path]), current_document=common.Precondition(exists=True), ) assert batch._write_pbs == [new_write_pb]
def _get_update_mask(self, allow_empty_mask=False) -> types.common.DocumentMask: mask_paths = [] for field_path in self.top_level_paths: if field_path not in self.transform_paths: mask_paths.append(field_path.to_api_repr()) return common.DocumentMask(field_paths=mask_paths)
def test__get_doc_mask_w_paths(): from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.base_client import _get_doc_mask field_paths = ["a.b", "c"] result = _get_doc_mask(field_paths) expected = common.DocumentMask(field_paths=field_paths) assert result == expected
def _get_update_mask( self, allow_empty_mask=False) -> Optional[types.common.DocumentMask]: # Mask uses dotted / quoted paths. mask_paths = [ field_path.to_api_repr() for field_path in self.merge if field_path not in self.transform_merge ] return common.DocumentMask(field_paths=mask_paths)
def _write_pb_for_update(document_path, update_values, field_paths): from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.types import document from google.cloud.firestore_v1.types import write from google.cloud.firestore_v1 import _helpers return write.Write( update=document.Document(name=document_path, fields=_helpers.encode_dict(update_values)), update_mask=common.DocumentMask(field_paths=field_paths), current_document=common.Precondition(exists=True), )
def _write_pb_for_set(document_path, document_data, merge): from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.types import document from google.cloud.firestore_v1.types import write from google.cloud.firestore_v1 import _helpers write_pbs = write.Write(update=document.Document( name=document_path, fields=_helpers.encode_dict(document_data))) if merge: field_paths = [ field_path for field_path, value in _helpers.extract_fields( document_data, _helpers.FieldPath()) ] field_paths = [ field_path.to_api_repr() for field_path in sorted(field_paths) ] mask = common.DocumentMask(field_paths=sorted(field_paths)) write_pbs._pb.update_mask.CopyFrom(mask._pb) return write_pbs
async def test_get_all(self): from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.async_document import DocumentSnapshot data1 = {"a": u"cheese"} data2 = {"b": True, "c": 18} info = self._info_for_get_all(data1, data2) client, document1, document2, response1, response2 = info # Exercise the mocked ``batch_get_documents``. field_paths = ["a", "b"] snapshots = await self._get_all_helper( client, [document1, document2], [response1, response2], field_paths=field_paths, ) self.assertEqual(len(snapshots), 2) snapshot1 = snapshots[0] self.assertIsInstance(snapshot1, DocumentSnapshot) self.assertIs(snapshot1._reference, document1) self.assertEqual(snapshot1._data, data1) snapshot2 = snapshots[1] self.assertIsInstance(snapshot2, DocumentSnapshot) self.assertIs(snapshot2._reference, document2) self.assertEqual(snapshot2._data, data2) # Verify the call to the mock. doc_paths = [document1._document_path, document2._document_path] mask = common.DocumentMask(field_paths=field_paths) client._firestore_api.batch_get_documents.assert_called_once_with( request={ "database": client._database_string, "documents": doc_paths, "mask": mask, "transaction": None, }, metadata=client._rpc_metadata, )
def _get_helper( self, field_paths=None, use_transaction=False, not_found=False, # This should be an impossible case, but we test against it for # completeness return_empty=False, retry=None, timeout=None, ): from google.cloud.firestore_v1 import _helpers from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.types import document from google.cloud.firestore_v1.types import firestore from google.cloud.firestore_v1.transaction import Transaction # Create a minimal fake GAPIC with a dummy response. create_time = 123 update_time = 234 read_time = 345 firestore_api = mock.Mock(spec=["batch_get_documents"]) response = mock.create_autospec(firestore.BatchGetDocumentsResponse) response.read_time = read_time response.found = mock.create_autospec(document.Document) response.found.fields = {} response.found.create_time = create_time response.found.update_time = update_time client = _make_client("donut-base") client._firestore_api_internal = firestore_api document_reference = self._make_one("where", "we-are", client=client) response.found.name = None if not_found else document_reference._document_path response.missing = document_reference._document_path if not_found else None def WhichOneof(val): return "missing" if not_found else "found" response._pb = response response._pb.WhichOneof = WhichOneof firestore_api.batch_get_documents.return_value = iter( [response] if not return_empty else []) if use_transaction: transaction = Transaction(client) transaction_id = transaction._id = b"asking-me-2" else: transaction = None kwargs = _helpers.make_retry_timeout_kwargs(retry, timeout) snapshot = document_reference.get(field_paths=field_paths, transaction=transaction, **kwargs) self.assertIs(snapshot.reference, document_reference) if not_found or return_empty: self.assertIsNone(snapshot._data) self.assertFalse(snapshot.exists) self.assertIsNotNone(snapshot.read_time) self.assertIsNone(snapshot.create_time) self.assertIsNone(snapshot.update_time) else: self.assertEqual(snapshot.to_dict(), {}) self.assertTrue(snapshot.exists) self.assertIs(snapshot.read_time, read_time) self.assertIs(snapshot.create_time, create_time) self.assertIs(snapshot.update_time, update_time) # Verify the request made to the API if field_paths is not None: mask = common.DocumentMask(field_paths=sorted(field_paths)) else: mask = None if use_transaction: expected_transaction_id = transaction_id else: expected_transaction_id = None firestore_api.batch_get_documents.assert_called_once_with( request={ "database": client._database_string, "documents": [document_reference._document_path], "mask": mask, "transaction": expected_transaction_id, }, metadata=client._rpc_metadata, **kwargs, )
async def _get_all_helper( self, num_snapshots=2, txn_id=None, retry=None, timeout=None ): from google.cloud.firestore_v1 import _helpers from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.async_document import DocumentSnapshot client = self._make_default_one() data1 = {"a": "cheese"} document1 = client.document("pineapple", "lamp1") document_pb1, read_time = _doc_get_info(document1._document_path, data1) response1 = _make_batch_response(found=document_pb1, read_time=read_time) data2 = {"b": True, "c": 18} document2 = client.document("pineapple", "lamp2") document, read_time = _doc_get_info(document2._document_path, data2) response2 = _make_batch_response(found=document, read_time=read_time) document3 = client.document("pineapple", "lamp3") response3 = _make_batch_response(missing=document3._document_path) expected_data = [data1, data2, None][:num_snapshots] documents = [document1, document2, document3][:num_snapshots] responses = [response1, response2, response3][:num_snapshots] field_paths = [ field_path for field_path in ["a", "b", None][:num_snapshots] if field_path ] kwargs = _helpers.make_retry_timeout_kwargs(retry, timeout) if txn_id is not None: transaction = client.transaction() transaction._id = txn_id kwargs["transaction"] = transaction snapshots = await self._invoke_get_all( client, documents, responses, field_paths=field_paths, **kwargs, ) self.assertEqual(len(snapshots), num_snapshots) for data, document, snapshot in zip(expected_data, documents, snapshots): self.assertIsInstance(snapshot, DocumentSnapshot) self.assertIs(snapshot._reference, document) if data is None: self.assertFalse(snapshot.exists) else: self.assertEqual(snapshot._data, data) # Verify the call to the mock. doc_paths = [document._document_path for document in documents] mask = common.DocumentMask(field_paths=field_paths) kwargs.pop("transaction", None) client._firestore_api.batch_get_documents.assert_called_once_with( request={ "database": client._database_string, "documents": doc_paths, "mask": mask, "transaction": txn_id, }, metadata=client._rpc_metadata, **kwargs, )
def get(self, field_paths=None, transaction=None) -> DocumentSnapshot: """Retrieve a snapshot of the current document. See :meth:`~google.cloud.firestore_v1.base_client.BaseClient.field_path` for more information on **field paths**. If a ``transaction`` is used and it already has write operations added, this method cannot be used (i.e. read-after-write is not allowed). Args: field_paths (Optional[Iterable[str, ...]]): An iterable of field paths (``.``-delimited list of field names) to use as a projection of document fields in the returned results. If no value is provided, all fields will be returned. transaction (Optional[:class:`~google.cloud.firestore_v1.transaction.Transaction`]): An existing transaction that this reference will be retrieved in. Returns: :class:`~google.cloud.firestore_v1.base_document.DocumentSnapshot`: A snapshot of the current document. If the document does not exist at the time of the snapshot is taken, the snapshot's :attr:`reference`, :attr:`data`, :attr:`update_time`, and :attr:`create_time` attributes will all be ``None`` and its :attr:`exists` attribute will be ``False``. """ if isinstance(field_paths, str): raise ValueError("'field_paths' must be a sequence of paths, not a string.") if field_paths is not None: mask = common.DocumentMask(field_paths=sorted(field_paths)) else: mask = None firestore_api = self._client._firestore_api try: document_pb = firestore_api.get_document( request={ "name": self._document_path, "mask": mask, "transaction": _helpers.get_transaction_id(transaction), }, metadata=self._client._rpc_metadata, ) except exceptions.NotFound: data = None exists = False create_time = None update_time = None else: data = _helpers.decode_dict(document_pb.fields, self._client) exists = True create_time = document_pb.create_time update_time = document_pb.update_time return DocumentSnapshot( reference=self, data=data, exists=exists, read_time=None, # No server read_time available create_time=create_time, update_time=update_time, )
async def _get_helper( self, field_paths=None, use_transaction=False, not_found=False, retry=None, timeout=None, ): from google.api_core.exceptions import NotFound from google.cloud.firestore_v1 import _helpers from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.types import document from google.cloud.firestore_v1.transaction import Transaction # Create a minimal fake GAPIC with a dummy response. create_time = 123 update_time = 234 firestore_api = AsyncMock(spec=["get_document"]) response = mock.create_autospec(document.Document) response.fields = {} response.create_time = create_time response.update_time = update_time if not_found: firestore_api.get_document.side_effect = NotFound("testing") else: firestore_api.get_document.return_value = response client = _make_client("donut-base") client._firestore_api_internal = firestore_api document = self._make_one("where", "we-are", client=client) if use_transaction: transaction = Transaction(client) transaction_id = transaction._id = b"asking-me-2" else: transaction = None kwargs = _helpers.make_retry_timeout_kwargs(retry, timeout) snapshot = await document.get( field_paths=field_paths, transaction=transaction, **kwargs, ) self.assertIs(snapshot.reference, document) if not_found: self.assertIsNone(snapshot._data) self.assertFalse(snapshot.exists) self.assertIsNone(snapshot.read_time) self.assertIsNone(snapshot.create_time) self.assertIsNone(snapshot.update_time) else: self.assertEqual(snapshot.to_dict(), {}) self.assertTrue(snapshot.exists) self.assertIsNone(snapshot.read_time) self.assertIs(snapshot.create_time, create_time) self.assertIs(snapshot.update_time, update_time) # Verify the request made to the API if field_paths is not None: mask = common.DocumentMask(field_paths=sorted(field_paths)) else: mask = None if use_transaction: expected_transaction_id = transaction_id else: expected_transaction_id = None firestore_api.get_document.assert_called_once_with( request={ "name": document._document_path, "mask": mask, "transaction": expected_transaction_id, }, metadata=client._rpc_metadata, **kwargs, )