From d199f21a2f08676b9375f054eca111bcc9a8b314 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 8 Dec 2025 16:56:05 -0600 Subject: [PATCH 1/2] Add bidder params and debug helpers to openrtb request --- crates/common/src/auction/formats.rs | 19 ++ crates/common/src/auction/types.rs | 2 + crates/common/src/integrations/prebid.rs | 261 ++++++++++++++++++++++- crates/js/lib/src/core/types.ts | 6 + 4 files changed, 280 insertions(+), 8 deletions(-) diff --git a/crates/common/src/auction/formats.rs b/crates/common/src/auction/formats.rs index c90308c..79ae469 100644 --- a/crates/common/src/auction/formats.rs +++ b/crates/common/src/auction/formats.rs @@ -39,6 +39,16 @@ pub struct AdRequest { pub struct AdUnit { pub code: String, pub media_types: Option, + pub bids: Option>, +} + +/// Bidder configuration from the request. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BidConfig { + pub bidder: String, + #[serde(default)] + pub params: serde_json::Value, } #[derive(Debug, Deserialize)] @@ -91,11 +101,20 @@ pub fn convert_tsjs_to_auction_request( }); } + // Extract bidder params from the bids array + let mut bidders = std::collections::HashMap::new(); + if let Some(bids) = &unit.bids { + for bid in bids { + bidders.insert(bid.bidder.clone(), bid.params.clone()); + } + } + slots.push(AdSlot { id: unit.code.clone(), formats, floor_price: None, targeting: std::collections::HashMap::new(), + bidders, }); } } diff --git a/crates/common/src/auction/types.rs b/crates/common/src/auction/types.rs index 6ea1242..2792ffc 100644 --- a/crates/common/src/auction/types.rs +++ b/crates/common/src/auction/types.rs @@ -37,6 +37,8 @@ pub struct AdSlot { pub floor_price: Option, /// Slot-specific targeting pub targeting: HashMap, + /// Bidder configurations (bidder name -> params) + pub bidders: HashMap, } /// Ad format specification. diff --git a/crates/common/src/integrations/prebid.rs b/crates/common/src/integrations/prebid.rs index fa6622a..4a38958 100644 --- a/crates/common/src/integrations/prebid.rs +++ b/crates/common/src/integrations/prebid.rs @@ -47,6 +47,8 @@ pub struct PrebidIntegrationConfig { pub debug: bool, #[serde(default)] pub script_handler: Option, + #[serde(default)] + pub debug_query_params: Option, } impl IntegrationConfig for PrebidIntegrationConfig { @@ -275,7 +277,7 @@ fn build_openrtb_from_ts( imp: imps, site: Some(Site { domain: Some(settings.publisher.domain.clone()), - page: Some(format!("https://{}", settings.publisher.domain)), + page: Some(format!("https://{}", &settings.publisher.domain)), }), } } @@ -318,6 +320,7 @@ async fn handle_prebid_auction( &fresh_id, settings, &req, + config, )?; let mut pbs_req = Request::new( @@ -332,6 +335,7 @@ async fn handle_prebid_auction( })?; log::info!("Sending request to Prebid Server"); + let backend_name = ensure_backend_from_url(&config.server_url)?; let mut pbs_response = pbs_req @@ -377,6 +381,7 @@ fn enhance_openrtb_request( fresh_id: &str, settings: &Settings, req: &Request, + config: &PrebidIntegrationConfig, ) -> Result<(), Report> { if !request["user"].is_object() { request["user"] = json!({}); @@ -413,10 +418,31 @@ fn enhance_openrtb_request( } if !request["site"].is_object() { + let mut page_url = format!("https://{}", settings.publisher.domain); + + // Append debug query params if configured + if let Some(ref params) = config.debug_query_params { + page_url = format!("{}?{}", page_url, params); + } + request["site"] = json!({ "domain": settings.publisher.domain, - "page": format!("https://{}", settings.publisher.domain), + "page": page_url, }); + } else if config.debug_query_params.is_some() { + // If site already exists, append debug params to existing page URL + if let Some(page_url) = request["site"]["page"].as_str() { + let params = config.debug_query_params.as_ref().unwrap(); + // Only append if params aren't already present + if !page_url.contains(params.as_str()) { + let updated_url = if page_url.contains('?') { + format!("{}&{}", page_url, params) + } else { + format!("{}?{}", page_url, params) + }; + request["site"]["page"] = json!(updated_url); + } + } } if let Some(request_signing_config) = &settings.request_signing { @@ -437,6 +463,16 @@ fn enhance_openrtb_request( } } + if config.debug { + if !request["ext"].is_object() { + request["ext"] = json!({}); + } + if !request["ext"]["prebid"].is_object() { + request["ext"]["prebid"] = json!({}); + } + request["ext"]["prebid"]["debug"] = json!(true); + } + Ok(()) } @@ -590,10 +626,12 @@ impl PrebidAuctionProvider { }) .collect(); - let mut bidder = std::collections::HashMap::new(); - for bidder_name in &self.config.bidders { - bidder.insert(bidder_name.clone(), Json::Object(serde_json::Map::new())); - } + // Use bidder params from the slot (passed through from the request) + let bidder: std::collections::HashMap = slot + .bidders + .iter() + .map(|(name, params)| (name.clone(), params.clone())) + .collect(); Imp { id: slot.id.clone(), @@ -778,6 +816,33 @@ impl AuctionProvider for PrebidAuctionProvider { } } + // Add debug flag if enabled + if self.config.debug { + if !openrtb_json["ext"]["prebid"].is_object() { + openrtb_json["ext"]["prebid"] = json!({}); + } + openrtb_json["ext"]["prebid"]["debug"] = json!(true); + } + + // Append debug query params to page URL if configured + if let Some(ref params) = self.config.debug_query_params { + if !params.is_empty() { + if let Some(page_url) = openrtb_json["site"]["page"].as_str() { + // Only append if params aren't already present + if !page_url.contains(params.as_str()) { + let updated_url = if page_url.contains('?') { + format!("{}&{}", page_url, params) + } else { + format!("{}?{}", page_url, params) + }; + openrtb_json["site"]["page"] = json!(updated_url); + } + } + } + } + + log::info!("Prebid OpenRTB request: {:#?}", openrtb_json); + // Create HTTP request let mut pbs_req = Request::new( Method::POST, @@ -818,6 +883,8 @@ impl AuctionProvider for PrebidAuctionProvider { } let body_bytes = response.take_body_bytes(); + log::info!("Prebid OpenRTB response: {}", String::from_utf8_lossy(&body_bytes)); + let mut response_json: Json = serde_json::from_slice(&body_bytes).change_context(TrustedServerError::Prebid { message: "Failed to parse Prebid response".to_string(), @@ -913,6 +980,7 @@ mod tests { auto_configure: true, debug: false, script_handler: None, + debug_query_params: None, } } @@ -1077,8 +1145,17 @@ mod tests { let mut req = Request::new(Method::POST, "https://edge.example/auction"); req.set_header("Sec-GPC", "1"); - enhance_openrtb_request(&mut request_json, synthetic_id, fresh_id, &settings, &req) - .expect("should enhance request"); + let config = base_config(); + + enhance_openrtb_request( + &mut request_json, + synthetic_id, + fresh_id, + &settings, + &req, + &config, + ) + .expect("should enhance request"); assert_eq!(request_json["user"]["id"], synthetic_id); assert_eq!(request_json["user"]["ext"]["synthetic_fresh"], fresh_id); @@ -1099,6 +1176,66 @@ mod tests { ); } + #[test] + fn enhance_openrtb_request_adds_debug_flag_when_enabled() { + let settings = make_settings(); + let mut request_json = json!({ + "id": "openrtb-request-id" + }); + + let synthetic_id = "synthetic-123"; + let fresh_id = "fresh-456"; + let req = Request::new(Method::POST, "https://edge.example/auction"); + + let mut config = base_config(); + config.debug = true; + + enhance_openrtb_request( + &mut request_json, + synthetic_id, + fresh_id, + &settings, + &req, + &config, + ) + .expect("should enhance request"); + + assert_eq!( + request_json["ext"]["prebid"]["debug"], true, + "debug flag should be set to true when config.debug is enabled" + ); + } + + #[test] + fn enhance_openrtb_request_does_not_add_debug_flag_when_disabled() { + let settings = make_settings(); + let mut request_json = json!({ + "id": "openrtb-request-id" + }); + + let synthetic_id = "synthetic-123"; + let fresh_id = "fresh-456"; + let req = Request::new(Method::POST, "https://edge.example/auction"); + + let mut config = base_config(); + config.debug = false; + + enhance_openrtb_request( + &mut request_json, + synthetic_id, + fresh_id, + &settings, + &req, + &config, + ) + .expect("should enhance request"); + + assert!( + request_json["ext"]["prebid"]["debug"].is_null(), + "debug flag should not be set when config.debug is disabled" + ); + } + #[test] fn transform_prebid_response_rewrites_creatives_and_tracking() { let mut response = json!({ @@ -1227,6 +1364,7 @@ server_url = "https://prebid.example" auto_configure: false, debug: false, script_handler: Some("/prebid.js".to_string()), + debug_query_params: None, }; let integration = PrebidIntegration::new(config); @@ -1261,6 +1399,7 @@ server_url = "https://prebid.example" auto_configure: false, debug: false, script_handler: Some("/prebid.js".to_string()), + debug_query_params: None, }; let integration = PrebidIntegration::new(config); @@ -1285,4 +1424,110 @@ server_url = "https://prebid.example" // Should have 0 routes when no script handler configured assert_eq!(routes.len(), 0); } + + #[test] + fn debug_query_params_appended_to_existing_site_page_in_enhance() { + let settings = make_settings(); + let mut config = base_config(); + config.debug_query_params = Some("kargo_debug=true".to_string()); + + let req = Request::new(Method::GET, "https://example.com/test"); + let synthetic_id = "test-synthetic-id"; + let fresh_id = "test-fresh-id"; + + // Test with existing site.page + let mut request = json!({ + "id": "test-id", + "site": { + "domain": "example.com", + "page": "https://example.com/page" + } + }); + + enhance_openrtb_request( + &mut request, + synthetic_id, + fresh_id, + &settings, + &req, + &config, + ) + .expect("should enhance request"); + + let page = request["site"]["page"].as_str().unwrap(); + assert_eq!(page, "https://example.com/page?kargo_debug=true"); + } + + #[test] + fn debug_query_params_appended_to_url_with_existing_query() { + let settings = make_settings(); + let mut config = base_config(); + config.debug_query_params = Some("kargo_debug=true".to_string()); + + let req = Request::new(Method::GET, "https://example.com/test"); + let synthetic_id = "test-synthetic-id"; + let fresh_id = "test-fresh-id"; + + // Test with existing query params in site.page + let mut request = json!({ + "id": "test-id", + "site": { + "domain": "example.com", + "page": "https://example.com/page?existing=param" + } + }); + + enhance_openrtb_request( + &mut request, + synthetic_id, + fresh_id, + &settings, + &req, + &config, + ) + .expect("should enhance request"); + + let page = request["site"]["page"].as_str().unwrap(); + assert_eq!( + page, + "https://example.com/page?existing=param&kargo_debug=true" + ); + } + + #[test] + fn debug_query_params_not_duplicated() { + // Verify that if params are already in the URL, they aren't added again + let settings = make_settings(); + let mut config = base_config(); + config.debug_query_params = Some("kargo_debug=true".to_string()); + + let req = Request::new(Method::GET, "https://example.com/test"); + let synthetic_id = "test-synthetic-id"; + let fresh_id = "test-fresh-id"; + + // Test with URL that already has the debug params + let mut request = json!({ + "id": "test-id", + "site": { + "domain": "example.com", + "page": "https://example.com/page?kargo_debug=true" + } + }); + + enhance_openrtb_request( + &mut request, + synthetic_id, + fresh_id, + &settings, + &req, + &config, + ) + .expect("should enhance request"); + + let page = request["site"]["page"].as_str().unwrap(); + // Should still only have params once + assert_eq!(page, "https://example.com/page?kargo_debug=true"); + // Verify params appear exactly once + assert_eq!(page.matches("kargo_debug=true").count(), 1); + } } diff --git a/crates/js/lib/src/core/types.ts b/crates/js/lib/src/core/types.ts index 3bf1b49..72365fb 100644 --- a/crates/js/lib/src/core/types.ts +++ b/crates/js/lib/src/core/types.ts @@ -9,9 +9,15 @@ export interface MediaTypes { banner?: Banner; } +export interface Bid { + bidder: string; + params?: Record; +} + export interface AdUnit { code: string; mediaTypes?: MediaTypes; + bids?: Bid[]; } export interface TsjsApi { From a8b5337c2dcbf1ab59d61751c9d59010b0746427 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 26 Jan 2026 11:12:48 -0600 Subject: [PATCH 2/2] clean up - use concrete types for openrtb - clean up duplicate code - removed log statements - adds fallback for empty bidders --- crates/common/src/auction/orchestrator.rs | 2 + .../common/src/integrations/adserver_mock.rs | 2 + crates/common/src/integrations/aps.rs | 2 + crates/common/src/integrations/prebid.rs | 241 ++++++++++-------- crates/common/src/openrtb.rs | 79 ++++++ 5 files changed, 214 insertions(+), 112 deletions(-) diff --git a/crates/common/src/auction/orchestrator.rs b/crates/common/src/auction/orchestrator.rs index 3163a57..09866b4 100644 --- a/crates/common/src/auction/orchestrator.rs +++ b/crates/common/src/auction/orchestrator.rs @@ -478,6 +478,7 @@ mod tests { }], floor_price: Some(1.50), targeting: HashMap::new(), + bidders: HashMap::new(), }, AdSlot { id: "sidebar".to_string(), @@ -488,6 +489,7 @@ mod tests { }], floor_price: Some(1.00), targeting: HashMap::new(), + bidders: HashMap::new(), }, ], publisher: PublisherInfo { diff --git a/crates/common/src/integrations/adserver_mock.rs b/crates/common/src/integrations/adserver_mock.rs index feaeecd..4b0e313 100644 --- a/crates/common/src/integrations/adserver_mock.rs +++ b/crates/common/src/integrations/adserver_mock.rs @@ -396,6 +396,7 @@ mod tests { }], floor_price: Some(1.50), targeting: HashMap::new(), + bidders: HashMap::new(), }], publisher: PublisherInfo { domain: "test.com".to_string(), @@ -568,6 +569,7 @@ mod tests { }], floor_price: None, targeting: HashMap::new(), + bidders: HashMap::new(), }], publisher: PublisherInfo { domain: "test.com".to_string(), diff --git a/crates/common/src/integrations/aps.rs b/crates/common/src/integrations/aps.rs index 6ff2f31..88747a6 100644 --- a/crates/common/src/integrations/aps.rs +++ b/crates/common/src/integrations/aps.rs @@ -580,6 +580,7 @@ mod tests { ], floor_price: Some(1.50), targeting: HashMap::new(), + bidders: HashMap::new(), }, AdSlot { id: "sidebar".to_string(), @@ -590,6 +591,7 @@ mod tests { }], floor_price: Some(1.00), targeting: HashMap::new(), + bidders: HashMap::new(), }, ], publisher: PublisherInfo { diff --git a/crates/common/src/integrations/prebid.rs b/crates/common/src/integrations/prebid.rs index 4a38958..5a52347 100644 --- a/crates/common/src/integrations/prebid.rs +++ b/crates/common/src/integrations/prebid.rs @@ -22,7 +22,10 @@ use crate::integrations::{ AttributeRewriteAction, IntegrationAttributeContext, IntegrationAttributeRewriter, IntegrationEndpoint, IntegrationProxy, IntegrationRegistration, }; -use crate::openrtb::{Banner, Format, Imp, ImpExt, OpenRtbRequest, PrebidImpExt, Site}; +use crate::openrtb::{ + Banner, Device, Format, Geo, Imp, ImpExt, OpenRtbRequest, PrebidExt, PrebidImpExt, Regs, + RegsExt, RequestExt, Site, TrustedServerExt, User, UserExt, +}; use crate::request_signing::RequestSigner; use crate::settings::{IntegrationConfig, Settings}; use crate::synthetic::{generate_synthetic_id, get_or_generate_synthetic_id}; @@ -279,6 +282,10 @@ fn build_openrtb_from_ts( domain: Some(settings.publisher.domain.clone()), page: Some(format!("https://{}", &settings.publisher.domain)), }), + user: None, + device: None, + regs: None, + ext: None, } } @@ -422,24 +429,18 @@ fn enhance_openrtb_request( // Append debug query params if configured if let Some(ref params) = config.debug_query_params { - page_url = format!("{}?{}", page_url, params); + page_url = append_query_params(&page_url, params); } request["site"] = json!({ "domain": settings.publisher.domain, "page": page_url, }); - } else if config.debug_query_params.is_some() { + } else if let Some(ref params) = config.debug_query_params { // If site already exists, append debug params to existing page URL if let Some(page_url) = request["site"]["page"].as_str() { - let params = config.debug_query_params.as_ref().unwrap(); - // Only append if params aren't already present - if !page_url.contains(params.as_str()) { - let updated_url = if page_url.contains('?') { - format!("{}&{}", page_url, params) - } else { - format!("{}?{}", page_url, params) - }; + let updated_url = append_query_params(page_url, params); + if updated_url != page_url { request["site"]["page"] = json!(updated_url); } } @@ -595,6 +596,19 @@ fn get_request_scheme(req: &Request) -> String { "https".to_string() } +/// Appends query parameters to a URL, handling both URLs with and without existing query strings. +/// Returns the original URL unchanged if params are empty or already present. +fn append_query_params(url: &str, params: &str) -> String { + if params.is_empty() || url.contains(params) { + return url.to_string(); + } + if url.contains('?') { + format!("{}&{}", url, params) + } else { + format!("{}?{}", url, params) + } +} + // ============================================================================ // Prebid Auction Provider // ============================================================================ @@ -610,8 +624,13 @@ impl PrebidAuctionProvider { Self { config } } - /// Convert auction request to OpenRTB format. - fn to_openrtb(&self, request: &AuctionRequest) -> OpenRtbRequest { + /// Convert auction request to OpenRTB format with all enrichments. + fn to_openrtb( + &self, + request: &AuctionRequest, + context: &AuctionContext<'_>, + signer: Option<(&RequestSigner, String)>, + ) -> OpenRtbRequest { let imps: Vec = request .slots .iter() @@ -627,12 +646,19 @@ impl PrebidAuctionProvider { .collect(); // Use bidder params from the slot (passed through from the request) - let bidder: std::collections::HashMap = slot + let mut bidder: HashMap = slot .bidders .iter() .map(|(name, params)| (name.clone(), params.clone())) .collect(); + // Fallback to config bidders if none provided + if bidder.is_empty() { + for b in &self.config.bidders { + bidder.insert(b.clone(), Json::Object(serde_json::Map::new())); + } + } + Imp { id: slot.id.clone(), banner: Some(Banner { format: formats }), @@ -643,13 +669,79 @@ impl PrebidAuctionProvider { }) .collect(); + // Build page URL with debug query params if configured + let page_url = request.publisher.page_url.as_ref().map(|url| { + if let Some(ref params) = self.config.debug_query_params { + append_query_params(url, params) + } else { + url.clone() + } + }); + + // Build user object + let user = Some(User { + id: Some(request.user.id.clone()), + ext: Some(UserExt { + synthetic_fresh: Some(request.user.fresh_id.clone()), + }), + }); + + // Build device object with geo if available + let device = request.device.as_ref().and_then(|d| { + d.geo.as_ref().map(|geo| Device { + geo: Some(Geo { + geo_type: 2, // IP address per OpenRTB spec + country: Some(geo.country.clone()), + city: Some(geo.city.clone()), + region: geo.region.clone(), + }), + }) + }); + + // Build regs object if Sec-GPC header is present + let regs = if context.request.get_header("Sec-GPC").is_some() { + Some(Regs { + ext: Some(RegsExt { + us_privacy: Some("1YYN".to_string()), + }), + }) + } else { + None + }; + + // Build ext object + let request_host = get_request_host(context.request); + let request_scheme = get_request_scheme(context.request); + + let (signature, kid) = signer + .map(|(s, sig)| (Some(sig), Some(s.kid.clone()))) + .unwrap_or((None, None)); + + let ext = Some(RequestExt { + prebid: if self.config.debug { + Some(PrebidExt { debug: Some(true) }) + } else { + None + }, + trusted_server: Some(TrustedServerExt { + signature, + kid, + request_host: Some(request_host), + request_scheme: Some(request_scheme), + }), + }); + OpenRtbRequest { id: request.id.clone(), imp: imps, site: Some(Site { domain: Some(request.publisher.domain.clone()), - page: request.publisher.page_url.clone(), + page: page_url, }), + user, + device, + regs, + ext, } } @@ -746,102 +838,28 @@ impl AuctionProvider for PrebidAuctionProvider { ) -> Result> { log::info!("Prebid: requesting bids for {} slots", request.slots.len()); - // Convert to OpenRTB - let openrtb = self.to_openrtb(request); - let mut openrtb_json = - serde_json::to_value(&openrtb).change_context(TrustedServerError::Prebid { - message: "Failed to serialize OpenRTB request".to_string(), - })?; - - // Enhance with user info - if !openrtb_json["user"].is_object() { - openrtb_json["user"] = json!({}); - } - openrtb_json["user"]["id"] = json!(&request.user.id); - if !openrtb_json["user"]["ext"].is_object() { - openrtb_json["user"]["ext"] = json!({}); - } - openrtb_json["user"]["ext"]["synthetic_fresh"] = json!(&request.user.fresh_id); - - // Add device info if available - if let Some(device) = &request.device { - if let Some(geo) = &device.geo { - let geo_obj = json!({ - "type": 2, - "country": geo.country, - "city": geo.city, - "region": geo.region, - }); - if !openrtb_json["device"].is_object() { - openrtb_json["device"] = json!({}); - } - openrtb_json["device"]["geo"] = geo_obj; - } - } - - // Add privacy regulations based on Sec-GPC header - if context.request.get_header("Sec-GPC").is_some() { - if !openrtb_json["regs"].is_object() { - openrtb_json["regs"] = json!({}); - } - if !openrtb_json["regs"]["ext"].is_object() { - openrtb_json["regs"]["ext"] = json!({}); - } - openrtb_json["regs"]["ext"]["us_privacy"] = json!("1YYN"); - } - - if !openrtb_json["ext"].is_object() { - openrtb_json["ext"] = json!({}); - } - if !openrtb_json["ext"]["trusted_server"].is_object() { - openrtb_json["ext"]["trusted_server"] = json!({}); - } - - let request_host = get_request_host(context.request); - let request_scheme = get_request_scheme(context.request); - openrtb_json["ext"]["trusted_server"]["request_host"] = json!(request_host); - openrtb_json["ext"]["trusted_server"]["request_scheme"] = json!(request_scheme); - - // Add request signing if enabled - if let Some(request_signing_config) = &context.settings.request_signing { - if request_signing_config.enabled && openrtb_json["id"].is_string() { - let id = openrtb_json["id"] - .as_str() - .expect("should have string id when is_string checked"); - let signer = RequestSigner::from_config()?; - let signature = signer.sign(id.as_bytes())?; - - openrtb_json["ext"]["trusted_server"]["signature"] = json!(signature); - openrtb_json["ext"]["trusted_server"]["kid"] = json!(signer.kid); - } - } - - // Add debug flag if enabled - if self.config.debug { - if !openrtb_json["ext"]["prebid"].is_object() { - openrtb_json["ext"]["prebid"] = json!({}); - } - openrtb_json["ext"]["prebid"]["debug"] = json!(true); - } - - // Append debug query params to page URL if configured - if let Some(ref params) = self.config.debug_query_params { - if !params.is_empty() { - if let Some(page_url) = openrtb_json["site"]["page"].as_str() { - // Only append if params aren't already present - if !page_url.contains(params.as_str()) { - let updated_url = if page_url.contains('?') { - format!("{}&{}", page_url, params) - } else { - format!("{}?{}", page_url, params) - }; - openrtb_json["site"]["page"] = json!(updated_url); - } + // Create signer and compute signature if request signing is enabled + let signer_with_signature = + if let Some(request_signing_config) = &context.settings.request_signing { + if request_signing_config.enabled { + let signer = RequestSigner::from_config()?; + let signature = signer.sign(request.id.as_bytes())?; + Some((signer, signature)) + } else { + None } - } - } - - log::info!("Prebid OpenRTB request: {:#?}", openrtb_json); + } else { + None + }; + + // Convert to OpenRTB with all enrichments + let openrtb = self.to_openrtb( + request, + context, + signer_with_signature + .as_ref() + .map(|(s, sig)| (s, sig.clone())), + ); // Create HTTP request let mut pbs_req = Request::new( @@ -851,7 +869,7 @@ impl AuctionProvider for PrebidAuctionProvider { copy_request_headers(context.request, &mut pbs_req); pbs_req - .set_body_json(&openrtb_json) + .set_body_json(&openrtb) .change_context(TrustedServerError::Prebid { message: "Failed to set request body".to_string(), })?; @@ -883,7 +901,6 @@ impl AuctionProvider for PrebidAuctionProvider { } let body_bytes = response.take_body_bytes(); - log::info!("Prebid OpenRTB response: {}", String::from_utf8_lossy(&body_bytes)); let mut response_json: Json = serde_json::from_slice(&body_bytes).change_context(TrustedServerError::Prebid { diff --git a/crates/common/src/openrtb.rs b/crates/common/src/openrtb.rs index 6881af8..3d2d7b8 100644 --- a/crates/common/src/openrtb.rs +++ b/crates/common/src/openrtb.rs @@ -12,6 +12,14 @@ pub struct OpenRtbRequest { pub imp: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub site: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub device: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub regs: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub ext: Option, } #[derive(Debug, Serialize)] @@ -42,6 +50,77 @@ pub struct Site { pub page: Option, } +#[derive(Debug, Serialize, Default)] +pub struct User { + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub ext: Option, +} + +#[derive(Debug, Serialize, Default)] +pub struct UserExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub synthetic_fresh: Option, +} + +#[derive(Debug, Serialize, Default)] +pub struct Device { + #[serde(skip_serializing_if = "Option::is_none")] + pub geo: Option, +} + +#[derive(Debug, Serialize)] +pub struct Geo { + /// Location type per OpenRTB spec (1=GPS, 2=IP address, 3=user provided) + #[serde(rename = "type")] + pub geo_type: u8, + #[serde(skip_serializing_if = "Option::is_none")] + pub country: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub city: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub region: Option, +} + +#[derive(Debug, Serialize, Default)] +pub struct Regs { + #[serde(skip_serializing_if = "Option::is_none")] + pub ext: Option, +} + +#[derive(Debug, Serialize, Default)] +pub struct RegsExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub us_privacy: Option, +} + +#[derive(Debug, Serialize, Default)] +pub struct RequestExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub prebid: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub trusted_server: Option, +} + +#[derive(Debug, Serialize, Default)] +pub struct PrebidExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub debug: Option, +} + +#[derive(Debug, Serialize, Default)] +pub struct TrustedServerExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub signature: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub kid: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub request_host: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub request_scheme: Option, +} + #[derive(Debug, Serialize)] pub struct ImpExt { pub prebid: PrebidImpExt,