From 76a782599b4ecc4ecb9b0ce7acc6420ed9e1ec8e Mon Sep 17 00:00:00 2001 From: "Adam T. Carpenter" Date: Tue, 20 Oct 2020 09:50:08 -0400 Subject: back to basics -- working on the most-required functionality first, like handlers --- .../2020-10-04-235458_create_images/down.sql | 1 - .../2020-10-04-235458_create_images/up.sql | 5 -- .../migrations/2020-10-09-210205_photos/down.sql | 1 + .../migrations/2020-10-09-210205_photos/up.sql | 5 ++ .../2020-10-09-210247_photo_sets/down.sql | 1 + .../migrations/2020-10-09-210247_photo_sets/up.sql | 12 +++ dichroism/src/error.rs | 3 +- dichroism/src/handlers.rs | 79 ++++++++--------- dichroism/src/image_api.rs | 98 ---------------------- dichroism/src/image_repo.rs | 57 ------------- dichroism/src/main.rs | 8 +- dichroism/src/models.rs | 12 --- dichroism/src/models/mod.rs | 13 +++ dichroism/src/models/new_photo.rs | 6 ++ dichroism/src/models/new_photo_data.rs | 95 +++++++++++++++++++++ dichroism/src/models/new_photo_set.rs | 8 ++ dichroism/src/models/photo.rs | 5 ++ dichroism/src/models/photo_set.rs | 8 ++ dichroism/src/models/product.rs | 26 ++++++ dichroism/src/photo_repo.rs | 41 +++++++++ dichroism/src/product_api.rs | 0 dichroism/src/product_repo.rs | 19 +++-- dichroism/src/schema.rs | 17 +++- 23 files changed, 293 insertions(+), 227 deletions(-) delete mode 100644 dichroism/migrations/2020-10-04-235458_create_images/down.sql delete mode 100644 dichroism/migrations/2020-10-04-235458_create_images/up.sql create mode 100644 dichroism/migrations/2020-10-09-210205_photos/down.sql create mode 100644 dichroism/migrations/2020-10-09-210205_photos/up.sql create mode 100644 dichroism/migrations/2020-10-09-210247_photo_sets/down.sql create mode 100644 dichroism/migrations/2020-10-09-210247_photo_sets/up.sql delete mode 100644 dichroism/src/image_api.rs delete mode 100644 dichroism/src/image_repo.rs delete mode 100644 dichroism/src/models.rs create mode 100644 dichroism/src/models/mod.rs create mode 100644 dichroism/src/models/new_photo.rs create mode 100644 dichroism/src/models/new_photo_data.rs create mode 100644 dichroism/src/models/new_photo_set.rs create mode 100644 dichroism/src/models/photo.rs create mode 100644 dichroism/src/models/photo_set.rs create mode 100644 dichroism/src/models/product.rs create mode 100644 dichroism/src/photo_repo.rs delete mode 100644 dichroism/src/product_api.rs diff --git a/dichroism/migrations/2020-10-04-235458_create_images/down.sql b/dichroism/migrations/2020-10-04-235458_create_images/down.sql deleted file mode 100644 index bc3690f..0000000 --- a/dichroism/migrations/2020-10-04-235458_create_images/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE "images" diff --git a/dichroism/migrations/2020-10-04-235458_create_images/up.sql b/dichroism/migrations/2020-10-04-235458_create_images/up.sql deleted file mode 100644 index ba3b4c6..0000000 --- a/dichroism/migrations/2020-10-04-235458_create_images/up.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE "images" ( - "id" INTEGER NOT NULL, - "path" TEXT NOT NULL UNIQUE, - PRIMARY KEY("id") -) diff --git a/dichroism/migrations/2020-10-09-210205_photos/down.sql b/dichroism/migrations/2020-10-09-210205_photos/down.sql new file mode 100644 index 0000000..1f9850a --- /dev/null +++ b/dichroism/migrations/2020-10-09-210205_photos/down.sql @@ -0,0 +1 @@ +DROP TABLE "photos" diff --git a/dichroism/migrations/2020-10-09-210205_photos/up.sql b/dichroism/migrations/2020-10-09-210205_photos/up.sql new file mode 100644 index 0000000..0e74ff9 --- /dev/null +++ b/dichroism/migrations/2020-10-09-210205_photos/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE "photos" ( + "id" INTEGER NOT NULL, + "path" TEXT NOT NULL UNIQUE, + PRIMARY KEY("id") +) diff --git a/dichroism/migrations/2020-10-09-210247_photo_sets/down.sql b/dichroism/migrations/2020-10-09-210247_photo_sets/down.sql new file mode 100644 index 0000000..d02736a --- /dev/null +++ b/dichroism/migrations/2020-10-09-210247_photo_sets/down.sql @@ -0,0 +1 @@ +DROP TABLE "photo_sets" diff --git a/dichroism/migrations/2020-10-09-210247_photo_sets/up.sql b/dichroism/migrations/2020-10-09-210247_photo_sets/up.sql new file mode 100644 index 0000000..98e6050 --- /dev/null +++ b/dichroism/migrations/2020-10-09-210247_photo_sets/up.sql @@ -0,0 +1,12 @@ +CREATE TABLE "photo_sets" ( + "id" INTEGER NOT NULL, + "base" INTEGER NOT NULL, + "fullsize" INTEGER NOT NULL, + "thumbnail" INTEGER NOT NULL, + "original" INTEGER NOT NULL, + FOREIGN KEY("thumbnail") REFERENCES "photos"("id"), + FOREIGN KEY("original") REFERENCES "photos"("id"), + FOREIGN KEY("base") REFERENCES "photos"("id"), + FOREIGN KEY("fullsize") REFERENCES "photos"("id"), + PRIMARY KEY("id") +) diff --git a/dichroism/src/error.rs b/dichroism/src/error.rs index 43933d4..534f070 100644 --- a/dichroism/src/error.rs +++ b/dichroism/src/error.rs @@ -1,7 +1,7 @@ #[derive(Debug)] pub enum DichroismError { UriDataExtract, - InvalidImageRoot, + InvalidPhotoRoot, ImageWrite, } @@ -12,4 +12,3 @@ impl std::fmt::Display for DichroismError { write!(f, "{}", self) } } - diff --git a/dichroism/src/handlers.rs b/dichroism/src/handlers.rs index f8f10d3..1f657c2 100644 --- a/dichroism/src/handlers.rs +++ b/dichroism/src/handlers.rs @@ -1,53 +1,54 @@ -use super::image_repo; -use super::product_repo; +//use super::product_repo; use super::types::DbPool; -use crate::config::Config; -use crate::image_api; -use actix_web::{get, post, web, Error, HttpResponse, Responder}; +//use crate::config::Config; +use actix_web::{get, patch, post, web, Error, HttpResponse, Responder}; #[get("/")] async fn hello() -> impl Responder { HttpResponse::Ok().body("Hey, this is an API!") } -#[get("/images")] -async fn get_images(pool: web::Data) -> Result { - let conn = pool.get().expect("Couldn't get DB connection from pool."); - let images = web::block(move || image_repo::read_images(&conn)) - .await - .map_err(|e| { - eprintln!("{}", e); - HttpResponse::InternalServerError().finish() - })?; - dbg!(&images); - Ok(HttpResponse::Ok().json(images)) +#[get("/products")] +async fn get_products(_pool: web::Data) -> Result { + dbg!("got products"); + Ok(HttpResponse::Ok().finish()) + //let conn = pool.get().expect("Couldn't get DB connection from pool."); + //let products = web::block(move || product_repo::read_products(&conn)) + // .await + // .map_err(|e| { + // eprintln!("{}", e); + // HttpResponse::InternalServerError().finish() + // })?; + //Ok(HttpResponse::Ok().json(products)) } -#[post("/images")] -async fn create_image(_config: web::Data, req_body: String) -> impl Responder { - // let data = match image_api::extract_data(&req_body) { - // Err(e) => return HttpResponse::BadRequest().body(format!("fail: {}", e.to_string())), - // Ok(d) => d, - // }; +#[patch("/products/{id}")] +async fn update_product( + _pool: web::Data, + id: web::Path, + updated_product: web::Json, +) -> Result { + dbg!(id, updated_product); + Ok(HttpResponse::Ok().finish()) +} - // if let Err(e) = image_api::generate_images(data) { - // return HttpResponse::BadRequest().body(format!( - // "Unable to extract image from data URI: {}", - // e.to_string() - // )); - // } +#[post("/products")] +async fn create_product( + _pool: web::Data, + new_product: web::Json, +) -> Result { + dbg!(new_product); + Ok(HttpResponse::Ok().finish()) +} - HttpResponse::Ok().body("Image created.") +#[derive(Debug, Deserialize)] +pub struct NewProduct { + pub name: String, + pub description: String, } -#[get("/products")] -async fn get_products(pool: web::Data) -> Result { - let conn = pool.get().expect("Couldn't get DB connection from pool."); - let products = web::block(move || product_repo::read_products(&conn)) - .await - .map_err(|e| { - eprintln!("{}", e); - HttpResponse::InternalServerError().finish() - })?; - Ok(HttpResponse::Ok().json(products)) +#[derive(Debug, Deserialize)] +pub struct UpdatedProduct { + pub name: Option, + pub description: Option, } diff --git a/dichroism/src/image_api.rs b/dichroism/src/image_api.rs deleted file mode 100644 index 07b9552..0000000 --- a/dichroism/src/image_api.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::error::DichroismError; -use crate::result::Result; -use base64::decode; -use image::imageops::FilterType; -use image::DynamicImage; -use image::GenericImageView; -use regex::Regex; -use std::path::PathBuf; -use uuid::Uuid; - -use once_cell::sync::Lazy; - -static DATA_URI_RE: Lazy = Lazy::new(|| { - Regex::new("^data:image/(png|jpeg);base64,(?P.+)") - .expect("Couldn't parse data URI Regex.") -}); - -pub struct NewProductImageData { - original: DynamicImage, // original, just for safe-keeping - fullsize: DynamicImage, // full-size, "zoomed" view - base: DynamicImage, // basic viewing - thumbnail: DynamicImage, // tiny, square thumbnail -} - -impl NewProductImageData { - pub fn from_data_uri(uri: &str) -> Result { - let data = DATA_URI_RE - .captures(uri) - .ok_or(DichroismError::UriDataExtract)? - .name("data") - .ok_or(DichroismError::UriDataExtract)? - .as_str(); - let original = image::load_from_memory(&decode(data)?)?; - let fullsize = original.resize(1000, 1000, FilterType::Lanczos3); - let base = original.resize(640, 640, FilterType::Lanczos3); - - let (width, height) = original.dimensions(); - let thumbnail = if width > height { - let offset = (width - height) / 2; - original.crop_imm(offset, 0, width - offset * 2, height) - } else { - let offset = (height - width) / 2; - original.crop_imm(0, offset, width, height - offset * 2) - } - .resize(300, 300, FilterType::Lanczos3); - - Ok(NewProductImageData { - original, - fullsize, - base, - thumbnail, - }) - } - - pub fn commit(self, prefix: &str) -> Result { - Ok(NewProductImages { - original: self.commit_single(prefix, &self.original)?, - fullsize: self.commit_single(prefix, &self.fullsize)?, - base: self.commit_single(prefix, &self.base)?, - thumbnail: self.commit_single(prefix, &self.thumbnail)?, - }) - } - - fn commit_single(&self, prefix: &str, image: &DynamicImage) -> Result { - let base_name = Uuid::new_v3(&Uuid::NAMESPACE_OID, &image.to_bytes()) - .to_hyphenated() - .to_string(); - let mut path = PathBuf::new(); - path.push(prefix); - path.push(base_name); - path.set_extension("jpg"); - image.save(&path)?; - Ok(path.to_str().ok_or(DichroismError::ImageWrite)?.to_string()) - } -} - -pub struct NewProductImages { - original: String, - fullsize: String, - base: String, - thumbnail: String, -} - -#[cfg(test)] -mod tests { - use super::*; - - const TEST_DATA_URI: &str = include_str!("unit_test_data/img_data_uri.txt"); - const TEST_DATA_BASE64: &str = include_str!("unit_test_data/test_data_base64.txt"); - - #[test] - fn test_gen_product_images() { - NewProductImageData::from_data_uri(TEST_DATA_URI.trim()) - .unwrap() - .commit(".") - .unwrap(); - } -} diff --git a/dichroism/src/image_repo.rs b/dichroism/src/image_repo.rs deleted file mode 100644 index e0f420b..0000000 --- a/dichroism/src/image_repo.rs +++ /dev/null @@ -1,57 +0,0 @@ -use super::models::{NewProductImg, ProductImg}; -use diesel::prelude::*; -use diesel::result::Error; - -type DBConn = SqliteConnection; - -pub fn read_images(conn: &DBConn) -> Result, Error> { - use crate::schema::images::dsl::*; - let results = images.load::(conn)?; - Ok(results) -} - -fn read_images_by_path(conn: &DBConn, path: &str) -> Result, Error> { - use crate::schema::images::dsl::*; - let results = images.filter(path.eq(path)).load::(conn)?; - Ok(results) -} - -pub fn read_image_by_path(conn: &DBConn, path: &str) -> Result, Error> { - use crate::schema::images::dsl::*; - let results = images - .filter(path.eq(path)) - .limit(1) - .load::(conn)?; - Ok(results.first().cloned()) -} - -pub fn read_image_by_id(conn: &DBConn, id: i32) -> Result, Error> { - use crate::schema::images::dsl::*; - let results = images.filter(id.eq(id)).limit(1).load::(conn)?; - Ok(results.first().cloned()) -} - -pub fn create_image(conn: &DBConn, new_image: NewProductImg) -> Result, Error> { - use super::schema::images; - diesel::insert_into(images::table) - .values(&new_image) - .execute(conn)?; - read_image_by_path(conn, &new_image.path) -} - -pub fn update_image() { - todo!() -} - -pub fn delete_image() { - todo!() -} - -#[cfg(test)] -mod tests { - - #[test] - fn test() -> std::result::Result<(), Box> { - Ok(()) - } -} diff --git a/dichroism/src/main.rs b/dichroism/src/main.rs index 57ac652..fb5e2c8 100644 --- a/dichroism/src/main.rs +++ b/dichroism/src/main.rs @@ -15,9 +15,8 @@ mod config; mod constants; mod error; mod handlers; -mod image_api; -mod image_repo; mod models; +mod photo_repo; mod product_repo; mod result; mod schema; @@ -39,8 +38,9 @@ async fn main() -> Result<()> { .data(config.clone()) .data(pool.clone()) .service(handlers::hello) - .service(handlers::create_image) - .service(handlers::get_images) + .service(handlers::get_products) + .service(handlers::update_product) + .service(handlers::create_product) }); let mut listenfd = ListenFd::from_env(); diff --git a/dichroism/src/models.rs b/dichroism/src/models.rs deleted file mode 100644 index 0485237..0000000 --- a/dichroism/src/models.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[derive(Debug, Queryable, Serialize, Clone)] -pub struct ProductImg { - pub id: i32, - pub path: String, -} - -use super::schema::images; -#[table_name = "images"] -#[derive(Debug, Insertable)] -pub struct NewProductImg { - pub path: String, -} diff --git a/dichroism/src/models/mod.rs b/dichroism/src/models/mod.rs new file mode 100644 index 0000000..e0fc3bd --- /dev/null +++ b/dichroism/src/models/mod.rs @@ -0,0 +1,13 @@ +mod new_photo; +mod new_photo_data; +mod new_photo_set; +mod photo; +mod photo_set; +mod product; + +pub use new_photo::*; +pub use new_photo_data::*; +pub use new_photo_set::*; +pub use photo::*; +pub use photo_set::*; +pub use product::*; diff --git a/dichroism/src/models/new_photo.rs b/dichroism/src/models/new_photo.rs new file mode 100644 index 0000000..d348e95 --- /dev/null +++ b/dichroism/src/models/new_photo.rs @@ -0,0 +1,6 @@ +use crate::schema::photos; +#[table_name = "photos"] +#[derive(Debug, Insertable)] +pub struct NewPhoto { + pub path: String, +} diff --git a/dichroism/src/models/new_photo_data.rs b/dichroism/src/models/new_photo_data.rs new file mode 100644 index 0000000..19a6557 --- /dev/null +++ b/dichroism/src/models/new_photo_data.rs @@ -0,0 +1,95 @@ +use super::NewPhoto; +use super::NewPhotoSet; +use crate::error::DichroismError; +use crate::result::Result; +use base64::decode; +use image::imageops::FilterType; +use image::DynamicImage; +use image::GenericImageView; +use regex::Regex; +use std::path::PathBuf; +use uuid::Uuid; + +use once_cell::sync::Lazy; +static DATA_URI_RE: Lazy = Lazy::new(|| { + Regex::new("^data:image/(png|jpeg);base64,(?P.+)") + .expect("Couldn't parse data URI Regex.") +}); + +pub struct NewPhotoSetData { + original: DynamicImage, // original, just for safe-keeping + fullsize: DynamicImage, // full-size, "zoomed" view + base: DynamicImage, // basic viewing + thumbnail: DynamicImage, // tiny, square thumbnail +} + +impl NewPhotoSetData { + pub fn from_data_uri(uri: &str) -> Result { + let data = DATA_URI_RE + .captures(uri) + .ok_or(DichroismError::UriDataExtract)? + .name("data") + .ok_or(DichroismError::UriDataExtract)? + .as_str(); + let original = image::load_from_memory(&decode(data)?)?; + let fullsize = original.resize(1000, 1000, FilterType::Lanczos3); + let base = original.resize(640, 640, FilterType::Lanczos3); + + let (width, height) = original.dimensions(); + let thumbnail = if width > height { + let offset = (width - height) / 2; + original.crop_imm(offset, 0, width - offset * 2, height) + } else { + let offset = (height - width) / 2; + original.crop_imm(0, offset, width, height - offset * 2) + } + .resize(300, 300, FilterType::Lanczos3); + + Ok(NewPhotoSetData { + original, + fullsize, + base, + thumbnail, + }) + } + + pub fn commit(self, prefix: &str) -> Result { + Ok(NewPhotoSet { + original: self.commit_single(prefix, &self.original)?, + fullsize: self.commit_single(prefix, &self.fullsize)?, + base: self.commit_single(prefix, &self.base)?, + thumbnail: self.commit_single(prefix, &self.thumbnail)?, + }) + } + + fn commit_single(&self, prefix: &str, image: &DynamicImage) -> Result { + let base_name = Uuid::new_v3(&Uuid::NAMESPACE_OID, &image.to_bytes()) + .to_hyphenated() + .to_string(); + let mut path = PathBuf::new(); + path.push(prefix); + path.push(base_name); + path.set_extension("jpg"); + image.save(&path)?; + + Ok(NewPhoto { + path: path.to_str().ok_or(DichroismError::ImageWrite)?.to_string(), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_DATA_URI: &str = include_str!("../unit_test_data/img_data_uri.txt"); + const TEST_DATA_BASE64: &str = include_str!("../unit_test_data/test_data_base64.txt"); + + #[test] + fn test_gen_product_images() { + NewPhotoSetData::from_data_uri(TEST_DATA_URI.trim()) + .unwrap() + .commit(".") + .unwrap(); + } +} diff --git a/dichroism/src/models/new_photo_set.rs b/dichroism/src/models/new_photo_set.rs new file mode 100644 index 0000000..e907592 --- /dev/null +++ b/dichroism/src/models/new_photo_set.rs @@ -0,0 +1,8 @@ +use super::NewPhoto; + +pub struct NewPhotoSet { + pub original: NewPhoto, + pub fullsize: NewPhoto, + pub base: NewPhoto, + pub thumbnail: NewPhoto, +} diff --git a/dichroism/src/models/photo.rs b/dichroism/src/models/photo.rs new file mode 100644 index 0000000..2e8b3bf --- /dev/null +++ b/dichroism/src/models/photo.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Queryable, Serialize, Clone)] +pub struct Photo { + pub id: i32, + pub path: String, +} diff --git a/dichroism/src/models/photo_set.rs b/dichroism/src/models/photo_set.rs new file mode 100644 index 0000000..2f6c128 --- /dev/null +++ b/dichroism/src/models/photo_set.rs @@ -0,0 +1,8 @@ +#[derive(Debug, Queryable, Clone)] +pub struct PhotoSet { + pub id: i32, + pub base: i32, + pub fullsize: i32, + pub thumbnail: i32, + pub original: i32, +} diff --git a/dichroism/src/models/product.rs b/dichroism/src/models/product.rs new file mode 100644 index 0000000..9b81b8a --- /dev/null +++ b/dichroism/src/models/product.rs @@ -0,0 +1,26 @@ +#[derive(Debug, Clone, Queryable, Serialize)] +pub struct Product { + pub id: i32, + pub name: String, + pub quantity: i32, + pub cents: i32, + pub description: String, + pub featured: bool, + pub category_path: String, + pub photo_set: PhotoSet, +} + +#[derive(Debug, Clone, Serialize)] +pub struct PhotoSet { + pub id: i32, + pub base: i32, + pub fullsize: i32, + pub thumbnail: i32, + pub original: i32, +} + +#[derive(Debug, Clone, Serialize)] +pub struct Photo { + pub id: i32, + pub path: String, +} diff --git a/dichroism/src/photo_repo.rs b/dichroism/src/photo_repo.rs new file mode 100644 index 0000000..64390c8 --- /dev/null +++ b/dichroism/src/photo_repo.rs @@ -0,0 +1,41 @@ +use super::models::*; +use diesel::prelude::*; +use diesel::result::Error; + +type DBConn = SqliteConnection; + +pub fn read_photos(conn: &DBConn) -> Result, Error> { + use crate::schema::photos::dsl::*; + let results = photos.load::(conn)?; + Ok(results) +} + +fn read_photos_by_path(conn: &DBConn, path: &str) -> Result, Error> { + use crate::schema::photos::dsl::*; + let results = photos.filter(path.eq(path)).load::(conn)?; + Ok(results) +} + +pub fn read_photo_by_path(conn: &DBConn, path: &str) -> Result, Error> { + use crate::schema::photos::dsl::*; + let results = photos.filter(path.eq(path)).limit(1).load::(conn)?; + Ok(results.first().cloned()) +} + +pub fn read_photo_by_id(conn: &DBConn, id: i32) -> Result, Error> { + use crate::schema::photos::dsl::*; + let results = photos.filter(id.eq(id)).limit(1).load::(conn)?; + Ok(results.first().cloned()) +} + +pub fn create_photo(conn: &DBConn, new_photo: NewPhoto) -> Result, Error> { + use super::schema::photos; + diesel::insert_into(photos::table) + .values(&new_photo) + .execute(conn)?; + read_photo_by_path(conn, &new_photo.path) +} + +pub fn update_photo() { + todo!() +} diff --git a/dichroism/src/product_api.rs b/dichroism/src/product_api.rs deleted file mode 100644 index e69de29..0000000 diff --git a/dichroism/src/product_repo.rs b/dichroism/src/product_repo.rs index 67c5d3e..852b930 100644 --- a/dichroism/src/product_repo.rs +++ b/dichroism/src/product_repo.rs @@ -1,18 +1,21 @@ -use super::models::ProductImg; +use super::models::Product; use diesel::prelude::*; use diesel::result::Error; type DBConn = SqliteConnection; -pub fn read_products(_conn: &DBConn) -> Result, Error> { +pub fn create_product(conn: &DBConn) -> Result { todo!() } -#[cfg(test)] -mod tests { +pub fn read_products(_conn: &DBConn) -> Result, Error> { + todo!() +} + +pub fn update_product(conn: &DBConn) -> Result { + todo!() +} - #[test] - fn test() -> std::result::Result<(), Box> { - Ok(()) - } +pub fn delete_product(conn: &DBConn) -> Result<(), Error> { + todo!() } diff --git a/dichroism/src/schema.rs b/dichroism/src/schema.rs index e1c4408..5b038e6 100644 --- a/dichroism/src/schema.rs +++ b/dichroism/src/schema.rs @@ -1,6 +1,21 @@ table! { - images (id) { + photo_sets (id) { + id -> Integer, + base -> Integer, + fullsize -> Integer, + thumbnail -> Integer, + original -> Integer, + } +} + +table! { + photos (id) { id -> Integer, path -> Text, } } + +allow_tables_to_appear_in_same_query!( + photo_sets, + photos, +); -- cgit v1.2.3