feat(domain): FederationActionPort trait + avatar_url on RemoteActor

This commit is contained in:
2026-05-14 19:55:10 +02:00
parent 8eb59bfac6
commit 82f8772104
6 changed files with 54 additions and 1 deletions

View File

@@ -44,6 +44,6 @@ impl RemoteActorRepository for PgRemoteActorRepository {
"SELECT url,handle,display_name,inbox_url,shared_inbox_url,public_key,last_fetched_at FROM remote_actors WHERE url=$1"
).bind(url).fetch_optional(&self.pool).await
.map_err(|e| DomainError::Internal(e.to_string()))
.map(|o| o.map(|r| RemoteActor { url: r.url, handle: r.handle, display_name: r.display_name, inbox_url: r.inbox_url, shared_inbox_url: r.shared_inbox_url, public_key: r.public_key, last_fetched_at: r.last_fetched_at }))
.map(|o| o.map(|r| RemoteActor { url: r.url, handle: r.handle, display_name: r.display_name, inbox_url: r.inbox_url, shared_inbox_url: r.shared_inbox_url, public_key: r.public_key, avatar_url: None, last_fetched_at: r.last_fetched_at }))
}
}

View File

@@ -12,6 +12,8 @@ pub enum DomainError {
Conflict(String),
#[error("invalid input: {0}")]
InvalidInput(String),
#[error("external service error: {0}")]
ExternalService(String),
#[error("internal error: {0}")]
Internal(String),
}

View File

@@ -8,5 +8,6 @@ pub struct RemoteActor {
pub inbox_url: String,
pub shared_inbox_url: Option<String>,
pub public_key: String,
pub avatar_url: Option<String>,
pub last_fetched_at: DateTime<Utc>,
}

View File

@@ -194,6 +194,12 @@ pub trait RemoteActorRepository: Send + Sync {
async fn find_by_url(&self, url: &str) -> Result<Option<RemoteActor>, DomainError>;
}
#[async_trait]
pub trait FederationActionPort: Send + Sync {
async fn lookup_actor(&self, handle: &str) -> Result<RemoteActor, DomainError>;
async fn follow_remote(&self, local_user_id: &UserId, handle: &str) -> Result<(), DomainError>;
}
#[async_trait]
pub trait FeedRepository: Send + Sync {
async fn home_feed(

View File

@@ -534,6 +534,21 @@ impl RemoteActorRepository for TestStore {
}
}
#[async_trait]
impl FederationActionPort for TestStore {
async fn lookup_actor(&self, _handle: &str) -> Result<RemoteActor, DomainError> {
Err(DomainError::NotFound)
}
async fn follow_remote(
&self,
_local_user_id: &UserId,
_handle: &str,
) -> Result<(), DomainError> {
Ok(())
}
}
#[async_trait]
impl FeedRepository for TestStore {
async fn home_feed(
@@ -767,6 +782,32 @@ mod ap_repo_tests {
}
}
#[cfg(test)]
mod federation_port_tests {
use super::*;
use crate::value_objects::UserId;
fn uid() -> UserId {
UserId::new()
}
#[tokio::test]
async fn test_store_lookup_returns_not_found() {
let store = TestStore::default();
let err = store.lookup_actor("@alice@example.com").await.unwrap_err();
assert!(matches!(err, DomainError::NotFound));
}
#[tokio::test]
async fn test_store_follow_remote_is_noop_ok() {
let store = TestStore::default();
store
.follow_remote(&uid(), "@alice@example.com")
.await
.unwrap();
}
}
#[cfg(test)]
mod search_tests {
use super::*;

View File

@@ -28,6 +28,9 @@ impl IntoResponse for ApiError {
Self::Domain(DomainError::Forbidden) => (StatusCode::FORBIDDEN, "forbidden".into()),
Self::Domain(DomainError::Conflict(m)) => (StatusCode::CONFLICT, m),
Self::Domain(DomainError::InvalidInput(m)) => (StatusCode::UNPROCESSABLE_ENTITY, m),
Self::Domain(DomainError::ExternalService(_)) => {
(StatusCode::BAD_GATEWAY, "external service error".into())
}
Self::Domain(DomainError::Internal(_)) => (
StatusCode::INTERNAL_SERVER_ERROR,
"internal server error".into(),