Skip to content

Support per-section GAM ad unit paths via gam_unit_path templates#957

Open
prk-Jr wants to merge 10 commits into
mainfrom
954-per-section-gam-unit-path
Open

Support per-section GAM ad unit paths via gam_unit_path templates#957
prk-Jr wants to merge 10 commits into
mainfrom
954-per-section-gam-unit-path

Conversation

@prk-Jr

@prk-Jr prk-Jr commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • creative_opportunities.slot.gam_unit_path is now a template with {network_id}, {section}, and {slot_id} placeholders, so a publisher whose GAM ad unit varies by site section expresses that in one slot rule instead of one rule per (slot × section).
  • {section} is derived from the request path (first segment, sanitized), with a required section_root for / — the URL→section convention lives in config, not core.
  • Server-only change: gam_unit_path reaches googletag.defineSlot client-side but is not in the OpenRTB request, so the wire shape is unchanged and there is no JS change.

Changes

File Change
crates/trusted-server-core/src/creative_opportunities.rs Add UnitTemplatePart, parse_unit_template, sanitize_section, derive_section; slot compiled_unit cache + compile_unit_template/render_gam_unit_path/template_uses_section; config section_root + compile_unit_templates; validate_runtime now enforces section_root when {section} is used and drops the render-time emptiness check
crates/trusted-server-core/src/publisher.rs build_slot_json/build_ad_slots_script take request_path; render the template on both the initial-render and handle_page_bids (SPA) paths
crates/trusted-server-core/src/settings.rs prepare_runtime parses templates via compile_unit_templates before validation, surfacing parse errors as Configuration errors
docs/guide/configuration.md New "Creative Opportunities Configuration" section documenting templating, {section} derivation, the no-decode rule, and unmatched-route behavior
trusted-server.example.toml Document section_root and a templated-slot example
docs/superpowers/specs/, docs/superpowers/plans/ Design spec and implementation plan

Closes

Closes #954

Test plan

  • cargo test-fastly && cargo test-axum (also test-cloudflare, test-spin — all pass)
  • cargo fmt --all -- --check
  • cargo clippy-fastly (remaining clippy targets left to CI)
  • Docs format: cd docs && npm run format
  • JS tests / JS format — no JS change in this PR; left to CI
  • clippy-axum / clippy-cloudflare* / clippy-spin* — left to CI

New tests: template parsing (unknown placeholder, unmatched/nested brace, empty), section derivation (/, single/multi-segment, unsafe/undecoded segment new%20snew_20s), render_gam_unit_path (template, default, verbatim), startup validation (missing/invalid section_root, parse error), and initial-render/SPA equivalence for the same path.

Hardening note

Template parsing and section_root validation are config-derived and run at startup in Settings::prepare_runtime. Invalid enabled config fails startup via Report<TrustedServerError::Configuration> — no panic!, unwrap(), or expect() on the config path. compile_unit_templates rejects malformed templates (unknown placeholder, unmatched/nested brace, empty); validate_runtime rejects a {section} template with a missing or non-[A-Za-z0-9_-] section_root. Regression coverage: compile_unit_templates_surfaces_parse_error, validate_runtime_requires_section_root_when_template_uses_section, validate_runtime_rejects_invalid_section_root, and settings_rejects_creative_opportunity_slot_with_empty_gam_unit_path. Existing static gam_unit_path configs are unaffected (verbatim when no placeholders; /{network_id}/{slot_id} when absent).

Checklist

  • Changes follow CLAUDE.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses log macros (not println!)
  • New code has tests
  • No secrets or credentials committed

@prk-Jr prk-Jr self-assigned this Jul 23, 2026
@prk-Jr
prk-Jr requested review from ChristianPavilonis and aram356 and removed request for aram356 July 23, 2026 16:25
prk-Jr and others added 3 commits July 23, 2026 21:55
- Remove now-dead resolved_gam_unit_path (superseded by
  render_gam_unit_path) and its two tests; the default
  /<network_id>/<slot_id> path now lives solely in render_gam_unit_path
- Tighten compile_unit_template and render_gam_unit_path to pub(crate)
- Document that validate_runtime must run after compile_unit_templates
  for the {section} -> section_root check to fire
- Use fictional gam_network_id 99999 in the new build_slot_json test
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.

creative_opportunities: express per-section gam_unit_path without one rule per (slot × section)

1 participant