summaryrefslogtreecommitdiff
path: root/dichroism/src/handlers.rs
blob: b9420af4e5b91437c991904f18669e4c005d7a2d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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<DbPool>) -> Result<HttpResponse, Error> {
    let conn = pool.get().map_err(to_internal_error)?;
    let products: Vec<ProductGet> = 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<DbPool>,
    web::Json(patch): web::Json<ProductPatch>,
) -> Result<HttpResponse, Error> {
    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<DbPool>,
    web::Json(post): web::Json<ProductPost>,
) -> Result<HttpResponse, Error> {
    // 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::<ProductGet>(product.into()))
}

#[post("/photos")]
async fn post_photo(
    pool: web::Data<DbPool>,
    mut payload: Multipart,
) -> Result<HttpResponse, Error> {
    let mut responses: Vec<PhotoSetGet> = 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<u8> = 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))
}