def set(doc_ref: DocumentReference, *args, **kwargs) -> StatusCode: """Create a new document in Firestore. If the document is inside the "history" collection also create the "last_edit" timestamp field. Args: doc_ref: Firestore reference to the document that will be created. Raises: FirestoreConnectorError: if an arbitrary exception occurred after retrying. Caught by decorator to return error code. Returns: 0 success (1st or 2nd time tried) or -1 exception (error after retrying a second time - from decorator). """ try: doc_ref.set(*args, **kwargs, merge=True) _update_last_edit(doc_ref) return StatusCode.SUCCESS except Exception: __log_exception(4, doc_ref) sleep(EXCEPTION_SLEEP_TIME) try: # Retry doc_ref.set(*args, **kwargs, merge=True) _update_last_edit(doc_ref) return StatusCode.SUCCESS except Exception as exc: __log_exception(4, doc_ref, True) raise FirestoreConnectorError("set", exc)
def get(doc_ref: DocumentReference, *args, **kwargs) -> DocumentSnapshot: """Retrieve a document from Firestore Args: doc_ref: Firestore reference to the document. Raises: FirestoreConnectorError: if an arbitrary exception occurred after retrying. Caught by decorator to return error code. Returns: Firestore document object or -1 exception (error after retrying a second time - from decorator). """ try: return doc_ref.get(*args, **kwargs) except Exception: __log_exception(3, doc_ref) sleep(EXCEPTION_SLEEP_TIME) try: # Retry return doc_ref.get(*args, **kwargs) except Exception as exc: __log_exception(3, doc_ref, True) raise FirestoreConnectorError("get", exc)
def update(doc_ref: DocumentReference, *args, **kwargs) -> StatusCode: """Update a Firestore document. Args: doc_ref: Firestore reference to the document that will be updated. Raises: FirestoreConnectorError: if an arbitrary exception occurred after retrying. Caught by decorator to return error code. Returns: 0 success (1st or 2nd time tried) or -1 exception (error after retrying a second time - from decorator). """ try: doc_ref.update(*args, **kwargs) _update_last_edit(doc_ref) return StatusCode.SUCCESS except Exception: __log_exception(2, doc_ref) sleep(EXCEPTION_SLEEP_TIME) try: # Retry doc_ref.update(*args, **kwargs) _update_last_edit(doc_ref) return StatusCode.SUCCESS except Exception as exc: __log_exception(2, doc_ref, True) raise FirestoreConnectorError("update", exc)
def _update_last_edit(doc_ref: DocumentReference) -> None: """If the document reference points to the history collection then update its "last_edit" field to the currrent datetime. This datetame is parsed as a Timestamp in the document. Args: doc_ref: Firestore object reference to the document. """ _path = doc_ref.path.split("/") if len(_path) == 2 and _path[0] == "history": # why is this using bare firestore update instead of update function? doc_ref.update({"last_edit": datetime.now(timezone.utc)})
def document(self, *document_path: Tuple[str]) -> DocumentReference: """Get a reference to a document in a collection. For a top-level document: .. code-block:: python >>> client.document('collek/shun') >>> # is the same as >>> client.document('collek', 'shun') For a document in a sub-collection: .. code-block:: python >>> client.document('mydocs/doc/subcol/child') >>> # is the same as >>> client.document('mydocs', 'doc', 'subcol', 'child') Documents in sub-collections can be nested deeper in a similar fashion. Args: document_path (Tuple[str, ...]): Can either be * A single ``/``-delimited path to a document * A tuple of document path segments Returns: :class:`~google.cloud.firestore_v1.document.DocumentReference`: A reference to a document in a collection. """ return DocumentReference(*self._document_path_helper(*document_path), client=self)
def test_get_document_ref(self): from google.cloud.firestore_v1.document import DocumentReference client = mock.Mock(spec=["get_all"]) transaction = self._make_one(client) ref = DocumentReference("documents", "doc-id") result = transaction.get(ref) client.get_all.assert_called_once_with([ref], transaction=transaction.id) self.assertIs(result, client.get_all.return_value)
def test_clean_dict(document): doc_dict = document.to_dict() doc_dict.update({ 'ref': DocumentReference('collection', 'doc'), 'nan_value': np.nan }) result_dict = clean_dict(doc_dict) assert result_dict.get('ref') is None assert result_dict.get('nan_value') is None
def deactivate_player(self, player: DocumentReference) -> None: batch = self._client.batch() player_ref = self._client.document("games/{}/players/{}".format( self._game_id, player.id)) tribe_ref = self._client.document("games/{}/tribes/{}".format( self._game_id, player.get('tribe_id'))) team_ref = self._client.document("games/{}/teams/{}".format( self._game_id, player.get('team_id'))) game_ref = self._client.document("games/{}".format(self._game_id)) batch.update(player_ref, {'active': False}) batch.update(tribe_ref, {'count_players': Increment(-1)}) batch.update(team_ref, {'count_players': Increment(-1)}) batch.update(game_ref, {'count_players': Increment(-1)}) users = self._client.collection("users").where( "phone_number", "==", player.get("phone_number")).get() if len(users) > 0: user_ref = users[0].reference batch.update(user_ref, {'game_id': None}) batch.commit()
def test_missing(self): from google.cloud.firestore_v1.document import DocumentReference ref_string = self._dummy_ref_string() response_pb = _make_batch_response(missing=ref_string) document = DocumentReference("fizz", "bazz", client=mock.sentinel.client) reference_map = {ref_string: document} snapshot = self._call_fut(response_pb, reference_map) self.assertFalse(snapshot.exists) self.assertEqual(snapshot.id, "bazz") self.assertIsNone(snapshot._data)
def delete_subcolls(batch: WriteBatch, doc: DocumentReference): """Delete documents within all subcollections of the provided document.""" colls: Sequence[CollectionReference] = doc.collections() for coll in colls: for subcoll_doc_ref in coll.list_documents(): delete_subcolls(batch, subcoll_doc_ref) # The break is required such that the 'else' block below is executed if colls is empty. break else: # No collections within the provided document delete_doc(batch, doc)
def test__parse_batch_get_missing(): from google.cloud.firestore_v1.document import DocumentReference from google.cloud.firestore_v1.base_client import _parse_batch_get ref_string = _dummy_ref_string() response_pb = _make_batch_response(missing=ref_string) document = DocumentReference("fizz", "bazz", client=mock.sentinel.client) reference_map = {ref_string: document} snapshot = _parse_batch_get(response_pb, reference_map, mock.sentinel.client) assert not snapshot.exists assert snapshot.id == "bazz" assert snapshot._data is None
def _transaction_get_w_document_ref_helper(retry=None, timeout=None): from google.cloud.firestore_v1.document import DocumentReference from google.cloud.firestore_v1 import _helpers client = mock.Mock(spec=["get_all"]) transaction = _make_transaction(client) ref = DocumentReference("documents", "doc-id") kwargs = _helpers.make_retry_timeout_kwargs(retry, timeout) result = transaction.get(ref, **kwargs) assert result is client.get_all.return_value client.get_all.assert_called_once_with([ref], transaction=transaction, **kwargs)
def _add_bundle_element(self, bundle_element: BundleElement, *, client: BaseClient, type: str): # type: ignore """Applies BundleElements to this FirestoreBundle instance as a part of deserializing a FirestoreBundle string. """ from google.cloud.firestore_v1.types.document import Document if getattr(self, "_doc_metadata_map", None) is None: self._doc_metadata_map = {} if type == "metadata": self._deserialized_metadata = bundle_element.metadata # type: ignore elif type == "namedQuery": self.named_queries[ bundle_element.named_query. name] = bundle_element.named_query # type: ignore elif type == "documentMetadata": self._doc_metadata_map[bundle_element.document_metadata. name] = bundle_element.document_metadata elif type == "document": doc_ref_value = _helpers.DocumentReferenceValue( bundle_element.document.name) snapshot = DocumentSnapshot( data=_helpers.decode_dict( Document(mapping=bundle_element.document).fields, client), exists=True, reference=DocumentReference( doc_ref_value.collection_name, doc_ref_value.document_id, client=client, ), read_time=self._doc_metadata_map[ bundle_element.document.name].read_time, create_time=bundle_element.document. create_time, # type: ignore update_time=bundle_element.document. update_time, # type: ignore ) self.add_document(snapshot) bundled_document = self.documents.get( snapshot.reference._document_path) for query_name in self._doc_metadata_map[ bundle_element.document.name].queries: bundled_document.metadata.queries.append( query_name) # type: ignore else: raise ValueError(f"Unexpected type of BundleElement: {type}")
def document(self, *document_path): """Get a reference to a document in a collection. For a top-level document: .. code-block:: python >>> client.document('collek/shun') >>> # is the same as >>> client.document('collek', 'shun') For a document in a sub-collection: .. code-block:: python >>> client.document('mydocs/doc/subcol/child') >>> # is the same as >>> client.document('mydocs', 'doc', 'subcol', 'child') Documents in sub-collections can be nested deeper in a similar fashion. Args: document_path (Tuple[str, ...]): Can either be * A single ``/``-delimited path to a document * A tuple of document path segments Returns: ~.firestore_v1.document.DocumentReference: A reference to a document in a collection. """ if len(document_path) == 1: path = document_path[0].split(_helpers.DOCUMENT_PATH_DELIMITER) else: path = document_path # DocumentReference takes a relative path. Strip the database string if present. base_path = self._database_string + "/documents/" joined_path = _helpers.DOCUMENT_PATH_DELIMITER.join(path) if joined_path.startswith(base_path): joined_path = joined_path[len(base_path) :] path = joined_path.split(_helpers.DOCUMENT_PATH_DELIMITER) return DocumentReference(*path, client=self)
def build_document_snapshot( *, collection_name: str = "col", document_id: str = "doc", client: typing.Optional[BaseClient] = None, data: typing.Optional[typing.Dict] = None, exists: bool = True, create_time: typing.Optional[Timestamp] = None, read_time: typing.Optional[Timestamp] = None, update_time: typing.Optional[Timestamp] = None, ) -> DocumentSnapshot: return DocumentSnapshot( DocumentReference(collection_name, document_id, client=client), data or {"hello", "world"}, exists=exists, read_time=read_time or build_timestamp(), create_time=create_time or build_timestamp(), update_time=update_time or build_timestamp(), )
def _make_document_reference(*args, **kwargs): from google.cloud.firestore_v1.document import DocumentReference return DocumentReference(*args, **kwargs)
def __init__(self, document: DocumentReference): setattr(self, 'id', document.id) document_dict = document.to_dict() if document_dict: for k, v in document_dict.items(): setattr(self, k, v)