- ID
- a3370188-045f-427f-a305-0b5c32bb53ce
BE-7895 Exp 242 - Add cookbook sharing via Sqid-based URL
- source :: https://hungryroot.atlassian.net/browse/BE-7895
- tags :: Hungryroot API Project
- epic :: EP-650 Exp 242: Favorites + User Cookbooks
- sprint :: [[id:b9ee0cab-ac1a-47ee-b813-9efc4f1a778f][HR BE Sprint 112]]
- status :: Status - In Progress
- priority :: Priority - Medium
Summary
Replace the Base62 slug sharing introduced in BE-7819 with Sqid-based sharing. Add a ~visibility~ field to ~Cookbook~ controlling access. Remove Base62 slug infrastructure entirely.
Sqids are URL-safe, reversible, and designed to avoid offensive words. Derived deterministically from the cookbook's integer PK — no storage needed on the model.
Sqid generation
Use the ~sqids~ Python package. Encode the cookbook's integer PK:
#+begin_src python
from sqids import Sqids
sqids = Sqids()
def encode_cookbook_id(pk: int) -> str:
return sqids.encode([pk])
def decode_cookbook_sqid(sqid: str) -> int | None:
numbers = sqids.decode(sqid)
return numbers[0] if numbers else None
#+end_src
Sqid is deterministic and reversible — no need to store it on the model.
Visibility field
Add ~visibility~ to ~Cookbook~ using ~TextChoices~:
| Value | Description |
|-----------+--------------------------------------|
| ~shared~ | Accessible via Sqid-based share URL |
| ~private~ | Visible only to owner |
Default: ~private~. Share URL returns 404 if ~visibility=private~.
Scope
- ~cookbooks/models.py~ — add ~Visibility(TextChoices)~ and ~visibility~ field (default ~private~); migration required.
- ~cookbooks/services.py~ — add Sqid encode/decode helpers; update share logic to use Sqid and set ~visibility=shared~.
- ~cookbooks/views.py~ — accept Sqid as cookbook identifier; return 404 if ~visibility=private~.
- ~cookbooks/views.py~ — update list view default filter to exclude ~private~ cookbooks; remove Favorites filter.
- Remove all Base62 slug code introduced in BE-7819.
Out of scope
- Favorites GET endpoint and FavoritesService core logic.
- Any frontend URL changes beyond the API contract.
Acceptance criteria
- [ ] ~Cookbook~ has ~visibility~ field (~shared~ / ~private~, default ~private~); migration included.
- [ ] Sqid encodes/decodes cookbook PK (round-trip); not stored on model.
- [ ] ~/cookbooks/<sqid>/~ returns 200 for ~shared~ cookbook; 404 for ~private~.
- [ ] Cookbooks list view defaults to excluding ~private~ cookbooks.
- [ ] Favorites filter removed from list view.
- [ ] All Base62 slug code removed.
- [ ] Unit tests: encode/decode round-trip, lookup by Sqid, visibility access rules (shared=200, private=404).
- [ ] ~prek run ruff~ and ~prek run ruff-format~ clean on touched files.