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)})
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
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
Example #8
0
 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()
Example #9
0
    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)
Example #10
0
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)
Example #11
0
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
Example #12
0
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)
Example #13
0
    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}")
Example #14
0
    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(),
    )
Example #16
0
def _make_document_reference(*args, **kwargs):
    from google.cloud.firestore_v1.document import DocumentReference

    return DocumentReference(*args, **kwargs)
Example #17
0
 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)