@@ -11,6 +11,7 @@ This crate allows easy access to your Google Firestore DB via service account or
11
11
Minimum Rust version: 1.38
12
12
13
13
Features:
14
+ * Asynchronous API
14
15
* Subset of the Firestore v1 API
15
16
* Optionally handles authentication and token refreshing for you
16
17
* Support for the downloadable Google service account json file from [ Google Clound console] ( https://console.cloud.google.com/apis/credentials/serviceaccountkey ) .
@@ -36,7 +37,7 @@ Limitations:
36
37
37
38
This crate operates on DTOs (Data transfer objects) for type-safe operations on your Firestore DB.
38
39
39
- ``` rust
40
+ ``` rust,no_run
40
41
use firestore_db_and_auth::{Credentials, ServiceSession, documents, errors::Result};
41
42
use serde::{Serialize,Deserialize};
42
43
@@ -78,34 +79,36 @@ fn write_partial(session: &ServiceSession) -> Result<()> {
78
79
79
80
Read the document with the id "service_test" from the Firestore "tests" collection:
80
81
81
- ``` rust
82
+ ``` rust,no_run
83
+ use firestore_db_and_auth::{documents};
82
84
let obj : DemoDTO = documents::read(&session, "tests", "service_test")?;
83
85
```
84
86
85
- For listing all documents of the "tests" collection you want to use the ` List ` struct which implements the ` Iterator ` trait .
86
- It will hide the complexity of the paging API and fetches new documents when necessary:
87
+ For listing all documents of the "tests" collection you want to use the ` list ` method which implements an async stream .
88
+ This hide the complexity of the paging API and fetches new documents when necessary:
87
89
88
- ``` rust
90
+ ``` rust,no_run
89
91
use firestore_db_and_auth::{documents};
90
92
91
- let values : documents :: List < DemoDTO , _ > = documents :: list (& session , " tests" );
92
- for doc_result in values {
93
+ let mut stream = documents::list(&session, "tests");
94
+ while let Some(Ok( doc_result)) = stream.next().await {
93
95
// The document is wrapped in a Result<> because fetching new data could have failed
94
- let (doc , _metadata ) = doc_result ? ;
96
+ let (doc, _metadata) = doc_result;
97
+ let doc: DemoDTO = doc;
95
98
println!("{:?}", doc);
96
99
}
97
100
```
98
101
99
102
* Note:* The resulting list or list cursor is a snapshot view with a limited lifetime.
100
- You cannot keep the iterator for long or expect new documents to appear in an ongoing iteration.
103
+ You cannot keep the iterator/stream for long or expect new documents to appear in an ongoing iteration.
101
104
102
- For quering the database you would use the ` query ` method.
105
+ For querying the database you would use the ` query ` method.
103
106
In the following example the collection "tests" is queried for document(s) with the "id" field equal to "Sam Weiss".
104
107
105
- ``` rust
108
+ ``` rust,no_run
106
109
use firestore_db_and_auth::{documents, dto};
107
110
108
- let values = documents :: query (& session , " tests" , " Sam Weiss" . into (), dto :: FieldOperator :: EQUAL , " id" )? ;
111
+ let values = documents::query(&session, "tests", "Sam Weiss".into(), dto::FieldOperator::EQUAL, "id").await ?;
109
112
for metadata in values {
110
113
println!("id: {}, created: {}, updated: {}", metadata.name.as_ref().unwrap(), metadata.create_time.as_ref().unwrap(), metadata.update_time.as_ref().unwrap());
111
114
// Fetch the actual document
@@ -128,10 +131,10 @@ This custom error type wraps all possible errors (IO, Reqwest, JWT errors etc)
128
131
and Google REST API errors. If you want to specifically check for an API error,
129
132
you could do so:
130
133
131
- ``` rust
134
+ ``` rust,no_run
132
135
use firestore_db_and_auth::{documents, errors::FirebaseError};
133
136
134
- let r = documents :: delete (& session , " tests/non_existing" , true );
137
+ let r = documents::delete(&session, "tests/non_existing", true).await ;
135
138
if let Err(e) = r.err() {
136
139
if let FirebaseError::APIError(code, message, context) = e {
137
140
assert_eq!(code, 404);
@@ -151,13 +154,13 @@ It may be the collection or document id or any other context information.
151
154
The file should contain ` "private_key_id": ... ` .
152
155
2 . Add another field ` "api_key" : "YOUR_API_KEY" ` and replace YOUR_API_KEY with your * Web API key* , to be found in the [ Google Firebase console] ( https://console.firebase.google.com ) in "Project Overview -> Settings - > General".
153
156
154
- ``` rust
157
+ ``` rust,no_run
155
158
use firestore_db_and_auth::{Credentials, ServiceSession};
156
159
157
160
/// Create credentials object. You may as well do that programmatically.
158
- let cred = Credentials :: from_file (" firebase-service-account.json" )
161
+ let cred = Credentials::from_file("firebase-service-account.json").await
159
162
.expect("Read credentials file")
160
- . download_jwkset ()
163
+ .download_jwkset().await
161
164
.expect("Failed to download public keys");
162
165
/// To use any of the Firestore methods, you need a session first. You either want
163
166
/// an impersonated session bound to a Firebase Auth user or a service account session.
@@ -170,33 +173,33 @@ let session = ServiceSession::new(&cred)
170
173
You can create a user session in various ways.
171
174
If you just have the firebase Auth user_id, you would follow these steps:
172
175
173
- ``` rust
176
+ ``` rust,no_run
174
177
use firestore_db_and_auth::{Credentials, sessions};
175
178
176
179
/// Create credentials object. You may as well do that programmatically.
177
- let cred = Credentials :: from_file (" firebase-service-account.json" )
180
+ let cred = Credentials::from_file("firebase-service-account.json").await
178
181
.expect("Read credentials file")
179
- . download_jwkset ()
182
+ .download_jwkset().await
180
183
.expect("Failed to download public keys");
181
184
182
185
/// To use any of the Firestore methods, you need a session first.
183
186
/// Create an impersonated session bound to a Firebase Auth user via your service account credentials.
184
- let session = UserSession :: by_user_id (& cred , " the_user_id" )
187
+ let session = UserSession::by_user_id(&cred, "the_user_id").await
185
188
.expect("Create a user session");
186
189
```
187
190
188
191
If you already have a valid refresh token and want to generate an access token (and a session object), you do this instead:
189
192
190
- ``` rust
193
+ ``` rust,no_run
191
194
let refresh_token = "fkjandsfbajsbfd;asbfdaosa.asduabsifdabsda,fd,a,sdbasfadfasfas.dasdasbfadusbflansf";
192
- let session = UserSession :: by_refresh_token (& cred , & refresh_token )? ;
195
+ let session = UserSession::by_refresh_token(&cred, &refresh_token).await ?;
193
196
```
194
197
195
198
Another way of retrieving a session object is by providing a valid access token like so:
196
199
197
- ``` rust
200
+ ``` rust,no_run
198
201
let access_token = "fkjandsfbajsbfd;asbfdaosa.asduabsifdabsda,fd,a,sdbasfadfasfas.dasdasbfadusbflansf";
199
- let session = UserSession :: by_access_token (& cred , & access_token )? ;
202
+ let session = UserSession::by_access_token(&cred, &access_token).await ?;
200
203
```
201
204
202
205
The ` by_access_token ` method will fail if the token is not valid anymore.
@@ -220,16 +223,16 @@ First download the 2 public key files:
220
223
221
224
Create a ` Credentials ` object like so:
222
225
223
- ``` rust
226
+ ``` rust,no_run
224
227
use firestore_db_and_auth::Credentials;
225
- let c = Credentials :: new (include_str! (" firebase-service-account.json" ))?
226
- . with_jwkset (& JWKSet :: new (include_str! (" firebase-service-account.jwks" ))? )? ;
228
+ let c = Credentials::new(include_str!("firebase-service-account.json")).await ?
229
+ .with_jwkset(&JWKSet::new(include_str!("firebase-service-account.jwks"))?).await ?;
227
230
```
228
231
229
232
> Please note though, that Googles JWK keys change periodically.
230
233
> You probably want to redeploy your service with fresh public keys about every three weeks.
231
234
>
232
- > For long running service you want to call Credentials::download_google_jwks() periodically.
235
+ > For long- running service you want to call Credentials::download_google_jwks() periodically.
233
236
234
237
### More information
235
238
@@ -244,19 +247,10 @@ To perform a full integration test (`cargo test`), you need a valid "firebase-se
244
247
The tests expect a Firebase user with the ID given in ` examples/test_user_id.txt ` to exist.
245
248
[ More Information] ( /doc/integration_tests.md )
246
249
247
- ## Async vs Sync
248
-
249
- This crate uses reqwest under the hood as http client.
250
- reqwest supports blocking and async/await APIs.
251
-
252
- Right now only blocking APIs are provided, some async/await variants are
253
- gated behind an "unstable" cargo feature.
254
-
255
250
#### What can be done to make this crate more awesome
256
251
257
252
This library does not have the ambition to mirror the http/gRPC API 1:1.
258
253
There are auto-generated libraries for this purpose. But the following fits into the crates schema:
259
254
260
- * Data streaming via gRPC/Protobuf
261
255
* Nice to have: Transactions, batch_get support for Firestore
262
256
0 commit comments