build / repo webhook write api

This commit is contained in:
mbecker20
2024-07-05 02:02:03 -07:00
parent c99a33880e
commit a21d49d224
6 changed files with 340 additions and 16 deletions

View File

@@ -318,7 +318,7 @@ impl Resolve<GetBuildWebhookEnabled, User> for State {
user: User,
) -> anyhow::Result<GetBuildWebhookEnabledResponse> {
let Some(github) = github_client() else {
return Err(anyhow!("github_webhook_app is not configured"));
return Err(anyhow!("github_webhook_app is not configured in core config toml"));
};
let build = resource::get_check_permissions::<Build>(

View File

@@ -128,7 +128,7 @@ impl Resolve<GetRepoWebhooksEnabled, User> for State {
user: User,
) -> anyhow::Result<GetRepoWebhooksEnabledResponse> {
let Some(github) = github_client() else {
return Err(anyhow!("github_webhook_app is not configured"));
return Err(anyhow!("github_webhook_app is not configured in core config toml"));
};
let repo = resource::get_check_permissions::<Repo>(

View File

@@ -1,10 +1,21 @@
use anyhow::{anyhow, Context};
use monitor_client::{
api::write::*,
entities::{build::Build, permission::PermissionLevel, user::User},
entities::{
build::Build, config::core::CoreConfig,
permission::PermissionLevel, user::User, NoData,
},
};
use octorust::types::{
ReposCreateWebhookRequest, ReposCreateWebhookRequestConfig,
};
use resolver_api::Resolve;
use crate::{resource, state::State};
use crate::{
config::core_config,
resource,
state::{github_client, State},
};
impl Resolve<CreateBuild, User> for State {
#[instrument(name = "CreateBuild", skip(self, user))]
@@ -58,21 +69,153 @@ impl Resolve<UpdateBuild, User> for State {
}
impl Resolve<CreateBuildWebhook, User> for State {
#[instrument(name = "CreateBuildWebhook", skip(self, user))]
async fn resolve(
&self,
CreateBuildWebhook { build }: CreateBuildWebhook,
user: User,
) -> anyhow::Result<CreateBuildWebhookResponse> {
todo!()
let Some(github) = github_client() else {
return Err(anyhow!("github_webhook_app is not configured in core config toml"));
};
let build = resource::get_check_permissions::<Build>(
&build,
&user,
PermissionLevel::Write,
)
.await?;
if build.config.repo.is_empty() {
return Err(anyhow!(
"No repo configured, can't create webhook"
));
}
let mut split = build.config.repo.split('/');
let owner = split.next().context("Build repo has no owner")?;
let CoreConfig {
host,
github_webhook_base_url,
github_webhook_app,
..
} = core_config();
if !github_webhook_app.owners.iter().any(|o| o == owner) {
return Err(anyhow!(
"Cannot manage repo webhooks under owner {owner}"
));
}
let repo =
split.next().context("Build repo has no repo after the /")?;
let github_repos = github.repos();
// First make sure the webhook isn't already created (inactive ones are ignored)
let webhooks = github_repos
.list_all_webhooks(owner, repo)
.await
.context("failed to list all webhooks on repo")?
.body;
let host = github_webhook_base_url.as_ref().unwrap_or(host);
let url = format!("{host}/listener/github/build/{}", build.id);
for webhook in webhooks {
if webhook.active && webhook.config.url == url {
return Ok(NoData {});
}
}
// Now good to create the webhook
let request = ReposCreateWebhookRequest {
active: Some(true),
config: Some(ReposCreateWebhookRequestConfig {
url,
secret: core_config().github_webhook_secret.to_string(),
content_type: String::from("json"),
insecure_ssl: None,
digest: Default::default(),
token: Default::default(),
}),
events: vec![String::from("push")],
name: String::from("web"),
};
github_repos
.create_webhook(owner, repo, &request)
.await
.context("failed to create webhook")?;
Ok(NoData {})
}
}
impl Resolve<DeleteBuildWebhook, User> for State {
#[instrument(name = "DeleteBuildWebhook", skip(self, user))]
async fn resolve(
&self,
DeleteBuildWebhook { build }: DeleteBuildWebhook,
user: User,
) -> anyhow::Result<DeleteBuildWebhookResponse> {
todo!()
let Some(github) = github_client() else {
return Err(anyhow!("github_webhook_app is not configured in core config toml"));
};
let build = resource::get_check_permissions::<Build>(
&build,
&user,
PermissionLevel::Write,
)
.await?;
if build.config.repo.is_empty() {
return Err(anyhow!(
"No repo configured, can't delete webhook"
));
}
let mut split = build.config.repo.split('/');
let owner = split.next().context("Build repo has no owner")?;
let CoreConfig {
host,
github_webhook_base_url,
github_webhook_app,
..
} = core_config();
if !github_webhook_app.owners.iter().any(|o| o == owner) {
return Err(anyhow!(
"Cannot manage repo webhooks under owner {owner}"
));
}
let repo =
split.next().context("Build repo has no repo after the /")?;
let github_repos = github.repos();
let webhooks = github_repos
.list_all_webhooks(owner, repo)
.await
.context("failed to list all webhooks on repo")?
.body;
let host = github_webhook_base_url.as_ref().unwrap_or(host);
let url = format!("{host}/listener/github/build/{}", build.id);
for webhook in webhooks {
if webhook.active && webhook.config.url == url {
github_repos
.delete_webhook(owner, repo, webhook.id)
.await
.context("failed to delete webhook")?;
return Ok(NoData {});
}
}
Err(anyhow!("Didn't find any webhook to delete"))
}
}

View File

@@ -1,10 +1,21 @@
use anyhow::{anyhow, Context};
use monitor_client::{
api::write::*,
entities::{permission::PermissionLevel, repo::Repo, user::User},
entities::{
config::core::CoreConfig, permission::PermissionLevel,
repo::Repo, user::User, NoData,
},
};
use octorust::types::{
ReposCreateWebhookRequest, ReposCreateWebhookRequestConfig,
};
use resolver_api::Resolve;
use crate::{resource, state::State};
use crate::{
config::core_config,
resource,
state::{github_client, State},
};
impl Resolve<CreateRepo, User> for State {
#[instrument(name = "CreateRepo", skip(self, user))]
@@ -58,21 +69,172 @@ impl Resolve<UpdateRepo, User> for State {
}
impl Resolve<CreateRepoWebhook, User> for State {
#[instrument(name = "CreateRepoWebhook", skip(self, user))]
async fn resolve(
&self,
CreateRepoWebhook { repo }: CreateRepoWebhook,
_: User,
CreateRepoWebhook { repo, action }: CreateRepoWebhook,
user: User,
) -> anyhow::Result<CreateRepoWebhookResponse> {
todo!()
let Some(github) = github_client() else {
return Err(anyhow!(
"github_webhook_app is not configured in core config toml"
));
};
let repo = resource::get_check_permissions::<Repo>(
&repo,
&user,
PermissionLevel::Write,
)
.await?;
if repo.config.repo.is_empty() {
return Err(anyhow!(
"No repo configured, can't create webhook"
));
}
let mut split = repo.config.repo.split('/');
let owner = split.next().context("Repo repo has no owner")?;
let CoreConfig {
host,
github_webhook_base_url,
github_webhook_app,
..
} = core_config();
if !github_webhook_app.owners.iter().any(|o| o == owner) {
return Err(anyhow!(
"Cannot manage repo webhooks under owner {owner}"
));
}
let repo_name =
split.next().context("Repo repo has no repo after the /")?;
let github_repos = github.repos();
// First make sure the webhook isn't already created (inactive ones are ignored)
let webhooks = github_repos
.list_all_webhooks(owner, repo_name)
.await
.context("failed to list all webhooks on repo")?
.body;
let host = github_webhook_base_url.as_ref().unwrap_or(host);
let url = match action {
RepoWebhookAction::Clone => {
format!("{host}/listener/github/repo/{}/clone", repo.id)
}
RepoWebhookAction::Pull => {
format!("{host}/listener/github/repo/{}/pull", repo.id)
}
};
for webhook in webhooks {
if webhook.active && webhook.config.url == url {
return Ok(NoData {});
}
}
// Now good to create the webhook
let request = ReposCreateWebhookRequest {
active: Some(true),
config: Some(ReposCreateWebhookRequestConfig {
url,
secret: core_config().github_webhook_secret.to_string(),
content_type: String::from("json"),
insecure_ssl: None,
digest: Default::default(),
token: Default::default(),
}),
events: vec![String::from("push")],
name: String::from("web"),
};
github_repos
.create_webhook(owner, repo_name, &request)
.await
.context("failed to create webhook")?;
Ok(NoData {})
}
}
impl Resolve<DeleteRepoWebhook, User> for State {
#[instrument(name = "DeleteRepoWebhook", skip(self, user))]
async fn resolve(
&self,
DeleteRepoWebhook { repo }: DeleteRepoWebhook,
_: User,
DeleteRepoWebhook { repo, action }: DeleteRepoWebhook,
user: User,
) -> anyhow::Result<DeleteRepoWebhookResponse> {
todo!()
let Some(github) = github_client() else {
return Err(anyhow!(
"github_webhook_app is not configured in core config toml"
));
};
let repo = resource::get_check_permissions::<Repo>(
&repo,
&user,
PermissionLevel::Write,
)
.await?;
if repo.config.repo.is_empty() {
return Err(anyhow!(
"No repo configured, can't create webhook"
));
}
let mut split = repo.config.repo.split('/');
let owner = split.next().context("Repo repo has no owner")?;
let CoreConfig {
host,
github_webhook_base_url,
github_webhook_app,
..
} = core_config();
if !github_webhook_app.owners.iter().any(|o| o == owner) {
return Err(anyhow!(
"Cannot manage repo webhooks under owner {owner}"
));
}
let repo_name =
split.next().context("Repo repo has no repo after the /")?;
let github_repos = github.repos();
// First make sure the webhook isn't already created (inactive ones are ignored)
let webhooks = github_repos
.list_all_webhooks(owner, repo_name)
.await
.context("failed to list all webhooks on repo")?
.body;
let host = github_webhook_base_url.as_ref().unwrap_or(host);
let url = match action {
RepoWebhookAction::Clone => {
format!("{host}/listener/github/repo/{}/clone", repo.id)
}
RepoWebhookAction::Pull => {
format!("{host}/listener/github/repo/{}/pull", repo.id)
}
};
for webhook in webhooks {
if webhook.active && webhook.config.url == url {
github_repos
.delete_webhook(owner, repo_name, webhook.id)
.await
.context("failed to delete webhook")?;
return Ok(NoData {});
}
}
Err(anyhow!("Didn't find any webhook to delete"))
}
}

View File

@@ -83,6 +83,8 @@ pub struct UpdateBuild {
//
/// Create a webhook on the github repo attached to the build
/// passed in request. Response: [CreateBuildWebhookResponse]
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
@@ -100,6 +102,8 @@ pub type CreateBuildWebhookResponse = NoData;
//
/// Delete a webhook on the github repo attached to the build
/// passed in request. Response: [CreateBuildWebhookResponse]
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
@@ -113,4 +117,4 @@ pub struct DeleteBuildWebhook {
}
#[typeshare]
pub type DeleteBuildWebhookResponse = NoData;
pub type DeleteBuildWebhookResponse = NoData;

View File

@@ -86,6 +86,15 @@ pub struct UpdateRepo {
//
#[typeshare]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RepoWebhookAction {
Clone,
Pull,
}
/// Create a webhook on the github repo attached to the (monitor) repo
/// passed in request. Response: [CreateRepoWebhookResponse]
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
@@ -96,6 +105,8 @@ pub struct CreateRepoWebhook {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub repo: String,
/// "Clone" or "Pull"
pub action: RepoWebhookAction,
}
#[typeshare]
@@ -103,6 +114,8 @@ pub type CreateRepoWebhookResponse = NoData;
//
/// Delete the webhook on the github repo attached to the (monitor) repo
/// passed in request. Response: [DeleteRepoWebhookResponse]
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
@@ -113,7 +126,9 @@ pub struct DeleteRepoWebhook {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub repo: String,
/// "Clone" or "Pull"
pub action: RepoWebhookAction,
}
#[typeshare]
pub type DeleteRepoWebhookResponse = NoData;
pub type DeleteRepoWebhookResponse = NoData;