forked from kemitix/git-next
feat: Webhook query paths include forge alias
This allows for more than one forge to be configured and for the webhook to correctly route incoming messages.
This commit is contained in:
parent
17148e74b6
commit
206e64cd5b
17 changed files with 119 additions and 80 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, Hostname, RepoAlias, RepoBranches,
|
||||
ApiToken, BranchName, ForgeAlias, ForgeDetails, ForgeType, Hostname, RepoAlias, RepoBranches,
|
||||
RepoConfig, RepoConfigSource, RepoPath, User,
|
||||
};
|
||||
|
||||
|
@ -25,8 +25,8 @@ pub fn hostname(n: u32) -> Hostname {
|
|||
Hostname::new(format!("hostname-{}", n))
|
||||
}
|
||||
|
||||
pub fn forge_name(n: u32) -> ForgeName {
|
||||
ForgeName::new(format!("forge-name-{}", n))
|
||||
pub fn forge_name(n: u32) -> ForgeAlias {
|
||||
ForgeAlias::new(format!("forge-name-{}", n))
|
||||
}
|
||||
|
||||
pub fn branch_name(n: u32) -> BranchName {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::{ApiToken, ForgeConfig, ForgeName, ForgeType, Hostname, User};
|
||||
use crate::{ApiToken, ForgeAlias, ForgeConfig, ForgeType, Hostname, User};
|
||||
|
||||
/// The derived information about a Forge, used to create interactions with it
|
||||
#[derive(Clone, Default, Debug, derive_more::Constructor, derive_with::With)]
|
||||
pub struct ForgeDetails {
|
||||
forge_name: ForgeName,
|
||||
forge_alias: ForgeAlias,
|
||||
forge_type: ForgeType,
|
||||
hostname: Hostname,
|
||||
user: User,
|
||||
|
@ -12,8 +12,8 @@ pub struct ForgeDetails {
|
|||
// Private SSH Key Path
|
||||
}
|
||||
impl ForgeDetails {
|
||||
pub const fn forge_name(&self) -> &ForgeName {
|
||||
&self.forge_name
|
||||
pub const fn forge_alias(&self) -> &ForgeAlias {
|
||||
&self.forge_alias
|
||||
}
|
||||
pub const fn forge_type(&self) -> ForgeType {
|
||||
self.forge_type
|
||||
|
@ -28,10 +28,10 @@ impl ForgeDetails {
|
|||
&self.token
|
||||
}
|
||||
}
|
||||
impl From<(&ForgeName, &ForgeConfig)> for ForgeDetails {
|
||||
fn from(forge: (&ForgeName, &ForgeConfig)) -> Self {
|
||||
impl From<(&ForgeAlias, &ForgeConfig)> for ForgeDetails {
|
||||
fn from(forge: (&ForgeAlias, &ForgeConfig)) -> Self {
|
||||
Self {
|
||||
forge_name: forge.0.clone(),
|
||||
forge_alias: forge.0.clone(),
|
||||
forge_type: forge.1.forge_type(),
|
||||
hostname: forge.1.hostname(),
|
||||
user: forge.1.user(),
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
/// The name of a Forge to connect to
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq, derive_more::Constructor, derive_more::Display)]
|
||||
pub struct ForgeName(String);
|
||||
impl From<&ForgeName> for PathBuf {
|
||||
fn from(value: &ForgeName) -> Self {
|
||||
#[derive(
|
||||
Clone, Default, Debug, Hash, PartialEq, Eq, derive_more::Constructor, derive_more::Display,
|
||||
)]
|
||||
pub struct ForgeAlias(String);
|
||||
impl From<&ForgeAlias> for PathBuf {
|
||||
fn from(value: &ForgeAlias) -> Self {
|
||||
Self::from(&value.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ pub use api_token::ApiToken;
|
|||
pub use branch_name::BranchName;
|
||||
pub use forge_config::ForgeConfig;
|
||||
pub use forge_details::ForgeDetails;
|
||||
pub use forge_name::ForgeName;
|
||||
pub use forge_name::ForgeAlias;
|
||||
pub use forge_type::ForgeType;
|
||||
pub use git_dir::GitDir;
|
||||
pub use host_name::Hostname;
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::{
|
|||
use kxio::fs::FileSystem;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{ForgeConfig, ForgeName};
|
||||
use crate::{ForgeAlias, ForgeConfig, RepoAlias};
|
||||
|
||||
#[derive(Debug, derive_more::From, derive_more::Display)]
|
||||
pub enum Error {
|
||||
|
@ -43,10 +43,10 @@ impl ServerConfig {
|
|||
toml::from_str(&str).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn forges(&self) -> impl Iterator<Item = (ForgeName, &ForgeConfig)> {
|
||||
pub fn forges(&self) -> impl Iterator<Item = (ForgeAlias, &ForgeConfig)> {
|
||||
self.forge
|
||||
.iter()
|
||||
.map(|(name, forge)| (ForgeName::new(name.clone()), forge))
|
||||
.map(|(alias, forge)| (ForgeAlias::new(alias.clone()), forge))
|
||||
}
|
||||
|
||||
pub const fn storage(&self) -> &ServerStorage {
|
||||
|
@ -84,8 +84,9 @@ pub struct Webhook {
|
|||
url: String,
|
||||
}
|
||||
impl Webhook {
|
||||
pub fn url(&self) -> WebhookUrl {
|
||||
WebhookUrl(self.url.clone())
|
||||
pub fn url(&self, forge_alias: &ForgeAlias, repo_alias: &RepoAlias) -> WebhookUrl {
|
||||
let base_url = &self.url;
|
||||
WebhookUrl(format!("{base_url}/{forge_alias}/{repo_alias}"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ mod forge_details {
|
|||
|
||||
use secrecy::ExposeSecret;
|
||||
|
||||
use crate::{ApiToken, ForgeConfig, ForgeDetails, ForgeName, ForgeType, Hostname, User};
|
||||
use crate::{ApiToken, ForgeAlias, ForgeConfig, ForgeDetails, ForgeType, Hostname, User};
|
||||
|
||||
#[test]
|
||||
fn should_return_forge_name() {
|
||||
|
@ -260,11 +260,11 @@ mod forge_details {
|
|||
let hostname = Hostname::new("localhost".to_string());
|
||||
let user = User::new("bob".to_string());
|
||||
let token = ApiToken::new("alpha".to_string().into());
|
||||
let forge_name = ForgeName::new("gamma".to_string());
|
||||
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||
let forge_details =
|
||||
ForgeDetails::new(forge_name.clone(), forge_type, hostname, user, token);
|
||||
|
||||
let result = forge_details.forge_name();
|
||||
let result = forge_details.forge_alias();
|
||||
|
||||
assert_eq!(result, &forge_name);
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ mod forge_details {
|
|||
let hostname = Hostname::new("localhost".to_string());
|
||||
let user = User::new("bob".to_string());
|
||||
let token = ApiToken::new("alpha".to_string().into());
|
||||
let forge_name = ForgeName::new("gamma".to_string());
|
||||
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
|
||||
|
||||
let result = forge_details.forge_type();
|
||||
|
@ -287,7 +287,7 @@ mod forge_details {
|
|||
let hostname = Hostname::new("localhost".to_string());
|
||||
let user = User::new("bob".to_string());
|
||||
let token = ApiToken::new("alpha".to_string().into());
|
||||
let forge_name = ForgeName::new("gamma".to_string());
|
||||
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||
let forge_details =
|
||||
ForgeDetails::new(forge_name, forge_type, hostname.clone(), user, token);
|
||||
|
||||
|
@ -301,7 +301,7 @@ mod forge_details {
|
|||
let hostname = Hostname::new("localhost".to_string());
|
||||
let user = User::new("bob".to_string());
|
||||
let token = ApiToken::new("alpha".to_string().into());
|
||||
let forge_name = ForgeName::new("gamma".to_string());
|
||||
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||
let forge_details =
|
||||
ForgeDetails::new(forge_name, forge_type, hostname, user.clone(), token);
|
||||
|
||||
|
@ -315,7 +315,7 @@ mod forge_details {
|
|||
let hostname = Hostname::new("localhost".to_string());
|
||||
let user = User::new("bob".to_string());
|
||||
let token = ApiToken::new("alpha".to_string().into());
|
||||
let forge_name = ForgeName::new("gamma".to_string());
|
||||
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||
let forge_details =
|
||||
ForgeDetails::new(forge_name, forge_type, hostname, user, token.clone());
|
||||
|
||||
|
@ -329,7 +329,7 @@ mod forge_details {
|
|||
let hostname = Hostname::new("localhost".to_string());
|
||||
let user = User::new("bob".to_string());
|
||||
let token = ApiToken::new("alpha".to_string().into());
|
||||
let forge_name = ForgeName::new("gamma".to_string());
|
||||
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
|
||||
|
||||
let result = forge_details.with_hostname(Hostname::new("remotehost".to_string()));
|
||||
|
@ -342,7 +342,7 @@ mod forge_details {
|
|||
let hostname = Hostname::new("localhost".to_string());
|
||||
let user = User::new("bob".to_string());
|
||||
let token = ApiToken::new("alpha".to_string().into());
|
||||
let forge_name = ForgeName::new("gamma".to_string());
|
||||
let forge_alias = ForgeAlias::new("gamma".to_string());
|
||||
let forge_config = ForgeConfig::new(
|
||||
forge_type,
|
||||
"localhost".to_string(),
|
||||
|
@ -351,9 +351,9 @@ mod forge_details {
|
|||
BTreeMap::new(),
|
||||
);
|
||||
|
||||
let forge_details = ForgeDetails::from((&forge_name, &forge_config));
|
||||
let forge_details = ForgeDetails::from((&forge_alias, &forge_config));
|
||||
|
||||
assert_eq!(forge_details.forge_name(), &forge_name);
|
||||
assert_eq!(forge_details.forge_alias(), &forge_alias);
|
||||
assert_eq!(forge_details.hostname(), &hostname);
|
||||
assert_eq!(forge_details.user(), &user);
|
||||
assert_eq!(forge_details.token().expose_secret(), token.expose_secret());
|
||||
|
@ -362,13 +362,13 @@ mod forge_details {
|
|||
mod forge_name {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::ForgeName;
|
||||
use crate::ForgeAlias;
|
||||
|
||||
#[test]
|
||||
fn should_convert_to_pathbuf() {
|
||||
let forge_name = ForgeName::new("alpha".to_string());
|
||||
let forge_alias = ForgeAlias::new("alpha".to_string());
|
||||
|
||||
let pathbuf: PathBuf = (&forge_name).into();
|
||||
let pathbuf: PathBuf = (&forge_alias).into();
|
||||
|
||||
assert_eq!(pathbuf, PathBuf::new().join("alpha"));
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ impl ForgeJo {
|
|||
}
|
||||
#[async_trait::async_trait]
|
||||
impl git::ForgeLike for ForgeJo {
|
||||
fn name(&self) -> String {
|
||||
fn forge_alias(&self) -> String {
|
||||
"forgejo".to_string()
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ impl MockForgeEnv {
|
|||
}
|
||||
#[async_trait::async_trait]
|
||||
impl git::ForgeLike for MockForgeEnv {
|
||||
fn name(&self) -> String {
|
||||
fn forge_alias(&self) -> String {
|
||||
"mock".to_string()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ mod github;
|
|||
#[test]
|
||||
fn test_mock_name() {
|
||||
let forge = Forge::new_mock();
|
||||
assert_eq!(forge.name(), "mock");
|
||||
assert_eq!(forge.forge_alias(), "mock");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -30,5 +30,5 @@ fn test_forgejo_name() {
|
|||
config::GitDir::new(fs.base()),
|
||||
);
|
||||
let forge = Forge::new_forgejo(repo_details, net);
|
||||
assert_eq!(forge.name(), "forgejo");
|
||||
assert_eq!(forge.forge_alias(), "forgejo");
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate as git;
|
|||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ForgeLike {
|
||||
fn name(&self) -> String;
|
||||
fn forge_alias(&self) -> String;
|
||||
|
||||
/// Checks the results of any (e.g. CI) status checks for the commit.
|
||||
async fn commit_status(&self, commit: &git::Commit) -> git::commit::Status;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use git_next_config::{
|
||||
BranchName, ForgeConfig, ForgeDetails, ForgeName, GitDir, RepoAlias, RepoConfig, RepoPath,
|
||||
BranchName, ForgeAlias, ForgeConfig, ForgeDetails, GitDir, RepoAlias, RepoConfig, RepoPath,
|
||||
ServerRepoConfig,
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@ use super::{Generation, GitRemote};
|
|||
/// The derived information about a repo, used to interact with it
|
||||
#[derive(Clone, Default, Debug, derive_more::Display, derive_with::With)]
|
||||
#[display("gen-{}:{}:{}/{}:{}@{}/{}@{}", generation, forge.forge_type(),
|
||||
forge.forge_name(), repo_alias, forge.user(), forge.hostname(), repo_path,
|
||||
forge.forge_alias(), repo_alias, forge.user(), forge.hostname(), repo_path,
|
||||
branch)]
|
||||
pub struct RepoDetails {
|
||||
pub generation: Generation,
|
||||
|
@ -24,7 +24,7 @@ impl RepoDetails {
|
|||
generation: Generation,
|
||||
repo_alias: &RepoAlias,
|
||||
server_repo_config: &ServerRepoConfig,
|
||||
forge_name: &ForgeName,
|
||||
forge_alias: &ForgeAlias,
|
||||
forge_config: &ForgeConfig,
|
||||
gitdir: GitDir,
|
||||
) -> Self {
|
||||
|
@ -36,7 +36,7 @@ impl RepoDetails {
|
|||
branch: server_repo_config.branch(),
|
||||
gitdir,
|
||||
forge: ForgeDetails::new(
|
||||
forge_name.clone(),
|
||||
forge_alias.clone(),
|
||||
forge_config.forge_type(),
|
||||
forge_config.hostname(),
|
||||
forge_config.user(),
|
||||
|
|
|
@ -92,7 +92,7 @@ mod repo_details {
|
|||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
|
||||
use git_next_config::{
|
||||
ForgeConfig, ForgeName, ForgeType, GitDir, Hostname, RepoAlias, RepoPath, ServerRepoConfig,
|
||||
ForgeAlias, ForgeConfig, ForgeType, GitDir, Hostname, RepoAlias, RepoPath, ServerRepoConfig,
|
||||
};
|
||||
use secrecy::ExposeSecret;
|
||||
|
||||
|
@ -111,7 +111,7 @@ mod repo_details {
|
|||
None,
|
||||
None,
|
||||
),
|
||||
&ForgeName::new("default".to_string()),
|
||||
&ForgeAlias::new("default".to_string()),
|
||||
&ForgeConfig::new(
|
||||
ForgeType::MockForge,
|
||||
"host".to_string(),
|
||||
|
@ -140,7 +140,7 @@ mod repo_details {
|
|||
None,
|
||||
None,
|
||||
),
|
||||
&ForgeName::new("default".to_string()),
|
||||
&ForgeAlias::new("default".to_string()),
|
||||
&ForgeConfig::new(
|
||||
ForgeType::MockForge,
|
||||
"host".to_string(),
|
||||
|
|
|
@ -20,7 +20,7 @@ use kxio::network::Network;
|
|||
use tracing::{debug, info, warn, Instrument};
|
||||
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
#[display("{}:{}:{}", generation, repo_details.forge.forge_name(), repo_details.repo_alias)]
|
||||
#[display("{}:{}:{}", generation, repo_details.forge.forge_alias(), repo_details.repo_alias)]
|
||||
pub struct RepoActor {
|
||||
generation: git::Generation,
|
||||
message_token: MessageToken,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use actix::prelude::*;
|
||||
use git_next_config::{
|
||||
server::{Webhook, WebhookUrl},
|
||||
BranchName, RepoAlias, RepoBranches,
|
||||
BranchName, ForgeAlias, RepoAlias, RepoBranches,
|
||||
};
|
||||
use git_next_git as git;
|
||||
use kxio::network::{self, json};
|
||||
|
@ -75,7 +75,9 @@ pub async fn register(
|
|||
return;
|
||||
};
|
||||
|
||||
let webhook_url = webhook.url();
|
||||
let forge_alias = repo_details.forge.forge_alias();
|
||||
let repo_alias = &repo_details.repo_alias;
|
||||
let webhook_url = webhook.url(forge_alias, repo_alias);
|
||||
// remove any lingering webhooks for the same URL
|
||||
let existing_webhook_ids = find_existing_webhooks(&repo_details, &webhook_url, &net).await;
|
||||
for webhook_id in existing_webhook_ids {
|
||||
|
@ -89,7 +91,6 @@ pub async fn register(
|
|||
let url = network::NetUrl::new(format!(
|
||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||
));
|
||||
let repo_alias = &repo_details.repo_alias;
|
||||
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
|
||||
let authorisation = WebhookAuth::generate();
|
||||
let body = json!({
|
||||
|
@ -98,7 +99,7 @@ pub async fn register(
|
|||
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
||||
"config": {
|
||||
"content_type": "json",
|
||||
"url": format!("{}/{}", webhook_url.as_ref(), repo_alias),
|
||||
"url": webhook_url.as_ref(),
|
||||
},
|
||||
"events": [ "push" ],
|
||||
"type": "forgejo"
|
||||
|
@ -156,7 +157,7 @@ async fn find_existing_webhooks(
|
|||
}
|
||||
for hook in list {
|
||||
if let Some(existing_url) = hook.config.get("url") {
|
||||
if existing_url.starts_with(webhook_url.as_ref()) {
|
||||
if existing_url == webhook_url.as_ref() {
|
||||
ids.push(hook.id());
|
||||
}
|
||||
}
|
||||
|
@ -312,12 +313,15 @@ struct HeadCommit {
|
|||
#[derive(Message, Debug, Clone, derive_more::Constructor)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct WebhookMessage {
|
||||
// forge // TODO: (#58) differentiate between multiple forges
|
||||
forge_alias: ForgeAlias,
|
||||
repo_alias: RepoAlias,
|
||||
authorisation: WebhookAuth,
|
||||
body: Body,
|
||||
}
|
||||
impl WebhookMessage {
|
||||
pub const fn forge_alias(&self) -> &ForgeAlias {
|
||||
&self.forge_alias
|
||||
}
|
||||
pub const fn repo_alias(&self) -> &RepoAlias {
|
||||
&self.repo_alias
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use actix::prelude::*;
|
|||
|
||||
use config::server::{ServerConfig, ServerStorage, Webhook};
|
||||
use git_next_config::{
|
||||
self as config, ForgeConfig, ForgeName, GitDir, RepoAlias, ServerRepoConfig,
|
||||
self as config, ForgeAlias, ForgeConfig, GitDir, RepoAlias, ServerRepoConfig,
|
||||
};
|
||||
use git_next_git::{Generation, RepoDetails, Repository};
|
||||
use git_next_repo_actor::{CloneRepo, RepoActor};
|
||||
|
@ -99,11 +99,13 @@ impl Handler<ServerConfig> for Server {
|
|||
let webhook = server_config.webhook();
|
||||
|
||||
// Forge Actors
|
||||
for (forge_name, forge_config) in server_config.forges() {
|
||||
self.create_forge_repos(forge_config, forge_name.clone(), server_storage, webhook)
|
||||
for (forge_alias, forge_config) in server_config.forges() {
|
||||
self.create_forge_repos(forge_config, forge_alias.clone(), server_storage, webhook)
|
||||
.into_iter()
|
||||
.map(|a| self.start_actor(a))
|
||||
.map(|(alias, addr)| AddWebhookRecipient(alias, addr.recipient()))
|
||||
.map(|(repo_alias, addr)| {
|
||||
AddWebhookRecipient::new(forge_alias.clone(), repo_alias, addr.recipient())
|
||||
})
|
||||
.for_each(|msg| webhook_router.do_send(msg));
|
||||
}
|
||||
|
||||
|
@ -146,10 +148,10 @@ impl Server {
|
|||
fn create_forge_repos(
|
||||
&self,
|
||||
forge_config: &ForgeConfig,
|
||||
forge_name: ForgeName,
|
||||
forge_name: ForgeAlias,
|
||||
server_storage: &ServerStorage,
|
||||
webhook: &Webhook,
|
||||
) -> Vec<(ForgeName, RepoAlias, RepoActor)> {
|
||||
) -> Vec<(ForgeAlias, RepoAlias, RepoActor)> {
|
||||
let span =
|
||||
tracing::info_span!("create_forge_repos", name = %forge_name, config = %forge_config);
|
||||
|
||||
|
@ -170,11 +172,11 @@ impl Server {
|
|||
|
||||
fn create_actor(
|
||||
&self,
|
||||
forge_name: ForgeName,
|
||||
forge_name: ForgeAlias,
|
||||
forge_config: ForgeConfig,
|
||||
server_storage: &ServerStorage,
|
||||
webhook: &Webhook,
|
||||
) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeName, RepoAlias, RepoActor) {
|
||||
) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeAlias, RepoAlias, RepoActor) {
|
||||
let server_storage = server_storage.clone();
|
||||
let webhook = webhook.clone();
|
||||
let net = self.net.clone();
|
||||
|
@ -219,7 +221,7 @@ impl Server {
|
|||
|
||||
fn start_actor(
|
||||
&self,
|
||||
actor: (ForgeName, RepoAlias, RepoActor),
|
||||
actor: (ForgeAlias, RepoAlias, RepoActor),
|
||||
) -> (RepoAlias, Addr<RepoActor>) {
|
||||
let (forge_name, repo_alias, actor) = actor;
|
||||
let span = tracing::info_span!("start_actor", forge = %forge_name, repo = %repo_alias);
|
||||
|
|
|
@ -2,20 +2,21 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use actix::prelude::*;
|
||||
use git_next_config::RepoAlias;
|
||||
use derive_more::Constructor;
|
||||
use git_next_config::{ForgeAlias, RepoAlias};
|
||||
use git_next_repo_actor::webhook::WebhookMessage;
|
||||
use tracing::{debug, info};
|
||||
|
||||
pub struct WebhookRouter {
|
||||
span: tracing::Span,
|
||||
repos: HashMap<RepoAlias, Recipient<WebhookMessage>>,
|
||||
recipients: HashMap<ForgeAlias, HashMap<RepoAlias, Recipient<WebhookMessage>>>,
|
||||
}
|
||||
impl WebhookRouter {
|
||||
pub fn new() -> Self {
|
||||
let span = tracing::info_span!("WebhookRouter");
|
||||
Self {
|
||||
span,
|
||||
repos: Default::default(),
|
||||
recipients: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,24 +29,39 @@ impl Handler<WebhookMessage> for WebhookRouter {
|
|||
|
||||
fn handle(&mut self, msg: WebhookMessage, _ctx: &mut Self::Context) -> Self::Result {
|
||||
let _gaurd = self.span.enter();
|
||||
let forge_alias = msg.forge_alias();
|
||||
let repo_alias = msg.repo_alias();
|
||||
debug!(repo = %repo_alias, "Router...");
|
||||
if let Some(recipient) = self.repos.get(repo_alias) {
|
||||
debug!(forge = %forge_alias, repo = %repo_alias, "Router...");
|
||||
let Some(forge_repos) = self.recipients.get(forge_alias) else {
|
||||
return;
|
||||
};
|
||||
let Some(recipient) = forge_repos.get(repo_alias) else {
|
||||
return;
|
||||
};
|
||||
info!(repo = %repo_alias, "Sending to Recipient");
|
||||
recipient.do_send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[derive(Message, Constructor)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct AddWebhookRecipient(pub RepoAlias, pub Recipient<WebhookMessage>);
|
||||
pub struct AddWebhookRecipient {
|
||||
pub forge_alias: ForgeAlias,
|
||||
pub repo_alias: RepoAlias,
|
||||
pub recipient: Recipient<WebhookMessage>,
|
||||
}
|
||||
impl Handler<AddWebhookRecipient> for WebhookRouter {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: AddWebhookRecipient, _ctx: &mut Self::Context) -> Self::Result {
|
||||
let _gaurd = self.span.enter();
|
||||
info!(repo = %msg.0, "Register Recipient");
|
||||
self.repos.insert(msg.0, msg.1);
|
||||
info!(forge = %msg.forge_alias, repo = %msg.repo_alias, "Register Recipient");
|
||||
if !self.recipients.contains_key(&msg.forge_alias) {
|
||||
self.recipients
|
||||
.insert(msg.forge_alias.clone(), HashMap::new());
|
||||
}
|
||||
self.recipients
|
||||
.get_mut(&msg.forge_alias)
|
||||
.map(|repos| repos.insert(msg.repo_alias, msg.recipient));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::net::SocketAddr;
|
|||
|
||||
use actix::prelude::*;
|
||||
|
||||
use git_next_config::RepoAlias;
|
||||
use git_next_config::{ForgeAlias, RepoAlias};
|
||||
use git_next_repo_actor::webhook::{self, WebhookAuth, WebhookMessage};
|
||||
use tracing::{info, warn};
|
||||
use warp::reject::Rejection;
|
||||
|
@ -15,17 +15,19 @@ pub async fn start(socket_addr: SocketAddr, address: actix::prelude::Recipient<W
|
|||
let route = warp::post()
|
||||
.map(move || address.clone())
|
||||
.and(warp::path::param())
|
||||
// .and(warp::query::raw())
|
||||
.and(warp::path::param())
|
||||
.and(warp::header::headers_cloned())
|
||||
.and(warp::body::bytes())
|
||||
.and_then(
|
||||
|recipient: Recipient<WebhookMessage>,
|
||||
path: String,
|
||||
forge_alias: String,
|
||||
repo_alias: String,
|
||||
// query: String,
|
||||
headers: warp::http::HeaderMap,
|
||||
body: bytes::Bytes| async move {
|
||||
info!("POST received");
|
||||
let repo_alias = RepoAlias::new(path);
|
||||
let forge_alias = ForgeAlias::new(forge_alias);
|
||||
let repo_alias = RepoAlias::new(repo_alias);
|
||||
let bytes = body.to_vec();
|
||||
let body = webhook::Body::new(String::from_utf8_lossy(&bytes).to_string());
|
||||
headers.get("Authorization").map_or_else(
|
||||
|
@ -34,10 +36,22 @@ pub async fn start(socket_addr: SocketAddr, address: actix::prelude::Recipient<W
|
|||
Err(warp::reject())
|
||||
},
|
||||
|authorisation_header| {
|
||||
info!(?repo_alias, ?authorisation_header, "Received webhook",);
|
||||
info!(
|
||||
forge = %forge_alias,
|
||||
repo = %repo_alias,
|
||||
?authorisation_header,
|
||||
"Received webhook",
|
||||
);
|
||||
// TODO: (#86) Authorization isn't presented consistently, allow each forge
|
||||
// to parse the authorization from the request
|
||||
match parse_auth(authorisation_header) {
|
||||
Ok(authorisation) => {
|
||||
let message = WebhookMessage::new(repo_alias, authorisation, body);
|
||||
let message = WebhookMessage::new(
|
||||
forge_alias,
|
||||
repo_alias,
|
||||
authorisation,
|
||||
body,
|
||||
);
|
||||
recipient
|
||||
.try_send(message)
|
||||
.map(|_| {
|
||||
|
|
Loading…
Reference in a new issue