Photos
This commit is contained in:
parent
e426fd5f31
commit
6a4c38593f
@ -1 +1,264 @@
|
||||
use db_utils::PgT;
|
||||
use model::v2::*;
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AllPhotos {
|
||||
pub limit: i32,
|
||||
pub offset: i32,
|
||||
}
|
||||
|
||||
impl AllPhotos {
|
||||
pub async fn run(self, pool: &mut PgT<'_>) -> Result<Vec<Photo>> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, local_path, file_name, unique_name
|
||||
FROM photos
|
||||
ORDER BY id ASC
|
||||
LIMIT $1 OFFSET $2
|
||||
"#,
|
||||
)
|
||||
.bind(self.limit)
|
||||
.bind(self.offset)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
Error::All
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CreatePhoto {
|
||||
/// Local FILE path
|
||||
pub local_path: LocalPath,
|
||||
/// Only file name, this part should be also included in `local_path`
|
||||
pub file_name: FileName,
|
||||
pub unique_name: UniqueName,
|
||||
}
|
||||
|
||||
impl CreatePhoto {
|
||||
pub async fn run(self, pool: &mut PgT<'_>) -> Result<Photo> {
|
||||
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(self.file_name)
|
||||
.bind(self.local_path)
|
||||
.bind(self.unique_name)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
Error::Create
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//######################################
|
||||
// SPECIAL CASES
|
||||
//######################################
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PhotosForProducts {
|
||||
pub product_ids: Vec<ProductId>,
|
||||
}
|
||||
|
||||
impl PhotosForProducts {
|
||||
pub async fn run(self, pool: &mut PgT<'_>) -> Result<Vec<model::ProductLinkedPhoto>> {
|
||||
tracing::debug!("all product ids {:?}", self.product_ids);
|
||||
let res: Vec<model::ProductLinkedPhoto> = db_utils::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
|
||||
INNER JOIN product_variants
|
||||
ON product_variants.id = product_photos.product_variant_id
|
||||
INNER JOIN products
|
||||
ON product_variants.product_id = products.id
|
||||
WHERE
|
||||
"#,
|
||||
" products.id =",
|
||||
)
|
||||
.with_sorting("photos.id ASC")
|
||||
.allow_over_max()
|
||||
.with_size(1000)
|
||||
.load(
|
||||
self.product_ids.len(),
|
||||
self.product_ids.into_iter().map(|id| *id),
|
||||
|e| {
|
||||
tracing::error!("{}", e);
|
||||
dbg!(e);
|
||||
Error::PhotosForProducts
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
tracing::debug!("product linked photos {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use config::UpdateConfig;
|
||||
use model::v2::*;
|
||||
use model::Day;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct NoOpts;
|
||||
|
||||
impl UpdateConfig for NoOpts {}
|
||||
|
||||
use super::*;
|
||||
use crate::db::product_photos::*;
|
||||
use crate::db::product_variants::*;
|
||||
use crate::db::products::*;
|
||||
|
||||
async fn test_product(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Product {
|
||||
CreateProduct {
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
category: None,
|
||||
deliver_days_flag: Days(vec![Day::Friday, Day::Sunday]),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_product_photo(
|
||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
product_variant_id: ProductVariantId,
|
||||
) -> ProductPhoto {
|
||||
let photo = test_photo(&mut *t, None, None, None).await;
|
||||
|
||||
CreateProductPhoto {
|
||||
product_variant_id,
|
||||
photo_id: photo.id,
|
||||
}
|
||||
.run(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 {
|
||||
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())),
|
||||
),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
|
||||
CreateProductVariant {
|
||||
product_id,
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
|
||||
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
|
||||
price: Default::default(),
|
||||
}
|
||||
.run(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 product_variant_1 = test_product_variant(product_1.id, &mut t).await;
|
||||
let p1 = test_product_photo(&mut t, product_variant_1.id).await;
|
||||
let p2 = test_product_photo(&mut t, product_variant_1.id).await;
|
||||
let p3 = test_product_photo(&mut t, product_variant_1.id).await;
|
||||
|
||||
let product_2 = test_product(&mut t).await;
|
||||
let product_variant_2 = test_product_variant(product_2.id, &mut t).await;
|
||||
let _p4 = test_product_photo(&mut t, product_variant_2.id).await;
|
||||
let _p5 = test_product_photo(&mut t, product_variant_2.id).await;
|
||||
let _p6 = test_product_photo(&mut t, product_variant_2.id).await;
|
||||
|
||||
let product_3 = test_product(&mut t).await;
|
||||
let product_variant_3 = test_product_variant(product_3.id, &mut t).await;
|
||||
let p7 = test_product_photo(&mut t, product_variant_3.id).await;
|
||||
let p8 = test_product_photo(&mut t, product_variant_3.id).await;
|
||||
let p9 = test_product_photo(&mut t, product_variant_3.id).await;
|
||||
|
||||
let mut all = PhotosForProducts {
|
||||
product_ids: vec![product_1.id, product_3.id],
|
||||
}
|
||||
.run(&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
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ mod tests {
|
||||
impl UpdateConfig for NoOpts {}
|
||||
|
||||
use super::*;
|
||||
use crate::db::photos::*;
|
||||
use crate::db::products::*;
|
||||
|
||||
async fn test_product(t: &mut PgT<'_>) -> Product {
|
||||
@ -123,8 +124,8 @@ mod tests {
|
||||
CreateProductVariant {
|
||||
product_id,
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
short_description: ProductShortDesc::new("ajs9d8ua9sdu9ahsd98has"),
|
||||
long_description: ProductLongDesc::new("hja89sdy9yha9sdy98ayusd9hya9sy8dh"),
|
||||
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
|
||||
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
|
||||
price: Default::default(),
|
||||
}
|
||||
.run(t)
|
||||
|
Loading…
Reference in New Issue
Block a user