feat(domain): ActorConnectionSummary, ConnectionType, RemoteActorConnectionRepository, FetchActorConnections event
This commit is contained in:
@@ -1599,6 +1599,20 @@ impl domain::ports::FederationActionPort for ActivityPubService {
|
|||||||
|
|
||||||
Ok(notes)
|
Ok(notes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn fetch_actor_urls_from_collection(
|
||||||
|
&self,
|
||||||
|
_collection_url: &str,
|
||||||
|
) -> Result<Vec<String>, domain::errors::DomainError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve_actor_profiles(
|
||||||
|
&self,
|
||||||
|
_urls: Vec<String>,
|
||||||
|
) -> Vec<domain::models::actor_connection_summary::ActorConnectionSummary> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ pub enum EventPayload {
|
|||||||
actor_ap_url: String,
|
actor_ap_url: String,
|
||||||
outbox_url: String,
|
outbox_url: String,
|
||||||
},
|
},
|
||||||
|
FetchActorConnections {
|
||||||
|
actor_ap_url: String,
|
||||||
|
collection_url: String,
|
||||||
|
connection_type: String,
|
||||||
|
page: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventPayload {
|
impl EventPayload {
|
||||||
@@ -93,6 +99,7 @@ impl EventPayload {
|
|||||||
Self::UserUnblocked { .. } => "users.unblocked",
|
Self::UserUnblocked { .. } => "users.unblocked",
|
||||||
Self::UserRegistered { .. } => "users.registered",
|
Self::UserRegistered { .. } => "users.registered",
|
||||||
Self::FetchRemoteActorPosts { .. } => "federation.fetch_outbox",
|
Self::FetchRemoteActorPosts { .. } => "federation.fetch_outbox",
|
||||||
|
Self::FetchActorConnections { .. } => "federation.fetch_connections",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,6 +216,17 @@ impl From<&DomainEvent> for EventPayload {
|
|||||||
actor_ap_url: actor_ap_url.clone(),
|
actor_ap_url: actor_ap_url.clone(),
|
||||||
outbox_url: outbox_url.clone(),
|
outbox_url: outbox_url.clone(),
|
||||||
},
|
},
|
||||||
|
DomainEvent::FetchActorConnections {
|
||||||
|
actor_ap_url,
|
||||||
|
collection_url,
|
||||||
|
connection_type,
|
||||||
|
page,
|
||||||
|
} => Self::FetchActorConnections {
|
||||||
|
actor_ap_url: actor_ap_url.clone(),
|
||||||
|
collection_url: collection_url.clone(),
|
||||||
|
connection_type: connection_type.clone(),
|
||||||
|
page: *page,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,6 +352,17 @@ impl TryFrom<EventPayload> for DomainEvent {
|
|||||||
actor_ap_url,
|
actor_ap_url,
|
||||||
outbox_url,
|
outbox_url,
|
||||||
},
|
},
|
||||||
|
EventPayload::FetchActorConnections {
|
||||||
|
actor_ap_url,
|
||||||
|
collection_url,
|
||||||
|
connection_type,
|
||||||
|
page,
|
||||||
|
} => DomainEvent::FetchActorConnections {
|
||||||
|
actor_ap_url,
|
||||||
|
collection_url,
|
||||||
|
connection_type,
|
||||||
|
page,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,6 +448,12 @@ mod tests {
|
|||||||
actor_ap_url: "https://mastodon.social/users/alice".into(),
|
actor_ap_url: "https://mastodon.social/users/alice".into(),
|
||||||
outbox_url: "https://mastodon.social/users/alice/outbox".into(),
|
outbox_url: "https://mastodon.social/users/alice/outbox".into(),
|
||||||
},
|
},
|
||||||
|
EventPayload::FetchActorConnections {
|
||||||
|
actor_ap_url: "https://mastodon.social/users/alice".into(),
|
||||||
|
collection_url: "https://mastodon.social/users/alice/followers".into(),
|
||||||
|
connection_type: "followers".into(),
|
||||||
|
page: 1,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let mut subjects: Vec<&str> = samples.iter().map(|p| p.subject()).collect();
|
let mut subjects: Vec<&str> = samples.iter().map(|p| p.subject()).collect();
|
||||||
subjects.sort();
|
subjects.sort();
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ pub enum DomainEvent {
|
|||||||
actor_ap_url: String,
|
actor_ap_url: String,
|
||||||
outbox_url: String,
|
outbox_url: String,
|
||||||
},
|
},
|
||||||
|
FetchActorConnections {
|
||||||
|
actor_ap_url: String,
|
||||||
|
collection_url: String,
|
||||||
|
connection_type: String,
|
||||||
|
page: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventEnvelope {
|
pub struct EventEnvelope {
|
||||||
|
|||||||
7
crates/domain/src/models/actor_connection_summary.rs
Normal file
7
crates/domain/src/models/actor_connection_summary.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ActorConnectionSummary {
|
||||||
|
pub url: String,
|
||||||
|
pub handle: String,
|
||||||
|
pub display_name: Option<String>,
|
||||||
|
pub avatar_url: Option<String>,
|
||||||
|
}
|
||||||
14
crates/domain/src/models/connection_type.rs
Normal file
14
crates/domain/src/models/connection_type.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ConnectionType {
|
||||||
|
Followers,
|
||||||
|
Following,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionType {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Followers => "followers",
|
||||||
|
Self::Following => "following",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
pub mod actor_connection_summary;
|
||||||
pub mod api_key;
|
pub mod api_key;
|
||||||
|
pub mod connection_type;
|
||||||
pub mod feed;
|
pub mod feed;
|
||||||
pub mod notification;
|
pub mod notification;
|
||||||
pub mod remote_actor;
|
pub mod remote_actor;
|
||||||
|
|||||||
@@ -194,6 +194,31 @@ 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 RemoteActorConnectionRepository: Send + Sync {
|
||||||
|
async fn upsert_connections(
|
||||||
|
&self,
|
||||||
|
actor_url: &str,
|
||||||
|
connection_type: &str,
|
||||||
|
page: u32,
|
||||||
|
actors: &[crate::models::actor_connection_summary::ActorConnectionSummary],
|
||||||
|
) -> Result<(), DomainError>;
|
||||||
|
|
||||||
|
async fn list_connections(
|
||||||
|
&self,
|
||||||
|
actor_url: &str,
|
||||||
|
connection_type: &str,
|
||||||
|
page: u32,
|
||||||
|
) -> Result<Vec<crate::models::actor_connection_summary::ActorConnectionSummary>, DomainError>;
|
||||||
|
|
||||||
|
async fn connection_page_age(
|
||||||
|
&self,
|
||||||
|
actor_url: &str,
|
||||||
|
connection_type: &str,
|
||||||
|
page: u32,
|
||||||
|
) -> Result<Option<chrono::DateTime<chrono::Utc>>, DomainError>;
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait FederationActionPort: Send + Sync {
|
pub trait FederationActionPort: Send + Sync {
|
||||||
async fn lookup_actor(&self, handle: &str) -> Result<RemoteActor, DomainError>;
|
async fn lookup_actor(&self, handle: &str) -> Result<RemoteActor, DomainError>;
|
||||||
@@ -214,6 +239,16 @@ pub trait FederationActionPort: Send + Sync {
|
|||||||
outbox_url: &str,
|
outbox_url: &str,
|
||||||
page: u32,
|
page: u32,
|
||||||
) -> Result<Vec<crate::models::remote_note::RemoteNote>, DomainError>;
|
) -> Result<Vec<crate::models::remote_note::RemoteNote>, DomainError>;
|
||||||
|
|
||||||
|
async fn fetch_actor_urls_from_collection(
|
||||||
|
&self,
|
||||||
|
collection_url: &str,
|
||||||
|
) -> Result<Vec<String>, DomainError>;
|
||||||
|
|
||||||
|
async fn resolve_actor_profiles(
|
||||||
|
&self,
|
||||||
|
urls: Vec<String>,
|
||||||
|
) -> Vec<crate::models::actor_connection_summary::ActorConnectionSummary>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -575,6 +575,52 @@ impl FederationActionPort for TestStore {
|
|||||||
) -> Result<Vec<crate::models::remote_note::RemoteNote>, DomainError> {
|
) -> Result<Vec<crate::models::remote_note::RemoteNote>, DomainError> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn fetch_actor_urls_from_collection(
|
||||||
|
&self,
|
||||||
|
_collection_url: &str,
|
||||||
|
) -> Result<Vec<String>, DomainError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve_actor_profiles(
|
||||||
|
&self,
|
||||||
|
_urls: Vec<String>,
|
||||||
|
) -> Vec<crate::models::actor_connection_summary::ActorConnectionSummary> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl RemoteActorConnectionRepository for TestStore {
|
||||||
|
async fn upsert_connections(
|
||||||
|
&self,
|
||||||
|
_actor_url: &str,
|
||||||
|
_connection_type: &str,
|
||||||
|
_page: u32,
|
||||||
|
_actors: &[crate::models::actor_connection_summary::ActorConnectionSummary],
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_connections(
|
||||||
|
&self,
|
||||||
|
_actor_url: &str,
|
||||||
|
_connection_type: &str,
|
||||||
|
_page: u32,
|
||||||
|
) -> Result<Vec<crate::models::actor_connection_summary::ActorConnectionSummary>, DomainError>
|
||||||
|
{
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connection_page_age(
|
||||||
|
&self,
|
||||||
|
_actor_url: &str,
|
||||||
|
_connection_type: &str,
|
||||||
|
_page: u32,
|
||||||
|
) -> Result<Option<chrono::DateTime<chrono::Utc>>, DomainError> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -851,6 +897,25 @@ mod federation_port_tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(notes.is_empty());
|
assert!(notes.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_store_resolve_actor_profiles_returns_empty() {
|
||||||
|
let store = TestStore::default();
|
||||||
|
let result = store
|
||||||
|
.resolve_actor_profiles(vec!["https://example.com/users/alice".into()])
|
||||||
|
.await;
|
||||||
|
assert!(result.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_store_fetch_collection_urls_returns_empty() {
|
||||||
|
let store = TestStore::default();
|
||||||
|
let urls = store
|
||||||
|
.fetch_actor_urls_from_collection("https://example.com/users/alice/followers")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(urls.is_empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
Reference in New Issue
Block a user