diff --git a/mongodb-store/CHANGELOG.md b/mongodb-store/CHANGELOG.md index ab6eb41..fd25d5c 100644 --- a/mongodb-store/CHANGELOG.md +++ b/mongodb-store/CHANGELOG.md @@ -1,5 +1,9 @@ # Unreleased +# 0.12.0 + +- Update `tower-sessions` to `0.12.0` and implement `SessionStore::create`. + # 0.11.0 - Update `tower-sessions` to `0.11.0` diff --git a/mongodb-store/Cargo.toml b/mongodb-store/Cargo.toml index 918d5d5..b411307 100644 --- a/mongodb-store/Cargo.toml +++ b/mongodb-store/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tower-sessions-mongodb-store" description = "MongoDB session store for `tower-sessions`." -version = "0.11.0" +version = "0.12.0" edition = "2021" authors = ["Max Countryman "] license = "MIT" diff --git a/mongodb-store/src/lib.rs b/mongodb-store/src/lib.rs index 51a512b..7f473c3 100644 --- a/mongodb-store/src/lib.rs +++ b/mongodb-store/src/lib.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use bson::{doc, to_document}; pub use mongodb; -use mongodb::{options::UpdateOptions, Client, Collection}; +use mongodb::{options::UpdateOptions, Client, ClientSession, Collection}; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use tower_sessions_core::{ @@ -53,6 +53,7 @@ struct MongoDBSessionRecord { /// A MongoDB session store. #[derive(Clone, Debug)] pub struct MongoDBStore { + client: Client, collection: Collection, } @@ -73,8 +74,49 @@ impl MongoDBStore { pub fn new(client: Client, database: String) -> Self { Self { collection: client.database(&database).collection("sessions"), + client, } } + + async fn id_exists( + &self, + session: &mut ClientSession, + record: &Record, + ) -> session_store::Result { + Ok(self + .collection + .find_one_with_session(doc! { "_id": record.id.to_string() }, None, session) + .await + .map_err(MongoDBStoreError::MongoDB)? + .is_some()) + } + + async fn save_with_session( + &self, + session: &mut ClientSession, + record: &Record, + ) -> session_store::Result<()> { + let doc = to_document(&MongoDBSessionRecord { + data: bson::Binary { + subtype: bson::spec::BinarySubtype::Generic, + bytes: rmp_serde::to_vec(record).map_err(MongoDBStoreError::Encode)?, + }, + expiry_date: bson::DateTime::from(record.expiry_date), + }) + .map_err(MongoDBStoreError::BsonSerialize)?; + + self.collection + .update_one_with_session( + doc! { "_id": record.id.to_string() }, + doc! { "$set": doc }, + UpdateOptions::builder().upsert(true).build(), + session, + ) + .await + .map_err(MongoDBStoreError::MongoDB)?; + + Ok(()) + } } #[async_trait] @@ -94,22 +136,48 @@ impl ExpiredDeletion for MongoDBStore { #[async_trait] impl SessionStore for MongoDBStore { + async fn create(&self, record: &mut Record) -> session_store::Result<()> { + let mut session = self + .client + .start_session(None) + .await + .map_err(MongoDBStoreError::MongoDB)?; + + session + .start_transaction(None) + .await + .map_err(MongoDBStoreError::MongoDB)?; + + while self.id_exists(&mut session, record).await? { + record.id = Id::default(); + } + + self.save_with_session(&mut session, record).await?; + + session + .commit_transaction() + .await + .map_err(MongoDBStoreError::MongoDB)?; + + Ok(()) + } + async fn save(&self, record: &Record) -> session_store::Result<()> { - let doc = to_document(&MongoDBSessionRecord { - data: bson::Binary { - subtype: bson::spec::BinarySubtype::Generic, - bytes: rmp_serde::to_vec(record).map_err(MongoDBStoreError::Encode)?, - }, - expiry_date: bson::DateTime::from(record.expiry_date), - }) - .map_err(MongoDBStoreError::BsonSerialize)?; + let mut session = self + .client + .start_session(None) + .await + .map_err(MongoDBStoreError::MongoDB)?; - self.collection - .update_one( - doc! { "_id": record.id.to_string() }, - doc! { "$set": doc }, - UpdateOptions::builder().upsert(true).build(), - ) + session + .start_transaction(None) + .await + .map_err(MongoDBStoreError::MongoDB)?; + + self.save_with_session(&mut session, record).await?; + + session + .commit_transaction() .await .map_err(MongoDBStoreError::MongoDB)?;