feat: load FederationConfig with custom dependency injector utility

This commit is contained in:
Aravinth Manivannan 2024-06-07 15:15:36 +05:30
parent 1b557e045d
commit 2fb54938f6
Signed by: realaravinth
GPG key ID: F8F50389936984FF
7 changed files with 420 additions and 65 deletions

1
Cargo.lock generated
View file

@ -1235,6 +1235,7 @@ dependencies = [
"derive-getters", "derive-getters",
"derive_builder", "derive_builder",
"derive_more", "derive_more",
"enum_delegate",
"lazy_static", "lazy_static",
"log", "log",
"mockall", "mockall",

View file

@ -35,6 +35,7 @@ actix-identity = "0.7.1"
derive_builder = "0.20.0" derive_builder = "0.20.0"
derive-getters = "0.4.0" derive-getters = "0.4.0"
activitypub_federation = "0.5.6" activitypub_federation = "0.5.6"
enum_delegate = "0.2.0"
[dev-dependencies] [dev-dependencies]

View file

@ -8,7 +8,9 @@ use actix_web::web::Data;
use super::RoutesRepository; use super::RoutesRepository;
use crate::federation::adapter::out::forge::forge_factory::FederationForgeAdapterFactoryInterface; use crate::federation::adapter::out::forge::forge_factory::FederationForgeAdapterFactoryInterface;
use crate::federation::application::port::out::db::{get_person::*, save_person::*}; use crate::federation::application::port::out::db::{get_person::*, save_person::*};
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;
pub type WebFederationOutDBGetPersonObj = Data<FederationOutDBGetPersonObj>; pub type WebFederationOutDBGetPersonObj = Data<FederationOutDBGetPersonObj>;
pub type WebFederationOutDBSavePersonObj = Data<FederationOutDBSavePersonObj>; pub type WebFederationOutDBSavePersonObj = Data<FederationOutDBSavePersonObj>;
@ -16,3 +18,15 @@ pub type WebFederationOutDBSavePersonObj = Data<FederationOutDBSavePersonObj>;
pub type WebFederationForgeRepositoryInterface = pub type WebFederationForgeRepositoryInterface =
Data<Arc<dyn ForgeRepositoryInterface<Arc<dyn FederationForgeAdapterFactoryInterface>>>>; Data<Arc<dyn ForgeRepositoryInterface<Arc<dyn FederationForgeAdapterFactoryInterface>>>>;
pub type WebFederationRouteRepository = Data<Arc<RoutesRepository>>; pub type WebFederationRouteRepository = Data<Arc<RoutesRepository>>;
#[derive(Default, Clone)]
pub struct FData(pub Arc<Dependencies>);
impl std::ops::Deref for FData {
type Target = Dependencies;
fn deref<'a>(&'a self) -> &'a Self::Target {
&self.0
}
}
pub type WebFederationConfig = Data<Arc<FederationConfig<FData>>>;

View file

