feat(compat): v2.5 get_products adapter (stage 5b)#668
Merged
Conversation
Port of pricing-adapter.ts (inverted from JS direction) plus brand/catalog/channel translation. JS is v3→v2 (client side); our server-side use case is v2→v3, so request and response swap roles. * brand_manifest (v2.5 URL) ↔ brand.domain (v3 BrandReference) * promoted_offerings (v2.5 nested) ↔ catalog (v3 discriminated union) * Channels — lossy both directions: - video → olv+ctv, audio → streaming_audio, native → display, retail → retail_media - response collapses back, deduped * Pricing options: - rate + is_fixed ↔ fixed_price - price_guidance.floor ↔ top-level floor_price - Percentile fields stay in price_guidance 20 new tests cover both directions, idempotency on v3-already-shape, and unknown-channel pass-through. Updated test_v2_5_tool_without_adapter_raises_invalid_request to use create_media_buy (still unported in Stage 5c). Stage 5c will port create_media_buy + update_media_buy (creative-adapter.ts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review feedback on PR #668 flagged that the v2/v3 precedence in ``_normalize_pricing_option`` is correct but undocumented. Add a test that pins ``rate`` (v2) over ``fixed_price`` (v3) when both are present — matches the JS reference implementation's behaviour and documents intent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
bokelley
added a commit
that referenced
this pull request
May 11, 2026
…e 5c) (#669) * feat(compat): v2.5 get_products adapter (stage 5b) Port of pricing-adapter.ts (inverted from JS direction) plus brand/catalog/channel translation. JS is v3→v2 (client side); our server-side use case is v2→v3, so request and response swap roles. * brand_manifest (v2.5 URL) ↔ brand.domain (v3 BrandReference) * promoted_offerings (v2.5 nested) ↔ catalog (v3 discriminated union) * Channels — lossy both directions: - video → olv+ctv, audio → streaming_audio, native → display, retail → retail_media - response collapses back, deduped * Pricing options: - rate + is_fixed ↔ fixed_price - price_guidance.floor ↔ top-level floor_price - Percentile fields stay in price_guidance 20 new tests cover both directions, idempotency on v3-already-shape, and unknown-channel pass-through. Updated test_v2_5_tool_without_adapter_raises_invalid_request to use create_media_buy (still unported in Stage 5c). Stage 5c will port create_media_buy + update_media_buy (creative-adapter.ts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(compat): pin v2/v3 precedence for half-migrated pricing options Review feedback on PR #668 flagged that the v2/v3 precedence in ``_normalize_pricing_option`` is correct but undocumented. Add a test that pins ``rate`` (v2) over ``fixed_price`` (v3) when both are present — matches the JS reference implementation's behaviour and documents intent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(compat): v2.5 create_media_buy + update_media_buy adapters (stage 5c) Closes out the v2.5 → v3 adapter catalog. Shared helpers in ``_media_buy_helpers`` (creative_ids ↔ creative_assignments, brand manifest, null-array coercion). Translations: * Package shape: v2.5 creative_ids ↔ v3 creative_assignments. The response-direction collapse drops v3-only weight + placement_ids silently — v2.5 buyers can't surface them, so the alternative (raise on every response with weights) rejects the typical case. * create_media_buy: brand_manifest URL → brand.domain. * Null-array coercion (creative_assignments / creative_ids / products set to null become absent fields). update_media_buy does NOT translate brand_manifest — updates don't carry brand in v3, and v2.5 buyers sending the field on an update are passing through metadata the handler can ignore. Tested explicitly. Updated test_v2_5_tool_without_adapter_raises_invalid_request to use check_governance (v3-added tool with no v2.5 adapter) since create_media_buy is now covered. Full v2.5 catalog now adapted: sync_creatives, list_creative_formats, preview_creative, get_products, create_media_buy, update_media_buy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(compat): consolidate strip_url_scheme into shared module Review feedback on PR #669. The helper was defined byte-for-byte in both ``_media_buy_helpers.py`` and ``get_products.py``; future edits would drift. Move to ``adcp.compat.legacy.v2_5._url`` so both import the same canonical implementation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stage 5b of the versioned-schema-validation port. Follow-up to #667 (Stage 5).
Summary
Port of
src/lib/utils/pricing-adapter.ts(inverted from the JS direction) plus brand/catalog/channel translation forget_products.The JS adapter is v3→v2 (client side, talking to v2 servers); our server-side use case is v2→v3 (buyer claims v2.5, server speaks v3 internally), so request and response swap roles.
Translations
brand_manifest(v2.5 URL string) ↔brand(v3 BrandReference)promoted_offerings(v2.5 nested) ↔catalog(v3 discriminated union)videofans out toolv+ctv,audio→streaming_audio, etc.olv+ctvcollapse back tovideo(deduped)rate+is_fixed↔fixed_priceprice_guidance.floor↔floor_priceprice_guidanceStage 5c will
Port the remaining v2.5 adapters (
create_media_buy,update_media_buy) which depend on the JScreative-adapter.tshelpers (~300 lines coveringcreative_ids↔creative_assignmentsand proposal-mode lossy handling).Test plan
pytest tests/ -q --ignore=tests/integration— 4495 passedruff check+mypy🤖 Generated with Claude Code