Add update shop & owner endpoints
This commit is contained in:
parent
441bc979f2
commit
97a2ac52dc
@ -17,6 +17,7 @@ pub fn shops(env: Environment) -> impl Filter<Extract = impl Reply, Error = Reje
|
|||||||
warp::path("shops").and(
|
warp::path("shops").and(
|
||||||
get_shop(env.clone())
|
get_shop(env.clone())
|
||||||
.or(delete_shop(env.clone()))
|
.or(delete_shop(env.clone()))
|
||||||
|
.or(update_shop(env.clone()))
|
||||||
.or(create_shop(env.clone()))
|
.or(create_shop(env.clone()))
|
||||||
.or(list_shops(env)),
|
.or(list_shops(env)),
|
||||||
)
|
)
|
||||||
@ -26,6 +27,7 @@ pub fn owners(env: Environment) -> impl Filter<Extract = impl Reply, Error = Rej
|
|||||||
warp::path("owners").and(
|
warp::path("owners").and(
|
||||||
get_owner(env.clone())
|
get_owner(env.clone())
|
||||||
.or(delete_owner(env.clone()))
|
.or(delete_owner(env.clone()))
|
||||||
|
.or(update_owner(env.clone()))
|
||||||
.or(create_owner(env.clone()))
|
.or(create_owner(env.clone()))
|
||||||
.or(list_owners(env)),
|
.or(list_owners(env)),
|
||||||
)
|
)
|
||||||
@ -81,6 +83,17 @@ pub fn delete_shop(
|
|||||||
.and_then(handlers::delete_shop)
|
.and_then(handlers::delete_shop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_shop(
|
||||||
|
env: Environment,
|
||||||
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
|
warp::path::param()
|
||||||
|
.and(warp::patch())
|
||||||
|
.and(json_body::<Shop>())
|
||||||
|
.and(warp::header::optional("api-key"))
|
||||||
|
.and(with_env(env))
|
||||||
|
.and_then(handlers::update_shop)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_shops(
|
pub fn list_shops(
|
||||||
env: Environment,
|
env: Environment,
|
||||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
@ -121,6 +134,17 @@ pub fn delete_owner(
|
|||||||
.and_then(handlers::delete_owner)
|
.and_then(handlers::delete_owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_owner(
|
||||||
|
env: Environment,
|
||||||
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
|
warp::path::param()
|
||||||
|
.and(warp::patch())
|
||||||
|
.and(json_body::<Owner>())
|
||||||
|
.and(warp::header::optional("api-key"))
|
||||||
|
.and(with_env(env))
|
||||||
|
.and_then(handlers::update_owner)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_owners(
|
pub fn list_owners(
|
||||||
env: Environment,
|
env: Environment,
|
||||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
|
@ -7,7 +7,7 @@ use uuid::Uuid;
|
|||||||
use warp::reply::{json, with_header, with_status};
|
use warp::reply::{json, with_header, with_status};
|
||||||
use warp::{Rejection, Reply};
|
use warp::{Rejection, Reply};
|
||||||
|
|
||||||
use super::models::{InteriorRefList, ListParams, Model, Owner, Shop, MerchandiseList};
|
use super::models::{InteriorRefList, ListParams, Model, UpdateableModel, Owner, Shop, MerchandiseList};
|
||||||
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;
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ pub async fn create_shop(
|
|||||||
..shop
|
..shop
|
||||||
};
|
};
|
||||||
let saved_shop = shop_with_owner_id
|
let saved_shop = shop_with_owner_id
|
||||||
.save(&env.db)
|
.create(&env.db)
|
||||||
.await
|
.await
|
||||||
.map_err(reject_anyhow)?;
|
.map_err(reject_anyhow)?;
|
||||||
let url = saved_shop.url(&env.api_url).map_err(reject_anyhow)?;
|
let url = saved_shop.url(&env.api_url).map_err(reject_anyhow)?;
|
||||||
@ -85,6 +85,43 @@ pub async fn create_shop(
|
|||||||
Ok(reply)
|
Ok(reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_shop(
|
||||||
|
id: i32,
|
||||||
|
shop: Shop,
|
||||||
|
api_key: Option<Uuid>,
|
||||||
|
env: Environment,
|
||||||
|
) -> Result<impl Reply, Rejection> {
|
||||||
|
let owner_id = authenticate(&env, api_key).await.map_err(reject_anyhow)?;
|
||||||
|
let shop_with_id_and_owner_id = if shop.owner_id.is_some() {
|
||||||
|
// allows an owner to transfer ownership of shop to another owner
|
||||||
|
Shop {
|
||||||
|
id: Some(id),
|
||||||
|
..shop
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Shop {
|
||||||
|
id: Some(id),
|
||||||
|
owner_id: Some(owner_id),
|
||||||
|
..shop
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let updated_shop = shop_with_id_and_owner_id
|
||||||
|
.update(&env.db, owner_id, id)
|
||||||
|
.await
|
||||||
|
.map_err(reject_anyhow)?;
|
||||||
|
let url = updated_shop.url(&env.api_url).map_err(reject_anyhow)?;
|
||||||
|
let reply = json(&updated_shop);
|
||||||
|
let reply = with_header(reply, "Location", url.as_str());
|
||||||
|
let reply = with_status(reply, StatusCode::CREATED);
|
||||||
|
env.caches
|
||||||
|
.shop
|
||||||
|
.delete_response(id)
|
||||||
|
.await
|
||||||
|
.map_err(reject_anyhow)?;
|
||||||
|
env.caches.list_shops.clear().await;
|
||||||
|
Ok(reply)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn delete_shop(
|
pub async fn delete_shop(
|
||||||
id: i32,
|
id: i32,
|
||||||
api_key: Option<Uuid>,
|
api_key: Option<Uuid>,
|
||||||
@ -151,7 +188,7 @@ pub async fn create_owner(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
let saved_owner = owner_with_ip_and_key
|
let saved_owner = owner_with_ip_and_key
|
||||||
.save(&env.db)
|
.create(&env.db)
|
||||||
.await
|
.await
|
||||||
.map_err(reject_anyhow)?;
|
.map_err(reject_anyhow)?;
|
||||||
let url = saved_owner.url(&env.api_url).map_err(reject_anyhow)?;
|
let url = saved_owner.url(&env.api_url).map_err(reject_anyhow)?;
|
||||||
@ -165,6 +202,34 @@ pub async fn create_owner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_owner(
|
||||||
|
id: i32,
|
||||||
|
owner: Owner,
|
||||||
|
api_key: Option<Uuid>,
|
||||||
|
env: Environment,
|
||||||
|
) -> Result<impl Reply, Rejection> {
|
||||||
|
let owner_id = authenticate(&env, api_key).await.map_err(reject_anyhow)?;
|
||||||
|
let owner_with_id = Owner {
|
||||||
|
id: Some(id),
|
||||||
|
..owner
|
||||||
|
};
|
||||||
|
let updated_owner = owner_with_id
|
||||||
|
.update(&env.db, owner_id, id)
|
||||||
|
.await
|
||||||
|
.map_err(reject_anyhow)?;
|
||||||
|
let url = updated_owner.url(&env.api_url).map_err(reject_anyhow)?;
|
||||||
|
let reply = json(&updated_owner);
|
||||||
|
let reply = with_header(reply, "Location", url.as_str());
|
||||||
|
let reply = with_status(reply, StatusCode::CREATED);
|
||||||
|
env.caches
|
||||||
|
.owner
|
||||||
|
.delete_response(id)
|
||||||
|
.await
|
||||||
|
.map_err(reject_anyhow)?;
|
||||||
|
env.caches.list_owners.clear().await;
|
||||||
|
Ok(reply)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn delete_owner(
|
pub async fn delete_owner(
|
||||||
id: i32,
|
id: i32,
|
||||||
api_key: Option<Uuid>,
|
api_key: Option<Uuid>,
|
||||||
@ -227,7 +292,7 @@ pub async fn create_interior_ref_list(
|
|||||||
..interior_ref_list
|
..interior_ref_list
|
||||||
};
|
};
|
||||||
let saved_interior_ref_list = ref_list_with_owner_id
|
let saved_interior_ref_list = ref_list_with_owner_id
|
||||||
.save(&env.db)
|
.create(&env.db)
|
||||||
.await
|
.await
|
||||||
.map_err(reject_anyhow)?;
|
.map_err(reject_anyhow)?;
|
||||||
let url = saved_interior_ref_list
|
let url = saved_interior_ref_list
|
||||||
@ -297,7 +362,7 @@ pub async fn create_merchandise_list(
|
|||||||
..merchandise_list
|
..merchandise_list
|
||||||
};
|
};
|
||||||
let saved_merchandise_list = ref_list_with_owner_id
|
let saved_merchandise_list = ref_list_with_owner_id
|
||||||
.save(&env.db)
|
.create(&env.db)
|
||||||
.await
|
.await
|
||||||
.map_err(reject_anyhow)?;
|
.map_err(reject_anyhow)?;
|
||||||
let url = saved_merchandise_list
|
let url = saved_merchandise_list
|
||||||
|
@ -59,7 +59,7 @@ impl Model for InteriorRefList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self, db))]
|
#[instrument(level = "debug", skip(self, db))]
|
||||||
async fn save(self, db: &PgPool) -> Result<Self> {
|
async fn create(self, db: &PgPool) -> Result<Self> {
|
||||||
// TODO:
|
// TODO:
|
||||||
// * Decide if I'll need to make the same changes to merchandise and transactions
|
// * Decide if I'll need to make the same changes to merchandise and transactions
|
||||||
// - answer depends on how many rows of each I expect to insert in one go
|
// - answer depends on how many rows of each I expect to insert in one go
|
||||||
|
@ -55,7 +55,7 @@ impl Model for MerchandiseList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self, db))]
|
#[instrument(level = "debug", skip(self, db))]
|
||||||
async fn save(self, db: &PgPool) -> Result<Self> {
|
async fn create(self, db: &PgPool) -> Result<Self> {
|
||||||
Ok(sqlx::query_as_unchecked!(
|
Ok(sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"INSERT INTO merchandise_lists
|
"INSERT INTO merchandise_lists
|
||||||
|
@ -9,7 +9,7 @@ pub mod owner;
|
|||||||
pub mod shop;
|
pub mod shop;
|
||||||
|
|
||||||
pub use interior_ref_list::InteriorRefList;
|
pub use interior_ref_list::InteriorRefList;
|
||||||
pub use model::Model;
|
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;
|
pub use merchandise_list::MerchandiseList;
|
||||||
|
@ -23,7 +23,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn get(db: &PgPool, id: i32) -> Result<Self>;
|
async fn get(db: &PgPool, id: i32) -> Result<Self>;
|
||||||
async fn save(self, db: &PgPool) -> Result<Self>;
|
async fn create(self, db: &PgPool) -> Result<Self>;
|
||||||
async fn delete(db: &PgPool, owner_id: i32, id: i32) -> Result<u64>;
|
async fn delete(db: &PgPool, owner_id: i32, id: i32) -> Result<u64>;
|
||||||
async fn list(db: &PgPool, list_params: &ListParams) -> Result<Vec<Self>>;
|
async fn list(db: &PgPool, list_params: &ListParams) -> Result<Vec<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait UpdateableModel
|
||||||
|
where
|
||||||
|
Self: std::marker::Sized,
|
||||||
|
{
|
||||||
|
async fn update(self, db: &PgPool, owner_id: i32, id: i32) -> Result<Self>;
|
||||||
|
}
|
@ -8,7 +8,7 @@ use tracing::instrument;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::ListParams;
|
use super::ListParams;
|
||||||
use super::Model;
|
use super::{Model, UpdateableModel};
|
||||||
use crate::problem::forbidden_permission;
|
use crate::problem::forbidden_permission;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
@ -43,7 +43,7 @@ impl Model for Owner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self, db))]
|
#[instrument(level = "debug", skip(self, db))]
|
||||||
async fn save(self, db: &PgPool) -> Result<Self> {
|
async fn create(self, db: &PgPool) -> Result<Self> {
|
||||||
Ok(sqlx::query_as!(
|
Ok(sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"INSERT INTO owners
|
"INSERT INTO owners
|
||||||
@ -103,3 +103,31 @@ impl Model for Owner {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl UpdateableModel for Owner {
|
||||||
|
#[instrument(level = "debug", skip(self, db))]
|
||||||
|
async fn update(self, db: &PgPool, owner_id: i32, id: i32) -> Result<Self> {
|
||||||
|
let owner = sqlx::query!("SELECT id FROM owners WHERE id = $1", id)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await?;
|
||||||
|
if owner.id == owner_id {
|
||||||
|
Ok(sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"UPDATE owners SET
|
||||||
|
name = $2,
|
||||||
|
mod_version = $3,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING *",
|
||||||
|
id,
|
||||||
|
self.name,
|
||||||
|
self.mod_version,
|
||||||
|
)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await?)
|
||||||
|
} else {
|
||||||
|
return Err(forbidden_permission());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ use sqlx::postgres::PgPool;
|
|||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use super::ListParams;
|
use super::ListParams;
|
||||||
use super::Model;
|
use super::{Model, UpdateableModel};
|
||||||
use crate::problem::forbidden_permission;
|
use crate::problem::forbidden_permission;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
@ -43,7 +43,7 @@ impl Model for Shop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self, db))]
|
#[instrument(level = "debug", skip(self, db))]
|
||||||
async fn save(self, db: &PgPool) -> Result<Self> {
|
async fn create(self, db: &PgPool) -> Result<Self> {
|
||||||
Ok(sqlx::query_as!(
|
Ok(sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"INSERT INTO shops
|
"INSERT INTO shops
|
||||||
@ -102,3 +102,34 @@ impl Model for Shop {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl UpdateableModel for Shop {
|
||||||
|
#[instrument(level = "debug", skip(self, db))]
|
||||||
|
async fn update(self, db: &PgPool, owner_id: i32, id: i32) -> Result<Self> {
|
||||||
|
let shop = sqlx::query!("SELECT owner_id FROM shops WHERE id = $1", id)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await?;
|
||||||
|
if shop.owner_id == owner_id {
|
||||||
|
Ok(sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"UPDATE shops SET
|
||||||
|
name = $2,
|
||||||
|
owner_id = $3,
|
||||||
|
description = $4,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING *",
|
||||||
|
id,
|
||||||
|
self.name,
|
||||||
|
self.owner_id,
|
||||||
|
self.description,
|
||||||
|
)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await?)
|
||||||
|
} else {
|
||||||
|
return Err(forbidden_permission());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user