Skip to content

Commit 5654d59

Browse files
committed
feat(ads-client): add OHTTP support (AC-94)
- Route ad requests through an OHTTP relay via `ohttp: bool` in `MozAdsRequestOptions` - Channel is configured once at startup via `viaduct::ohttp::configure_ohttp_channel("ads-client", ...)` - Refactored `RequestCachePolicy` into an enum with `Duration` TTL fields - Renamed `send_with_policy` → `send_with_options`, `use_ohttp` → `ohttp` - `build()` is infallible again (OHTTP config moved out of builder) - Documented OHTTP usage with JavaScript (HNT), Kotlin, and Swift examples
1 parent 47fa0cf commit 5654d59

15 files changed

Lines changed: 1427 additions & 697 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
## ✨ What's New ✨
99

10+
### Ads Client
11+
- Add OHTTP support via `enable_ohttp(relay_url, gateway_config_url)` builder method on `MozAdsClientBuilder`. ([AC-94](https://mozilla-hub.atlassian.net/browse/AC-94))
12+
- Add `try_build()` method on `MozAdsClientBuilder` returning a `Result` for proper error handling. The existing `build()` method is now deprecated.
13+
1014
### Logins
1115
- New `allow_empty_passwords` feature flag to allow storing logins with empty passwords. This feature is intended to be enabled on desktop during the migration.
1216
- Add `ignore_form_action_origin_validation_errors` feature flag that allows logins with non-URL `form_action_origin` values (e.g. "email", "UserCode") to be imported without error. URL normalization for valid URLs is still applied.

components/ads-client/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ once_cell = "1.5"
2727
uniffi = { version = "0.31" }
2828
url = { version = "2", features = ["serde"] }
2929
uuid = { version = "1.3", features = ["v4"] }
30-
viaduct = { path = "../viaduct" }
30+
viaduct = { path = "../viaduct", features = ["ohttp"] }
3131
sql-support = { path = "../support/sql" }
3232

3333
[dev-dependencies]

components/ads-client/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ cargo test -p ads-client-integration-tests --test mars test_contract_image_stagi
4444

4545
## Usage
4646

47-
Please refer to `./docs/usage.md` for information on using the component.
47+
Language-specific usage guides are available in `./docs/`:
48+
49+
- [JavaScript (HNT)](./docs/usage-javascript.md)
50+
- [Kotlin](./docs/usage-kotlin.md)
51+
- [Swift](./docs/usage-swift.md)

components/ads-client/docs/usage-javascript.md

Lines changed: 410 additions & 0 deletions
Large diffs are not rendered by default.

components/ads-client/docs/usage-kotlin.md

Lines changed: 419 additions & 0 deletions
Large diffs are not rendered by default.

components/ads-client/docs/usage-swift.md

Lines changed: 418 additions & 0 deletions
Large diffs are not rendered by default.

components/ads-client/docs/usage.md

Lines changed: 5 additions & 569 deletions
Large diffs are not rendered by default.

components/ads-client/integration-tests/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ ads-client = { path = ".." }
1212
serde_json = "1"
1313
url = "2"
1414
mockito = { version = "0.31", default-features = false }
15-
viaduct = { path = "../../../components/viaduct" }
15+
viaduct = { path = "../../../components/viaduct", features = ["ohttp"] }
1616
viaduct-dev = { path = "../../../components/support/viaduct-dev" }
1717
viaduct-hyper = { path = "../../../components/support/viaduct-hyper" }

components/ads-client/integration-tests/tests/mars.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::sync::Arc;
77

88
use ads_client::{
99
MozAdsClientBuilder, MozAdsEnvironment, MozAdsPlacementRequest,
10-
MozAdsPlacementRequestWithCount, MozAdsReportReason,
10+
MozAdsPlacementRequestWithCount, MozAdsReportReason, MozAdsRequestOptions,
1111
};
1212

1313
fn init_backend() {
@@ -171,3 +171,31 @@ fn test_report_ad() {
171171
let result = client.report_ad(report_url.to_string(), MozAdsReportReason::NotInterested);
172172
assert!(result.is_ok(), "report_ad failed: {:?}", result.err());
173173
}
174+
175+
#[test]
176+
#[ignore = "integration test: run manually with -- --ignored"]
177+
fn test_contract_tile_ohttp_prod() {
178+
init_backend();
179+
viaduct::ohttp::configure_ohttp_channel(
180+
"ads-client".to_string(),
181+
viaduct::ohttp::OhttpConfig {
182+
relay_url: "https://mozilla-ohttp.fastly-edge.com/".to_string(),
183+
gateway_host: "prod.ohttp-gateway.prod.webservices.mozgcp.net".to_string(),
184+
},
185+
)
186+
.expect("OHTTP channel configuration should succeed");
187+
let client = prod_client();
188+
let placements = client
189+
.request_tile_ads(
190+
vec![MozAdsPlacementRequest {
191+
placement_id: "mock_tile_1".to_string(),
192+
iab_content: None,
193+
}],
194+
Some(MozAdsRequestOptions {
195+
ohttp: true,
196+
cache_policy: None,
197+
}),
198+
)
199+
.expect("tile ad request over OHTTP should succeed");
200+
assert!(placements.contains_key("mock_tile_1"));
201+
}

components/ads-client/src/client.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl ReportReason {
2626
}
2727
}
2828
}
29-
use crate::http_cache::{HttpCache, RequestCachePolicy};
29+
use crate::http_cache::{HttpCache, RequestOptions};
3030
use crate::mars::MARSClient;
3131
use crate::telemetry::Telemetry;
3232
use ad_request::{AdPlacementRequest, AdRequest};
@@ -96,9 +96,9 @@ where
9696

9797
let client = MARSClient::new(http_cache, telemetry.clone());
9898
let client = Self {
99-
environment,
100-
context_id_component,
10199
client,
100+
context_id_component,
101+
environment,
102102
telemetry: telemetry.clone(),
103103
rotation_days,
104104
};
@@ -108,9 +108,9 @@ where
108108

109109
let client = MARSClient::new(None, telemetry.clone());
110110
let client = Self {
111-
environment,
112-
context_id_component,
113111
client,
112+
context_id_component,
113+
environment,
114114
telemetry: telemetry.clone(),
115115
rotation_days,
116116
};
@@ -121,16 +121,16 @@ where
121121
fn request_ads<A>(
122122
&self,
123123
ad_placement_requests: Vec<AdPlacementRequest>,
124-
options: Option<RequestCachePolicy>,
124+
options: Option<RequestOptions>,
125125
) -> Result<AdResponse<A>, RequestAdsError>
126126
where
127127
A: AdResponseValue,
128128
{
129129
let context_id = self.get_context_id()?;
130130
let url = self.environment.into_url("ads");
131131
let ad_request = AdRequest::try_new(context_id, ad_placement_requests, url)?;
132-
let cache_policy = options.unwrap_or_default();
133-
let (mut response, request_hash) = self.client.fetch_ads::<A>(ad_request, &cache_policy)?;
132+
let options = options.unwrap_or_default();
133+
let (mut response, request_hash) = self.client.fetch_ads::<A>(ad_request, &options)?;
134134
response.add_request_hash_to_callbacks(&request_hash);
135135
response.add_placement_info_to_report_callbacks();
136136
Ok(response)
@@ -139,7 +139,7 @@ where
139139
pub fn request_image_ads(
140140
&self,
141141
ad_placement_requests: Vec<AdPlacementRequest>,
142-
options: Option<RequestCachePolicy>,
142+
options: Option<RequestOptions>,
143143
) -> Result<HashMap<String, AdImage>, RequestAdsError> {
144144
let response = self
145145
.request_ads::<AdImage>(ad_placement_requests, options)
@@ -153,7 +153,7 @@ where
153153
pub fn request_spoc_ads(
154154
&self,
155155
ad_placement_requests: Vec<AdPlacementRequest>,
156-
options: Option<RequestCachePolicy>,
156+
options: Option<RequestOptions>,
157157
) -> Result<HashMap<String, Vec<AdSpoc>>, RequestAdsError> {
158158
let result = self.request_ads::<AdSpoc>(ad_placement_requests, options);
159159
result
@@ -169,7 +169,7 @@ where
169169
pub fn request_tile_ads(
170170
&self,
171171
ad_placement_requests: Vec<AdPlacementRequest>,
172-
options: Option<RequestCachePolicy>,
172+
options: Option<RequestOptions>,
173173
) -> Result<HashMap<String, AdTile>, RequestAdsError> {
174174
let result = self.request_ads::<AdTile>(ad_placement_requests, options);
175175
result
@@ -268,21 +268,21 @@ mod tests {
268268
Box::new(DefaultContextIdCallback),
269269
);
270270
AdsClient {
271-
environment: Environment::Test,
272-
context_id_component,
273271
client,
274-
telemetry: MozAdsTelemetryWrapper::noop(),
272+
context_id_component,
273+
environment: Environment::Test,
275274
rotation_days: DEFAULT_ROTATION_DAYS,
275+
telemetry: MozAdsTelemetryWrapper::noop(),
276276
}
277277
}
278278

279279
#[test]
280280
fn test_get_context_id() {
281281
let config = AdsClientConfig {
282-
environment: Environment::Test,
283282
cache_config: None,
284-
telemetry: MozAdsTelemetryWrapper::noop(),
283+
environment: Environment::Test,
285284
rotation_days: None,
285+
telemetry: MozAdsTelemetryWrapper::noop(),
286286
};
287287
let client = AdsClient::new(config);
288288
let context_id = client.get_context_id().unwrap();
@@ -394,7 +394,7 @@ mod tests {
394394
ads_client
395395
.request_ads::<AdImage>(
396396
make_happy_placement_requests(),
397-
Some(RequestCachePolicy::default()),
397+
Some(RequestOptions::default()),
398398
)
399399
.unwrap();
400400
}

0 commit comments

Comments
 (0)