/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Fetch the interest data fn fetch_interest_data_inner(
rs: impl RelevancyRemoteSettingsClient,
) -> Result<Vec<(Interest, UrlHash)>> { let remote_settings_response = rs.get_records()?; letmut result = vec![];
for record in remote_settings_response.records { let attachment_data = match &record.attachment {
None => return Err(Error::FetchInterestDataError),
Some(a) => rs.get_attachment(&a.location)?,
}; let interest = get_interest(&record)?; let urls = get_hash_urls(attachment_data)?;
result.extend(std::iter::repeat(interest).zip(urls));
}
Ok(result)
}
let parsed_attachment_data: Vec<RelevancyAttachmentData> = from_json_slice(&attachment_data)?;
for attachment_data in parsed_attachment_data { let hash_url = STANDARD
.decode(attachment_data.domain)
.map_err(|_| Error::Base64DecodeError("Invalid base64 error".to_string()))?; let url_hash = hash_url.try_into().map_err(|_| {
Error::Base64DecodeError("Base64 string has wrong number of bytes".to_string())
})?;
hash_urls.push(url_hash);
}
Ok(hash_urls)
}
/// Extract Interest from the record info fn get_interest(record: &RemoteSettingsRecord) -> Result<Interest> { let record_fields: RelevancyRecord =
from_json(serde_json::Value::Object(record.fields.clone()))?; let custom_details = record_fields.record_custom_details; let category_code = custom_details.category_to_domains.category_code;
Interest::try_from(category_code as u32)
}
/// Insert Interests into Db fn insert_interest_data(data: Vec<(Interest, UrlHash)>, dao: &mutRelevancyDao) -> Result<()> { for chunk in data.chunks(WRITE_CHUNK_SIZE) {
dao.err_if_interrupted()?; for (interest, hash_url) in chunk {
dao.add_url_interest(*hash_url, *interest)?;
}
}
Ok(())
}
#[cfg(test)] mod test {
use std::{cell::RefCell, collections::HashMap};
use anyhow::Context; use remote_settings::RemoteSettingsResponse; use serde_json::json;
/// A snapshot containing fake Remote Settings records and attachments for /// the store to ingest. We use snapshots to test the store's behavior in a /// data-driven way. struct Snapshot {
records: Vec<RemoteSettingsRecord>,
attachments: HashMap<&'static str, Vec<u8>>,
}
impl Snapshot { /// Creates a snapshot from a JSON value that represents a collection of /// Relevancy Remote Settings records. /// /// You can use the [`serde_json::json!`] macro to construct the JSON /// value, then pass it to this function. It's easier to use the /// `Snapshot::with_records(json!(...))` idiom than to construct the /// records by hand. fn with_records(value: serde_json::Value) -> anyhow::Result<Self> {
Ok(Self {
records: serde_json::from_value(value)
.context("Couldn't create snapshot with Remote Settings records")?,
attachments: HashMap::new(),
})
}
/// Adds a data attachment to the snapshot. fn with_data( mutself,
location: &'static str,
value: serde_json::Value,
) -> anyhow::Result<Self> { self.attachments.insert(
location,
serde_json::to_vec(&value).context("Couldn't add data attachment to snapshot")?,
);
Ok(self)
}
}
/// A fake Remote Settings client that returns records and attachments from /// a snapshot. struct SnapshotSettingsClient { /// The current snapshot. You can modify it using /// [`RefCell::borrow_mut()`] to simulate remote updates in tests.
snapshot: RefCell<Snapshot>,
}
impl SnapshotSettingsClient { /// Creates a client with an initial snapshot. fn with_snapshot(snapshot: Snapshot) -> Self { Self {
snapshot: RefCell::new(snapshot),
}
}
}
impl RelevancyRemoteSettingsClient for SnapshotSettingsClient { fn get_records(&self) -> Result<RemoteSettingsResponse> { let records = self.snapshot.borrow().records.clone(); let last_modified = records
.iter()
.map(|record: &RemoteSettingsRecord| record.last_modified)
.max()
.unwrap_or(0);
Ok(RemoteSettingsResponse {
records,
last_modified,
})
}
#[test] fn test_interest_vectors() { let db = RelevancyDb::new_for_test();
db.read_write(|dao| { // Test that the interest data matches the values we started from in // `bin/generate-test-data.rs`
#[test] fn test_variations_on_the_url() { let db = RelevancyDb::new_for_test();
db.read_write(|dao| {
dao.add_url_interest(hash_url("https://espn.com").unwrap(), Interest::Sports)?;
dao.add_url_interest(hash_url("https://nascar.com").unwrap(), Interest::Autos)?;
dao.add_url_interest(hash_url("https://nascar.com").unwrap(), Interest::Sports)?;
// Different paths/queries should work
assert_eq!(
dao.get_url_interest_vector("https://espn.com/foo/bar/?baz")
.unwrap(),
InterestVector {
sports: 1,
..InterestVector::default()
}
); // Different schemes should too
assert_eq!(
dao.get_url_interest_vector("http://espn.com/").unwrap(),
InterestVector {
sports: 1,
..InterestVector::default()
}
); // But changes to the domain shouldn't
assert_eq!(
dao.get_url_interest_vector("http://espn2.com/").unwrap(),
InterestVector::default()
); // However, extra components past the 2nd one in the domain are ignored
assert_eq!(
dao.get_url_interest_vector("https://www.nascar.com/")
.unwrap(),
InterestVector {
autos: 1,
sports: 1,
..InterestVector::default()
}
);
Ok(())
})
.unwrap();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.