No empty ingredients
This commit is contained in:
parent
8a4616fb13
commit
a8f1050af9
3 changed files with 39 additions and 1 deletions
19
api/meals.py
19
api/meals.py
|
|
@ -233,6 +233,15 @@ async def create_meal_scoped(
|
||||||
msg = meals.validate_meal(domain_meal)
|
msg = meals.validate_meal(domain_meal)
|
||||||
if msg:
|
if msg:
|
||||||
return error_response(request, 400, msg)
|
return error_response(request, 400, msg)
|
||||||
|
# Disallow empty extra ingredients (no product and no textual content)
|
||||||
|
for ing in domain_meal.extra_ingredients:
|
||||||
|
name = (ing.name or "").strip()
|
||||||
|
line = (ing.line or "").strip()
|
||||||
|
has_product = getattr(ing, "product", None) is not None or (
|
||||||
|
getattr(ing, "product_id", None) is not None and getattr(ing, "product_id") >= 0
|
||||||
|
)
|
||||||
|
if not has_product and name == "" and line == "":
|
||||||
|
return error_response(request, 400, "Ingredient must include a name or line or a product")
|
||||||
# Proactive validation via repositories
|
# Proactive validation via repositories
|
||||||
hid = household["id"]
|
hid = household["id"]
|
||||||
# Validate members exist as users (do not require household membership here to preserve existing behavior/tests)
|
# Validate members exist as users (do not require household membership here to preserve existing behavior/tests)
|
||||||
|
|
@ -368,6 +377,16 @@ async def update_meal_scoped(
|
||||||
400,
|
400,
|
||||||
f"Invalid recipe id(s): {', '.join(map(str, sorted(set(invalid_recipes))))}",
|
f"Invalid recipe id(s): {', '.join(map(str, sorted(set(invalid_recipes))))}",
|
||||||
)
|
)
|
||||||
|
# Disallow empty extra ingredients on update as well
|
||||||
|
for ing in domain_meal.extra_ingredients:
|
||||||
|
name = (ing.name or "").strip()
|
||||||
|
line = (ing.line or "").strip()
|
||||||
|
has_product = getattr(ing, "product", None) is not None or (
|
||||||
|
getattr(ing, "product_id", None) is not None and getattr(ing, "product_id") >= 0
|
||||||
|
)
|
||||||
|
if not has_product and name == "" and line == "":
|
||||||
|
return error_response(request, 400, "Ingredient must include a name or line or a product")
|
||||||
|
|
||||||
await meals.update_meal(conn, domain_meal)
|
await meals.update_meal(conn, domain_meal)
|
||||||
# Return updated state
|
# Return updated state
|
||||||
updated = await meals.find_meal_by_id_scoped(conn, meal_id, hid)
|
updated = await meals.find_meal_by_id_scoped(conn, meal_id, hid)
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,15 @@ async def create_recipe(
|
||||||
):
|
):
|
||||||
if not recipe.ingredients:
|
if not recipe.ingredients:
|
||||||
return error_response(None, 400, "Recipe must have at least one ingredient")
|
return error_response(None, 400, "Recipe must have at least one ingredient")
|
||||||
|
# Disallow empty ingredients with neither product nor textual content
|
||||||
|
for ing in recipe.ingredients:
|
||||||
|
name = (ing.name or "").strip()
|
||||||
|
line = (ing.line or "").strip()
|
||||||
|
has_product = getattr(ing, "product", None) is not None or (
|
||||||
|
getattr(ing, "product_id", None) is not None and getattr(ing, "product_id") >= 0
|
||||||
|
)
|
||||||
|
if not has_product and name == "" and line == "":
|
||||||
|
return error_response(None, 400, "Ingredient must include a name or line or a product")
|
||||||
hid = household["id"]
|
hid = household["id"]
|
||||||
# Build domain model and insert
|
# Build domain model and insert
|
||||||
r = recipes.Recipe(
|
r = recipes.Recipe(
|
||||||
|
|
@ -205,7 +214,10 @@ async def create_recipe(
|
||||||
ingredient.recipe_id = r.id
|
ingredient.recipe_id = r.id
|
||||||
if ingredient.product:
|
if ingredient.product:
|
||||||
ingredient.product_id = ingredient.product.id
|
ingredient.product_id = ingredient.product.id
|
||||||
await ingredients_mod.insert_ingredient(conn, ingredient)
|
try:
|
||||||
|
await ingredients_mod.insert_ingredient(conn, ingredient)
|
||||||
|
except ValueError as e:
|
||||||
|
return error_response(None, 400, str(e))
|
||||||
response.headers["Location"] = f"/api/v1/households/{household['slug']}/recipes/{r.id}"
|
response.headers["Location"] = f"/api/v1/households/{household['slug']}/recipes/{r.id}"
|
||||||
created = MemberRef(id=user.id, display_name=user.display_name)
|
created = MemberRef(id=user.id, display_name=user.display_name)
|
||||||
out = RecipeOut(
|
out = RecipeOut(
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ async def insert_ingredient(conn, ingredient: Ingredient):
|
||||||
if ingredient.product_id is None or ingredient.product_id < 0:
|
if ingredient.product_id is None or ingredient.product_id < 0:
|
||||||
ingredient.product_id = None
|
ingredient.product_id = None
|
||||||
|
|
||||||
|
# Disallow empty ingredients (no product and no textual content)
|
||||||
|
name = (ingredient.name or "").strip()
|
||||||
|
line = (ingredient.line or "").strip()
|
||||||
|
has_product = ingredient.product_id is not None and ingredient.product_id >= 0
|
||||||
|
if not has_product and name == "" and line == "":
|
||||||
|
raise ValueError("Ingredient must include a name or line or a product")
|
||||||
|
|
||||||
async with conn.execute(
|
async with conn.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO Ingredient (name, line, preparation, unit, quantity, product_id, recipe_id, meal_id)
|
INSERT INTO Ingredient (name, line, preparation, unit, quantity, product_id, recipe_id, meal_id)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue