Add update by shop id handlers

This commit is contained in:
Tyler Hallada 2020-10-25 03:16:46 -04:00
parent df48b64ffd
commit 58583a553a
6 changed files with 224 additions and 45 deletions

View File

@ -123,11 +123,11 @@ where
Ok(cached_response)
}
pub async fn delete_response(&self, key: K) -> Result<Option<CachedResponse>> {
pub async fn delete_response(&self, key: K) -> Option<CachedResponse> {
let mut guard = self.lru_mutex.lock().await;
let cached_response = guard.pop(&key);
self.log_with_key(&key, "delete_response");
Ok(cached_response)
cached_response
}
}

View File

@ -21,6 +21,7 @@ pub struct Caches {
pub list_interior_ref_lists: Cache<ListParams, CachedResponse>,
pub list_merchandise_lists: Cache<ListParams, CachedResponse>,
pub interior_ref_list_by_shop_id: Cache<i32, CachedResponse>,
pub merchandise_list_by_shop_id: Cache<i32, CachedResponse>,
}
impl Caches {
@ -36,6 +37,7 @@ impl Caches {
list_interior_ref_lists: Cache::new("list_interior_ref_lists", 100),
list_merchandise_lists: Cache::new("list_merchandise_lists", 100),
interior_ref_list_by_shop_id: Cache::new("interior_ref_list_by_shop_id", 100),
merchandise_list_by_shop_id: Cache::new("merchandise_list_by_shop_id", 100),
}
}
}

View File

