From 2d95c8b81a58b33e98b07690543d9ff64325f3cc Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 3 Jul 2025 10:53:12 -0700 Subject: [PATCH 1/6] Added publisher config --- Cargo.lock | 1 + crates/common/Cargo.toml | 1 + crates/common/src/cookies.rs | 18 ++-- crates/common/src/gdpr.rs | 44 ++++------ crates/common/src/lib.rs | 1 + crates/common/src/prebid.rs | 42 ++++----- crates/common/src/settings.rs | 141 +++++++++++------------------- crates/common/src/synthetic.rs | 34 ++----- crates/common/src/test_support.rs | 49 +++++++++++ crates/fastly/src/main.rs | 5 +- trusted-server.toml | 5 ++ 11 files changed, 167 insertions(+), 174 deletions(-) create mode 100644 crates/common/src/test_support.rs diff --git a/Cargo.lock b/Cargo.lock index 90acdec9..14603d02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1543,6 +1543,7 @@ dependencies = [ "http", "log", "log-fastly", + "regex", "serde", "serde_json", "sha2 0.10.9", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 3b52f744..6f24dac6 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -27,4 +27,5 @@ tokio = { version = "1.46", features = ["sync", "macros", "io-util", "rt", "time url = "2.4.1" [dev-dependencies] +regex = "1.1.1" temp-env = "0.3.6" diff --git a/crates/common/src/cookies.rs b/crates/common/src/cookies.rs index 3ec61fa0..b4494d87 100644 --- a/crates/common/src/cookies.rs +++ b/crates/common/src/cookies.rs @@ -2,6 +2,8 @@ use cookie::{Cookie, CookieJar}; use fastly::http::header; use fastly::Request; +use crate::settings::Settings; + const COOKIE_MAX_AGE: i32 = 365 * 24 * 60 * 60; // 1 year // return empty cookie jar for unparsable cookies @@ -31,15 +33,17 @@ pub fn handle_request_cookies(req: &Request) -> Option { } } -pub fn create_synthetic_cookie(synthetic_id: &str) -> String { +pub fn create_synthetic_cookie(settings: &Settings, synthetic_id: &str) -> String { format!( - "synthetic_id={}; Domain=.auburndao.com; Path=/; Secure; SameSite=Lax; Max-Age={}", - synthetic_id, COOKIE_MAX_AGE, + "synthetic_id={}; Domain={}; Path=/; Secure; SameSite=Lax; Max-Age={}", + synthetic_id, settings.publisher.cookie_domain, COOKIE_MAX_AGE, ) } #[cfg(test)] mod tests { + use crate::test_support::tests::create_test_settings; + use super::*; #[test] @@ -113,10 +117,14 @@ mod tests { #[test] fn test_create_synthetic_cookie() { - let result = create_synthetic_cookie("12345"); + let settings = create_test_settings(); + let result = create_synthetic_cookie(&settings, "12345"); assert_eq!( result, - "synthetic_id=12345; Domain=.auburndao.com; Path=/; Secure; SameSite=Lax; Max-Age=31536000" + format!( + "synthetic_id=12345; Domain={}; Path=/; Secure; SameSite=Lax; Max-Age={}", + settings.publisher.cookie_domain, COOKIE_MAX_AGE, + ) ); } } diff --git a/crates/common/src/gdpr.rs b/crates/common/src/gdpr.rs index 86e53fcb..13ab4c60 100644 --- a/crates/common/src/gdpr.rs +++ b/crates/common/src/gdpr.rs @@ -58,14 +58,15 @@ pub fn get_consent_from_request(req: &Request) -> Option { None } -pub fn create_consent_cookie(consent: &GdprConsent) -> String { +pub fn create_consent_cookie(settings: &Settings, consent: &GdprConsent) -> String { format!( - "gdpr_consent={}; Domain=.auburndao.com; Path=/; Secure; SameSite=Lax; Max-Age=31536000", - serde_json::to_string(consent).unwrap_or_default() + "gdpr_consent={}; Domain={}; Path=/; Secure; SameSite=Lax; Max-Age=31536000", + serde_json::to_string(consent).unwrap_or_default(), + settings.publisher.cookie_domain, ) } -pub fn handle_consent_request(_settings: &Settings, req: Request) -> Result { +pub fn handle_consent_request(settings: &Settings, req: Request) -> Result { match *req.get_method() { Method::GET => { // Return current consent status @@ -81,7 +82,10 @@ pub fn handle_consent_request(_settings: &Settings, req: Request) -> Result { @@ -132,23 +136,7 @@ mod tests { use super::*; use fastly::{Body, Request}; - fn create_test_settings() -> Settings { - Settings { - ad_server: crate::settings::AdServer { - ad_partner_url: "https://test.com".to_string(), - sync_url: "https://sync.test.com".to_string(), - }, - prebid: crate::settings::Prebid { - server_url: "https://prebid.test.com".to_string(), - }, - synthetic: crate::settings::Synthetic { - counter_store: "test-counter".to_string(), - opid_store: "test-opid".to_string(), - secret_key: "test-secret".to_string(), - template: "{{test}}".to_string(), - }, - } - } + use crate::test_support::tests::create_test_settings; #[test] fn test_gdpr_consent_default() { @@ -196,6 +184,7 @@ mod tests { #[test] fn test_create_consent_cookie() { + let settings = create_test_settings(); let consent = GdprConsent { analytics: true, advertising: true, @@ -204,9 +193,9 @@ mod tests { version: "1.0".to_string(), }; - let cookie = create_consent_cookie(&consent); + let cookie = create_consent_cookie(&settings, &consent); assert!(cookie.starts_with("gdpr_consent=")); - assert!(cookie.contains("Domain=.auburndao.com")); + assert!(cookie.contains(format!("Domain={}", settings.publisher.cookie_domain).as_str())); assert!(cookie.contains("Path=/")); assert!(cookie.contains("Secure")); assert!(cookie.contains("SameSite=Lax")); @@ -297,7 +286,12 @@ mod tests { let set_cookie = response.get_header_str(header::SET_COOKIE); assert!(set_cookie.is_some()); assert!(set_cookie.unwrap().contains("gdpr_consent=")); - assert!(set_cookie.unwrap().contains("Domain=.auburndao.com")); + + println!("SFDSFD {:?}", set_cookie); + + assert!(set_cookie + .unwrap() + .contains(format!("Domain={}", settings.publisher.cookie_domain).as_str())); // Check response body let body = response.into_body_str(); diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 7dff5724..3fde24ad 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -7,4 +7,5 @@ pub mod privacy; pub mod settings; pub mod synthetic; pub mod templates; +pub mod test_support; pub mod why; diff --git a/crates/common/src/prebid.rs b/crates/common/src/prebid.rs index d418f2a4..d94a60cc 100644 --- a/crates/common/src/prebid.rs +++ b/crates/common/src/prebid.rs @@ -64,7 +64,7 @@ impl PrebidRequest { .and_then(|o| url::Url::parse(o).ok()) .and_then(|u| u.host_str().map(|h| h.to_string())) }) - .unwrap_or_else(|| "auburndao.com".to_string()); + .unwrap_or_else(|| settings.publisher.domain.clone()); log::info!("Detected domain: {}", domain); @@ -192,23 +192,7 @@ mod tests { use super::*; use fastly::Request; - fn create_test_settings() -> Settings { - Settings { - ad_server: crate::settings::AdServer { - ad_partner_url: "https://test.com".to_string(), - sync_url: "https://sync.test.com".to_string(), - }, - prebid: crate::settings::Prebid { - server_url: "https://prebid.test.com/openrtb2/auction".to_string(), - }, - synthetic: crate::settings::Synthetic { - counter_store: "test-counter".to_string(), - opid_store: "test-opid".to_string(), - secret_key: "test-secret-key".to_string(), - template: "{{client_ip}}:{{user_agent}}:{{first_party_id}}:{{auth_user_id}}:{{publisher_domain}}:{{accept_language}}".to_string(), - }, - } - } + use crate::test_support::tests::create_test_settings; #[test] fn test_prebid_request_new_with_full_headers() { @@ -258,31 +242,34 @@ mod tests { #[test] fn test_prebid_request_domain_fallback() { let settings = create_test_settings(); - let req = Request::get("https://example.com/test"); + let url = format!("https://{}", settings.publisher.domain); + let req = Request::get(url.clone()); // No referer or origin headers let prebid_req = PrebidRequest::new(&settings, &req).unwrap(); - assert_eq!(prebid_req.domain, "auburndao.com"); - assert_eq!(prebid_req.origin, "https://auburndao.com"); + assert_eq!(prebid_req.domain, settings.publisher.domain); + assert_eq!(prebid_req.origin, url); } #[test] fn test_prebid_request_invalid_url_in_referer() { let settings = create_test_settings(); - let mut req = Request::get("https://example.com/test"); + let url = format!("https://{}/test", settings.publisher.domain); + let mut req = Request::get(url); req.set_header(header::REFERER, "not-a-valid-url"); let prebid_req = PrebidRequest::new(&settings, &req).unwrap(); // Should fallback to default domain - assert_eq!(prebid_req.domain, "auburndao.com"); + assert_eq!(prebid_req.domain, settings.publisher.domain); } #[test] fn test_prebid_request_x_forwarded_for_parsing() { let settings = create_test_settings(); - let mut req = Request::get("https://example.com/test"); + let url = format!("https://{}/test", settings.publisher.domain); + let mut req = Request::get(url); req.set_header(HEADER_X_FORWARDED_FOR, "192.168.1.1, 10.0.0.1, 172.16.0.1"); let prebid_req = PrebidRequest::new(&settings, &req).unwrap(); @@ -330,18 +317,19 @@ mod tests { #[test] fn test_prebid_request_edge_cases() { let settings = create_test_settings(); + let url = format!("https://{}/test", settings.publisher.domain); // Test with empty X-Forwarded-For - let mut req = Request::get("https://example.com/test"); + let mut req = Request::get(url.clone()); req.set_header(HEADER_X_FORWARDED_FOR, ""); let prebid_req = PrebidRequest::new(&settings, &req).unwrap(); assert!(!prebid_req.client_ip.is_empty() || prebid_req.client_ip.is_empty()); // Test with malformed origin - let mut req2 = Request::get("https://example.com/test"); + let mut req2 = Request::get(url.clone()); req2.set_header(header::ORIGIN, "://invalid"); let prebid_req2 = PrebidRequest::new(&settings, &req2).unwrap(); - assert_eq!(prebid_req2.domain, "auburndao.com"); + assert_eq!(prebid_req2.domain, settings.publisher.domain); } // Note: Testing send_bid_request would require mocking the Fastly backend, diff --git a/crates/common/src/settings.rs b/crates/common/src/settings.rs index ae294a7d..9bccb8b8 100644 --- a/crates/common/src/settings.rs +++ b/crates/common/src/settings.rs @@ -10,6 +10,14 @@ pub struct AdServer { pub sync_url: String, } +#[derive(Debug, Deserialize)] +#[allow(unused)] +pub struct Publisher { + pub domain: String, + pub cookie_domain: String, + pub origin_url: String, +} + #[derive(Debug, Deserialize)] #[allow(unused)] pub struct Prebid { @@ -29,6 +37,7 @@ pub struct Synthetic { #[allow(unused)] pub struct Settings { pub ad_server: AdServer, + pub publisher: Publisher, pub prebid: Prebid, pub synthetic: Synthetic, } @@ -60,6 +69,9 @@ impl Settings { #[cfg(test)] mod tests { use super::*; + use regex::Regex; + + use crate::test_support::tests::crate_test_settings_str; #[test] fn test_settings_new() { @@ -71,6 +83,9 @@ mod tests { // Verify basic structure is loaded assert!(!settings.ad_server.ad_partner_url.is_empty()); assert!(!settings.ad_server.sync_url.is_empty()); + assert!(!settings.publisher.domain.is_empty()); + assert!(!settings.publisher.cookie_domain.is_empty()); + assert!(!settings.publisher.origin_url.is_empty()); assert!(!settings.prebid.server_url.is_empty()); assert!(!settings.synthetic.counter_store.is_empty()); assert!(!settings.synthetic.opid_store.is_empty()); @@ -80,58 +95,43 @@ mod tests { #[test] fn test_settings_from_valid_toml() { - let toml_str = r#" - [ad_server] - ad_partner_url = "https://example-ad.com/serve" - sync_url = "https://example-ad.com/sync" - - [prebid] - server_url = "https://prebid.example.com/openrtb2/auction" + let toml_str = crate_test_settings_str(); + let settings: Result = Settings::from_toml(&toml_str); - [synthetic] - counter_store = "test-counter-store" - opid_store = "test-opid-store" - secret_key = "test-secret-key-1234567890" - template = "{{client_ip}}:{{user_agent}}:{{first_party_id}}:{{auth_user_id}}:{{publisher_domain}}:{{accept_language}}" - "#; - - let settings = Settings::from_toml(toml_str); assert!(settings.is_ok()); let settings = settings.unwrap(); assert_eq!( settings.ad_server.ad_partner_url, - "https://example-ad.com/serve" + "https://test-adpartner.com" + ); + assert_eq!( + settings.ad_server.sync_url, + "https://test-adpartner.com/synthetic_id={{synthetic_id}}" ); - assert_eq!(settings.ad_server.sync_url, "https://example-ad.com/sync"); assert_eq!( settings.prebid.server_url, - "https://prebid.example.com/openrtb2/auction" + "https://test-prebid.com/openrtb2/auction" + ); + assert_eq!(settings.publisher.domain, "test-publisher.com"); + assert_eq!(settings.publisher.cookie_domain, ".test-publisher.com"); + assert_eq!( + settings.publisher.origin_url, + "https://origin.test-publisher.com" ); assert_eq!(settings.synthetic.counter_store, "test-counter-store"); assert_eq!(settings.synthetic.opid_store, "test-opid-store"); - assert_eq!(settings.synthetic.secret_key, "test-secret-key-1234567890"); + assert_eq!(settings.synthetic.secret_key, "test-secret-key"); assert!(settings.synthetic.template.contains("{{client_ip}}")); } #[test] fn test_settings_missing_required_fields() { - let toml_str = r#" - [ad_server] - ad_partner_url = "https://example-ad.com/serve" - # Missing sync_url - - [prebid] - server_url = "https://prebid.example.com/openrtb2/auction" - - [synthetic] - counter_store = "test-counter-store" - opid_store = "test-opid-store" - secret_key = "test-secret-key" - template = "{{client_ip}}" - "#; + let re = Regex::new(r"ad_partner_url = .*").unwrap(); + let toml_str = crate_test_settings_str(); + let toml_str = re.replace(&toml_str, ""); - let settings = Settings::from_toml(toml_str); + let settings = Settings::from_toml(&toml_str); assert!( settings.is_err(), "Should fail when required fields are missing" @@ -148,71 +148,43 @@ mod tests { #[test] fn test_settings_invalid_toml_syntax() { - let toml_str = r#" - [ad_server - ad_partner_url = "https://example-ad.com/serve" - "#; + let re = Regex::new(r"\]").unwrap(); + let toml_str = crate_test_settings_str(); + let toml_str = re.replace(&toml_str, ""); - let settings = Settings::from_toml(toml_str); + let settings = Settings::from_toml(&toml_str); assert!(settings.is_err(), "Should fail with invalid TOML syntax"); } #[test] fn test_settings_partial_config() { - let toml_str = r#" - [ad_server] - ad_partner_url = "https://example-ad.com/serve" - sync_url = "https://example-ad.com/sync" - "#; + let re = Regex::new(r"\[ad_server\]").unwrap(); + let toml_str = crate_test_settings_str(); + let toml_str = re.replace(&toml_str, ""); - let settings = Settings::from_toml(toml_str); + let settings = Settings::from_toml(&toml_str); assert!(settings.is_err(), "Should fail when sections are missing"); } #[test] fn test_settings_extra_fields() { - let toml_str = r#" - [ad_server] - ad_partner_url = "https://example-ad.com/serve" - sync_url = "https://example-ad.com/sync" - extra_field = "should be ignored" - - [prebid] - server_url = "https://prebid.example.com/openrtb2/auction" - - [synthetic] - counter_store = "test-counter-store" - opid_store = "test-opid-store" - secret_key = "test-secret-key-1234567890" - template = "{{client_ip}}" - "#; + let toml_str = crate_test_settings_str() + "\nhello = 1"; - let settings = Settings::from_toml(toml_str); + let settings = Settings::from_toml(&toml_str); assert!(settings.is_ok(), "Extra fields should be ignored"); } #[test] fn test_set_env() { - let toml_str = r#" - [ad_server] - # ad_partner_url will be set by env variable - sync_url = "https://example-ad.com/sync" - - [prebid] - server_url = "https://prebid.example.com/openrtb2/auction" - - [synthetic] - counter_store = "test-counter-stor e" - opid_store = "test-opid-store" - secret_key = "test-secret-key-1234567890" - template = "{{client_ip}}" - "#; + let re = Regex::new(r"ad_partner_url = .*").unwrap(); + let toml_str = crate_test_settings_str(); + let toml_str = re.replace(&toml_str, ""); temp_env::with_var( "TRUSTED_SERVER__AD_SERVER__AD_PARTNER_URL", Some("https://change-ad.com/serve"), || { - let settings = Settings::from_toml(toml_str); + let settings = Settings::from_toml(&toml_str); assert!(settings.is_ok(), "Settings should load from embedded TOML"); assert_eq!( @@ -225,26 +197,13 @@ mod tests { #[test] fn test_override_env() { - let toml_str = r#" - [ad_server] - ad_partner_url = "https://example-ad.com/serve" - sync_url = "https://example-ad.com/sync" - - [prebid] - server_url = "https://prebid.example.com/openrtb2/auction" - - [synthetic] - counter_store = "test-counter-stor e" - opid_store = "test-opid-store" - secret_key = "test-secret-key-1234567890" - template = "{{client_ip}}" - "#; + let toml_str = crate_test_settings_str(); temp_env::with_var( "TRUSTED_SERVER__AD_SERVER__AD_PARTNER_URL", Some("https://change-ad.com/serve"), || { - let settings = Settings::from_toml(toml_str); + let settings = Settings::from_toml(&toml_str); assert!(settings.is_ok(), "Settings should load from embedded TOML"); assert_eq!( diff --git a/crates/common/src/synthetic.rs b/crates/common/src/synthetic.rs index 59fbce90..5d0f79fe 100644 --- a/crates/common/src/synthetic.rs +++ b/crates/common/src/synthetic.rs @@ -96,9 +96,11 @@ pub fn get_or_generate_synthetic_id(settings: &Settings, req: &Request) -> Strin #[cfg(test)] mod tests { use super::*; - use crate::constants::HEADER_X_PUB_USER_ID; use fastly::http::{HeaderName, HeaderValue}; + use crate::constants::HEADER_X_PUB_USER_ID; + use crate::test_support::tests::create_test_settings; + fn create_test_request(headers: Vec<(HeaderName, &str)>) -> Request { let mut req = Request::new("GET", "http://example.com"); for (key, value) in headers { @@ -108,32 +110,14 @@ mod tests { req } - fn create_settings() -> Settings { - Settings { - ad_server: crate::settings::AdServer { - ad_partner_url: "https://example.com".to_string(), - sync_url: "https://example.com/synthetic_id={{synthetic_id}}".to_string(), - }, - prebid: crate::settings::Prebid { - server_url: "https://example.com".to_string(), - }, - synthetic: crate::settings::Synthetic { - counter_store: "https://example.com".to_string(), - opid_store: "https://example.com".to_string(), - secret_key: "secret_key".to_string(), - template: "{{ client_ip }}:{{ user_agent }}:{{ first_party_id }}:{{ auth_user_id }}:{{ publisher_domain }}:{{ accept_language }}".to_string(), - }, - } - } - #[test] fn test_generate_synthetic_id() { - let settings: Settings = create_settings(); + let settings: Settings = create_test_settings(); let req = create_test_request(vec![ (header::USER_AGENT, "Mozilla/5.0"), (header::COOKIE, "pub_userid=12345"), (HEADER_X_PUB_USER_ID, "67890"), - (header::HOST, "example.com"), + (header::HOST, settings.publisher.domain.as_str()), (header::ACCEPT_LANGUAGE, "en-US,en;q=0.9"), ]); @@ -141,13 +125,13 @@ mod tests { log::info!("Generated synthetic ID: {}", synthetic_id); assert_eq!( synthetic_id, - "07cd73bb8c7db39753ab6b10198b10c3237a3f5a6d2232c6ce578f2c2a623e56" + "a1748067b3908f2c9e0f6ea30a341328ba4b84de45448b13d1007030df14a98e" ) } #[test] fn test_get_or_generate_synthetic_id_with_header() { - let settings = create_settings(); + let settings = create_test_settings(); let req = create_test_request(vec![( HEADER_SYNTHETIC_TRUSTED_SERVER, "existing_synthetic_id", @@ -159,7 +143,7 @@ mod tests { #[test] fn test_get_or_generate_synthetic_id_with_cookie() { - let settings = create_settings(); + let settings = create_test_settings(); let req = create_test_request(vec![(header::COOKIE, "synthetic_id=existing_cookie_id")]); let synthetic_id = get_or_generate_synthetic_id(&settings, &req); @@ -168,7 +152,7 @@ mod tests { #[test] fn test_get_or_generate_synthetic_id_generate_new() { - let settings = create_settings(); + let settings = create_test_settings(); let req = create_test_request(vec![]); let synthetic_id = get_or_generate_synthetic_id(&settings, &req); diff --git a/crates/common/src/test_support.rs b/crates/common/src/test_support.rs new file mode 100644 index 00000000..4dfaa4e9 --- /dev/null +++ b/crates/common/src/test_support.rs @@ -0,0 +1,49 @@ +#[cfg(test)] +pub mod tests { + use crate::settings::{AdServer, Prebid, Publisher, Settings, Synthetic}; + + pub fn crate_test_settings_str() -> String { + return r#" + [ad_server] + ad_partner_url = "https://test-adpartner.com" + sync_url = "https://test-adpartner.com/synthetic_id={{synthetic_id}}" + + [publisher] + domain = "test-publisher.com" + cookie_domain = ".test-publisher.com" + origin_url= "https://origin.test-publisher.com" + + [prebid] + server_url = "https://test-prebid.com/openrtb2/auction" + + [synthetic] + counter_store = "test-counter-store" + opid_store = "test-opid-store" + secret_key = "test-secret-key" + template = "{{client_ip}}:{{user_agent}}:{{first_party_id}}:{{auth_user_id}}:{{publisher_domain}}:{{accept_language}}" + "#.to_string(); + } + + pub fn create_test_settings() -> Settings { + Settings { + ad_server: AdServer { + ad_partner_url: "https://test-adpartner.com".into(), + sync_url: "https://test-adpartner.com/synthetic_id={{synthetic_id}}".to_string(), + }, + publisher: Publisher { + domain: "test-publisher.com".to_string(), + cookie_domain: ".test-publisher.com".to_string(), + origin_url: "origin.test-publisher.com".to_string(), + }, + prebid: Prebid { + server_url: "https://test-prebid.com/openrtb2/auction".to_string(), + }, + synthetic: Synthetic { + counter_store: "test_counter_store".to_string(), + opid_store: "test-opid-store".to_string(), + secret_key: "test-secret-key".to_string(), + template: "{{client_ip}}:{{user_agent}}:{{first_party_id}}:{{auth_user_id}}:{{publisher_domain}}:{{accept_language}}".to_string(), + }, + } + } +} diff --git a/crates/fastly/src/main.rs b/crates/fastly/src/main.rs index 3e69c4b1..5ea5267e 100644 --- a/crates/fastly/src/main.rs +++ b/crates/fastly/src/main.rs @@ -182,7 +182,10 @@ fn handle_main_page(settings: &Settings, mut req: Request) -> Result Date: Thu, 3 Jul 2025 10:56:01 -0700 Subject: [PATCH 2/6] Fixed publisher origin --- trusted-server.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trusted-server.toml b/trusted-server.toml index fa93a42d..587607ea 100644 --- a/trusted-server.toml +++ b/trusted-server.toml @@ -1,7 +1,7 @@ [publisher] domain = "test-publisher.com" cookie_domain = ".test-publisher.com" -origin_url = "http://127.0.0.1" +origin_url = "https://origin.test-publisher.com" [ad_server] ad_partner_url = "equativ_ad_api_2" From 66ec01ddf7269ead4dc8ee1282e4cdc1206e7dae Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 3 Jul 2025 10:58:48 -0700 Subject: [PATCH 3/6] Fixed clippy error --- crates/common/src/test_support.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/common/src/test_support.rs b/crates/common/src/test_support.rs index 4dfaa4e9..67f7262a 100644 --- a/crates/common/src/test_support.rs +++ b/crates/common/src/test_support.rs @@ -3,7 +3,7 @@ pub mod tests { use crate::settings::{AdServer, Prebid, Publisher, Settings, Synthetic}; pub fn crate_test_settings_str() -> String { - return r#" + r#" [ad_server] ad_partner_url = "https://test-adpartner.com" sync_url = "https://test-adpartner.com/synthetic_id={{synthetic_id}}" @@ -21,7 +21,7 @@ pub mod tests { opid_store = "test-opid-store" secret_key = "test-secret-key" template = "{{client_ip}}:{{user_agent}}:{{first_party_id}}:{{auth_user_id}}:{{publisher_domain}}:{{accept_language}}" - "#.to_string(); + "#.to_string() } pub fn create_test_settings() -> Settings { From 9de8af682d208849d15f683e862569436aa22940 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 3 Jul 2025 21:32:29 -0700 Subject: [PATCH 4/6] Removed println --- crates/common/src/gdpr.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/common/src/gdpr.rs b/crates/common/src/gdpr.rs index 13ab4c60..48b3c515 100644 --- a/crates/common/src/gdpr.rs +++ b/crates/common/src/gdpr.rs @@ -287,8 +287,6 @@ mod tests { assert!(set_cookie.is_some()); assert!(set_cookie.unwrap().contains("gdpr_consent=")); - println!("SFDSFD {:?}", set_cookie); - assert!(set_cookie .unwrap() .contains(format!("Domain={}", settings.publisher.cookie_domain).as_str())); From 4883a7abbd157819496fb7113cc1834364c3436f Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 3 Jul 2025 22:24:54 -0700 Subject: [PATCH 5/6] Detect changes to environment variable --- crates/common/Cargo.toml | 5 +++ crates/common/build.rs | 59 +++++++++++++++++++++++++++++++++++ crates/common/src/settings.rs | 39 ++++++++++++++--------- 3 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 crates/common/build.rs diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 6f24dac6..01eee869 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -26,6 +26,11 @@ sha2 = "0.10.9" tokio = { version = "1.46", features = ["sync", "macros", "io-util", "rt", "time"] } url = "2.4.1" +[build-dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.91" +config = "0.15.11" + [dev-dependencies] regex = "1.1.1" temp-env = "0.3.6" diff --git a/crates/common/build.rs b/crates/common/build.rs new file mode 100644 index 00000000..25930227 --- /dev/null +++ b/crates/common/build.rs @@ -0,0 +1,59 @@ +#[path = "src/settings.rs"] +mod settings; + +use serde_json::Value; +use std::collections::HashSet; + +fn main() { + // Watch the settings.rs file for changes + println!("cargo:rerun-if-changed=../../trusted-server.toml"); + + // Create a default Settings instance and convert to JSON to discover all fields + let default_settings = settings::Settings::default(); + let settings_json = serde_json::to_value(&default_settings).unwrap(); + + let mut env_vars = HashSet::new(); + collect_env_vars( + &settings_json, + settings::ENVIRONMENT_VARIABLE_PREFIX, + settings::ENVIRONMENT_VARIABLE_SEPARATOR, + &mut env_vars, + vec![], + ); + + // Print rerun-if-env-changed for each variable + let mut sorted_vars: Vec<_> = env_vars.into_iter().collect(); + sorted_vars.sort(); + + for var in sorted_vars { + println!("cargo:rerun-if-env-changed={}", var); + } +} + +fn collect_env_vars( + value: &Value, + prefix: &str, + sep: &str, + env_vars: &mut HashSet, + path: Vec, +) { + if let Value::Object(map) = value { + for (key, val) in map { + let mut new_path = path.clone(); + new_path.push(key.to_uppercase()); + + match val { + Value::String(_) | Value::Number(_) | Value::Bool(_) => { + // Leaf node - create environment variable + let env_var = format!("{}{}{}", prefix, sep, new_path.join(sep)); + env_vars.insert(env_var); + } + Value::Object(_) => { + // Recurse into nested objects + collect_env_vars(val, prefix, sep, env_vars, new_path); + } + _ => {} + } + } + } +} diff --git a/crates/common/src/settings.rs b/crates/common/src/settings.rs index 9bccb8b8..8bf69ba9 100644 --- a/crates/common/src/settings.rs +++ b/crates/common/src/settings.rs @@ -1,31 +1,30 @@ use std::str; use config::{Config, ConfigError, Environment, File, FileFormat}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Deserialize)] -#[allow(unused)] +pub const ENVIRONMENT_VARIABLE_PREFIX: &str = "TRUSTED_SERVER"; +pub const ENVIRONMENT_VARIABLE_SEPARATOR: &str = "__"; + +#[derive(Debug, Default, Deserialize, Serialize)] pub struct AdServer { pub ad_partner_url: String, pub sync_url: String, } -#[derive(Debug, Deserialize)] -#[allow(unused)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct Publisher { pub domain: String, pub cookie_domain: String, pub origin_url: String, } -#[derive(Debug, Deserialize)] -#[allow(unused)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct Prebid { pub server_url: String, } -#[derive(Debug, Deserialize)] -#[allow(unused)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct Synthetic { pub counter_store: String, pub opid_store: String, @@ -33,8 +32,7 @@ pub struct Synthetic { pub template: String, } -#[derive(Debug, Deserialize)] -#[allow(unused)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct Settings { pub ad_server: AdServer, pub publisher: Publisher, @@ -42,6 +40,7 @@ pub struct Settings { pub synthetic: Synthetic, } +#[allow(unused)] impl Settings { pub fn new() -> Result { let toml_bytes = include_bytes!("../../../trusted-server.toml"); @@ -52,8 +51,8 @@ impl Settings { pub fn from_toml(toml_str: &str) -> Result { let environment = Environment::default() - .prefix("TRUSTED_SERVER") - .separator("__"); + .prefix(ENVIRONMENT_VARIABLE_PREFIX) + .separator(ENVIRONMENT_VARIABLE_SEPARATOR); let toml = File::from_str(toml_str, FileFormat::Toml); let config = Config::builder() @@ -181,7 +180,12 @@ mod tests { let toml_str = re.replace(&toml_str, ""); temp_env::with_var( - "TRUSTED_SERVER__AD_SERVER__AD_PARTNER_URL", + format!( + "{}{}AD_SERVER{}AD_PARTNER_URL", + ENVIRONMENT_VARIABLE_PREFIX, + ENVIRONMENT_VARIABLE_SEPARATOR, + ENVIRONMENT_VARIABLE_SEPARATOR + ), Some("https://change-ad.com/serve"), || { let settings = Settings::from_toml(&toml_str); @@ -200,7 +204,12 @@ mod tests { let toml_str = crate_test_settings_str(); temp_env::with_var( - "TRUSTED_SERVER__AD_SERVER__AD_PARTNER_URL", + format!( + "{}{}AD_SERVER{}AD_PARTNER_URL", + ENVIRONMENT_VARIABLE_PREFIX, + ENVIRONMENT_VARIABLE_SEPARATOR, + ENVIRONMENT_VARIABLE_SEPARATOR + ), Some("https://change-ad.com/serve"), || { let settings = Settings::from_toml(&toml_str); From 8a260bf1eac3aec9707c270a81ab5d1ecdc69f81 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 3 Jul 2025 22:36:21 -0700 Subject: [PATCH 6/6] Small refactor --- crates/common/build.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/crates/common/build.rs b/crates/common/build.rs index 25930227..f25ec0db 100644 --- a/crates/common/build.rs +++ b/crates/common/build.rs @@ -13,13 +13,7 @@ fn main() { let settings_json = serde_json::to_value(&default_settings).unwrap(); let mut env_vars = HashSet::new(); - collect_env_vars( - &settings_json, - settings::ENVIRONMENT_VARIABLE_PREFIX, - settings::ENVIRONMENT_VARIABLE_SEPARATOR, - &mut env_vars, - vec![], - ); + collect_env_vars(&settings_json, &mut env_vars, vec![]); // Print rerun-if-env-changed for each variable let mut sorted_vars: Vec<_> = env_vars.into_iter().collect(); @@ -30,13 +24,7 @@ fn main() { } } -fn collect_env_vars( - value: &Value, - prefix: &str, - sep: &str, - env_vars: &mut HashSet, - path: Vec, -) { +fn collect_env_vars(value: &Value, env_vars: &mut HashSet, path: Vec) { if let Value::Object(map) = value { for (key, val) in map { let mut new_path = path.clone(); @@ -45,12 +33,17 @@ fn collect_env_vars( match val { Value::String(_) | Value::Number(_) | Value::Bool(_) => { // Leaf node - create environment variable - let env_var = format!("{}{}{}", prefix, sep, new_path.join(sep)); + let env_var = format!( + "{}{}{}", + settings::ENVIRONMENT_VARIABLE_PREFIX, + settings::ENVIRONMENT_VARIABLE_SEPARATOR, + new_path.join(settings::ENVIRONMENT_VARIABLE_SEPARATOR) + ); env_vars.insert(env_var); } Value::Object(_) => { // Recurse into nested objects - collect_env_vars(val, prefix, sep, env_vars, new_path); + collect_env_vars(val, env_vars, new_path); } _ => {} }