202 lines
4.8 KiB
Rust
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);
|
|
}
|
|
}
|