Skip to content

Commit 4dfc45d

Browse files
committed
feat(ads-client): add reason parameter to report_ad per MARS /v1/t spec
The /v1/t tracking endpoint requires a `reason` query parameter for report interactions. Add MozAdsReportReason enum with Inappropriate, NotInterested, and SeenTooManyTimes variants and thread it through the full stack, appending it to the callback URL before the request.
1 parent 2e1e86c commit 4dfc45d

5 files changed

Lines changed: 57 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
### Ads Client
1313
- Added `rotation_days` parameter to `MozAdsClientBuilder` to allow embedders to configure the context ID rotation period. ([#7262](https://github.com/mozilla/application-services/pull/7262))
14+
- Added `reason` parameter to `report_ad` to comply with the MARS `/v1/t` tracking endpoint spec. Accepted values: `inappropriate`, `not_interested`, `seen_too_many_times`.
1415

1516
### Logins
1617
- **BREAKING**: Removed `time_of_last_breach` field from `LoginMeta` and `Login`. This can be derived from Remote Settings during runtime instead.

components/ads-client/src/client.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@ use std::time::Duration;
99
use crate::client::ad_response::{AdImage, AdResponse, AdResponseValue, AdSpoc, AdTile};
1010
use crate::client::config::{AdsClientConfig, Environment};
1111
use crate::error::{RecordClickError, RecordImpressionError, ReportAdError, RequestAdsError};
12+
13+
#[derive(Clone, Copy, Debug, PartialEq)]
14+
pub enum ReportReason {
15+
Inappropriate,
16+
NotInterested,
17+
SeenTooManyTimes,
18+
}
19+
20+
impl ReportReason {
21+
pub fn as_str(&self) -> &'static str {
22+
match self {
23+
ReportReason::Inappropriate => "inappropriate",
24+
ReportReason::NotInterested => "not_interested",
25+
ReportReason::SeenTooManyTimes => "seen_too_many_times",
26+
}
27+
}
28+
}
1229
use crate::http_cache::{HttpCache, RequestCachePolicy};
1330
use crate::mars::MARSClient;
1431
use crate::telemetry::Telemetry;
@@ -199,9 +216,9 @@ where
199216
})
200217
}
201218

202-
pub fn report_ad(&self, report_url: Url) -> Result<(), ReportAdError> {
219+
pub fn report_ad(&self, report_url: Url, reason: ReportReason) -> Result<(), ReportAdError> {
203220
self.client
204-
.report_ad(report_url)
221+
.report_ad(report_url, reason)
205222
.inspect_err(|e| {
206223
self.telemetry.record(e);
207224
})

components/ads-client/src/ffi.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::client::ad_request::{AdContentCategory, AdPlacementRequest, IABConten
1111
use crate::client::ad_response::{
1212
AdCallbacks, AdImage, AdSpoc, AdTile, SpocFrequencyCaps, SpocRanking,
1313
};
14+
use crate::client::ReportReason;
1415
use crate::client::config::{AdsCacheConfig, AdsClientConfig, Environment};
1516
use crate::client::AdsClient;
1617
use crate::error::ComponentError;
@@ -191,6 +192,23 @@ pub struct MozAdsRequestCachePolicy {
191192
pub ttl_seconds: Option<u64>,
192193
}
193194

195+
#[derive(Clone, Copy, Debug, uniffi::Enum)]
196+
pub enum MozAdsReportReason {
197+
Inappropriate,
198+
NotInterested,
199+
SeenTooManyTimes,
200+
}
201+
202+
impl From<MozAdsReportReason> for ReportReason {
203+
fn from(reason: MozAdsReportReason) -> Self {
204+
match reason {
205+
MozAdsReportReason::Inappropriate => ReportReason::Inappropriate,
206+
MozAdsReportReason::NotInterested => ReportReason::NotInterested,
207+
MozAdsReportReason::SeenTooManyTimes => ReportReason::SeenTooManyTimes,
208+
}
209+
}
210+
}
211+
194212
#[derive(Clone, Copy, Debug, Default, uniffi::Enum)]
195213
pub enum MozAdsCacheMode {
196214
#[default]

components/ads-client/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,17 @@ impl MozAdsClient {
111111
}
112112

113113
#[handle_error(ComponentError)]
114-
pub fn report_ad(&self, report_url: String) -> AdsClientApiResult<()> {
114+
pub fn report_ad(
115+
&self,
116+
report_url: String,
117+
reason: MozAdsReportReason,
118+
) -> AdsClientApiResult<()> {
115119
let url = AdsClientUrl::parse(&report_url)
116120
.map_err(|e| ComponentError::ReportAd(CallbackRequestError::InvalidUrl(e).into()))?;
117121
let inner = self.inner.lock();
118-
inner.report_ad(url).map_err(ComponentError::ReportAd)
122+
inner
123+
.report_ad(url, reason.into())
124+
.map_err(ComponentError::ReportAd)
119125
}
120126

121127
pub fn clear_cache(&self) -> AdsClientApiResult<()> {

components/ads-client/src/mars.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::{
77
client::{
88
ad_request::AdRequest,
99
ad_response::{AdResponse, AdResponseValue},
10+
config::Environment,
11+
ReportReason,
1012
},
1113
error::{
1214
check_http_status_for_error, CallbackRequestError, FetchAdsError, RecordClickError,
@@ -88,7 +90,10 @@ where
8890
Ok(())
8991
}
9092

91-
pub fn report_ad(&self, callback: Url) -> Result<(), ReportAdError> {
93+
pub fn report_ad(&self, mut callback: Url, reason: ReportReason) -> Result<(), ReportAdError> {
94+
callback
95+
.query_pairs_mut()
96+
.append_pair("reason", reason.as_str());
9297
Ok(self.make_callback_request(callback)?)
9398
}
9499

@@ -140,6 +145,10 @@ mod tests {
140145
fn test_report_ad_with_valid_url_should_succeed() {
141146
viaduct_dev::init_backend_dev();
142147
let _m = mock("GET", "/report_ad_callback_url")
148+
.match_query(mockito::Matcher::UrlEncoded(
149+
"reason".into(),
150+
"not_interested".into(),
151+
))
143152
.with_status(200)
144153
.create();
145154

@@ -149,7 +158,7 @@ mod tests {
149158
&mockito::server_url()
150159
))
151160
.unwrap();
152-
let result = client.report_ad(url);
161+
let result = client.report_ad(url, ReportReason::NotInterested);
153162
assert!(result.is_ok());
154163
}
155164

0 commit comments

Comments
 (0)