Skip to content

feat(compat): v2.5 get_products adapter (stage 5b)#668

Merged
bokelley merged 2 commits into
mainfrom
claude/versioned-schemas-stage-5b
May 11, 2026
Merged

feat(compat): v2.5 get_products adapter (stage 5b)#668
bokelley merged 2 commits into
mainfrom
claude/versioned-schemas-stage-5b

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

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 for get_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)
  • Channels — lossy in both directions:
    • Request: video fans out to olv+ctv, audiostreaming_audio, etc.
    • Response: olv+ctv collapse back to video (deduped)
  • Pricing options:
    • rate + is_fixedfixed_price
    • price_guidance.floorfloor_price
    • Percentile fields stay in price_guidance

Stage 5c will

Port the remaining v2.5 adapters (create_media_buy, update_media_buy) which depend on the JS creative-adapter.ts helpers (~300 lines covering creative_idscreative_assignments and proposal-mode lossy handling).

Test plan

  • pytest tests/ -q --ignore=tests/integration — 4495 passed
  • 20 new tests both directions + v3-already-shape idempotency
  • ruff check + mypy

🤖 Generated with Claude Code

bokelley and others added 2 commits May 11, 2026 07:10
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>
@bokelley bokelley merged commit 2025520 into main May 11, 2026
18 of 30 checks passed
@bokelley bokelley deleted the claude/versioned-schemas-stage-5b branch May 11, 2026 11:55
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>
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