generic over executor types

This commit is contained in:
Brandon Thomas
2023-03-12 04:12:22 -04:00
parent ae0522419a
commit 2821240cc0
9 changed files with 168 additions and 29 deletions
Generated
+11
View File
@@ -2101,6 +2101,7 @@ dependencies = [
"serde_derive",
"serde_json",
"sqlx",
"sqlx_mysql_helpers",
"tokens",
"toml",
]
@@ -6322,6 +6323,14 @@ dependencies = [
"tokio-native-tls",
]
[[package]]
name = "sqlx_mysql_helpers"
version = "0.0.1"
dependencies = [
"errors",
"sqlx",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@@ -7750,6 +7759,7 @@ dependencies = [
"database_queries",
"dotenv",
"enums",
"errors",
"hmac 0.10.1",
"http_server_common",
"jwt",
@@ -7767,6 +7777,7 @@ dependencies = [
"serde_json",
"sha2 0.9.5",
"sqlx",
"sqlx_mysql_helpers",
"time 0.2.26",
"tokens",
"toml",
+1
View File
@@ -36,6 +36,7 @@ members = [
"crates/lib/media",
"crates/lib/redis_common",
"crates/lib/sqlite_queries",
"crates/lib/sqlx_mysql_helpers",
"crates/lib/subprocess_common",
"crates/lib/tts_common",
"crates/lib/url_config",
+1
View File
@@ -21,6 +21,7 @@ container_common = { path = "../container_common" }
enums = { path = "../../_types/enums" }
errors = { path = "../errors" }
reusable_types = { path = "../../_types/reusable_types" }
sqlx_mysql_helpers = { path = "../sqlx_mysql_helpers" }
tokens = { path = "../../_types/tokens" }
# External
@@ -5,8 +5,7 @@
use anyhow::anyhow;
use errors::AnyhowResult;
use log::warn;
use sqlx::MySql;
use sqlx::pool::PoolConnection;
use sqlx::{Executor, MySql};
#[derive(Clone, Serialize, Deserialize)]
pub struct SessionRecord {
@@ -14,12 +13,12 @@ pub struct SessionRecord {
pub user_token: String,
}
pub async fn get_user_session_by_token_light(
mysql_connection: &mut PoolConnection<MySql>,
pub async fn get_user_session_by_token_light<'e, 'c : 'e, E>(
mysql_executor: E,
session_token: &str,
) -> AnyhowResult<Option<SessionRecord>> {
// NB: Lookup failure is Err(RowNotFound).
) -> AnyhowResult<Option<SessionRecord>>
where E: 'e + Executor<'c, Database = MySql>
{
let maybe_session_record = sqlx::query_as!(
SessionRecord,
r#"
@@ -32,7 +31,7 @@ AND deleted_at IS NULL
"#,
session_token,
)
.fetch_one(mysql_connection)
.fetch_one(mysql_executor)
.await;
match maybe_session_record {
+26
View File
@@ -0,0 +1,26 @@
[package]
name = "sqlx_mysql_helpers"
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
publish = false
[lib]
name = "sqlx_mysql_helpers"
path = "src/lib.rs"
[dependencies]
# Internal
errors = { path = "../errors" }
# MySql Database
# "offline" supports typesafe builds without a live database
# "rustls" doesn't appear to work with IP addresses, per:
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
sqlx = { version = "0.6.2", features = [ "mysql", "runtime-actix-native-tls", "offline", "chrono" ] }
[dev-dependencies]
# None yet
@@ -0,0 +1,78 @@
use sqlx::{MySql, MySqlPool};
use sqlx::pool::PoolConnection;
use errors::AnyhowResult;
/// Pass a handle to MySql without caring about the type.
pub enum MySqlHandle {
ConnectionPool(MySqlPool),
Connection(PoolConnection<MySql>),
}
/// Pass a handle to MySql without caring about the type.
/// NB: The connection is typically owned, whereas the connection pool is not.
pub enum MySqlHandleMixedRef<'a> {
ConnectionPool(&'a MySqlPool),
Connection(PoolConnection<MySql>),
}
/// Pass a handle to MySql without caring about the type.
pub enum MySqlHandleRef<'a> {
ConnectionPool(&'a MySqlPool),
Connection(&'a mut PoolConnection<MySql>),
}
//impl MySqlHandle {
// pub fn as_ref(&mut self) -> MySqlHandleRef {
// match self {
// Self::ConnectionPool(inner) => MySqlHandleRef::ConnectionPool(inner),
// Self::Connection(inner) => MySqlHandleRef::Connection(inner),
// }
// }
//}
impl <'a> MySqlHandleMixedRef<'a> {
/// Consume the handle and return a connection.
pub async fn to_discrete_connection(self) -> AnyhowResult<PoolConnection<MySql>> {
match self {
Self::Connection(connection) => Ok(connection),
Self::ConnectionPool(pool) => Ok(pool.acquire().await?),
}
}
}
//impl <'a> MySqlHandleRef<'a> {
// /// Consume the handle and return a connection.
// pub async fn to_discrete_connection(self) -> AnyhowResult<()> {
// let _r = match self {
// Self::Connection(connection) => Ok(connection),
// Self::ConnectionPool(pool) => {
// let connection = pool.acquire().await?;
// Ok(connection)
// },
// };
//
// Ok(())
// }
//}
//
///// Exists to hold ownership (if necessary)
//pub struct MySqlConnectionWrapper<'a> {
// maybe_owned: Option<PoolConnection<MySql>>,
// reference: &'a mut PoolConnection<MySql>,
//}
// TODO:
//impl <'a> MySqlConnectionRef<'a> {
// pub async fn get_pool_connection(&'a mut self) -> AnyhowResult<&'a PoolConnection<MySql>> {
// match self {
// MySqlConnectionRef::ConnectionPool(pool) => {
// let connection = pool.acquire().await?;
// Ok(connection)
// }
// MySqlConnectionRef::Connection(inner) => Ok(inner),
// }
//
// }
//}
+20
View File
@@ -0,0 +1,20 @@
//! sqlx_mysql_helpers
//!
//! Collect reusable Sqlx functions and patterns, particularly for MySQL databases.
//!
// Never allow these
#![forbid(private_in_public)]
#![forbid(unused_must_use)] // NB: It's unsafe to not close/check some things
// Okay to toggle
#![forbid(unreachable_patterns)]
#![forbid(unused_imports)]
#![forbid(unused_mut)]
#![forbid(unused_variables)]
// Always allow
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod any_connection;
+2
View File
@@ -18,10 +18,12 @@ path = "src/lib.rs"
container_common = { path = "../../../lib/container_common" }
database_queries = { path = "../../../lib/database_queries" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
http_server_common = { path = "../../../lib/http_server_common" }
redis_caching = { path = "../../../lib/caching/redis_caching" }
redis_common = { path = "../../../lib/redis_common" }
reusable_types = { path = "../../../_types/reusable_types" }
sqlx_mysql_helpers = { path = "../../../lib/sqlx_mysql_helpers" }
tokens = { path = "../../../_types/tokens" }
user_input_common = { path = "../../../lib/user_input_common" }
@@ -1,20 +1,21 @@
// NB: Incrementally getting rid of build warnings...
#![forbid(unused_imports)]
#![forbid(unused_mut)]
#![forbid(unused_variables)]
//#![forbid(unused_imports)]
//#![forbid(unused_mut)]
//#![forbid(unused_variables)]
use actix_web::HttpRequest;
use log::warn;
use container_common::anyhow_result::AnyhowResult;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::utils::user_session_extended::{UserSessionExtended, UserSessionPreferences, UserSessionPremiumPlanInfo, UserSessionRoleAndPermissions, UserSessionSubscriptionPlan, UserSessionUserDetails};
use database_queries::queries::users::user_sessions::get_user_session_by_token::{get_user_session_by_token, SessionUserRecord};
use database_queries::queries::users::user_sessions::get_user_session_by_token_light::{get_user_session_by_token_light, SessionRecord};
use database_queries::queries::users::user_subscriptions::list_active_user_subscriptions::list_active_user_subscriptions;
use log::warn;
use redis_caching::redis_ttl_cache::{RedisTtlCache, RedisTtlCacheConnection};
use sqlx::pool::PoolConnection;
use sqlx::{MySqlPool, MySql};
use redis_common::redis_cache_keys::RedisCacheKeys;
use sqlx::pool::PoolConnection;
use sqlx::{MySqlPool, MySql, Acquire, Executor};
use sqlx_mysql_helpers::any_connection::MySqlHandleMixedRef;
#[derive(Clone)]
pub struct SessionChecker {
@@ -48,8 +49,8 @@ impl SessionChecker {
pool: &MySqlPool
) -> AnyhowResult<Option<SessionRecord>>
{
let mut connection = pool.acquire().await?;
self.maybe_get_session_light_from_connection(request, &mut connection).await
//let mysql_handle = MySqlHandleMixedRef::ConnectionPool(pool);
self.do_session_light_lookup_and_cookie_decode(request, pool).await
}
pub async fn maybe_get_session_light_from_connection(
@@ -58,42 +59,42 @@ impl SessionChecker {
mysql_connection: &mut PoolConnection<MySql>,
) -> AnyhowResult<Option<SessionRecord>>
{
let session_token = match self.cookie_manager.decode_session_token_from_request(request)? {
None => return Ok(None),
Some(session_token) => session_token,
};
get_user_session_by_token_light(mysql_connection, &session_token).await
//let mysql_handle = MySqlHandleMixedRef::Connection(mysql_connection);
self.do_session_light_lookup_and_cookie_decode(request, mysql_connection).await
}
async fn maybe_get_session_light_from_connection_cached(
async fn do_session_light_lookup_and_cookie_decode<'e, 'c : 'e, E>(
&self,
request: &HttpRequest,
mysql_connection: &mut PoolConnection<MySql>,
//mysql_handle: MySqlHandleMixedRef<'_>,
mysql_executor: E,
) -> AnyhowResult<Option<SessionRecord>>
where E: 'e + Executor<'c, Database = MySql>
{
let session_token = match self.cookie_manager.decode_session_token_from_request(request)? {
None => return Ok(None),
Some(session_token) => session_token,
};
self.do_session_light_lookup(mysql_connection, &session_token).await
self.do_session_light_lookup(mysql_executor, &session_token).await
}
async fn do_session_light_lookup(
async fn do_session_light_lookup<'e, 'c : 'e, E>(
&self,
mysql_connection: &mut PoolConnection<MySql>,
//mysql_handle: MySqlHandleMixedRef<'_>,
mysql_executor: E,
session_token: &str,
) -> AnyhowResult<Option<SessionRecord>>
where E: 'e + Executor<'c, Database = MySql>
{
match self.maybe_get_redis_cache_connection() {
None => {
get_user_session_by_token_light(mysql_connection, session_token).await
get_user_session_by_token_light(mysql_executor, session_token).await
}
Some(mut redis_ttl_cache) => {
let cache_key = RedisCacheKeys::session_record_light(session_token);
redis_ttl_cache.lazy_load_if_not_cached(&cache_key, move || {
get_user_session_by_token_light(mysql_connection, session_token)
get_user_session_by_token_light(mysql_executor, session_token)
}).await
}
}