server: Add a route to add a recipe
This commit is contained in:
parent
82b466f52c
commit
e49a5829c4
3 changed files with 83 additions and 0 deletions
|
|
@ -83,3 +83,16 @@ pub struct EditIngredientRequest {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub has_unit: bool,
|
pub has_unit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct CreateRecipeRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub rating: u8,
|
||||||
|
pub ingredients: Vec<(i64, f64)>,
|
||||||
|
pub steps: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct CreateRecipeResponse {
|
||||||
|
pub id: i64,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use crate::entity::{prelude::*, user};
|
||||||
|
|
||||||
mod household;
|
mod household;
|
||||||
mod ingredients;
|
mod ingredients;
|
||||||
|
mod recipe;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
enum RouteError {
|
enum RouteError {
|
||||||
|
|
@ -42,6 +43,8 @@ enum RouteError {
|
||||||
PathRejection(#[from] axum::extract::rejection::PathRejection),
|
PathRejection(#[from] axum::extract::rejection::PathRejection),
|
||||||
#[error("The supplied ressource does not exist")]
|
#[error("The supplied ressource does not exist")]
|
||||||
RessourceNotFound,
|
RessourceNotFound,
|
||||||
|
#[error("The request was malformed")]
|
||||||
|
InvalidRequest(String),
|
||||||
#[error("Error in DB transaction")]
|
#[error("Error in DB transaction")]
|
||||||
TxnError(#[from] TransactionError<Box<RouteError>>),
|
TxnError(#[from] TransactionError<Box<RouteError>>),
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +76,7 @@ impl IntoResponse for RouteError {
|
||||||
)
|
)
|
||||||
.into_response(),
|
.into_response(),
|
||||||
RouteError::RessourceNotFound => StatusCode::NOT_FOUND.into_response(),
|
RouteError::RessourceNotFound => StatusCode::NOT_FOUND.into_response(),
|
||||||
|
RouteError::InvalidRequest(reason) => (StatusCode::BAD_REQUEST, reason).into_response(),
|
||||||
e => {
|
e => {
|
||||||
tracing::error!("Internal error: {e:?}");
|
tracing::error!("Internal error: {e:?}");
|
||||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
|
|
@ -212,4 +216,8 @@ pub(crate) fn router(api_allowed: Option<HeaderValue>) -> Router<AppState> {
|
||||||
.get(ingredients::list_ingredients)
|
.get(ingredients::list_ingredients)
|
||||||
.layer(mk_service(vec![Method::GET, Method::POST])),
|
.layer(mk_service(vec![Method::GET, Method::POST])),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/household/:house_id/recipe",
|
||||||
|
post(recipe::create_recipe).layer(mk_service(vec![Method::POST])),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
62
src/routes/recipe.rs
Normal file
62
src/routes/recipe.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
use api::{CreateRecipeRequest, CreateRecipeResponse};
|
||||||
|
use axum::{extract::State, Json};
|
||||||
|
use sea_orm::{prelude::*, ActiveValue, TransactionTrait};
|
||||||
|
|
||||||
|
use crate::entity::{ingredient, prelude::*, recipe, recipe_ingerdients, recipe_steps};
|
||||||
|
|
||||||
|
use super::{household::AuthorizedHousehold, AppState, JsonResult, RouteError};
|
||||||
|
|
||||||
|
pub(super) async fn create_recipe(
|
||||||
|
AuthorizedHousehold(household): AuthorizedHousehold,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(request): Json<CreateRecipeRequest>,
|
||||||
|
) -> JsonResult<CreateRecipeResponse> {
|
||||||
|
let id = state
|
||||||
|
.db
|
||||||
|
.transaction(|txn| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let model = recipe::ActiveModel {
|
||||||
|
name: ActiveValue::Set(request.name),
|
||||||
|
ranking: ActiveValue::Set(request.rating as i32),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let recipe = model.insert(txn).await?;
|
||||||
|
for (num, text) in request.steps.into_iter().enumerate() {
|
||||||
|
let model = recipe_steps::ActiveModel {
|
||||||
|
num: ActiveValue::Set(num as _),
|
||||||
|
recipe_id: ActiveValue::Set(recipe.id),
|
||||||
|
text: ActiveValue::Set(text),
|
||||||
|
};
|
||||||
|
|
||||||
|
model.insert(txn).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ig, amount) in request.ingredients {
|
||||||
|
if 0 == household
|
||||||
|
.find_related(Ingredient)
|
||||||
|
.filter(ingredient::Column::Id.eq(ig))
|
||||||
|
.count(txn)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Err(RouteError::InvalidRequest(format!(
|
||||||
|
"No such ingredient {ig}"
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let model = recipe_ingerdients::ActiveModel {
|
||||||
|
recipe_id: ActiveValue::Set(recipe.id),
|
||||||
|
ingredient_id: ActiveValue::Set(ig),
|
||||||
|
amount: ActiveValue::Set(amount),
|
||||||
|
};
|
||||||
|
|
||||||
|
model.insert(txn).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(recipe.id)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(CreateRecipeResponse { id }.into())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue