one interior_ref_list and merchandise_list per shop
still some bugs with routing I need to figure out
This commit is contained in:
parent
3f124ce439
commit
df48b64ffd
@ -20,7 +20,7 @@ pub struct Caches {
|
||||
pub list_owners: Cache<ListParams, CachedResponse>,
|
||||
pub list_interior_ref_lists: Cache<ListParams, CachedResponse>,
|
||||
pub list_merchandise_lists: Cache<ListParams, CachedResponse>,
|
||||
pub latest_interior_ref_list_by_shop_id: Cache<i32, CachedResponse>,
|
||||
pub interior_ref_list_by_shop_id: Cache<i32, CachedResponse>,
|
||||
}
|
||||
|
||||
impl Caches {
|
||||
@ -35,10 +35,7 @@ impl Caches {
|
||||
list_owners: Cache::new("list_owners", 100),
|
||||
list_interior_ref_lists: Cache::new("list_interior_ref_lists", 100),
|
||||
list_merchandise_lists: Cache::new("list_merchandise_lists", 100),
|
||||
latest_interior_ref_list_by_shop_id: Cache::new(
|
||||
"latest_interior_ref_list_by_shop_id",
|
||||
100,
|
||||
),
|
||||
interior_ref_list_by_shop_id: Cache::new("interior_ref_list_by_shop_id", 100),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,10 @@ pub fn migration() -> String {
|
||||
|
||||
m.create_table("merchandise_lists", |t| {
|
||||
t.add_column("id", types::primary().indexed(true));
|
||||
t.add_column("shop_id", types::foreign("shops", "id").indexed(true));
|
||||
t.add_column(
|
||||
"shop_id",
|
||||
types::foreign("shops", "id").indexed(true).unique(true),
|
||||
);
|
||||
t.add_column("owner_id", types::foreign("owners", "id").indexed(true));
|
||||
t.add_column("form_list", types::custom("jsonb"));
|
||||
t.add_column("created_at", types::custom("timestamp(3)"));
|
||||
@ -59,7 +62,10 @@ pub fn migration() -> String {
|
||||
|
||||
m.create_table("interior_ref_lists", |t| {
|
||||
t.add_column("id", types::primary().indexed(true));
|
||||
t.add_column("shop_id", types::foreign("shops", "id").indexed(true));
|
||||
t.add_column(
|
||||
"shop_id",
|
||||
types::foreign("shops", "id").indexed(true).unique(true),
|
||||
);
|
||||
t.add_column("owner_id", types::foreign("owners", "id").indexed(true));
|
||||
t.add_column("ref_list", types::custom("jsonb"));
|
||||
t.add_column("created_at", types::custom("timestamp(3)"));
|
||||
|
@ -1,6 +1,7 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use http::StatusCode;
|
||||
use ipnetwork::IpNetwork;
|
||||
use sqlx::types::Json;
|
||||
use std::net::SocketAddr;
|
||||
use tracing::instrument;
|
||||
use uuid::Uuid;
|
||||
@ -79,6 +80,35 @@ pub async fn create_shop(
|
||||
.create(&env.db)
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
|
||||
// also save empty interior_ref_list and merchandise_list rows
|
||||
if let Some(shop_id) = saved_shop.id {
|
||||
let interior_ref_list = InteriorRefList {
|
||||
id: None,
|
||||
shop_id,
|
||||
owner_id: Some(owner_id),
|
||||
ref_list: Json::default(),
|
||||
created_at: None,
|
||||
updated_at: None,
|
||||
};
|
||||
interior_ref_list
|
||||
.create(&env.db)
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
let merchandise_list = MerchandiseList {
|
||||
id: None,
|
||||
shop_id,
|
||||
owner_id: Some(owner_id),
|
||||
form_list: Json::default(),
|
||||
created_at: None,
|
||||
updated_at: None,
|
||||
};
|
||||
merchandise_list
|
||||
.create(&env.db)
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
}
|
||||
|
||||
let url = saved_shop.url(&env.api_url).map_err(reject_anyhow)?;
|
||||
let reply = json(&saved_shop);
|
||||
let reply = with_header(reply, "Location", url.as_str());
|
||||
@ -139,10 +169,7 @@ pub async fn delete_shop(
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
env.caches.list_shops.clear().await;
|
||||
env.caches
|
||||
.latest_interior_ref_list_by_shop_id
|
||||
.delete(id)
|
||||
.await;
|
||||
env.caches.interior_ref_list_by_shop_id.delete(id).await;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
@ -307,12 +334,55 @@ pub async fn create_interior_ref_list(
|
||||
let reply = with_status(reply, StatusCode::CREATED);
|
||||
env.caches.list_interior_ref_lists.clear().await;
|
||||
env.caches
|
||||
.latest_interior_ref_list_by_shop_id
|
||||
.interior_ref_list_by_shop_id
|
||||
.delete(saved_interior_ref_list.shop_id)
|
||||
.await;
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
pub async fn update_interior_ref_list(
|
||||
id: i32,
|
||||
interior_ref_list: InteriorRefList,
|
||||
api_key: Option<Uuid>,
|
||||
env: Environment,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
let owner_id = authenticate(&env, api_key).await.map_err(reject_anyhow)?;
|
||||
let interior_ref_list_with_id_and_owner_id = if interior_ref_list.owner_id.is_some() {
|
||||
InteriorRefList {
|
||||
id: Some(id),
|
||||
..interior_ref_list
|
||||
}
|
||||
} else {
|
||||
InteriorRefList {
|
||||
id: Some(id),
|
||||
owner_id: Some(owner_id),
|
||||
..interior_ref_list
|
||||
}
|
||||
};
|
||||
let updated_interior_ref_list = interior_ref_list_with_id_and_owner_id
|
||||
.update(&env.db, owner_id, id)
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
let url = updated_interior_ref_list
|
||||
.url(&env.api_url)
|
||||
.map_err(reject_anyhow)?;
|
||||
let reply = json(&updated_interior_ref_list);
|
||||
let reply = with_header(reply, "Location", url.as_str());
|
||||
let reply = with_status(reply, StatusCode::CREATED);
|
||||
env.caches
|
||||
.interior_ref_list
|
||||
.delete_response(id)
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
env.caches
|
||||
.interior_ref_list_by_shop_id
|
||||
.delete_response(updated_interior_ref_list.shop_id)
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
env.caches.list_interior_ref_lists.clear().await;
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
pub async fn delete_interior_ref_list(
|
||||
id: i32,
|
||||
api_key: Option<Uuid>,
|
||||
@ -332,21 +402,20 @@ pub async fn delete_interior_ref_list(
|
||||
.map_err(reject_anyhow)?;
|
||||
env.caches.list_interior_ref_lists.clear().await;
|
||||
env.caches
|
||||
.latest_interior_ref_list_by_shop_id
|
||||
.interior_ref_list_by_shop_id
|
||||
.delete(interior_ref_list.shop_id)
|
||||
.await;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
pub async fn get_latest_interior_ref_list_by_shop_id(
|
||||
pub async fn get_interior_ref_list_by_shop_id(
|
||||
shop_id: i32,
|
||||
env: Environment,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
env.caches
|
||||
.latest_interior_ref_list_by_shop_id
|
||||
.interior_ref_list_by_shop_id
|
||||
.get_response(shop_id, || async {
|
||||
let interior_ref_list =
|
||||
InteriorRefList::get_latest_by_shop_id(&env.db, shop_id).await?;
|
||||
let interior_ref_list = InteriorRefList::get_by_shop_id(&env.db, shop_id).await?;
|
||||
let reply = json(&interior_ref_list);
|
||||
let reply = with_status(reply, StatusCode::OK);
|
||||
Ok(reply)
|
||||
@ -406,6 +475,44 @@ pub async fn create_merchandise_list(
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
pub async fn update_merchandise_list(
|
||||
id: i32,
|
||||
merchandise_list: MerchandiseList,
|
||||
api_key: Option<Uuid>,
|
||||
env: Environment,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
let owner_id = authenticate(&env, api_key).await.map_err(reject_anyhow)?;
|
||||
let merchandise_list_with_id_and_owner_id = if merchandise_list.owner_id.is_some() {
|
||||
MerchandiseList {
|
||||
id: Some(id),
|
||||
..merchandise_list
|
||||
}
|
||||
} else {
|
||||
MerchandiseList {
|
||||
id: Some(id),
|
||||
owner_id: Some(owner_id),
|
||||
..merchandise_list
|
||||
}
|
||||
};
|
||||
let updated_merchandise_list = merchandise_list_with_id_and_owner_id
|
||||
.update(&env.db, owner_id, id)
|
||||
.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(id)
|
||||
.await
|
||||
.map_err(reject_anyhow)?;
|
||||
env.caches.list_merchandise_lists.clear().await;
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
pub async fn delete_merchandise_list(
|
||||
id: i32,
|
||||
api_key: Option<Uuid>,
|
||||
|
30
src/main.rs
30
src/main.rs
@ -71,6 +71,7 @@ where
|
||||
{
|
||||
warp::body::content_length_limit(1024 * 64).and(warp::body::json())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
dotenv().ok();
|
||||
@ -199,6 +200,15 @@ async fn main() -> Result<()> {
|
||||
.and(with_env(env.clone()))
|
||||
.and_then(handlers::delete_interior_ref_list),
|
||||
);
|
||||
let update_interior_ref_list_handler = warp::path("interior_ref_lists").and(
|
||||
warp::path::param()
|
||||
.and(warp::path::end())
|
||||
.and(warp::patch())
|
||||
.and(json_body::<InteriorRefList>())
|
||||
.and(warp::header::optional("api-key"))
|
||||
.and(with_env(env.clone()))
|
||||
.and_then(handlers::update_interior_ref_list),
|
||||
);
|
||||
let list_interior_ref_lists_handler = warp::path("interior_ref_lists").and(
|
||||
warp::path::end()
|
||||
.and(warp::get())
|
||||
@ -206,13 +216,13 @@ async fn main() -> Result<()> {
|
||||
.and(with_env(env.clone()))
|
||||
.and_then(handlers::list_interior_ref_lists),
|
||||
);
|
||||
let get_latest_interior_ref_list_by_shop_id_handler = warp::path("shops").and(
|
||||
let get_interior_ref_list_by_shop_id_handler = warp::path("shops").and(
|
||||
warp::path::param()
|
||||
.and(warp::path("latest_interior_ref_list"))
|
||||
.and(warp::path("interior_ref_list"))
|
||||
.and(warp::path::end())
|
||||
.and(warp::get())
|
||||
.and(with_env(env.clone()))
|
||||
.and_then(handlers::get_latest_interior_ref_list_by_shop_id),
|
||||
.and_then(handlers::get_interior_ref_list_by_shop_id),
|
||||
);
|
||||
let get_merchandise_list_handler = warp::path("merchandise_lists").and(
|
||||
warp::path::param()
|
||||
@ -237,6 +247,15 @@ async fn main() -> Result<()> {
|
||||
.and(with_env(env.clone()))
|
||||
.and_then(handlers::delete_merchandise_list),
|
||||
);
|
||||
let update_merchandise_list_handler = warp::path("merchandise_lists").and(
|
||||
warp::path::param()
|
||||
.and(warp::path::end())
|
||||
.and(warp::patch())
|
||||
.and(json_body::<MerchandiseList>())
|
||||
.and(warp::header::optional("api-key"))
|
||||
.and(with_env(env.clone()))
|
||||
.and_then(handlers::update_merchandise_list),
|
||||
);
|
||||
let list_merchandise_lists_handler = warp::path("merchandise_lists").and(
|
||||
warp::path::end()
|
||||
.and(warp::get())
|
||||
@ -258,15 +277,18 @@ async fn main() -> Result<()> {
|
||||
update_shop_handler,
|
||||
create_shop_handler,
|
||||
list_shops_handler,
|
||||
get_latest_interior_ref_list_by_shop_id_handler,
|
||||
get_interior_ref_list_by_shop_id_handler,
|
||||
get_interior_ref_list_handler,
|
||||
delete_interior_ref_list_handler,
|
||||
update_interior_ref_list_handler,
|
||||
create_interior_ref_list_handler,
|
||||
list_interior_ref_lists_handler,
|
||||
get_merchandise_list_handler,
|
||||
delete_merchandise_list_handler,
|
||||
update_merchandise_list_handler,
|
||||
create_merchandise_list_handler,
|
||||
list_merchandise_lists_handler,
|
||||
// warp::any().map(|| StatusCode::NOT_FOUND),
|
||||
))
|
||||
.recover(problem::unpack_problem)
|
||||
.with(warp::compression::gzip())
|
||||
|
@ -7,7 +7,7 @@ use sqlx::types::Json;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::ListParams;
|
||||
use super::Model;
|
||||
use super::{Model, UpdateableModel};
|
||||
use crate::problem::forbidden_permission;
|
||||
|
||||
// sqlx queries for this model need to be `query_as_unchecked!` because `query_as!` does not
|
||||
@ -126,15 +126,41 @@ impl Model for InteriorRefList {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UpdateableModel for InteriorRefList {
|
||||
#[instrument(level = "debug", skip(self, db))]
|
||||
async fn update(self, db: &PgPool, owner_id: i32, id: i32) -> Result<Self> {
|
||||
let interior_ref_list =
|
||||
sqlx::query!("SELECT owner_id FROM interior_ref_lists WHERE id = $1", id)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
if interior_ref_list.owner_id == owner_id {
|
||||
Ok(sqlx::query_as_unchecked!(
|
||||
Self,
|
||||
"UPDATE interior_ref_lists SET
|
||||
ref_list = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
RETURNING *",
|
||||
id,
|
||||
self.ref_list,
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?)
|
||||
} else {
|
||||
return Err(forbidden_permission());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InteriorRefList {
|
||||
#[instrument(level = "debug", skip(db))]
|
||||
pub async fn get_latest_by_shop_id(db: &PgPool, shop_id: i32) -> Result<Self> {
|
||||
pub async fn get_by_shop_id(db: &PgPool, shop_id: i32) -> Result<Self> {
|
||||
sqlx::query_as_unchecked!(
|
||||
Self,
|
||||
"SELECT interior_ref_lists.* FROM interior_ref_lists
|
||||
INNER JOIN shops ON (interior_ref_lists.shop_id = shops.id)
|
||||
WHERE shops.id = $1
|
||||
ORDER BY interior_ref_lists.created_at DESC
|
||||
LIMIT 1",
|
||||
shop_id,
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ use sqlx::types::Json;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::ListParams;
|
||||
use super::Model;
|
||||
use super::{Model, UpdateableModel};
|
||||
use crate::problem::forbidden_permission;
|
||||
|
||||
// sqlx queries for this model need to be `query_as_unchecked!` because `query_as!` does not
|
||||
@ -117,3 +117,30 @@ impl Model for MerchandiseList {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UpdateableModel for MerchandiseList {
|
||||
#[instrument(level = "debug", skip(self, db))]
|
||||
async fn update(self, db: &PgPool, owner_id: i32, id: i32) -> Result<Self> {
|
||||
let merchandise_list =
|
||||
sqlx::query!("SELECT owner_id FROM merchandise_lists WHERE id = $1", id)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
if merchandise_list.owner_id == owner_id {
|
||||
Ok(sqlx::query_as_unchecked!(
|
||||
Self,
|
||||
"UPDATE merchandise_lists SET
|
||||
form_list = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
RETURNING *",
|
||||
id,
|
||||
self.form_list,
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?)
|
||||
} else {
|
||||
return Err(forbidden_permission());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user