mod models;
mod operations;
mod schema;
use std::sync::{Arc, RwLock};
use diesel::r2d2::{ConnectionManager, Pool};
use crate::biome::refresh_tokens::store::{RefreshTokenError, RefreshTokenStore};
use crate::store::pool::ConnectionPool;
use operations::{
add_token::RefreshTokenStoreAddTokenOperation,
fetch_token::RefreshTokenStoreFetchTokenOperation,
remove_token::RefreshTokenStoreRemoveTokenOperation,
update_token::RefreshTokenStoreUpdateTokenOperation, RefreshTokenStoreOperations,
};
pub struct DieselRefreshTokenStore<C: diesel::Connection + 'static> {
connection_pool: ConnectionPool<C>,
}
impl<C: diesel::Connection> DieselRefreshTokenStore<C> {
pub fn new(connection_pool: Pool<ConnectionManager<C>>) -> Self {
Self {
connection_pool: connection_pool.into(),
}
}
pub fn new_with_write_exclusivity(
connection_pool: Arc<RwLock<Pool<ConnectionManager<C>>>>,
) -> Self {
Self {
connection_pool: connection_pool.into(),
}
}
}
#[cfg(feature = "postgres")]
impl RefreshTokenStore for DieselRefreshTokenStore<diesel::pg::PgConnection> {
fn add_token(&self, user_id: &str, token: &str) -> Result<(), RefreshTokenError> {
self.connection_pool
.execute_write(|conn| RefreshTokenStoreOperations::new(conn).add_token(user_id, token))
}
fn remove_token(&self, user_id: &str) -> Result<(), RefreshTokenError> {
self.connection_pool
.execute_write(|conn| RefreshTokenStoreOperations::new(conn).remove_token(user_id))
}
fn update_token(&self, user_id: &str, token: &str) -> Result<(), RefreshTokenError> {
self.connection_pool.execute_write(|conn| {
RefreshTokenStoreOperations::new(conn).update_token(user_id, token)
})
}
fn fetch_token(&self, user_id: &str) -> Result<String, RefreshTokenError> {
self.connection_pool
.execute_read(|conn| RefreshTokenStoreOperations::new(conn).fetch_token(user_id))
}
}
#[cfg(feature = "sqlite")]
impl RefreshTokenStore for DieselRefreshTokenStore<diesel::sqlite::SqliteConnection> {
fn add_token(&self, user_id: &str, token: &str) -> Result<(), RefreshTokenError> {
self.connection_pool
.execute_write(|conn| RefreshTokenStoreOperations::new(conn).add_token(user_id, token))
}
fn remove_token(&self, user_id: &str) -> Result<(), RefreshTokenError> {
self.connection_pool
.execute_write(|conn| RefreshTokenStoreOperations::new(conn).remove_token(user_id))
}
fn update_token(&self, user_id: &str, token: &str) -> Result<(), RefreshTokenError> {
self.connection_pool.execute_write(|conn| {
RefreshTokenStoreOperations::new(conn).update_token(user_id, token)
})
}
fn fetch_token(&self, user_id: &str) -> Result<String, RefreshTokenError> {
self.connection_pool
.execute_read(|conn| RefreshTokenStoreOperations::new(conn).fetch_token(user_id))
}
}
#[cfg(all(test, feature = "sqlite"))]
pub mod tests {
use super::*;
use crate::migrations::run_sqlite_migrations;
use diesel::{
r2d2::{ConnectionManager, Pool},
sqlite::SqliteConnection,
};
#[test]
fn sqlite_add_and_fetch() {
let pool = create_connection_pool_and_migrate();
let store = DieselRefreshTokenStore::new(pool);
store
.add_token("user1", "token1")
.expect("Failed to add token1");
store
.add_token("user2", "token2")
.expect("Failed to add token2");
store
.add_token("user3", "token3")
.expect("Failed to add token3");
assert_eq!(
store.fetch_token("user1").expect("Failed to fetch token1"),
"token1",
);
assert_eq!(
store.fetch_token("user2").expect("Failed to fetch token2"),
"token2",
);
assert_eq!(
store.fetch_token("user3").expect("Failed to fetch token3"),
"token3",
);
match store.fetch_token("user4") {
Err(RefreshTokenError::NotFoundError(_)) => {}
res => panic!(
"Expected Err(UserStoreError::NotFoundError), got {:?} instead",
res
),
}
}
#[test]
fn sqlite_update() {
let pool = create_connection_pool_and_migrate();
let store = DieselRefreshTokenStore::new(pool);
store
.add_token("user", "token1")
.expect("Failed to add token");
assert_eq!(
store.fetch_token("user").expect("Failed to fetch token1"),
"token1",
);
store
.update_token("user", "token2")
.expect("Failed to update token");
assert_eq!(
store.fetch_token("user").expect("Failed to fetch token2"),
"token2",
);
}
#[test]
fn sqlite_remove() {
let pool = create_connection_pool_and_migrate();
let store = DieselRefreshTokenStore::new(pool);
store
.add_token("user1", "token1")
.expect("Failed to add token1");
store
.add_token("user2", "token2")
.expect("Failed to add token2");
store
.add_token("user3", "token3")
.expect("Failed to add token3");
store
.remove_token("user3")
.expect("Failed to remove token3");
match store.fetch_token("user3") {
Err(RefreshTokenError::NotFoundError(_)) => {}
res => panic!(
"Expected Err(RefreshTokenError::NotFoundError), got {:?} instead",
res
),
}
}
fn create_connection_pool_and_migrate() -> Pool<ConnectionManager<SqliteConnection>> {
let connection_manager = ConnectionManager::<SqliteConnection>::new(":memory:");
let pool = Pool::builder()
.max_size(1)
.build(connection_manager)
.expect("Failed to build connection pool");
run_sqlite_migrations(&*pool.get().expect("Failed to get connection for migrations"))
.expect("Failed to run migrations");
pool
}
}