feat: unfollow repository (stub), resolve actor in Undo, and hook Undo in Repository inbox handler #88

Merged
realaravinth merged 1 commit from resolve-undo-activity into master 2024-09-12 15:43:22 +05:30
5 changed files with 143 additions and 12 deletions

View file

@ -14,11 +14,11 @@ mod outbox;
pub mod routes;
mod utils;
mod accept_activity;
mod delete_activity;
pub mod accept_activity;
pub mod delete_activity;
mod follow_activity;
mod remote_actor;
mod undo_activity;
pub mod undo_activity;
pub use super::errors::WebJsonRepsonse;

View file

@ -12,8 +12,12 @@ use serde::{Deserialize, Serialize};
use url::Url;
use crate::federation::adapter::input::web::types::*;
use crate::federation::application::services::errors::FederationServiceError;
use crate::federation::application::services::unfollow_person_service::*;
use crate::federation::application::services::{
errors::*,
unfollow_person_service::{command::*, *},
unfollow_repository_service::{command::*, *},
};
use crate::federation::domain::*;
#[derive(Clone, Getters, Builder, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
@ -49,6 +53,34 @@ impl ActivityHandler for Undo {
async fn receive(self, data: &APData<Self::DataType>) -> Result<(), Self::Error> {
// NOTE: Currently, only Undo supported activity is following. Improve resolving underlying
// actor, as new stuff is implemented
self.unfollow_person(data).await?;
self.unfollow_repository(data).await?;
Ok(())
}
}
impl Undo {
async fn unfollow_repository(&self, data: &APData<FData>) -> FederationServiceResult<()> {
let out_db_repository_followers_adapter = data
.get::<WebFederationOutDBRepositoryFollowersObj>()
.unwrap()
.clone();
let s = UnfollowRepositoryServiceBuilder::default()
.out_db_repository_followers_adapter(
out_db_repository_followers_adapter.as_ref().clone(),
)
.build()
.unwrap();
s.unfollow_repository(UnfollowRepositoryCommand::new_command(
self.actor().clone(),
self.object().clone(),
))
.await?;
Ok(())
}
async fn unfollow_person(&self, data: &APData<FData>) -> FederationServiceResult<()> {
let out_db_person_followers_adapter = data
.get::<WebFederationOutDBPersonFollowersObj>()
.unwrap()
@ -58,12 +90,11 @@ impl ActivityHandler for Undo {
.out_db_person_followers_adapter(out_db_person_followers_adapter.as_ref().clone())
.build()
.unwrap();
s.unfollow_person(command::UnfollowPersonCommand::new_command(
s.unfollow_person(UnfollowPersonCommand::new_command(
self.actor().clone(),
self.object().clone(),
))
.await?;
Ok(())
}
}
@ -76,8 +107,11 @@ mod tests {
use super::*;
use crate::{
federation::application::port::out::db::person_followers::*,
tests::bdd::IS_CALLED_ONLY_ONCE, utils::data::Dependencies,
federation::application::port::out::db::{
person_followers::*, repository_followers::mock_follow_repository_delete,
},
tests::bdd::IS_CALLED_ONLY_ONCE,
utils::data::Dependencies,
};
fn get_undo() -> Undo {
@ -86,13 +120,24 @@ mod tests {
id: Url::parse("https://example.com/actor#undo").unwrap(),
actor: Url::parse("https://example.com/actor").unwrap(),
object: Url::parse("https://example.com/actor").unwrap(),
object: Url::parse("https://example.com/activity").unwrap(),
}
}
#[actix_rt::test]
async fn test_undo_activity_verify() {
let settings = crate::settings::Settings::new().unwrap();
let mut settings = crate::settings::Settings::new().unwrap();
{
let p = Person::default();
let mut forge_url = p.html_url().clone();
forge_url.set_path("");
settings.forges.forgejo.url = forge_url.clone();
settings.forges.github.url = forge_url.clone();
settings.server.domain = p.actor_id().host_str().unwrap().to_owned();
}
let data = FederationConfig::builder()
.domain(settings.server.domain.clone())
.app_data(FData(Arc::new(Dependencies::new())))
@ -147,13 +192,17 @@ mod tests {
}
#[actix_rt::test]
async fn test_undo_activity_receive() {
async fn test_undo_activity_receive_unfollow_person_repository() {
let settings = crate::settings::Settings::new().unwrap();
let mut deps = Dependencies::new();
deps.insert(WebFederationOutDBPersonFollowersObj::new(
mock_follow_person_delete(IS_CALLED_ONLY_ONCE),
));
deps.insert(WebFederationOutDBRepositoryFollowersObj::new(
mock_follow_repository_delete(IS_CALLED_ONLY_ONCE),
));
let data = FederationConfig::builder()
.domain(settings.server.domain.clone())
.app_data(FData(Arc::new(deps)))

View file

@ -72,6 +72,7 @@ pub struct ForgeFedRepository {
#[enum_delegate::implement(ActivityHandler)]
pub enum RepositoryAcceptedActivities {
Follow(crate::federation::domain::follow_activity::Follow),
Undo(crate::federation::adapter::input::web::person::undo_activity::Undo),
}
impl ForgeFedRepositoryBuilder {

View file

@ -21,3 +21,4 @@ pub mod get_remote_actor_service;
pub mod resolve_actor_type_service;
pub mod save_remote_actor_service;
pub mod unfollow_person_service;
pub mod unfollow_repository_service;

View file

@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use derive_builder::Builder;
use derive_getters::Getters;
use serde::{Deserialize, Serialize};
use url::Url;
use super::errors::*;
use crate::federation::application::port::out::db::repository_followers::*;
pub mod command {
use super::*;
#[derive(Debug, Getters, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct UnfollowRepositoryCommand {
follower: Url,
init_activity_id: Url,
}
impl UnfollowRepositoryCommand {
pub fn new_command(follower: Url, init_activity_id: Url) -> Self {
Self {
follower,
init_activity_id,
}
}
}
}
#[async_trait::async_trait]
pub trait UnfollowRepositoryUseCase: Send + Sync {
async fn unfollow_repository(
&self,
cmd: command::UnfollowRepositoryCommand,
) -> FederationServiceResult<()>;
}
#[derive(Builder, Clone)]
pub struct UnfollowRepositoryService {
out_db_repository_followers_adapter: FederationOutDBRepositoryFollowersObj,
}
#[async_trait::async_trait]
impl UnfollowRepositoryUseCase for UnfollowRepositoryService {
async fn unfollow_repository(
&self,
cmd: command::UnfollowRepositoryCommand,
) -> FederationServiceResult<()> {
self.out_db_repository_followers_adapter
.delete(cmd.follower(), cmd.init_activity_id())
.await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::bdd::*;
#[actix_rt::test]
async fn test_service() {
let follower_url = Url::parse("https://git.example.com/foo").unwrap();
let init_activity_id = Url::parse("https://ap.example.com/bar").unwrap();
let cmd = command::UnfollowRepositoryCommand::new_command(
follower_url.clone(),
init_activity_id.clone(),
);
// retrieved from DB
let s = UnfollowRepositoryServiceBuilder::default()
.out_db_repository_followers_adapter(mock_follow_repository_delete(IS_CALLED_ONLY_ONCE))
.build()
.unwrap();
s.unfollow_repository(cmd.clone()).await.unwrap();
}
}