Skip to content

Commit f0e4e11

Browse files
mkawalecdavidgraeff
andcommitted
Migrate API to async
* Uses Tokio 1.x and a recent reqwest * Migrate a few examples * Move some APIs from iterator to async stream Co-authored-by: David Gräff <[email protected]> Signed-off-by: Michał Kawalec <[email protected]> Signed-off-by: David Gräff <[email protected]>
1 parent beedaf3 commit f0e4e11

20 files changed

+530
-430
lines changed

Cargo.toml

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "firestore-db-and-auth"
3-
version = "0.6.1"
3+
version = "0.8.0"
44
authors = ["David Gräff <[email protected]>"]
55
edition = "2018"
66
license = "MIT"
@@ -12,13 +12,20 @@ maintenance = { status = "passively-maintained" }
1212
repository = "https://github.com/davidgraeff/firestore-db-and-auth-rs"
1313

1414
[dependencies]
15+
bytes = "1.1"
16+
cache_control = "0.2"
1517
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking"] }
1618
serde = { version = "1.0", features = ["derive"] }
1719
serde_json = "1.0"
1820
chrono = { version = "0.4", features = ["serde"] }
1921
biscuit = "0.5"
2022
ring = "0.16"
2123
base64 = "0.13"
24+
async-trait = "0.1"
25+
tokio = { version = "1.13", features = ["macros"] }
26+
futures = "0.3"
27+
pin-project = "1.0"
28+
http = "0.2"
2229

2330
[dependencies.rocket]
2431
version = "0.4.6"

examples/create_read_write_document.rs

