Skip to content

feat: publish deal.created from every booking path - #85

Open
numarasSigmaSoftware wants to merge 8 commits into
IABTechLab:mainfrom
numarasSigmaSoftware:feature/booking-events
Open

numarasSigmaSoftware wants to merge 8 commits into
IABTechLab:mainfrom
numarasSigmaSoftware:feature/booking-events

Conversation

@numarasSigmaSoftware

@numarasSigmaSoftware numarasSigmaSoftware commented Sep 15, 2026

Copy link
Copy Markdown

Problem

Only the approval flow and the negotiation service publish events. A deal booked over REST left GET /events empty, so a client polling the feed never learned a deal existed. Verified on a running instance: two bookings, event count zero.

Change

Add _emit_deal_created() and call it after each booking path persists its deal: quote booking, from-template, curated, bulk create, and migration. The payload carries source (one of those five), deal_type, status, product_id, and quote_id.

deal.created is an audit-class event in the event model, so the emission is fail-closed by design: if the bus fails, the event is written to the audit fallback log and the booking still returns 200; if that write also fails, the error propagates after the deal is already persisted. Both branches are pinned by tests. The retry-after-failure ordering, where a failure after the quote is flipped leaves a retry with a 409 and no deal id, applies to any post-persist failure and predates this change; it is tracked as #88.

Docs: the event-bus overview now describes the real emitters and the source values; changelog entry under Unreleased; data/audit_fallback.jsonl is gitignored. The fallback file is the same class of leftover as the database in #82: the audience-plan tests wrote it through a fake storage that backed the event bus, and now publish to an in-memory bus instead.

Verification

  • tests/unit/test_booking_events.py: one test per booking path (five), bus failure with fallback written and booking at 200, fallback failure propagating after persist, idempotent replay emitting exactly once, and every payload asserted on all five keys. Each behavior test was seen failing before its change.
  • tests/unit/test_audience_plan_validation.py now publishes to an in-memory bus, so its fake storage no longer trips the audit fallback; a suite run on head leaves the tree clean.
  • The quote booking test authenticates as a buyer, since fix: booking accepted anonymous callers - require verified buyer matching the quote #77 requires a verified buyer on POST /api/v1/deals.
  • Full unit suite on a fresh database: all pass. On a reused working directory one pre-existing test fails, test_self_asserted_advertiser_identity_is_floored, because the suite leaves ad_seller.db behind; that is Unit suite leaves ./ad_seller.db behind, so a second consecutive local run can fail #82 and is not touched by this PR.

Independent of #84; the deal_service.py hunks are disjoint.

Not changed

From-template booking has no idempotency lookup, so a retried request creates a second deal and now a second event. Pre-existing; tracked as #87.

Only the approval flow and the negotiation service emitted events. A
deal booked over REST (quote -> book, from-template, or curated) left
GET /events empty, so a client polling the feed never learned a deal
existed.

Add _emit_deal_created() and call it after each booking path persists
its deal. The payload carries source (quote | template | curated),
deal_type, status, product_id and quote_id. Fail-open like every
non-audit event: a bus failure never fails the booking.

Tests authenticate the quote booking call, since IABTechLab#77 requires a
verified buyer on POST /api/v1/deals.
…nches

_emit_deal_created is fail-closed, not fail-open: deal.created is in
AUDIT_EVENT_TYPES, so a bus failure writes the event to the audit fallback
file and the booking still succeeds; if that write also fails, the error
propagates after the deal is already persisted. No code-path change.

Two tests pin the branches over HTTP on the quote path.
bulk_deal_operations (create branch) and migrate_deal persisted a new deal
without publishing deal.created, so the event feed missed those bookings.
Both now call _emit_deal_created right after storage.set_deal, with
source "bulk" and "migration"; the helper docstring lists all five sources.
No emission for deprecate or for the old deal in a migration.
Same quote booked twice over HTTP with the same idempotency_key: both
responses are 200 with the same deal_id and the bus holds exactly one
deal.created. Test-only; the emission already sits after the replay
short-circuit.
Each payload-inspecting test now asserts source, deal_type, status,
product_id and quote_id (None where the path books without a quote)
through one module-level _assert_payload helper shared by the five tests.
The http_client fixture's _FakeStorage has no ``set``, so the booking's
audit-class deal.created fell back to data/audit_fallback.jsonl on every
run. The fixture now patches get_event_bus to an InMemoryEventBus for
every test that books through it, and the fallback path is gitignored
next to *.db.
The DEAL_CREATED row said the event came from an accepted proposal; it is published by every booking path, with payload.source naming which one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant