firestore_db_and_auth/documents/
read.rs

1use super::*;
2use std::io::Read;
3
4///
5/// Read a document of a specific type from a collection by its Firestore document name
6///
7/// ## Arguments
8/// * `auth` The authentication token
9/// * `document_name` The document path / collection and document id; For example `projects/my_project/databases/(default)/documents/tests/test`
10pub async fn read_by_name<T>(auth: &impl FirebaseAuthBearer, document_name: &str) -> Result<T>
11where
12    for<'b> T: Deserialize<'b>,
13{
14    let resp = request_document(auth, document_name).await?;
15
16    // We take the raw response first in order to provide
17    // more complete errors on deserialization failure
18    let full = resp.bytes().await?;
19    let json = serde_json::from_slice(&full).map_err(|e| FirebaseError::SerdeVerbose {
20        doc: Some(String::from(document_name)),
21        input_doc: String::from_utf8_lossy(&full).to_string(),
22        ser: e,
23    })?;
24
25    Ok(document_to_pod(&json, Some(&full))?)
26}
27
28///
29/// Read a document of a specific type from a collection
30///
31/// ## Arguments
32/// * `auth` The authentication token
33/// * `path` The document path / collection; For example `my_collection` or `a/nested/collection`
34/// * `document_id` The document id. Make sure that you do not include the document id to the path argument.
35pub async fn read<T>(auth: &impl FirebaseAuthBearer, path: &str, document_id: &str) -> Result<T>
36where
37    for<'b> T: Deserialize<'b>,
38{
39    let document_name = document_name(&auth.project_id(), path, document_id);
40    read_by_name(auth, &document_name).await
41}
42
43/// Return the raw unparsed content of the Firestore document. Methods like
44/// [`read()`](../documents/fn.read.html) will deserialize the JSON-encoded
45/// response into a known type `T`
46///
47/// Note that this leverages [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and the `read_to_string()` method to chunk the
48/// response. This will raise `FirebaseError::IO` if there are errors reading the stream. Please
49/// see [`read_to_end()`](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_to_end)
50pub async fn contents(auth: &impl FirebaseAuthBearer, path: &str, document_id: &str) -> Result<String> {
51    let document_name = document_name(&auth.project_id(), path, document_id);
52    let resp = request_document(auth, &document_name).await?;
53    resp.text().await.map_err(|e| FirebaseError::Request(e))
54}
55
56/// Executes the request to retrieve the document. Returns the response from `reqwest`
57async fn request_document(auth: &impl FirebaseAuthBearer, document_name: &str) -> Result<reqwest::Response> {
58    let url = firebase_url_base(document_name.as_ref());
59
60    let resp = auth
61        .client()
62        .get(&url)
63        .bearer_auth(auth.access_token().await)
64        .send()
65        .await?;
66
67    extract_google_api_error_async(resp, || document_name.to_owned()).await
68}
69
70/// Simple method to join the path and document identifier in correct format
71fn document_name(project_id: &str, path: &str, document_id: &str) -> String {
72    format!(
73        "projects/{}/databases/(default)/documents/{}/{}",
74        project_id, path, document_id
75    )
76}
77
78#[test]
79fn it_document_name_joins_paths() {
80    let project_id = "firebase-project";
81    let path = "one/two/three";
82    let document_id = "my-document";
83    assert_eq!(
84        document_name(&project_id, &path, &document_id),
85        "projects/firebase-project/databases/(default)/documents/one/two/three/my-document"
86    );
87}
88
89#[test]
90fn it_document_name_joins_invalid_path_fragments() {
91    let project_id = "firebase-project";
92    let path = "one/two//three/";
93    let document_id = "///my-document";
94    assert_eq!(
95        document_name(&project_id, &path, &document_id),
96        "projects/firebase-project/databases/(default)/documents/one/two//three/////my-document"
97    );
98}