Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

### Ads-Client
- Telemetry is now a process-wide singleton internally; the binding API is unchanged, but the `MozAdsTelemetry` callback now receives metrics from every client in the process.
- `request_spoc_or_image_ads` is added to return ads that may ambiguously be spocs or images.
- Added support for relevance scores ([AC-205](https://mozilla-hub.atlassian.net/browse/AC-205)) ([AC-206](https://mozilla-hub.atlassian.net/browse/AC-206)) Accepts a HashMap<String, f64>.

### Autofill
Expand Down
51 changes: 51 additions & 0 deletions components/ads-client/integration-tests/tests/mars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,57 @@ fn test_contract_spoc_prod() {
assert!(placements.get("mock_spoc_1").unwrap().len() == 3);
}

// Note: this test does not actually test a mock placement that returns spoc *and* image ads, as no such placement currently exists,
// and direct staging calls may be stochastic (depends on inventory, etc.).
// TODO: When such a mock placement exists, add a test for it.
#[test]
#[ignore = "integration test: run manually with -- --ignored"]
fn test_contract_spoc_or_image_spocs_prod() {
init_backend();

let client = prod_client();
let result = client.request_spoc_or_image_ads(
vec![MozAdsPlacementRequestWithCount {
count: 3,
iab_content: None,
placement_id: "mock_spoc_1".to_string(),
}],
None,
);

assert!(result.is_ok(), "Spoc ad request failed: {:?}", result.err());
let placements = result.unwrap();
assert!(placements.contains_key("mock_spoc_1"));
assert!(placements.get("mock_spoc_1").unwrap().len() == 3);
}

// Note: this test does not actually test a mock placement that returns spoc *and* image ads, as no such placement currently exists,
// and direct staging calls may be stochastic (depends on inventory, etc.).
// TODO: When such a mock placement exists, add a test for it.
#[test]
#[ignore = "integration test: run manually with -- --ignored"]
fn test_contract_spoc_or_image_images_prod() {
init_backend();

let client = prod_client();
let result = client.request_spoc_or_image_ads(
vec![MozAdsPlacementRequestWithCount {
count: 1,
iab_content: None,
placement_id: "mock_billboard_1".to_string(),
}],
None,
);

assert!(
result.is_ok(),
"Image ad request failed: {:?}",
result.err()
);
let placements = result.unwrap();
assert!(placements.contains_key("mock_billboard_1"));
}

#[test]
#[ignore = "integration test: run manually with -- --ignored"]
fn test_contract_tile_prod() {
Expand Down
7 changes: 7 additions & 0 deletions components/ads-client/src/ads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ pub struct AdTile {
pub url: Url,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(untagged)]
pub enum AdImageOrSpoc {
Image(AdImage),
Spoc(AdSpoc),
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct SpocFrequencyCaps {
pub cap_key: String,
Expand Down
32 changes: 31 additions & 1 deletion components/ads-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use crate::ads::{AdImage, AdSpoc, AdTile};
use crate::ads::{AdImage, AdImageOrSpoc, AdSpoc, AdTile};
#[cfg(feature = "stateful")]
use crate::ads_store::AdsStore;
use crate::bytesize::ByteSize;
Expand Down Expand Up @@ -232,6 +232,36 @@ impl AdsClient {
})
}

// Sometimes, spoc array requests can return multiple types of ads for specific placements.
// This allows both types to be returned without error (as instances of the enum `AdImageOrSpoc`)
// TODO: This, as well as the other synchronous functions, are to be deprecated
// when the stateful client rework is complete.
pub fn request_spoc_or_image_ads(
&self,
ad_placement_requests: Vec<AdPlacementRequest>,
flags: AdRequestFlags,
options: Option<CachePolicy>,
ohttp: bool,
blocks: Vec<String>,
) -> Result<HashMap<String, Vec<AdImageOrSpoc>>, RequestAdsError> {
let result = self.request_ads::<AdImageOrSpoc>(
ad_placement_requests,
flags,
options,
ohttp,
blocks,
AdRequestRelevance::default(),
);
result
.inspect_err(|e| {
telemetry::record_client_error(ClientOperation::RequestAds, e);
})
.map(|response| {
telemetry::record_client_operation(ClientOperation::RequestAds);
response.data
})
}

pub fn request_tile_ads(
&self,
ad_placement_requests: Vec<AdPlacementRequest>,
Expand Down
19 changes: 18 additions & 1 deletion components/ads-client/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

pub mod error;
pub mod telemetry;
use crate::ads::{AdCallbacks, AdImage, AdSpoc, AdTile, SpocFrequencyCaps, SpocRanking};
use crate::ads::{
AdCallbacks, AdImage, AdImageOrSpoc, AdSpoc, AdTile, SpocFrequencyCaps, SpocRanking,
};
#[cfg(feature = "stateful")]
use crate::client::config::AdsStoreConfig;
use crate::client::config::{AdsCacheConfig, AdsClientConfig};
Expand Down Expand Up @@ -241,6 +243,12 @@ pub struct MozAdsSpoc {
pub url: AdsClientUrl,
}

#[derive(Debug, PartialEq, uniffi::Enum)]
pub enum MozAdsImageOrSpoc {
Image(MozAdsImage),
Spoc(MozAdsSpoc),
}

#[derive(Debug, PartialEq, uniffi::Record)]
pub struct MozAdsSpocFrequencyCaps {
pub cap_key: String,
Expand Down Expand Up @@ -303,6 +311,15 @@ impl From<SpocRanking> for MozAdsSpocRanking {
}
}

impl From<AdImageOrSpoc> for MozAdsImageOrSpoc {
fn from(ad: AdImageOrSpoc) -> Self {
match ad {
AdImageOrSpoc::Image(ad) => MozAdsImageOrSpoc::Image(ad.into()),
AdImageOrSpoc::Spoc(ad) => MozAdsImageOrSpoc::Spoc(ad.into()),
}
}
}

impl From<AdImage> for MozAdsImage {
fn from(img: AdImage) -> Self {
Self {
Expand Down
23 changes: 23 additions & 0 deletions components/ads-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,29 @@ impl MozAdsClient {
.collect())
}

#[handle_error(ComponentError)]
#[uniffi::method(default(options = None))]
pub fn request_spoc_or_image_ads(
&self,
moz_ad_requests: Vec<MozAdsPlacementRequestWithCount>,
options: Option<MozAdsRequestOptions>,
) -> AdsClientApiResult<HashMap<String, Vec<MozAdsImageOrSpoc>>> {
let requests: Vec<AdPlacementRequest> = moz_ad_requests.iter().map(|r| r.into()).collect();
let options = options.unwrap_or_default();
let flags = AdRequestFlags::from(&options);
let ohttp = options.ohttp;
let cache_policy = options.cache_policy.map(CachePolicy::from);
let blocks = options.blocks;
let response = self
.inner
.request_spoc_or_image_ads(requests, flags, cache_policy, ohttp, blocks)
.map_err(ComponentError::RequestAds)?;
Ok(response
.into_iter()
.map(|(k, v)| (k, v.into_iter().map(|spoc| spoc.into()).collect()))
.collect())
}

#[handle_error(ComponentError)]
#[uniffi::method(default(options = None))]
pub fn request_tile_ads(
Expand Down
18 changes: 17 additions & 1 deletion components/ads-client/src/mars/ad_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use crate::ads::{AdCallbacks, AdImage, AdSpoc, AdTile};
use crate::ads::{AdCallbacks, AdImage, AdImageOrSpoc, AdSpoc, AdTile};
use crate::http_cache::RequestHash;
use crate::telemetry;
use serde::de::DeserializeOwned;
Expand Down Expand Up @@ -102,6 +102,22 @@ impl AdResponseValue for AdSpoc {
}
}

impl AdResponseValue for AdImageOrSpoc {
fn callbacks_mut(&mut self) -> &mut AdCallbacks {
match self {
AdImageOrSpoc::Image(ad) => &mut ad.callbacks,
AdImageOrSpoc::Spoc(ad) => &mut ad.callbacks,
}
}

fn cap_key(&self) -> Option<String> {
match self {
AdImageOrSpoc::Image(_ad) => None,
AdImageOrSpoc::Spoc(ad) => Some(ad.caps.cap_key.clone()),
}
}
}

impl AdResponseValue for AdTile {
fn callbacks_mut(&mut self) -> &mut AdCallbacks {
&mut self.callbacks
Expand Down
Loading