Skip to content

Commit

Permalink
Feat/database encapsulation (#1052)
Browse files Browse the repository at this point in the history
* Use better connection management

* Use clippy

* Only print error that id3v2 tag is not supported.

* Updated frankenstein
  • Loading branch information
SamTV12345 authored Dec 10, 2024
1 parent 9c6fdc7 commit 2707e20
Show file tree
Hide file tree
Showing 60 changed files with 831 additions and 1,149 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ env_logger = "0.11.5"
chrono = {version = "0.4.38", default-features=false, features = ["serde"]}
rss = "2.0.11"
actix-ws = "0.3.0"
frankenstein = "0.35.0"
frankenstein = "0.37.0"
regex = "1.11.0"
xml-builder = "0.5.2"
diesel = { version = "2.2.5", features = ["chrono", "r2d2"] }
Expand All @@ -60,7 +60,7 @@ sha1 = "0.10.6"
sha256 = "1.5.0"
strfmt="0.2.4"
urlencoding="2.1.3"
id3 = "1.16.0"
id3 = "1.15.0"
mp4ameta = "0.11.0"
file-format = "0.26.0"

Expand Down
2 changes: 1 addition & 1 deletion diesel.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# see https://diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/dbconfig/schemas/sqlite/schema.rs"
file = "src/adapters/persistence/dbconfig/schemas/sqlite/schema.rs"

[migrations_directory]
dir = "migrations"
10 changes: 2 additions & 8 deletions src/adapters/api/controllers/device_controller.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::gpodder::device::dto::device_post::DevicePost;
use crate::models::session::Session;
use crate::utils::error::CustomError;
use crate::DbPool;
use actix_web::web::Data;
use actix_web::{get, post, Scope};
use actix_web::{web, HttpResponse};
use crate::adapters::api::models::device::device_response::DeviceResponse;
Expand All @@ -15,8 +13,7 @@ use crate::application::usecases::devices::query_use_case::QueryUseCase;
pub async fn post_device(
query: web::Path<(String, String)>,
device_post: web::Json<DevicePost>,
opt_flag: Option<web::ReqData<Session>>,
conn: Data<DbPool>,
opt_flag: Option<web::ReqData<Session>>
) -> Result<HttpResponse, CustomError> {
match opt_flag {
Some(flag) => {
Expand All @@ -33,8 +30,7 @@ pub async fn post_device(
caption: device_post.caption.clone(),
};

let pool = conn.get_ref();
let device = DeviceService::create(device_create.into(), pool)?;
let device = DeviceService::create(device_create.into())?;
let result = DeviceResponse::from(&device);

Ok(HttpResponse::Ok().json(result))
Expand All @@ -47,7 +43,6 @@ pub async fn post_device(
pub async fn get_devices_of_user(
query: web::Path<String>,
opt_flag: Option<web::ReqData<Session>>,
pool: Data<DbPool>,
) -> Result<HttpResponse, CustomError> {
match opt_flag {
Some(flag) => {
Expand All @@ -56,7 +51,6 @@ pub async fn get_devices_of_user(
}
let devices = DeviceService::query_by_username(
query.clone(),
pool.get_ref(),
)?;

let dtos = devices
Expand Down
94 changes: 94 additions & 0 deletions src/adapters/persistence/dbconfig/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use std::process::exit;
use std::sync::OnceLock;
use std::time::Duration;
use diesel::Connection;
use diesel::r2d2::ConnectionManager;
use r2d2::Pool;
use crate::adapters::persistence::dbconfig::DBType;
use crate::constants::inner_constants::ENVIRONMENT_SERVICE;
use crate::DbPool;

#[derive(Debug)]
pub struct ConnectionOptions {
pub enable_wal: bool,
pub enable_foreign_keys: bool,
pub busy_timeout: Option<Duration>,
}

impl r2d2::CustomizeConnection<DBType, diesel::r2d2::Error> for ConnectionOptions {
fn on_acquire(&self, conn: &mut DBType) -> Result<(), diesel::r2d2::Error> {
use diesel::connection::SimpleConnection;
(|| {
if self.enable_wal {
conn.batch_execute("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL;")?;
}
if self.enable_foreign_keys {
conn.batch_execute("PRAGMA foreign_keys = ON;")?;
}
if let Some(d) = self.busy_timeout {
conn.batch_execute(&format!("PRAGMA busy_timeout = {};", d.as_millis()))?;
}
Ok(())
})()
.map_err(diesel::r2d2::Error::QueryError)
}
}

pub fn establish_connection() -> DBType {
let database_url = &ENVIRONMENT_SERVICE.get().unwrap().database_url;
DBType::establish(database_url).unwrap_or_else(|e| {
log::error!("Error connecting to {} with reason {}", database_url, e);
exit(1)
})
}

static POOL: OnceLock<DbPool> = OnceLock::new();


pub fn get_connection() -> r2d2::PooledConnection<ConnectionManager<DBType>> {
POOL.get_or_init(init_pool).get().unwrap()
}

fn init_pool() -> DbPool {
let conn = establish_connection();
match conn {
DBType::Postgresql(_) => {
init_postgres_db_pool(&ENVIRONMENT_SERVICE.get().unwrap().database_url)
.expect("Failed to connect to database")
}
DBType::Sqlite(_) => {
init_sqlite_db_pool(&ENVIRONMENT_SERVICE.get().unwrap().database_url)
.expect("Failed to connect to database")
}
}
}


fn init_postgres_db_pool(
database_url: &str,
) -> Result<Pool<ConnectionManager<DBType>>, String> {
let env_service = ENVIRONMENT_SERVICE.get().unwrap();
let db_connections = env_service.conn_number;
let manager = ConnectionManager::<DBType>::new(database_url);
let pool = Pool::builder()
.max_size(db_connections as u32)
.build(manager)
.expect("Failed to create pool.");
Ok(pool)
}

fn init_sqlite_db_pool(
database_url: &str,
) -> Result<Pool<ConnectionManager<DBType>>, String> {
let manager = ConnectionManager::<DBType>::new(database_url);
let pool = Pool::builder()
.max_size(16)
.connection_customizer(Box::new(ConnectionOptions {
enable_wal: true,
enable_foreign_keys: true,
busy_timeout: Some(Duration::from_secs(120)),
}))
.build(manager)
.unwrap();
Ok(pool)
}
54 changes: 54 additions & 0 deletions src/adapters/persistence/dbconfig/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#[path = "schemas/sqlite/schema.rs"]
pub mod schema;
pub mod db;

use diesel::QueryResult;

#[derive(diesel::MultiConnection)]
pub enum DBType {
Postgresql(diesel::PgConnection),
Sqlite(diesel::SqliteConnection),
}

#[macro_export]
macro_rules! import_database_config {
() => {
pub const SQLITE_MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations/sqlite");

pub const POSTGRES_MIGRATIONS: EmbeddedMigrations =
embed_migrations!("./migrations/postgres");
};
}

#[macro_export]
macro_rules! execute_with_conn {
($diesel_func:expr) => {
{
use $crate::get_connection;
use std::ops::DerefMut;

let mut conn = get_connection();
let _ = match conn.deref_mut() {
$crate::adapters::persistence::dbconfig::DBType::Sqlite(conn) => return $diesel_func
(conn),
$crate::adapters::persistence::dbconfig::DBType::Postgresql(conn) => return $diesel_func(conn),
};
}
};
}

#[macro_export]
macro_rules! insert_with_conn {
($diesel_func:expr) => {
{

use $crate::get_connection;
use std::ops::DerefMut;
let mut conn = get_connection();
let _ = match conn.deref_mut() {
$crate::adapters::persistence::dbconfig::DBType::Sqlite(conn) => $diesel_func(conn),
$crate::adapters::persistence::dbconfig::DBType::Postgresql(conn) => $diesel_func(conn),
};
}
};
}
File renamed without changes.
3 changes: 2 additions & 1 deletion src/adapters/persistence/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod repositories;
pub mod model;
pub mod model;
pub mod dbconfig;
14 changes: 7 additions & 7 deletions src/adapters/persistence/model/device/device_entity.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::dbconfig::schema::devices;
use crate::adapters::persistence::dbconfig::schema::devices;
use crate::gpodder::device::dto::device_post::DevicePost;
use crate::{execute_with_conn, DBType as DbConnection};
use diesel::QueryDsl;
Expand Down Expand Up @@ -55,10 +55,10 @@ impl DeviceEntity {
}

#[allow(clippy::redundant_closure_call)]
pub fn save(&self, conn: &mut DbConnection) -> Result<DeviceEntity, diesel::result::Error> {
use crate::dbconfig::schema::devices::dsl::*;

execute_with_conn!(conn, |conn| diesel::insert_into(devices)
pub fn save(&self) -> Result<DeviceEntity, diesel::result::Error> {
use crate::adapters::persistence::dbconfig::schema::devices::dsl::*;
execute_with_conn!(|conn| diesel::insert_into(devices)
.values(self)
.get_result(conn));
}
Expand All @@ -67,7 +67,7 @@ impl DeviceEntity {
conn: &mut DbConnection,
username_to_insert: String,
) -> Result<Vec<DeviceEntity>, diesel::result::Error> {
use crate::dbconfig::schema::devices::dsl::*;
use crate::adapters::persistence::dbconfig::schema::devices::dsl::*;
devices
.filter(username.eq(username_to_insert))
.load::<DeviceEntity>(conn)
Expand All @@ -78,7 +78,7 @@ impl DeviceEntity {
username1: &str,
conn: &mut DbConnection,
) -> Result<usize, diesel::result::Error> {
use crate::dbconfig::schema::devices::dsl::*;
use crate::adapters::persistence::dbconfig::schema::devices::dsl::*;
diesel::delete(devices.filter(username.eq(username1))).execute(conn)
}
}
21 changes: 11 additions & 10 deletions src/adapters/persistence/repositories/device/device_repository.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
use diesel::RunQueryDsl;
use crate::adapters::persistence::model::device::device_entity::DeviceEntity;
use crate::application::repositories::device_repository::DeviceRepository;
use crate::dbconfig::DBType;
use crate::domain::models::device::model::Device;
use crate::execute_with_conn;
use crate::utils::error::{map_db_error, CustomError};

pub struct DeviceRepositoryImpl;

use diesel::QueryDsl;
use crate::dbconfig::schema::devices::dsl::*;
use crate::adapters::persistence::dbconfig::schema::devices::dsl::*;
use diesel::ExpressionMethods;
use DBType as DbConnection;
use crate::adapters::persistence::dbconfig::db::get_connection;

impl DeviceRepository for DeviceRepositoryImpl {
fn create(device: Device, conn: &mut DBType) -> Result<Device, CustomError> {
fn create(device: Device) -> Result<Device, CustomError> {
let device_entity: DeviceEntity = device.into();
execute_with_conn!(conn, |conn| diesel::insert_into(devices)
execute_with_conn!(|conn| diesel::insert_into(devices)
.values(device_entity)
.get_result(conn)
.map_err(map_db_error)
.map(|device_entity: DeviceEntity| device_entity.into()))

}

fn get_devices_of_user(username_to_find: String, conn: &mut DBType) -> Result<Vec<Device>,
fn get_devices_of_user(username_to_find: String) -> Result<Vec<Device>,
CustomError> {
devices
.filter(username.eq(username_to_find))
.load::<DeviceEntity>(conn)
.load::<DeviceEntity>(&mut get_connection())
.map(|device_entity| device_entity.into_iter().map(|device_entity| device_entity.into()).collect())
.map_err(map_db_error)
}

fn delete_by_username(username1: &str, conn: &mut DBType) -> Result<(), CustomError> {
use crate::dbconfig::schema::devices::dsl::*;
diesel::delete(devices.filter(username.eq(username1))).execute(conn).map(|_|()).map_err(map_db_error)
fn delete_by_username(username1: &str) -> Result<(), CustomError> {
use crate::adapters::persistence::dbconfig::schema::devices::dsl::*;
diesel::delete(devices.filter(username.eq(username1))).execute(&mut get_connection()).map(|_|())
.map_err
(map_db_error)
}
}
7 changes: 3 additions & 4 deletions src/application/repositories/device_repository.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use crate::dbconfig::DBType;
use crate::domain::models::device::model::Device;
use crate::utils::error::CustomError;

pub trait DeviceRepository {
fn create(device: Device, conn: &mut DBType) -> Result<Device, CustomError>;
fn get_devices_of_user(username: String, conn: &mut DBType) -> Result<Vec<Device>, CustomError>;
fn delete_by_username(username: &str, conn: &mut DBType) -> Result<(), CustomError>;
fn create(device: Device) -> Result<Device, CustomError>;
fn get_devices_of_user(username: String) -> Result<Vec<Device>, CustomError>;
fn delete_by_username(username: &str) -> Result<(), CustomError>;
}
13 changes: 6 additions & 7 deletions src/application/services/device/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,26 @@ use crate::application::repositories::device_repository::DeviceRepository;
use crate::application::usecases::devices::create_use_case::CreateUseCase;
use crate::application::usecases::devices::edit_use_case::EditUseCase;
use crate::application::usecases::devices::query_use_case::QueryUseCase;
use crate::DbPool;
use crate::domain::models::device::model::Device;
use crate::utils::error::CustomError;

pub struct DeviceService;


impl CreateUseCase for DeviceService {
fn create(device_to_safe: Device, pool: &DbPool) -> Result<Device, CustomError> {
DeviceRepositoryImpl::create(device_to_safe, &mut pool.get().unwrap())
fn create(device_to_safe: Device) -> Result<Device, CustomError> {
DeviceRepositoryImpl::create(device_to_safe)
}
}

impl QueryUseCase for DeviceService {
fn query_by_username(username: String, pool: &DbPool) -> Result<Vec<Device>, CustomError> {
DeviceRepositoryImpl::get_devices_of_user(username, &mut pool.get().unwrap())
fn query_by_username(username: String) -> Result<Vec<Device>, CustomError> {
DeviceRepositoryImpl::get_devices_of_user(username)
}
}

impl EditUseCase for DeviceService {
fn delete_by_username(username: &str, conn: &DbPool) -> Result<(), CustomError> {
DeviceRepositoryImpl::delete_by_username(username, &mut conn.get().unwrap())
fn delete_by_username(username: &str) -> Result<(), CustomError> {
DeviceRepositoryImpl::delete_by_username(username)
}
}
3 changes: 1 addition & 2 deletions src/application/usecases/devices/create_use_case.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::DbPool;
use crate::domain::models::device::model::Device;
use crate::utils::error::CustomError;

pub trait CreateUseCase {
fn create(device_to_safe: Device, conn: &DbPool) -> Result<Device, CustomError>;
fn create(device_to_safe: Device) -> Result<Device, CustomError>;
}
3 changes: 1 addition & 2 deletions src/application/usecases/devices/edit_use_case.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::DbPool;
use crate::utils::error::CustomError;

pub trait EditUseCase {
fn delete_by_username(username: &str, conn: &DbPool) -> Result<(), CustomError>;
fn delete_by_username(username: &str) -> Result<(), CustomError>;
}
3 changes: 1 addition & 2 deletions src/application/usecases/devices/query_use_case.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::DbPool;
use crate::domain::models::device::model::Device;
use crate::utils::error::CustomError;

pub trait QueryUseCase {
fn query_by_username(username: String, pool: &DbPool) -> Result<Vec<Device>, CustomError>;
fn query_by_username(username: String) -> Result<Vec<Device>, CustomError>;
}
Loading

0 comments on commit 2707e20

Please sign in to comment.