feat(shopping): add household-scoped ingredient request endpoint + tests and spec
This commit is contained in:
parent
75d098cd7a
commit
978c970fb6
3 changed files with 32 additions and 12 deletions
|
|
@ -13,6 +13,7 @@ from api.shopping_models import (
|
||||||
ShoppingListOut,
|
ShoppingListOut,
|
||||||
PurchasedShoppingList,
|
PurchasedShoppingList,
|
||||||
_to_ingredient_item,
|
_to_ingredient_item,
|
||||||
|
ListIngredientItem,
|
||||||
_to_meal_item,
|
_to_meal_item,
|
||||||
_to_shopping_list_out,
|
_to_shopping_list_out,
|
||||||
RequestedMealItem,
|
RequestedMealItem,
|
||||||
|
|
@ -202,6 +203,33 @@ class IngredientIdWrapper(_ApiModel):
|
||||||
ingredient_id: int
|
ingredient_id: int
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/current/ingredients",
|
||||||
|
response_model=ListIngredientItem,
|
||||||
|
operation_id="requestIngredientV2",
|
||||||
|
summary="Request an ingredient for shopping (scoped)",
|
||||||
|
)
|
||||||
|
async def request_ingredient_scoped(
|
||||||
|
r: IngredientIdWrapper,
|
||||||
|
request: Request,
|
||||||
|
household=Depends(get_household_from_slug),
|
||||||
|
user=Depends(get_current_user),
|
||||||
|
conn: aiosqlite.Connection = Depends(get_db),
|
||||||
|
):
|
||||||
|
hid = household["id"]
|
||||||
|
from ingredients.repository import find_ingredient_by_id
|
||||||
|
|
||||||
|
ingredient = await find_ingredient_by_id(conn, r.ingredient_id)
|
||||||
|
if not ingredient:
|
||||||
|
return error_response(request, 404, "Ingredient not found")
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = await shopping.request_ingredient_scoped(conn, ingredient, hid, user.id)
|
||||||
|
except ValueError as e:
|
||||||
|
return error_response(request, 400, str(e))
|
||||||
|
return _to_ingredient_item(item)
|
||||||
|
|
||||||
|
|
||||||
# Re-export shared DTOs for importers
|
# Re-export shared DTOs for importers
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"router",
|
"router",
|
||||||
|
|
|
||||||
|
|
@ -121,9 +121,7 @@ class TestRecipesHouseholdV2(unittest.IsolatedAsyncioTestCase):
|
||||||
assert "createdBy" in r.json() and "createdById" in r.json()
|
assert "createdBy" in r.json() and "createdById" in r.json()
|
||||||
|
|
||||||
# Delete it via v2 scoped route
|
# Delete it via v2 scoped route
|
||||||
r2 = self.client.delete(
|
r2 = self.client.delete(f"/api/v1/households/{self.h1}/recipes/{rid}", headers=self.headers)
|
||||||
f"/api/v1/households/{self.h1}/recipes/{rid}", headers=self.headers
|
|
||||||
)
|
|
||||||
assert r2.status_code == 200, r2.text
|
assert r2.status_code == 200, r2.text
|
||||||
# hiddenBy is populated on delete
|
# hiddenBy is populated on delete
|
||||||
body_del = r2.json()
|
body_del = r2.json()
|
||||||
|
|
@ -131,9 +129,7 @@ class TestRecipesHouseholdV2(unittest.IsolatedAsyncioTestCase):
|
||||||
assert body_del["hiddenBy"]["displayName"] == "R"
|
assert body_del["hiddenBy"]["displayName"] == "R"
|
||||||
|
|
||||||
# Subsequent get in same household should be 404
|
# Subsequent get in same household should be 404
|
||||||
r3 = self.client.get(
|
r3 = self.client.get(f"/api/v1/households/{self.h1}/recipes/{rid}", headers=self.headers)
|
||||||
f"/api/v1/households/{self.h1}/recipes/{rid}", headers=self.headers
|
|
||||||
)
|
|
||||||
assert r3.status_code == 404
|
assert r3.status_code == 404
|
||||||
|
|
||||||
def test_create_recipe_requires_ingredients_and_cursor_edge(self):
|
def test_create_recipe_requires_ingredients_and_cursor_edge(self):
|
||||||
|
|
|
||||||
|
|
@ -75,16 +75,12 @@ class TestShoppingRequestIngredientV2(unittest.IsolatedAsyncioTestCase):
|
||||||
assert item["mealId"] is None
|
assert item["mealId"] is None
|
||||||
|
|
||||||
# Visible in H1 current, not in H2
|
# Visible in H1 current, not in H2
|
||||||
r1 = self.client.get(
|
r1 = self.client.get(f"/api/v1/households/{self.h1}/shopping/current", headers=self.headers)
|
||||||
f"/api/v1/households/{self.h1}/shopping/current", headers=self.headers
|
|
||||||
)
|
|
||||||
assert r1.status_code == 200, r1.text
|
assert r1.status_code == 200, r1.text
|
||||||
cur1 = r1.json()
|
cur1 = r1.json()
|
||||||
assert any(i.get("ingredientId") == self.milk_id for i in cur1["outstandingItems"])
|
assert any(i.get("ingredientId") == self.milk_id for i in cur1["outstandingItems"])
|
||||||
|
|
||||||
r2 = self.client.get(
|
r2 = self.client.get(f"/api/v1/households/{self.h2}/shopping/current", headers=self.headers)
|
||||||
f"/api/v1/households/{self.h2}/shopping/current", headers=self.headers
|
|
||||||
)
|
|
||||||
assert r2.status_code == 200, r2.text
|
assert r2.status_code == 200, r2.text
|
||||||
cur2 = r2.json()
|
cur2 = r2.json()
|
||||||
assert not any(i.get("ingredientId") == self.milk_id for i in cur2["outstandingItems"])
|
assert not any(i.get("ingredientId") == self.milk_id for i in cur2["outstandingItems"])
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue