Add buy_merchandise endpoint

Temporary for now, eventually this should actually create a transaction record.

All it does now is update the merchandise quantity.
This commit is contained in:
Tyler Hallada 2020-10-26 00:51:01 -04:00
parent 58583a553a
commit e0bba0254c
4 changed files with 106 additions and 3 deletions

View File

@ -9,7 +9,8 @@ use warp::reply::{json, with_header, with_status};
use warp::{Rejection, Reply}; use warp::{Rejection, Reply};
use super::models::{ use super::models::{
InteriorRefList, ListParams, MerchandiseList, Model, Owner, Shop, UpdateableModel, InteriorRefList, ListParams, MerchandiseList, MerchandiseParams, Model, Owner, Shop,
UpdateableModel,
}; };
use super::problem::{reject_anyhow, unauthorized_no_api_key, unauthorized_no_owner}; use super::problem::{reject_anyhow, unauthorized_no_api_key, unauthorized_no_owner};
use super::Environment; use super::Environment;
@ -608,3 +609,42 @@ pub async fn get_merchandise_list_by_shop_id(
}) })
.await .await
} }
pub async fn buy_merchandise(
shop_id: i32,
merchandise_params: MerchandiseParams,
api_key: Option<Uuid>,
env: Environment,
) -> Result<impl Reply, Rejection> {
let owner_id = authenticate(&env, api_key).await.map_err(reject_anyhow)?;
// TODO: create transaction
let updated_merchandise_list = MerchandiseList::update_merchandise_quantity(
&env.db,
shop_id,
&(merchandise_params.mod_name),
merchandise_params.local_form_id,
merchandise_params.quantity_delta,
)
.await
.map_err(reject_anyhow)?;
let url = updated_merchandise_list
.url(&env.api_url)
.map_err(reject_anyhow)?;
let reply = json(&updated_merchandise_list);
let reply = with_header(reply, "Location", url.as_str());
let reply = with_status(reply, StatusCode::CREATED);
env.caches
.merchandise_list
.delete_response(
updated_merchandise_list
.id
.expect("saved merchandise_list has no id"),
)
.await;
env.caches
.merchandise_list_by_shop_id
.delete_response(updated_merchandise_list.shop_id)
.await;
env.caches.list_merchandise_lists.clear().await;
Ok(reply)
}

View File

@ -23,7 +23,7 @@ mod problem;
use caches::Caches; use caches::Caches;
use models::interior_ref_list::InteriorRefList; use models::interior_ref_list::InteriorRefList;
use models::merchandise_list::MerchandiseList; use models::merchandise_list::{MerchandiseList, MerchandiseParams};
use models::owner::Owner; use models::owner::Owner;
use models::shop::Shop; use models::shop::Shop;
use models::ListParams; use models::ListParams;
@ -292,6 +292,16 @@ async fn main() -> Result<()> {
.and(with_env(env.clone())) .and(with_env(env.clone()))
.and_then(handlers::get_merchandise_list_by_shop_id), .and_then(handlers::get_merchandise_list_by_shop_id),
); );
let buy_merchandise_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path("merchandise_list"))
.and(warp::path::end())
.and(warp::post())
.and(warp::query::<MerchandiseParams>())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::buy_merchandise),
);
let routes = warp::path("v1") let routes = warp::path("v1")
.and(balanced_or_tree!( .and(balanced_or_tree!(
@ -310,6 +320,7 @@ async fn main() -> Result<()> {
get_merchandise_list_by_shop_id_handler, get_merchandise_list_by_shop_id_handler,
update_interior_ref_list_by_shop_id_handler, update_interior_ref_list_by_shop_id_handler,
update_merchandise_list_by_shop_id_handler, update_merchandise_list_by_shop_id_handler,
buy_merchandise_handler,
get_interior_ref_list_handler, get_interior_ref_list_handler,
delete_interior_ref_list_handler, delete_interior_ref_list_handler,
update_interior_ref_list_handler, update_interior_ref_list_handler,

View File

@ -2,6 +2,7 @@ use anyhow::{Error, Result};
use async_trait::async_trait; use async_trait::async_trait;
use chrono::prelude::*; use chrono::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json;
use sqlx::postgres::PgPool; use sqlx::postgres::PgPool;
use sqlx::types::Json; use sqlx::types::Json;
use tracing::instrument; use tracing::instrument;
@ -36,6 +37,13 @@ pub struct MerchandiseList {
pub updated_at: Option<NaiveDateTime>, pub updated_at: Option<NaiveDateTime>,
} }
#[derive(Debug, Eq, PartialEq, Hash, Clone, Deserialize)]
pub struct MerchandiseParams {
pub mod_name: String,
pub local_form_id: i32,
pub quantity_delta: i32,
}
#[async_trait] #[async_trait]
impl Model for MerchandiseList { impl Model for MerchandiseList {
fn resource_name() -> &'static str { fn resource_name() -> &'static str {
@ -185,4 +193,48 @@ impl MerchandiseList {
return Err(forbidden_permission()); return Err(forbidden_permission());
} }
} }
#[instrument(level = "debug", skip(db))]
pub async fn update_merchandise_quantity(
db: &PgPool,
shop_id: i32,
mod_name: &str,
local_form_id: i32,
quantity_delta: i32,
) -> Result<Self> {
Ok(sqlx::query_as_unchecked!(
Self,
"UPDATE
merchandise_lists
SET
form_list =
jsonb_set(
form_list,
array[elem_index::text, 'quantity'],
to_jsonb(quantity::int + $4),
true
)
FROM (
SELECT
pos - 1 as elem_index,
elem->>'quantity' as quantity
FROM
merchandise_lists,
jsonb_array_elements(form_list) with ordinality arr(elem, pos)
WHERE
shop_id = $1 AND
elem->>'mod_name' = $2::text AND
elem->>'local_form_id' = $3::text
) sub
WHERE
shop_id = $1
RETURNING merchandise_lists.*",
shop_id,
mod_name,
local_form_id,
quantity_delta,
)
.fetch_one(db)
.await?)
}
} }

View File

@ -9,10 +9,10 @@ pub mod owner;
pub mod shop; pub mod shop;
pub use interior_ref_list::InteriorRefList; pub use interior_ref_list::InteriorRefList;
pub use merchandise_list::{MerchandiseList, MerchandiseParams};
pub use model::{Model, UpdateableModel}; pub use model::{Model, UpdateableModel};
pub use owner::Owner; pub use owner::Owner;
pub use shop::Shop; pub use shop::Shop;
pub use merchandise_list::MerchandiseList;
#[derive(Debug, Eq, PartialEq, Hash, Clone, Deserialize)] #[derive(Debug, Eq, PartialEq, Hash, Clone, Deserialize)]
pub enum Order { pub enum Order {