Skip to content

Commit 5ce339a

Browse files
committed
Add roundtripping of cap_key in sposored content impression callback urls
1 parent f1137e8 commit 5ce339a

2 files changed

Lines changed: 138 additions & 2 deletions

File tree

components/ads-client/src/client.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ where
121121

122122
pub fn record_impression(
123123
&self,
124-
impression_url: Url,
124+
mut impression_url: Url,
125125
ohttp: bool,
126126
) -> Result<(), RecordImpressionError> {
127127
// TODO: Re-enable cache invalidation behind a Nimbus experiment.
@@ -130,6 +130,26 @@ where
130130
// if let Some(request_hash) = pop_request_hash_from_url(&mut impression_url) {
131131
// let _ = self.client.invalidate_cache_by_hash(&request_hash);
132132
// }
133+
134+
// TODO: Add count call with _cap_key for impression capping logic
135+
if let Some((_, _cap_key)) = impression_url
136+
.query_pairs()
137+
.find(|(key, _)| key == "cap_key")
138+
{
139+
let original_url = impression_url.clone();
140+
impression_url
141+
.query_pairs_mut()
142+
.clear()
143+
.extend_pairs(
144+
original_url
145+
.query_pairs()
146+
.collect::<Vec<_>>()
147+
.iter()
148+
.filter(|(key, _)| key != "cap_key"),
149+
)
150+
.finish();
151+
}
152+
133153
self.client
134154
.record_impression(impression_url, ohttp)
135155
.inspect_err(|e| {
@@ -368,6 +388,33 @@ mod tests {
368388
m.assert();
369389
}
370390

391+
#[test]
392+
fn test_record_impression_removes_cap_key() {
393+
viaduct_dev::init_backend_dev();
394+
let mars_client = MARSClient::new(Environment::Test, None, MozAdsTelemetryWrapper::noop());
395+
let ads_client = new_with_mars_client(mars_client);
396+
397+
let base_url = mockito::server_url();
398+
let path_and_query = "/impression?kept=example";
399+
let callback_url = Url::parse(&format!("{}{}", base_url, path_and_query)).unwrap();
400+
401+
let mock = mockito::mock("GET", path_and_query)
402+
.with_status(200)
403+
.create();
404+
405+
ads_client.record_impression(callback_url, false).unwrap();
406+
407+
mock.assert();
408+
409+
let callback_url_with_cap_key =
410+
Url::parse(&format!("{}{}&cap_key=test", base_url, path_and_query)).unwrap();
411+
ads_client
412+
.record_impression(callback_url_with_cap_key, false)
413+
.unwrap();
414+
415+
mock.expect(2).assert();
416+
}
417+
371418
#[test]
372419
#[ignore = "Cache invalidation temporarily disabled - will be re-enabled behind Nimbus experiment"]
373420
fn test_record_click_invalidates_cache() {

components/ads-client/src/mars/ad_response.rs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl<A: AdResponseValue> AdResponse<A> {
4747
let hash_str = request_hash.to_string();
4848
for (placement_id, ads) in self.data.iter_mut() {
4949
for (position, ad) in ads.iter_mut().enumerate() {
50+
let impression_enriching_params = ad.impression_enriching_params();
5051
let callbacks = ad.callbacks_mut();
5152
callbacks
5253
.click
@@ -55,7 +56,8 @@ impl<A: AdResponseValue> AdResponse<A> {
5556
callbacks
5657
.impression
5758
.query_pairs_mut()
58-
.append_pair("request_hash", &hash_str);
59+
.append_pair("request_hash", &hash_str)
60+
.extend_pairs(impression_enriching_params);
5961
if let Some(report_url) = callbacks.report.as_mut() {
6062
report_url
6163
.query_pairs_mut()
@@ -161,6 +163,9 @@ pub struct AdCallbacks {
161163

162164
pub trait AdResponseValue: DeserializeOwned {
163165
fn callbacks_mut(&mut self) -> &mut AdCallbacks;
166+
fn impression_enriching_params(&self) -> Vec<(String, String)> {
167+
vec![]
168+
}
164169
}
165170

166171
impl AdResponseValue for AdImage {
@@ -173,6 +178,10 @@ impl AdResponseValue for AdSpoc {
173178
fn callbacks_mut(&mut self) -> &mut AdCallbacks {
174179
&mut self.callbacks
175180
}
181+
182+
fn impression_enriching_params(&self) -> Vec<(String, String)> {
183+
vec![("cap_key".to_owned(), self.caps.cap_key.clone())]
184+
}
176185
}
177186

178187
impl AdResponseValue for AdTile {
@@ -514,6 +523,86 @@ mod tests {
514523
assert!(report_1.contains("position=1"));
515524
}
516525

526+
#[test]
527+
fn test_enrich_callbacks_spoc_impressions_have_cap_key() {
528+
let mut response = AdResponse {
529+
data: HashMap::from([(
530+
"tile1".into(),
531+
vec![
532+
AdSpoc {
533+
block_key: "block_key1".into(),
534+
callbacks: AdCallbacks {
535+
click: Url::parse("https://example.com/click1").unwrap(),
536+
impression: Url::parse("https://example.com/impression1").unwrap(),
537+
report: Some(Url::parse("https://example.com/report1").unwrap()),
538+
},
539+
caps: SpocFrequencyCaps {
540+
cap_key: "cap_key1".into(),
541+
day: 100,
542+
},
543+
domain: "1.example.com".into(),
544+
excerpt: "excerpt1".into(),
545+
format: "format1".into(),
546+
image_url: "https://example.com/image1.png".into(),
547+
url: "https://example.com/ad1".into(),
548+
ranking: SpocRanking {
549+
priority: 1,
550+
personalization_models: None,
551+
item_score: 1.0,
552+
},
553+
sponsor: "sponsor1".into(),
554+
sponsored_by_override: None,
555+
title: "title1".into(),
556+
},
557+
AdSpoc {
558+
block_key: "block_key2".into(),
559+
callbacks: AdCallbacks {
560+
click: Url::parse("https://example.com/click2").unwrap(),
561+
impression: Url::parse("https://example.com/impression2").unwrap(),
562+
report: Some(Url::parse("https://example.com/report2").unwrap()),
563+
},
564+
caps: SpocFrequencyCaps {
565+
cap_key: "cap_key2".into(),
566+
day: 200,
567+
},
568+
domain: "2.example.com".into(),
569+
excerpt: "excerpt2".into(),
570+
format: "format2".into(),
571+
image_url: "https://example.com/image2.png".into(),
572+
url: "https://example.com/ad2".into(),
573+
ranking: SpocRanking {
574+
priority: 2,
575+
personalization_models: None,
576+
item_score: 2.0,
577+
},
578+
sponsor: "sponsor2".into(),
579+
sponsored_by_override: None,
580+
title: "title2".into(),
581+
},
582+
],
583+
)]),
584+
};
585+
586+
let request_hash = RequestHash::from("abc123def456");
587+
response.enrich_callbacks(&request_hash);
588+
589+
let ads = &response.data["tile1"];
590+
591+
assert!(ads[0]
592+
.callbacks
593+
.impression
594+
.query()
595+
.unwrap_or("")
596+
.contains("cap_key=cap_key1"));
597+
598+
assert!(ads[1]
599+
.callbacks
600+
.impression
601+
.query()
602+
.unwrap_or("")
603+
.contains("cap_key=cap_key2"));
604+
}
605+
517606
#[test]
518607
fn test_enrich_callbacks_skips_ads_without_report_url() {
519608
let mut response = AdResponse {

0 commit comments

Comments
 (0)