From 433d1d9c84a7fc1f53eb4982ee69e06bff30aa39 Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Mon, 10 Jan 2022 11:05:23 +0100 Subject: [PATCH 1/6] Update dependencies Signed-off-by: Aaron Erhardt --- Cargo.toml | 27 ++++++------ examples/tide/Cargo.toml | 11 +++++ .../src/main.rs} | 3 -- src/lib.rs | 43 +++++++++---------- tests/test.rs | 2 +- 5 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 examples/tide/Cargo.toml rename examples/{mongodb_session_store.rs => tide/src/main.rs} (95%) diff --git a/Cargo.toml b/Cargo.toml index b0e31d1..e3a776c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,33 +1,36 @@ [package] name = "async-mongodb-session" -version = "2.0.0" +version = "3.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/yoshuawuyts/async-mongodb-session" documentation = "https://docs.rs/async-mongodb-session" description = "An async-session implementation for MongoDB" readme = "README.md" -edition = "2018" +edition = "2021" keywords = ["tide", "web", "async", "session", "mongodb"] categories = [ "network-programming", "asynchronous", - "web-programming::http-server" + "web-programming::http-server", ] authors = [ - "Yoshua Wuyts ", - "Irina Shestak ", - "Anton Whalley ", - "Javier Viola " + "Yoshua Wuyts ", + "Irina Shestak ", + "Anton Whalley ", + "Javier Viola ", + "Aaron Erhardt ", ] [features] [dependencies] -mongodb = { version = "1.1.1", default-features = false, features = ["async-std-runtime"] } -async-session = "2.0.0" +async-session = "3" +mongodb = { version = "2.1", default-features = false, features = [ + "async-std-runtime", + "bson-chrono-0_4", +] } [dev-dependencies] -async-std = { version = "1.8.0", features = ["attributes"] } -rand = {version = "0.7.3"} +async-std = { version = "1.10", features = ["attributes"] } lazy_static = "1" -tide = "0.15" +rand = { version = "0.8" } diff --git a/examples/tide/Cargo.toml b/examples/tide/Cargo.toml new file mode 100644 index 0000000..0a75208 --- /dev/null +++ b/examples/tide/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "tide" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-mongodb-session = { path = "../../" } +tide = "0.17.0-beta.1" +async-std = "1.10" diff --git a/examples/mongodb_session_store.rs b/examples/tide/src/main.rs similarity index 95% rename from examples/mongodb_session_store.rs rename to examples/tide/src/main.rs index 355040e..c3aa3a7 100644 --- a/examples/mongodb_session_store.rs +++ b/examples/tide/src/main.rs @@ -1,6 +1,3 @@ -extern crate async_mongodb_session; -extern crate tide; - use async_mongodb_session::MongodbSessionStore; #[async_std::main] diff --git a/src/lib.rs b/src/lib.rs index 37ec7e7..9d9cc2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,11 +13,11 @@ #![forbid(unsafe_code, future_incompatible, rust_2018_idioms)] #![deny(missing_debug_implementations, nonstandard_style)] -#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)] +#![warn(missing_docs, rustdoc::missing_doc_code_examples, unreachable_pub)] use async_session::chrono::{Duration, Utc}; use async_session::{async_trait, Result, Session, SessionStore}; -use mongodb::bson; +use mongodb::bson::{self, Document}; use mongodb::bson::{doc, Bson}; use mongodb::options::{ReplaceOptions, SelectionCriteria}; use mongodb::Client; @@ -25,9 +25,8 @@ use mongodb::Client; /// A MongoDB session store. #[derive(Debug, Clone)] pub struct MongodbSessionStore { - client: mongodb::Client, - db: String, - coll_name: String, + collection: mongodb::Collection, + database: mongodb::Database, } impl MongodbSessionStore { @@ -40,9 +39,9 @@ impl MongodbSessionStore { /// .await?; /// # Ok(()) }) } /// ``` - pub async fn new(uri: &str, db: &str, coll_name: &str) -> mongodb::error::Result { + pub async fn new(uri: &str, db_name: &str, coll_name: &str) -> mongodb::error::Result { let client = Client::with_uri_str(uri).await?; - let middleware = Self::from_client(client, db, coll_name); + let middleware = Self::from_client(client, db_name, coll_name); middleware.create_expire_index("expireAt", 0).await?; Ok(middleware) } @@ -66,16 +65,17 @@ impl MongodbSessionStore { /// let store = MongodbSessionStore::from_client(client, "db_name", "collection"); /// # Ok(()) }) } /// ``` - pub fn from_client(client: Client, db: &str, coll_name: &str) -> Self { + pub fn from_client(client: Client, db_name: &str, coll_name: &str) -> Self { + let database = client.database(db_name); + let collection = database.collection(coll_name); Self { - client, - db: db.to_string(), - coll_name: coll_name.to_string(), + database, + collection, } } /// Initialize the default expiration mechanism, based on the document expiration - /// that mongodb provides https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-at-a-specific-clock-time. + /// that mongodb provides . /// The default ttl applyed to sessions without expiry is 20 minutes. /// If the `expireAt` date field contains a date in the past, mongodb considers the document expired and will be deleted. /// Note: mongodb runs the expiration logic every 60 seconds. @@ -89,7 +89,7 @@ impl MongodbSessionStore { /// # Ok(()) }) } /// ``` pub async fn initialize(&self) -> Result { - &self.index_on_expiry_at().await?; + self.index_on_expiry_at().await?; Ok(()) } @@ -102,7 +102,7 @@ impl MongodbSessionStore { expire_after_seconds: u32, ) -> mongodb::error::Result<()> { let create_index = doc! { - "createIndexes": &self.coll_name, + "createIndexes": self.collection.name(), "indexes": [ { "key" : { field_name: 1 }, @@ -111,8 +111,7 @@ impl MongodbSessionStore { } ] }; - self.client - .database(&self.db) + self.database .run_command( create_index, SelectionCriteria::ReadPreference(mongodb::options::ReadPreference::Primary), @@ -123,7 +122,7 @@ impl MongodbSessionStore { /// Create a new index for the `expireAt` property, allowing to expire sessions at a specific clock time. /// If the `expireAt` date field contains a date in the past, mongodb considers the document expired and will be deleted. - /// https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-at-a-specific-clock-time + /// /// ```rust /// # fn main() -> async_session::Result { async_std::task::block_on(async { /// # use async_mongodb_session::MongodbSessionStore; @@ -142,7 +141,7 @@ impl MongodbSessionStore { #[async_trait] impl SessionStore for MongodbSessionStore { async fn store_session(&self, session: Session) -> Result> { - let coll = self.client.database(&self.db).collection(&self.coll_name); + let coll = &self.collection; let value = bson::to_bson(&session)?; let id = session.id(); @@ -162,7 +161,7 @@ impl SessionStore for MongodbSessionStore { async fn load_session(&self, cookie_value: String) -> Result> { let id = Session::id_from_cookie_value(&cookie_value)?; - let coll = self.client.database(&self.db).collection(&self.coll_name); + let coll = &self.collection; let filter = doc! { "session_id": id }; match coll.find_one(filter, None).await? { None => Ok(None), @@ -175,7 +174,7 @@ impl SessionStore for MongodbSessionStore { // https://docs.mongodb.com/manual/core/index-ttl/#timing-of-the-delete-operation // This prevents those documents being returned if let Some(expiry_at) = doc.get("expireAt").and_then(Bson::as_datetime) { - if expiry_at < &Utc::now() { + if expiry_at.to_chrono() < Utc::now() { return Ok(None); } } @@ -185,14 +184,14 @@ impl SessionStore for MongodbSessionStore { } async fn destroy_session(&self, session: Session) -> Result { - let coll = self.client.database(&self.db).collection(&self.coll_name); + let coll = &self.collection; coll.delete_one(doc! { "session_id": session.id() }, None) .await?; Ok(()) } async fn clear_store(&self) -> Result { - let coll = self.client.database(&self.db).collection(&self.coll_name); + let coll = &self.collection; coll.drop(None).await?; self.initialize().await?; Ok(()) diff --git a/tests/test.rs b/tests/test.rs index 16379c3..ad17166 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -17,7 +17,7 @@ mod tests { #[test] fn test_from_client() -> async_session::Result { async_std::task::block_on(async { - let client_options = match ClientOptions::parse(&CONNECTION_STRING).await { + let client_options = match ClientOptions::parse(CONNECTION_STRING.as_str()).await { Ok(c) => c, Err(e) => panic!("Client Options Failed: {}", e), }; From 3dd4514758d7fb737f75535a8ed3009dea78b829 Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Mon, 10 Jan 2022 15:21:29 +0100 Subject: [PATCH 2/6] Add optional tokio-runtime feature Signed-off-by: Aaron Erhardt --- Cargo.toml | 8 ++- src/lib.rs | 3 +- tests/test.rs | 191 +++++++++++++++++++++++++++----------------------- 3 files changed, 111 insertions(+), 91 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e3a776c..eef4cad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,14 @@ authors = [ ] [features] +#default = ["async-std-runtime"] +default = ["tokio-runtime"] +async-std-runtime = ["mongodb/async-std-runtime"] +tokio-runtime = ["mongodb/tokio-runtime"] [dependencies] async-session = "3" -mongodb = { version = "2.1", default-features = false, features = [ - "async-std-runtime", +mongodb = { package = "mongodb", version = "2.1", default-features = false, features = [ "bson-chrono-0_4", ] } @@ -34,3 +37,4 @@ mongodb = { version = "2.1", default-features = false, features = [ async-std = { version = "1.10", features = ["attributes"] } lazy_static = "1" rand = { version = "0.8" } +tokio = { version = "1", features = ["rt"] } diff --git a/src/lib.rs b/src/lib.rs index 9d9cc2f..6eb441f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,8 +17,7 @@ use async_session::chrono::{Duration, Utc}; use async_session::{async_trait, Result, Session, SessionStore}; -use mongodb::bson::{self, Document}; -use mongodb::bson::{doc, Bson}; +use mongodb::bson::{self, doc, Bson, Document}; use mongodb::options::{ReplaceOptions, SelectionCriteria}; use mongodb::Client; diff --git a/tests/test.rs b/tests/test.rs index ad17166..2839c1a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,5 +1,17 @@ #[cfg(test)] mod tests { + use std::future::Future; + + #[cfg(feature = "async-std-runtime")] + fn run_test(future: F) -> F::Output { + async_std::task::block_on(future) + } + #[cfg(feature = "tokio-runtime")] + fn run_test(future: F) -> F::Output { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(future) + } + use async_mongodb_session::*; use async_session::{Session, SessionStore}; use lazy_static::lazy_static; @@ -14,106 +26,111 @@ mod tests { format!("mongodb://{}:{}/", HOST.as_str(), PORT.as_str()); } - #[test] - fn test_from_client() -> async_session::Result { - async_std::task::block_on(async { - let client_options = match ClientOptions::parse(CONNECTION_STRING.as_str()).await { - Ok(c) => c, - Err(e) => panic!("Client Options Failed: {}", e), - }; - - let client = match Client::with_options(client_options) { - Ok(c) => c, - Err(e) => panic!("Client Creation Failed: {}", e), - }; - - let store = MongodbSessionStore::from_client(client, "db_name", "collection"); - let mut rng = rand::thread_rng(); - let n2: u16 = rng.gen(); - let key = format!("key-{}", n2); - let value = format!("value-{}", n2); - let mut session = Session::new(); - session.insert(&key, &value)?; - - let cookie_value = store.store_session(session).await?.unwrap(); - let session = store.load_session(cookie_value).await?.unwrap(); - assert_eq!(&session.get::(&key).unwrap(), &value); - - Ok(()) - }) + async fn from_client() -> async_session::Result { + let client_options = match ClientOptions::parse(CONNECTION_STRING.as_str()).await { + Ok(c) => c, + Err(e) => panic!("Client Options Failed: {}", e), + }; + + let client = match Client::with_options(client_options) { + Ok(c) => c, + Err(e) => panic!("Client Creation Failed: {}", e), + }; + + let store = MongodbSessionStore::from_client(client, "db_name", "collection"); + let mut rng = rand::thread_rng(); + let n2: u16 = rng.gen(); + let key = format!("key-{}", n2); + let value = format!("value-{}", n2); + let mut session = Session::new(); + session.insert(&key, &value)?; + + let cookie_value = store.store_session(session).await?.unwrap(); + let session = store.load_session(cookie_value).await?.unwrap(); + assert_eq!(&session.get::(&key).unwrap(), &value); + + Ok(()) } - #[test] - fn test_new() -> async_session::Result { - async_std::task::block_on(async { - let store = - MongodbSessionStore::new(&CONNECTION_STRING, "db_name", "collection").await?; - - let mut rng = rand::thread_rng(); - let n2: u16 = rng.gen(); - let key = format!("key-{}", n2); - let value = format!("value-{}", n2); - let mut session = Session::new(); - session.insert(&key, &value)?; - - let cookie_value = store.store_session(session).await?.unwrap(); - let session = store.load_session(cookie_value).await?.unwrap(); - assert_eq!(&session.get::(&key).unwrap(), &value); - - Ok(()) - }) + async fn new() -> async_session::Result { + let store = MongodbSessionStore::new(&CONNECTION_STRING, "db_name", "collection").await?; + + let mut rng = rand::thread_rng(); + let n2: u16 = rng.gen(); + let key = format!("key-{}", n2); + let value = format!("value-{}", n2); + let mut session = Session::new(); + session.insert(&key, &value)?; + + let cookie_value = store.store_session(session).await?.unwrap(); + let session = store.load_session(cookie_value).await?.unwrap(); + assert_eq!(&session.get::(&key).unwrap(), &value); + + Ok(()) } - #[test] - fn test_with_expire() -> async_session::Result { - async_std::task::block_on(async { - let store = - MongodbSessionStore::new(&CONNECTION_STRING, "db_name", "collection").await?; - - store.initialize().await?; - - let mut rng = rand::thread_rng(); - let n2: u16 = rng.gen(); - let key = format!("key-{}", n2); - let value = format!("value-{}", n2); - let mut session = Session::new(); - session.expire_in(std::time::Duration::from_secs(5)); - session.insert(&key, &value)?; - - let cookie_value = store.store_session(session).await?.unwrap(); - let session = store.load_session(cookie_value).await?.unwrap(); - assert_eq!(&session.get::(&key).unwrap(), &value); - - Ok(()) - }) + async fn with_expire() -> async_session::Result { + let store = MongodbSessionStore::new(&CONNECTION_STRING, "db_name", "collection").await?; + + store.initialize().await?; + + let mut rng = rand::thread_rng(); + let n2: u16 = rng.gen(); + let key = format!("key-{}", n2); + let value = format!("value-{}", n2); + let mut session = Session::new(); + session.expire_in(std::time::Duration::from_secs(5)); + session.insert(&key, &value)?; + + let cookie_value = store.store_session(session).await?.unwrap(); + let session = store.load_session(cookie_value).await?.unwrap(); + assert_eq!(&session.get::(&key).unwrap(), &value); + + Ok(()) } - #[test] - fn test_check_expired() -> async_session::Result { + async fn check_expired() -> async_session::Result { use async_std::task; use std::time::Duration; - async_std::task::block_on(async { - let store = - MongodbSessionStore::new(&CONNECTION_STRING, "db_name", "collection").await?; + let store = MongodbSessionStore::new(&CONNECTION_STRING, "db_name", "collection").await?; + + store.initialize().await?; - store.initialize().await?; + let mut rng = rand::thread_rng(); + let n2: u16 = rng.gen(); + let key = format!("key-{}", n2); + let value = format!("value-{}", n2); + let mut session = Session::new(); + session.expire_in(Duration::from_secs(1)); + session.insert(&key, &value)?; - let mut rng = rand::thread_rng(); - let n2: u16 = rng.gen(); - let key = format!("key-{}", n2); - let value = format!("value-{}", n2); - let mut session = Session::new(); - session.expire_in(Duration::from_secs(1)); - session.insert(&key, &value)?; + let cookie_value = store.store_session(session).await?.unwrap(); - let cookie_value = store.store_session(session).await?.unwrap(); + task::sleep(Duration::from_secs(1)).await; + let session_to_recover = store.load_session(cookie_value).await?; - task::sleep(Duration::from_secs(1)).await; - let session_to_recover = store.load_session(cookie_value).await?; + assert!(&session_to_recover.is_none()); - assert!(&session_to_recover.is_none()); + Ok(()) + } - Ok(()) - }) + #[test] + fn test_from_client() -> async_session::Result { + run_test(from_client()) + } + + #[test] + fn test_new() -> async_session::Result { + run_test(new()) + } + + #[test] + fn test_with_expire() -> async_session::Result { + run_test(with_expire()) + } + + #[test] + fn test_check_expired() -> async_session::Result { + run_test(check_expired()) } } From 7c37f074b8e6ce3e3c8563d4669f72f64b59c87a Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Sun, 21 Aug 2022 11:13:38 +0200 Subject: [PATCH 3/6] Format code Signed-off-by: Aaron Erhardt --- src/lib.rs | 2 +- tests/test.rs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 03c3a58..6eb441f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,4 +195,4 @@ impl SessionStore for MongodbSessionStore { self.initialize().await?; Ok(()) } -} \ No newline at end of file +} diff --git a/tests/test.rs b/tests/test.rs index 08fe8d8..5cd0a30 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -6,6 +6,7 @@ mod tests { fn run_test(future: F) -> F::Output { async_std::task::block_on(future) } + #[cfg(feature = "tokio-runtime")] fn run_test(future: F) -> F::Output { let rt = tokio::runtime::Runtime::new().unwrap(); @@ -22,8 +23,10 @@ mod tests { lazy_static! { static ref HOST: String = env::var("HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); static ref PORT: String = env::var("PORT").unwrap_or_else(|_| "27017".to_string()); - static ref DATABASE: String = env::var("DATABASE").unwrap_or_else(|_| "db_name".to_string()); - static ref COLLECTION: String = env::var("COLLECTION").unwrap_or_else(|_| "collection".to_string()); + static ref DATABASE: String = + env::var("DATABASE").unwrap_or_else(|_| "db_name".to_string()); + static ref COLLECTION: String = + env::var("COLLECTION").unwrap_or_else(|_| "collection".to_string()); static ref CONNECTION_STRING: String = format!("mongodb://{}:{}/", HOST.as_str(), PORT.as_str()); } @@ -38,7 +41,7 @@ mod tests { Ok(c) => c, Err(e) => panic!("Client Creation Failed: {}", e), }; - + let store = MongodbSessionStore::from_client(client, &DATABASE, &COLLECTION); let mut rng = rand::thread_rng(); let n2: u16 = rng.gen(); From 1ecccc79446f17c91465ef73b419965801dedf56 Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Sun, 21 Aug 2022 11:17:40 +0200 Subject: [PATCH 4/6] Keep async std as default Signed-off-by: Aaron Erhardt --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eef4cad..663f0cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,7 @@ authors = [ ] [features] -#default = ["async-std-runtime"] -default = ["tokio-runtime"] +default = ["async-std-runtime"] async-std-runtime = ["mongodb/async-std-runtime"] tokio-runtime = ["mongodb/tokio-runtime"] From 398ca9fcfbda83b4fbd0492ac907ace2c5400acd Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Sun, 21 Aug 2022 11:20:23 +0200 Subject: [PATCH 5/6] Update version numbers Signed-off-by: Aaron Erhardt --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 663f0cb..e72aa01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,12 +28,12 @@ tokio-runtime = ["mongodb/tokio-runtime"] [dependencies] async-session = "3" -mongodb = { package = "mongodb", version = "2.1", default-features = false, features = [ +mongodb = { package = "mongodb", version = "2.3", default-features = false, features = [ "bson-chrono-0_4", ] } [dev-dependencies] -async-std = { version = "1.10", features = ["attributes"] } -lazy_static = "1" -rand = { version = "0.8" } -tokio = { version = "1", features = ["rt"] } +async-std = { version = "1.12", features = ["attributes"] } +lazy_static = "1.4" +rand = "0.8.5" +tokio = { version = "1.20", features = ["rt"] } From c45c11352628867d2dd8c1ff46db86b0f6ee51e6 Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Sun, 21 Aug 2022 12:16:54 +0200 Subject: [PATCH 6/6] Minor improvements Signed-off-by: Aaron Erhardt --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6eb441f..2c7e771 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,8 +88,7 @@ impl MongodbSessionStore { /// # Ok(()) }) } /// ``` pub async fn initialize(&self) -> Result { - self.index_on_expiry_at().await?; - Ok(()) + self.index_on_expiry_at().await } /// private associated function @@ -147,7 +146,6 @@ impl SessionStore for MongodbSessionStore { let query = doc! { "session_id": id }; let expire_at = match session.expiry() { None => Utc::now() + Duration::from_std(std::time::Duration::from_secs(1200)).unwrap(), - Some(expiry) => *{ expiry }, }; let replacement = doc! { "session_id": id, "session": value, "expireAt": expire_at, "created": Utc::now() };