Highly ambitious ATProtocol AppView service and sdks
at main 5.1 kB view raw
1use crate::cache::SliceCache; 2use crate::errors::ActorResolverError; 3use atproto_identity::{ 4 plc::query as plc_query, 5 resolve::{InputType, parse_input}, 6 web::query as web_query, 7}; 8use reqwest::Client; 9use serde::{Deserialize, Serialize}; 10use std::sync::Arc; 11use tokio::sync::Mutex; 12 13#[derive(Debug, Clone, Serialize, Deserialize)] 14pub struct ActorData { 15 pub did: String, 16 pub handle: Option<String>, 17 pub pds: String, 18} 19 20pub async fn resolve_actor_data( 21 client: &Client, 22 did: &str, 23) -> Result<ActorData, ActorResolverError> { 24 resolve_actor_data_cached(client, did, None).await 25} 26 27pub async fn resolve_actor_data_cached( 28 client: &Client, 29 did: &str, 30 cache: Option<Arc<Mutex<SliceCache>>>, 31) -> Result<ActorData, ActorResolverError> { 32 // Try cache first if provided 33 if let Some(cache) = &cache { 34 let cached_result = { 35 let mut cache_lock = cache.lock().await; 36 cache_lock.get_cached_did_resolution(did).await 37 }; 38 39 if let Ok(Some(actor_data_value)) = cached_result 40 && let Ok(actor_data) = serde_json::from_value::<ActorData>(actor_data_value) 41 { 42 return Ok(actor_data); 43 } 44 } 45 46 // Cache miss - resolve from PLC/web 47 let actor_data = resolve_actor_data_impl(client, did).await?; 48 49 // Cache the result if cache is provided 50 if let Some(cache) = &cache 51 && let Ok(actor_data_value) = serde_json::to_value(&actor_data) 52 { 53 let mut cache_lock = cache.lock().await; 54 let _ = cache_lock 55 .cache_did_resolution(did, &actor_data_value) 56 .await; 57 } 58 59 Ok(actor_data) 60} 61 62pub async fn resolve_actor_data_with_retry( 63 client: &Client, 64 did: &str, 65 cache: Option<Arc<Mutex<SliceCache>>>, 66 invalidate_cache_on_retry: bool, 67) -> Result<ActorData, ActorResolverError> { 68 match resolve_actor_data_cached(client, did, cache.clone()).await { 69 Ok(actor_data) => Ok(actor_data), 70 Err(e) => { 71 // If we should invalidate cache on retry and we have a cache 72 if invalidate_cache_on_retry { 73 if let Some(cache) = &cache { 74 let mut cache_lock = cache.lock().await; 75 let _ = cache_lock.invalidate_did_resolution(did).await; 76 } 77 78 // Retry once with fresh resolution 79 resolve_actor_data_cached(client, did, cache).await 80 } else { 81 Err(e) 82 } 83 } 84 } 85} 86 87async fn resolve_actor_data_impl( 88 client: &Client, 89 did: &str, 90) -> Result<ActorData, ActorResolverError> { 91 let (pds_url, handle) = match parse_input(did) { 92 Ok(InputType::Plc(did_str)) => match plc_query(client, "plc.directory", &did_str).await { 93 Ok(did_doc) => { 94 let pds = did_doc 95 .service 96 .iter() 97 .find(|service| service.r#type.contains("AtprotoPersonalDataServer")) 98 .map(|service| service.service_endpoint.clone()) 99 .map(|url| url.to_string()) 100 .unwrap_or_else(|| "https://bsky.social".to_string()); 101 let handle = did_doc 102 .also_known_as 103 .iter() 104 .find(|aka| aka.starts_with("at://")) 105 .map(|aka| aka.strip_prefix("at://").unwrap_or(aka).to_string()); 106 (pds, handle) 107 } 108 Err(e) => { 109 return Err(ActorResolverError::ResolveFailed(format!( 110 "Failed to query PLC for {}: {:?}", 111 did, e 112 ))); 113 } 114 }, 115 Ok(InputType::Web(did_str)) => match web_query(client, &did_str).await { 116 Ok(did_doc) => { 117 let pds = did_doc 118 .service 119 .iter() 120 .find(|service| service.r#type.contains("AtprotoPersonalDataServer")) 121 .map(|service| service.service_endpoint.clone()) 122 .map(|url| url.to_string()) 123 .unwrap_or_else(|| "https://bsky.social".to_string()); 124 let handle = did_doc 125 .also_known_as 126 .iter() 127 .find(|aka| aka.starts_with("at://")) 128 .map(|aka| aka.strip_prefix("at://").unwrap_or(aka).to_string()); 129 (pds, handle) 130 } 131 Err(e) => { 132 return Err(ActorResolverError::ResolveFailed(format!( 133 "Failed to query web DID for {}: {:?}", 134 did, e 135 ))); 136 } 137 }, 138 Ok(InputType::Handle(_)) => { 139 return Err(ActorResolverError::InvalidSubject); 140 } 141 Err(e) => { 142 return Err(ActorResolverError::ParseFailed(format!( 143 "Failed to parse DID {}: {:?}", 144 did, e 145 ))); 146 } 147 }; 148 149 Ok(ActorData { 150 did: did.to_string(), 151 handle, 152 pds: pds_url, 153 }) 154}