generics, endpoint

This commit is contained in:
Brandon Thomas committed 2025-09-24 19:11:10 -04:00
1 parent 87b4cfb3f2
commit 1fbbbc5d7d
9 files changed
+142 -11

No files matched your search

@@ -1,5 +1,6 @@
pub mod database_insert_error;
pub mod database_query_error;
pub mod mysql_error;
pub mod select_exactly_one_error;
pub mod select_optional_record_error;
pub mod mysql_upsert_error;
pub mod subtypes;
@@ -0,0 +1,26 @@
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
/// Implement this marker trait for subtypes.
pub trait CrateError : Error + Debug + Display {}
#[derive(Debug)]
pub struct MysqlError<E: CrateError> {
/// Inner error type
pub inner: E
}
impl <E> Error for MysqlError <E> where E: CrateError {}
impl <E> Display for MysqlError<E> where E: CrateError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "MysqlError: {}", self.inner)
}
}
// impl From<UpsertError> for MysqlError<UpsertError> {
// fn from(value: UpsertError) -> Self {
// MysqlError { inner: value }
// }
// }
@@ -0,0 +1 @@
pub mod upsert_error;
@@ -1,9 +1,10 @@
use crate::errors::mysql_error::{CrateError, MysqlError};
use log::error;
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum MysqlUpsertError {
pub enum UpsertError {
/// A duplicate key error occurred.
DuplicateKeyError(String),
@@ -11,18 +12,20 @@ pub enum MysqlUpsertError {
SqlxError(sqlx::Error),
}
impl Error for MysqlUpsertError {}
impl Error for UpsertError {}
impl Display for MysqlUpsertError {
impl CrateError for UpsertError {}
impl Display for UpsertError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
MysqlUpsertError::DuplicateKeyError(reason) => write!(f, "DuplicateKeyError: {}", reason),
MysqlUpsertError::SqlxError(e) => write!(f, "SQLx error: {:?}", e),
UpsertError::DuplicateKeyError(reason) => write!(f, "DuplicateKeyError: {}", reason),
UpsertError::SqlxError(e) => write!(f, "SQLx error: {:?}", e),
}
}
}
impl From<sqlx::Error> for MysqlUpsertError {
impl From<sqlx::Error> for UpsertError {
fn from(err: sqlx::Error) -> Self {
if let Some(db_err) = err.as_database_error() {
// NB: SQLSTATE[23000]: Integrity constraint violation
@@ -39,3 +42,10 @@ impl From<sqlx::Error> for MysqlUpsertError {
Self::SqlxError(err)
}
}
impl From<sqlx::Error> for MysqlError<UpsertError> {
fn from(err: sqlx::Error) -> Self {
let inner = UpsertError::from(err);
MysqlError { inner }
}
}
@@ -1 +1 @@
mod upsert_analytics_app_active_user;
pub mod upsert_analytics_app_active_user;
@@ -1,4 +1,5 @@
use crate::errors::mysql_upsert_error::MysqlUpsertError;
use crate::errors::mysql_error::MysqlError;
use crate::errors::subtypes::upsert_error::UpsertError;
use enums::common::payments_namespace::PaymentsNamespace;
use sqlx::mysql::MySqlArguments;
use sqlx::pool::PoolConnection;
@@ -44,7 +45,10 @@ ON DUPLICATE KEY UPDATE
)
}
pub async fn upsert_with_connection(&self, mysql_connection: &mut PoolConnection<MySql>) -> Result<(), MysqlUpsertError> {
pub async fn upsert_with_connection(
&self,
mysql_connection: &mut PoolConnection<MySql>
) -> Result<(), MysqlError<UpsertError>> {
let _query_result = self.query()
.execute(&mut **mysql_connection)
.await?;
@@ -3,6 +3,7 @@ use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError};
use std::error::Error;
use std::fmt::{Display, Formatter};
use log::error;
use mysql_queries::errors::mysql_error::{CrateError, MysqlError};
#[derive(Debug)]
pub enum CommonWebError {
@@ -84,6 +85,20 @@ impl From<sqlx::Error> for CommonWebError {
}
}
impl From<anyhow::Error> for CommonWebError {
fn from(value: anyhow::Error) -> Self {
error!("Uncaught anyhow::Error being coerced to CommonWebError : {:?}", value);
CommonWebError::ServerError
}
}
impl <T: CrateError> From<MysqlError<T>> for CommonWebError {
fn from(value: MysqlError<T>) -> Self {
error!("MysqlError being coerced to CommonWebError : {:?}", value);
CommonWebError::ServerError
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -0,0 +1,73 @@
use std::sync::Arc;
use crate::http_server::common_responses::common_web_error::CommonWebError;
use crate::state::server_state::ServerState;
use actix_web::web::Json;
use actix_web::{web, HttpRequest};
use enums::common::payments_namespace::PaymentsNamespace;
use http_server_common::request::get_request_ip::get_request_ip;
use mysql_queries::queries::analytics_active_users::upsert_analytics_app_active_user::UpsertAnalyticsAppActiveUser;
use utoipa::ToSchema;
#[derive(Deserialize, ToSchema)]
pub struct LogAppActiveUserRequest {
/// An override for the application version.
maybe_app_version: Option<String>,
}
#[derive(Serialize, ToSchema)]
pub struct LogAppActiveUserResponse {
pub success: bool,
}
/// Log an active app user - user must be logged in.
#[utoipa::path(
post,
tag = "Analytics",
path = "/v1/analytics/active_user",
responses(
(status = 200, description = "Success", body = LogAppActiveUserSuccessResponse),
),
params(
("request" = LogAppActiveUserRequest, description = "Payload for Request"),
)
)]
pub async fn log_browser_session_handler(
http_request: HttpRequest,
request: Json<LogAppActiveUserRequest>,
server_state: web::Data<Arc<ServerState>>,
) -> Result<Json<LogAppActiveUserResponse>, CommonWebError>
{
let mut mysql_connection = server_state.mysql_pool
.acquire()
.await?;
let maybe_user_session = server_state
.session_checker
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
.await?;
let user_token = maybe_user_session
.ok_or(CommonWebError::NotAuthorized)?
.user_token;
let ip_address = get_request_ip(&http_request);
let app_version = request.maybe_app_version
.as_deref()
.unwrap_or("unknown");
let upsert = UpsertAnalyticsAppActiveUser {
namespace: PaymentsNamespace::Artcraft,
user_token: &user_token,
ip_address: &ip_address,
app_version,
};
upsert.upsert_with_connection(&mut mysql_connection).await?;
Ok(Json(LogAppActiveUserResponse {
success: true,
}))
}
@@ -1 +1,2 @@
pub mod log_browser_session_handler;
pub mod log_browser_session_handler;
pub mod log_app_active_user_handler;