From 854b924a28b6c47b898100b389078f873b1f907b Mon Sep 17 00:00:00 2001 From: jableader Date: Sat, 1 Nov 2025 23:39:14 +1100 Subject: [PATCH] feat(api): scope ingredient parse to household and align recipe parse-from-url with RecipeCreate --- backend-spec.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend-spec.md b/backend-spec.md index 33671bf..cf1f24c 100644 --- a/backend-spec.md +++ b/backend-spec.md @@ -5,8 +5,8 @@ - DELETE `/api/v1/households/{householdSlug}/shopping/current/meals/{mealId}` unrequests the meal (scoped) and returns `{ ok: true }`. - POST `/api/v1/households/{householdSlug}/shopping/current/ingredients` requests an ad‑hoc ingredient scoped to household+user; duplicates deduped per user per household. - DELETE `/api/v1/households/{householdSlug}/shopping/current/ingredients` removes an ad‑hoc ingredient request for the current user in this household; idempotent. - - POST `/api/v1/households/{householdSlug}/recipes/parse-from-url` returns structured JSON‑LD recipe data for a URL (stateless; household auth enforced). - - GET `/api/v1/recipes/ingredients/parse?ingredients=...&ingredients=...` parses raw ingredient lines into Ingredient DTOs (stateless, public); attempts best-effort product matching. + - POST `/api/v1/households/{householdSlug}/recipes/parse-from-url` returns a RecipeCreate payload parsed from a URL (stateless; household auth enforced). Shape matches the body accepted by `POST /recipes`. + - GET `/api/v1/households/{householdSlug}/ingredients/parse?line=...` parses a single ingredient line into an `Ingredient` (scoped; JWT + membership). Attempts best-effort product matching. - Comprehensive v2 coverage exists for scoping, purchases, request/unrequest, meals CRUD/consumed, and OpenAPI security. PASS. # Backend Specification: Household Multi-Tenancy (v2) @@ -43,8 +43,8 @@ Special-case 401: Removed. v1 cookie-based auth and routes have been retired in - Recipes (`api/recipes.py`) - GET `/api/v1/recipes` → `Page`; loads ingredients per page. - GET `/api/v1/recipes/{id}` → full recipe (ingredients + createdBy). - - POST `/api/v1/households/{householdSlug}/recipes/parse-from-url` (auth required) → scrape/parse a recipe URL; 404 if not found. - - GET `/api/v1/recipes/ingredients/parse?ingredients=...&ingredients=...` → parse raw ingredient lines (no auth); matches existing products. Implemented in v2 as public utility under the same path. + - POST `/api/v1/households/{householdSlug}/recipes/parse-from-url` (auth required) → scrape/parse a recipe URL; returns `RecipeCreate` (no id/createdBy); 404 if not found. + - GET `/api/v1/households/{householdSlug}/ingredients/parse?line=...` → parse a single raw ingredient line (JWT + membership); matches existing products. - POST `/api/v1/recipes` (auth required) → validate (≥1 ingredient), perform versioning (hide base if id≥0), set createdById, insert ingredients; sets `Location` header. - DELETE `/api/v1/recipes/{id}` (auth required) → soft-delete (hide) recipe. @@ -205,8 +205,8 @@ Route surface lockdown: Completed: - Auth endpoints (`/api/v1/auth/*`) migrated to JWT with tokens/refresh cookie. - Household-scoped recipes/meals/shopping endpoints in place. -- `POST /api/v1/households/{householdSlug}/recipes/parse-from-url` implemented. -- `GET /api/v1/recipes/ingredients/parse` ported as a public stateless NLP parser returning `Ingredient[]`. +- `POST /api/v1/households/{householdSlug}/recipes/parse-from-url` implemented (returns `RecipeCreate`). +- `GET /api/v1/households/{householdSlug}/ingredients/parse` implemented (returns single `Ingredient`, scoped). Outstanding (tracked): - None identified blocking parity for shopping/recipes needed by the frontend as of 2025-11-01. Re-check if any v1 product scrape/create endpoint needs re-exposure; current frontend uses household flows and parsing utilities.