server: Add a route to add a recipe

This commit is contained in:
traxys 2023-06-22 22:29:53 +02:00
parent 82b466f52c
commit e49a5829c4
3 changed files with 83 additions and 0 deletions

View file

@ -83,3 +83,16 @@ pub struct EditIngredientRequest {
#[serde(default)]
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,
}

View file

@ -23,6 +23,7 @@ use crate::entity::{prelude::*, user};
mod household;
mod ingredients;
mod recipe;
#[derive(thiserror::Error, Debug)]
enum RouteError {
@ -42,6 +43,8 @@ enum RouteError {
PathRejection(#[from] axum::extract::rejection::PathRejection),
#[error("The supplied ressource does not exist")]
RessourceNotFound,
#[error("The request was malformed")]
InvalidRequest(String),
#[error("Error in DB transaction")]
TxnError(#[from] TransactionError<Box<RouteError>>),
}
@ -73,6 +76,7 @@ impl IntoResponse for RouteError {
)
.into_response(),
RouteError::RessourceNotFound => StatusCode::NOT_FOUND.into_response(),
RouteError::InvalidRequest(reason) => (StatusCode::BAD_REQUEST, reason).into_response(),
e => {
tracing::error!("Internal error: {e:?}");
StatusCode::INTERNAL_SERVER_ERROR.into_response()
@ -212,4 +216,8 @@ pub(crate) fn router(api_allowed: Option<HeaderValue>) -> Router<AppState> {
.get(ingredients::list_ingredients)
.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
View 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())
}