From d56d34cc274a2f674f98ce6f7f919b80504d0643 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 17 May 2026 12:25:53 +0200 Subject: [PATCH] refactor: replace long arg lists with input/config structs and builder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Thought::new_local → NewThought struct (7 args → 1) - UserWriter::update_profile → UpdateProfileInput struct (6 args → 2) - update_profile use case → UpdateProfileInput (8 args → 3) - ActivityPubService::new → builder pattern (9 args → 5 required + 4 optional setters) - accept_note → AcceptNoteInput struct (8 args → 1) - ThoughtNote::new_public → ThoughtNoteInput struct (8 args → 1) Remove all #[allow(clippy::too_many_arguments)] annotations. --- .../adapters/activitypub-base/src/ap_ports.rs | 25 ++--- crates/adapters/activitypub-base/src/lib.rs | 4 +- .../adapters/activitypub-base/src/service.rs | 77 ++++++++++++---- crates/adapters/activitypub/src/handler.rs | 54 +++++------ crates/adapters/activitypub/src/note/mod.rs | 41 +++++---- crates/adapters/activitypub/src/note/tests.rs | 20 ++-- crates/adapters/postgres-search/src/tests.rs | 20 ++-- .../adapters/postgres/src/activitypub/mod.rs | 24 ++--- .../postgres/src/activitypub/tests.rs | 40 ++++---- crates/adapters/postgres/src/feed/tests.rs | 20 ++-- crates/adapters/postgres/src/tag/tests.rs | 20 ++-- crates/adapters/postgres/src/test_helpers.rs | 20 ++-- crates/adapters/postgres/src/thought/tests.rs | 92 +++++++++---------- crates/adapters/postgres/src/user/mod.rs | 18 ++-- crates/adapters/postgres/src/user/tests.rs | 15 +-- .../src/services/federation_event/tests.rs | 56 +++++------ .../src/services/notification_event/tests.rs | 92 +++++++++---------- crates/application/src/testing.rs | 9 +- .../application/src/use_cases/auth/tests.rs | 36 +------- .../application/src/use_cases/profile/mod.rs | 23 ++--- .../application/src/use_cases/social/tests.rs | 24 +++-- .../application/src/use_cases/thoughts/mod.rs | 18 ++-- .../src/use_cases/thoughts/tests.rs | 36 ++++---- crates/bootstrap/src/factory.rs | 10 +- crates/domain/src/models/thought.rs | 34 +++---- crates/domain/src/models/user.rs | 9 ++ crates/domain/src/ports.rs | 8 +- crates/domain/src/testing/mod.rs | 18 ++-- crates/presentation/src/handlers/users/mod.rs | 17 ++-- crates/presentation/src/testing.rs | 9 +- crates/worker/src/factory.rs | 10 +- 31 files changed, 449 insertions(+), 450 deletions(-) diff --git a/crates/adapters/activitypub-base/src/ap_ports.rs b/crates/adapters/activitypub-base/src/ap_ports.rs index 471814f..2403b29 100644 --- a/crates/adapters/activitypub-base/src/ap_ports.rs +++ b/crates/adapters/activitypub-base/src/ap_ports.rs @@ -5,6 +5,17 @@ use domain::{ value_objects::{ThoughtId, UserId, Username}, }; +pub struct AcceptNoteInput<'a> { + pub ap_id: &'a str, + pub author_id: &'a UserId, + pub content: &'a str, + pub published: chrono::DateTime, + pub sensitive: bool, + pub content_warning: Option, + pub visibility: &'a str, + pub in_reply_to: Option<&'a str>, +} + /// AP-protocol endpoints for a locally-stored user (local or interned remote). #[derive(Debug, Clone)] pub struct ActorApUrls { @@ -61,18 +72,8 @@ pub trait ActivityPubRepository: Send + Sync { // ── Inbox processing (remote → local) ─────────────────────────── /// Persist an incoming remote Note. Idempotent on ap_id. - #[allow(clippy::too_many_arguments)] - async fn accept_note( - &self, - ap_id: &str, - author_id: &UserId, - content: &str, - published: chrono::DateTime, - sensitive: bool, - content_warning: Option, - visibility: &str, - in_reply_to: Option<&str>, - ) -> Result; + + async fn accept_note(&self, input: AcceptNoteInput<'_>) -> Result; /// Apply an Update to a previously accepted remote Note. async fn apply_note_update(&self, ap_id: &str, new_content: &str) -> Result<(), DomainError>; diff --git a/crates/adapters/activitypub-base/src/lib.rs b/crates/adapters/activitypub-base/src/lib.rs index 75bd509..b4d53dd 100644 --- a/crates/adapters/activitypub-base/src/lib.rs +++ b/crates/adapters/activitypub-base/src/lib.rs @@ -18,7 +18,9 @@ pub mod user; pub mod webfinger; pub use activitypub_federation::kinds::object::NoteType; -pub use ap_ports::{ActivityPubRepository, ActorApUrls, OutboundFederationPort, OutboxEntry}; +pub use ap_ports::{ + AcceptNoteInput, ActivityPubRepository, ActorApUrls, OutboundFederationPort, OutboxEntry, +}; pub use content::ApObjectHandler; pub use data::FederationData; pub use error::Error; diff --git a/crates/adapters/activitypub-base/src/service.rs b/crates/adapters/activitypub-base/src/service.rs index e3d27d7..26a83cb 100644 --- a/crates/adapters/activitypub-base/src/service.rs +++ b/crates/adapters/activitypub-base/src/service.rs @@ -163,34 +163,73 @@ pub struct ActivityPubService { connections_repo: Arc, } +pub struct ActivityPubServiceBuilder { + repo: Arc, + user_repo: Arc, + object_handler: Arc, + base_url: String, + connections_repo: Arc, + allow_registration: bool, + software_name: String, + debug: bool, + event_publisher: Option>, +} + +impl ActivityPubServiceBuilder { + pub fn allow_registration(mut self, v: bool) -> Self { + self.allow_registration = v; + self + } + pub fn software_name(mut self, v: impl Into) -> Self { + self.software_name = v.into(); + self + } + pub fn debug(mut self, v: bool) -> Self { + self.debug = v; + self + } + pub fn event_publisher(mut self, v: Arc) -> Self { + self.event_publisher = Some(v); + self + } + pub async fn build(self) -> anyhow::Result { + let data = FederationData::new( + self.repo, + self.user_repo, + self.object_handler, + self.base_url.clone(), + self.allow_registration, + self.software_name, + self.event_publisher, + ); + let federation_config = ApFederationConfig::new(data, self.debug).await?; + Ok(ActivityPubService { + federation_config, + base_url: self.base_url, + connections_repo: self.connections_repo, + }) + } +} + impl ActivityPubService { - #[allow(clippy::too_many_arguments)] - pub async fn new( + pub fn builder( repo: Arc, user_repo: Arc, object_handler: Arc, - base_url: String, - allow_registration: bool, - software_name: String, - debug: bool, - event_publisher: Option>, + base_url: impl Into, connections_repo: Arc, - ) -> anyhow::Result { - let data = FederationData::new( + ) -> ActivityPubServiceBuilder { + ActivityPubServiceBuilder { repo, user_repo, object_handler, - base_url.clone(), - allow_registration, - software_name, - event_publisher, - ); - let federation_config = ApFederationConfig::new(data, debug).await?; - Ok(Self { - federation_config, - base_url, + base_url: base_url.into(), connections_repo, - }) + allow_registration: false, + software_name: String::new(), + debug: false, + event_publisher: None, + } } pub fn federation_config(&self) -> &ApFederationConfig { diff --git a/crates/adapters/activitypub/src/handler.rs b/crates/adapters/activitypub/src/handler.rs index 7267b74..a104022 100644 --- a/crates/adapters/activitypub/src/handler.rs +++ b/crates/adapters/activitypub/src/handler.rs @@ -7,9 +7,9 @@ use chrono::{DateTime, Utc}; use std::sync::Arc; use url::Url; -use crate::note::ThoughtNote; +use crate::note::{ThoughtNote, ThoughtNoteInput}; use crate::urls::ThoughtsUrls; -use activitypub_base::{ActivityPubRepository, ApObjectHandler}; +use activitypub_base::{AcceptNoteInput, ActivityPubRepository, ApObjectHandler}; use domain::ports::{EventPublisher, TagRepository}; use domain::value_objects::UserId; @@ -58,16 +58,16 @@ impl ApObjectHandler for ThoughtsObjectHandler { .thought .in_reply_to_id .map(|id| self.urls.thought_url(id.as_uuid())); - let note = ThoughtNote::new_public( - note_url.clone(), + let note = ThoughtNote::new_public(ThoughtNoteInput { + id: note_url.clone(), actor_url, - e.thought.content.as_str().to_owned(), - e.thought.created_at, + content: e.thought.content.as_str().to_owned(), + published: e.thought.created_at, in_reply_to, - e.thought.sensitive, - e.thought.content_warning, - followers, - ); + sensitive: e.thought.sensitive, + summary: e.thought.content_warning, + followers_url: followers, + }); Ok((note_url, serde_json::to_value(¬e)?)) }) .collect() @@ -96,16 +96,16 @@ impl ApObjectHandler for ThoughtsObjectHandler { .thought .in_reply_to_id .map(|id| self.urls.thought_url(id.as_uuid())); - let note = ThoughtNote::new_public( - note_url.clone(), + let note = ThoughtNote::new_public(ThoughtNoteInput { + id: note_url.clone(), actor_url, - e.thought.content.as_str().to_owned(), - created_at, + content: e.thought.content.as_str().to_owned(), + published: created_at, in_reply_to, - e.thought.sensitive, - e.thought.content_warning, - followers, - ); + sensitive: e.thought.sensitive, + summary: e.thought.content_warning, + followers_url: followers, + }); Ok((note_url, serde_json::to_value(¬e)?, created_at)) }) .collect() @@ -143,16 +143,16 @@ impl ApObjectHandler for ThoughtsObjectHandler { let thought_id = self .repo - .accept_note( - ap_id.as_str(), - &author_id, - ¬e.content, - note.published, - note.sensitive, - note.summary, + .accept_note(AcceptNoteInput { + ap_id: ap_id.as_str(), + author_id: &author_id, + content: ¬e.content, + published: note.published, + sensitive: note.sensitive, + content_warning: note.summary, visibility, - note.in_reply_to.as_ref().map(|u| u.as_str()), - ) + in_reply_to: note.in_reply_to.as_ref().map(|u| u.as_str()), + }) .await .map_err(|e| anyhow!("{e}"))?; diff --git a/crates/adapters/activitypub/src/note/mod.rs b/crates/adapters/activitypub/src/note/mod.rs index 1c398e9..bb56929 100644 --- a/crates/adapters/activitypub/src/note/mod.rs +++ b/crates/adapters/activitypub/src/note/mod.rs @@ -30,30 +30,31 @@ pub struct ThoughtNote { pub tag: Vec, } +pub struct ThoughtNoteInput { + pub id: Url, + pub actor_url: Url, + pub content: String, + pub published: DateTime, + pub in_reply_to: Option, + pub sensitive: bool, + pub summary: Option, + pub followers_url: Url, +} + impl ThoughtNote { - #[allow(clippy::too_many_arguments)] - pub fn new_public( - id: Url, - actor_url: Url, - content: String, - published: DateTime, - in_reply_to: Option, - sensitive: bool, - summary: Option, - followers_url: Url, - ) -> Self { + pub fn new_public(p: ThoughtNoteInput) -> Self { Self { kind: Default::default(), - url: Some(id.clone()), - id, - attributed_to: actor_url, - content, - published, + url: Some(p.id.clone()), + id: p.id, + attributed_to: p.actor_url, + content: p.content, + published: p.published, to: vec![AS_PUBLIC.to_string()], - cc: vec![followers_url.to_string()], - in_reply_to, - sensitive, - summary, + cc: vec![p.followers_url.to_string()], + in_reply_to: p.in_reply_to, + sensitive: p.sensitive, + summary: p.summary, tag: Vec::new(), } } diff --git a/crates/adapters/activitypub/src/note/tests.rs b/crates/adapters/activitypub/src/note/tests.rs index 786326e..23044cc 100644 --- a/crates/adapters/activitypub/src/note/tests.rs +++ b/crates/adapters/activitypub/src/note/tests.rs @@ -2,16 +2,16 @@ use super::*; #[test] fn note_serializes_with_public_audience() { - let note = ThoughtNote::new_public( - "https://example.com/thoughts/1".parse().unwrap(), - "https://example.com/users/alice".parse().unwrap(), - "Hello world".to_string(), - chrono::Utc::now(), - None, - false, - None, - "https://example.com/users/alice/followers".parse().unwrap(), - ); + let note = ThoughtNote::new_public(super::ThoughtNoteInput { + id: "https://example.com/thoughts/1".parse().unwrap(), + actor_url: "https://example.com/users/alice".parse().unwrap(), + content: "Hello world".to_string(), + published: chrono::Utc::now(), + in_reply_to: None, + sensitive: false, + summary: None, + followers_url: "https://example.com/users/alice/followers".parse().unwrap(), + }); let json = serde_json::to_string(¬e).unwrap(); assert!(json.contains(AS_PUBLIC)); assert!(json.contains("Hello world")); diff --git a/crates/adapters/postgres-search/src/tests.rs b/crates/adapters/postgres-search/src/tests.rs index 5fe6bb8..3ef4937 100644 --- a/crates/adapters/postgres-search/src/tests.rs +++ b/crates/adapters/postgres-search/src/tests.rs @@ -1,7 +1,7 @@ use super::*; use domain::{ models::{ - thought::{Thought, Visibility}, + thought::{NewThought, Thought, Visibility}, user::User, }, ports::{SearchPort, ThoughtRepository, UserWriter}, @@ -19,15 +19,15 @@ async fn seed_thought(pool: &sqlx::PgPool, username: &str, content: &str) -> (Us PasswordHash("h".into()), ); urepo.save(&u).await.unwrap(); - let t = Thought::new_local( - ThoughtId::new(), - u.id.clone(), - Content::new_local(content).unwrap(), - None, - Visibility::Public, - None, - false, - ); + let t = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: u.id.clone(), + content: Content::new_local(content).unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); trepo.save(&t).await.unwrap(); (u, t) } diff --git a/crates/adapters/postgres/src/activitypub/mod.rs b/crates/adapters/postgres/src/activitypub/mod.rs index af31713..b615d75 100644 --- a/crates/adapters/postgres/src/activitypub/mod.rs +++ b/crates/adapters/postgres/src/activitypub/mod.rs @@ -6,7 +6,7 @@ const THOUGHTS_PATH_PREFIX: &str = "/thoughts/"; use chrono::{DateTime, Utc}; use sqlx::PgPool; -use activitypub_base::{ActivityPubRepository, ActorApUrls, OutboxEntry}; +use activitypub_base::{AcceptNoteInput, ActivityPubRepository, ActorApUrls, OutboxEntry}; use domain::{ errors::DomainError, models::thought::{Thought, Visibility}, @@ -210,17 +210,17 @@ impl ActivityPubRepository for PgActivityPubRepository { .map(|_| ()) } - async fn accept_note( - &self, - ap_id: &str, - author_id: &UserId, - content: &str, - published: DateTime, - sensitive: bool, - content_warning: Option, - visibility: &str, - in_reply_to: Option<&str>, - ) -> Result { + async fn accept_note(&self, input: AcceptNoteInput<'_>) -> Result { + let AcceptNoteInput { + ap_id, + author_id, + content, + published, + sensitive, + content_warning, + visibility, + in_reply_to, + } = input; let capped: String = content.chars().take(MAX_REMOTE_CONTENT_CHARS).collect(); let (in_reply_to_id, in_reply_to_url) = match in_reply_to { Some(url) => { diff --git a/crates/adapters/postgres/src/activitypub/tests.rs b/crates/adapters/postgres/src/activitypub/tests.rs index f49da8e..f035a1e 100644 --- a/crates/adapters/postgres/src/activitypub/tests.rs +++ b/crates/adapters/postgres/src/activitypub/tests.rs @@ -1,5 +1,5 @@ use super::*; -use activitypub_base::ActivityPubRepository; +use activitypub_base::{AcceptNoteInput, ActivityPubRepository}; #[sqlx::test(migrations = "./migrations")] async fn intern_remote_actor_is_idempotent(pool: sqlx::PgPool) { @@ -16,16 +16,16 @@ async fn accept_and_retract_note(pool: sqlx::PgPool) { let actor_url = "https://remote.example/users/bob"; let ap_id = "https://remote.example/notes/1"; let author = repo.intern_remote_actor(actor_url).await.unwrap(); - repo.accept_note( + repo.accept_note(AcceptNoteInput { ap_id, - &author, - "hello from remote", - chrono::Utc::now(), - false, - None, - "public", - None, - ) + author_id: &author, + content: "hello from remote", + published: chrono::Utc::now(), + sensitive: false, + content_warning: None, + visibility: "public", + in_reply_to: None, + }) .await .unwrap(); repo.retract_note(ap_id).await.unwrap(); @@ -46,16 +46,16 @@ async fn accept_note_returns_thought_id(pool: sqlx::PgPool) { .unwrap(); let thought_id = repo - .accept_note( - "https://remote.example/notes/1", - &actor_user_id, - "Hello #rust world", - chrono::Utc::now(), - false, - None, - "public", - None, - ) + .accept_note(AcceptNoteInput { + ap_id: "https://remote.example/notes/1", + author_id: &actor_user_id, + content: "Hello #rust world", + published: chrono::Utc::now(), + sensitive: false, + content_warning: None, + visibility: "public", + in_reply_to: None, + }) .await .unwrap(); diff --git a/crates/adapters/postgres/src/feed/tests.rs b/crates/adapters/postgres/src/feed/tests.rs index 5105320..51f8b79 100644 --- a/crates/adapters/postgres/src/feed/tests.rs +++ b/crates/adapters/postgres/src/feed/tests.rs @@ -3,7 +3,7 @@ use crate::{thought::PgThoughtRepository, user::PgUserRepository}; use domain::{ models::{ feed::PageParams, - thought::{Thought, Visibility}, + thought::{NewThought, Thought, Visibility}, user::User, }, ports::{FeedQuery, ThoughtRepository, UserWriter}, @@ -20,15 +20,15 @@ async fn seed(pool: &sqlx::PgPool, username: &str, content: &str) -> (User, Thou PasswordHash("h".into()), ); urepo.save(&u).await.unwrap(); - let t = Thought::new_local( - ThoughtId::new(), - u.id.clone(), - Content::new_local(content).unwrap(), - None, - Visibility::Public, - None, - false, - ); + let t = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: u.id.clone(), + content: Content::new_local(content).unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); trepo.save(&t).await.unwrap(); (u, t) } diff --git a/crates/adapters/postgres/src/tag/tests.rs b/crates/adapters/postgres/src/tag/tests.rs index e9642d7..5d6a205 100644 --- a/crates/adapters/postgres/src/tag/tests.rs +++ b/crates/adapters/postgres/src/tag/tests.rs @@ -3,7 +3,7 @@ use crate::{thought::PgThoughtRepository, user::PgUserRepository}; use domain::ports::{ThoughtRepository, UserWriter}; use domain::{ models::{ - thought::{Thought, Visibility}, + thought::{NewThought, Thought, Visibility}, user::User, }, value_objects::*, @@ -29,15 +29,15 @@ async fn attach_and_list(pool: sqlx::PgPool) { PasswordHash("h".into()), ); urepo.save(&u).await.unwrap(); - let t = Thought::new_local( - ThoughtId::new(), - u.id.clone(), - Content::new_local("hi").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let t = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: u.id.clone(), + content: Content::new_local("hi").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); trepo.save(&t).await.unwrap(); let repo = PgTagRepository::new(pool); let tag = repo.find_or_create("greetings").await.unwrap(); diff --git a/crates/adapters/postgres/src/test_helpers.rs b/crates/adapters/postgres/src/test_helpers.rs index ea0262d..bb64233 100644 --- a/crates/adapters/postgres/src/test_helpers.rs +++ b/crates/adapters/postgres/src/test_helpers.rs @@ -1,7 +1,7 @@ use crate::{thought::PgThoughtRepository, user::PgUserRepository}; use domain::{ models::{ - thought::{Thought, Visibility}, + thought::{NewThought, Thought, Visibility}, user::User, }, ports::{ThoughtRepository, UserWriter}, @@ -23,15 +23,15 @@ pub async fn seed_user(pool: &sqlx::PgPool, username: &str, email: &str) -> User pub async fn seed_user_and_thought(pool: &sqlx::PgPool) -> (User, Thought) { let user = seed_user(pool, "alice", "alice@ex.com").await; let trepo = PgThoughtRepository::new(pool.clone()); - let t = Thought::new_local( - ThoughtId::new(), - user.id.clone(), - Content::new_local("hi").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let t = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: user.id.clone(), + content: Content::new_local("hi").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); trepo.save(&t).await.unwrap(); (user, t) } diff --git a/crates/adapters/postgres/src/thought/tests.rs b/crates/adapters/postgres/src/thought/tests.rs index 8a52ce8..3a1dd3e 100644 --- a/crates/adapters/postgres/src/thought/tests.rs +++ b/crates/adapters/postgres/src/thought/tests.rs @@ -1,7 +1,7 @@ use super::*; use crate::test_helpers::seed_user; use domain::{ - models::thought::{Thought, Visibility}, + models::thought::{NewThought, Thought, Visibility}, value_objects::*, }; @@ -9,15 +9,15 @@ use domain::{ async fn save_and_find_thought(pool: sqlx::PgPool) { let user = seed_user(&pool, "alice", "alice@ex.com").await; let repo = PgThoughtRepository::new(pool); - let t = Thought::new_local( - ThoughtId::new(), - user.id.clone(), - Content::new_local("hello world").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let t = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: user.id.clone(), + content: Content::new_local("hello world").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); repo.save(&t).await.unwrap(); let found = repo.find_by_id(&t.id).await.unwrap().unwrap(); assert_eq!(found.content.as_str(), "hello world"); @@ -28,15 +28,15 @@ async fn save_and_find_thought(pool: sqlx::PgPool) { async fn delete_thought(pool: sqlx::PgPool) { let user = seed_user(&pool, "bob", "bob@ex.com").await; let repo = PgThoughtRepository::new(pool); - let t = Thought::new_local( - ThoughtId::new(), - user.id.clone(), - Content::new_local("bye").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let t = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: user.id.clone(), + content: Content::new_local("bye").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); repo.save(&t).await.unwrap(); repo.delete(&t.id, &user.id).await.unwrap(); assert!(repo.find_by_id(&t.id).await.unwrap().is_none()); @@ -47,15 +47,15 @@ async fn delete_wrong_owner_returns_not_found(pool: sqlx::PgPool) { let alice = seed_user(&pool, "alice", "alice@ex.com").await; let bob = seed_user(&pool, "bob", "bob@ex.com").await; let repo = PgThoughtRepository::new(pool); - let t = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("secret").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let t = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("secret").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); repo.save(&t).await.unwrap(); let err = repo.delete(&t.id, &bob.id).await.unwrap_err(); assert!(matches!(err, DomainError::NotFound)); @@ -65,24 +65,24 @@ async fn delete_wrong_owner_returns_not_found(pool: sqlx::PgPool) { async fn get_thread_returns_root_and_replies(pool: sqlx::PgPool) { let user = seed_user(&pool, "charlie", "charlie@ex.com").await; let repo = PgThoughtRepository::new(pool); - let root = Thought::new_local( - ThoughtId::new(), - user.id.clone(), - Content::new_local("root").unwrap(), - None, - Visibility::Public, - None, - false, - ); - let reply = Thought::new_local( - ThoughtId::new(), - user.id.clone(), - Content::new_local("reply").unwrap(), - Some(root.id.clone()), - Visibility::Public, - None, - false, - ); + let root = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: user.id.clone(), + content: Content::new_local("root").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); + let reply = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: user.id.clone(), + content: Content::new_local("reply").unwrap(), + in_reply_to_id: Some(root.id.clone()), + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); repo.save(&root).await.unwrap(); repo.save(&reply).await.unwrap(); let thread = repo.get_thread(&root.id).await.unwrap(); diff --git a/crates/adapters/postgres/src/user/mod.rs b/crates/adapters/postgres/src/user/mod.rs index 9e30ad8..6c4fd67 100644 --- a/crates/adapters/postgres/src/user/mod.rs +++ b/crates/adapters/postgres/src/user/mod.rs @@ -4,7 +4,7 @@ use chrono::{DateTime, Utc}; use domain::{ errors::DomainError, models::feed::{PageParams, Paginated, UserSummary}, - models::user::User, + models::user::{UpdateProfileInput, User}, ports::{UserReader, UserWriter}, value_objects::{Email, PasswordHash, UserId, Username}, }; @@ -265,21 +265,17 @@ impl UserWriter for PgUserRepository { async fn update_profile( &self, user_id: &UserId, - display_name: Option, - bio: Option, - avatar_url: Option, - header_url: Option, - custom_css: Option, + input: UpdateProfileInput, ) -> Result<(), DomainError> { sqlx::query( "UPDATE users SET display_name=$2,bio=$3,avatar_url=$4,header_url=$5,custom_css=$6,updated_at=NOW() WHERE id=$1" ) .bind(user_id.as_uuid()) - .bind(display_name) - .bind(bio) - .bind(avatar_url) - .bind(header_url) - .bind(custom_css) + .bind(input.display_name) + .bind(input.bio) + .bind(input.avatar_url) + .bind(input.header_url) + .bind(input.custom_css) .execute(&self.pool) .await .into_domain() diff --git a/crates/adapters/postgres/src/user/tests.rs b/crates/adapters/postgres/src/user/tests.rs index a7fc6d1..5531509 100644 --- a/crates/adapters/postgres/src/user/tests.rs +++ b/crates/adapters/postgres/src/user/tests.rs @@ -1,5 +1,8 @@ use super::*; -use domain::{models::user::User, value_objects::*}; +use domain::{ + models::user::{UpdateProfileInput, User}, + value_objects::*, +}; #[sqlx::test(migrations = "./migrations")] async fn save_and_find_by_id(pool: sqlx::PgPool) { @@ -55,11 +58,11 @@ async fn update_profile_changes_fields(pool: sqlx::PgPool) { repo.save(&user).await.unwrap(); repo.update_profile( &user.id, - Some("Charlie".into()), - Some("bio".into()), - None, - None, - None, + UpdateProfileInput { + display_name: Some("Charlie".into()), + bio: Some("bio".into()), + ..Default::default() + }, ) .await .unwrap(); diff --git a/crates/application/src/services/federation_event/tests.rs b/crates/application/src/services/federation_event/tests.rs index b6ac54e..1ab2e43 100644 --- a/crates/application/src/services/federation_event/tests.rs +++ b/crates/application/src/services/federation_event/tests.rs @@ -5,7 +5,7 @@ use async_trait::async_trait; use domain::{ errors::DomainError, events::DomainEvent, - models::thought::{Thought, Visibility}, + models::thought::{NewThought, Thought, Visibility}, models::user::User, testing::TestStore, value_objects::*, @@ -92,15 +92,15 @@ fn alice() -> User { } fn local_thought(author_id: UserId) -> Thought { - Thought::new_local( - ThoughtId::new(), - author_id, - Content::new_local("hello").unwrap(), - None, - Visibility::Public, - None, - false, - ) + Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: author_id, + content: Content::new_local("hello").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }) } fn svc(store: &TestStore, spy: Arc) -> FederationEventService { @@ -275,15 +275,15 @@ async fn boost_of_remote_thought_announces_remote_ap_id() { async fn direct_thought_created_does_not_broadcast() { let store = TestStore::default(); let alice = alice(); - let thought = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("private").unwrap(), - None, - Visibility::Direct, - None, - false, - ); + let thought = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("private").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Direct, + content_warning: None, + sensitive: false, + }); store.users.lock().unwrap().push(alice.clone()); store.thoughts.lock().unwrap().push(thought.clone()); @@ -304,15 +304,15 @@ async fn direct_thought_created_does_not_broadcast() { async fn followers_only_thought_does_not_broadcast_publicly() { let store = TestStore::default(); let alice = alice(); - let thought = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("for followers").unwrap(), - None, - Visibility::Followers, - None, - false, - ); + let thought = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("for followers").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Followers, + content_warning: None, + sensitive: false, + }); store.users.lock().unwrap().push(alice.clone()); store.thoughts.lock().unwrap().push(thought.clone()); diff --git a/crates/application/src/services/notification_event/tests.rs b/crates/application/src/services/notification_event/tests.rs index 15658da..7435f97 100644 --- a/crates/application/src/services/notification_event/tests.rs +++ b/crates/application/src/services/notification_event/tests.rs @@ -2,7 +2,7 @@ use super::*; use domain::{ models::{ notification::NotificationKind, - thought::{Thought, Visibility}, + thought::{NewThought, Thought, Visibility}, user::User, }, testing::TestStore, @@ -24,15 +24,15 @@ async fn like_creates_notification_for_thought_author() { let store = TestStore::default(); let alice = alice(); let bob_id = UserId::new(); - let thought = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("hello").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let thought = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("hello").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); store.thoughts.lock().unwrap().push(thought.clone()); let svc = NotificationEventService { thoughts: Arc::new(store.clone()), @@ -54,15 +54,15 @@ async fn like_creates_notification_for_thought_author() { async fn self_like_creates_no_notification() { let store = TestStore::default(); let alice = alice(); - let thought = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("hello").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let thought = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("hello").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); store.thoughts.lock().unwrap().push(thought.clone()); let svc = NotificationEventService { thoughts: Arc::new(store.clone()), @@ -103,15 +103,15 @@ async fn reply_creates_notification_for_original_author() { let store = TestStore::default(); let alice = alice(); let bob_id = UserId::new(); - let original = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("original").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let original = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("original").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); store.thoughts.lock().unwrap().push(original.clone()); let svc = NotificationEventService { thoughts: Arc::new(store.clone()), @@ -133,15 +133,15 @@ async fn reply_creates_notification_for_original_author() { async fn self_reply_creates_no_notification() { let store = TestStore::default(); let alice = alice(); - let original = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("original").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let original = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("original").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); store.thoughts.lock().unwrap().push(original.clone()); let svc = NotificationEventService { thoughts: Arc::new(store.clone()), @@ -161,15 +161,15 @@ async fn self_reply_creates_no_notification() { async fn self_boost_creates_no_notification() { let store = TestStore::default(); let alice = alice(); - let thought = Thought::new_local( - ThoughtId::new(), - alice.id.clone(), - Content::new_local("hello").unwrap(), - None, - Visibility::Public, - None, - false, - ); + let thought = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: alice.id.clone(), + content: Content::new_local("hello").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); store.thoughts.lock().unwrap().push(thought.clone()); let svc = NotificationEventService { thoughts: Arc::new(store.clone()), diff --git a/crates/application/src/testing.rs b/crates/application/src/testing.rs index 64e2653..81a93ff 100644 --- a/crates/application/src/testing.rs +++ b/crates/application/src/testing.rs @@ -95,14 +95,7 @@ impl ActivityPubRepository for TestApRepo { } async fn accept_note( &self, - _ap_id: &str, - _author_id: &UserId, - _content: &str, - _published: chrono::DateTime, - _sensitive: bool, - _content_warning: Option, - _visibility: &str, - _in_reply_to: Option<&str>, + _input: activitypub_base::AcceptNoteInput<'_>, ) -> Result { Ok(ThoughtId::from_uuid(uuid::Uuid::new_v4())) } diff --git a/crates/application/src/use_cases/auth/tests.rs b/crates/application/src/use_cases/auth/tests.rs index d5985eb..009223d 100644 --- a/crates/application/src/use_cases/auth/tests.rs +++ b/crates/application/src/use_cases/auth/tests.rs @@ -5,7 +5,7 @@ use domain::{ events::DomainEvent, models::{ feed::{PageParams, Paginated, UserSummary}, - user::User, + user::{UpdateProfileInput, User}, }, ports::{AuthService, GeneratedToken, PasswordHasher, UserReader, UserWriter}, testing::{NoOpEventPublisher, TestStore}, @@ -56,22 +56,9 @@ impl UserWriter for ConflictOnSaveStore { async fn update_profile( &self, user_id: &UserId, - display_name: Option, - bio: Option, - avatar_url: Option, - header_url: Option, - custom_css: Option, + input: UpdateProfileInput, ) -> Result<(), DomainError> { - self.0 - .update_profile( - user_id, - display_name, - bio, - avatar_url, - header_url, - custom_css, - ) - .await + self.0.update_profile(user_id, input).await } } @@ -114,22 +101,9 @@ impl UserWriter for EmailConflictOnSaveStore { async fn update_profile( &self, user_id: &UserId, - display_name: Option, - bio: Option, - avatar_url: Option, - header_url: Option, - custom_css: Option, + input: UpdateProfileInput, ) -> Result<(), DomainError> { - self.0 - .update_profile( - user_id, - display_name, - bio, - avatar_url, - header_url, - custom_css, - ) - .await + self.0.update_profile(user_id, input).await } } diff --git a/crates/application/src/use_cases/profile/mod.rs b/crates/application/src/use_cases/profile/mod.rs index a308ac6..791be0c 100644 --- a/crates/application/src/use_cases/profile/mod.rs +++ b/crates/application/src/use_cases/profile/mod.rs @@ -3,7 +3,10 @@ const MAX_TOP_FRIENDS: usize = 8; use domain::{ errors::DomainError, events::DomainEvent, - models::{top_friend::TopFriend, user::User}, + models::{ + top_friend::TopFriend, + user::{UpdateProfileInput, User}, + }, ports::{EventPublisher, TopFriendRepository, UserReader, UserWriter}, value_objects::{UserId, Username}, }; @@ -41,27 +44,13 @@ pub async fn get_user_by_id_or_username( } } -#[allow(clippy::too_many_arguments)] pub async fn update_profile( users: &dyn UserWriter, events: &dyn EventPublisher, user_id: &UserId, - display_name: Option, - bio: Option, - avatar_url: Option, - header_url: Option, - custom_css: Option, + input: UpdateProfileInput, ) -> Result<(), DomainError> { - users - .update_profile( - user_id, - display_name, - bio, - avatar_url, - header_url, - custom_css, - ) - .await?; + users.update_profile(user_id, input).await?; events .publish(&DomainEvent::ProfileUpdated { user_id: user_id.clone(), diff --git a/crates/application/src/use_cases/social/tests.rs b/crates/application/src/use_cases/social/tests.rs index aabf855..53bc32b 100644 --- a/crates/application/src/use_cases/social/tests.rs +++ b/crates/application/src/use_cases/social/tests.rs @@ -1,7 +1,7 @@ use super::*; use domain::{ models::{ - thought::{Thought, Visibility}, + thought::{NewThought, Thought, Visibility}, user::User, }, testing::TestStore, @@ -22,15 +22,19 @@ async fn like_and_unlike() { let store = TestStore::default(); let alice = user("alice"); let tid = ThoughtId::new(); - store.thoughts.lock().unwrap().push(Thought::new_local( - tid.clone(), - alice.id.clone(), - Content::new_local("hi").unwrap(), - None, - Visibility::Public, - None, - false, - )); + store + .thoughts + .lock() + .unwrap() + .push(Thought::new_local(NewThought { + id: tid.clone(), + user_id: alice.id.clone(), + content: Content::new_local("hi").unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + })); like_thought(&store, &store, &alice.id, &tid).await.unwrap(); assert_eq!(store.likes.lock().unwrap().len(), 1); unlike_thought(&store, &store, &alice.id, &tid) diff --git a/crates/application/src/use_cases/thoughts/mod.rs b/crates/application/src/use_cases/thoughts/mod.rs index 4b79989..b7a3220 100644 --- a/crates/application/src/use_cases/thoughts/mod.rs +++ b/crates/application/src/use_cases/thoughts/mod.rs @@ -3,7 +3,7 @@ use domain::{ events::DomainEvent, models::{ feed::{EngagementStats, FeedEntry}, - thought::{Thought, Visibility}, + thought::{NewThought, Thought, Visibility}, }, ports::{ EngagementRepository, EventPublisher, OutboxWriter, TagRepository, ThoughtRepository, @@ -46,15 +46,15 @@ pub async fn create_thought( Some("direct") => Visibility::Direct, _ => Visibility::Public, }; - let thought = Thought::new_local( - ThoughtId::new(), - input.user_id, - content.clone(), - input.in_reply_to_id.clone(), + let thought = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: input.user_id, + content: content.clone(), + in_reply_to_id: input.in_reply_to_id.clone(), visibility, - input.content_warning, - input.sensitive, - ); + content_warning: input.content_warning, + sensitive: input.sensitive, + }); thoughts.save(&thought).await?; // Extract and attach hashtags from content. diff --git a/crates/application/src/use_cases/thoughts/tests.rs b/crates/application/src/use_cases/thoughts/tests.rs index c44d265..ea1b091 100644 --- a/crates/application/src/use_cases/thoughts/tests.rs +++ b/crates/application/src/use_cases/thoughts/tests.rs @@ -222,7 +222,7 @@ async fn create_reply_sets_in_reply_to_id() { // enrichment_tests (combined from second cfg(test) block) -use domain::models::thought::{Thought, Visibility}; +use domain::models::thought::{NewThought, Thought, Visibility}; use domain::ports::{ThoughtRepository, UserWriter}; fn make_user() -> User { @@ -235,15 +235,15 @@ fn make_user() -> User { } fn make_thought(user_id: UserId) -> Thought { - Thought::new_local( - ThoughtId::new(), + Thought::new_local(NewThought { + id: ThoughtId::new(), user_id, - Content::new_local(String::from("hello")).unwrap(), - None, - Visibility::Public, - None, - false, - ) + content: Content::new_local(String::from("hello")).unwrap(), + in_reply_to_id: None, + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }) } #[tokio::test] @@ -287,15 +287,15 @@ async fn get_thread_views_batches_correctly() { ::save(&store, &root) .await .unwrap(); - let reply = Thought::new_local( - ThoughtId::new(), - user.id.clone(), - Content::new_local(String::from("reply")).unwrap(), - Some(root.id.clone()), - Visibility::Public, - None, - false, - ); + let reply = Thought::new_local(NewThought { + id: ThoughtId::new(), + user_id: user.id.clone(), + content: Content::new_local(String::from("reply")).unwrap(), + in_reply_to_id: Some(root.id.clone()), + visibility: Visibility::Public, + content_warning: None, + sensitive: false, + }); ::save(&store, &reply) .await .unwrap(); diff --git a/crates/bootstrap/src/factory.rs b/crates/bootstrap/src/factory.rs index 73da3f0..f9d4004 100644 --- a/crates/bootstrap/src/factory.rs +++ b/crates/bootstrap/src/factory.rs @@ -73,7 +73,7 @@ pub async fn build(cfg: &Config) -> Infrastructure { // 3. ActivityPub federation let ap_service = Arc::new( - ActivityPubService::new( + ActivityPubService::builder( Arc::new(PostgresFederationRepository::new(pool.clone())), Arc::new(PostgresApUserRepository::new( pool.clone(), @@ -86,12 +86,12 @@ pub async fn build(cfg: &Config) -> Infrastructure { Arc::new(postgres::tag::PgTagRepository::new(pool.clone())), )), cfg.base_url.clone(), - cfg.allow_registration, - "thoughts".to_string(), - cfg.debug, - None, Arc::new(PgRemoteActorConnectionRepository::new(pool.clone())), ) + .allow_registration(cfg.allow_registration) + .software_name("thoughts") + .debug(cfg.debug) + .build() .await .expect("Failed to build ActivityPubService"), ); diff --git a/crates/domain/src/models/thought.rs b/crates/domain/src/models/thought.rs index 72c1953..03f854b 100644 --- a/crates/domain/src/models/thought.rs +++ b/crates/domain/src/models/thought.rs @@ -46,24 +46,26 @@ impl Visibility { } } +pub struct NewThought { + pub id: ThoughtId, + pub user_id: UserId, + pub content: Content, + pub in_reply_to_id: Option, + pub visibility: Visibility, + pub content_warning: Option, + pub sensitive: bool, +} + impl Thought { - pub fn new_local( - id: ThoughtId, - user_id: UserId, - content: Content, - in_reply_to_id: Option, - visibility: Visibility, - content_warning: Option, - sensitive: bool, - ) -> Self { + pub fn new_local(p: NewThought) -> Self { Self { - id, - user_id, - content, - in_reply_to_id, - visibility, - content_warning, - sensitive, + id: p.id, + user_id: p.user_id, + content: p.content, + in_reply_to_id: p.in_reply_to_id, + visibility: p.visibility, + content_warning: p.content_warning, + sensitive: p.sensitive, local: true, created_at: Utc::now(), updated_at: None, diff --git a/crates/domain/src/models/user.rs b/crates/domain/src/models/user.rs index 4e61f3c..f303aba 100644 --- a/crates/domain/src/models/user.rs +++ b/crates/domain/src/models/user.rs @@ -1,6 +1,15 @@ use crate::value_objects::{Email, PasswordHash, UserId, Username}; use chrono::{DateTime, Utc}; +#[derive(Debug, Default, Clone)] +pub struct UpdateProfileInput { + pub display_name: Option, + pub bio: Option, + pub avatar_url: Option, + pub header_url: Option, + pub custom_css: Option, +} + #[derive(Debug, Clone)] pub struct User { pub id: UserId, diff --git a/crates/domain/src/ports.rs b/crates/domain/src/ports.rs index 6333cf1..7705f1a 100644 --- a/crates/domain/src/ports.rs +++ b/crates/domain/src/ports.rs @@ -12,7 +12,7 @@ use crate::{ tag::Tag, thought::Thought, top_friend::TopFriend, - user::User, + user::{UpdateProfileInput, User}, }, value_objects::{ ApiKeyId, Content, Email, NotificationId, PasswordHash, ThoughtId, UserId, Username, @@ -69,11 +69,7 @@ pub trait UserWriter: Send + Sync { async fn update_profile( &self, user_id: &UserId, - display_name: Option, - bio: Option, - avatar_url: Option, - header_url: Option, - custom_css: Option, + input: UpdateProfileInput, ) -> Result<(), DomainError>; } diff --git a/crates/domain/src/testing/mod.rs b/crates/domain/src/testing/mod.rs index a546a5f..cb13e59 100644 --- a/crates/domain/src/testing/mod.rs +++ b/crates/domain/src/testing/mod.rs @@ -10,7 +10,7 @@ use crate::{ tag::Tag, thought::Thought, top_friend::TopFriend, - user::User, + user::{UpdateProfileInput, User}, }, ports::*, value_objects::{ @@ -125,11 +125,7 @@ impl UserWriter for TestStore { async fn update_profile( &self, user_id: &UserId, - display_name: Option, - bio: Option, - avatar_url: Option, - header_url: Option, - custom_css: Option, + input: UpdateProfileInput, ) -> Result<(), DomainError> { if let Some(u) = self .users @@ -138,11 +134,11 @@ impl UserWriter for TestStore { .iter_mut() .find(|u| &u.id == user_id) { - u.display_name = display_name; - u.bio = bio; - u.avatar_url = avatar_url; - u.header_url = header_url; - u.custom_css = custom_css; + u.display_name = input.display_name; + u.bio = input.bio; + u.avatar_url = input.avatar_url; + u.header_url = input.header_url; + u.custom_css = input.custom_css; } Ok(()) } diff --git a/crates/presentation/src/handlers/users/mod.rs b/crates/presentation/src/handlers/users/mod.rs index 5671f15..e050e8b 100644 --- a/crates/presentation/src/handlers/users/mod.rs +++ b/crates/presentation/src/handlers/users/mod.rs @@ -17,8 +17,9 @@ use axum::{ response::{IntoResponse, Response}, Json, }; -use domain::ports::{ - EventPublisher, FederationActionPort, FollowRepository, SearchPort, UserRepository, +use domain::{ + models::user::UpdateProfileInput, + ports::{EventPublisher, FederationActionPort, FollowRepository, SearchPort, UserRepository}, }; use std::sync::Arc; @@ -96,11 +97,13 @@ pub async fn patch_profile( &*d.users, &*d.events, &uid, - body.display_name, - body.bio, - body.avatar_url, - body.header_url, - body.custom_css, + UpdateProfileInput { + display_name: body.display_name, + bio: body.bio, + avatar_url: body.avatar_url, + header_url: body.header_url, + custom_css: body.custom_css, + }, ) .await?; let user = fetch_user(&*d.users, &uid).await?; diff --git a/crates/presentation/src/testing.rs b/crates/presentation/src/testing.rs index a67dd8d..41e264e 100644 --- a/crates/presentation/src/testing.rs +++ b/crates/presentation/src/testing.rs @@ -68,14 +68,7 @@ impl ActivityPubRepository for NoOpApRepo { } async fn accept_note( &self, - _ap_id: &str, - _author_id: &UserId, - _content: &str, - _published: chrono::DateTime, - _sensitive: bool, - _content_warning: Option, - _visibility: &str, - _in_reply_to: Option<&str>, + _input: activitypub_base::AcceptNoteInput<'_>, ) -> Result { Ok(ThoughtId::from_uuid(uuid::Uuid::new_v4())) } diff --git a/crates/worker/src/factory.rs b/crates/worker/src/factory.rs index d3c9fbf..2c97261 100644 --- a/crates/worker/src/factory.rs +++ b/crates/worker/src/factory.rs @@ -40,7 +40,7 @@ pub async fn build(database_url: &str, base_url: &str, nats_url: &str) -> Worker // ActivityPub service (for federation fan-out) let ap_service = Arc::new( - ActivityPubService::new( + ActivityPubService::builder( Arc::new(PostgresFederationRepository::new(pool.clone())), Arc::new(PostgresApUserRepository::new( pool.clone(), @@ -52,13 +52,11 @@ pub async fn build(database_url: &str, base_url: &str, nats_url: &str) -> Worker None, Arc::new(postgres::tag::PgTagRepository::new(pool.clone())), )), - base_url.to_string(), - false, - "thoughts".to_string(), - false, - None, + base_url, Arc::new(PgRemoteActorConnectionRepository::new(pool.clone())), ) + .software_name("thoughts") + .build() .await .expect("ActivityPubService build failed"), );