bazzar/crates/database_manager/src/product_photos.rs

202 lines
4.8 KiB
Rust

use crate::{db_async_handler, Result};
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
pub enum Error {
#[error("Failed to attach photo to product")]
Create,
#[error("Failed to load all product photos")]
All,
#[error("Failed to delete product photo")]
Delete,
}
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<model::ProductPhoto>>")]
pub struct AllProductPhotos;
db_async_handler!(
AllProductPhotos,
all_product_photos,
Vec<model::ProductPhoto>,
inner_all_product_photos
);
pub(crate) async fn all_product_photos(
_msg: AllProductPhotos,
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Vec<model::ProductPhoto>> {
sqlx::query_as(
r#"
SELECT id, product_id, photo_id
FROM product_photos
ORDER BY id ASC
"#,
)
.fetch_all(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
Error::All.into()
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<model::ProductPhoto>")]
pub struct CreateProductPhoto {
pub product_id: model::ProductId,
pub photo_id: model::PhotoId,
}
db_async_handler!(
CreateProductPhoto,
create_product_photo,
model::ProductPhoto,
inner_create_product_photo
);
pub(crate) async fn create_product_photo(
msg: CreateProductPhoto,
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<model::ProductPhoto> {
sqlx::query_as(
r#"
INSERT INTO product_photos(product_id, photo_id)
VALUES ($1, $2)
RETURNING id, product_id, photo_id
"#,
)
.bind(msg.product_id)
.bind(msg.photo_id)
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{:?}", e);
Error::Create.into()
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<Option<model::ProductPhoto>>")]
pub struct DeleteProductPhoto {
pub id: model::ProductPhotoId,
}
db_async_handler!(
DeleteProductPhoto,
delete_product_photo,
Option<model::ProductPhoto>,
inner_delete_product_photo
);
pub(crate) async fn delete_product_photo(
msg: DeleteProductPhoto,
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Option<model::ProductPhoto>> {
sqlx::query_as(
r#"
DELETE FROM product_photos
WHERE id = $1
RETURNING id, product_id, photo_id
"#,
)
.bind(msg.id)
.fetch_optional(pool)
.await
.map_err(|e| {
tracing::error!("{:?}", e);
Error::Delete.into()
})
}
#[cfg(test)]
mod tests {
use config::UpdateConfig;
use model::*;
use uuid::Uuid;
pub struct NoOpts;
impl UpdateConfig for NoOpts {}
use crate::*;
async fn test_product(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Product {
create_product(
CreateProduct {
name: ProductName::new(format!("{}", Uuid::new_v4())),
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
category: None,
price: Price::from_u32(4687),
deliver_days_flag: Days(vec![Day::Friday, Day::Sunday]),
},
t,
)
.await
.unwrap()
}
async fn test_photo(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Photo {
crate::create_photo(
CreatePhoto {
local_path: LocalPath::new(format!("{}", Uuid::new_v4())),
file_name: FileName::new(format!("{}", Uuid::new_v4())),
unique_name: UniqueName::new(format!("{}", Uuid::new_v4())),
},
t,
)
.await
.unwrap()
}
async fn test_product_photo(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> ProductPhoto {
let product = test_product(t).await;
let photo = test_photo(t).await;
create_product_photo(
CreateProductPhoto {
product_id: product.id,
photo_id: photo.id,
},
t,
)
.await
.unwrap()
}
#[actix::test]
async fn create_photo() {
testx::db_t_ref!(t);
test_product_photo(&mut t).await;
testx::db_rollback!(t);
}
#[actix::test]
async fn delete() {
testx::db_t_ref!(t);
let p1 = test_product_photo(&mut t).await;
let p2 = test_product_photo(&mut t).await;
let p3 = test_product_photo(&mut t).await;
let deleted = delete_product_photo(DeleteProductPhoto { id: p2.id }, &mut t)
.await
.unwrap();
testx::db_rollback!(t);
assert_ne!(deleted, Some(p1));
assert_eq!(deleted, Some(p2));
assert_ne!(deleted, Some(p3));
}
#[actix::test]
async fn create() {
testx::db_t_ref!(t);
test_product_photo(&mut t).await;
testx::db_rollback!(t);
}
}