+54-41
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use firestore_db_and_auth::{documents, dto, errors, sessions, Credentials, Fireb
33
use firestore_db_and_auth::documents::WriteResult;
44
use serde::{Deserialize, Serialize};
55

6+
use futures::stream::StreamExt;
7+
68
mod utils;
79

810
#[derive(Debug, Serialize, Deserialize)]
@@ -21,7 +23,7 @@ struct DemoDTOPartial {
2123
an_int: u32,
2224
}
2325

24-
fn write_document(session: &mut ServiceSession, doc_id: &str) -> errors::Result<WriteResult> {
26+
async fn write_document(session: &mut ServiceSession, doc_id: &str) -> errors::Result<WriteResult> {
2527
println!("Write document");
2628

2729
let obj = DemoDTO {
@@ -30,10 +32,10 @@ fn write_document(session: &mut ServiceSession, doc_id: &str) -> errors::Result<
3032
a_timestamp: chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, true),
3133
};
3234

33-
documents::write(session, "tests", Some(doc_id), &obj, documents::WriteOptions::default())
35+
documents::write(session, "tests", Some(doc_id), &obj, documents::WriteOptions::default()).await
3436
}
3537

36-
fn write_partial_document(session: &mut ServiceSession, doc_id: &str) -> errors::Result<WriteResult> {
38+
async fn write_partial_document(session: &mut ServiceSession, doc_id: &str) -> errors::Result<WriteResult> {
3739
println!("Partial write document");
3840

3941
let obj = DemoDTOPartial {
@@ -48,6 +50,7 @@ fn write_partial_document(session: &mut ServiceSession, doc_id: &str) -> errors:
4850
&obj,
4951
documents::WriteOptions { merge: true },
5052
)
53+
.await
5154
}
5255

5356
fn check_write(result: WriteResult, doc_id: &str) {
@@ -62,25 +65,25 @@ fn check_write(result: WriteResult, doc_id: &str) {
6265
);
6366
}
6467

65-
fn service_account_session(cred: Credentials) -> errors::Result<()> {
66-
let mut session = ServiceSession::new(cred).unwrap();
67-
let b = session.access_token().to_owned();
68+
async fn service_account_session(cred: Credentials) -> errors::Result<()> {
69+
let mut session = ServiceSession::new(cred).await.unwrap();
70+
let b = session.access_token().await.to_owned();
6871

6972
let doc_id = "service_test";
70-
check_write(write_document(&mut session, doc_id)?, doc_id);
73+
check_write(write_document(&mut session, doc_id).await?, doc_id);
7174

7275
// Check if cached value is used
73-
assert_eq!(session.access_token(), b);
76+
assert_eq!(session.access_token().await, b);
7477

7578
println!("Read and compare document");
76-
let read: DemoDTO = documents::read(&mut session, "tests", doc_id)?;
79+
let read: DemoDTO = documents::read(&mut session, "tests", doc_id).await?;
7780

7881
assert_eq!(read.a_string, "abcd");
7982
assert_eq!(read.an_int, 14);
8083

81-
check_write(write_partial_document(&mut session, doc_id)?, doc_id);
84+
check_write(write_partial_document(&mut session, doc_id).await?, doc_id);
8285
println!("Read and compare document");
83-
let read: DemoDTOPartial = documents::read(&mut session, "tests", doc_id)?;
86+
let read: DemoDTOPartial = documents::read(&mut session, "tests", doc_id).await?;
8487

8588
// Should be updated
8689
assert_eq!(read.an_int, 16);
@@ -90,14 +93,15 @@ fn service_account_session(cred: Credentials) -> errors::Result<()> {
9093
Ok(())
9194
}
9295

93-
fn user_account_session(cred: Credentials) -> errors::Result<()> {
94-
let user_session = utils::user_session_with_cached_refresh_token(&cred)?;
96+
async fn user_account_session(cred: Credentials) -> errors::Result<()> {
97+
let user_session = utils::user_session_with_cached_refresh_token(&cred).await?;
9598

9699
assert_eq!(user_session.user_id, utils::TEST_USER_ID);
97100
assert_eq!(user_session.project_id(), cred.project_id);
98101

99102
println!("user::Session::by_access_token");
100-
let user_session = sessions::user::Session::by_access_token(&cred, &user_session.access_token_unchecked())?;
103+
let user_session =
104+
sessions::user::Session::by_access_token(&cred, &user_session.access_token_unchecked().await).await?;
101105

102106
assert_eq!(user_session.user_id, utils::TEST_USER_ID);
103107

@@ -117,13 +121,14 @@ fn user_account_session(cred: Credentials) -> errors::Result<()> {
117121
Some(doc_id),
118122
&obj,
119123
documents::WriteOptions::default(),
120-
)?,
124+
)
125+
.await?,
121126
doc_id,
122127
);
123128

124129
// Test reading
125130
println!("user::Session documents::read");
126-
let read: DemoDTO = documents::read(&user_session, "tests", doc_id)?;
131+
let read: DemoDTO = documents::read(&user_session, "tests", doc_id).await?;
127132

128133
assert_eq!(read.a_string, "abc");
129134
assert_eq!(read.an_int, 12);
@@ -135,22 +140,25 @@ fn user_account_session(cred: Credentials) -> errors::Result<()> {
135140
"abc".into(),
136141
dto::FieldOperator::EQUAL,
137142
"a_string",
138-
)?
143+
)
144+
.await?
139145
.collect();
140146
assert_eq!(results.len(), 1);
141-
let doc: DemoDTO = documents::read_by_name(&user_session, &results.get(0).unwrap().name)?;
147+
let doc: DemoDTO = documents::read_by_name(&user_session, &results.get(0).unwrap().name).await?;
142148
assert_eq!(doc.a_string, "abc");
143149

144150
let mut count = 0;
145-
let list_it: documents::List<DemoDTO, _> = documents::list(&user_session, "tests".to_owned());
151+
let list_it = documents::list(&user_session, "tests".to_owned())
152+
.collect::<Vec<errors::Result<(DemoDTO, _)>>>()
153+
.await;
146154
for _doc in list_it {
147155
count += 1;
148156
}
149157
assert_eq!(count, 2);
150158

151159
// test if the call fails for a non existing document
152160
println!("user::Session documents::delete");
153-
let r = documents::delete(&user_session, "tests/non_existing", true);
161+
let r = documents::delete(&user_session, "tests/non_existing", true).await;
154162
assert!(r.is_err());
155163
match r.err().unwrap() {
156164
errors::FirebaseError::APIError(code, message, context) => {
@@ -161,7 +169,7 @@ fn user_account_session(cred: Credentials) -> errors::Result<()> {
161169
_ => panic!("Expected an APIError"),
162170
};
163171

164-
documents::delete(&user_session, &("tests/".to_owned() + doc_id), false)?;
172+
documents::delete(&user_session, &("tests/".to_owned() + doc_id), false).await?;
165173

166174
// Check if document is indeed removed
167175
println!("user::Session documents::query");
@@ -171,71 +179,76 @@ fn user_account_session(cred: Credentials) -> errors::Result<()> {
171179
"abc".into(),
172180
dto::FieldOperator::EQUAL,
173181
"a_string",
174-
)?
182+
)
183+
.await?
175184
.count();
176185
assert_eq!(count, 0);
177186

178187
println!("user::Session documents::query for f64");
179188
let f: f64 = 13.37;
180-
let count = documents::query(&user_session, "tests", f.into(), dto::FieldOperator::EQUAL, "a_float")?.count();
189+
190+
let count = documents::query(&user_session, "tests", f.into(), dto::FieldOperator::EQUAL, "a_float").await?;
191+
192+
let count = count.count();
181193
assert_eq!(count, 0);
182194

183195
Ok(())
184196
}
185197

186-
fn main() -> errors::Result<()> {
198+
#[tokio::main]
199+
async fn main() -> errors::Result<()> {
187200
// Search for a credentials file in the root directory
188201
use std::path::PathBuf;
189202
let mut credential_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
190203
credential_file.push("firebase-service-account.json");
191-
let mut cred = Credentials::from_file(credential_file.to_str().unwrap())?;
204+
let cred = Credentials::from_file(credential_file.to_str().unwrap()).await?;
192205

193206
// Only download the public keys once, and cache them.
194-
let jwkset = utils::from_cache_file(credential_file.with_file_name("cached_jwks.jwks").as_path(), &cred)?;
207+
let jwkset = utils::from_cache_file(credential_file.with_file_name("cached_jwks.jwks").as_path(), &cred).await?;
195208
cred.add_jwks_public_keys(&jwkset);
196-
cred.verify()?;
209+
cred.verify().await?;
197210

198211
// Perform some db operations via a service account session
199-
service_account_session(cred.clone())?;
212+
service_account_session(cred.clone()).await?;
200213

201214
// Perform some db operations via a firebase user session
202-
user_account_session(cred)?;
215+
user_account_session(cred).await?;
203216

204217
Ok(())
205218
}
206219

207220
/// For integration tests and doc code snippets: Create a Credentials instance.
208221
/// Necessary public jwk sets are downloaded or re-used if already present.
209222
#[cfg(test)]
210-
fn valid_test_credentials() -> errors::Result<Credentials> {
223+
async fn valid_test_credentials() -> errors::Result<Credentials> {
211224
use std::path::PathBuf;
212225
let mut jwks_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
213226
jwks_path.push("firebase-service-account.jwks");
214227

215-
let mut cred: Credentials = Credentials::new(include_str!("../firebase-service-account.json"))?;
228+
let mut cred: Credentials = Credentials::new(include_str!("../tests/service-account-test.json"))?;
216229

217230
// Only download the public keys once, and cache them.
218-
let jwkset = utils::from_cache_file(jwks_path.as_path(), &cred)?;
231+
let jwkset = utils::from_cache_file(jwks_path.as_path(), &cred).await?;
219232
cred.add_jwks_public_keys(&jwkset);
220233
cred.verify()?;
221234

222235
Ok(cred)
223236
}
224237

225-
#[test]
226-
fn valid_test_credentials_test() -> errors::Result<()> {
227-
valid_test_credentials()?;
238+
#[tokio::test]
239+
async fn valid_test_credentials_test() -> errors::Result<()> {
240+
valid_test_credentials().await?;
228241
Ok(())
229242
}
230243

231-
#[test]
232-
fn service_account_session_test() -> errors::Result<()> {
233-
service_account_session(valid_test_credentials()?)?;
244+
#[tokio::test]
245+
async fn service_account_session_test() -> errors::Result<()> {
246+
service_account_session(valid_test_credentials().await?).await?;
234247
Ok(())
235248
}
236249

237-
#[test]
238-
fn user_account_session_test() -> errors::Result<()> {
239-
user_account_session(valid_test_credentials()?)?;
250+
#[tokio::test]
251+
async fn user_account_session_test() -> errors::Result<()> {
252+
user_account_session(valid_test_credentials().await?).await?;
240253
Ok(())
241254
}

examples/firebase_user.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ use firestore_db_and_auth::*;
33

44
const TEST_USER_ID: &str = include_str!("test_user_id.txt");
55

6-
fn main() -> errors::Result<()> {
7-
let cred = Credentials::from_file("firebase-service-account.json").expect("Read credentials file");
6+
#[tokio::main]
7+
async fn main() -> errors::Result<()> {
8+
let cred = Credentials::from_file("firebase-service-account.json")
9+
.await
10+
.expect("Read credentials file");
811

9-
let user_session = UserSession::by_user_id(&cred, TEST_USER_ID, false)?;
12+
let user_session = UserSession::by_user_id(&cred, TEST_USER_ID, false).await?;
1013

1114
println!("users::user_info");
12-
let user_info_container = users::user_info(&user_session)?;
15+
let user_info_container = users::user_info(&user_session).await?;
1316
assert_eq!(user_info_container.users[0].localId.as_ref().unwrap(), TEST_USER_ID);
1417

1518
Ok(())

examples/own_auth.rs

+11-15
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,37 @@
1-
use firestore_db_and_auth::errors::FirebaseError::APIError;
21
use firestore_db_and_auth::{documents, errors, Credentials, FirebaseAuthBearer};
32

43
/// Define your own structure that will implement the FirebaseAuthBearer trait
54
struct MyOwnSession {
65
/// The google credentials
76
pub credentials: Credentials,
8-
pub blocking_client: reqwest::blocking::Client,
97
pub client: reqwest::Client,
108
access_token: String,
119
}
1210

11+
#[async_trait::async_trait]
1312
impl FirebaseAuthBearer for MyOwnSession {
1413
fn project_id(&self) -> &str {
1514
&self.credentials.project_id
1615
}
1716
/// An access token. If a refresh token is known and the access token expired,
1817
/// the implementation should try to refresh the access token before returning.
19-
fn access_token(&self) -> String {
18+
async fn access_token(&self) -> String {
2019
self.access_token.clone()
2120
}
2221
/// The access token, unchecked. Might be expired or in other ways invalid.
23-
fn access_token_unchecked(&self) -> String {
22+
async fn access_token_unchecked(&self) -> String {
2423
self.access_token.clone()
2524
}
2625
/// The reqwest http client.
2726
/// The `Client` holds a connection pool internally, so it is advised that it is reused for multiple, successive connections.
28-
fn client(&self) -> &reqwest::blocking::Client {
29-
&self.blocking_client
30-
}
31-
32-
fn client_async(&self) -> &reqwest::Client {
27+
fn client(&self) -> &reqwest::Client {
3328
&self.client
3429
}
3530
}
3631

37-
fn main() -> errors::Result<()> {
38-
let credentials = Credentials::from_file("firebase-service-account.json")?;
32+
#[tokio::main]
33+
async fn main() -> errors::Result<()> {
34+
let credentials = Credentials::from_file("firebase-service-account.json").await?;
3935
#[derive(serde::Serialize)]
4036
struct TestData {
4137
an_int: u32,
@@ -44,7 +40,6 @@ fn main() -> errors::Result<()> {
4440

4541
let session = MyOwnSession {
4642
credentials,
47-
blocking_client: reqwest::blocking::Client::new(),
4843
client: reqwest::Client::new(),
4944
access_token: "The access token".to_owned(),
5045
};
@@ -56,12 +51,13 @@ fn main() -> errors::Result<()> {
5651
Some("test_doc"),
5752
&t,
5853
documents::WriteOptions::default(),
59-
)?;
54+
)
55+
.await?;
6056
Ok(())
6157
}
6258

63-
#[test]
64-
fn own_auth_test() {
59+
#[tokio::test]
60+
async fn own_auth_test() {
6561
if let Err(APIError(code, str_code, context)) = main() {
6662
assert_eq!(str_code, "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.");
6763
assert_eq!(context, "test_doc");

examples/session_cookie.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@ use chrono::Duration;
44

55
mod utils;
66

7-
fn main() -> Result<(), FirebaseError> {
7+
#[tokio::main]
8+
async fn main() -> Result<(), FirebaseError> {
89
// Search for a credentials file in the root directory
910
use std::path::PathBuf;
1011

1112
let mut credential_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1213
credential_file.push("firebase-service-account.json");
13-
let mut cred = Credentials::from_file(credential_file.to_str().unwrap())?;
14+
let cred = Credentials::from_file(credential_file.to_str().unwrap()).await?;
1415

1516
// Only download the public keys once, and cache them.
16-
let jwkset = utils::from_cache_file(credential_file.with_file_name("cached_jwks.jwks").as_path(), &cred)?;
17+
let jwkset = utils::from_cache_file(credential_file.with_file_name("cached_jwks.jwks").as_path(), &cred).await?;
1718
cred.add_jwks_public_keys(&jwkset);
18-
cred.verify()?;
19+
cred.verify().await?;
1920

20-
let user_session = utils::user_session_with_cached_refresh_token(&cred)?;
21+
let user_session = utils::user_session_with_cached_refresh_token(&cred).await?;
2122

22-
let cookie = session_cookie::create(&cred, user_session.access_token(), Duration::seconds(3600))?;
23+
let cookie = session_cookie::create(&cred, user_session.access_token().await, Duration::seconds(3600)).await?;
2324
println!("Created session cookie: {}", cookie);
2425

2526
Ok(())

0 commit comments

Comments
 (0)