gitlab/api/projects/merge_requests/
merge_requests.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 std::collections::BTreeSet;
8use std::iter;
9
10use chrono::{DateTime, Utc};
11use derive_builder::Builder;
12
13use crate::api::common::{NameOrId, SortOrder, YesNo};
14use crate::api::endpoint_prelude::*;
15use crate::api::helpers::{Labels, ReactionEmoji};
16use crate::api::merge_requests::{
17    ApproverIds, Assignee, MergeRequestMilestone, MergeRequestOrderBy, MergeRequestScope,
18    MergeRequestState, MergeRequestView,
19};
20
21#[derive(Debug, Clone)]
22#[non_exhaustive]
23enum ApprovedBy<'a> {
24    Any,
25    None,
26    AllOfIds(BTreeSet<u64>),
27    AllOfUsernames(BTreeSet<Cow<'a, str>>),
28}
29
30impl ApprovedBy<'_> {
31    fn add_params<'b>(&'b self, params: &mut QueryParams<'b>) {
32        match self {
33            ApprovedBy::Any => {
34                params.push("approved_by_ids", "Any");
35            },
36            ApprovedBy::None => {
37                params.push("approved_by_ids", "None");
38            },
39            ApprovedBy::AllOfIds(ids) => {
40                params.extend(ids.iter().map(|&id| ("approved_by_ids[]", id)));
41            },
42            ApprovedBy::AllOfUsernames(usernames) => {
43                params.extend(
44                    usernames
45                        .iter()
46                        .map(|username| ("approved_by_usernames[]", username)),
47                );
48            },
49        }
50    }
51}
52
53/// Query for merge requests within a project.
54///
55/// TODO: Negation (not) filters are not yet supported.
56#[derive(Debug, Builder, Clone)]
57#[builder(setter(strip_option))]
58pub struct MergeRequests<'a> {
59    /// The project to query for merge requests.
60    #[builder(setter(into))]
61    project: NameOrId<'a>,
62
63    /// Filter merge requests with specific internal IDs.
64    #[builder(setter(name = "_iids"), default, private)]
65    iids: BTreeSet<u64>,
66    /// Filter merge requests based on state.
67    #[builder(default)]
68    state: Option<MergeRequestState>,
69    /// Filter merge requests with a milestone title.
70    #[builder(setter(name = "_milestone"), default, private)]
71    milestone: Option<MergeRequestMilestone<'a>>,
72    /// The view of the merge request.
73    ///
74    /// This field can restrict the amount of data returned.
75    #[builder(default)]
76    view: Option<MergeRequestView>,
77    /// Filter merge requests based on labels.
78    #[builder(setter(name = "_labels"), default, private)]
79    labels: Option<Labels<'a>>,
80    /// Include label details in the result.
81    #[builder(default)]
82    with_labels_details: Option<bool>,
83    /// Request that the merge status field be updated.
84    #[builder(default)]
85    with_merge_status_recheck: Option<bool>,
86    /// Filter merge requests created after a point in time.
87    #[builder(default)]
88    created_after: Option<DateTime<Utc>>,
89    /// Filter merge requests created before a point in time.
90    #[builder(default)]
91    created_before: Option<DateTime<Utc>>,
92    /// Filter merge requests last updated after a point in time.
93    #[builder(default)]
94    updated_after: Option<DateTime<Utc>>,
95    /// Filter merge requests last updated before a point in time.
96    #[builder(default)]
97    updated_before: Option<DateTime<Utc>>,
98    /// Filter merge requests within a scope.
99    #[builder(default)]
100    scope: Option<MergeRequestScope>,
101    /// Filter merge requests by author.
102    #[builder(setter(into), default)]
103    author: Option<NameOrId<'a>>,
104    /// Filter merge requests by assignees.
105    #[builder(setter(name = "_assignee"), default, private)]
106    assignee: Option<Assignee>,
107    /// Filter merge requests by approvers.
108    #[builder(setter(name = "_approver_ids"), default, private)]
109    approver_ids: Option<ApproverIds>,
110    /// Filter merge requests by approvals.
111    #[builder(setter(name = "_approved_by"), default, private)]
112    approved_by: Option<ApprovedBy<'a>>,
113    /// Filter merge requests by approved state.
114    #[builder(default, setter(into))]
115    approved: Option<YesNo>,
116    /// Filter merge requests by reviewers.
117    #[builder(setter(into), default)]
118    reviewer: Option<NameOrId<'a>>,
119    /// Filter merge requests by the API caller's reactions.
120    #[builder(setter(name = "_my_reaction_emoji"), default, private)]
121    my_reaction_emoji: Option<ReactionEmoji<'a>>,
122    /// Filter merge requests by source branch.
123    #[builder(setter(into), default)]
124    source_branch: Option<Cow<'a, str>>,
125    /// Filter merge requests by target branch.
126    #[builder(setter(into), default)]
127    target_branch: Option<Cow<'a, str>>,
128    /// Filter merge requests by WIP state
129    #[builder(setter(into), default)]
130    wip: Option<YesNo>,
131    /// Filter merge requests by deployed environment.
132    #[builder(setter(into), default)]
133    environment: Option<Cow<'a, str>>,
134    /// Filter merge requests by those deployed after a point in time.
135    #[builder(default)]
136    deployed_after: Option<DateTime<Utc>>,
137    /// Filter merge requests by those deployed before a point in time.
138    #[builder(default)]
139    deployed_before: Option<DateTime<Utc>>,
140
141    // TODO: support `not`
142    /// Filter merge requests with a search query.
143    #[builder(setter(into), default)]
144    search: Option<Cow<'a, str>>,
145
146    /// Order results by a given key.
147    #[builder(default)]
148    order_by: Option<MergeRequestOrderBy>,
149    /// The sort order for returned results.
150    #[builder(default)]
151    sort: Option<SortOrder>,
152}
153
154impl<'a> MergeRequests<'a> {
155    /// Create a builder for the endpoint.
156    pub fn builder() -> MergeRequestsBuilder<'a> {
157        MergeRequestsBuilder::default()
158    }
159}
160
161impl<'a> MergeRequestsBuilder<'a> {
162    /// Return a merge request with an internal ID.
163    pub fn iid(&mut self, iid: u64) -> &mut Self {
164        self.iids.get_or_insert_with(BTreeSet::new).insert(iid);
165        self
166    }
167
168    /// Return merge requests with one of a set of internal IDs.
169    pub fn iids<I>(&mut self, iter: I) -> &mut Self
170    where
171        I: Iterator<Item = u64>,
172    {
173        self.iids.get_or_insert_with(BTreeSet::new).extend(iter);
174        self
175    }
176
177    /// Filter unlabeled merge requests.
178    pub fn unlabeled(&mut self) -> &mut Self {
179        self.labels = Some(Some(Labels::None));
180        self
181    }
182
183    /// Filter merge requests with any label.
184    pub fn with_any_label(&mut self) -> &mut Self {
185        self.labels = Some(Some(Labels::Any));
186        self
187    }
188
189    /// Filter merge requests with a given label.
190    pub fn label<L>(&mut self, label: L) -> &mut Self
191    where
192        L: Into<Cow<'a, str>>,
193    {
194        let label = label.into();
195        let labels = if let Some(Some(Labels::AllOf(mut set))) = self.labels.take() {
196            set.push(label);
197            set
198        } else {
199            iter::once(label).collect()
200        };
201        self.labels = Some(Some(Labels::AllOf(labels)));
202        self
203    }
204
205    /// Filter merge requests with all of the given labels.
206    pub fn labels<I, L>(&mut self, iter: I) -> &mut Self
207    where
208        I: IntoIterator<Item = L>,
209        L: Into<Cow<'a, str>>,
210    {
211        let iter = iter.into_iter().map(Into::into);
212        let labels = if let Some(Some(Labels::AllOf(mut set))) = self.labels.take() {
213            set.extend(iter);
214            set
215        } else {
216            iter.collect()
217        };
218        self.labels = Some(Some(Labels::AllOf(labels)));
219        self
220    }
221
222    /// Filter merge requests without a milestone.
223    pub fn without_milestone(&mut self) -> &mut Self {
224        self.milestone = Some(Some(MergeRequestMilestone::None));
225        self
226    }
227
228    /// Filter merge requests with any milestone.
229    pub fn any_milestone(&mut self) -> &mut Self {
230        self.milestone = Some(Some(MergeRequestMilestone::Any));
231        self
232    }
233
234    /// Filter merge requests with a given milestone.
235    pub fn milestone<M>(&mut self, milestone: M) -> &mut Self
236    where
237        M: Into<Cow<'a, str>>,
238    {
239        self.milestone = Some(Some(MergeRequestMilestone::Named(milestone.into())));
240        self
241    }
242
243    /// Filter unassigned merge requests.
244    pub fn unassigned(&mut self) -> &mut Self {
245        self.assignee = Some(Some(Assignee::Unassigned));
246        self
247    }
248
249    /// Filter assigned merge requests.
250    pub fn assigned(&mut self) -> &mut Self {
251        self.assignee = Some(Some(Assignee::Assigned));
252        self
253    }
254
255    /// Filter merge requests assigned to a user (by ID).
256    pub fn assignee_id(&mut self, assignee: u64) -> &mut Self {
257        self.assignee = Some(Some(Assignee::Id(assignee)));
258        self
259    }
260
261    /// Filter merge requests which have no approvers.
262    pub fn no_approvers(&mut self) -> &mut Self {
263        self.approver_ids = Some(Some(ApproverIds::None));
264        self
265    }
266
267    /// Filter merge requests which have any approver(s).
268    pub fn any_approvers(&mut self) -> &mut Self {
269        self.approver_ids = Some(Some(ApproverIds::Any));
270        self
271    }
272
273    /// Filter merge requests with a specified approver (by ID).
274    pub fn approver_id(&mut self, approver: u64) -> &mut Self {
275        let approver_ids = if let Some(Some(ApproverIds::AllOf(mut set))) = self.approver_ids.take()
276        {
277            set.insert(approver);
278            set
279        } else {
280            [approver].iter().copied().collect()
281        };
282        self.approver_ids = Some(Some(ApproverIds::AllOf(approver_ids)));
283        self
284    }
285
286    /// Filter merge requests with specified approver (by ID).
287    pub fn approver_ids<I>(&mut self, iter: I) -> &mut Self
288    where
289        I: Iterator<Item = u64>,
290    {
291        let approver_ids = if let Some(Some(ApproverIds::AllOf(mut set))) = self.approver_ids.take()
292        {
293            set.extend(iter);
294            set
295        } else {
296            iter.collect()
297        };
298        self.approver_ids = Some(Some(ApproverIds::AllOf(approver_ids)));
299        self
300    }
301
302    /// Filter merge requests without approvals.
303    pub fn no_approvals(&mut self) -> &mut Self {
304        self.approved_by = Some(Some(ApprovedBy::None));
305        self
306    }
307
308    /// Filter merge requests with any approvals.
309    pub fn any_approvals(&mut self) -> &mut Self {
310        self.approved_by = Some(Some(ApprovedBy::Any));
311        self
312    }
313
314    /// Filter merge requests approved by a specific user (by ID).
315    ///
316    /// Note: Mutually exclusive to querying by usernames.
317    pub fn approved_by_id(&mut self, approved_by: u64) -> &mut Self {
318        let approved_by = if let Some(Some(ApprovedBy::AllOfIds(mut set))) = self.approved_by.take()
319        {
320            set.insert(approved_by);
321            set
322        } else {
323            [approved_by].iter().copied().collect()
324        };
325        self.approved_by = Some(Some(ApprovedBy::AllOfIds(approved_by)));
326        self
327    }
328
329    /// Filter merge requests approved by a specific set of users (by ID).
330    ///
331    /// Note: Mutually exclusive to querying by usernames.
332    pub fn approved_by_ids<I>(&mut self, iter: I) -> &mut Self
333    where
334        I: Iterator<Item = u64>,
335    {
336        let approved_by_ids =
337            if let Some(Some(ApprovedBy::AllOfIds(mut set))) = self.approved_by.take() {
338                set.extend(iter);
339                set
340            } else {
341                iter.collect()
342            };
343        self.approved_by = Some(Some(ApprovedBy::AllOfIds(approved_by_ids)));
344        self
345    }
346
347    /// Filter merge requests approved by a specific user (by username).
348    ///
349    /// Note: Mutually exclusive to querying by IDs.
350    pub fn approved_by_username<U>(&mut self, username: U) -> &mut Self
351    where
352        U: Into<Cow<'a, str>>,
353    {
354        let approved_by_usernames =
355            if let Some(Some(ApprovedBy::AllOfUsernames(mut set))) = self.approved_by.take() {
356                set.insert(username.into());
357                set
358            } else {
359                [username.into()].iter().cloned().collect()
360            };
361        self.approved_by = Some(Some(ApprovedBy::AllOfUsernames(approved_by_usernames)));
362        self
363    }
364
365    /// Filter merge requests approved by a specific set of users (by username).
366    ///
367    /// Note: Mutually exclusive to querying by IDs.
368    pub fn approved_by_usernames<I, U>(&mut self, iter: I) -> &mut Self
369    where
370        I: Iterator<Item = U>,
371        U: Into<Cow<'a, str>>,
372    {
373        let approved_by_usernames =
374            if let Some(Some(ApprovedBy::AllOfUsernames(mut set))) = self.approved_by.take() {
375                set.extend(iter.map(Into::into));
376                set
377            } else {
378                iter.map(Into::into).collect()
379            };
380        self.approved_by = Some(Some(ApprovedBy::AllOfUsernames(approved_by_usernames)));
381        self
382    }
383
384    /// Filter merge requests without a reaction by the API caller.
385    pub fn no_reaction(&mut self) -> &mut Self {
386        self.my_reaction_emoji = Some(Some(ReactionEmoji::None));
387        self
388    }
389
390    /// Filter merge requests with any reaction by the API caller.
391    pub fn any_reaction(&mut self) -> &mut Self {
392        self.my_reaction_emoji = Some(Some(ReactionEmoji::Any));
393        self
394    }
395
396    /// Filter merge requests with a specific reaction by the API caller.
397    pub fn my_reaction<E>(&mut self, emoji: E) -> &mut Self
398    where
399        E: Into<Cow<'a, str>>,
400    {
401        self.my_reaction_emoji = Some(Some(ReactionEmoji::Emoji(emoji.into())));
402        self
403    }
404}
405
406impl Endpoint for MergeRequests<'_> {
407    fn method(&self) -> Method {
408        Method::GET
409    }
410
411    fn endpoint(&self) -> Cow<'static, str> {
412        format!("projects/{}/merge_requests", self.project).into()
413    }
414
415    fn parameters(&self) -> QueryParams {
416        let mut params = QueryParams::default();
417
418        params
419            .extend(self.iids.iter().map(|&value| ("iids[]", value)))
420            .push_opt("state", self.state)
421            .push_opt("milestone", self.milestone.as_ref())
422            .push_opt("view", self.view)
423            .push_opt("labels", self.labels.as_ref())
424            .push_opt("with_labels_details", self.with_labels_details)
425            .push_opt("with_merge_status_recheck", self.with_merge_status_recheck)
426            .push_opt("created_after", self.created_after)
427            .push_opt("created_before", self.created_before)
428            .push_opt("updated_after", self.updated_after)
429            .push_opt("updated_before", self.updated_before)
430            .push_opt("scope", self.scope)
431            .push_opt("approved", self.approved)
432            .push_opt("my_reaction_emoji", self.my_reaction_emoji.as_ref())
433            .push_opt("source_branch", self.source_branch.as_ref())
434            .push_opt("target_branch", self.target_branch.as_ref())
435            .push_opt("search", self.search.as_ref())
436            .push_opt("wip", self.wip)
437            .push_opt("environment", self.environment.as_ref())
438            .push_opt("deployed_after", self.deployed_after)
439            .push_opt("deployed_before", self.deployed_before)
440            .push_opt("order_by", self.order_by)
441            .push_opt("sort", self.sort);
442
443        if let Some(author) = self.author.as_ref() {
444            match author {
445                NameOrId::Name(name) => {
446                    params.push("author_username", name);
447                },
448                NameOrId::Id(id) => {
449                    params.push("author_id", *id);
450                },
451            }
452        }
453        if let Some(assignee) = self.assignee.as_ref() {
454            assignee.add_params(&mut params);
455        }
456        if let Some(approver_ids) = self.approver_ids.as_ref() {
457            approver_ids.add_params(&mut params);
458        }
459        if let Some(approved_by) = self.approved_by.as_ref() {
460            approved_by.add_params(&mut params);
461        }
462        if let Some(reviewer) = self.reviewer.as_ref() {
463            match reviewer {
464                NameOrId::Name(name) => {
465                    params.push("reviewer_username", name);
466                },
467                NameOrId::Id(id) => {
468                    params.push("reviewer_id", *id);
469                },
470            }
471        }
472
473        params
474    }
475}
476
477impl Pageable for MergeRequests<'_> {}
478
479#[cfg(test)]
480mod tests {
481    use chrono::{TimeZone, Utc};
482
483    use crate::api::common::{SortOrder, YesNo};
484    use crate::api::projects::merge_requests::{
485        MergeRequestOrderBy, MergeRequestScope, MergeRequestState, MergeRequestView, MergeRequests,
486        MergeRequestsBuilderError,
487    };
488    use crate::api::{self, Query};
489    use crate::test::client::{ExpectedUrl, SingleTestClient};
490
491    #[test]
492    fn project_is_needed() {
493        let err = MergeRequests::builder().build().unwrap_err();
494        crate::test::assert_missing_field!(err, MergeRequestsBuilderError, "project");
495    }
496
497    #[test]
498    fn project_is_sufficient() {
499        MergeRequests::builder().project(1).build().unwrap();
500    }
501
502    #[test]
503    fn endpoint() {
504        let endpoint = ExpectedUrl::builder()
505            .endpoint("projects/simple%2Fproject/merge_requests")
506            .build()
507            .unwrap();
508        let client = SingleTestClient::new_raw(endpoint, "");
509
510        let endpoint = MergeRequests::builder()
511            .project("simple/project")
512            .build()
513            .unwrap();
514        api::ignore(endpoint).query(&client).unwrap();
515    }
516
517    #[test]
518    fn endpoint_iids() {
519        let endpoint = ExpectedUrl::builder()
520            .endpoint("projects/simple%2Fproject/merge_requests")
521            .add_query_params(&[("iids[]", "1"), ("iids[]", "2")])
522            .build()
523            .unwrap();
524        let client = SingleTestClient::new_raw(endpoint, "");
525
526        let endpoint = MergeRequests::builder()
527            .project("simple/project")
528            .iid(1)
529            .iids([1, 2].iter().copied())
530            .build()
531            .unwrap();
532        api::ignore(endpoint).query(&client).unwrap();
533    }
534
535    #[test]
536    fn endpoint_state() {
537        let endpoint = ExpectedUrl::builder()
538            .endpoint("projects/simple%2Fproject/merge_requests")
539            .add_query_params(&[("state", "locked")])
540            .build()
541            .unwrap();
542        let client = SingleTestClient::new_raw(endpoint, "");
543
544        let endpoint = MergeRequests::builder()
545            .project("simple/project")
546            .state(MergeRequestState::Locked)
547            .build()
548            .unwrap();
549        api::ignore(endpoint).query(&client).unwrap();
550    }
551
552    #[test]
553    fn endpoint_milestone_none() {
554        let endpoint = ExpectedUrl::builder()
555            .endpoint("projects/simple%2Fproject/merge_requests")
556            .add_query_params(&[("milestone", "None")])
557            .build()
558            .unwrap();
559        let client = SingleTestClient::new_raw(endpoint, "");
560
561        let endpoint = MergeRequests::builder()
562            .project("simple/project")
563            .without_milestone()
564            .build()
565            .unwrap();
566        api::ignore(endpoint).query(&client).unwrap();
567    }
568
569    #[test]
570    fn endpoint_milestone_any() {
571        let endpoint = ExpectedUrl::builder()
572            .endpoint("projects/simple%2Fproject/merge_requests")
573            .add_query_params(&[("milestone", "Any")])
574            .build()
575            .unwrap();
576        let client = SingleTestClient::new_raw(endpoint, "");
577
578        let endpoint = MergeRequests::builder()
579            .project("simple/project")
580            .any_milestone()
581            .build()
582            .unwrap();
583        api::ignore(endpoint).query(&client).unwrap();
584    }
585
586    #[test]
587    fn endpoint_milestone() {
588        let endpoint = ExpectedUrl::builder()
589            .endpoint("projects/simple%2Fproject/merge_requests")
590            .add_query_params(&[("milestone", "1.0")])
591            .build()
592            .unwrap();
593        let client = SingleTestClient::new_raw(endpoint, "");
594
595        let endpoint = MergeRequests::builder()
596            .project("simple/project")
597            .milestone("1.0")
598            .build()
599            .unwrap();
600        api::ignore(endpoint).query(&client).unwrap();
601    }
602
603    #[test]
604    fn endpoint_view() {
605        let endpoint = ExpectedUrl::builder()
606            .endpoint("projects/simple%2Fproject/merge_requests")
607            .add_query_params(&[("view", "simple")])
608            .build()
609            .unwrap();
610        let client = SingleTestClient::new_raw(endpoint, "");
611
612        let endpoint = MergeRequests::builder()
613            .project("simple/project")
614            .view(MergeRequestView::Simple)
615            .build()
616            .unwrap();
617        api::ignore(endpoint).query(&client).unwrap();
618    }
619
620    #[test]
621    fn endpoint_labels() {
622        let endpoint = ExpectedUrl::builder()
623            .endpoint("projects/simple%2Fproject/merge_requests")
624            .add_query_params(&[("labels", "label,label1,label2")])
625            .build()
626            .unwrap();
627        let client = SingleTestClient::new_raw(endpoint, "");
628
629        let endpoint = MergeRequests::builder()
630            .project("simple/project")
631            .label("label")
632            .labels(["label1", "label2"].iter().cloned())
633            .build()
634            .unwrap();
635        api::ignore(endpoint).query(&client).unwrap();
636    }
637
638    #[test]
639    fn endpoint_labels_unlabeled() {
640        let endpoint = ExpectedUrl::builder()
641            .endpoint("projects/simple%2Fproject/merge_requests")
642            .add_query_params(&[("labels", "None")])
643            .build()
644            .unwrap();
645        let client = SingleTestClient::new_raw(endpoint, "");
646
647        let endpoint = MergeRequests::builder()
648            .project("simple/project")
649            .unlabeled()
650            .build()
651            .unwrap();
652        api::ignore(endpoint).query(&client).unwrap();
653    }
654
655    #[test]
656    fn endpoint_labels_any() {
657        let endpoint = ExpectedUrl::builder()
658            .endpoint("projects/simple%2Fproject/merge_requests")
659            .add_query_params(&[("labels", "Any")])
660            .build()
661            .unwrap();
662        let client = SingleTestClient::new_raw(endpoint, "");
663
664        let endpoint = MergeRequests::builder()
665            .project("simple/project")
666            .with_any_label()
667            .build()
668            .unwrap();
669        api::ignore(endpoint).query(&client).unwrap();
670    }
671
672    #[test]
673    fn endpoint_with_labels_details() {
674        let endpoint = ExpectedUrl::builder()
675            .endpoint("projects/simple%2Fproject/merge_requests")
676            .add_query_params(&[("with_labels_details", "true")])
677            .build()
678            .unwrap();
679        let client = SingleTestClient::new_raw(endpoint, "");
680
681        let endpoint = MergeRequests::builder()
682            .project("simple/project")
683            .with_labels_details(true)
684            .build()
685            .unwrap();
686        api::ignore(endpoint).query(&client).unwrap();
687    }
688
689    #[test]
690    fn endpoint_with_merge_status_recheck() {
691        let endpoint = ExpectedUrl::builder()
692            .endpoint("projects/simple%2Fproject/merge_requests")
693            .add_query_params(&[("with_merge_status_recheck", "true")])
694            .build()
695            .unwrap();
696        let client = SingleTestClient::new_raw(endpoint, "");
697
698        let endpoint = MergeRequests::builder()
699            .project("simple/project")
700            .with_merge_status_recheck(true)
701            .build()
702            .unwrap();
703        api::ignore(endpoint).query(&client).unwrap();
704    }
705
706    #[test]
707    fn endpoint_created_after() {
708        let endpoint = ExpectedUrl::builder()
709            .endpoint("projects/simple%2Fproject/merge_requests")
710            .add_query_params(&[("created_after", "2020-01-01T00:00:00Z")])
711            .build()
712            .unwrap();
713        let client = SingleTestClient::new_raw(endpoint, "");
714
715        let endpoint = MergeRequests::builder()
716            .project("simple/project")
717            .created_after(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
718            .build()
719            .unwrap();
720        api::ignore(endpoint).query(&client).unwrap();
721    }
722
723    #[test]
724    fn endpoint_created_before() {
725        let endpoint = ExpectedUrl::builder()
726            .endpoint("projects/simple%2Fproject/merge_requests")
727            .add_query_params(&[("created_before", "2020-01-01T00:00:00Z")])
728            .build()
729            .unwrap();
730        let client = SingleTestClient::new_raw(endpoint, "");
731
732        let endpoint = MergeRequests::builder()
733            .project("simple/project")
734            .created_before(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
735            .build()
736            .unwrap();
737        api::ignore(endpoint).query(&client).unwrap();
738    }
739
740    #[test]
741    fn endpoint_updated_after() {
742        let endpoint = ExpectedUrl::builder()
743            .endpoint("projects/simple%2Fproject/merge_requests")
744            .add_query_params(&[("updated_after", "2020-01-01T00:00:00Z")])
745            .build()
746            .unwrap();
747        let client = SingleTestClient::new_raw(endpoint, "");
748
749        let endpoint = MergeRequests::builder()
750            .project("simple/project")
751            .updated_after(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
752            .build()
753            .unwrap();
754        api::ignore(endpoint).query(&client).unwrap();
755    }
756
757    #[test]
758    fn endpoint_updated_before() {
759        let endpoint = ExpectedUrl::builder()
760            .endpoint("projects/simple%2Fproject/merge_requests")
761            .add_query_params(&[("updated_before", "2020-01-01T00:00:00Z")])
762            .build()
763            .unwrap();
764        let client = SingleTestClient::new_raw(endpoint, "");
765
766        let endpoint = MergeRequests::builder()
767            .project("simple/project")
768            .updated_before(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
769            .build()
770            .unwrap();
771        api::ignore(endpoint).query(&client).unwrap();
772    }
773
774    #[test]
775    fn endpoint_scope() {
776        let endpoint = ExpectedUrl::builder()
777            .endpoint("projects/simple%2Fproject/merge_requests")
778            .add_query_params(&[("scope", "all")])
779            .build()
780            .unwrap();
781        let client = SingleTestClient::new_raw(endpoint, "");
782
783        let endpoint = MergeRequests::builder()
784            .project("simple/project")
785            .scope(MergeRequestScope::All)
786            .build()
787            .unwrap();
788        api::ignore(endpoint).query(&client).unwrap();
789    }
790
791    #[test]
792    fn endpoint_author() {
793        let endpoint = ExpectedUrl::builder()
794            .endpoint("projects/simple%2Fproject/merge_requests")
795            .add_query_params(&[("author_id", "1")])
796            .build()
797            .unwrap();
798        let client = SingleTestClient::new_raw(endpoint, "");
799
800        let endpoint = MergeRequests::builder()
801            .project("simple/project")
802            .author(1)
803            .build()
804            .unwrap();
805        api::ignore(endpoint).query(&client).unwrap();
806    }
807
808    #[test]
809    fn endpoint_author_name() {
810        let endpoint = ExpectedUrl::builder()
811            .endpoint("projects/simple%2Fproject/merge_requests")
812            .add_query_params(&[("author_username", "name")])
813            .build()
814            .unwrap();
815        let client = SingleTestClient::new_raw(endpoint, "");
816
817        let endpoint = MergeRequests::builder()
818            .project("simple/project")
819            .author("name")
820            .build()
821            .unwrap();
822        api::ignore(endpoint).query(&client).unwrap();
823    }
824
825    #[test]
826    fn endpoint_assignee_unassigned() {
827        let endpoint = ExpectedUrl::builder()
828            .endpoint("projects/simple%2Fproject/merge_requests")
829            .add_query_params(&[("assignee_id", "None")])
830            .build()
831            .unwrap();
832        let client = SingleTestClient::new_raw(endpoint, "");
833
834        let endpoint = MergeRequests::builder()
835            .project("simple/project")
836            .unassigned()
837            .build()
838            .unwrap();
839        api::ignore(endpoint).query(&client).unwrap();
840    }
841
842    #[test]
843    fn endpoint_assignee_assigned() {
844        let endpoint = ExpectedUrl::builder()
845            .endpoint("projects/simple%2Fproject/merge_requests")
846            .add_query_params(&[("assignee_id", "Any")])
847            .build()
848            .unwrap();
849        let client = SingleTestClient::new_raw(endpoint, "");
850
851        let endpoint = MergeRequests::builder()
852            .project("simple/project")
853            .assigned()
854            .build()
855            .unwrap();
856        api::ignore(endpoint).query(&client).unwrap();
857    }
858
859    #[test]
860    fn endpoint_assignee_id() {
861        let endpoint = ExpectedUrl::builder()
862            .endpoint("projects/simple%2Fproject/merge_requests")
863            .add_query_params(&[("assignee_id", "1")])
864            .build()
865            .unwrap();
866        let client = SingleTestClient::new_raw(endpoint, "");
867
868        let endpoint = MergeRequests::builder()
869            .project("simple/project")
870            .assignee_id(1)
871            .build()
872            .unwrap();
873        api::ignore(endpoint).query(&client).unwrap();
874    }
875
876    #[test]
877    fn endpoint_approvers_none() {
878        let endpoint = ExpectedUrl::builder()
879            .endpoint("projects/simple%2Fproject/merge_requests")
880            .add_query_params(&[("approver_ids", "None")])
881            .build()
882            .unwrap();
883        let client = SingleTestClient::new_raw(endpoint, "");
884
885        let endpoint = MergeRequests::builder()
886            .project("simple/project")
887            .no_approvers()
888            .build()
889            .unwrap();
890        api::ignore(endpoint).query(&client).unwrap();
891    }
892
893    #[test]
894    fn endpoint_approvers_any() {
895        let endpoint = ExpectedUrl::builder()
896            .endpoint("projects/simple%2Fproject/merge_requests")
897            .add_query_params(&[("approver_ids", "Any")])
898            .build()
899            .unwrap();
900        let client = SingleTestClient::new_raw(endpoint, "");
901
902        let endpoint = MergeRequests::builder()
903            .project("simple/project")
904            .any_approvers()
905            .build()
906            .unwrap();
907        api::ignore(endpoint).query(&client).unwrap();
908    }
909
910    #[test]
911    fn endpoint_approver_ids() {
912        let endpoint = ExpectedUrl::builder()
913            .endpoint("projects/simple%2Fproject/merge_requests")
914            .add_query_params(&[("approver_ids[]", "1"), ("approver_ids[]", "2")])
915            .build()
916            .unwrap();
917        let client = SingleTestClient::new_raw(endpoint, "");
918
919        let endpoint = MergeRequests::builder()
920            .project("simple/project")
921            .approver_id(1)
922            .approver_ids([1, 2].iter().copied())
923            .build()
924            .unwrap();
925        api::ignore(endpoint).query(&client).unwrap();
926    }
927
928    #[test]
929    fn endpoint_approvals_none() {
930        let endpoint = ExpectedUrl::builder()
931            .endpoint("projects/simple%2Fproject/merge_requests")
932            .add_query_params(&[("approved_by_ids", "None")])
933            .build()
934            .unwrap();
935        let client = SingleTestClient::new_raw(endpoint, "");
936
937        let endpoint = MergeRequests::builder()
938            .project("simple/project")
939            .no_approvals()
940            .build()
941            .unwrap();
942        api::ignore(endpoint).query(&client).unwrap();
943    }
944
945    #[test]
946    fn endpoint_approvals_any() {
947        let endpoint = ExpectedUrl::builder()
948            .endpoint("projects/simple%2Fproject/merge_requests")
949            .add_query_params(&[("approved_by_ids", "Any")])
950            .build()
951            .unwrap();
952        let client = SingleTestClient::new_raw(endpoint, "");
953
954        let endpoint = MergeRequests::builder()
955            .project("simple/project")
956            .any_approvals()
957            .build()
958            .unwrap();
959        api::ignore(endpoint).query(&client).unwrap();
960    }
961
962    #[test]
963    fn endpoint_approved_by_ids() {
964        let endpoint = ExpectedUrl::builder()
965            .endpoint("projects/simple%2Fproject/merge_requests")
966            .add_query_params(&[("approved_by_ids[]", "1"), ("approved_by_ids[]", "2")])
967            .build()
968            .unwrap();
969        let client = SingleTestClient::new_raw(endpoint, "");
970
971        let endpoint = MergeRequests::builder()
972            .project("simple/project")
973            .approved_by_id(1)
974            .approved_by_ids([1, 2].iter().copied())
975            .build()
976            .unwrap();
977        api::ignore(endpoint).query(&client).unwrap();
978    }
979
980    #[test]
981    fn endpoint_approved_by_usernames() {
982        let endpoint = ExpectedUrl::builder()
983            .endpoint("projects/simple%2Fproject/merge_requests")
984            .add_query_params(&[
985                ("approved_by_usernames[]", "thing1"),
986                ("approved_by_usernames[]", "thing2"),
987            ])
988            .build()
989            .unwrap();
990        let client = SingleTestClient::new_raw(endpoint, "");
991
992        let endpoint = MergeRequests::builder()
993            .project("simple/project")
994            .approved_by_username("thing1")
995            .approved_by_usernames(["thing1", "thing2"].iter().copied())
996            .build()
997            .unwrap();
998        api::ignore(endpoint).query(&client).unwrap();
999    }
1000
1001    #[test]
1002    fn endpoint_approved() {
1003        let endpoint = ExpectedUrl::builder()
1004            .endpoint("projects/simple%2Fproject/merge_requests")
1005            .add_query_params(&[("approved", "yes")])
1006            .build()
1007            .unwrap();
1008        let client = SingleTestClient::new_raw(endpoint, "");
1009
1010        let endpoint = MergeRequests::builder()
1011            .project("simple/project")
1012            .approved(true)
1013            .build()
1014            .unwrap();
1015        api::ignore(endpoint).query(&client).unwrap();
1016    }
1017
1018    #[test]
1019    fn endpoint_reviewer() {
1020        let endpoint = ExpectedUrl::builder()
1021            .endpoint("projects/simple%2Fproject/merge_requests")
1022            .add_query_params(&[("reviewer_id", "1")])
1023            .build()
1024            .unwrap();
1025        let client = SingleTestClient::new_raw(endpoint, "");
1026
1027        let endpoint = MergeRequests::builder()
1028            .project("simple/project")
1029            .reviewer(1)
1030            .build()
1031            .unwrap();
1032        api::ignore(endpoint).query(&client).unwrap();
1033    }
1034
1035    #[test]
1036    fn endpoint_reviewer_name() {
1037        let endpoint = ExpectedUrl::builder()
1038            .endpoint("projects/simple%2Fproject/merge_requests")
1039            .add_query_params(&[("reviewer_username", "name")])
1040            .build()
1041            .unwrap();
1042        let client = SingleTestClient::new_raw(endpoint, "");
1043
1044        let endpoint = MergeRequests::builder()
1045            .project("simple/project")
1046            .reviewer("name")
1047            .build()
1048            .unwrap();
1049        api::ignore(endpoint).query(&client).unwrap();
1050    }
1051
1052    #[test]
1053    fn endpoint_my_reaction_emoji() {
1054        let endpoint = ExpectedUrl::builder()
1055            .endpoint("projects/simple%2Fproject/merge_requests")
1056            .add_query_params(&[("my_reaction_emoji", "tada")])
1057            .build()
1058            .unwrap();
1059        let client = SingleTestClient::new_raw(endpoint, "");
1060
1061        let endpoint = MergeRequests::builder()
1062            .project("simple/project")
1063            .my_reaction("tada")
1064            .build()
1065            .unwrap();
1066        api::ignore(endpoint).query(&client).unwrap();
1067    }
1068
1069    #[test]
1070    fn endpoint_my_reaction_emoji_no_reaction() {
1071        let endpoint = ExpectedUrl::builder()
1072            .endpoint("projects/simple%2Fproject/merge_requests")
1073            .add_query_params(&[("my_reaction_emoji", "None")])
1074            .build()
1075            .unwrap();
1076        let client = SingleTestClient::new_raw(endpoint, "");
1077
1078        let endpoint = MergeRequests::builder()
1079            .project("simple/project")
1080            .no_reaction()
1081            .build()
1082            .unwrap();
1083        api::ignore(endpoint).query(&client).unwrap();
1084    }
1085
1086    #[test]
1087    fn endpoint_my_reaction_emoji_any_reaction() {
1088        let endpoint = ExpectedUrl::builder()
1089            .endpoint("projects/simple%2Fproject/merge_requests")
1090            .add_query_params(&[("my_reaction_emoji", "Any")])
1091            .build()
1092            .unwrap();
1093        let client = SingleTestClient::new_raw(endpoint, "");
1094
1095        let endpoint = MergeRequests::builder()
1096            .project("simple/project")
1097            .any_reaction()
1098            .build()
1099            .unwrap();
1100        api::ignore(endpoint).query(&client).unwrap();
1101    }
1102
1103    #[test]
1104    fn endpoint_target_branch() {
1105        let endpoint = ExpectedUrl::builder()
1106            .endpoint("projects/simple%2Fproject/merge_requests")
1107            .add_query_params(&[("target_branch", "target/branch")])
1108            .build()
1109            .unwrap();
1110        let client = SingleTestClient::new_raw(endpoint, "");
1111
1112        let endpoint = MergeRequests::builder()
1113            .project("simple/project")
1114            .target_branch("target/branch")
1115            .build()
1116            .unwrap();
1117        api::ignore(endpoint).query(&client).unwrap();
1118    }
1119
1120    #[test]
1121    fn endpoint_wip() {
1122        let endpoint = ExpectedUrl::builder()
1123            .endpoint("projects/simple%2Fproject/merge_requests")
1124            .add_query_params(&[("wip", "yes")])
1125            .build()
1126            .unwrap();
1127        let client = SingleTestClient::new_raw(endpoint, "");
1128
1129        let endpoint = MergeRequests::builder()
1130            .project("simple/project")
1131            .wip(YesNo::Yes)
1132            .build()
1133            .unwrap();
1134        api::ignore(endpoint).query(&client).unwrap();
1135    }
1136
1137    #[test]
1138    fn endpoint_environment() {
1139        let endpoint = ExpectedUrl::builder()
1140            .endpoint("projects/simple%2Fproject/merge_requests")
1141            .add_query_params(&[("environment", "env")])
1142            .build()
1143            .unwrap();
1144        let client = SingleTestClient::new_raw(endpoint, "");
1145
1146        let endpoint = MergeRequests::builder()
1147            .project("simple/project")
1148            .environment("env")
1149            .build()
1150            .unwrap();
1151        api::ignore(endpoint).query(&client).unwrap();
1152    }
1153
1154    #[test]
1155    fn endpoint_deployed_before() {
1156        let endpoint = ExpectedUrl::builder()
1157            .endpoint("projects/simple%2Fproject/merge_requests")
1158            .add_query_params(&[("deployed_before", "2020-01-01T00:00:00Z")])
1159            .build()
1160            .unwrap();
1161        let client = SingleTestClient::new_raw(endpoint, "");
1162
1163        let endpoint = MergeRequests::builder()
1164            .project("simple/project")
1165            .deployed_before(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
1166            .build()
1167            .unwrap();
1168        api::ignore(endpoint).query(&client).unwrap();
1169    }
1170
1171    #[test]
1172    fn endpoint_deployed_after() {
1173        let endpoint = ExpectedUrl::builder()
1174            .endpoint("projects/simple%2Fproject/merge_requests")
1175            .add_query_params(&[("deployed_after", "2020-01-01T00:00:00Z")])
1176            .build()
1177            .unwrap();
1178        let client = SingleTestClient::new_raw(endpoint, "");
1179
1180        let endpoint = MergeRequests::builder()
1181            .project("simple/project")
1182            .deployed_after(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
1183            .build()
1184            .unwrap();
1185        api::ignore(endpoint).query(&client).unwrap();
1186    }
1187
1188    #[test]
1189    fn endpoint_search() {
1190        let endpoint = ExpectedUrl::builder()
1191            .endpoint("projects/simple%2Fproject/merge_requests")
1192            .add_query_params(&[("search", "query")])
1193            .build()
1194            .unwrap();
1195        let client = SingleTestClient::new_raw(endpoint, "");
1196
1197        let endpoint = MergeRequests::builder()
1198            .project("simple/project")
1199            .search("query")
1200            .build()
1201            .unwrap();
1202        api::ignore(endpoint).query(&client).unwrap();
1203    }
1204
1205    #[test]
1206    fn endpoint_order_by() {
1207        let endpoint = ExpectedUrl::builder()
1208            .endpoint("projects/simple%2Fproject/merge_requests")
1209            .add_query_params(&[("order_by", "created_at")])
1210            .build()
1211            .unwrap();
1212        let client = SingleTestClient::new_raw(endpoint, "");
1213
1214        let endpoint = MergeRequests::builder()
1215            .project("simple/project")
1216            .order_by(MergeRequestOrderBy::CreatedAt)
1217            .build()
1218            .unwrap();
1219        api::ignore(endpoint).query(&client).unwrap();
1220    }
1221
1222    #[test]
1223    fn endpoint_sort() {
1224        let endpoint = ExpectedUrl::builder()
1225            .endpoint("projects/simple%2Fproject/merge_requests")
1226            .add_query_params(&[("sort", "desc")])
1227            .build()
1228            .unwrap();
1229        let client = SingleTestClient::new_raw(endpoint, "");
1230
1231        let endpoint = MergeRequests::builder()
1232            .project("simple/project")
1233            .sort(SortOrder::Descending)
1234            .build()
1235            .unwrap();
1236        api::ignore(endpoint).query(&client).unwrap();
1237    }
1238}