Skip to main content

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