feat(domain): FederationActionPort trait + avatar_url on RemoteActor
This commit is contained in:
@@ -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"
|
"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
|
).bind(url).fetch_optional(&self.pool).await
|
||||||
.map_err(|e| DomainError::Internal(e.to_string()))
|
.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 }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ pub enum DomainError {
|
|||||||
Conflict(String),
|
Conflict(String),
|
||||||
#[error("invalid input: {0}")]
|
#[error("invalid input: {0}")]
|
||||||
InvalidInput(String),
|
InvalidInput(String),
|
||||||
|
#[error("external service error: {0}")]
|
||||||
|
ExternalService(String),
|
||||||
#[error("internal error: {0}")]
|
#[error("internal error: {0}")]
|
||||||
Internal(String),
|
Internal(String),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ pub struct RemoteActor {
|
|||||||
pub inbox_url: String,
|
pub inbox_url: String,
|
||||||
pub shared_inbox_url: Option<String>,
|
pub shared_inbox_url: Option<String>,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
|
pub avatar_url: Option<String>,
|
||||||
pub last_fetched_at: DateTime<Utc>,
|
pub last_fetched_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,6 +194,12 @@ pub trait RemoteActorRepository: Send + Sync {
|
|||||||
async fn find_by_url(&self, url: &str) -> Result<Option<RemoteActor>, DomainError>;
|
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]
|
#[async_trait]
|
||||||
pub trait FeedRepository: Send + Sync {
|
pub trait FeedRepository: Send + Sync {
|
||||||
async fn home_feed(
|
async fn home_feed(
|
||||||
|
|||||||
@@ -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]
|
#[async_trait]
|
||||||
impl FeedRepository for TestStore {
|
impl FeedRepository for TestStore {
|
||||||
async fn home_feed(
|
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)]
|
#[cfg(test)]
|
||||||
mod search_tests {
|
mod search_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ impl IntoResponse for ApiError {
|
|||||||
Self::Domain(DomainError::Forbidden) => (StatusCode::FORBIDDEN, "forbidden".into()),
|
Self::Domain(DomainError::Forbidden) => (StatusCode::FORBIDDEN, "forbidden".into()),
|
||||||
Self::Domain(DomainError::Conflict(m)) => (StatusCode::CONFLICT, m),
|
Self::Domain(DomainError::Conflict(m)) => (StatusCode::CONFLICT, m),
|
||||||
Self::Domain(DomainError::InvalidInput(m)) => (StatusCode::UNPROCESSABLE_ENTITY, 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(_)) => (
|
Self::Domain(DomainError::Internal(_)) => (
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
"internal server error".into(),
|
"internal server error".into(),
|
||||||
|
|||||||
Reference in New Issue
Block a user