use crate::dtos::*; use crate::image_service; use crate::models::Product; use crate::repo::{photo_set_repo, product_repo}; use crate::types::DbPool; use actix_multipart::Multipart; use actix_web::{get, patch, post, web, Error, HttpResponse, Responder}; use futures::{StreamExt, TryStreamExt}; fn to_internal_error(e: impl std::error::Error) -> HttpResponse { HttpResponse::InternalServerError().body(e.to_string()) } #[get("/")] async fn hello() -> impl Responder { HttpResponse::Ok().body("Hey, this is an API!") } #[get("/products")] async fn get_products(pool: web::Data) -> Result { let conn = pool.get().map_err(to_internal_error)?; let products: Vec = web::block(move || product_repo::find_all(&conn)) .await .map_err(to_internal_error)? .into_iter() .map(|p| p.into()) .collect(); Ok(HttpResponse::Ok().json(products)) } #[patch("/products")] async fn patch_product( pool: web::Data, web::Json(patch): web::Json, ) -> Result { let id = patch.id; // get product referenced by patch let conn = pool.get().map_err(to_internal_error)?; let mut product = web::block(move || product_repo::find(&conn, id)) .await .map_err(to_internal_error)? .ok_or_else(|| HttpResponse::NotFound().finish())?; // get photo set referenced by patch if let Some(id) = patch.photo_set { let conn = pool.get().map_err(to_internal_error)?; let photo_set = web::block(move || photo_set_repo::find(&conn, id)) .await .map_err(to_internal_error)? .ok_or_else(|| HttpResponse::NotFound().body("Photo set not found"))?; product.photo_set = photo_set; } // update product fields patch.patch(&mut product); // store the updated product let conn = pool.get().map_err(to_internal_error)?; let product = web::block(move || product_repo::store(&conn, product)) .await .map_err(to_internal_error)?; Ok(HttpResponse::Ok().json(ProductGet::from(product))) } #[post("/products")] async fn post_product( pool: web::Data, web::Json(post): web::Json, ) -> Result { // find associated photo set let photo_set = post.photo_set; let conn = pool.get().map_err(to_internal_error)?; let photo_set = web::block(move || photo_set_repo::find(&conn, photo_set)) .await .map_err(to_internal_error)? .ok_or_else(|| HttpResponse::NotFound().body("Photo set not found."))?; // create new product let product = Product { id: None, name: post.name, quantity: post.quantity, cents: post.cents, description: post.description, featured: post.featured, category: post.category_path, photo_set, }; // store product let conn = pool.get().map_err(to_internal_error)?; let product = web::block(move || product_repo::store(&conn, product)) .await .map_err(to_internal_error)?; Ok(HttpResponse::Ok().json::(product.into())) } #[post("/photos")] async fn post_photo( pool: web::Data, mut payload: Multipart, ) -> Result { let mut responses: Vec = Vec::new(); while let Some(mut field) = payload .try_next() .await .map_err(|e| HttpResponse::BadRequest().body(e.to_string()))? { // grab all bytes let mut data: Vec = Vec::new(); while let Some(chunk) = field.next().await { data.extend(chunk?); } // create new photo_set let photo_set = web::block(move || image_service::generate_photo_set(&data)) .await .map_err(to_internal_error)?; let conn = pool.get().map_err(to_internal_error)?; let photo_set = web::block(move || photo_set_repo::store(&conn, photo_set)) .await .map_err(to_internal_error)?; responses.push(photo_set.into()); } Ok(HttpResponse::Ok().json(responses)) }