Files
warren/backend/src/lib/outbound/sqlite/warrens.rs
409 6fa26b3ddb remove sqlite extensions to fix docker issue
UUIDs are now generated in the backend before insertion
2025-09-07 17:35:07 +02:00

387 lines
9.9 KiB
Rust

use anyhow::{Context as _, anyhow};
use sqlx::{Acquire as _, SqliteConnection};
use uuid::Uuid;
use crate::domain::warren::{
models::{
file::AbsoluteFilePath,
share::{
CreateShareError, CreateShareRequest, CreateShareResponse, DeleteShareError,
DeleteShareRequest, DeleteShareResponse, GetShareError, GetShareRequest,
ListSharesError, ListSharesRequest, ListSharesResponse, Share,
VerifySharePasswordError, VerifySharePasswordRequest, VerifySharePasswordResponse,
},
warren::{
CreateWarrenError, CreateWarrenRequest, DeleteWarrenError, DeleteWarrenRequest,
EditWarrenError, EditWarrenRequest, FetchWarrenError, FetchWarrenRequest,
FetchWarrensError, FetchWarrensRequest, ListWarrensError, ListWarrensRequest, Warren,
WarrenName,
},
},
ports::WarrenRepository,
};
use super::{Sqlite, is_not_found_error};
impl WarrenRepository for Sqlite {
async fn create_warren(
&self,
request: CreateWarrenRequest,
) -> Result<Warren, CreateWarrenError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
let warren = self
.create_warren(&mut connection, request.name(), request.path())
.await
.context("Failed to create new warren")?;
Ok(warren)
}
async fn edit_warren(&self, request: EditWarrenRequest) -> Result<Warren, EditWarrenError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
let warren = self
.edit_warren(
&mut connection,
request.id(),
request.name(),
request.path(),
)
.await
.context("Failed to edit existing warren")?;
Ok(warren)
}
async fn delete_warren(
&self,
request: DeleteWarrenRequest,
) -> Result<Warren, DeleteWarrenError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
let warren = self
.delete_warren(&mut connection, request.id())
.await
.context("Failed to delete existing warren")?;
Ok(warren)
}
async fn fetch_warrens(
&self,
request: FetchWarrensRequest,
) -> Result<Vec<Warren>, FetchWarrensError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
let warrens = self
.fetch_warrens(&mut connection, request.ids())
.await
.map_err(|err| anyhow!(err).context("Failed to fetch warrens"))?;
Ok(warrens)
}
async fn list_warrens(
&self,
_request: ListWarrensRequest,
) -> Result<Vec<Warren>, ListWarrensError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
let warrens = self
.fetch_all_warrens(&mut connection)
.await
.map_err(|err| anyhow!(err).context("Failed to list all warrens"))?;
Ok(warrens)
}
async fn fetch_warren(&self, request: FetchWarrenRequest) -> Result<Warren, FetchWarrenError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
let warren = self
.get_warren(&mut connection, request.id())
.await
.map_err(|err| {
if is_not_found_error(&err) {
return FetchWarrenError::NotFound(request.id().clone());
}
anyhow!(err)
.context(format!("Failed to fetch warren with id {:?}", request.id()))
.into()
})?;
Ok(warren)
}
async fn get_warren_share(&self, request: GetShareRequest) -> Result<Share, GetShareError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
super::share::get_share(&mut connection, request)
.await
.map_err(Into::into)
}
async fn create_warren_share(
&self,
request: CreateShareRequest,
) -> Result<CreateShareResponse, CreateShareError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
super::share::create_share(&mut connection, request)
.await
.map(CreateShareResponse::new)
.map_err(Into::into)
}
async fn list_warren_shares(
&self,
request: ListSharesRequest,
) -> Result<ListSharesResponse, ListSharesError> {
let warren = self.fetch_warren((&request).into()).await?;
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
let path = request.path().clone();
super::share::list_shares(&mut connection, request)
.await
.map(|shares| ListSharesResponse::new(warren, path, shares))
.map_err(Into::into)
}
async fn delete_warren_share(
&self,
request: DeleteShareRequest,
) -> Result<DeleteShareResponse, DeleteShareError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
super::share::delete_share(&mut connection, request)
.await
.map(DeleteShareResponse::new)
.map_err(Into::into)
}
async fn verify_warren_share_password(
&self,
request: VerifySharePasswordRequest,
) -> Result<VerifySharePasswordResponse, VerifySharePasswordError> {
let mut connection = self
.pool
.acquire()
.await
.context("Failed to get a Sqlite connection")?;
super::share::verify_password(&mut connection, request)
.await
.map(VerifySharePasswordResponse::new)
.map_err(Into::into)
}
}
impl Sqlite {
async fn create_warren(
&self,
connection: &mut SqliteConnection,
name: &WarrenName,
path: &AbsoluteFilePath,
) -> Result<Warren, sqlx::Error> {
let mut tx = connection.begin().await?;
let warren: Warren = sqlx::query_as(
"
INSERT INTO warrens (
id,
name,
path
) VALUES (
$1,
$2,
$3
)
RETURNING
*
",
)
.bind(Uuid::new_v4())
.bind(name)
.bind(path)
.fetch_one(&mut *tx)
.await?;
tx.commit().await?;
Ok(warren)
}
async fn edit_warren(
&self,
connection: &mut SqliteConnection,
id: &Uuid,
name: &WarrenName,
path: &AbsoluteFilePath,
) -> Result<Warren, sqlx::Error> {
let mut tx = connection.begin().await?;
let warren: Warren = sqlx::query_as(
"
UPDATE
warrens
SET
name = $2,
path = $3
WHERE
id = $1
RETURNING
*
",
)
.bind(id)
.bind(name)
.bind(path)
.fetch_one(&mut *tx)
.await?;
tx.commit().await?;
Ok(warren)
}
async fn delete_warren(
&self,
connection: &mut SqliteConnection,
id: &Uuid,
) -> Result<Warren, sqlx::Error> {
let mut tx = connection.begin().await?;
let warren: Warren = sqlx::query_as(
"
DELETE FROM
warrens
WHERE
id = $1
RETURNING
*
",
)
.bind(id)
.fetch_one(&mut *tx)
.await?;
tx.commit().await?;
Ok(warren)
}
async fn get_warren(
&self,
connection: &mut SqliteConnection,
id: &Uuid,
) -> Result<Warren, sqlx::Error> {
let warren: Warren = sqlx::query_as(
"
SELECT
*
FROM
warrens
WHERE
id = $1
",
)
.bind(id)
.fetch_one(connection)
.await?;
Ok(warren)
}
async fn fetch_warrens(
&self,
connection: &mut SqliteConnection,
ids: &[Uuid],
) -> Result<Vec<Warren>, sqlx::Error> {
let mut ids_as_string = ids.into_iter().fold(String::new(), |mut acc, id| {
let encoded = hex::encode(id.as_bytes());
acc.push_str("x'");
acc.push_str(encoded.as_str());
acc.push_str("',");
acc
});
ids_as_string.pop();
let warrens: Vec<Warren> = sqlx::query_as::<sqlx::Sqlite, Warren>(&format!(
"
SELECT
*
FROM
warrens
WHERE
id IN ({ids_as_string})
",
))
.fetch_all(&mut *connection)
.await?;
Ok(warrens)
}
async fn fetch_all_warrens(
&self,
connection: &mut SqliteConnection,
) -> Result<Vec<Warren>, sqlx::Error> {
let warrens: Vec<Warren> = sqlx::query_as::<sqlx::Sqlite, Warren>(
"
SELECT
*
FROM
warrens
",
)
.fetch_all(&mut *connection)
.await?;
Ok(warrens)
}
}