-
Notifications
You must be signed in to change notification settings - Fork 230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DISCO-3211] Suggest: move to new remote settings API #6598
base: main
Are you sure you want to change the base?
Changes from all commits
be662ea
f584af2
24ded47
16a78e4
fd74e93
11b0606
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,14 +31,17 @@ | |
//! the new suggestion in their results, and return `Suggestion::T` variants | ||
//! as needed. | ||
|
||
use std::fmt; | ||
use std::{fmt, sync::Arc}; | ||
|
||
use remote_settings::{Attachment, RemoteSettingsRecord}; | ||
use serde::{Deserialize, Deserializer}; | ||
use remote_settings::{ | ||
Attachment, RemoteSettingsClient, RemoteSettingsError, RemoteSettingsRecord, | ||
RemoteSettingsService, | ||
}; | ||
use serde::{Deserialize, Deserializer, Serialize}; | ||
use serde_json::{Map, Value}; | ||
|
||
use crate::{ | ||
db::SuggestDao, error::Error, provider::SuggestionProvider, | ||
query::full_keywords_to_fts_content, Result, | ||
error::Error, provider::SuggestionProvider, query::full_keywords_to_fts_content, Result, | ||
}; | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
|
@@ -69,85 +72,56 @@ pub(crate) trait Client { | |
/// client-side filtering. | ||
/// | ||
/// Records that can't be parsed as [SuggestRecord] are ignored. | ||
fn get_records(&self, collection: Collection, dao: &mut SuggestDao) -> Result<Vec<Record>>; | ||
fn get_records(&self, collection: Collection) -> Result<Vec<Record>>; | ||
|
||
fn download_attachment(&self, record: &Record) -> Result<Vec<u8>>; | ||
} | ||
|
||
/// Implements the [Client] trait using a real remote settings client | ||
pub struct RemoteSettingsClient { | ||
pub struct SuggestRemoteSettingsClient { | ||
// Create a separate client for each collection name | ||
quicksuggest_client: remote_settings::RemoteSettings, | ||
fakespot_client: remote_settings::RemoteSettings, | ||
quicksuggest_client: Arc<RemoteSettingsClient>, | ||
fakespot_client: Arc<RemoteSettingsClient>, | ||
} | ||
|
||
impl RemoteSettingsClient { | ||
pub fn new( | ||
server: Option<remote_settings::RemoteSettingsServer>, | ||
bucket_name: Option<String>, | ||
server_url: Option<String>, | ||
) -> Result<Self> { | ||
impl SuggestRemoteSettingsClient { | ||
pub fn new(rs_service: &RemoteSettingsService) -> Result<Self> { | ||
Ok(Self { | ||
quicksuggest_client: remote_settings::RemoteSettings::new( | ||
remote_settings::RemoteSettingsConfig { | ||
server: server.clone(), | ||
bucket_name: bucket_name.clone(), | ||
collection_name: "quicksuggest".to_owned(), | ||
server_url: server_url.clone(), | ||
}, | ||
)?, | ||
fakespot_client: remote_settings::RemoteSettings::new( | ||
remote_settings::RemoteSettingsConfig { | ||
server, | ||
bucket_name, | ||
collection_name: "fakespot-suggest-products".to_owned(), | ||
server_url, | ||
}, | ||
)?, | ||
quicksuggest_client: rs_service.make_client("quicksuggest".to_owned())?, | ||
fakespot_client: rs_service.make_client("fakespot-suggest-products".to_owned())?, | ||
}) | ||
} | ||
|
||
fn client_for_collection(&self, collection: Collection) -> &remote_settings::RemoteSettings { | ||
fn client_for_collection(&self, collection: Collection) -> &RemoteSettingsClient { | ||
match collection { | ||
Collection::Fakespot => &self.fakespot_client, | ||
Collection::Quicksuggest => &self.quicksuggest_client, | ||
} | ||
} | ||
} | ||
|
||
impl Client for RemoteSettingsClient { | ||
fn get_records(&self, collection: Collection, dao: &mut SuggestDao) -> Result<Vec<Record>> { | ||
// For now, handle the cache manually. Once 6328 is merged, we should be able to delegate | ||
// this to remote_settings. | ||
impl Client for SuggestRemoteSettingsClient { | ||
fn get_records(&self, collection: Collection) -> Result<Vec<Record>> { | ||
let client = self.client_for_collection(collection); | ||
let cache = dao.read_cached_rs_data(collection.name()); | ||
let last_modified = match &cache { | ||
Some(response) => response.last_modified, | ||
None => 0, | ||
}; | ||
let response = match cache { | ||
None => client.get_records()?, | ||
Some(cache) => remote_settings::cache::merge_cache_and_response( | ||
cache, | ||
client.get_records_since(last_modified)?, | ||
), | ||
}; | ||
if last_modified != response.last_modified { | ||
dao.write_cached_rs_data(collection.name(), &response); | ||
client.sync()?; | ||
let response = client.get_records(false); | ||
0c0w3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
match response { | ||
Some(r) => Ok(r | ||
.into_iter() | ||
.filter_map(|r| Record::new(r, collection).ok()) | ||
.collect()), | ||
None => Err(Error::RemoteSettings(RemoteSettingsError::Other { | ||
reason: "Unable to get records".to_owned(), | ||
})), | ||
} | ||
|
||
Ok(response | ||
.records | ||
.into_iter() | ||
.filter_map(|r| Record::new(r, collection).ok()) | ||
.collect()) | ||
} | ||
|
||
fn download_attachment(&self, record: &Record) -> Result<Vec<u8>> { | ||
let converted_record: RemoteSettingsRecord = record.clone().into(); | ||
match &record.attachment { | ||
Some(a) => Ok(self | ||
Some(_) => Ok(self | ||
.client_for_collection(record.collection) | ||
.get_attachment(&a.location)?), | ||
.get_attachment(&converted_record)?), | ||
None => Err(Error::MissingAttachment(record.id.to_string())), | ||
} | ||
} | ||
|
@@ -181,11 +155,23 @@ impl Record { | |
} | ||
} | ||
|
||
impl From<Record> for RemoteSettingsRecord { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cargo clippy said impl There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, Rust will automatically generate the |
||
fn from(record: Record) -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we get a bug/ticket on file for removing our There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes sense it's more or less the same as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The key difference is the It's still a good idea and I'm pretty sure there's a way to remove some code duplication. Maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. created a jira in our maintenance epic for this |
||
RemoteSettingsRecord { | ||
id: record.id.to_string(), | ||
last_modified: record.last_modified, | ||
deleted: false, | ||
attachment: record.attachment.clone(), | ||
fields: record.payload.to_json_map(), | ||
} | ||
} | ||
} | ||
|
||
/// A record in the Suggest Remote Settings collection. | ||
/// | ||
/// Most Suggest records don't carry inline fields except for `type`. | ||
/// Suggestions themselves are typically stored in each record's attachment. | ||
#[derive(Clone, Debug, Deserialize)] | ||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
#[serde(tag = "type")] | ||
pub(crate) enum SuggestRecord { | ||
#[serde(rename = "icon")] | ||
|
@@ -214,6 +200,15 @@ pub(crate) enum SuggestRecord { | |
Geonames, | ||
} | ||
|
||
impl SuggestRecord { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels a little goofy just so that we can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, slightly goofy but this makes sense to me. |
||
fn to_json_map(&self) -> Map<String, Value> { | ||
match serde_json::to_value(self) { | ||
Ok(Value::Object(map)) => map, | ||
_ => unreachable!(), | ||
} | ||
} | ||
} | ||
|
||
/// Enum for the different record types that can be consumed. | ||
/// Extracting this from the serialization enum so that we can | ||
/// extend it to get type metadata. | ||
|
@@ -564,7 +559,7 @@ pub(crate) struct DownloadedFakespotSuggestion { | |
} | ||
|
||
/// An exposure suggestion record's inline data | ||
#[derive(Clone, Debug, Deserialize)] | ||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
pub(crate) struct DownloadedExposureRecord { | ||
pub suggestion_type: String, | ||
} | ||
|
@@ -644,11 +639,11 @@ impl FullOrPrefixKeywords<String> { | |
} | ||
|
||
/// Global Suggest configuration data to ingest from a configuration record | ||
#[derive(Clone, Debug, Deserialize)] | ||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
pub(crate) struct DownloadedGlobalConfig { | ||
pub configuration: DownloadedGlobalConfigInner, | ||
} | ||
#[derive(Clone, Debug, Deserialize)] | ||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
pub(crate) struct DownloadedGlobalConfigInner { | ||
/// The maximum number of times the user can click "Show less frequently" | ||
/// for a suggestion in the UI. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks for keeping this code up to date.