@ -145,11 +145,7 @@ pub async fn update_shop(
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.shop.delete_response(id).await;
env.caches.list_shops.clear().await;
Ok(reply)
}
@ -163,13 +159,16 @@ pub async fn delete_shop(
Shop::delete(&env.db, owner_id, id)
.await
.map_err(reject_anyhow)?;
env.caches
.shop
.delete_response(id)
.await
.map_err(reject_anyhow)?;
env.caches.shop.delete_response(id).await;
env.caches.list_shops.clear().await;
env.caches.interior_ref_list_by_shop_id.delete(id).await;
env.caches
.interior_ref_list_by_shop_id
.delete_response(id)
.await;
env.caches
.merchandise_list_by_shop_id
.delete_response(id)
.await;
Ok(StatusCode::NO_CONTENT)
}
@ -254,11 +253,7 @@ pub async fn update_owner(
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.owner.delete_response(id).await;
env.caches.list_owners.clear().await;
Ok(reply)
}
@ -272,11 +267,7 @@ pub async fn delete_owner(
Owner::delete(&env.db, owner_id, id)
.await
.map_err(reject_anyhow)?;
env.caches
.owner
.delete_response(id)
.await
.map_err(reject_anyhow)?;
env.caches.owner.delete_response(id).await;
env.caches
.owner_ids_by_api_key
.delete(api_key.expect("api-key has been validated during authenticate"))
@ -335,7 +326,7 @@ pub async fn create_interior_ref_list(
env.caches.list_interior_ref_lists.clear().await;
env.caches
.interior_ref_list_by_shop_id
.delete(saved_interior_ref_list.shop_id)
.delete_response(saved_interior_ref_list.shop_id)
.await;
Ok(reply)
}
@ -369,16 +360,48 @@ pub async fn update_interior_ref_list(
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.delete_response(id).await;
env.caches
.interior_ref_list_by_shop_id
.delete_response(updated_interior_ref_list.shop_id)
.await;
env.caches.list_interior_ref_lists.clear().await;
Ok(reply)
}
pub async fn update_interior_ref_list_by_shop_id(
shop_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_owner_id = InteriorRefList {
owner_id: Some(owner_id),
..interior_ref_list
};
let updated_interior_ref_list = interior_ref_list_with_owner_id
.update_by_shop_id(&env.db, owner_id, shop_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(
updated_interior_ref_list
.id
.expect("saved interior_ref_list has no id"),
)
.await;
env.caches
.interior_ref_list_by_shop_id
.delete_response(updated_interior_ref_list.shop_id)
.await;
env.caches.list_interior_ref_lists.clear().await;
Ok(reply)
}
@ -395,15 +418,11 @@ pub async fn delete_interior_ref_list(
InteriorRefList::delete(&env.db, owner_id, id)
.await
.map_err(reject_anyhow)?;
env.caches
.interior_ref_list
.delete_response(id)
.await
.map_err(reject_anyhow)?;
env.caches.interior_ref_list.delete_response(id).await;
env.caches.list_interior_ref_lists.clear().await;
env.caches
.interior_ref_list_by_shop_id
.delete(interior_ref_list.shop_id)
.delete_response(interior_ref_list.shop_id)
.await;
Ok(StatusCode::NO_CONTENT)
}
@ -472,6 +491,10 @@ pub async fn create_merchandise_list(
let reply = with_header(reply, "Location", url.as_str());
let reply = with_status(reply, StatusCode::CREATED);
env.caches.list_merchandise_lists.clear().await;
env.caches
.merchandise_list_by_shop_id
.delete_response(saved_merchandise_list.shop_id)
.await;
Ok(reply)
}
@ -504,11 +527,48 @@ pub async fn update_merchandise_list(
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;
env.caches
.merchandise_list
.delete_response(id)
.merchandise_list_by_shop_id
.delete_response(updated_merchandise_list.shop_id)
.await;
env.caches.list_merchandise_lists.clear().await;
Ok(reply)
}
pub async fn update_merchandise_list_by_shop_id(
shop_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_owner_id = MerchandiseList {
owner_id: Some(owner_id),
..merchandise_list
};
let updated_merchandise_list = merchandise_list_with_owner_id
.update_by_shop_id(&env.db, owner_id, shop_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(
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)
}
@ -519,14 +579,32 @@ pub async fn delete_merchandise_list(
env: Environment,
) -> Result<impl Reply, Rejection> {
let owner_id = authenticate(&env, api_key).await.map_err(reject_anyhow)?;
let merchandise_list = MerchandiseList::get(&env.db, id)
.await
.map_err(reject_anyhow)?;
MerchandiseList::delete(&env.db, owner_id, id)
.await
.map_err(reject_anyhow)?;
env.caches.merchandise_list.delete_response(id).await;
env.caches
.merchandise_list
.delete_response(id)
.await
.map_err(reject_anyhow)?;
.merchandise_list_by_shop_id
.delete_response(merchandise_list.shop_id)
.await;
env.caches.list_merchandise_lists.clear().await;
Ok(StatusCode::NO_CONTENT)
}
pub async fn get_merchandise_list_by_shop_id(
shop_id: i32,
env: Environment,
) -> Result<impl Reply, Rejection> {
env.caches
.merchandise_list_by_shop_id
.get_response(shop_id, || async {
let merchandise_list = MerchandiseList::get_by_shop_id(&env.db, shop_id).await?;
let reply = json(&merchandise_list);
let reply = with_status(reply, StatusCode::OK);
Ok(reply)
})
.await
}

View File

@ -164,6 +164,7 @@ async fn main() -> Result<()> {
);
let update_shop_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path::end())
.and(warp::patch())
.and(json_body::<Shop>())
.and(warp::header::optional("api-key"))
@ -209,6 +210,16 @@ async fn main() -> Result<()> {
.and(with_env(env.clone()))
.and_then(handlers::update_interior_ref_list),
);
let update_interior_ref_list_by_shop_id_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path("interior_ref_list"))
.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_by_shop_id),
);
let list_interior_ref_lists_handler = warp::path("interior_ref_lists").and(
warp::path::end()
.and(warp::get())
@ -256,6 +267,16 @@ async fn main() -> Result<()> {
.and(with_env(env.clone()))
.and_then(handlers::update_merchandise_list),
);
let update_merchandise_list_by_shop_id_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path("merchandise_list"))
.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_by_shop_id),
);
let list_merchandise_lists_handler = warp::path("merchandise_lists").and(
warp::path::end()
.and(warp::get())
@ -263,6 +284,14 @@ async fn main() -> Result<()> {
.and(with_env(env.clone()))
.and_then(handlers::list_merchandise_lists),
);
let get_merchandise_list_by_shop_id_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path("merchandise_list"))
.and(warp::path::end())
.and(warp::get())
.and(with_env(env.clone()))
.and_then(handlers::get_merchandise_list_by_shop_id),
);
let routes = warp::path("v1")
.and(balanced_or_tree!(
@ -278,6 +307,9 @@ async fn main() -> Result<()> {
create_shop_handler,
list_shops_handler,
get_interior_ref_list_by_shop_id_handler,
get_merchandise_list_by_shop_id_handler,
update_interior_ref_list_by_shop_id_handler,
update_merchandise_list_by_shop_id_handler,
get_interior_ref_list_handler,
delete_interior_ref_list_handler,
update_interior_ref_list_handler,

View File

@ -50,6 +50,7 @@ impl Model for InteriorRefList {
self.id
}
// TODO: this model will probably never need to be accessed through it's ID, should these methods be removed/unimplemented?
#[instrument(level = "debug", skip(db))]
async fn get(db: &PgPool, id: i32) -> Result<Self> {
sqlx::query_as_unchecked!(Self, "SELECT * FROM interior_ref_lists WHERE id = $1", id)
@ -158,14 +159,38 @@ impl InteriorRefList {
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
LIMIT 1",
"SELECT * FROM interior_ref_lists
WHERE shop_id = $1",
shop_id,
)
.fetch_one(db)
.await
.map_err(Error::new)
}
#[instrument(level = "debug", skip(self, db))]
pub async fn update_by_shop_id(self, db: &PgPool, owner_id: i32, shop_id: i32) -> Result<Self> {
let interior_ref_list = sqlx::query!(
"SELECT owner_id FROM interior_ref_lists WHERE shop_id = $1",
shop_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 shop_id = $1
RETURNING *",
shop_id,
self.ref_list,
)
.fetch_one(db)
.await?)
} else {
return Err(forbidden_permission());
}
}
}

View File

@ -46,6 +46,7 @@ impl Model for MerchandiseList {
self.id
}
// TODO: this model will probably never need to be accessed through it's ID, should these methods be removed/unimplemented?
#[instrument(level = "debug", skip(db))]
async fn get(db: &PgPool, id: i32) -> Result<Self> {
sqlx::query_as_unchecked!(Self, "SELECT * FROM merchandise_lists WHERE id = $1", id)
@ -144,3 +145,44 @@ impl UpdateableModel for MerchandiseList {
}
}
}
impl MerchandiseList {
#[instrument(level = "debug", skip(db))]
pub async fn get_by_shop_id(db: &PgPool, shop_id: i32) -> Result<Self> {
sqlx::query_as_unchecked!(
Self,
"SELECT * FROM merchandise_lists
WHERE shop_id = $1",
shop_id,
)
.fetch_one(db)
.await
.map_err(Error::new)
}
#[instrument(level = "debug", skip(db))]
pub async fn update_by_shop_id(self, db: &PgPool, owner_id: i32, shop_id: i32) -> Result<Self> {
let merchandise_list = sqlx::query!(
"SELECT owner_id FROM merchandise_lists WHERE shop_id = $1",
shop_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 shop_id = $1
RETURNING *",
shop_id,
self.form_list,
)
.fetch_one(db)
.await?)
} else {
return Err(forbidden_permission());
}
}
}