Org Web Adapter

hungryroot/jira/be_7819_exp_242_create_sharable_favorites_cookbook_shell_on_share.org

ID
f2ed1b3c-bd0b-4d00-af31-87e42cb6234b

BE-7819 Exp 242 - Create sharable Favorites Cookbook shell on share

- source :: https://hungryroot.atlassian.net/browse/BE-7819

- tags :: Hungryroot API Project

- epic :: EP-650 Exp 242: Favorites + User Cookbooks

- sprint :: HR BE Sprint 111

- status :: Status - Complete

- priority :: Priority - Medium

- points :: 1

Summary

When a user shares their favorites, create (or retrieve) a persistent ~Cookbook~ row with ~cookbook_type=FAVORITES~ and a unique, unguessable slug. The shell Cookbook is then accessible via the existing v3 endpoint ~/api/v3/cookbooks/<slug>/~.

~CookbookType.FAVORITES~ already exists. The current ~FavoritesService~ returns a virtual ~FavoritesEnvelope~ (id=None, slug computed inline) — this ticket makes it persistent with a real slug.

Slug generation

Use a *Base62-encoded random string* (characters =[A-Za-z0-9]=), 8 characters long. Statistically unlikely to collide over 13 million+ generations.

#+begin_src python

import os

import string

ALPHABET = string.ascii_letters + string.digits # 62 chars

def generate_slug(length: int = 8) -> str:

return "".join(ALPHABET[b % 62] for b in os.urandom(length))

#+end_src

On ~get_or_create~, generate a candidate slug and retry on the (rare) collision. Do *not* use =favorites-{customer.pk}= — slug should be opaque and unguessable so sharing is opt-in.

Scope

- ~cookbooks/services.py~ — add ~get_or_create_favorites_cookbook(customer)~ on ~FavoritesService~: look up existing ~Cookbook~ for customer with ~cookbook_type=FAVORITES~; if absent, generate Base62 slug and create with ~name="My Favorites"~, ~is_active=True~.

- ~cookbooks/views.py~ — call the above when share action is triggered; return slug in response so FE can build the share URL.

- Shell Cookbook from ~/api/v3/cookbooks/<slug>/~ need not carry items — items still served by favorites GET endpoint (BE-7818).

Out of scope

- New CookbookType value (already exists).

- The favorites GET endpoint (BE-7818) and FavoritesService core logic (BE-7817).

- Any additional cookbook API changes beyond slug being accessible.

Acceptance criteria

- [ ] ~get_or_create_favorites_cookbook(customer)~ creates a ~Cookbook~ row with Base62 slug and ~cookbook_type=FAVORITES~ on first call; returns same row on subsequent calls (idempotent).

- [ ] Slug is 8 Base62 characters (=[A-Za-z0-9]=), not derived from customer PK or any PII.

- [ ] Collision retry: if generated slug already exists, new one is generated (loop with max retries).

- [ ] ~GET /api/v3/cookbooks/<slug>/~ returns 200 for the created shell.

- [ ] Slug returned to FE when share action is triggered.

- [ ] Unit tests: first-call creates row, second-call is idempotent, slug is 8 chars, collision retry works.

- [ ] ~prek run ruff~ and ~prek run ruff-format~ clean on touched files.