@ -4,6 +4,7 @@
use std::sync::Arc; use std::sync::Arc;
use activitypub_federation::config::FederationConfig;
use actix_web::web; use actix_web::web;
use sqlx::postgres::PgPool; use sqlx::postgres::PgPool;
@ -11,19 +12,25 @@ pub mod input;
pub mod out; pub mod out;
use crate::settings; use crate::settings;
use crate::utils::data::Dependencies;
use crate::utils::forges::*;
use input::web::types::*; use input::web::types::*;
use out::db::postgres::DBOutPostgresAdapter; use out::db::postgres::DBOutPostgresAdapter;
use self::out::forge::forge_factory::*; use self::out::forge::forge_factory::*;
use self::out::forge::forgejo::ForgejoBuilder; use self::out::forge::forgejo::ForgejoBuilder;
use self::out::forge::github::GitHubBuilder; use self::out::forge::github::GitHubBuilder;
use crate::utils::forges::*;
//use out::forge::{forge_repository::ForgeRepository, forgejo::Forgejo, github::Github}; //use out::forge::{forge_repository::ForgeRepository, forgejo::Forgejo, github::Github};
pub fn load_adapters( #[derive(Clone)]
pool: PgPool, struct Adapters {
settings: &settings::Settings, out_db_get_person_adapter: WebFederationOutDBGetPersonObj,
) -> impl FnOnce(&mut web::ServiceConfig) { out_db_save_person_adapter: WebFederationOutDBSavePersonObj,
forge_repository_interface: WebFederationForgeRepositoryInterface,
}
impl Adapters {
fn new(pool: PgPool, settings: &settings::Settings) -> Self {
let db = DBOutPostgresAdapter::new(pool); let db = DBOutPostgresAdapter::new(pool);
let out_db_get_person_adapter = WebFederationOutDBGetPersonObj::new(Arc::new(db.clone())); let out_db_get_person_adapter = WebFederationOutDBGetPersonObj::new(Arc::new(db.clone()));
let out_db_save_person_adapter = WebFederationOutDBSavePersonObj::new(Arc::new(db.clone())); let out_db_save_person_adapter = WebFederationOutDBSavePersonObj::new(Arc::new(db.clone()));
@ -34,6 +41,8 @@ pub fn load_adapters(
.url(settings.forges.forgejo.url.clone()) .url(settings.forges.forgejo.url.clone())
.client_id(settings.forges.forgejo.client_id.clone()) .client_id(settings.forges.forgejo.client_id.clone())
.client_secret(settings.forges.forgejo.client_secret.clone()) .client_secret(settings.forges.forgejo.client_secret.clone())
.settings(settings.clone())
.routes(input::web::RoutesRepository::default())
.username(settings.forges.forgejo.user.username.clone()) .username(settings.forges.forgejo.user.username.clone())
.api_token(settings.forges.forgejo.user.api_token.clone()) .api_token(settings.forges.forgejo.user.api_token.clone())
.build() .build()
@ -46,51 +55,72 @@ pub fn load_adapters(
.url(settings.forges.github.url.clone()) .url(settings.forges.github.url.clone())
.api_url(settings.forges.github.api_url.clone()) .api_url(settings.forges.github.api_url.clone())
.client_id(settings.forges.github.client_id.clone()) .client_id(settings.forges.github.client_id.clone())
.settings(settings.clone())
.routes(input::web::RoutesRepository::default())
.client_secret(settings.forges.github.client_secret.clone()) .client_secret(settings.forges.github.client_secret.clone())
.username(settings.forges.github.user.username.clone()) .username(settings.forges.github.user.username.clone())
.personal_access_token(settings.forges.github.user.personal_access_token.clone()) .personal_access_token(
settings.forges.github.user.personal_access_token.clone(),
)
.build() .build()
.unwrap(), .unwrap(),
)); ));
// let forgejo = Forgejo::new(
// settings.forges.forgejo.url.clone(),
// settings.forges.forgejo.client_id.clone(),
// settings.forges.forgejo.client_secret.clone(),
// refresh_access_token_input_adapter.clone(),
// );
//
// let github = Github::new(
// settings.forges.github.url.clone(),
// settings.forges.github.api_url.clone(),
// settings.forges.github.client_id.clone(),
// settings.forges.github.client_secret.clone(),
// refresh_access_token_input_adapter.clone(),
// );
//
let forge_repository_interface = { let forge_repository_interface = {
let mut f = ForgeRepository::new(); let mut f = ForgeRepository::new();
f.add_forge(SupportedForges::Forgejo, forgejo); f.add_forge(SupportedForges::Forgejo, forgejo);
f.add_forge(SupportedForges::Github, github); f.add_forge(SupportedForges::Github, github);
// f.add_forge(SupportedForges::Github, github); let f: Arc<
let f: Arc<dyn ForgeRepositoryInterface<Arc<dyn FederationForgeAdapterFactoryInterface>>> = dyn ForgeRepositoryInterface<Arc<dyn FederationForgeAdapterFactoryInterface>>,
Arc::new(f); > = Arc::new(f);
WebFederationForgeRepositoryInterface::new(f) WebFederationForgeRepositoryInterface::new(f)
}; };
Self {
out_db_get_person_adapter,
out_db_save_person_adapter,
forge_repository_interface,
}
}
}
pub async fn federation_config(pool: PgPool, settings: &settings::Settings) -> WebFederationConfig {
let adapters = Adapters::new(pool, &settings);
//let mut data = FData::default();
let mut data = Dependencies::default();
data.insert(adapters.out_db_get_person_adapter.clone());
data.insert(adapters.out_db_save_person_adapter.clone());
data.insert(adapters.forge_repository_interface.clone());
data.insert(settings.clone());
data.insert(input::web::RoutesRepository::default());
data.insert(WebFederationRouteRepository::new(Arc::new(
input::web::RoutesRepository::default(),
)));
let data = FData(Arc::new(data));
actix_web::web::Data::new(Arc::new(
activitypub_federation::config::FederationConfig::builder()
.domain(settings.server.domain.clone())
.app_data(data.clone())
.build()
.await
.unwrap(),
))
}
pub fn load_adapters(
pool: PgPool,
settings: &settings::Settings,
) -> impl FnOnce(&mut web::ServiceConfig) {
let adapters = Adapters::new(pool, settings);
let f = move |cfg: &mut web::ServiceConfig| { let f = move |cfg: &mut web::ServiceConfig| {
cfg.configure(input::web::load_ctx()); cfg.configure(input::web::load_ctx());
cfg.app_data(out_db_get_person_adapter); cfg.app_data(adapters.out_db_get_person_adapter);
cfg.app_data(out_db_save_person_adapter); cfg.app_data(adapters.out_db_save_person_adapter);
cfg.app_data(forge_repository_interface); cfg.app_data(adapters.forge_repository_interface);
// cfg.app_data(forge_repository_interface);
//
// cfg.app_data(web::Data::new(refresh_access_token_input_adapter));
//
// cfg.app_data(s);
//
}; };
Box::new(f) Box::new(f)

View file

@ -44,6 +44,9 @@ async fn main() {
socket_addr, socket_addr,
settings.server.domain settings.server.domain
); );
let s = settings.clone();
let federation_config = federation::adapter::federation_config(db.pool.clone(), &s).await;
HttpServer::new(move || { HttpServer::new(move || {
App::new() App::new()
.wrap(IdentityMiddleware::default()) .wrap(IdentityMiddleware::default())
@ -62,6 +65,8 @@ async fn main() {
db.pool.clone(), db.pool.clone(),
&settings, &settings,
)) ))
// .app_data(utils::data::Extensions::default())
.app_data(federation_config.clone())
.configure(utils::random_string::GenerateRandomString::inject()) .configure(utils::random_string::GenerateRandomString::inject())
}) })
.bind(&socket_addr) .bind(&socket_addr)

