Skip to main content

gitlab/api/issues/
groups.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::borrow::Cow;
8use std::collections::BTreeSet;
9use std::iter;
10
11use chrono::{DateTime, Utc};
12use derive_builder::Builder;
13use http::Method;
14
15use crate::api::common::{CommaSeparatedList, NameOrId, SortOrder};
16use crate::api::helpers::{Labels, ReactionEmoji};
17use crate::api::{Endpoint, Pageable, QueryParams};
18
19use super::Assignee;
20use super::IssueDueDateFilter;
21use super::IssueEpic;
22use super::IssueHealthStatus;
23use super::IssueIteration;
24use super::IssueMilestone;
25use super::IssueOrderBy;
26use super::IssueScope;
27use super::IssueSearchScope;
28use super::IssueState;
29use super::IssueType;
30use super::IssueWeight;
31
32/// Query for issues within a group.
33///
34/// TODO: Negation (not) filters are not yet supported.
35#[derive(Debug, Builder, Clone)]
36#[builder(setter(strip_option))]
37pub struct GroupIssues<'a> {
38    /// The group to query for issues.
39    #[builder(setter(into))]
40    group: NameOrId<'a>,
41
42    /// Filter issues with specific IDs.
43    #[builder(setter(name = "_iids"), default, private)]
44    iids: BTreeSet<u64>,
45    /// Filter issues based on state.
46    #[builder(default)]
47    state: Option<IssueState>,
48    /// Filter issues based on labels.
49    #[builder(setter(name = "_labels"), default, private)]
50    labels: Option<Labels<'a>>,
51    /// Include label details in the result.
52    #[builder(default)]
53    with_labels_details: Option<bool>,
54    /// Filter by the iteration.
55    #[builder(default)]
56    iteration: Option<IssueIteration<'a>>,
57    /// Filter issues with a milestone.
58    #[builder(default)]
59    milestone_id: Option<IssueMilestone<'a>>,
60    /// Filter issues within a scope.
61    #[builder(default)]
62    scope: Option<IssueScope>,
63    /// Filter issues by author.
64    #[builder(setter(into), default)]
65    author: Option<NameOrId<'a>>,
66    /// Filter issues by assignees.
67    #[builder(setter(name = "_assignee"), default, private)]
68    assignee: Option<Assignee<'a>>,
69    /// Filter issues by the API caller's reactions.
70    #[builder(setter(name = "_my_reaction_emoji"), default, private)]
71    my_reaction_emoji: Option<ReactionEmoji<'a>>,
72    /// Return issues from non archived projects.
73    #[builder(default)]
74    non_archived: Option<bool>,
75    /// Filter issues by weight.
76    #[builder(default)]
77    weight: Option<IssueWeight>,
78    /// Filter issues with a search query.
79    #[builder(setter(into), default)]
80    search: Option<Cow<'a, str>>,
81    /// The scopes to search within.
82    #[builder(setter(name = "_search_in"), default, private)]
83    search_in: Option<CommaSeparatedList<IssueSearchScope>>,
84    /// Filter issues created after a point in time.
85    #[builder(default)]
86    created_after: Option<DateTime<Utc>>,
87    /// Filter issues created before a point in time.
88    #[builder(default)]
89    created_before: Option<DateTime<Utc>>,
90    /// Filter issues last updated after a point in time.
91    #[builder(default)]
92    updated_after: Option<DateTime<Utc>>,
93    /// Filter issues last updated before a point in time.
94    #[builder(default)]
95    updated_before: Option<DateTime<Utc>>,
96    /// Filter issues by confidentiality.
97    #[builder(default)]
98    confidential: Option<bool>,
99    /// Filter issues by due date.
100    #[builder(default)]
101    due_date: Option<IssueDueDateFilter>,
102    /// Filter by epic ID.
103    #[builder(setter(into), default)]
104    epic_id: Option<IssueEpic>,
105    /// Filter by issue type.
106    #[builder(default)]
107    issue_type: Option<IssueType>,
108    /// Filter by issue health status.
109    #[builder(default)]
110    health_status: Option<IssueHealthStatus>,
111
112    // TODO: How best to support this parameter?
113    // not
114    /// Order results by a given key.
115    #[builder(default)]
116    order_by: Option<IssueOrderBy>,
117    /// The sort order for returned results.
118    #[builder(default)]
119    sort: Option<SortOrder>,
120}
121
122impl<'a> GroupIssues<'a> {
123    /// Create a builder for the endpoint.
124    pub fn builder() -> GroupIssuesBuilder<'a> {
125        GroupIssuesBuilder::default()
126    }
127}
128
129impl<'a> GroupIssuesBuilder<'a> {
130    /// Return an issue with an ID.
131    pub fn iid(&mut self, iid: u64) -> &mut Self {
132        self.iids.get_or_insert_with(BTreeSet::new).insert(iid);
133        self
134    }
135
136    /// Return issues with one of a set of IDs.
137    pub fn iids<I>(&mut self, iter: I) -> &mut Self
138    where
139        I: Iterator<Item = u64>,
140    {
141        self.iids.get_or_insert_with(BTreeSet::new).extend(iter);
142        self
143    }
144
145    /// Filter unlabeled issues.
146    pub fn unlabeled(&mut self) -> &mut Self {
147        self.labels = Some(Some(Labels::None));
148        self
149    }
150
151    /// Filter issues with any label.
152    pub fn with_any_label(&mut self) -> &mut Self {
153        self.labels = Some(Some(Labels::Any));
154        self
155    }
156
157    /// Filter issues with a given label.
158    pub fn label<L>(&mut self, label: L) -> &mut Self
159    where
160        L: Into<Cow<'a, str>>,
161    {
162        let label = label.into();
163        let labels = if let Some(Some(Labels::AllOf(mut set))) = self.labels.take() {
164            set.push(label);
165            set
166        } else {
167            iter::once(label).collect()
168        };
169        self.labels = Some(Some(Labels::AllOf(labels)));
170        self
171    }
172
173    /// Filter issues with all of the given labels.
174    pub fn labels<I, L>(&mut self, iter: I) -> &mut Self
175    where
176        I: IntoIterator<Item = L>,
177        L: Into<Cow<'a, str>>,
178    {
179        let iter = iter.into_iter().map(Into::into);
180        let labels = if let Some(Some(Labels::AllOf(mut set))) = self.labels.take() {
181            set.extend(iter);
182            set
183        } else {
184            iter.collect()
185        };
186        self.labels = Some(Some(Labels::AllOf(labels)));
187        self
188    }
189
190    /// Filter unassigned issues.
191    pub fn unassigned(&mut self) -> &mut Self {
192        self.assignee = Some(Some(Assignee::Unassigned));
193        self
194    }
195
196    /// Filter assigned issues.
197    pub fn assigned(&mut self) -> &mut Self {
198        self.assignee = Some(Some(Assignee::Assigned));
199        self
200    }
201
202    /// Filter issues assigned to a user (by ID).
203    pub fn assignee_id(&mut self, assignee: u64) -> &mut Self {
204        self.assignee = Some(Some(Assignee::Id(assignee)));
205        self
206    }
207
208    /// Filter issues assigned to a users (by username).
209    pub fn assignee<A>(&mut self, assignee: A) -> &mut Self
210    where
211        A: Into<Cow<'a, str>>,
212    {
213        let assignee = assignee.into();
214        let assignees = if let Some(Some(Assignee::Usernames(mut set))) = self.assignee.take() {
215            set.insert(assignee);
216            set
217        } else {
218            let mut set = BTreeSet::new();
219            set.insert(assignee);
220            set
221        };
222        self.assignee = Some(Some(Assignee::Usernames(assignees)));
223        self
224    }
225
226    /// Filter issues assigned to a set of users.
227    pub fn assignees<I, A>(&mut self, iter: I) -> &mut Self
228    where
229        I: IntoIterator<Item = A>,
230        A: Into<Cow<'a, str>>,
231    {
232        let iter = iter.into_iter().map(Into::into);
233        let assignees = if let Some(Some(Assignee::Usernames(mut set))) = self.assignee.take() {
234            set.extend(iter);
235            set
236        } else {
237            iter.collect()
238        };
239        self.assignee = Some(Some(Assignee::Usernames(assignees)));
240        self
241    }
242
243    /// Filter issues without a reaction by the API caller.
244    pub fn no_reaction(&mut self) -> &mut Self {
245        self.my_reaction_emoji = Some(Some(ReactionEmoji::None));
246        self
247    }
248
249    /// Filter issues with any reaction by the API caller.
250    pub fn any_reaction(&mut self) -> &mut Self {
251        self.my_reaction_emoji = Some(Some(ReactionEmoji::Any));
252        self
253    }
254
255    /// Filter issues with a specific reaction by the API caller.
256    pub fn my_reaction<E>(&mut self, emoji: E) -> &mut Self
257    where
258        E: Into<Cow<'a, str>>,
259    {
260        self.my_reaction_emoji = Some(Some(ReactionEmoji::Emoji(emoji.into())));
261        self
262    }
263
264    /// The scopes to look for search query within.
265    pub fn search_in(&mut self, scope: IssueSearchScope) -> &mut Self {
266        self.search_in
267            .get_or_insert(None)
268            .get_or_insert_with(CommaSeparatedList::new)
269            .push(scope);
270        self
271    }
272}
273
274impl Endpoint for GroupIssues<'_> {
275    fn method(&self) -> Method {
276        Method::GET
277    }
278
279    fn endpoint(&self) -> Cow<'static, str> {
280        format!("groups/{}/issues", self.group).into()
281    }
282
283    fn parameters(&self) -> QueryParams<'_> {
284        let mut params = QueryParams::default();
285
286        params
287            .extend(self.iids.iter().map(|&value| ("iids[]", value)))
288            .push_opt("state", self.state)
289            .push_opt("labels", self.labels.as_ref())
290            .push_opt("with_labels_details", self.with_labels_details)
291            .push_opt("scope", self.scope)
292            .push_opt("my_reaction_emoji", self.my_reaction_emoji.as_ref())
293            .push_opt("non_archived", self.non_archived)
294            .push_opt("weight", self.weight)
295            .push_opt("search", self.search.as_ref())
296            .push_opt("in", self.search_in.as_ref())
297            .push_opt("created_after", self.created_after)
298            .push_opt("created_before", self.created_before)
299            .push_opt("updated_after", self.updated_after)
300            .push_opt("updated_before", self.updated_before)
301            .push_opt("confidential", self.confidential)
302            .push_opt("due_date", self.due_date)
303            .push_opt("epic_id", self.epic_id)
304            .push_opt("issue_type", self.issue_type)
305            .push_opt("health_status", self.health_status)
306            .push_opt("order_by", self.order_by)
307            .push_opt("sort", self.sort);
308
309        if let Some(milestone_id) = self.milestone_id.as_ref() {
310            match milestone_id {
311                IssueMilestone::Named(name) => {
312                    params.push("milestone", name);
313                },
314                milestone => {
315                    params.push("milestone_id", milestone);
316                },
317            }
318        }
319        if let Some(author) = self.author.as_ref() {
320            match author {
321                NameOrId::Name(name) => {
322                    params.push("author_username", name);
323                },
324                NameOrId::Id(id) => {
325                    params.push("author_id", *id);
326                },
327            }
328        }
329        if let Some(iteration) = self.iteration.as_ref() {
330            iteration.add_params(&mut params);
331        }
332        if let Some(assignee) = self.assignee.as_ref() {
333            assignee.add_params(&mut params);
334        }
335
336        params
337    }
338}
339
340impl Pageable for GroupIssues<'_> {}
341
342#[cfg(test)]
343mod tests {
344    use chrono::{TimeZone, Utc};
345
346    use crate::api::common::SortOrder;
347    use crate::api::issues::{
348        groups::GroupIssues, groups::GroupIssuesBuilderError, IssueDueDateFilter, IssueEpic,
349        IssueHealthStatus, IssueIteration, IssueMilestone, IssueOrderBy, IssueScope,
350        IssueSearchScope, IssueState, IssueType, IssueWeight,
351    };
352    use crate::api::{self, Query};
353    use crate::test::client::{ExpectedUrl, SingleTestClient};
354
355    #[test]
356    fn group_is_needed() {
357        let err = GroupIssues::builder().build().unwrap_err();
358        crate::test::assert_missing_field!(err, GroupIssuesBuilderError, "group");
359    }
360
361    #[test]
362    fn group_is_sufficient() {
363        GroupIssues::builder().group(1).build().unwrap();
364    }
365
366    #[test]
367    fn endpoint() {
368        let endpoint = ExpectedUrl::builder()
369            .endpoint("groups/simple%2Fgroup/issues")
370            .build()
371            .unwrap();
372        let client = SingleTestClient::new_raw(endpoint, "");
373
374        let endpoint = GroupIssues::builder()
375            .group("simple/group")
376            .build()
377            .unwrap();
378        api::ignore(endpoint).query(&client).unwrap();
379    }
380
381    #[test]
382    fn endpoint_iids() {
383        let endpoint = ExpectedUrl::builder()
384            .endpoint("groups/simple%2Fgroup/issues")
385            .add_query_params(&[("iids[]", "1"), ("iids[]", "2")])
386            .build()
387            .unwrap();
388        let client = SingleTestClient::new_raw(endpoint, "");
389
390        let endpoint = GroupIssues::builder()
391            .group("simple/group")
392            .iid(1)
393            .iids([1, 2].iter().copied())
394            .build()
395            .unwrap();
396        api::ignore(endpoint).query(&client).unwrap();
397    }
398
399    #[test]
400    fn endpoint_state() {
401        let endpoint = ExpectedUrl::builder()
402            .endpoint("groups/simple%2Fgroup/issues")
403            .add_query_params(&[("state", "closed")])
404            .build()
405            .unwrap();
406        let client = SingleTestClient::new_raw(endpoint, "");
407
408        let endpoint = GroupIssues::builder()
409            .group("simple/group")
410            .state(IssueState::Closed)
411            .build()
412            .unwrap();
413        api::ignore(endpoint).query(&client).unwrap();
414    }
415
416    #[test]
417    fn endpoint_labels() {
418        let endpoint = ExpectedUrl::builder()
419            .endpoint("groups/simple%2Fgroup/issues")
420            .add_query_params(&[("labels", "label,label1,label2")])
421            .build()
422            .unwrap();
423        let client = SingleTestClient::new_raw(endpoint, "");
424
425        let endpoint = GroupIssues::builder()
426            .group("simple/group")
427            .label("label")
428            .labels(["label1", "label2"].iter().cloned())
429            .build()
430            .unwrap();
431        api::ignore(endpoint).query(&client).unwrap();
432    }
433
434    #[test]
435    fn endpoint_labels_unlabeled() {
436        let endpoint = ExpectedUrl::builder()
437            .endpoint("groups/simple%2Fgroup/issues")
438            .add_query_params(&[("labels", "None")])
439            .build()
440            .unwrap();
441        let client = SingleTestClient::new_raw(endpoint, "");
442
443        let endpoint = GroupIssues::builder()
444            .group("simple/group")
445            .unlabeled()
446            .build()
447            .unwrap();
448        api::ignore(endpoint).query(&client).unwrap();
449    }
450
451    #[test]
452    fn endpoint_labels_any() {
453        let endpoint = ExpectedUrl::builder()
454            .endpoint("groups/simple%2Fgroup/issues")
455            .add_query_params(&[("labels", "Any")])
456            .build()
457            .unwrap();
458        let client = SingleTestClient::new_raw(endpoint, "");
459
460        let endpoint = GroupIssues::builder()
461            .group("simple/group")
462            .with_any_label()
463            .build()
464            .unwrap();
465        api::ignore(endpoint).query(&client).unwrap();
466    }
467
468    #[test]
469    fn endpoint_with_labels_details() {
470        let endpoint = ExpectedUrl::builder()
471            .endpoint("groups/simple%2Fgroup/issues")
472            .add_query_params(&[("with_labels_details", "true")])
473            .build()
474            .unwrap();
475        let client = SingleTestClient::new_raw(endpoint, "");
476
477        let endpoint = GroupIssues::builder()
478            .group("simple/group")
479            .with_labels_details(true)
480            .build()
481            .unwrap();
482        api::ignore(endpoint).query(&client).unwrap();
483    }
484
485    #[test]
486    fn endpoint_epic_id() {
487        let endpoint = ExpectedUrl::builder()
488            .endpoint("groups/simple%2Fgroup/issues")
489            .add_query_params(&[("epic_id", "4")])
490            .build()
491            .unwrap();
492        let client = SingleTestClient::new_raw(endpoint, "");
493
494        let endpoint = GroupIssues::builder()
495            .group("simple/group")
496            .epic_id(4)
497            .build()
498            .unwrap();
499        api::ignore(endpoint).query(&client).unwrap();
500    }
501
502    #[test]
503    fn endpoint_epic_any() {
504        let endpoint = ExpectedUrl::builder()
505            .endpoint("groups/simple%2Fgroup/issues")
506            .add_query_params(&[("epic_id", "Any")])
507            .build()
508            .unwrap();
509        let client = SingleTestClient::new_raw(endpoint, "");
510
511        let endpoint = GroupIssues::builder()
512            .group("simple/group")
513            .epic_id(IssueEpic::Any)
514            .build()
515            .unwrap();
516        api::ignore(endpoint).query(&client).unwrap();
517    }
518
519    #[test]
520    fn endpoint_epic_none() {
521        let endpoint = ExpectedUrl::builder()
522            .endpoint("groups/simple%2Fgroup/issues")
523            .add_query_params(&[("epic_id", "None")])
524            .build()
525            .unwrap();
526        let client = SingleTestClient::new_raw(endpoint, "");
527
528        let endpoint = GroupIssues::builder()
529            .group("simple/group")
530            .epic_id(IssueEpic::None)
531            .build()
532            .unwrap();
533        api::ignore(endpoint).query(&client).unwrap();
534    }
535
536    #[test]
537    fn endpoint_issue_type() {
538        let endpoint = ExpectedUrl::builder()
539            .endpoint("groups/simple%2Fgroup/issues")
540            .add_query_params(&[("issue_type", "incident")])
541            .build()
542            .unwrap();
543        let client = SingleTestClient::new_raw(endpoint, "");
544
545        let endpoint = GroupIssues::builder()
546            .group("simple/group")
547            .issue_type(IssueType::Incident)
548            .build()
549            .unwrap();
550        api::ignore(endpoint).query(&client).unwrap();
551    }
552
553    #[test]
554    fn endpoint_health_status() {
555        let endpoint = ExpectedUrl::builder()
556            .endpoint("groups/simple%2Fgroup/issues")
557            .add_query_params(&[("health_status", "at_risk")])
558            .build()
559            .unwrap();
560        let client = SingleTestClient::new_raw(endpoint, "");
561
562        let endpoint = GroupIssues::builder()
563            .group("simple/group")
564            .health_status(IssueHealthStatus::AtRisk)
565            .build()
566            .unwrap();
567        api::ignore(endpoint).query(&client).unwrap();
568    }
569
570    #[test]
571    fn endpoint_iteration_none() {
572        let endpoint = ExpectedUrl::builder()
573            .endpoint("groups/simple%2Fgroup/issues")
574            .add_query_params(&[("iteration_id", "None")])
575            .build()
576            .unwrap();
577        let client = SingleTestClient::new_raw(endpoint, "");
578
579        let endpoint = GroupIssues::builder()
580            .group("simple/group")
581            .iteration(IssueIteration::None)
582            .build()
583            .unwrap();
584        api::ignore(endpoint).query(&client).unwrap();
585    }
586
587    #[test]
588    fn endpoint_iteration_any() {
589        let endpoint = ExpectedUrl::builder()
590            .endpoint("groups/simple%2Fgroup/issues")
591            .add_query_params(&[("iteration_id", "Any")])
592            .build()
593            .unwrap();
594        let client = SingleTestClient::new_raw(endpoint, "");
595
596        let endpoint = GroupIssues::builder()
597            .group("simple/group")
598            .iteration(IssueIteration::Any)
599            .build()
600            .unwrap();
601        api::ignore(endpoint).query(&client).unwrap();
602    }
603
604    #[test]
605    fn endpoint_iteration_id() {
606        let endpoint = ExpectedUrl::builder()
607            .endpoint("groups/simple%2Fgroup/issues")
608            .add_query_params(&[("iteration_id", "1")])
609            .build()
610            .unwrap();
611        let client = SingleTestClient::new_raw(endpoint, "");
612
613        let endpoint = GroupIssues::builder()
614            .group("simple/group")
615            .iteration(IssueIteration::Id(1))
616            .build()
617            .unwrap();
618        api::ignore(endpoint).query(&client).unwrap();
619    }
620
621    #[test]
622    fn endpoint_iteration_title() {
623        let endpoint = ExpectedUrl::builder()
624            .endpoint("groups/simple%2Fgroup/issues")
625            .add_query_params(&[("iteration_title", "title")])
626            .build()
627            .unwrap();
628        let client = SingleTestClient::new_raw(endpoint, "");
629
630        let endpoint = GroupIssues::builder()
631            .group("simple/group")
632            .iteration(IssueIteration::Title("title".into()))
633            .build()
634            .unwrap();
635        api::ignore(endpoint).query(&client).unwrap();
636    }
637
638    #[test]
639    fn endpoint_milestone_id() {
640        let endpoint = ExpectedUrl::builder()
641            .endpoint("groups/simple%2Fgroup/issues")
642            .add_query_params(&[("milestone_id", "Upcoming")])
643            .build()
644            .unwrap();
645        let client = SingleTestClient::new_raw(endpoint, "");
646
647        let endpoint = GroupIssues::builder()
648            .group("simple/group")
649            .milestone_id(IssueMilestone::Upcoming)
650            .build()
651            .unwrap();
652        api::ignore(endpoint).query(&client).unwrap();
653    }
654
655    #[test]
656    fn endpoint_milestone_id_named() {
657        let endpoint = ExpectedUrl::builder()
658            .endpoint("groups/simple%2Fgroup/issues")
659            .add_query_params(&[("milestone", "1.0")])
660            .build()
661            .unwrap();
662        let client = SingleTestClient::new_raw(endpoint, "");
663
664        let endpoint = GroupIssues::builder()
665            .group("simple/group")
666            .milestone_id(IssueMilestone::named("1.0"))
667            .build()
668            .unwrap();
669        api::ignore(endpoint).query(&client).unwrap();
670    }
671
672    #[test]
673    fn endpoint_scope() {
674        let endpoint = ExpectedUrl::builder()
675            .endpoint("groups/simple%2Fgroup/issues")
676            .add_query_params(&[("scope", "all")])
677            .build()
678            .unwrap();
679        let client = SingleTestClient::new_raw(endpoint, "");
680
681        let endpoint = GroupIssues::builder()
682            .group("simple/group")
683            .scope(IssueScope::All)
684            .build()
685            .unwrap();
686        api::ignore(endpoint).query(&client).unwrap();
687    }
688
689    #[test]
690    fn endpoint_author_id() {
691        let endpoint = ExpectedUrl::builder()
692            .endpoint("groups/simple%2Fgroup/issues")
693            .add_query_params(&[("author_id", "1")])
694            .build()
695            .unwrap();
696        let client = SingleTestClient::new_raw(endpoint, "");
697
698        let endpoint = GroupIssues::builder()
699            .group("simple/group")
700            .author(1)
701            .build()
702            .unwrap();
703        api::ignore(endpoint).query(&client).unwrap();
704    }
705
706    #[test]
707    fn endpoint_author_name() {
708        let endpoint = ExpectedUrl::builder()
709            .endpoint("groups/simple%2Fgroup/issues")
710            .add_query_params(&[("author_username", "name")])
711            .build()
712            .unwrap();
713        let client = SingleTestClient::new_raw(endpoint, "");
714
715        let endpoint = GroupIssues::builder()
716            .group("simple/group")
717            .author("name")
718            .build()
719            .unwrap();
720        api::ignore(endpoint).query(&client).unwrap();
721    }
722
723    #[test]
724    fn endpoint_assignee_unassigned() {
725        let endpoint = ExpectedUrl::builder()
726            .endpoint("groups/simple%2Fgroup/issues")
727            .add_query_params(&[("assignee_id", "None")])
728            .build()
729            .unwrap();
730        let client = SingleTestClient::new_raw(endpoint, "");
731
732        let endpoint = GroupIssues::builder()
733            .group("simple/group")
734            .unassigned()
735            .build()
736            .unwrap();
737        api::ignore(endpoint).query(&client).unwrap();
738    }
739
740    #[test]
741    fn endpoint_assignee_assigned() {
742        let endpoint = ExpectedUrl::builder()
743            .endpoint("groups/simple%2Fgroup/issues")
744            .add_query_params(&[("assignee_id", "Any")])
745            .build()
746            .unwrap();
747        let client = SingleTestClient::new_raw(endpoint, "");
748
749        let endpoint = GroupIssues::builder()
750            .group("simple/group")
751            .assigned()
752            .build()
753            .unwrap();
754        api::ignore(endpoint).query(&client).unwrap();
755    }
756
757    #[test]
758    fn endpoint_assignee_id() {
759        let endpoint = ExpectedUrl::builder()
760            .endpoint("groups/simple%2Fgroup/issues")
761            .add_query_params(&[("assignee_id", "1")])
762            .build()
763            .unwrap();
764        let client = SingleTestClient::new_raw(endpoint, "");
765
766        let endpoint = GroupIssues::builder()
767            .group("simple/group")
768            .assignee_id(1)
769            .build()
770            .unwrap();
771        api::ignore(endpoint).query(&client).unwrap();
772    }
773
774    #[test]
775    fn endpoint_assignee_user() {
776        let endpoint = ExpectedUrl::builder()
777            .endpoint("groups/simple%2Fgroup/issues")
778            .add_query_params(&[
779                ("assignee_username[]", "name1"),
780                ("assignee_username[]", "name2"),
781            ])
782            .build()
783            .unwrap();
784        let client = SingleTestClient::new_raw(endpoint, "");
785
786        let endpoint = GroupIssues::builder()
787            .group("simple/group")
788            .assignee("name1")
789            .assignees(["name1", "name2"].iter().copied())
790            .build()
791            .unwrap();
792        api::ignore(endpoint).query(&client).unwrap();
793    }
794
795    #[test]
796    fn endpoint_my_reaction_emoji() {
797        let endpoint = ExpectedUrl::builder()
798            .endpoint("groups/simple%2Fgroup/issues")
799            .add_query_params(&[("my_reaction_emoji", "tada")])
800            .build()
801            .unwrap();
802        let client = SingleTestClient::new_raw(endpoint, "");
803
804        let endpoint = GroupIssues::builder()
805            .group("simple/group")
806            .my_reaction("tada")
807            .build()
808            .unwrap();
809        api::ignore(endpoint).query(&client).unwrap();
810    }
811
812    #[test]
813    fn endpoint_my_reaction_emoji_no_reaction() {
814        let endpoint = ExpectedUrl::builder()
815            .endpoint("groups/simple%2Fgroup/issues")
816            .add_query_params(&[("my_reaction_emoji", "None")])
817            .build()
818            .unwrap();
819        let client = SingleTestClient::new_raw(endpoint, "");
820
821        let endpoint = GroupIssues::builder()
822            .group("simple/group")
823            .no_reaction()
824            .build()
825            .unwrap();
826        api::ignore(endpoint).query(&client).unwrap();
827    }
828
829    #[test]
830    fn endpoint_my_reaction_emoji_any_reaction() {
831        let endpoint = ExpectedUrl::builder()
832            .endpoint("groups/simple%2Fgroup/issues")
833            .add_query_params(&[("my_reaction_emoji", "Any")])
834            .build()
835            .unwrap();
836        let client = SingleTestClient::new_raw(endpoint, "");
837
838        let endpoint = GroupIssues::builder()
839            .group("simple/group")
840            .any_reaction()
841            .build()
842            .unwrap();
843        api::ignore(endpoint).query(&client).unwrap();
844    }
845
846    #[test]
847    fn endpoint_non_archived() {
848        let endpoint = ExpectedUrl::builder()
849            .endpoint("groups/simple%2Fgroup/issues")
850            .add_query_params(&[("non_archived", "true")])
851            .build()
852            .unwrap();
853        let client = SingleTestClient::new_raw(endpoint, "");
854
855        let endpoint = GroupIssues::builder()
856            .group("simple/group")
857            .non_archived(true)
858            .build()
859            .unwrap();
860        api::ignore(endpoint).query(&client).unwrap();
861    }
862
863    #[test]
864    fn endpoint_weight() {
865        let endpoint = ExpectedUrl::builder()
866            .endpoint("groups/simple%2Fgroup/issues")
867            .add_query_params(&[("weight", "Any")])
868            .build()
869            .unwrap();
870        let client = SingleTestClient::new_raw(endpoint, "");
871
872        let endpoint = GroupIssues::builder()
873            .group("simple/group")
874            .weight(IssueWeight::Any)
875            .build()
876            .unwrap();
877        api::ignore(endpoint).query(&client).unwrap();
878    }
879
880    #[test]
881    fn endpoint_search() {
882        let endpoint = ExpectedUrl::builder()
883            .endpoint("groups/simple%2Fgroup/issues")
884            .add_query_params(&[("search", "query")])
885            .build()
886            .unwrap();
887        let client = SingleTestClient::new_raw(endpoint, "");
888
889        let endpoint = GroupIssues::builder()
890            .group("simple/group")
891            .search("query")
892            .build()
893            .unwrap();
894        api::ignore(endpoint).query(&client).unwrap();
895    }
896
897    #[test]
898    fn endpoint_search_in() {
899        let endpoint = ExpectedUrl::builder()
900            .endpoint("groups/simple%2Fgroup/issues")
901            .add_query_params(&[("in", "title,description")])
902            .build()
903            .unwrap();
904        let client = SingleTestClient::new_raw(endpoint, "");
905
906        let endpoint = GroupIssues::builder()
907            .group("simple/group")
908            .search_in(IssueSearchScope::Title)
909            .search_in(IssueSearchScope::Description)
910            .build()
911            .unwrap();
912        api::ignore(endpoint).query(&client).unwrap();
913    }
914
915    #[test]
916    fn endpoint_created_after() {
917        let endpoint = ExpectedUrl::builder()
918            .endpoint("groups/simple%2Fgroup/issues")
919            .add_query_params(&[("created_after", "2020-01-01T00:00:00Z")])
920            .build()
921            .unwrap();
922        let client = SingleTestClient::new_raw(endpoint, "");
923
924        let endpoint = GroupIssues::builder()
925            .group("simple/group")
926            .created_after(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
927            .build()
928            .unwrap();
929        api::ignore(endpoint).query(&client).unwrap();
930    }
931
932    #[test]
933    fn endpoint_created_before() {
934        let endpoint = ExpectedUrl::builder()
935            .endpoint("groups/simple%2Fgroup/issues")
936            .add_query_params(&[("created_before", "2020-01-01T00:00:00Z")])
937            .build()
938            .unwrap();
939        let client = SingleTestClient::new_raw(endpoint, "");
940
941        let endpoint = GroupIssues::builder()
942            .group("simple/group")
943            .created_before(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
944            .build()
945            .unwrap();
946        api::ignore(endpoint).query(&client).unwrap();
947    }
948
949    #[test]
950    fn endpoint_updated_after() {
951        let endpoint = ExpectedUrl::builder()
952            .endpoint("groups/simple%2Fgroup/issues")
953            .add_query_params(&[("updated_after", "2020-01-01T00:00:00Z")])
954            .build()
955            .unwrap();
956        let client = SingleTestClient::new_raw(endpoint, "");
957
958        let endpoint = GroupIssues::builder()
959            .group("simple/group")
960            .updated_after(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
961            .build()
962            .unwrap();
963        api::ignore(endpoint).query(&client).unwrap();
964    }
965
966    #[test]
967    fn endpoint_updated_before() {
968        let endpoint = ExpectedUrl::builder()
969            .endpoint("groups/simple%2Fgroup/issues")
970            .add_query_params(&[("updated_before", "2020-01-01T00:00:00Z")])
971            .build()
972            .unwrap();
973        let client = SingleTestClient::new_raw(endpoint, "");
974
975        let endpoint = GroupIssues::builder()
976            .group("simple/group")
977            .updated_before(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap())
978            .build()
979            .unwrap();
980        api::ignore(endpoint).query(&client).unwrap();
981    }
982
983    #[test]
984    fn endpoint_confidential() {
985        let endpoint = ExpectedUrl::builder()
986            .endpoint("groups/simple%2Fgroup/issues")
987            .add_query_params(&[("confidential", "true")])
988            .build()
989            .unwrap();
990        let client = SingleTestClient::new_raw(endpoint, "");
991
992        let endpoint = GroupIssues::builder()
993            .group("simple/group")
994            .confidential(true)
995            .build()
996            .unwrap();
997        api::ignore(endpoint).query(&client).unwrap();
998    }
999
1000    #[test]
1001    fn endpoint_due_date() {
1002        let endpoint = ExpectedUrl::builder()
1003            .endpoint("groups/simple%2Fgroup/issues")
1004            .add_query_params(&[("due_date", "week")])
1005            .build()
1006            .unwrap();
1007        let client = SingleTestClient::new_raw(endpoint, "");
1008
1009        let endpoint = GroupIssues::builder()
1010            .group("simple/group")
1011            .due_date(IssueDueDateFilter::ThisWeek)
1012            .build()
1013            .unwrap();
1014        api::ignore(endpoint).query(&client).unwrap();
1015    }
1016
1017    #[test]
1018    fn endpoint_order_by() {
1019        let endpoint = ExpectedUrl::builder()
1020            .endpoint("groups/simple%2Fgroup/issues")
1021            .add_query_params(&[("order_by", "due_date")])
1022            .build()
1023            .unwrap();
1024        let client = SingleTestClient::new_raw(endpoint, "");
1025
1026        let endpoint = GroupIssues::builder()
1027            .group("simple/group")
1028            .order_by(IssueOrderBy::DueDate)
1029            .build()
1030            .unwrap();
1031        api::ignore(endpoint).query(&client).unwrap();
1032    }
1033
1034    #[test]
1035    fn endpoint_sort() {
1036        let endpoint = ExpectedUrl::builder()
1037            .endpoint("groups/simple%2Fgroup/issues")
1038            .add_query_params(&[("sort", "desc")])
1039            .build()
1040            .unwrap();
1041        let client = SingleTestClient::new_raw(endpoint, "");
1042
1043        let endpoint = GroupIssues::builder()
1044            .group("simple/group")
1045            .sort(SortOrder::Descending)
1046            .build()
1047            .unwrap();
1048        api::ignore(endpoint).query(&client).unwrap();
1049    }
1050}