feat: load FederationConfig with custom dependency injector utility
This commit is contained in:
parent
1b557e045d
commit
2fb54938f6
7 changed files with 420 additions and 65 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1235,6 +1235,7 @@ dependencies = [
|
|||
"derive-getters",
|
||||
"derive_builder",
|
||||
"derive_more",
|
||||
"enum_delegate",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mockall",
|
||||
|
|
|
@ -35,6 +35,7 @@ actix-identity = "0.7.1"
|
|||
derive_builder = "0.20.0"
|
||||
derive-getters = "0.4.0"
|
||||
activitypub_federation = "0.5.6"
|
||||
enum_delegate = "0.2.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -8,7 +8,9 @@ use actix_web::web::Data;
|
|||
use super::RoutesRepository;
|
||||
use crate::federation::adapter::out::forge::forge_factory::FederationForgeAdapterFactoryInterface;
|
||||
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;
|
||||
use activitypub_federation::config::FederationConfig;
|
||||
|
||||
pub type WebFederationOutDBGetPersonObj = Data<FederationOutDBGetPersonObj>;
|
||||
pub type WebFederationOutDBSavePersonObj = Data<FederationOutDBSavePersonObj>;
|
||||
|
@ -16,3 +18,15 @@ pub type WebFederationOutDBSavePersonObj = Data<FederationOutDBSavePersonObj>;
|
|||
pub type WebFederationForgeRepositoryInterface =
|
||||
Data<Arc<dyn ForgeRepositoryInterface<Arc<dyn FederationForgeAdapterFactoryInterface>>>>;
|
||||
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>>>;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use activitypub_federation::config::FederationConfig;
|
||||
use actix_web::web;
|
||||
use sqlx::postgres::PgPool;
|
||||
|
||||
|
@ -11,19 +12,25 @@ pub mod input;
|
|||
pub mod out;
|
||||
|
||||
use crate::settings;
|
||||
use crate::utils::data::Dependencies;
|
||||
use crate::utils::forges::*;
|
||||
use input::web::types::*;
|
||||
use out::db::postgres::DBOutPostgresAdapter;
|
||||
|
||||
use self::out::forge::forge_factory::*;
|
||||
use self::out::forge::forgejo::ForgejoBuilder;
|
||||
use self::out::forge::github::GitHubBuilder;
|
||||
use crate::utils::forges::*;
|
||||
//use out::forge::{forge_repository::ForgeRepository, forgejo::Forgejo, github::Github};
|
||||
|
||||
pub fn load_adapters(
|
||||
pool: PgPool,
|
||||
settings: &settings::Settings,
|
||||
) -> impl FnOnce(&mut web::ServiceConfig) {
|
||||
#[derive(Clone)]
|
||||
struct Adapters {
|
||||
out_db_get_person_adapter: WebFederationOutDBGetPersonObj,
|
||||
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 out_db_get_person_adapter = WebFederationOutDBGetPersonObj::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())
|
||||
.client_id(settings.forges.forgejo.client_id.clone())
|
||||
.client_secret(settings.forges.forgejo.client_secret.clone())
|
||||
.settings(settings.clone())
|
||||
.routes(input::web::RoutesRepository::default())
|
||||
.username(settings.forges.forgejo.user.username.clone())
|
||||
.api_token(settings.forges.forgejo.user.api_token.clone())
|
||||
.build()
|
||||
|
@ -46,51 +55,72 @@ pub fn load_adapters(
|
|||
.url(settings.forges.github.url.clone())
|
||||
.api_url(settings.forges.github.api_url.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())
|
||||
.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()
|
||||
.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 mut f = ForgeRepository::new();
|
||||
f.add_forge(SupportedForges::Forgejo, forgejo);
|
||||
f.add_forge(SupportedForges::Github, github);
|
||||
// f.add_forge(SupportedForges::Github, github);
|
||||
let f: Arc<dyn ForgeRepositoryInterface<Arc<dyn FederationForgeAdapterFactoryInterface>>> =
|
||||
Arc::new(f);
|
||||
let f: Arc<
|
||||
dyn ForgeRepositoryInterface<Arc<dyn FederationForgeAdapterFactoryInterface>>,
|
||||
> = Arc::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| {
|
||||
cfg.configure(input::web::load_ctx());
|
||||
cfg.app_data(out_db_get_person_adapter);
|
||||
cfg.app_data(out_db_save_person_adapter);
|
||||
cfg.app_data(forge_repository_interface);
|
||||
|
||||
// cfg.app_data(forge_repository_interface);
|
||||
//
|
||||
// cfg.app_data(web::Data::new(refresh_access_token_input_adapter));
|
||||
//
|
||||
// cfg.app_data(s);
|
||||
//
|
||||
cfg.app_data(adapters.out_db_get_person_adapter);
|
||||
cfg.app_data(adapters.out_db_save_person_adapter);
|
||||
cfg.app_data(adapters.forge_repository_interface);
|
||||
};
|
||||
|
||||
Box::new(f)
|
||||
|
|
|
@ -44,6 +44,9 @@ async fn main() {
|
|||
socket_addr,
|
||||
settings.server.domain
|
||||
);
|
||||
|
||||
let s = settings.clone();
|
||||
let federation_config = federation::adapter::federation_config(db.pool.clone(), &s).await;
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(IdentityMiddleware::default())
|
||||
|
@ -62,6 +65,8 @@ async fn main() {
|
|||
db.pool.clone(),
|
||||
&settings,
|
||||
))
|
||||
// .app_data(utils::data::Extensions::default())
|
||||
.app_data(federation_config.clone())
|
||||
.configure(utils::random_string::GenerateRandomString::inject())
|
||||
})
|
||||
.bind(&socket_addr)
|
||||
|
|
303
src/utils/data.rs
Normal file
303
src/utils/data.rs
Normal 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));
|
||||
}
|
||||
}
|
|
@ -3,5 +3,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
pub mod absolute_url;
|
||||
pub mod data;
|
||||
pub mod forges;
|
||||
pub mod random_string;
|
||||
|
|
Loading…
Add table
Reference in a new issue