303
src/utils/data.rs Normal file
View file

@ -0,0 +1,303 @@
#![allow(dead_code)]
use std::{
any::{Any, TypeId},
collections::HashMap,
fmt,
hash::{BuildHasherDefault, Hasher},
};
/// A hasher for `TypeId`s that takes advantage of its known characteristics.
///
/// Author of `anymap` crate has done research on the topic:
/// https://github.com/chris-morgan/anymap/blob/2e9a5704/src/lib.rs#L599
#[derive(Debug, Default)]
struct NoOpHasher(u64);
impl Hasher for NoOpHasher {
fn write(&mut self, _bytes: &[u8]) {
unimplemented!("This NoOpHasher can only handle u64s")
}
fn write_u64(&mut self, i: u64) {
self.0 = i;
}
fn finish(&self) -> u64 {
self.0
}
}
/// A type map for request depedencies.
///
/// All entries into this map must be owned types (or static references).
#[derive(Default)]
pub struct Dependencies {
/// Use AHasher with a std HashMap with for faster lookups on the small `TypeId` keys.
map: HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<NoOpHasher>>,
}
impl Dependencies {
/// Creates an empty `Dependencies`.
#[inline]
pub fn new() -> Dependencies {
Dependencies {
map: HashMap::default(),
}
}
/// Insert an item into the map.
///
/// If an item of this type was already stored, it will be replaced and returned.
///
/// ```
/// # use actix_http::Dependencies;
/// let mut map = Dependencies::new();
/// assert_eq!(map.insert(""), None);
/// assert_eq!(map.insert(1u32), None);
/// assert_eq!(map.insert(2u32), Some(1u32));
/// assert_eq!(*map.get::<u32>().unwrap(), 2u32);
/// ```
pub fn insert<T: 'static + Send + Sync>(&mut self, val: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(downcast_owned)
}
/// Check if map contains an item of a given type.
///
/// ```
/// # use actix_http::Dependencies;
/// let mut map = Dependencies::new();
/// assert!(!map.contains::<u32>());
///
/// assert_eq!(map.insert(1u32), None);
/// assert!(map.contains::<u32>());
/// ```
pub fn contains<T: 'static>(&self) -> bool {
self.map.contains_key(&TypeId::of::<T>())
}
/// Get a reference to an item of a given type.
///
/// ```
/// # use actix_http::Dependencies;
/// let mut map = Dependencies::new();
/// map.insert(1u32);
/// assert_eq!(map.get::<u32>(), Some(&1u32));
/// ```
pub fn get<T: 'static>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_ref())
}
/// Get a mutable reference to an item of a given type.
///
/// ```
/// # use actix_http::Dependencies;
/// let mut map = Dependencies::new();
/// map.insert(1u32);
/// assert_eq!(map.get_mut::<u32>(), Some(&mut 1u32));
/// ```
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.map
.get_mut(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_mut())
}
/// Remove an item from the map of a given type.
///
/// If an item of this type was already stored, it will be returned.
///
/// ```
/// # use actix_http::Dependencies;
/// let mut map = Dependencies::new();
///
/// map.insert(1u32);
/// assert_eq!(map.get::<u32>(), Some(&1u32));
///
/// assert_eq!(map.remove::<u32>(), Some(1u32));
/// assert!(!map.contains::<u32>());
/// ```
pub fn remove<T: 'static>(&mut self) -> Option<T> {
self.map.remove(&TypeId::of::<T>()).and_then(downcast_owned)
}
/// Clear the `Dependencies` of all inserted depedencies.
///
/// ```
/// # use actix_http::Dependencies;
/// let mut map = Dependencies::new();
///
/// map.insert(1u32);
/// assert!(map.contains::<u32>());
///
/// map.clear();
/// assert!(!map.contains::<u32>());
/// ```
#[inline]
pub fn clear(&mut self) {
self.map.clear();
}
/// Extends self with the items from another `Dependencies`.
pub fn extend(&mut self, other: Dependencies) {
self.map.extend(other.map);
}
}
impl fmt::Debug for Dependencies {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dependencies").finish()
}
}
fn downcast_owned<T: 'static>(boxed: Box<dyn Any + Send + Sync>) -> Option<T> {
boxed.downcast().ok().map(|boxed| *boxed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_remove() {
let mut map = Dependencies::new();
map.insert::<i8>(123);
assert!(map.get::<i8>().is_some());
map.remove::<i8>();
assert!(map.get::<i8>().is_none());
}
#[test]
fn test_clear() {
let mut map = Dependencies::new();
map.insert::<i8>(8);
map.insert::<i16>(16);
map.insert::<i32>(32);
assert!(map.contains::<i8>());
assert!(map.contains::<i16>());
assert!(map.contains::<i32>());
map.clear();
assert!(!map.contains::<i8>());
assert!(!map.contains::<i16>());
assert!(!map.contains::<i32>());
map.insert::<i8>(10);
assert_eq!(*map.get::<i8>().unwrap(), 10);
}
#[test]
fn test_integers() {
static A: u32 = 8;
let mut map = Dependencies::new();
map.insert::<i8>(8);
map.insert::<i16>(16);
map.insert::<i32>(32);
map.insert::<i64>(64);
map.insert::<i128>(128);
map.insert::<u8>(8);
map.insert::<u16>(16);
map.insert::<u32>(32);
map.insert::<u64>(64);
map.insert::<u128>(128);
map.insert::<&'static u32>(&A);
assert!(map.get::<i8>().is_some());
assert!(map.get::<i16>().is_some());
assert!(map.get::<i32>().is_some());
assert!(map.get::<i64>().is_some());
assert!(map.get::<i128>().is_some());
assert!(map.get::<u8>().is_some());
assert!(map.get::<u16>().is_some());
assert!(map.get::<u32>().is_some());
assert!(map.get::<u64>().is_some());
assert!(map.get::<u128>().is_some());
assert!(map.get::<&'static u32>().is_some());
}
#[test]
fn test_composition() {
struct Magi<T>(pub T);
struct Madoka {
pub god: bool,
}
struct Homura {
pub attempts: usize,
}
struct Mami {
pub guns: usize,
}
let mut map = Dependencies::new();
map.insert(Magi(Madoka { god: false }));
map.insert(Magi(Homura { attempts: 0 }));
map.insert(Magi(Mami { guns: 999 }));
assert!(!map.get::<Magi<Madoka>>().unwrap().0.god);
assert_eq!(0, map.get::<Magi<Homura>>().unwrap().0.attempts);
assert_eq!(999, map.get::<Magi<Mami>>().unwrap().0.guns);
}
#[test]
fn test_depedencies() {
#[derive(Debug, PartialEq)]
struct MyType(i32);
let mut depedencies = Dependencies::new();
depedencies.insert(5i32);
depedencies.insert(MyType(10));
assert_eq!(depedencies.get(), Some(&5i32));
assert_eq!(depedencies.get_mut(), Some(&mut 5i32));
assert_eq!(depedencies.remove::<i32>(), Some(5i32));
assert!(depedencies.get::<i32>().is_none());
assert_eq!(depedencies.get::<bool>(), None);
assert_eq!(depedencies.get(), Some(&MyType(10)));
}
#[test]
fn test_extend() {
#[derive(Debug, PartialEq)]
struct MyType(i32);
let mut depedencies = Dependencies::new();
depedencies.insert(5i32);
depedencies.insert(MyType(10));
let mut other = Dependencies::new();
other.insert(15i32);
other.insert(20u8);
depedencies.extend(other);
assert_eq!(depedencies.get(), Some(&15i32));
assert_eq!(depedencies.get_mut(), Some(&mut 15i32));
assert_eq!(depedencies.remove::<i32>(), Some(15i32));
assert!(depedencies.get::<i32>().is_none());
assert_eq!(depedencies.get::<bool>(), None);
assert_eq!(depedencies.get(), Some(&MyType(10)));
assert_eq!(depedencies.get(), Some(&20u8));
assert_eq!(depedencies.get_mut(), Some(&mut 20u8));
}
}

View file

@ -3,5 +3,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
pub mod absolute_url; pub mod absolute_url;
pub mod data;
pub mod forges; pub mod forges;
pub mod random_string; pub mod random_string;