server: Add routes to manage ingredients

This commit is contained in:
traxys 2023-05-29 22:01:49 +02:00
parent 14fbde812f
commit c23da789a1
3 changed files with 146 additions and 1 deletions

View file

@ -48,3 +48,33 @@ pub struct UserInfo {
pub name: String,
pub id: Uuid,
}
#[derive(Serialize, Deserialize)]
pub struct CreateIngredientRequest {
pub name: String,
pub unit: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct CreateIngredientResponse {
pub id: i64,
}
#[derive(Serialize, Deserialize)]
pub struct IngredientInfo {
pub name: String,
pub unit: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct IngredientList {
pub ingredients: HashMap<i64, IngredientInfo>,
}
#[derive(Serialize, Deserialize)]
pub struct EditIngredientRequest {
pub name: Option<String>,
pub unit: Option<String>,
#[serde(default)]
pub has_unit: bool,
}

99
src/routes/ingredients.rs Normal file
View file

@ -0,0 +1,99 @@
use axum::{
extract::{Path, State},
Json,
};
use api::{
CreateIngredientRequest, CreateIngredientResponse, EditIngredientRequest, EmptyResponse,
IngredientInfo, IngredientList,
};
use sea_orm::{prelude::*, ActiveValue};
use serde::Deserialize;
use super::{household::AuthorizedHousehold, AppState, JsonResult};
use crate::entity::{ingredient, prelude::*};
pub(super) async fn create_ingredient(
AuthorizedHousehold(household): AuthorizedHousehold,
State(state): State<AppState>,
Json(request): Json<CreateIngredientRequest>,
) -> JsonResult<CreateIngredientResponse> {
let model = ingredient::ActiveModel {
household: ActiveValue::Set(household.id),
name: ActiveValue::Set(request.name),
unit: ActiveValue::Set(request.unit),
..Default::default()
};
let ingredient = model.insert(&state.db).await?;
Ok(Json(CreateIngredientResponse { id: ingredient.id }))
}
pub(super) async fn list_ingredients(
AuthorizedHousehold(household): AuthorizedHousehold,
State(state): State<AppState>,
) -> JsonResult<IngredientList> {
let ingredients = household.find_related(Ingredient).all(&state.db).await?;
Ok(Json(IngredientList {
ingredients: ingredients
.into_iter()
.map(|m| {
(
m.id,
IngredientInfo {
name: m.name,
unit: m.unit,
},
)
})
.collect(),
}))
}
#[derive(Deserialize)]
pub struct IngredientId {
iid: i64,
}
pub(super) async fn remove_ingredient(
AuthorizedHousehold(household): AuthorizedHousehold,
State(state): State<AppState>,
Path(IngredientId { iid }): Path<IngredientId>,
) -> JsonResult<EmptyResponse> {
Ingredient::delete_by_id(iid)
.filter(ingredient::Column::Household.eq(household.id))
.exec(&state.db)
.await?;
Ok(Json(EmptyResponse {}))
}
pub(super) async fn edit_ingredient(
AuthorizedHousehold(household): AuthorizedHousehold,
State(state): State<AppState>,
Path(IngredientId { iid }): Path<IngredientId>,
Json(request): Json<EditIngredientRequest>,
) -> JsonResult<EmptyResponse> {
let Some(ingredient) = Ingredient::find_by_id(iid)
.filter(ingredient::Column::Household.eq(household.id))
.one(&state.db)
.await? else {
return Err(super::RouteError::RessourceNotFound);
};
let mut ingredient: ingredient::ActiveModel = ingredient.into();
if let Some(name) = request.name {
ingredient.name = ActiveValue::Set(name);
}
if request.has_unit || request.unit.is_some() {
ingredient.unit = ActiveValue::Set(request.unit);
}
ingredient.update(&state.db).await?;
Ok(Json(EmptyResponse {}))
}

View file

@ -11,7 +11,7 @@ use axum::{
HeaderValue, Method, StatusCode,
},
response::IntoResponse,
routing::{get, post, put},
routing::{delete, get, post, put},
Json, Router, TypedHeader,
};
use jwt_simple::prelude::*;
@ -22,6 +22,7 @@ use tower_http::cors::{self, AllowOrigin, CorsLayer};
use crate::entity::{prelude::*, user};
mod household;
mod ingredients;
#[derive(thiserror::Error, Debug)]
enum RouteError {
@ -39,6 +40,8 @@ enum RouteError {
Unauthorized,
#[error("Could not fetch required value from path")]
PathRejection(#[from] axum::extract::rejection::PathRejection),
#[error("The supplied ressource does not exist")]
RessourceNotFound,
}
impl IntoResponse for RouteError {
@ -60,6 +63,7 @@ impl IntoResponse for RouteError {
"Unauthorized to access this ressource",
)
.into_response(),
RouteError::RessourceNotFound => StatusCode::NOT_FOUND.into_response(),
e => {
tracing::error!("Internal error: {e:?}");
StatusCode::INTERNAL_SERVER_ERROR.into_response()
@ -186,4 +190,16 @@ pub(crate) fn router(api_allowed: Option<HeaderValue>) -> Router<AppState> {
.delete(household::leave)
.layer(mk_service(vec![Method::PUT, Method::DELETE])),
)
.route(
"/household/:house_id/ingredients/:iid",
delete(ingredients::remove_ingredient)
.patch(ingredients::edit_ingredient)
.layer(mk_service(vec![Method::DELETE, Method::PATCH])),
)
.route(
"/household/:house_id/ingredients",
post(ingredients::create_ingredient)
.get(ingredients::list_ingredients)
.layer(mk_service(vec![Method::GET, Method::POST])),
)
}