gitlab/api/projects/hooks/
create.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use derive_builder::Builder;
8
9use crate::api::common::NameOrId;
10use crate::api::endpoint_prelude::*;
11use crate::api::ParamValue;
12
13/// The strategy to use for branch filter matching.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum BranchFilterStrategy {
16    /// Use wildcard matching for the branch filter.
17    Wildcard,
18    /// Use regular expression matching for the branch filter.
19    Regex,
20    /// Apply the hook to all branches.
21    AllBranches,
22}
23
24impl BranchFilterStrategy {
25    fn as_str(&self) -> &'static str {
26        match self {
27            BranchFilterStrategy::Wildcard => "wildcard",
28            BranchFilterStrategy::Regex => "regex",
29            BranchFilterStrategy::AllBranches => "all_branches",
30        }
31    }
32}
33
34impl ParamValue<'static> for BranchFilterStrategy {
35    fn as_value(&self) -> Cow<'static, str> {
36        self.as_str().into()
37    }
38}
39
40/// Create a new webhook for a project.
41#[derive(Debug, Builder, Clone)]
42#[builder(setter(strip_option))]
43pub struct CreateHook<'a> {
44    /// The project to create a webhook within.
45    #[builder(setter(into))]
46    project: NameOrId<'a>,
47
48    /// The URL for the webhook to contact.
49    #[builder(setter(into))]
50    url: Cow<'a, str>,
51    /// A name for the hook.
52    #[builder(setter(into), default)]
53    name: Option<Cow<'a, str>>,
54    /// A description for the hook.
55    #[builder(setter(into), default)]
56    description: Option<Cow<'a, str>>,
57
58    /// Whether to send push events for this webhook or not.
59    #[builder(default)]
60    push_events: Option<bool>,
61    /// Filter which branches send push events for this webhook.
62    #[builder(setter(into), default)]
63    push_events_branch_filter: Option<Cow<'a, str>>,
64    /// The strategy to use for `push_events_branch_filter`.
65    #[builder(setter(into), default)]
66    branch_filter_strategy: Option<BranchFilterStrategy>,
67    /// Whether to send issue events for this webhook or not.
68    #[builder(default)]
69    issues_events: Option<bool>,
70    /// Whether to send confidential issue events for this webhook or not.
71    #[builder(default)]
72    confidential_issues_events: Option<bool>,
73    /// Whether to send merge request events for this webhook or not.
74    #[builder(default)]
75    merge_requests_events: Option<bool>,
76    /// Whether to send tag events for this webhook or not.
77    #[builder(default)]
78    tag_push_events: Option<bool>,
79    /// Whether to send note (comment) events for this webhook or not.
80    #[builder(default)]
81    note_events: Option<bool>,
82    /// Whether to send confidential note (comment) events for this webhook or not.
83    #[builder(default)]
84    confidential_note_events: Option<bool>,
85    /// Whether to send job events for this webhook or not.
86    #[builder(default)]
87    job_events: Option<bool>,
88    /// Whether to send pipeline events for this webhook or not.
89    #[builder(default)]
90    pipeline_events: Option<bool>,
91    /// Whether to send wiki page events for this webhook or not.
92    #[builder(default)]
93    wiki_page_events: Option<bool>,
94    /// Whether to send deployment events for this webhook or not.
95    #[builder(default)]
96    deployment_events: Option<bool>,
97    /// Whether to send release events for this webhook or not.
98    #[builder(default)]
99    releases_events: Option<bool>,
100    /// Whether to send feature flag events for this webhook or not.
101    #[builder(default)]
102    feature_flag_events: Option<bool>,
103    /// Whether to send resource access token events for this webhook or not.
104    #[builder(default)]
105    resource_access_token_events: Option<bool>,
106
107    /// The template to use for the custom webhook.
108    #[builder(setter(into), default)]
109    custom_webhook_template: Option<Cow<'a, str>>,
110    /// Custom headers to send with the webhook.
111    #[builder(setter(name = "_custom_headers"), default, private)]
112    custom_headers: Vec<(Cow<'a, str>, Cow<'a, str>)>,
113
114    /// Whether to verify SSL/TLS certificates for the webhook endpoint or not.
115    #[builder(default)]
116    enable_ssl_verification: Option<bool>,
117    /// A secret token to include in webhook deliveries.
118    ///
119    /// This may be used to ensure that the webhook is actually coming from the GitLab instance.
120    #[builder(setter(into), default)]
121    token: Option<Cow<'a, str>>,
122}
123
124impl<'a> CreateHook<'a> {
125    /// Create a builder for the endpoint.
126    pub fn builder() -> CreateHookBuilder<'a> {
127        CreateHookBuilder::default()
128    }
129}
130
131impl<'a> CreateHookBuilder<'a> {
132    /// Add a single custom header.
133    pub fn custom_header<K, V>(&mut self, key: K, value: V) -> &mut Self
134    where
135        K: Into<Cow<'a, str>>,
136        V: Into<Cow<'a, str>>,
137    {
138        self.custom_headers
139            .get_or_insert_with(Vec::new)
140            .push((key.into(), value.into()));
141        self
142    }
143
144    /// Add multiple custom headers.
145    pub fn custom_headers<I, K, V>(&mut self, iter: I) -> &mut Self
146    where
147        I: Iterator<Item = (K, V)>,
148        K: Into<Cow<'a, str>>,
149        V: Into<Cow<'a, str>>,
150    {
151        self.custom_headers
152            .get_or_insert_with(Vec::new)
153            .extend(iter.map(|(k, v)| (k.into(), v.into())));
154        self
155    }
156}
157
158impl Endpoint for CreateHook<'_> {
159    fn method(&self) -> Method {
160        Method::POST
161    }
162
163    fn endpoint(&self) -> Cow<'static, str> {
164        format!("projects/{}/hooks", self.project).into()
165    }
166
167    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
168        let mut params = FormParams::default();
169
170        params
171            .push("url", &self.url)
172            .push_opt("name", self.name.as_ref())
173            .push_opt("description", self.description.as_ref())
174            .push_opt("push_events", self.push_events)
175            .push_opt(
176                "push_events_branch_filter",
177                self.push_events_branch_filter.as_ref(),
178            )
179            .push_opt("branch_filter_strategy", self.branch_filter_strategy)
180            .push_opt("issues_events", self.issues_events)
181            .push_opt(
182                "confidential_issues_events",
183                self.confidential_issues_events,
184            )
185            .push_opt("merge_requests_events", self.merge_requests_events)
186            .push_opt("tag_push_events", self.tag_push_events)
187            .push_opt("note_events", self.note_events)
188            .push_opt("confidential_note_events", self.confidential_note_events)
189            .push_opt("job_events", self.job_events)
190            .push_opt("pipeline_events", self.pipeline_events)
191            .push_opt("wiki_page_events", self.wiki_page_events)
192            .push_opt("deployment_events", self.deployment_events)
193            .push_opt("releases_events", self.releases_events)
194            .push_opt("feature_flag_events", self.feature_flag_events)
195            .push_opt(
196                "resource_access_token_events",
197                self.resource_access_token_events,
198            )
199            .push_opt(
200                "custom_webhook_template",
201                self.custom_webhook_template.as_ref(),
202            )
203            .push_opt("enable_ssl_verification", self.enable_ssl_verification)
204            .push_opt("token", self.token.as_ref());
205
206        for (key, value) in &self.custom_headers {
207            params.push(format!("custom_headers[{}]", key), value);
208        }
209
210        params.into_body()
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use http::Method;
217
218    use crate::api::projects::hooks::{BranchFilterStrategy, CreateHook, CreateHookBuilderError};
219    use crate::api::{self, Query};
220    use crate::test::client::{ExpectedUrl, SingleTestClient};
221
222    #[test]
223    fn test_project_branch_filter_strategy_as_str() {
224        let items = &[
225            (BranchFilterStrategy::Wildcard, "wildcard"),
226            (BranchFilterStrategy::Regex, "regex"),
227            (BranchFilterStrategy::AllBranches, "all_branches"),
228        ];
229
230        for (i, s) in items {
231            assert_eq!(i.as_str(), *s);
232        }
233    }
234
235    #[test]
236    fn project_and_url_are_necessary() {
237        let err = CreateHook::builder().build().unwrap_err();
238        crate::test::assert_missing_field!(err, CreateHookBuilderError, "project");
239    }
240
241    #[test]
242    fn project_is_necessary() {
243        let err = CreateHook::builder().url("url").build().unwrap_err();
244        crate::test::assert_missing_field!(err, CreateHookBuilderError, "project");
245    }
246
247    #[test]
248    fn url_is_necessary() {
249        let err = CreateHook::builder()
250            .project("project")
251            .build()
252            .unwrap_err();
253        crate::test::assert_missing_field!(err, CreateHookBuilderError, "url");
254    }
255
256    #[test]
257    fn project_and_url_are_sufficient() {
258        CreateHook::builder()
259            .project("project")
260            .url("url")
261            .build()
262            .unwrap();
263    }
264
265    #[test]
266    fn endpoint() {
267        let endpoint = ExpectedUrl::builder()
268            .method(Method::POST)
269            .endpoint("projects/simple%2Fproject/hooks")
270            .content_type("application/x-www-form-urlencoded")
271            .body_str("url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo")
272            .build()
273            .unwrap();
274        let client = SingleTestClient::new_raw(endpoint, "");
275
276        let endpoint = CreateHook::builder()
277            .project("simple/project")
278            .url("https://test.invalid/path?some=foo")
279            .build()
280            .unwrap();
281        api::ignore(endpoint).query(&client).unwrap();
282    }
283
284    #[test]
285    fn endpoint_name() {
286        let endpoint = ExpectedUrl::builder()
287            .method(Method::POST)
288            .endpoint("projects/simple%2Fproject/hooks")
289            .content_type("application/x-www-form-urlencoded")
290            .body_str(concat!(
291                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
292                "&name=my-hook",
293            ))
294            .build()
295            .unwrap();
296        let client = SingleTestClient::new_raw(endpoint, "");
297
298        let endpoint = CreateHook::builder()
299            .project("simple/project")
300            .url("https://test.invalid/path?some=foo")
301            .name("my-hook")
302            .build()
303            .unwrap();
304        api::ignore(endpoint).query(&client).unwrap();
305    }
306
307    #[test]
308    fn endpoint_description() {
309        let endpoint = ExpectedUrl::builder()
310            .method(Method::POST)
311            .endpoint("projects/simple%2Fproject/hooks")
312            .content_type("application/x-www-form-urlencoded")
313            .body_str(concat!(
314                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
315                "&description=A+test+hook",
316            ))
317            .build()
318            .unwrap();
319        let client = SingleTestClient::new_raw(endpoint, "");
320
321        let endpoint = CreateHook::builder()
322            .project("simple/project")
323            .url("https://test.invalid/path?some=foo")
324            .description("A test hook")
325            .build()
326            .unwrap();
327        api::ignore(endpoint).query(&client).unwrap();
328    }
329
330    #[test]
331    fn endpoint_push_events() {
332        let endpoint = ExpectedUrl::builder()
333            .method(Method::POST)
334            .endpoint("projects/simple%2Fproject/hooks")
335            .content_type("application/x-www-form-urlencoded")
336            .body_str(concat!(
337                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
338                "&push_events=true",
339            ))
340            .build()
341            .unwrap();
342        let client = SingleTestClient::new_raw(endpoint, "");
343
344        let endpoint = CreateHook::builder()
345            .project("simple/project")
346            .url("https://test.invalid/path?some=foo")
347            .push_events(true)
348            .build()
349            .unwrap();
350        api::ignore(endpoint).query(&client).unwrap();
351    }
352
353    #[test]
354    fn endpoint_push_events_branch_filter() {
355        let endpoint = ExpectedUrl::builder()
356            .method(Method::POST)
357            .endpoint("projects/simple%2Fproject/hooks")
358            .content_type("application/x-www-form-urlencoded")
359            .body_str(concat!(
360                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
361                "&push_events_branch_filter=branch%2F*%2Ffilter",
362            ))
363            .build()
364            .unwrap();
365        let client = SingleTestClient::new_raw(endpoint, "");
366
367        let endpoint = CreateHook::builder()
368            .project("simple/project")
369            .url("https://test.invalid/path?some=foo")
370            .push_events_branch_filter("branch/*/filter")
371            .build()
372            .unwrap();
373        api::ignore(endpoint).query(&client).unwrap();
374    }
375
376    #[test]
377    fn endpoint_branch_filter_strategy() {
378        let endpoint = ExpectedUrl::builder()
379            .method(Method::POST)
380            .endpoint("projects/simple%2Fproject/hooks")
381            .content_type("application/x-www-form-urlencoded")
382            .body_str(concat!(
383                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
384                "&branch_filter_strategy=regex",
385            ))
386            .build()
387            .unwrap();
388        let client = SingleTestClient::new_raw(endpoint, "");
389
390        let endpoint = CreateHook::builder()
391            .project("simple/project")
392            .url("https://test.invalid/path?some=foo")
393            .branch_filter_strategy(BranchFilterStrategy::Regex)
394            .build()
395            .unwrap();
396        api::ignore(endpoint).query(&client).unwrap();
397    }
398
399    #[test]
400    fn endpoint_issues_events() {
401        let endpoint = ExpectedUrl::builder()
402            .method(Method::POST)
403            .endpoint("projects/simple%2Fproject/hooks")
404            .content_type("application/x-www-form-urlencoded")
405            .body_str(concat!(
406                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
407                "&issues_events=false",
408            ))
409            .build()
410            .unwrap();
411        let client = SingleTestClient::new_raw(endpoint, "");
412
413        let endpoint = CreateHook::builder()
414            .project("simple/project")
415            .url("https://test.invalid/path?some=foo")
416            .issues_events(false)
417            .build()
418            .unwrap();
419        api::ignore(endpoint).query(&client).unwrap();
420    }
421
422    #[test]
423    fn endpoint_confidential_issues_events() {
424        let endpoint = ExpectedUrl::builder()
425            .method(Method::POST)
426            .endpoint("projects/simple%2Fproject/hooks")
427            .content_type("application/x-www-form-urlencoded")
428            .body_str(concat!(
429                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
430                "&confidential_issues_events=false",
431            ))
432            .build()
433            .unwrap();
434        let client = SingleTestClient::new_raw(endpoint, "");
435
436        let endpoint = CreateHook::builder()
437            .project("simple/project")
438            .url("https://test.invalid/path?some=foo")
439            .confidential_issues_events(false)
440            .build()
441            .unwrap();
442        api::ignore(endpoint).query(&client).unwrap();
443    }
444
445    #[test]
446    fn endpoint_merge_requests_events() {
447        let endpoint = ExpectedUrl::builder()
448            .method(Method::POST)
449            .endpoint("projects/simple%2Fproject/hooks")
450            .content_type("application/x-www-form-urlencoded")
451            .body_str(concat!(
452                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
453                "&merge_requests_events=false",
454            ))
455            .build()
456            .unwrap();
457        let client = SingleTestClient::new_raw(endpoint, "");
458
459        let endpoint = CreateHook::builder()
460            .project("simple/project")
461            .url("https://test.invalid/path?some=foo")
462            .merge_requests_events(false)
463            .build()
464            .unwrap();
465        api::ignore(endpoint).query(&client).unwrap();
466    }
467
468    #[test]
469    fn endpoint_tag_push_events() {
470        let endpoint = ExpectedUrl::builder()
471            .method(Method::POST)
472            .endpoint("projects/simple%2Fproject/hooks")
473            .content_type("application/x-www-form-urlencoded")
474            .body_str(concat!(
475                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
476                "&tag_push_events=false",
477            ))
478            .build()
479            .unwrap();
480        let client = SingleTestClient::new_raw(endpoint, "");
481
482        let endpoint = CreateHook::builder()
483            .project("simple/project")
484            .url("https://test.invalid/path?some=foo")
485            .tag_push_events(false)
486            .build()
487            .unwrap();
488        api::ignore(endpoint).query(&client).unwrap();
489    }
490
491    #[test]
492    fn endpoint_note_events() {
493        let endpoint = ExpectedUrl::builder()
494            .method(Method::POST)
495            .endpoint("projects/simple%2Fproject/hooks")
496            .content_type("application/x-www-form-urlencoded")
497            .body_str(concat!(
498                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
499                "&note_events=false",
500            ))
501            .build()
502            .unwrap();
503        let client = SingleTestClient::new_raw(endpoint, "");
504
505        let endpoint = CreateHook::builder()
506            .project("simple/project")
507            .url("https://test.invalid/path?some=foo")
508            .note_events(false)
509            .build()
510            .unwrap();
511        api::ignore(endpoint).query(&client).unwrap();
512    }
513
514    #[test]
515    fn endpoint_confidential_note_events() {
516        let endpoint = ExpectedUrl::builder()
517            .method(Method::POST)
518            .endpoint("projects/simple%2Fproject/hooks")
519            .content_type("application/x-www-form-urlencoded")
520            .body_str(concat!(
521                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
522                "&confidential_note_events=false",
523            ))
524            .build()
525            .unwrap();
526        let client = SingleTestClient::new_raw(endpoint, "");
527
528        let endpoint = CreateHook::builder()
529            .project("simple/project")
530            .url("https://test.invalid/path?some=foo")
531            .confidential_note_events(false)
532            .build()
533            .unwrap();
534        api::ignore(endpoint).query(&client).unwrap();
535    }
536
537    #[test]
538    fn endpoint_job_events() {
539        let endpoint = ExpectedUrl::builder()
540            .method(Method::POST)
541            .endpoint("projects/simple%2Fproject/hooks")
542            .content_type("application/x-www-form-urlencoded")
543            .body_str(concat!(
544                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
545                "&job_events=false",
546            ))
547            .build()
548            .unwrap();
549        let client = SingleTestClient::new_raw(endpoint, "");
550
551        let endpoint = CreateHook::builder()
552            .project("simple/project")
553            .url("https://test.invalid/path?some=foo")
554            .job_events(false)
555            .build()
556            .unwrap();
557        api::ignore(endpoint).query(&client).unwrap();
558    }
559
560    #[test]
561    fn endpoint_pipeline_events() {
562        let endpoint = ExpectedUrl::builder()
563            .method(Method::POST)
564            .endpoint("projects/simple%2Fproject/hooks")
565            .content_type("application/x-www-form-urlencoded")
566            .body_str(concat!(
567                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
568                "&pipeline_events=false",
569            ))
570            .build()
571            .unwrap();
572        let client = SingleTestClient::new_raw(endpoint, "");
573
574        let endpoint = CreateHook::builder()
575            .project("simple/project")
576            .url("https://test.invalid/path?some=foo")
577            .pipeline_events(false)
578            .build()
579            .unwrap();
580        api::ignore(endpoint).query(&client).unwrap();
581    }
582
583    #[test]
584    fn endpoint_wiki_page_events() {
585        let endpoint = ExpectedUrl::builder()
586            .method(Method::POST)
587            .endpoint("projects/simple%2Fproject/hooks")
588            .content_type("application/x-www-form-urlencoded")
589            .body_str(concat!(
590                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
591                "&wiki_page_events=false",
592            ))
593            .build()
594            .unwrap();
595        let client = SingleTestClient::new_raw(endpoint, "");
596
597        let endpoint = CreateHook::builder()
598            .project("simple/project")
599            .url("https://test.invalid/path?some=foo")
600            .wiki_page_events(false)
601            .build()
602            .unwrap();
603        api::ignore(endpoint).query(&client).unwrap();
604    }
605
606    #[test]
607    fn endpoint_deployment_events() {
608        let endpoint = ExpectedUrl::builder()
609            .method(Method::POST)
610            .endpoint("projects/simple%2Fproject/hooks")
611            .content_type("application/x-www-form-urlencoded")
612            .body_str(concat!(
613                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
614                "&deployment_events=false",
615            ))
616            .build()
617            .unwrap();
618        let client = SingleTestClient::new_raw(endpoint, "");
619
620        let endpoint = CreateHook::builder()
621            .project("simple/project")
622            .url("https://test.invalid/path?some=foo")
623            .deployment_events(false)
624            .build()
625            .unwrap();
626        api::ignore(endpoint).query(&client).unwrap();
627    }
628
629    #[test]
630    fn endpoint_releases_events() {
631        let endpoint = ExpectedUrl::builder()
632            .method(Method::POST)
633            .endpoint("projects/simple%2Fproject/hooks")
634            .content_type("application/x-www-form-urlencoded")
635            .body_str(concat!(
636                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
637                "&releases_events=false",
638            ))
639            .build()
640            .unwrap();
641        let client = SingleTestClient::new_raw(endpoint, "");
642
643        let endpoint = CreateHook::builder()
644            .project("simple/project")
645            .url("https://test.invalid/path?some=foo")
646            .releases_events(false)
647            .build()
648            .unwrap();
649        api::ignore(endpoint).query(&client).unwrap();
650    }
651
652    #[test]
653    fn endpoint_feature_flag_events() {
654        let endpoint = ExpectedUrl::builder()
655            .method(Method::POST)
656            .endpoint("projects/simple%2Fproject/hooks")
657            .content_type("application/x-www-form-urlencoded")
658            .body_str(concat!(
659                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
660                "&feature_flag_events=true",
661            ))
662            .build()
663            .unwrap();
664        let client = SingleTestClient::new_raw(endpoint, "");
665
666        let endpoint = CreateHook::builder()
667            .project("simple/project")
668            .url("https://test.invalid/path?some=foo")
669            .feature_flag_events(true)
670            .build()
671            .unwrap();
672        api::ignore(endpoint).query(&client).unwrap();
673    }
674
675    #[test]
676    fn endpoint_resource_access_token_events() {
677        let endpoint = ExpectedUrl::builder()
678            .method(Method::POST)
679            .endpoint("projects/simple%2Fproject/hooks")
680            .content_type("application/x-www-form-urlencoded")
681            .body_str(concat!(
682                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
683                "&resource_access_token_events=true",
684            ))
685            .build()
686            .unwrap();
687        let client = SingleTestClient::new_raw(endpoint, "");
688
689        let endpoint = CreateHook::builder()
690            .project("simple/project")
691            .url("https://test.invalid/path?some=foo")
692            .resource_access_token_events(true)
693            .build()
694            .unwrap();
695        api::ignore(endpoint).query(&client).unwrap();
696    }
697
698    #[test]
699    fn endpoint_custom_webhook_template() {
700        let endpoint = ExpectedUrl::builder()
701            .method(Method::POST)
702            .endpoint("projects/simple%2Fproject/hooks")
703            .content_type("application/x-www-form-urlencoded")
704            .body_str(concat!(
705                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
706                "&custom_webhook_template=my-template-content",
707            ))
708            .build()
709            .unwrap();
710        let client = SingleTestClient::new_raw(endpoint, "");
711
712        let endpoint = CreateHook::builder()
713            .project("simple/project")
714            .url("https://test.invalid/path?some=foo")
715            .custom_webhook_template("my-template-content")
716            .build()
717            .unwrap();
718        api::ignore(endpoint).query(&client).unwrap();
719    }
720
721    #[test]
722    fn endpoint_custom_headers() {
723        let endpoint = ExpectedUrl::builder()
724            .method(Method::POST)
725            .endpoint("projects/simple%2Fproject/hooks")
726            .content_type("application/x-www-form-urlencoded")
727            .body_str(concat!(
728                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
729                "&custom_headers%5BX-Foo%5D=bar",
730                "&custom_headers%5BX-Another%5D=Value+With+Space",
731            ))
732            .build()
733            .unwrap();
734        let client = SingleTestClient::new_raw(endpoint, "");
735
736        let endpoint = CreateHook::builder()
737            .project("simple/project")
738            .url("https://test.invalid/path?some=foo")
739            .custom_header("X-Foo", "bar")
740            .custom_headers([("X-Another", "Value With Space")].iter().cloned())
741            .build()
742            .unwrap();
743        api::ignore(endpoint).query(&client).unwrap();
744    }
745
746    #[test]
747    fn endpoint_enable_ssl_verification() {
748        let endpoint = ExpectedUrl::builder()
749            .method(Method::POST)
750            .endpoint("projects/simple%2Fproject/hooks")
751            .content_type("application/x-www-form-urlencoded")
752            .body_str(concat!(
753                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
754                "&enable_ssl_verification=false",
755            ))
756            .build()
757            .unwrap();
758        let client = SingleTestClient::new_raw(endpoint, "");
759
760        let endpoint = CreateHook::builder()
761            .project("simple/project")
762            .url("https://test.invalid/path?some=foo")
763            .enable_ssl_verification(false)
764            .build()
765            .unwrap();
766        api::ignore(endpoint).query(&client).unwrap();
767    }
768
769    #[test]
770    fn endpoint_token() {
771        let endpoint = ExpectedUrl::builder()
772            .method(Method::POST)
773            .endpoint("projects/simple%2Fproject/hooks")
774            .content_type("application/x-www-form-urlencoded")
775            .body_str(concat!(
776                "url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
777                "&token=secret",
778            ))
779            .build()
780            .unwrap();
781        let client = SingleTestClient::new_raw(endpoint, "");
782
783        let endpoint = CreateHook::builder()
784            .project("simple/project")
785            .url("https://test.invalid/path?some=foo")
786            .token("secret")
787            .build()
788            .unwrap();
789        api::ignore(endpoint).query(&client).unwrap();
790    }
791}