use derive_builder::Builder;
use crate::api::common::NameOrId;
use crate::api::endpoint_prelude::*;
use crate::api::ParamValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BranchFilterStrategy {
Wildcard,
Regex,
AllBranches,
}
impl BranchFilterStrategy {
fn as_str(&self) -> &'static str {
match self {
BranchFilterStrategy::Wildcard => "wildcard",
BranchFilterStrategy::Regex => "regex",
BranchFilterStrategy::AllBranches => "all_branches",
}
}
}
impl ParamValue<'static> for BranchFilterStrategy {
fn as_value(&self) -> Cow<'static, str> {
self.as_str().into()
}
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option))]
pub struct CreateHook<'a> {
#[builder(setter(into))]
project: NameOrId<'a>,
#[builder(setter(into))]
url: Cow<'a, str>,
#[builder(setter(into), default)]
name: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
description: Option<Cow<'a, str>>,
#[builder(default)]
push_events: Option<bool>,
#[builder(setter(into), default)]
push_events_branch_filter: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
branch_filter_strategy: Option<BranchFilterStrategy>,
#[builder(default)]
issues_events: Option<bool>,
#[builder(default)]
confidential_issues_events: Option<bool>,
#[builder(default)]
merge_requests_events: Option<bool>,
#[builder(default)]
tag_push_events: Option<bool>,
#[builder(default)]
note_events: Option<bool>,
#[builder(default)]
confidential_note_events: Option<bool>,
#[builder(default)]
job_events: Option<bool>,
#[builder(default)]
pipeline_events: Option<bool>,
#[builder(default)]
wiki_page_events: Option<bool>,
#[builder(default)]
deployment_events: Option<bool>,
#[builder(default)]
releases_events: Option<bool>,
#[builder(default)]
feature_flag_events: Option<bool>,
#[builder(default)]
resource_access_token_events: Option<bool>,
#[builder(setter(into), default)]
custom_webhook_template: Option<Cow<'a, str>>,
#[builder(setter(name = "_custom_headers"), default, private)]
custom_headers: Vec<(Cow<'a, str>, Cow<'a, str>)>,
#[builder(default)]
enable_ssl_verification: Option<bool>,
#[builder(setter(into), default)]
token: Option<Cow<'a, str>>,
}
impl<'a> CreateHook<'a> {
pub fn builder() -> CreateHookBuilder<'a> {
CreateHookBuilder::default()
}
}
impl<'a> CreateHookBuilder<'a> {
pub fn custom_header<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: Into<Cow<'a, str>>,
V: Into<Cow<'a, str>>,
{
self.custom_headers
.get_or_insert_with(Vec::new)
.push((key.into(), value.into()));
self
}
pub fn custom_headers<I, K, V>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = (K, V)>,
K: Into<Cow<'a, str>>,
V: Into<Cow<'a, str>>,
{
self.custom_headers
.get_or_insert_with(Vec::new)
.extend(iter.map(|(k, v)| (k.into(), v.into())));
self
}
}
impl Endpoint for CreateHook<'_> {
fn method(&self) -> Method {
Method::POST
}
fn endpoint(&self) -> Cow<'static, str> {
format!("projects/{}/hooks", self.project).into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
let mut params = FormParams::default();
params
.push("url", &self.url)
.push_opt("name", self.name.as_ref())
.push_opt("description", self.description.as_ref())
.push_opt("push_events", self.push_events)
.push_opt(
"push_events_branch_filter",
self.push_events_branch_filter.as_ref(),
)
.push_opt("branch_filter_strategy", self.branch_filter_strategy)
.push_opt("issues_events", self.issues_events)
.push_opt(
"confidential_issues_events",
self.confidential_issues_events,
)
.push_opt("merge_requests_events", self.merge_requests_events)
.push_opt("tag_push_events", self.tag_push_events)
.push_opt("note_events", self.note_events)
.push_opt("confidential_note_events", self.confidential_note_events)
.push_opt("job_events", self.job_events)
.push_opt("pipeline_events", self.pipeline_events)
.push_opt("wiki_page_events", self.wiki_page_events)
.push_opt("deployment_events", self.deployment_events)
.push_opt("releases_events", self.releases_events)
.push_opt("feature_flag_events", self.feature_flag_events)
.push_opt(
"resource_access_token_events",
self.resource_access_token_events,
)
.push_opt(
"custom_webhook_template",
self.custom_webhook_template.as_ref(),
)
.push_opt("enable_ssl_verification", self.enable_ssl_verification)
.push_opt("token", self.token.as_ref());
for (key, value) in &self.custom_headers {
params.push(format!("custom_headers[{key}]"), value);
}
params.into_body()
}
}
#[cfg(test)]
mod tests {
use http::Method;
use crate::api::projects::hooks::{BranchFilterStrategy, CreateHook, CreateHookBuilderError};
use crate::api::{self, Query};
use crate::test::client::{ExpectedUrl, SingleTestClient};
#[test]
fn test_project_branch_filter_strategy_as_str() {
let items = &[
(BranchFilterStrategy::Wildcard, "wildcard"),
(BranchFilterStrategy::Regex, "regex"),
(BranchFilterStrategy::AllBranches, "all_branches"),
];
for (i, s) in items {
assert_eq!(i.as_str(), *s);
}
}
#[test]
fn project_and_url_are_necessary() {
let err = CreateHook::builder().build().unwrap_err();
crate::test::assert_missing_field!(err, CreateHookBuilderError, "project");
}
#[test]
fn project_is_necessary() {
let err = CreateHook::builder().url("url").build().unwrap_err();
crate::test::assert_missing_field!(err, CreateHookBuilderError, "project");
}
#[test]
fn url_is_necessary() {
let err = CreateHook::builder()
.project("project")
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateHookBuilderError, "url");
}
#[test]
fn project_and_url_are_sufficient() {
CreateHook::builder()
.project("project")
.url("url")
.build()
.unwrap();
}
#[test]
fn endpoint() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str("url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_name() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&name=my-hook",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.name("my-hook")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_description() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&description=A+test+hook",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.description("A test hook")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_push_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&push_events=true",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.push_events(true)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_push_events_branch_filter() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&push_events_branch_filter=branch%2F*%2Ffilter",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.push_events_branch_filter("branch/*/filter")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_branch_filter_strategy() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&branch_filter_strategy=regex",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.branch_filter_strategy(BranchFilterStrategy::Regex)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_issues_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&issues_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.issues_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_confidential_issues_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&confidential_issues_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.confidential_issues_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_merge_requests_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&merge_requests_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.merge_requests_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_tag_push_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&tag_push_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.tag_push_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_note_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"¬e_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.note_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_confidential_note_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&confidential_note_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.confidential_note_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_job_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&job_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.job_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_pipeline_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&pipeline_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.pipeline_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_wiki_page_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&wiki_page_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.wiki_page_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_deployment_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&deployment_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.deployment_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_releases_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&releases_events=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.releases_events(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_feature_flag_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&feature_flag_events=true",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.feature_flag_events(true)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_resource_access_token_events() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&resource_access_token_events=true",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.resource_access_token_events(true)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_custom_webhook_template() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&custom_webhook_template=my-template-content",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.custom_webhook_template("my-template-content")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_custom_headers() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&custom_headers%5BX-Foo%5D=bar",
"&custom_headers%5BX-Another%5D=Value+With+Space",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.custom_header("X-Foo", "bar")
.custom_headers([("X-Another", "Value With Space")].iter().cloned())
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_enable_ssl_verification() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&enable_ssl_verification=false",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.enable_ssl_verification(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_token() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/hooks")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
"&token=secret",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateHook::builder()
.project("simple/project")
.url("https://test.invalid/path?some=foo")
.token("secret")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
}