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