262 lines
7.1 KiB
Rust
262 lines
7.1 KiB
Rust
use crate::{MultiLoad, Result};
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
|
pub enum Error {
|
|
#[error("Failed to create photo")]
|
|
Create,
|
|
#[error("Failed to fetch all photo")]
|
|
All,
|
|
#[error("Failed to fetch photos for products")]
|
|
PhotosForProducts,
|
|
}
|
|
|
|
#[derive(actix::Message)]
|
|
#[rtype(result = "Result<Vec<model::Photo>>")]
|
|
pub struct AllPhotos;
|
|
|
|
crate::db_async_handler!(AllPhotos, all_photos, Vec<model::Photo>, inner_all_photos);
|
|
|
|
pub(crate) async fn all_photos<'e, E>(_msg: AllPhotos, pool: E) -> Result<Vec<model::Photo>>
|
|
where
|
|
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
|
|
{
|
|
sqlx::query_as(
|
|
r#"
|
|
SELECT id, local_path, file_name, unique_name
|
|
FROM photos
|
|
ORDER BY id ASC
|
|
"#,
|
|
)
|
|
.fetch_all(pool)
|
|
.await
|
|
.map_err(|e| {
|
|
tracing::error!("{e:?}");
|
|
crate::Error::Photo(Error::All)
|
|
})
|
|
}
|
|
|
|
#[derive(actix::Message)]
|
|
#[rtype(result = "Result<model::Photo>")]
|
|
pub struct CreatePhoto {
|
|
/// Local FILE path
|
|
pub local_path: model::LocalPath,
|
|
/// Only file name, this part should be also included in `local_path`
|
|
pub file_name: model::FileName,
|
|
pub unique_name: model::UniqueName,
|
|
}
|
|
|
|
crate::db_async_handler!(CreatePhoto, create_photo, model::Photo, inner_create_photo);
|
|
|
|
pub(crate) async fn create_photo<'e, E>(msg: CreatePhoto, pool: E) -> Result<model::Photo>
|
|
where
|
|
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
|
|
{
|
|
sqlx::query_as(
|
|
r#"
|
|
INSERT INTO photos (file_name, local_path, unique_name)
|
|
VALUES ($1, $2, $3)
|
|
RETURNING id, local_path, file_name, unique_name
|
|
"#,
|
|
)
|
|
.bind(msg.file_name)
|
|
.bind(msg.local_path)
|
|
.bind(msg.unique_name)
|
|
.fetch_one(pool)
|
|
.await
|
|
.map_err(|e| {
|
|
tracing::error!("{e:?}");
|
|
crate::Error::Photo(Error::Create)
|
|
})
|
|
}
|
|
|
|
//######################################
|
|
// SPECIAL CASES
|
|
//######################################
|
|
|
|
#[derive(actix::Message)]
|
|
#[rtype(result = "Result<Vec<model::ProductLinkedPhoto>>")]
|
|
pub struct PhotosForProducts {
|
|
pub product_ids: Vec<model::ProductId>,
|
|
}
|
|
|
|
crate::db_async_handler!(
|
|
PhotosForProducts,
|
|
photos_for_products,
|
|
Vec<model::ProductLinkedPhoto>,
|
|
inner_photos_for_products
|
|
);
|
|
|
|
pub(crate) async fn photos_for_products(
|
|
msg: PhotosForProducts,
|
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
) -> Result<Vec<model::ProductLinkedPhoto>> {
|
|
tracing::debug!("all product ids {:?}", msg.product_ids);
|
|
let res: Vec<model::ProductLinkedPhoto> = MultiLoad::new(
|
|
pool,
|
|
r#"
|
|
SELECT photos.id AS photo_id,
|
|
photos.local_path AS local_path,
|
|
photos.file_name AS file_name,
|
|
product_photos.product_id AS product_id,
|
|
photos.unique_name AS unique_name
|
|
FROM photos
|
|
INNER JOIN product_photos
|
|
ON photos.id = product_photos.photo_id
|
|
WHERE
|
|
"#,
|
|
" product_photos.product_id =",
|
|
)
|
|
.with_sorting("photos.id ASC")
|
|
.load(
|
|
msg.product_ids.len(),
|
|
msg.product_ids.into_iter().map(|id| *id),
|
|
|e| {
|
|
tracing::error!("{}", e);
|
|
dbg!(e);
|
|
crate::Error::Photo(Error::PhotosForProducts)
|
|
},
|
|
)
|
|
.await?;
|
|
tracing::debug!("product linked photos {:?}", res);
|
|
Ok(res)
|
|
}
|
|
|
|
#[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_product_photo(
|
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
product_id: ProductId,
|
|
) -> ProductPhoto {
|
|
let photo = test_photo(&mut *t, None, None, None).await;
|
|
|
|
create_product_photo(
|
|
CreateProductPhoto {
|
|
product_id,
|
|
photo_id: photo.id,
|
|
},
|
|
t,
|
|
)
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
async fn test_photo(
|
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
local_path: Option<String>,
|
|
file_name: Option<String>,
|
|
unique_name: Option<String>,
|
|
) -> Photo {
|
|
super::create_photo(
|
|
CreatePhoto {
|
|
local_path: LocalPath::new(
|
|
local_path.unwrap_or_else(|| format!("{}", Uuid::new_v4())),
|
|
),
|
|
file_name: FileName::new(
|
|
file_name.unwrap_or_else(|| format!("{}", Uuid::new_v4())),
|
|
),
|
|
unique_name: UniqueName::new(
|
|
unique_name.unwrap_or_else(|| format!("{}", Uuid::new_v4())),
|
|
),
|
|
},
|
|
t,
|
|
)
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
#[actix::test]
|
|
async fn create_photo() {
|
|
testx::db_t_ref!(t);
|
|
|
|
test_photo(&mut t, None, None, None).await;
|
|
|
|
testx::db_rollback!(t);
|
|
}
|
|
|
|
#[actix::test]
|
|
async fn all() {
|
|
testx::db_t_ref!(t);
|
|
|
|
let p1 = test_photo(&mut t, None, None, None).await;
|
|
let p2 = test_photo(&mut t, None, None, None).await;
|
|
let p3 = test_photo(&mut t, None, None, None).await;
|
|
|
|
let all = all_photos(AllPhotos, &mut t).await.unwrap();
|
|
|
|
testx::db_rollback!(t);
|
|
assert_eq!(all, vec![p1, p2, p3]);
|
|
}
|
|
|
|
#[actix::test]
|
|
async fn products_photos() {
|
|
testx::db_t_ref!(t);
|
|
|
|
let product_1 = test_product(&mut t).await;
|
|
let p1 = test_product_photo(&mut t, product_1.id).await;
|
|
let p2 = test_product_photo(&mut t, product_1.id).await;
|
|
let p3 = test_product_photo(&mut t, product_1.id).await;
|
|
|
|
let product_2 = test_product(&mut t).await;
|
|
let _p4 = test_product_photo(&mut t, product_2.id).await;
|
|
let _p5 = test_product_photo(&mut t, product_2.id).await;
|
|
let _p6 = test_product_photo(&mut t, product_2.id).await;
|
|
|
|
let product_3 = test_product(&mut t).await;
|
|
let p7 = test_product_photo(&mut t, product_3.id).await;
|
|
let p8 = test_product_photo(&mut t, product_3.id).await;
|
|
let p9 = test_product_photo(&mut t, product_3.id).await;
|
|
|
|
let mut all = photos_for_products(
|
|
PhotosForProducts {
|
|
product_ids: vec![product_1.id, product_3.id],
|
|
},
|
|
&mut t,
|
|
)
|
|
.await
|
|
.unwrap()
|
|
.into_iter()
|
|
.map(|p| p.photo_id)
|
|
.collect::<Vec<PhotoId>>();
|
|
all.sort_by(|left, right| left.partial_cmp(right).unwrap());
|
|
|
|
testx::db_rollback!(t);
|
|
assert_eq!(
|
|
all,
|
|
vec![
|
|
p1.photo_id,
|
|
p2.photo_id,
|
|
p3.photo_id,
|
|
p7.photo_id,
|
|
p8.photo_id,
|
|
p9.photo_id
|
|
]
|
|
);
|
|
}
|
|
}
|