Compare commits
4 commits
6a95b9bcbd
...
412c4cbc73
Author | SHA1 | Date | |
---|---|---|---|
412c4cbc73 | |||
4babf95810 | |||
8773036ec1 | |||
58073520c0 |
19 changed files with 861 additions and 8 deletions
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT \n html_url,\n private_key,\n public_key,\n actor_id,\n preferred_username\n FROM\n system_actors\n WHERE\n preferred_username = $1;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "html_url",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "private_key",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "public_key",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "actor_id",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "preferred_username",
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "22e49802eb938f79f7c79eb068e2ad2bbeb8333e2dab1947a59c1371adfec716"
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT \n html_url,\n private_key,\n public_key,\n actor_id,\n preferred_username\n FROM\n system_actors\n WHERE\n actor_id = $1;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "html_url",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "private_key",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "public_key",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "actor_id",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "preferred_username",
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "284d471aba6b703ca6dc1c733780ca140cbb720a85df7724d646916c0a317c81"
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT \n html_url,\n private_key,\n public_key,\n actor_id,\n preferred_username\n FROM\n system_actors\n WHERE\n html_url = $1;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "html_url",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "private_key",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "public_key",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "actor_id",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "preferred_username",
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "2f109a98ec4771efbc4e2b22acde35447eca3e2d32bf618c620e8284a5c3e4da"
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO system_actors \n (\n html_url,\n public_key,\n private_key,\n actor_id,\n preferred_username\n )\n VALUES\n ($1, $2, $3, $4, $5); ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "cf84713e9f31a02670a13e9f92f7e7bb1d025c80b30ce82730a9d83b258592e1"
|
||||||
|
}
|
14
migrations/20241022111256_remote_actor_send_queue.sql
Normal file
14
migrations/20241022111256_remote_actor_send_queue.sql
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS remote_actor_person_queue (
|
||||||
|
remote_actor_id INTEGER REFERENCES remote_actors (ID) NOT NULL,
|
||||||
|
person_id INTEGER REFERENCES persons (ID) NOT NULL,
|
||||||
|
UNIQUE(remote_actor_id, person_id),
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS remote_actor_repository_queue (
|
||||||
|
remote_actor_id INTEGER REFERENCES remote_actors (ID) NOT NULL,
|
||||||
|
repository_id INTEGER REFERENCES repositories (ID) NOT NULL,
|
||||||
|
UNIQUE(remote_actor_id, repository_id),
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
13
migrations/20241119123128_system_actors.sql
Normal file
13
migrations/20241119123128_system_actors.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS system_actors (
|
||||||
|
preferred_username TEXT NOT NULL UNIQUE,
|
||||||
|
html_url TEXT NOT NULL UNIQUE,
|
||||||
|
actor_id TEXT NOT NULL UNIQUE,
|
||||||
|
private_key TEXT NOT NULL UNIQUE,
|
||||||
|
public_key TEXT NOT NULL UNIQUE,
|
||||||
|
UNIQUE (preferred_username, html_url),
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS system_actors_html_url ON system_actors (html_url);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS system_actors_actor_id ON system_actors (actor_id);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS system_actors_preferred_username ON system_actors (preferred_username);
|
|
@ -16,6 +16,7 @@ pub mod types;
|
||||||
mod webfinger;
|
mod webfinger;
|
||||||
|
|
||||||
pub use errors::WebJsonRepsonse;
|
pub use errors::WebJsonRepsonse;
|
||||||
|
pub use routes::system_actor::*;
|
||||||
pub use routes::APObjectIDGenerator;
|
pub use routes::APObjectIDGenerator;
|
||||||
pub use routes::APObjectRoutes;
|
pub use routes::APObjectRoutes;
|
||||||
pub use routes::RoutesRepository;
|
pub use routes::RoutesRepository;
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub struct RoutesRepository {
|
||||||
pub person: PersonRoutes,
|
pub person: PersonRoutes,
|
||||||
pub repository: RepositoryRoutes,
|
pub repository: RepositoryRoutes,
|
||||||
pub ap_object: APObjectRoutes,
|
pub ap_object: APObjectRoutes,
|
||||||
|
pub system_actor: system_actor::SystemActorRoutes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RoutesRepository {
|
impl Default for RoutesRepository {
|
||||||
|
@ -25,6 +26,7 @@ impl Default for RoutesRepository {
|
||||||
person: PersonRoutes::default(),
|
person: PersonRoutes::default(),
|
||||||
repository: RepositoryRoutes::default(),
|
repository: RepositoryRoutes::default(),
|
||||||
ap_object: APObjectRoutes::default(),
|
ap_object: APObjectRoutes::default(),
|
||||||
|
system_actor: system_actor::SystemActorRoutes::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,3 +78,68 @@ impl GenerateObjectID for APObjectIDGenerator {
|
||||||
crate::utils::absolute_url::absolute_url(&self.s, &path)
|
crate::utils::absolute_url::absolute_url(&self.s, &path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) mod system_actor {
|
||||||
|
use super::*;
|
||||||
|
use crate::federation::domain::system_actor::GenerateSystemActorRoutes;
|
||||||
|
use crate::{settings::Settings, utils::forges::*};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct SystemActorRoutes {
|
||||||
|
actor: String,
|
||||||
|
inbox: String,
|
||||||
|
outbox: String,
|
||||||
|
followers: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SystemActorRoutes {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
actor: "/system/{username}".into(),
|
||||||
|
inbox: "/system/{username}/inbox".into(),
|
||||||
|
outbox: "/system/{username}/outbox".into(),
|
||||||
|
followers: "/system/{username}/followers".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemActorRoutes {
|
||||||
|
pub fn actor(&self, username: &str) -> String {
|
||||||
|
self.actor.replace("{username}", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inbox(&self, username: &str) -> String {
|
||||||
|
self.inbox.replace("{username}", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn outbox(&self, username: &str) -> String {
|
||||||
|
self.outbox.replace("{username}", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn followers(&self, username: &str) -> String {
|
||||||
|
self.followers.replace("{username}", username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemActorRouteGenerator {
|
||||||
|
s: crate::settings::Settings,
|
||||||
|
routes: SystemActorRoutes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemActorRouteGenerator {
|
||||||
|
pub fn new(s: crate::settings::Settings, routes: SystemActorRoutes) -> Self {
|
||||||
|
Self { s, routes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenerateSystemActorRoutes for SystemActorRouteGenerator {
|
||||||
|
fn generate_actor_id(&self, username: &str) -> Result<Url, url::ParseError> {
|
||||||
|
let path = self.routes.actor(username);
|
||||||
|
crate::utils::absolute_url::absolute_url(&self.s, &path)
|
||||||
|
}
|
||||||
|
fn generate_html_url(&self, username: &str) -> Result<Url, url::ParseError> {
|
||||||
|
let path = self.routes.actor(username);
|
||||||
|
crate::utils::absolute_url::absolute_url(&self.s, &path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ use crate::federation::application::port::out::forge::{
|
||||||
parse_username_from_url::FederationOutForgeParseUsernameFromUrlObj,
|
parse_username_from_url::FederationOutForgeParseUsernameFromUrlObj,
|
||||||
};
|
};
|
||||||
use crate::federation::application::services::cache_ap_local_object_service::CacheAPLocalObjectServiceObj;
|
use crate::federation::application::services::cache_ap_local_object_service::CacheAPLocalObjectServiceObj;
|
||||||
|
use crate::federation::application::services::get_system_actor_service::GetSystemActorServiceObj;
|
||||||
use crate::utils::data::Dependencies;
|
use crate::utils::data::Dependencies;
|
||||||
pub(super) use crate::utils::forges::forge_repository::ForgeRepositoryInterface;
|
pub(super) use crate::utils::forges::forge_repository::ForgeRepositoryInterface;
|
||||||
use activitypub_federation::config::FederationConfig;
|
use activitypub_federation::config::FederationConfig;
|
||||||
|
@ -57,6 +58,7 @@ pub type WebFederationOutForgePollPersonActivitiesObj =
|
||||||
pub type WebFederationRouteRepository = Data<Arc<RoutesRepository>>;
|
pub type WebFederationRouteRepository = Data<Arc<RoutesRepository>>;
|
||||||
|
|
||||||
pub type WebFederationCacheAPLocalObjectService = Data<CacheAPLocalObjectServiceObj>;
|
pub type WebFederationCacheAPLocalObjectService = Data<CacheAPLocalObjectServiceObj>;
|
||||||
|
pub type WebFederationGetSystemActorService = Data<GetSystemActorServiceObj>;
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct FData(pub Arc<Dependencies>);
|
pub struct FData(pub Arc<Dependencies>);
|
||||||
|
|
|
@ -22,8 +22,12 @@ use self::out::forge::forgejo::ForgejoBuilder;
|
||||||
use self::out::forge::github::GitHubBuilder;
|
use self::out::forge::github::GitHubBuilder;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
application::services::cache_ap_local_object_service::{
|
application::services::{
|
||||||
CacheAPLocalObjectService, CacheAPLocalObjectServiceBuilder,
|
cache_ap_local_object_service::{
|
||||||
|
CacheAPLocalObjectService, CacheAPLocalObjectServiceBuilder,
|
||||||
|
},
|
||||||
|
get_system_actor_service::*,
|
||||||
|
init_system_actor_service::*,
|
||||||
},
|
},
|
||||||
domain::*,
|
domain::*,
|
||||||
};
|
};
|
||||||
|
@ -49,6 +53,7 @@ struct Adapters {
|
||||||
forge_repository_interface: WebFederationForgeRepositoryInterface,
|
forge_repository_interface: WebFederationForgeRepositoryInterface,
|
||||||
|
|
||||||
cache_ap_local_obect_service: WebFederationCacheAPLocalObjectService,
|
cache_ap_local_obect_service: WebFederationCacheAPLocalObjectService,
|
||||||
|
get_system_actor_service: WebFederationGetSystemActorService,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Adapters {
|
impl Adapters {
|
||||||
|
@ -142,6 +147,34 @@ impl Adapters {
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let get_system_actor_service = {
|
||||||
|
let out_db_system_actor_adapter = Arc::new(db.clone());
|
||||||
|
|
||||||
|
let init_system_actor_service = {
|
||||||
|
let system_actor_routes_generator =
|
||||||
|
Arc::new(input::web::SystemActorRouteGenerator::new(
|
||||||
|
settings.clone(),
|
||||||
|
input::web::RoutesRepository::default().system_actor,
|
||||||
|
));
|
||||||
|
|
||||||
|
Arc::new(
|
||||||
|
InitSystemActorServiceBuilder::default()
|
||||||
|
.out_db_system_actor_adapter(out_db_system_actor_adapter.clone())
|
||||||
|
.system_actor_routes_generator(system_actor_routes_generator)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
WebFederationGetSystemActorService::new(Arc::new(
|
||||||
|
GetSystemActorServiceBuilder::default()
|
||||||
|
.out_db_system_actor_adapter(out_db_system_actor_adapter.clone())
|
||||||
|
.init_system_actor_service(init_system_actor_service.clone())
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
out_db_get_person_adapter,
|
out_db_get_person_adapter,
|
||||||
out_db_save_person_adapter,
|
out_db_save_person_adapter,
|
||||||
|
@ -159,18 +192,13 @@ impl Adapters {
|
||||||
forge_repository_interface,
|
forge_repository_interface,
|
||||||
|
|
||||||
cache_ap_local_obect_service,
|
cache_ap_local_obect_service,
|
||||||
|
get_system_actor_service,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn federation_config(pool: PgPool, settings: &settings::Settings) -> WebFederationConfig {
|
pub async fn federation_config(pool: PgPool, settings: &settings::Settings) -> WebFederationConfig {
|
||||||
let adapters = Adapters::new(pool, settings);
|
let adapters = Adapters::new(pool, settings);
|
||||||
let object_id_generator: FederationGenerateObjectID =
|
|
||||||
Arc::new(input::web::APObjectIDGenerator::new(
|
|
||||||
settings.clone(),
|
|
||||||
input::web::RoutesRepository::default().ap_object,
|
|
||||||
));
|
|
||||||
// let object_id_generator: fn(uuid: Uuid) -> Result<Url, url::ParseError> = object_id_generator.generate;
|
|
||||||
|
|
||||||
let mut data = Dependencies::default();
|
let mut data = Dependencies::default();
|
||||||
data.insert(adapters.out_db_get_person_adapter.clone());
|
data.insert(adapters.out_db_get_person_adapter.clone());
|
||||||
|
@ -196,6 +224,7 @@ pub async fn federation_config(pool: PgPool, settings: &settings::Settings) -> W
|
||||||
)));
|
)));
|
||||||
|
|
||||||
data.insert(adapters.cache_ap_local_obect_service.clone());
|
data.insert(adapters.cache_ap_local_obect_service.clone());
|
||||||
|
data.insert(adapters.get_system_actor_service.clone());
|
||||||
|
|
||||||
let data = FData(Arc::new(data));
|
let data = FData(Arc::new(data));
|
||||||
|
|
||||||
|
@ -242,6 +271,7 @@ pub fn load_adapters(
|
||||||
cfg.app_data(adapters.forge_repository_interface);
|
cfg.app_data(adapters.forge_repository_interface);
|
||||||
|
|
||||||
cfg.app_data(adapters.cache_ap_local_obect_service.clone());
|
cfg.app_data(adapters.cache_ap_local_obect_service.clone());
|
||||||
|
cfg.app_data(adapters.get_system_actor_service.clone());
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::new(f)
|
Box::new(f)
|
||||||
|
|
|
@ -19,6 +19,7 @@ mod person_followers;
|
||||||
mod poll_person_checkpoint;
|
mod poll_person_checkpoint;
|
||||||
mod repository_followers;
|
mod repository_followers;
|
||||||
mod save_remote_actor;
|
mod save_remote_actor;
|
||||||
|
mod system_actor;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DBOutPostgresAdapter {
|
pub struct DBOutPostgresAdapter {
|
||||||
|
|
195
src/federation/adapter/out/db/postgres/system_actor.rs
Normal file
195
src/federation/adapter/out/db/postgres/system_actor.rs
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use super::DBOutPostgresAdapter;
|
||||||
|
use crate::federation::application::port::out::db::{errors::*, system_actor::*};
|
||||||
|
use crate::federation::domain::{system_actor::*, *};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
struct InternalSystemActor {
|
||||||
|
html_url: String,
|
||||||
|
actor_id: String,
|
||||||
|
private_key: String,
|
||||||
|
public_key: String,
|
||||||
|
preferred_username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InternalSystemActor> for SystemActor {
|
||||||
|
fn from(v: InternalSystemActor) -> Self {
|
||||||
|
let keys = KeypairBuilder::default()
|
||||||
|
.private_key(Some(v.private_key))
|
||||||
|
.public_key(Some(v.public_key))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
SystemActorBuilder::default()
|
||||||
|
.preferred_username(v.preferred_username)
|
||||||
|
.html_url(Url::parse(&v.html_url).unwrap())
|
||||||
|
.actor_id(Url::parse(&v.actor_id).unwrap())
|
||||||
|
.keys(keys)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SystemActorPort for DBOutPostgresAdapter {
|
||||||
|
async fn create(&self, s: &SystemActor) -> FederationOutDBPortResult<()> {
|
||||||
|
if !s.keys().savable() {
|
||||||
|
log::error!("system actor is not savable: keypair doesn't exist");
|
||||||
|
return Err(FederationOutDBPortError::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO system_actors
|
||||||
|
(
|
||||||
|
html_url,
|
||||||
|
public_key,
|
||||||
|
private_key,
|
||||||
|
actor_id,
|
||||||
|
preferred_username
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4, $5); ",
|
||||||
|
s.html_url().as_str(),
|
||||||
|
s.keys().public_key().as_ref().unwrap(),
|
||||||
|
s.keys().private_key().as_ref().unwrap(),
|
||||||
|
s.actor_id().as_str(),
|
||||||
|
s.preferred_username()
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_from_actor_id(
|
||||||
|
&self,
|
||||||
|
actor_id: &Url,
|
||||||
|
) -> FederationOutDBPortResult<Option<SystemActor>> {
|
||||||
|
match sqlx::query_as!(
|
||||||
|
InternalSystemActor,
|
||||||
|
"SELECT
|
||||||
|
html_url,
|
||||||
|
private_key,
|
||||||
|
public_key,
|
||||||
|
actor_id,
|
||||||
|
preferred_username
|
||||||
|
FROM
|
||||||
|
system_actors
|
||||||
|
WHERE
|
||||||
|
actor_id = $1;",
|
||||||
|
actor_id.as_str(),
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => Ok(Some(res.into())),
|
||||||
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn get_from_html_url(
|
||||||
|
&self,
|
||||||
|
html_url: &Url,
|
||||||
|
) -> FederationOutDBPortResult<Option<SystemActor>> {
|
||||||
|
match sqlx::query_as!(
|
||||||
|
InternalSystemActor,
|
||||||
|
"SELECT
|
||||||
|
html_url,
|
||||||
|
private_key,
|
||||||
|
public_key,
|
||||||
|
actor_id,
|
||||||
|
preferred_username
|
||||||
|
FROM
|
||||||
|
system_actors
|
||||||
|
WHERE
|
||||||
|
html_url = $1;",
|
||||||
|
html_url.as_str(),
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => Ok(Some(res.into())),
|
||||||
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_from_preferred_username(
|
||||||
|
&self,
|
||||||
|
preferred_username: &str,
|
||||||
|
) -> FederationOutDBPortResult<Option<SystemActor>> {
|
||||||
|
match sqlx::query_as!(
|
||||||
|
InternalSystemActor,
|
||||||
|
"SELECT
|
||||||
|
html_url,
|
||||||
|
private_key,
|
||||||
|
public_key,
|
||||||
|
actor_id,
|
||||||
|
preferred_username
|
||||||
|
FROM
|
||||||
|
system_actors
|
||||||
|
WHERE
|
||||||
|
preferred_username = $1;",
|
||||||
|
preferred_username,
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => Ok(Some(res.into())),
|
||||||
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_postgres_system_actor() {
|
||||||
|
let settings = crate::settings::tests::get_settings().await;
|
||||||
|
let db = super::DBOutPostgresAdapter::new(
|
||||||
|
sqlx::postgres::PgPool::connect(&settings.database.url)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let mut sa = SystemActor::default();
|
||||||
|
|
||||||
|
assert!(db
|
||||||
|
.get_from_preferred_username(sa.preferred_username())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_none());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
db.create(&sa).await,
|
||||||
|
Err(FederationOutDBPortError::InternalError),
|
||||||
|
"keys aren't generated, so unsavable"
|
||||||
|
);
|
||||||
|
|
||||||
|
sa.generate_keys();
|
||||||
|
db.create(&sa).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
db.get_from_html_url(sa.html_url()).await.unwrap().unwrap(),
|
||||||
|
sa
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
db.get_from_preferred_username(sa.preferred_username())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
|
sa
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
db.get_from_actor_id(sa.actor_id()).await.unwrap().unwrap(),
|
||||||
|
sa
|
||||||
|
);
|
||||||
|
|
||||||
|
settings.drop_db().await;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,3 +15,4 @@ pub mod repository_followers;
|
||||||
pub mod save_person;
|
pub mod save_person;
|
||||||
pub mod save_remote_actor;
|
pub mod save_remote_actor;
|
||||||
pub mod save_repository;
|
pub mod save_repository;
|
||||||
|
pub mod system_actor;
|
||||||
|
|
86
src/federation/application/port/out/db/system_actor.rs
Normal file
86
src/federation/application/port/out/db/system_actor.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use mockall::predicate::*;
|
||||||
|
use mockall::*;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use super::errors::*;
|
||||||
|
use crate::federation::domain::system_actor::*;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
#[cfg(test)]
|
||||||
|
pub use tests::*;
|
||||||
|
|
||||||
|
#[automock]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait SystemActorPort: Send + Sync {
|
||||||
|
async fn create(&self, cmd: &SystemActor) -> FederationOutDBPortResult<()>;
|
||||||
|
async fn get_from_html_url(
|
||||||
|
&self,
|
||||||
|
html_url: &Url,
|
||||||
|
) -> FederationOutDBPortResult<Option<SystemActor>>;
|
||||||
|
async fn get_from_actor_id(
|
||||||
|
&self,
|
||||||
|
actor_id: &Url,
|
||||||
|
) -> FederationOutDBPortResult<Option<SystemActor>>;
|
||||||
|
async fn get_from_preferred_username(
|
||||||
|
&self,
|
||||||
|
preferred_username: &str,
|
||||||
|
) -> FederationOutDBPortResult<Option<SystemActor>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type FederationOutDBSystemActorObj = std::sync::Arc<dyn SystemActorPort>;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub fn mock_get_system_actor_from_preferred_username(
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> FederationOutDBSystemActorObj {
|
||||||
|
let mut m = MockSystemActorPort::new();
|
||||||
|
let mut system_actor = SystemActor::default();
|
||||||
|
system_actor.generate_keys();
|
||||||
|
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_get_from_preferred_username()
|
||||||
|
.times(times)
|
||||||
|
.return_const(Ok(Some(system_actor)));
|
||||||
|
} else {
|
||||||
|
m.expect_get_from_preferred_username()
|
||||||
|
.return_const(Ok(Some(system_actor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mock_get_system_actor_from_preferred_username_returns_none(
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> FederationOutDBSystemActorObj {
|
||||||
|
let mut m = MockSystemActorPort::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_get_from_preferred_username()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_| Ok(None));
|
||||||
|
} else {
|
||||||
|
m.expect_get_from_preferred_username()
|
||||||
|
.returning(|_| Ok(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mock_system_actor_create(times: Option<usize>) -> FederationOutDBSystemActorObj {
|
||||||
|
let mut m = MockSystemActorPort::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_create().times(times).returning(|_| Ok(()));
|
||||||
|
} else {
|
||||||
|
m.expect_create().returning(|_| Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 system_actor::SystemActorBuilder;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use super::errors::*;
|
||||||
|
use super::init_system_actor_service::*;
|
||||||
|
use crate::federation::application::port::out::db::system_actor::*;
|
||||||
|
use crate::federation::domain::{system_actor::*, *};
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait GetSystemActorUseCase: Send + Sync {
|
||||||
|
async fn get_system_actor(&self) -> FederationServiceResult<SystemActor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Builder, Clone)]
|
||||||
|
pub struct GetSystemActorService {
|
||||||
|
out_db_system_actor_adapter: FederationOutDBSystemActorObj,
|
||||||
|
init_system_actor_service: InitSystemActorServiceObj,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type GetSystemActorServiceObj = std::sync::Arc<dyn GetSystemActorUseCase>;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl GetSystemActorUseCase for GetSystemActorService {
|
||||||
|
async fn get_system_actor(&self) -> FederationServiceResult<SystemActor> {
|
||||||
|
let system_actor = match self
|
||||||
|
.out_db_system_actor_adapter
|
||||||
|
.get_from_preferred_username(SYSTEM_ACTOR_PREFERRED_USERNAME)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Some(system_actor) => system_actor,
|
||||||
|
None => self.init_system_actor_service.init_system_actor().await?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(system_actor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::tests::bdd::*;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_service_get_from_db() {
|
||||||
|
// retrieved from DB
|
||||||
|
let s = GetSystemActorServiceBuilder::default()
|
||||||
|
.out_db_system_actor_adapter(mock_get_system_actor_from_preferred_username(
|
||||||
|
IS_CALLED_ONLY_ONCE,
|
||||||
|
))
|
||||||
|
.init_system_actor_service(mock_init_system_actor_service(IS_NEVER_CALLED))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let resp = s.get_system_actor().await.unwrap();
|
||||||
|
assert_eq!(resp.preferred_username(), SYSTEM_ACTOR_PREFERRED_USERNAME);
|
||||||
|
assert!(resp.keys().savable());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_service_no_db_generate_and_save() {
|
||||||
|
// doesn't exist, so generating and saving
|
||||||
|
let s = GetSystemActorServiceBuilder::default()
|
||||||
|
.out_db_system_actor_adapter(
|
||||||
|
mock_get_system_actor_from_preferred_username_returns_none(IS_CALLED_ONLY_ONCE),
|
||||||
|
)
|
||||||
|
.init_system_actor_service(mock_init_system_actor_service(IS_CALLED_ONLY_ONCE))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let resp = s.get_system_actor().await.unwrap();
|
||||||
|
assert_eq!(resp.preferred_username(), SYSTEM_ACTOR_PREFERRED_USERNAME);
|
||||||
|
assert!(resp.keys().savable());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
//use actix_rt::(;
|
||||||
|
use derive_builder::Builder;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use mockall::predicate::*;
|
||||||
|
use mockall::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use system_actor::SystemActorBuilder;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
#[cfg(test)]
|
||||||
|
pub use tests::*;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use super::errors::*;
|
||||||
|
use crate::federation::application::port::out::db::system_actor::*;
|
||||||
|
use crate::federation::domain::{system_actor::*, *};
|
||||||
|
|
||||||
|
#[automock]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait InitSystemActorUseCase: Send + Sync {
|
||||||
|
async fn init_system_actor(&self) -> FederationServiceResult<SystemActor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type InitSystemActorServiceObj = std::sync::Arc<dyn InitSystemActorUseCase>;
|
||||||
|
|
||||||
|
#[derive(Builder, Clone)]
|
||||||
|
pub struct InitSystemActorService {
|
||||||
|
out_db_system_actor_adapter: FederationOutDBSystemActorObj,
|
||||||
|
system_actor_routes_generator: FederationGenerateSystemActorRoutes,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl InitSystemActorUseCase for InitSystemActorService {
|
||||||
|
async fn init_system_actor(&self) -> FederationServiceResult<SystemActor> {
|
||||||
|
let actor_id = self
|
||||||
|
.system_actor_routes_generator
|
||||||
|
.generate_actor_id(SYSTEM_ACTOR_PREFERRED_USERNAME)
|
||||||
|
.unwrap();
|
||||||
|
let html_url = self
|
||||||
|
.system_actor_routes_generator
|
||||||
|
.generate_html_url(SYSTEM_ACTOR_PREFERRED_USERNAME)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut system_actor = SystemActorBuilder::default()
|
||||||
|
.preferred_username(SYSTEM_ACTOR_PREFERRED_USERNAME.into())
|
||||||
|
.html_url(html_url)
|
||||||
|
.actor_id(actor_id)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
system_actor.generate_keys();
|
||||||
|
|
||||||
|
self.out_db_system_actor_adapter
|
||||||
|
.create(&system_actor)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(system_actor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::tests::bdd::*;
|
||||||
|
|
||||||
|
pub fn mock_init_system_actor_service(times: Option<usize>) -> InitSystemActorServiceObj {
|
||||||
|
let mut m = MockInitSystemActorUseCase::new();
|
||||||
|
let mut system_actor = SystemActor::default();
|
||||||
|
system_actor.generate_keys();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_init_system_actor()
|
||||||
|
.times(times)
|
||||||
|
.return_const(Ok(system_actor));
|
||||||
|
} else {
|
||||||
|
m.expect_init_system_actor().return_const(Ok(system_actor));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sync::Arc::new(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_service() {
|
||||||
|
let s = InitSystemActorServiceBuilder::default()
|
||||||
|
.out_db_system_actor_adapter(mock_system_actor_create(IS_CALLED_ONLY_ONCE))
|
||||||
|
.system_actor_routes_generator(mock_generate_system_actor_routes(IS_CALLED_ONLY_ONCE))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let resp = s.init_system_actor().await.unwrap();
|
||||||
|
assert!(resp.keys().savable());
|
||||||
|
assert_eq!(resp.preferred_username(), SYSTEM_ACTOR_PREFERRED_USERNAME);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ pub mod get_person_follower_list_service;
|
||||||
pub mod get_person_from_follow_init_activity_id;
|
pub mod get_person_from_follow_init_activity_id;
|
||||||
pub mod get_remote_actor_service;
|
pub mod get_remote_actor_service;
|
||||||
pub mod get_repository_from_follow_init_activity_id;
|
pub mod get_repository_from_follow_init_activity_id;
|
||||||
|
pub mod get_system_actor_service;
|
||||||
|
pub mod init_system_actor_service;
|
||||||
pub mod poll_person_service;
|
pub mod poll_person_service;
|
||||||
pub mod resolve_actor_type_service;
|
pub mod resolve_actor_type_service;
|
||||||
pub mod save_remote_actor_service;
|
pub mod save_remote_actor_service;
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub mod follow_activity;
|
||||||
pub mod follow_repository;
|
pub mod follow_repository;
|
||||||
pub mod followers;
|
pub mod followers;
|
||||||
pub mod remote_actor;
|
pub mod remote_actor;
|
||||||
|
pub mod system_actor;
|
||||||
pub mod visibility;
|
pub mod visibility;
|
||||||
|
|
||||||
// aggregates
|
// aggregates
|
||||||
|
@ -42,6 +43,8 @@ pub enum SupportedActorType {
|
||||||
Patch,
|
Patch,
|
||||||
#[display(fmt = "team")]
|
#[display(fmt = "team")]
|
||||||
Team,
|
Team,
|
||||||
|
#[display(fmt = "system_actor")]
|
||||||
|
SystemActor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Display, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Display, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -59,6 +62,7 @@ impl FromStr for SupportedActorType {
|
||||||
"ticket" => Ok(SupportedActorType::Ticket),
|
"ticket" => Ok(SupportedActorType::Ticket),
|
||||||
"patch" => Ok(SupportedActorType::Patch),
|
"patch" => Ok(SupportedActorType::Patch),
|
||||||
"team" => Ok(SupportedActorType::Team),
|
"team" => Ok(SupportedActorType::Team),
|
||||||
|
"system_actor" => Ok(SupportedActorType::SystemActor),
|
||||||
_ => Err(SupportedActorError::UnsupportedActor),
|
_ => Err(SupportedActorError::UnsupportedActor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,6 +293,7 @@ pub mod tests {
|
||||||
assert_eq!("ticket", SupportedActorType::Ticket.to_string());
|
assert_eq!("ticket", SupportedActorType::Ticket.to_string());
|
||||||
assert_eq!("patch", SupportedActorType::Patch.to_string());
|
assert_eq!("patch", SupportedActorType::Patch.to_string());
|
||||||
assert_eq!("team", SupportedActorType::Team.to_string());
|
assert_eq!("team", SupportedActorType::Team.to_string());
|
||||||
|
assert_eq!("system_actor", SupportedActorType::SystemActor.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mock_cachable(times: Option<usize>) -> Box<dyn Cachable> {
|
pub fn mock_cachable(times: Option<usize>) -> Box<dyn Cachable> {
|
||||||
|
|
103
src/federation/domain/system_actor.rs
Normal file
103
src/federation/domain/system_actor.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// 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 derive_more::Display;
|
||||||
|
use mockall::predicate::*;
|
||||||
|
use mockall::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
#[cfg(test)]
|
||||||
|
pub use tests::*;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use super::{Keypair, SupportedActorType, WebFingerable};
|
||||||
|
|
||||||
|
pub const SYSTEM_ACTOR_PREFERRED_USERNAME: &str = "system_actor";
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Builder, Clone, Getters, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
|
pub struct SystemActor {
|
||||||
|
preferred_username: String,
|
||||||
|
actor_id: Url,
|
||||||
|
html_url: Url,
|
||||||
|
#[builder(default)]
|
||||||
|
keys: Keypair,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SystemActor {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
preferred_username: SYSTEM_ACTOR_PREFERRED_USERNAME.into(),
|
||||||
|
html_url: Url::parse(&format!(
|
||||||
|
"https://example.com/{SYSTEM_ACTOR_PREFERRED_USERNAME}"
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
actor_id: Url::parse(&format!(
|
||||||
|
"https://forgeflux.example.com/system/{SYSTEM_ACTOR_PREFERRED_USERNAME}"
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
keys: Keypair::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemActor {
|
||||||
|
pub fn generate_keys(&mut self) {
|
||||||
|
self.keys.generate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebFingerable for SystemActor {
|
||||||
|
fn username(&self) -> String {
|
||||||
|
self.preferred_username().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn html_page(&self) -> &Url {
|
||||||
|
self.html_url()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn actor_type(&self) -> SupportedActorType {
|
||||||
|
SupportedActorType::SystemActor
|
||||||
|
}
|
||||||
|
|
||||||
|
fn profile_photo(&self) -> Option<&Url> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automock]
|
||||||
|
pub trait GenerateSystemActorRoutes: Send + Sync {
|
||||||
|
fn generate_actor_id(&self, username: &str) -> Result<Url, url::ParseError>;
|
||||||
|
fn generate_html_url(&self, username: &str) -> Result<Url, url::ParseError>;
|
||||||
|
}
|
||||||
|
pub type FederationGenerateSystemActorRoutes = std::sync::Arc<dyn GenerateSystemActorRoutes>;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn mock_generate_system_actor_routes(
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> FederationGenerateSystemActorRoutes {
|
||||||
|
let mut m = MockGenerateSystemActorRoutes::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_generate_actor_id()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_| Ok(Url::parse("https://mock_obj_id.example.com").unwrap()));
|
||||||
|
m.expect_generate_html_url()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_| Ok(Url::parse("https://mock_obj_id.example.com").unwrap()));
|
||||||
|
} else {
|
||||||
|
m.expect_generate_actor_id()
|
||||||
|
.returning(|_| Ok(Url::parse("https://mock_obj_id.example.com").unwrap()));
|
||||||
|
m.expect_generate_html_url()
|
||||||
|
.returning(|_| Ok(Url::parse("https://mock_obj_id.example.com").unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sync::Arc::new(m)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue