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