fix(unfurl): key the cache on available provider credentials (#1688)

This commit is contained in:
Hampus
2026-08-16 23:19:51 +02:00
committed by GitHub
parent 40da982f57
commit 96643ab20d
+101 -13
View File
@@ -19,6 +19,14 @@ impl UnfurlRouter {
.build(), .build(),
} }
} }
fn invalidate_all_variants(&self, url: &str) {
for mode in [NsfwMode::Block, NsfwMode::Flag, NsfwMode::Allow] {
for capability in PROVIDER_CAPABILITIES {
self.l1.invalidate(&unfurl_cache_key(url, mode, capability));
}
}
}
} }
impl RouterService for UnfurlRouter { impl RouterService for UnfurlRouter {
@@ -43,7 +51,8 @@ impl RouterService for UnfurlRouter {
nsfw_mode, nsfw_mode,
bypass_cache, bypass_cache,
cache_only, cache_only,
.. youtube_api_key,
klipy_api_key,
} => { } => {
if *bypass_cache { if *bypass_cache {
return None; return None;
@@ -51,7 +60,11 @@ impl RouterService for UnfurlRouter {
let mode = if *cache_only { "cache-only" } else { "full" }; let mode = if *cache_only { "cache-only" } else { "full" };
Some(format!( Some(format!(
"{mode}:{}", "{mode}:{}",
unfurl_cache_key(url, nsfw_mode.unwrap_or_default()) unfurl_cache_key(
url,
nsfw_mode.unwrap_or_default(),
provider_capability(youtube_api_key, klipy_api_key)
)
)) ))
} }
UnfurlRequest::Invalidate { .. } => None, UnfurlRequest::Invalidate { .. } => None,
@@ -64,12 +77,18 @@ impl RouterService for UnfurlRouter {
url, url,
nsfw_mode, nsfw_mode,
bypass_cache, bypass_cache,
youtube_api_key,
klipy_api_key,
.. ..
} => { } => {
if *bypass_cache { if *bypass_cache {
None None
} else { } else {
let key = unfurl_cache_key(url, nsfw_mode.unwrap_or_default()); let key = unfurl_cache_key(
url,
nsfw_mode.unwrap_or_default(),
provider_capability(youtube_api_key, klipy_api_key),
);
self.l1.get(&key) self.l1.get(&key)
} }
} }
@@ -79,38 +98,58 @@ impl RouterService for UnfurlRouter {
fn l1_insert(&self, req: &UnfurlRequest, resp: &UnfurlResponse) { fn l1_insert(&self, req: &UnfurlRequest, resp: &UnfurlResponse) {
match req { match req {
UnfurlRequest::Unfurl { url, nsfw_mode, .. } => { UnfurlRequest::Unfurl {
url,
nsfw_mode,
youtube_api_key,
klipy_api_key,
..
} => {
if let UnfurlResponse::Resolved(result) = resp if let UnfurlResponse::Resolved(result) = resp
&& !result.embeds.is_empty() && !result.embeds.is_empty()
{ {
self.l1.insert( self.l1.insert(
unfurl_cache_key(url, nsfw_mode.unwrap_or_default()), unfurl_cache_key(
url,
nsfw_mode.unwrap_or_default(),
provider_capability(youtube_api_key, klipy_api_key),
),
resp.clone(), resp.clone(),
); );
} }
} }
UnfurlRequest::Invalidate { url } => { UnfurlRequest::Invalidate { url } => {
self.l1.invalidate(&unfurl_cache_key(url, NsfwMode::Block)); self.invalidate_all_variants(url);
self.l1.invalidate(&unfurl_cache_key(url, NsfwMode::Flag));
self.l1.invalidate(&unfurl_cache_key(url, NsfwMode::Allow));
} }
} }
} }
fn l1_invalidate(&self, key: &str) { fn l1_invalidate(&self, key: &str) {
self.l1.invalidate(&unfurl_cache_key(key, NsfwMode::Block)); self.invalidate_all_variants(key);
self.l1.invalidate(&unfurl_cache_key(key, NsfwMode::Flag));
self.l1.invalidate(&unfurl_cache_key(key, NsfwMode::Allow));
} }
} }
fn unfurl_cache_key(url: &str, nsfw_mode: NsfwMode) -> String { const PROVIDER_CAPABILITIES: &[&str] = &["none", "youtube", "klipy", "youtube+klipy"];
fn provider_capability(
youtube_api_key: &Option<String>,
klipy_api_key: &Option<String>,
) -> &'static str {
match (youtube_api_key.is_some(), klipy_api_key.is_some()) {
(true, true) => "youtube+klipy",
(true, false) => "youtube",
(false, true) => "klipy",
(false, false) => "none",
}
}
fn unfurl_cache_key(url: &str, nsfw_mode: NsfwMode, capability: &str) -> String {
let mode = match nsfw_mode { let mode = match nsfw_mode {
NsfwMode::Block => "block", NsfwMode::Block => "block",
NsfwMode::Flag => "flag", NsfwMode::Flag => "flag",
NsfwMode::Allow => "allow", NsfwMode::Allow => "allow",
}; };
format!("{mode}:{url}") format!("{mode}:{capability}:{url}")
} }
#[cfg(test)] #[cfg(test)]
@@ -128,6 +167,55 @@ mod tests {
} }
} }
fn resolved_response() -> UnfurlResponse {
UnfurlResponse::Resolved(std::sync::Arc::new(crate::types::UnfurlResult {
embeds: vec![crate::types::MessageEmbed::new("link")],
cache_ttl_seconds: None,
}))
}
fn unfurl(url: &str, youtube_api_key: Option<&str>) -> UnfurlRequest {
UnfurlRequest::Unfurl {
url: url.to_owned(),
nsfw_mode: Some(NsfwMode::Block),
bypass_cache: false,
cache_only: false,
youtube_api_key: youtube_api_key.map(str::to_owned),
klipy_api_key: None,
}
}
#[test]
fn configuring_a_youtube_key_does_not_reuse_the_keyless_cache_entry() {
let router = UnfurlRouter::new(16);
let url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
router.l1_insert(&unfurl(url, None), &resolved_response());
assert!(
router.l1_lookup(&unfurl(url, Some("configured"))).is_none(),
"a result resolved without a YouTube key must not be served once a key is configured"
);
assert!(
router.l1_lookup(&unfurl(url, None)).is_some(),
"the keyless entry must still serve keyless requests"
);
}
#[test]
fn invalidate_clears_every_provider_capability_variant() {
let router = UnfurlRouter::new(16);
let url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
router.l1_insert(&unfurl(url, None), &resolved_response());
router.l1_insert(&unfurl(url, Some("configured")), &resolved_response());
router.l1_insert(
&UnfurlRequest::Invalidate {
url: url.to_owned(),
},
&resolved_response(),
);
assert!(router.l1_lookup(&unfurl(url, None)).is_none());
assert!(router.l1_lookup(&unfurl(url, Some("configured"))).is_none());
}
#[test] #[test]
fn coalesce_key_separates_cache_only_from_full_unfurls() { fn coalesce_key_separates_cache_only_from_full_unfurls() {
assert_ne!( assert_ne!(