forked from kemitix/git-next
feat(repo/webhook): Replace webhook if it already exists
Closes kemitix/git-next#45
This commit is contained in:
parent
dfd7d32c94
commit
ec9571a182
2 changed files with 58 additions and 7 deletions
|
@ -3,14 +3,14 @@ use kxio::network::{self, json};
|
|||
use tracing::{debug, info, warn};
|
||||
use ulid::DecodeError;
|
||||
|
||||
use std::{fmt::Display, ops::Deref, str::FromStr};
|
||||
use std::{collections::HashMap, fmt::Display, ops::Deref, str::FromStr};
|
||||
|
||||
use crate::server::{
|
||||
actors::{
|
||||
repo::{RepoActor, ValidateRepo, WebhookRegistered},
|
||||
webhook::WebhookMessage,
|
||||
},
|
||||
config::{RepoBranches, Webhook},
|
||||
config::{RepoBranches, Webhook, WebhookUrl},
|
||||
forge,
|
||||
};
|
||||
|
||||
|
@ -75,14 +75,14 @@ pub async fn unregister(
|
|||
url,
|
||||
network::NetRequestHeaders::new(),
|
||||
network::RequestBody::None,
|
||||
network::ResponseType::Json,
|
||||
network::ResponseType::None,
|
||||
None,
|
||||
network::NetRequestLogging::None,
|
||||
);
|
||||
let result = net.delete(request).await;
|
||||
match result {
|
||||
Ok(_) => info!(?webhook_id, "unregistered webhook"),
|
||||
Err(_) => warn!(?webhook_id, "Failed to unregister webhook"),
|
||||
Err(err) => warn!(?webhook_id, ?err, "Failed to unregister webhook"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,9 +92,17 @@ pub async fn register(
|
|||
addr: actix::prelude::Addr<super::RepoActor>,
|
||||
net: network::Network,
|
||||
) {
|
||||
let Some(repo_config) = repo_details.config else {
|
||||
let Some(repo_config) = repo_details.config.clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let webhook_url = webhook.url();
|
||||
// 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 {
|
||||
unregister(webhook_id, repo_details.clone(), net.clone()).await;
|
||||
}
|
||||
|
||||
info!("Registering webhook");
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let path = repo_details.repo;
|
||||
|
@ -103,8 +111,7 @@ pub async fn register(
|
|||
let url = network::NetUrl::new(format!(
|
||||
"https://{hostname}/api/v1/repos/{path}/hooks?token={token}"
|
||||
));
|
||||
let webhook_url = webhook.url();
|
||||
let repo_alias = repo_details.name;
|
||||
let repo_alias = &repo_details.name;
|
||||
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
|
||||
let authorisation = WebhookAuth::generate();
|
||||
let body = json!({
|
||||
|
@ -139,9 +146,52 @@ pub async fn register(
|
|||
}
|
||||
}
|
||||
|
||||
async fn find_existing_webhooks(
|
||||
repo_details: &crate::server::config::RepoDetails,
|
||||
webhook_url: &WebhookUrl,
|
||||
net: &network::Network,
|
||||
) -> Vec<WebhookId> {
|
||||
let mut ids: Vec<WebhookId> = vec![];
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let path = &repo_details.repo;
|
||||
let mut page = 1;
|
||||
loop {
|
||||
use secrecy::ExposeSecret;
|
||||
let token = &repo_details.forge.token.expose_secret();
|
||||
let url = format!("https://{hostname}/api/v1/repos/{path}/hooks?page={page}&token={token}");
|
||||
let net_url = network::NetUrl::new(url);
|
||||
let request = network::NetRequest::new(
|
||||
network::RequestMethod::Get,
|
||||
net_url,
|
||||
network::NetRequestHeaders::new(),
|
||||
network::RequestBody::None,
|
||||
network::ResponseType::Json,
|
||||
None,
|
||||
network::NetRequestLogging::None,
|
||||
);
|
||||
let result = net.get::<Vec<Hook>>(request).await;
|
||||
if let Ok(response) = result {
|
||||
if let Some(list) = response.response_body() {
|
||||
if list.is_empty() {
|
||||
return ids;
|
||||
}
|
||||
for hook in list {
|
||||
if let Some(existing_url) = hook.config.get("url") {
|
||||
if existing_url.starts_with(webhook_url.as_ref()) {
|
||||
ids.push(hook.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
page += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct Hook {
|
||||
id: i64,
|
||||
config: HashMap<String, String>,
|
||||
}
|
||||
impl Hook {
|
||||
fn id(&self) -> WebhookId {
|
||||
|
|
|
@ -45,6 +45,7 @@ impl Webhook {
|
|||
}
|
||||
|
||||
/// The URL for the webhook where forges should send their updates
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
||||
pub struct WebhookUrl(String);
|
||||
impl AsRef<str> for WebhookUrl {
|
||||
fn as_ref(&self) -> &str {
|
||||
|
|
Loading…
Reference in a new issue