Implement TicketFlow's error contract. No solutions.md before submitting.
Exercise 1 — The taxonomy
- List TicketFlow's domain exceptions with their status: seat taken (409), reservation expired (410), event sold out (409), payment declined (402 or 409 — decide and justify), gift card balance insufficient (409 or 422 — decide), not authenticated (401, DRF handles it).
- For each one write the complete problem+json (type, title, status, detail) exactly as the frontend's client would see it.
Exercise 2 — The handler
- Implement
problem_handlerwithto_problem()andproblem_response(), wire it in settings and make DRF's validation errors come out as problem+json (status 400, type.../validation-error, anderrorswith the field → messages dict). - Write the contract test: POST to reservations with an invalid body → 400 with Content-Type
application/problem+jsonand atypeending invalidation-error. - Migrate an OLDER view (the one still returning
Response({"error":...})): delete its try/except, raise the domain exception, and verify with the test that the response didn't change (the test is 28's safety net).
Exercise 3 — Actionable extra
- Add
extratoSeatUnavailable(conflicting_seats)and toReservationExpired(retry_after)— in the latter case also theRetry-Afterheader. Where does the handler inject them? - Test: expired reservation → 410 with header
Retry-After: 30and body withretry_after: 30.
Exercise 4 — The healthy 500
- Force a 500 (a view raising
ZeroDivisionErroron a test endpoint). Verify: generic problem+json body WITHOUT stack, log withlogger.exceptionand the trace_id present. - Add the
http_5xx_total{path}metric (in-memory or Redis counter, 46 formalizes it) incremented in the handler. Why in the handler and not in a separate middleware? (hint: one single place). - Decide and write: should a 429 (23's rate limit) be logged with warning or is it noise? And a 401?
Exercise 5 — The client that programs against the contract
- Write a test that consumes the API with a fake HTTP client and applies ALL the rules: every 4xx/5xx error has an absolute URI
type, a non-emptytitle, astatusmatching the HTTP status,trace_idpresent on 5xx. A singleassert_problem(response)function. - Paste it into 3 different tests (validation, domain, 500). How many lines did it save you versus writing the assertions by hand in each one?
Submit
Paste the taxonomy, the complete handler, the contract test and the 500 test. Next: Lesson 27 — 12-factor configuration.