diff --git a/crates/config/src/server.rs b/crates/config/src/server.rs index b042889..df69b14 100644 --- a/crates/config/src/server.rs +++ b/crates/config/src/server.rs @@ -41,7 +41,8 @@ type Result = core::result::Result; )] pub struct ServerConfig { http: Http, - webhook: Webhook, + webhook: InboundWebhook, + notifications: Notification, storage: ServerStorage, pub forge: BTreeMap, } @@ -64,7 +65,11 @@ impl ServerConfig { &self.storage } - pub const fn webhook(&self) -> &Webhook { + pub const fn notifications(&self) -> &Notification { + &self.notifications + } + + pub const fn webhook(&self) -> &InboundWebhook { &self.webhook } @@ -113,10 +118,10 @@ impl Http { serde::Deserialize, derive_more::Constructor, )] -pub struct Webhook { +pub struct InboundWebhook { url: String, } -impl Webhook { +impl InboundWebhook { pub fn url(&self, forge_alias: &ForgeAlias, repo_alias: &RepoAlias) -> WebhookUrl { let base_url = &self.url; WebhookUrl(format!("{base_url}/{forge_alias}/{repo_alias}")) @@ -149,3 +154,79 @@ impl ServerStorage { self.path.as_path() } } + +/// Identifier for the type of Notification +#[derive( + Clone, + Default, + Debug, + derive_more::From, + PartialEq, + Eq, + PartialOrd, + Ord, + serde::Deserialize, + Copy, +)] +pub enum NotificationType { + #[default] + None, + Webhook, +} + +/// Defines the Webhook Forges should send updates to +/// Must be an address that is accessible from the remote forge +#[derive( + Clone, + Debug, + derive_more::From, + PartialEq, + Eq, + PartialOrd, + Ord, + derive_more::AsRef, + serde::Deserialize, +)] +pub struct Notification { + r#type: NotificationType, + webhook: Option, +} +impl Notification { + pub const fn none() -> Self { + Self { + r#type: NotificationType::None, + webhook: None, + } + } + pub const fn webhook(webhook: OutboundWebhook) -> Self { + Self { + r#type: NotificationType::Webhook, + webhook: Some(webhook), + } + } + + pub fn webhook_url(&self) -> Option { + self.webhook.clone().map(|x| x.url) + } +} +impl Default for Notification { + fn default() -> Self { + Self::none() + } +} + +#[derive( + Clone, + Debug, + derive_more::From, + PartialEq, + Eq, + PartialOrd, + Ord, + derive_more::AsRef, + serde::Deserialize, + derive_more::Constructor, +)] +pub struct OutboundWebhook { + url: String, +} diff --git a/crates/config/src/tests.rs b/crates/config/src/tests.rs index 16e337e..45b6e57 100644 --- a/crates/config/src/tests.rs +++ b/crates/config/src/tests.rs @@ -7,9 +7,9 @@ use std::collections::BTreeMap; use std::path::PathBuf; use crate::server::Http; +use crate::server::InboundWebhook; use crate::server::ServerConfig; use crate::server::ServerStorage; -use crate::server::Webhook; use crate::webhook::push::Branch; mod url; @@ -470,6 +470,10 @@ mod server { let http_port = server_config.http()?.port(); let webhook_url = server_config.webhook().base_url(); let storage_path = server_config.storage().path(); + let notification_webhook_url = server_config + .notifications() + .webhook_url() + .unwrap_or_default(); let forge_alias = server_config .forges() .next() @@ -519,6 +523,10 @@ url = "{webhook_url}" [storage] path = {storage_path:?} +[notifications] +type = "Webhook" +webhook = {{ url = "{notification_webhook_url}" }} + [forge.{forge_alias}] forge_type = "{forge_type}" hostname = "{forge_hostname}" @@ -687,6 +695,8 @@ mod push { } mod given { + use crate::server::{Notification, OutboundWebhook}; + use super::*; use rand::Rng as _; use std::{ @@ -709,7 +719,8 @@ mod given { pub fn a_server_config() -> ServerConfig { ServerConfig::new( an_http(), - a_webhook(), + an_inbound_webhook(), + a_notification_config(), a_server_storage(), some_forge_configs(), ) @@ -730,12 +741,18 @@ mod given { pub fn a_port() -> u16 { rand::thread_rng().gen() } - pub fn a_webhook() -> Webhook { - Webhook::new(a_name()) + pub fn an_inbound_webhook() -> InboundWebhook { + InboundWebhook::new(a_name()) + } + pub fn an_outbound_webhook() -> OutboundWebhook { + OutboundWebhook::new(a_name()) } pub fn a_server_storage() -> ServerStorage { ServerStorage::new(a_name().into()) } + pub fn a_notification_config() -> Notification { + Notification::webhook(an_outbound_webhook()) + } pub fn some_forge_configs() -> BTreeMap { [(a_name(), a_forge_config())].into() } diff --git a/crates/forge-forgejo/src/tests.rs b/crates/forge-forgejo/src/tests.rs index 0baeb1a..2feeef3 100644 --- a/crates/forge-forgejo/src/tests.rs +++ b/crates/forge-forgejo/src/tests.rs @@ -703,7 +703,7 @@ mod forgejo { forge_alias: &config::ForgeAlias, repo_alias: &config::RepoAlias, ) -> git_next_config::server::WebhookUrl { - config::server::Webhook::new(a_name()).url(forge_alias, repo_alias) + config::server::InboundWebhook::new(a_name()).url(forge_alias, repo_alias) } pub fn any_webhook_url() -> git_next_config::server::WebhookUrl { diff --git a/crates/forge-github/src/tests/mod.rs b/crates/forge-github/src/tests/mod.rs index 5079853..986211f 100644 --- a/crates/forge-github/src/tests/mod.rs +++ b/crates/forge-github/src/tests/mod.rs @@ -510,7 +510,7 @@ mod github { use std::path::PathBuf; - use git_next_config::{server::Webhook, WebhookId}; + use git_next_config::{server::InboundWebhook, WebhookId}; use kxio::network::{MockNetwork, StatusCode}; use rand::RngCore; use serde_json::json; @@ -648,7 +648,7 @@ mod github { forge_alias: &ForgeAlias, repo_alias: &RepoAlias, ) -> git_next_config::server::WebhookUrl { - Webhook::new(a_name()).url(forge_alias, repo_alias) + InboundWebhook::new(a_name()).url(forge_alias, repo_alias) } pub fn any_webhook_url() -> git_next_config::server::WebhookUrl { given::a_webhook_url(&given::a_forge_alias(), &given::a_repo_alias()) diff --git a/crates/repo-actor/src/lib.rs b/crates/repo-actor/src/lib.rs index 24caf4f..91b6f96 100644 --- a/crates/repo-actor/src/lib.rs +++ b/crates/repo-actor/src/lib.rs @@ -37,7 +37,7 @@ pub struct RepoActor { generation: git::Generation, message_token: messages::MessageToken, repo_details: git::RepoDetails, - webhook: config::server::Webhook, + webhook: config::server::InboundWebhook, webhook_id: Option, // INFO: if [None] then no webhook is configured webhook_auth: Option, // INFO: if [None] then no webhook is configured last_main_commit: Option, @@ -53,7 +53,7 @@ impl RepoActor { pub fn new( repo_details: git::RepoDetails, forge: Box, - webhook: config::server::Webhook, + webhook: config::server::InboundWebhook, generation: git::Generation, net: Network, repository_factory: Box, diff --git a/crates/repo-actor/src/tests/given.rs b/crates/repo-actor/src/tests/given.rs index 84f0fc3..4d56efa 100644 --- a/crates/repo-actor/src/tests/given.rs +++ b/crates/repo-actor/src/tests/given.rs @@ -57,7 +57,7 @@ pub fn a_webhook_url( forge_alias: &ForgeAlias, repo_alias: &RepoAlias, ) -> git_next_config::server::WebhookUrl { - config::server::Webhook::new(a_name()).url(forge_alias, repo_alias) + config::server::InboundWebhook::new(a_name()).url(forge_alias, repo_alias) } pub fn a_name() -> String { @@ -179,8 +179,8 @@ pub fn a_message_token() -> MessageToken { MessageToken::default() } -pub fn a_webhook(url: &WebhookUrl) -> Webhook { - Webhook::new(url.clone().into()) +pub fn a_webhook(url: &WebhookUrl) -> InboundWebhook { + InboundWebhook::new(url.clone().into()) } pub fn a_forge() -> Box { diff --git a/crates/repo-actor/src/tests/mod.rs b/crates/repo-actor/src/tests/mod.rs index d6a003d..b92f3e1 100644 --- a/crates/repo-actor/src/tests/mod.rs +++ b/crates/repo-actor/src/tests/mod.rs @@ -7,7 +7,7 @@ use actor::{ }; use assert2::let_assert; use config::{ - server::{Webhook, WebhookUrl}, + server::{InboundWebhook, WebhookUrl}, webhook::forge_notification::Body, BranchName, ForgeAlias, ForgeConfig, ForgeNotification, ForgeType, GitDir, RegisteredWebhook, RepoAlias, RepoBranches, RepoConfig, ServerRepoConfig, WebhookAuth, WebhookId, @@ -82,7 +82,7 @@ pub struct RepoActorView { pub generation: git::Generation, pub message_token: MessageToken, pub repo_details: git::RepoDetails, - pub webhook: config::server::Webhook, + pub webhook: config::server::InboundWebhook, pub webhook_id: Option, // INFO: if [None] then no webhook is configured pub webhook_auth: Option, // INFO: if [None] then no webhook is configured pub last_main_commit: Option, diff --git a/crates/server-actor/src/lib.rs b/crates/server-actor/src/lib.rs index 17257f4..47bae97 100644 --- a/crates/server-actor/src/lib.rs +++ b/crates/server-actor/src/lib.rs @@ -7,7 +7,7 @@ pub mod messages; use actix::prelude::*; use git_next_config as config; -use git_next_config::server::{ServerConfig, ServerStorage, Webhook}; +use git_next_config::server::{InboundWebhook, ServerConfig, ServerStorage}; use git_next_config::{ForgeAlias, ForgeConfig, GitDir, RepoAlias, ServerRepoConfig}; use git_next_git::{Generation, RepoDetails}; use git_next_repo_actor::{messages::CloneRepo, RepoActor}; @@ -104,7 +104,7 @@ impl Server { forge_config: &ForgeConfig, forge_name: ForgeAlias, server_storage: &ServerStorage, - webhook: &Webhook, + webhook: &InboundWebhook, ) -> Vec<(ForgeAlias, RepoAlias, RepoActor)> { let span = tracing::info_span!("create_forge_repos", name = %forge_name, config = %forge_config); @@ -129,7 +129,7 @@ impl Server { forge_name: ForgeAlias, forge_config: ForgeConfig, server_storage: &ServerStorage, - webhook: &Webhook, + webhook: &InboundWebhook, ) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeAlias, RepoAlias, RepoActor) { let server_storage = server_storage.clone(); let webhook = webhook.clone(); diff --git a/crates/server-actor/src/tests/receive_server_config.rs b/crates/server-actor/src/tests/receive_server_config.rs index a571988..de3f483 100644 --- a/crates/server-actor/src/tests/receive_server_config.rs +++ b/crates/server-actor/src/tests/receive_server_config.rs @@ -1,7 +1,7 @@ // use crate::{tests::given, ReceiveServerConfig, Server}; use actix::prelude::*; -use git_next_config::server::{Http, ServerConfig, ServerStorage, Webhook}; +use git_next_config::server::{Http, InboundWebhook, Notification, ServerConfig, ServerStorage}; use std::{ collections::BTreeMap, sync::{Arc, RwLock}, @@ -21,7 +21,8 @@ async fn when_webhook_url_has_trailing_slash_should_not_send() { // collaborators let http = Http::new("0.0.0.0".to_string(), 80); - let webhook = Webhook::new("http://localhost/".to_string()); // With trailing slash + let webhook = InboundWebhook::new("http://localhost/".to_string()); // With trailing slash + let notifications = Notification::none(); let server_storage = ServerStorage::new((fs.base()).to_path_buf()); let repos = BTreeMap::default(); @@ -35,6 +36,7 @@ async fn when_webhook_url_has_trailing_slash_should_not_send() { .do_send(ReceiveServerConfig::new(ServerConfig::new( http, webhook, + notifications, server_storage, repos, ))); diff --git a/crates/server/server-default.toml b/crates/server/server-default.toml index 2e87e42..eedef5e 100644 --- a/crates/server/server-default.toml +++ b/crates/server/server-default.toml @@ -8,6 +8,10 @@ url = "https://localhost:8080" # don't include any query path or a trailing slas [storage] path = "./data" +[notifications] +type = "WebHook" +webhook = { url = "https://localhost:9090" } + [forge] [forge.default]