No empty ingredients

This commit is contained in:
jableader 2025-11-02 18:10:27 +11:00
parent 8a4616fb13
commit a8f1050af9
3 changed files with 39 additions and 1 deletions

View file

@ -233,6 +233,15 @@ async def create_meal_scoped(
msg = meals.validate_meal(domain_meal)
if 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
hid = household["id"]
# 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,
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)
# Return updated state
updated = await meals.find_meal_by_id_scoped(conn, meal_id, hid)

View file

@ -189,6 +189,15 @@ async def create_recipe(
):
if not recipe.ingredients:
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"]
# Build domain model and insert
r = recipes.Recipe(
@ -205,7 +214,10 @@ async def create_recipe(
ingredient.recipe_id = r.id
if ingredient.product:
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}"
created = MemberRef(id=user.id, display_name=user.display_name)
out = RecipeOut(

View file

@ -37,6 +37,13 @@ async def insert_ingredient(conn, ingredient: Ingredient):
if ingredient.product_id is None or ingredient.product_id < 0:
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(
"""
INSERT INTO Ingredient (name, line, preparation, unit, quantity, product_id, recipe_id, meal_id)