Skip to content

Commit de2ec0a

Browse files
David Graeffdavidgraeff
David Graeff
authored andcommitted
Fix examples, tests and update rocket integration
Signed-off-by: David Graeff <[email protected]>
1 parent f0e4e11 commit de2ec0a

16 files changed

+147
-123
lines changed

CHANGELOG.md

+5-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.8.0] - 2024-01-22
10+
911
### Added
1012

1113
- Credentials::download_google_jwks(): Update/replace public keys. Useful for long running services.
@@ -14,14 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1416

1517
### Changed
1618

17-
- Support for reqwest 0.11 / Tokio 1.0
18-
- Prefer to use `AsRef<str>` when passing params
19-
- Add ability to read raw document contents (without deserializing the JSON)
20-
- [Breaking] Change Credentials::new: No JWKSet parameter, use with_jwkset or download_jwkset
21-
- [Breaking] Change Credentials::from_file: Do not download jwks anymore. Use with_jwkset or download_jwkset.
22-
- [Breaking] Rename JWKSetDTO to JWKSet
23-
- [Breaking] jwt::download_google_jwks returns a string and not a DTO anymore for better error reporting
24-
- [Breaking] jwt::download_google_jwks_async is behind the unstable feature now, as originally intended
19+
- [Breaking] Async only API
20+
- [Breaking] Rocket example uses the Rocket 0.5 release
21+
- [Breaking] Migrated to Rust edition 2021
2522

2623
## [0.6.1] - 2020-11-12
2724

Cargo.toml

+10-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "firestore-db-and-auth"
33
version = "0.8.0"
44
authors = ["David Gräff <[email protected]>"]
5-
edition = "2018"
5+
edition = "2021"
66
license = "MIT"
77
description = "This crate allows easy access to your Google Firestore DB via service account or OAuth impersonated Google Firebase Auth credentials."
88
readme = "readme.md"
@@ -14,21 +14,24 @@ repository = "https://github.com/davidgraeff/firestore-db-and-auth-rs"
1414
[dependencies]
1515
bytes = "1.1"
1616
cache_control = "0.2"
17-
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking"] }
17+
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking", "hyper-rustls"] }
1818
serde = { version = "1.0", features = ["derive"] }
1919
serde_json = "1.0"
2020
chrono = { version = "0.4", features = ["serde"] }
21-
biscuit = "0.5"
22-
ring = "0.16"
23-
base64 = "0.13"
21+
biscuit = "0.7"
22+
ring = "0.17"
23+
base64 = "0.21"
2424
async-trait = "0.1"
2525
tokio = { version = "1.13", features = ["macros"] }
2626
futures = "0.3"
2727
pin-project = "1.0"
28-
http = "0.2"
28+
http = "1.0"
29+
30+
[dev-dependencies]
31+
tokio-test = "0.4"
2932

3033
[dependencies.rocket]
31-
version = "0.4.6"
34+
version = "0.5.0"
3235
default-features = false
3336
optional = true
3437

examples/create_read_write_document.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ async fn main() -> errors::Result<()> {
205205

206206
// Only download the public keys once, and cache them.
207207
let jwkset = utils::from_cache_file(credential_file.with_file_name("cached_jwks.jwks").as_path(), &cred).await?;
208-
cred.add_jwks_public_keys(&jwkset);
208+
cred.add_jwks_public_keys(&jwkset).await;
209209
cred.verify().await?;
210210

211211
// Perform some db operations via a service account session
@@ -225,12 +225,12 @@ async fn valid_test_credentials() -> errors::Result<Credentials> {
225225
let mut jwks_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
226226
jwks_path.push("firebase-service-account.jwks");
227227

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

230230
// Only download the public keys once, and cache them.
231231
let jwkset = utils::from_cache_file(jwks_path.as_path(), &cred).await?;
232-
cred.add_jwks_public_keys(&jwkset);
233-
cred.verify()?;
232+
cred.add_jwks_public_keys(&jwkset).await;
233+
cred.verify().await?;
234234

235235
Ok(cred)
236236
}

examples/own_auth.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use firestore_db_and_auth::errors::FirebaseError::APIError;
12
use firestore_db_and_auth::{documents, errors, Credentials, FirebaseAuthBearer};
23

34
/// Define your own structure that will implement the FirebaseAuthBearer trait
@@ -29,8 +30,7 @@ impl FirebaseAuthBearer for MyOwnSession {
2930
}
3031
}
3132

32-
#[tokio::main]
33-
async fn main() -> errors::Result<()> {
33+
async fn run() -> errors::Result<()> {
3434
let credentials = Credentials::from_file("firebase-service-account.json").await?;
3535
#[derive(serde::Serialize)]
3636
struct TestData {
@@ -56,9 +56,14 @@ async fn main() -> errors::Result<()> {
5656
Ok(())
5757
}
5858

59+
#[tokio::main]
60+
async fn main() -> errors::Result<()> {
61+
run().await
62+
}
63+
5964
#[tokio::test]
6065
async fn own_auth_test() {
61-
if let Err(APIError(code, str_code, context)) = main() {
66+
if let Err(APIError(code, str_code, context)) = run().await {
6267
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.");
6368
assert_eq!(context, "test_doc");
6469
assert_eq!(code, 401);

examples/session_cookie.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async fn main() -> Result<(), FirebaseError> {
1515

1616
// Only download the public keys once, and cache them.
1717
let jwkset = utils::from_cache_file(credential_file.with_file_name("cached_jwks.jwks").as_path(), &cred).await?;
18-
cred.add_jwks_public_keys(&jwkset);
18+
cred.add_jwks_public_keys(&jwkset).await;
1919
cred.verify().await?;
2020

2121
let user_session = utils::user_session_with_cached_refresh_token(&cred).await?;

examples/utils/mod.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ pub async fn from_cache_file(cache_file: &std::path::Path, c: &Credentials) -> e
4646
} else {
4747
// If not present, download the two jwks (specific service account + google system account),
4848
// merge them into one set of keys and store them in the cache file.
49-
let mut jwks = JWKSet::new(&download_google_jwks(&c.client_email).await?.0)?;
50-
jwks.keys
51-
.append(&mut JWKSet::new(&download_google_jwks("[email protected]").await?.0)?.keys);
49+
let jwk_set_1 = download_google_jwks(&c.client_email).await?;
50+
let jwk_set_2 = download_google_jwks("[email protected]").await?;
51+
52+
let mut jwks = JWKSet::new(&jwk_set_1.0)?;
53+
jwks.keys.append(&mut JWKSet::new(&jwk_set_2.0)?.keys);
5254
let f = File::create(cache_file)?;
5355
serde_json::to_writer_pretty(f, &jwks)?;
5456
jwks
@@ -64,12 +66,12 @@ pub async fn valid_test_credentials() -> errors::Result<Credentials> {
6466
let mut jwks_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
6567
jwks_path.push("firebase-service-account.jwks");
6668

67-
let mut cred: Credentials = Credentials::new(include_str!("../../tests/service-account-test.json"))?;
69+
let cred: Credentials = Credentials::new(include_str!("../../firebase-service-account.json")).await?;
6870

6971
// Only download the public keys once, and cache them.
7072
let jwkset = from_cache_file(jwks_path.as_path(), &cred).await?;
71-
cred.add_jwks_public_keys(&jwkset);
72-
cred.verify()?;
73+
cred.add_jwks_public_keys(&jwkset).await;
74+
cred.verify().await?;
7375

7476
Ok(cred)
7577
}

readme.md

+32-38
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This crate allows easy access to your Google Firestore DB via service account or
1111
Minimum Rust version: 1.38
1212

1313
Features:
14+
* Asynchronous API
1415
* Subset of the Firestore v1 API
1516
* Optionally handles authentication and token refreshing for you
1617
* 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:
3637

3738
This crate operates on DTOs (Data transfer objects) for type-safe operations on your Firestore DB.
3839

39-
```rust
40+
```rust,no_run
4041
use firestore_db_and_auth::{Credentials, ServiceSession, documents, errors::Result};
4142
use serde::{Serialize,Deserialize};
4243
@@ -78,34 +79,36 @@ fn write_partial(session: &ServiceSession) -> Result<()> {
7879

7980
Read the document with the id "service_test" from the Firestore "tests" collection:
8081

81-
```rust
82+
```rust,no_run
83+
use firestore_db_and_auth::{documents};
8284
let obj : DemoDTO = documents::read(&session, "tests", "service_test")?;
8385
```
8486

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:
8789

88-
```rust
90+
```rust,no_run
8991
use firestore_db_and_auth::{documents};
9092
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 {
9395
// 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;
9598
println!("{:?}", doc);
9699
}
97100
```
98101

99102
*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.
101104

102-
For quering the database you would use the `query` method.
105+
For querying the database you would use the `query` method.
103106
In the following example the collection "tests" is queried for document(s) with the "id" field equal to "Sam Weiss".
104107

105-
```rust
108+
```rust,no_run
106109
use firestore_db_and_auth::{documents, dto};
107110
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?;
109112
for metadata in values {
110113
println!("id: {}, created: {}, updated: {}", metadata.name.as_ref().unwrap(), metadata.create_time.as_ref().unwrap(), metadata.update_time.as_ref().unwrap());
111114
// Fetch the actual document
@@ -128,10 +131,10 @@ This custom error type wraps all possible errors (IO, Reqwest, JWT errors etc)
128131
and Google REST API errors. If you want to specifically check for an API error,
129132
you could do so:
130133

131-
```rust
134+
```rust,no_run
132135
use firestore_db_and_auth::{documents, errors::FirebaseError};
133136
134-
let r = documents::delete(&session, "tests/non_existing", true);
137+
let r = documents::delete(&session, "tests/non_existing", true).await;
135138
if let Err(e) = r.err() {
136139
if let FirebaseError::APIError(code, message, context) = e {
137140
assert_eq!(code, 404);
@@ -151,13 +154,13 @@ It may be the collection or document id or any other context information.
151154
The file should contain `"private_key_id": ...`.
152155
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".
153156

154-
```rust
157+
```rust,no_run
155158
use firestore_db_and_auth::{Credentials, ServiceSession};
156159
157160
/// 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
159162
.expect("Read credentials file")
160-
.download_jwkset()
163+
.download_jwkset().await
161164
.expect("Failed to download public keys");
162165
/// To use any of the Firestore methods, you need a session first. You either want
163166
/// an impersonated session bound to a Firebase Auth user or a service account session.
@@ -170,33 +173,33 @@ let session = ServiceSession::new(&cred)
170173
You can create a user session in various ways.
171174
If you just have the firebase Auth user_id, you would follow these steps:
172175

173-
```rust
176+
```rust,no_run
174177
use firestore_db_and_auth::{Credentials, sessions};
175178
176179
/// 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
178181
.expect("Read credentials file")
179-
.download_jwkset()
182+
.download_jwkset().await
180183
.expect("Failed to download public keys");
181184
182185
/// To use any of the Firestore methods, you need a session first.
183186
/// 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
185188
.expect("Create a user session");
186189
```
187190

188191
If you already have a valid refresh token and want to generate an access token (and a session object), you do this instead:
189192

190-
```rust
193+
```rust,no_run
191194
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?;
193196
```
194197

195198
Another way of retrieving a session object is by providing a valid access token like so:
196199

197-
```rust
200+
```rust,no_run
198201
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?;
200203
```
201204

202205
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:
220223

221224
Create a `Credentials` object like so:
222225

223-
```rust
226+
```rust,no_run
224227
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?;
227230
```
228231

229232
> Please note though, that Googles JWK keys change periodically.
230233
> You probably want to redeploy your service with fresh public keys about every three weeks.
231234
>
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.
233236
234237
### More information
235238

@@ -244,19 +247,10 @@ To perform a full integration test (`cargo test`), you need a valid "firebase-se
244247
The tests expect a Firebase user with the ID given in `examples/test_user_id.txt` to exist.
245248
[More Information](/doc/integration_tests.md)
246249

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-
255250
#### What can be done to make this crate more awesome
256251

257252
This library does not have the ambition to mirror the http/gRPC API 1:1.
258253
There are auto-generated libraries for this purpose. But the following fits into the crates schema:
259254

260-
* Data streaming via gRPC/Protobuf
261255
* Nice to have: Transactions, batch_get support for Firestore
262256

0 commit comments

Comments
 (0)