1use derive_builder::Builder;
92use reqwest::Method;
93use std::borrow::Cow;
94
95use crate::api::attachments::Attachment;
96use crate::api::custom_fields::CustomFieldEssentialsWithValue;
97use crate::api::enumerations::IssuePriorityEssentials;
98use crate::api::groups::{Group, GroupEssentials};
99use crate::api::issue_categories::IssueCategoryEssentials;
100use crate::api::issue_relations::IssueRelation;
101use crate::api::issue_statuses::IssueStatusEssentials;
102use crate::api::projects::ProjectEssentials;
103use crate::api::trackers::TrackerEssentials;
104use crate::api::users::UserEssentials;
105use crate::api::versions::VersionEssentials;
106use crate::api::{Endpoint, NoPagination, Pageable, QueryParams, ReturnsJsonResponse};
107use serde::Serialize;
108
109#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
112pub struct AssigneeEssentials {
113 pub id: u64,
115 pub name: String,
117}
118
119impl From<UserEssentials> for AssigneeEssentials {
120 fn from(v: UserEssentials) -> Self {
121 AssigneeEssentials {
122 id: v.id,
123 name: v.name,
124 }
125 }
126}
127
128impl From<&UserEssentials> for AssigneeEssentials {
129 fn from(v: &UserEssentials) -> Self {
130 AssigneeEssentials {
131 id: v.id,
132 name: v.name.to_owned(),
133 }
134 }
135}
136
137impl From<GroupEssentials> for AssigneeEssentials {
138 fn from(v: GroupEssentials) -> Self {
139 AssigneeEssentials {
140 id: v.id,
141 name: v.name,
142 }
143 }
144}
145
146impl From<&GroupEssentials> for AssigneeEssentials {
147 fn from(v: &GroupEssentials) -> Self {
148 AssigneeEssentials {
149 id: v.id,
150 name: v.name.to_owned(),
151 }
152 }
153}
154
155impl From<Group> for AssigneeEssentials {
156 fn from(v: Group) -> Self {
157 AssigneeEssentials {
158 id: v.id,
159 name: v.name,
160 }
161 }
162}
163
164impl From<&Group> for AssigneeEssentials {
165 fn from(v: &Group) -> Self {
166 AssigneeEssentials {
167 id: v.id,
168 name: v.name.to_owned(),
169 }
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
176pub struct IssueEssentials {
177 pub id: u64,
179}
180
181impl From<Issue> for IssueEssentials {
182 fn from(v: Issue) -> Self {
183 IssueEssentials { id: v.id }
184 }
185}
186
187impl From<&Issue> for IssueEssentials {
188 fn from(v: &Issue) -> Self {
189 IssueEssentials { id: v.id }
190 }
191}
192
193#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
196pub struct RepositoryEssentials {
197 pub id: u64,
199 pub identifier: String,
201}
202
203#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
205pub struct IssueChangeset {
206 revision: String,
208 user: UserEssentials,
210 comments: String,
212 #[serde(
214 serialize_with = "crate::api::serialize_rfc3339",
215 deserialize_with = "crate::api::deserialize_rfc3339"
216 )]
217 committed_on: time::OffsetDateTime,
218}
219
220#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
222pub enum ChangePropertyType {
223 #[serde(rename = "attr")]
225 Attr,
226 #[serde(rename = "cf")]
228 Cf,
229 #[serde(rename = "relation")]
231 Relation,
232 #[serde(rename = "attachment")]
234 Attachment,
235}
236
237#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
239pub struct JournalChange {
240 pub name: String,
242 pub old_value: Option<String>,
244 pub new_value: Option<String>,
246 pub property: ChangePropertyType,
248}
249
250#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
252pub struct Journal {
253 pub id: u64,
255 pub user: UserEssentials,
257 pub notes: Option<String>,
259 pub private_notes: bool,
261 #[serde(
263 serialize_with = "crate::api::serialize_rfc3339",
264 deserialize_with = "crate::api::deserialize_rfc3339"
265 )]
266 pub created_on: time::OffsetDateTime,
267 #[serde(
269 serialize_with = "crate::api::serialize_rfc3339",
270 deserialize_with = "crate::api::deserialize_rfc3339"
271 )]
272 pub updated_on: time::OffsetDateTime,
273 #[serde(default, skip_serializing_if = "Option::is_none")]
275 pub updated_by: Option<UserEssentials>,
276 pub details: Vec<JournalChange>,
278}
279
280#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
282pub struct ChildIssue {
283 pub id: u64,
285 pub subject: String,
287 pub tracker: TrackerEssentials,
289 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub children: Option<Vec<ChildIssue>>,
292}
293
294#[derive(Debug, Clone, Serialize, serde::Deserialize)]
298pub struct Issue {
299 pub id: u64,
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub parent: Option<IssueEssentials>,
304 pub project: ProjectEssentials,
306 pub tracker: TrackerEssentials,
308 pub status: IssueStatusEssentials,
310 pub priority: IssuePriorityEssentials,
312 pub author: UserEssentials,
314 #[serde(skip_serializing_if = "Option::is_none")]
316 pub assigned_to: Option<AssigneeEssentials>,
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub category: Option<IssueCategoryEssentials>,
320 #[serde(rename = "fixed_version", skip_serializing_if = "Option::is_none")]
322 pub version: Option<VersionEssentials>,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub subject: Option<String>,
326 pub description: Option<String>,
328 is_private: Option<bool>,
330 pub start_date: Option<time::Date>,
332 pub due_date: Option<time::Date>,
334 #[serde(
336 serialize_with = "crate::api::serialize_optional_rfc3339",
337 deserialize_with = "crate::api::deserialize_optional_rfc3339"
338 )]
339 pub closed_on: Option<time::OffsetDateTime>,
340 pub done_ratio: u64,
342 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub custom_fields: Option<Vec<CustomFieldEssentialsWithValue>>,
345 pub estimated_hours: Option<f64>,
347 #[serde(
349 serialize_with = "crate::api::serialize_rfc3339",
350 deserialize_with = "crate::api::deserialize_rfc3339"
351 )]
352 pub created_on: time::OffsetDateTime,
353 #[serde(
355 serialize_with = "crate::api::serialize_rfc3339",
356 deserialize_with = "crate::api::deserialize_rfc3339"
357 )]
358 pub updated_on: time::OffsetDateTime,
359 #[serde(default, skip_serializing_if = "Option::is_none")]
361 pub attachments: Option<Vec<Attachment>>,
362 #[serde(default, skip_serializing_if = "Option::is_none")]
364 pub relations: Option<Vec<IssueRelation>>,
365 #[serde(default, skip_serializing_if = "Option::is_none")]
367 pub changesets: Option<Vec<IssueChangeset>>,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
370 pub journals: Option<Vec<Journal>>,
371 #[serde(default, skip_serializing_if = "Option::is_none")]
373 pub children: Option<Vec<ChildIssue>>,
374 #[serde(default, skip_serializing_if = "Option::is_none")]
376 pub watchers: Option<Vec<UserEssentials>>,
377 #[serde(default, skip_serializing_if = "Option::is_none")]
379 pub spent_hours: Option<f64>,
380 #[serde(default, skip_serializing_if = "Option::is_none")]
382 pub total_spent_hours: Option<f64>,
383 #[serde(default, skip_serializing_if = "Option::is_none")]
385 pub total_estimated_hours: Option<f64>,
386}
387
388#[derive(Debug, Clone)]
390pub enum SubProjectFilter {
391 OnlyParentProject,
393 TheseSubProjects(Vec<u64>),
395 NotTheseSubProjects(Vec<u64>),
397}
398
399impl std::fmt::Display for SubProjectFilter {
400 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
401 match self {
402 SubProjectFilter::OnlyParentProject => {
403 write!(f, "!*")
404 }
405 SubProjectFilter::TheseSubProjects(ids) => {
406 let s: String = ids
407 .iter()
408 .map(|e| e.to_string())
409 .collect::<Vec<_>>()
410 .join(",");
411 write!(f, "{s}")
412 }
413 SubProjectFilter::NotTheseSubProjects(ids) => {
414 let s: String = ids
415 .iter()
416 .map(|e| format!("!{e}"))
417 .collect::<Vec<_>>()
418 .join(",");
419 write!(f, "{s}")
420 }
421 }
422 }
423}
424
425#[derive(Debug, Clone)]
427pub enum StatusFilter {
428 Open,
430 Closed,
432 All,
434 TheseStatuses(Vec<u64>),
436 NotTheseStatuses(Vec<u64>),
438}
439
440impl std::fmt::Display for StatusFilter {
441 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442 match self {
443 StatusFilter::Open => {
444 write!(f, "open")
445 }
446 StatusFilter::Closed => {
447 write!(f, "closed")
448 }
449 StatusFilter::All => {
450 write!(f, "*")
451 }
452 StatusFilter::TheseStatuses(ids) => {
453 let s: String = ids
454 .iter()
455 .map(|e| e.to_string())
456 .collect::<Vec<_>>()
457 .join(",");
458 write!(f, "{s}")
459 }
460 StatusFilter::NotTheseStatuses(ids) => {
461 let s: String = ids
462 .iter()
463 .map(|e| format!("!{e}"))
464 .collect::<Vec<_>>()
465 .join(",");
466 write!(f, "{s}")
467 }
468 }
469 }
470}
471
472#[derive(Debug, Clone)]
474pub enum AuthorFilter {
475 AnyAuthor,
477 Me,
479 NotMe,
481 TheseAuthors(Vec<u64>),
483 NotTheseAuthors(Vec<u64>),
485}
486
487impl std::fmt::Display for AuthorFilter {
488 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489 match self {
490 AuthorFilter::AnyAuthor => {
491 write!(f, "*")
492 }
493 AuthorFilter::Me => {
494 write!(f, "me")
495 }
496 AuthorFilter::NotMe => {
497 write!(f, "!me")
498 }
499 AuthorFilter::TheseAuthors(ids) => {
500 let s: String = ids
501 .iter()
502 .map(|e| e.to_string())
503 .collect::<Vec<_>>()
504 .join(",");
505 write!(f, "{s}")
506 }
507 AuthorFilter::NotTheseAuthors(ids) => {
508 let s: String = ids
509 .iter()
510 .map(|e| format!("!{e}"))
511 .collect::<Vec<_>>()
512 .join(",");
513 write!(f, "{s}")
514 }
515 }
516 }
517}
518
519#[derive(Debug, Clone)]
521pub enum AssigneeFilter {
522 AnyAssignee,
524 Me,
526 NotMe,
528 TheseAssignees(Vec<u64>),
530 NotTheseAssignees(Vec<u64>),
532 NoAssignee,
534}
535
536impl std::fmt::Display for AssigneeFilter {
537 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538 match self {
539 AssigneeFilter::AnyAssignee => {
540 write!(f, "*")
541 }
542 AssigneeFilter::Me => {
543 write!(f, "me")
544 }
545 AssigneeFilter::NotMe => {
546 write!(f, "!me")
547 }
548 AssigneeFilter::TheseAssignees(ids) => {
549 let s: String = ids
550 .iter()
551 .map(|e| e.to_string())
552 .collect::<Vec<_>>()
553 .join(",");
554 write!(f, "{s}")
555 }
556 AssigneeFilter::NotTheseAssignees(ids) => {
557 let s: String = ids
558 .iter()
559 .map(|e| format!("!{e}"))
560 .collect::<Vec<_>>()
561 .join(",");
562 write!(f, "{s}")
563 }
564 AssigneeFilter::NoAssignee => {
565 write!(f, "!*")
566 }
567 }
568 }
569}
570
571#[derive(Debug, Clone)]
573pub enum StringFieldFilter {
574 ExactMatch(String),
576 SubStringMatch(String),
578}
579
580impl std::fmt::Display for StringFieldFilter {
581 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
582 match self {
583 StringFieldFilter::ExactMatch(s) => {
584 write!(f, "{s}")
585 }
586 StringFieldFilter::SubStringMatch(s) => {
587 write!(f, "~{s}")
588 }
589 }
590 }
591}
592
593#[derive(Debug, Clone)]
596pub enum DateTimeFilterPast {
597 ExactMatch(time::OffsetDateTime),
599 Range(time::OffsetDateTime, time::OffsetDateTime),
601 LessThanOrEqual(time::OffsetDateTime),
603 GreaterThanOrEqual(time::OffsetDateTime),
605 LessThanDaysAgo(u32),
607 MoreThanDaysAgo(u32),
609 WithinPastDays(u32),
611 ExactDaysAgo(u32),
613 Today,
615 Yesterday,
617 ThisWeek,
619 LastWeek,
621 LastTwoWeeks,
623 ThisMonth,
625 LastMonth,
627 ThisYear,
629 Unset,
631 Any,
633}
634
635impl std::fmt::Display for DateTimeFilterPast {
636 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
637 let format =
638 time::macros::format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z");
639 match self {
640 DateTimeFilterPast::ExactMatch(v) => {
641 write!(
642 f,
643 "{}",
644 v.format(&format).expect(
645 "Error formatting OffsetDateTime in DateTimeFilterPast::ExactMatch"
646 )
647 )
648 }
649 DateTimeFilterPast::Range(v_start, v_end) => {
650 write!(
651 f,
652 "><{}|{}",
653 v_start.format(&format).expect(
654 "Error formatting first OffsetDateTime in DateTimeFilterPast::Range"
655 ),
656 v_end.format(&format).expect(
657 "Error formatting second OffsetDateTime in DateTimeFilterPast::Range"
658 ),
659 )
660 }
661 DateTimeFilterPast::LessThanOrEqual(v) => {
662 write!(
663 f,
664 "<={}",
665 v.format(&format).expect(
666 "Error formatting OffsetDateTime in DateTimeFilterPast::LessThanOrEqual"
667 )
668 )
669 }
670 DateTimeFilterPast::GreaterThanOrEqual(v) => {
671 write!(
672 f,
673 ">={}",
674 v.format(&format).expect(
675 "Error formatting OffsetDateTime in DateTimeFilterPast::GreaterThanOrEqual"
676 )
677 )
678 }
679 DateTimeFilterPast::LessThanDaysAgo(d) => {
680 write!(f, ">t-{}", d)
681 }
682 DateTimeFilterPast::MoreThanDaysAgo(d) => {
683 write!(f, "<t-{}", d)
684 }
685 DateTimeFilterPast::WithinPastDays(d) => {
686 write!(f, "><t-{}", d)
687 }
688 DateTimeFilterPast::ExactDaysAgo(d) => {
689 write!(f, "t-{}", d)
690 }
691 DateTimeFilterPast::Today => {
692 write!(f, "t")
693 }
694 DateTimeFilterPast::Yesterday => {
695 write!(f, "ld")
696 }
697 DateTimeFilterPast::ThisWeek => {
698 write!(f, "w")
699 }
700 DateTimeFilterPast::LastWeek => {
701 write!(f, "lw")
702 }
703 DateTimeFilterPast::LastTwoWeeks => {
704 write!(f, "l2w")
705 }
706 DateTimeFilterPast::ThisMonth => {
707 write!(f, "m")
708 }
709 DateTimeFilterPast::LastMonth => {
710 write!(f, "lm")
711 }
712 DateTimeFilterPast::ThisYear => {
713 write!(f, "y")
714 }
715 DateTimeFilterPast::Unset => {
716 write!(f, "!*")
717 }
718 DateTimeFilterPast::Any => {
719 write!(f, "*")
720 }
721 }
722 }
723}
724#[derive(Debug, Clone)]
727pub enum DateFilter {
728 ExactMatch(time::Date),
730 Range(time::Date, time::Date),
732 LessThanOrEqual(time::Date),
734 GreaterThanOrEqual(time::Date),
736 LessThanDaysAgo(u32),
738 MoreThanDaysAgo(u32),
740 WithinPastDays(u32),
742 ExactDaysAgo(u32),
744 InLessThanDays(u32),
746 InMoreThanDays(u32),
748 WithinFutureDays(u32),
750 InExactDays(u32),
752 Today,
754 Yesterday,
756 Tomorrow,
758 ThisWeek,
760 LastWeek,
762 LastTwoWeeks,
764 NextWeek,
766 ThisMonth,
768 LastMonth,
770 NextMonth,
772 ThisYear,
774 Unset,
776 Any,
778}
779
780impl std::fmt::Display for DateFilter {
781 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
782 let format = time::macros::format_description!("[year]-[month]-[day]");
783 match self {
784 DateFilter::ExactMatch(v) => {
785 write!(
786 f,
787 "{}",
788 v.format(&format)
789 .expect("Error formatting Date in DateFilter::ExactMatch")
790 )
791 }
792 DateFilter::Range(v_start, v_end) => {
793 write!(
794 f,
795 "><{}|{}",
796 v_start
797 .format(&format)
798 .expect("Error formatting first Date in DateFilter::Range"),
799 v_end
800 .format(&format)
801 .expect("Error formatting second Date in DateFilter::Range"),
802 )
803 }
804 DateFilter::LessThanOrEqual(v) => {
805 write!(
806 f,
807 "<={}",
808 v.format(&format)
809 .expect("Error formatting Date in DateFilter::LessThanOrEqual")
810 )
811 }
812 DateFilter::GreaterThanOrEqual(v) => {
813 write!(
814 f,
815 ">={}",
816 v.format(&format)
817 .expect("Error formatting Date in DateFilter::GreaterThanOrEqual")
818 )
819 }
820 DateFilter::LessThanDaysAgo(d) => {
821 write!(f, ">t-{}", d)
822 }
823 DateFilter::MoreThanDaysAgo(d) => {
824 write!(f, "<t-{}", d)
825 }
826 DateFilter::WithinPastDays(d) => {
827 write!(f, "><t-{}", d)
828 }
829 DateFilter::ExactDaysAgo(d) => {
830 write!(f, "t-{}", d)
831 }
832 DateFilter::InLessThanDays(d) => {
833 write!(f, "<t+{}", d)
834 }
835 DateFilter::InMoreThanDays(d) => {
836 write!(f, ">t+{}", d)
837 }
838 DateFilter::WithinFutureDays(d) => {
839 write!(f, "><t+{}", d)
840 }
841 DateFilter::InExactDays(d) => {
842 write!(f, "t+{}", d)
843 }
844 DateFilter::Today => {
845 write!(f, "t")
846 }
847 DateFilter::Yesterday => {
848 write!(f, "ld")
849 }
850 DateFilter::Tomorrow => {
851 write!(f, "nd")
852 }
853 DateFilter::ThisWeek => {
854 write!(f, "w")
855 }
856 DateFilter::LastWeek => {
857 write!(f, "lw")
858 }
859 DateFilter::LastTwoWeeks => {
860 write!(f, "l2w")
861 }
862 DateFilter::NextWeek => {
863 write!(f, "nw")
864 }
865 DateFilter::ThisMonth => {
866 write!(f, "m")
867 }
868 DateFilter::LastMonth => {
869 write!(f, "lm")
870 }
871 DateFilter::NextMonth => {
872 write!(f, "nm")
873 }
874 DateFilter::ThisYear => {
875 write!(f, "y")
876 }
877 DateFilter::Unset => {
878 write!(f, "!*")
879 }
880 DateFilter::Any => {
881 write!(f, "*")
882 }
883 }
884 }
885}
886
887#[derive(Debug, Clone)]
889pub enum SortByColumn {
890 Forward {
892 column_name: String,
894 },
895 Reverse {
897 column_name: String,
899 },
900}
901
902impl std::fmt::Display for SortByColumn {
903 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
904 match self {
905 SortByColumn::Forward { column_name } => {
906 write!(f, "{column_name}")
907 }
908 SortByColumn::Reverse { column_name } => {
909 write!(f, "{column_name}:desc")
910 }
911 }
912 }
913}
914
915#[derive(Debug, Clone)]
917pub enum IssueListInclude {
918 Attachments,
920 Relations,
922}
923
924impl std::fmt::Display for IssueListInclude {
925 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
926 match self {
927 Self::Attachments => {
928 write!(f, "attachments")
929 }
930 Self::Relations => {
931 write!(f, "relations")
932 }
933 }
934 }
935}
936
937#[derive(Debug, Clone, Builder)]
939#[builder(setter(strip_option))]
940pub struct ListIssues {
941 #[builder(default)]
943 include: Option<Vec<IssueListInclude>>,
944 #[builder(default)]
946 sort: Option<Vec<SortByColumn>>,
947 #[builder(default)]
949 issue_id: Option<Vec<u64>>,
950 #[builder(default)]
952 project_id: Option<Vec<u64>>,
953 #[builder(default)]
955 subproject_id: Option<SubProjectFilter>,
956 #[builder(default)]
958 tracker_id: Option<Vec<u64>>,
959 #[builder(default)]
961 priority_id: Option<Vec<u64>>,
962 #[builder(default)]
964 parent_id: Option<Vec<u64>>,
965 #[builder(default)]
967 category_id: Option<Vec<u64>>,
968 #[builder(default)]
970 status_id: Option<StatusFilter>,
971 #[builder(default)]
973 subject: Option<StringFieldFilter>,
974 #[builder(default)]
976 description: Option<StringFieldFilter>,
977 #[builder(default)]
979 author: Option<AuthorFilter>,
980 #[builder(default)]
982 assignee: Option<AssigneeFilter>,
983 #[builder(default)]
985 query_id: Option<u64>,
986 #[builder(default)]
988 version_id: Option<Vec<u64>>,
989 #[builder(default)]
991 created_on: Option<DateTimeFilterPast>,
992 #[builder(default)]
994 updated_on: Option<DateTimeFilterPast>,
995 #[builder(default)]
997 start_date: Option<DateFilter>,
998 #[builder(default)]
1000 due_date: Option<DateFilter>,
1001}
1002
1003impl ReturnsJsonResponse for ListIssues {}
1004
1005impl Pageable for ListIssues {
1006 fn response_wrapper_key(&self) -> String {
1007 "issues".to_string()
1008 }
1009}
1010
1011impl ListIssues {
1012 #[must_use]
1014 pub fn builder() -> ListIssuesBuilder {
1015 ListIssuesBuilder::default()
1016 }
1017}
1018
1019impl Endpoint for ListIssues {
1020 fn method(&self) -> Method {
1021 Method::GET
1022 }
1023
1024 fn endpoint(&self) -> Cow<'static, str> {
1025 "issues.json".into()
1026 }
1027
1028 fn parameters(&self) -> QueryParams<'_> {
1029 let mut params = QueryParams::default();
1030 params.push_opt("include", self.include.as_ref());
1031 params.push_opt("sort", self.sort.as_ref());
1032 params.push_opt("issue_id", self.issue_id.as_ref());
1033 params.push_opt("project_id", self.project_id.as_ref());
1034 params.push_opt(
1035 "subproject_id",
1036 self.subproject_id.as_ref().map(|s| s.to_string()),
1037 );
1038 params.push_opt("tracker_id", self.tracker_id.as_ref());
1039 params.push_opt("priority_id", self.priority_id.as_ref());
1040 params.push_opt("parent_id", self.parent_id.as_ref());
1041 params.push_opt("category_id", self.category_id.as_ref());
1042 params.push_opt("status_id", self.status_id.as_ref().map(|s| s.to_string()));
1043 params.push_opt("subject", self.subject.as_ref().map(|s| s.to_string()));
1044 params.push_opt(
1045 "description",
1046 self.description.as_ref().map(|s| s.to_string()),
1047 );
1048 params.push_opt("author_id", self.author.as_ref().map(|s| s.to_string()));
1049 params.push_opt(
1050 "assigned_to_id",
1051 self.assignee.as_ref().map(|s| s.to_string()),
1052 );
1053 params.push_opt("query_id", self.query_id);
1054 params.push_opt("fixed_version_id", self.version_id.as_ref());
1055 params.push_opt(
1056 "created_on",
1057 self.created_on.as_ref().map(|s| s.to_string()),
1058 );
1059 params.push_opt(
1060 "updated_on",
1061 self.updated_on.as_ref().map(|s| s.to_string()),
1062 );
1063 params.push_opt(
1064 "start_date",
1065 self.start_date.as_ref().map(|s| s.to_string()),
1066 );
1067 params.push_opt("due_date", self.due_date.as_ref().map(|s| s.to_string()));
1068 params
1069 }
1070}
1071
1072#[derive(Debug, Clone)]
1074pub enum IssueInclude {
1075 Children,
1077 Attachments,
1079 Relations,
1081 Changesets,
1083 Journals,
1085 Watchers,
1087 AllowedStatuses,
1100}
1101
1102impl std::fmt::Display for IssueInclude {
1103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1104 match self {
1105 Self::Children => {
1106 write!(f, "children")
1107 }
1108 Self::Attachments => {
1109 write!(f, "attachments")
1110 }
1111 Self::Relations => {
1112 write!(f, "relations")
1113 }
1114 Self::Changesets => {
1115 write!(f, "changesets")
1116 }
1117 Self::Journals => {
1118 write!(f, "journals")
1119 }
1120 Self::Watchers => {
1121 write!(f, "watchers")
1122 }
1123 Self::AllowedStatuses => {
1124 write!(f, "allowed_statuses")
1125 }
1126 }
1127 }
1128}
1129
1130#[derive(Debug, Clone, Builder)]
1132#[builder(setter(strip_option))]
1133pub struct GetIssue {
1134 id: u64,
1136 #[builder(default)]
1138 include: Option<Vec<IssueInclude>>,
1139}
1140
1141impl ReturnsJsonResponse for GetIssue {}
1142impl NoPagination for GetIssue {}
1143
1144impl GetIssue {
1145 #[must_use]
1147 pub fn builder() -> GetIssueBuilder {
1148 GetIssueBuilder::default()
1149 }
1150}
1151
1152impl Endpoint for GetIssue {
1153 fn method(&self) -> Method {
1154 Method::GET
1155 }
1156
1157 fn endpoint(&self) -> Cow<'static, str> {
1158 format!("issues/{}.json", &self.id).into()
1159 }
1160
1161 fn parameters(&self) -> QueryParams<'_> {
1162 let mut params = QueryParams::default();
1163 params.push_opt("include", self.include.as_ref());
1164 params
1165 }
1166}
1167
1168#[derive(Debug, Clone, Serialize, serde::Deserialize)]
1170pub struct CustomField<'a> {
1171 pub id: u64,
1173 pub name: Option<Cow<'a, str>>,
1175 pub value: Cow<'a, str>,
1177}
1178
1179#[derive(Debug, Clone, Serialize)]
1182pub struct UploadedAttachment<'a> {
1183 pub token: Cow<'a, str>,
1185 pub filename: Cow<'a, str>,
1187 #[serde(skip_serializing_if = "Option::is_none")]
1189 pub description: Option<Cow<'a, str>>,
1190 pub content_type: Cow<'a, str>,
1192}
1193
1194#[serde_with::skip_serializing_none]
1196#[derive(Debug, Clone, Builder, Serialize)]
1197#[builder(setter(strip_option))]
1198pub struct CreateIssue<'a> {
1199 project_id: u64,
1201 #[builder(default)]
1203 tracker_id: Option<u64>,
1204 #[builder(default)]
1206 status_id: Option<u64>,
1207 #[builder(default)]
1209 priority_id: Option<u64>,
1210 #[builder(setter(into), default)]
1212 subject: Option<Cow<'a, str>>,
1213 #[builder(setter(into), default)]
1215 description: Option<Cow<'a, str>>,
1216 #[builder(default)]
1218 category_id: Option<u64>,
1219 #[builder(default, setter(name = "version"))]
1221 fixed_version_id: Option<u64>,
1222 #[builder(default)]
1224 assigned_to_id: Option<u64>,
1225 #[builder(default)]
1227 parent_issue_id: Option<u64>,
1228 #[builder(default)]
1230 custom_fields: Option<Vec<CustomField<'a>>>,
1231 #[builder(default)]
1233 watcher_user_ids: Option<Vec<u64>>,
1234 #[builder(default)]
1236 is_private: Option<bool>,
1237 #[builder(default)]
1239 estimated_hours: Option<f64>,
1240 #[builder(default)]
1242 uploads: Option<Vec<UploadedAttachment<'a>>>,
1243}
1244
1245impl<'a> CreateIssue<'a> {
1246 #[must_use]
1248 pub fn builder() -> CreateIssueBuilder<'a> {
1249 CreateIssueBuilder::default()
1250 }
1251}
1252
1253impl ReturnsJsonResponse for CreateIssue<'_> {}
1254impl NoPagination for CreateIssue<'_> {}
1255
1256impl Endpoint for CreateIssue<'_> {
1257 fn method(&self) -> Method {
1258 Method::POST
1259 }
1260
1261 fn endpoint(&self) -> Cow<'static, str> {
1262 "issues.json".into()
1263 }
1264
1265 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, crate::Error> {
1266 Ok(Some((
1267 "application/json",
1268 serde_json::to_vec(&IssueWrapper::<CreateIssue> {
1269 issue: (*self).to_owned(),
1270 })?,
1271 )))
1272 }
1273}
1274
1275#[serde_with::skip_serializing_none]
1277#[derive(Debug, Clone, Builder, Serialize)]
1278#[builder(setter(strip_option))]
1279pub struct UpdateIssue<'a> {
1280 #[serde(skip_serializing)]
1282 id: u64,
1283 #[builder(default)]
1285 project_id: Option<u64>,
1286 #[builder(default)]
1288 tracker_id: Option<u64>,
1289 #[builder(default)]
1291 status_id: Option<u64>,
1292 #[builder(default)]
1294 priority_id: Option<u64>,
1295 #[builder(setter(into), default)]
1297 subject: Option<Cow<'a, str>>,
1298 #[builder(setter(into), default)]
1300 description: Option<Cow<'a, str>>,
1301 #[builder(default)]
1303 category_id: Option<u64>,
1304 #[builder(default, setter(name = "version"))]
1306 fixed_version_id: Option<u64>,
1307 #[builder(default)]
1309 assigned_to_id: Option<u64>,
1310 #[builder(default)]
1312 parent_issue_id: Option<u64>,
1313 #[builder(default)]
1315 custom_fields: Option<Vec<CustomField<'a>>>,
1316 #[builder(default)]
1318 watcher_user_ids: Option<Vec<u64>>,
1319 #[builder(default)]
1321 is_private: Option<bool>,
1322 #[builder(default)]
1324 estimated_hours: Option<f64>,
1325 #[builder(default)]
1327 notes: Option<Cow<'a, str>>,
1328 #[builder(default)]
1330 private_notes: Option<bool>,
1331 #[builder(default)]
1333 uploads: Option<Vec<UploadedAttachment<'a>>>,
1334}
1335
1336impl<'a> UpdateIssue<'a> {
1337 #[must_use]
1339 pub fn builder() -> UpdateIssueBuilder<'a> {
1340 UpdateIssueBuilder::default()
1341 }
1342}
1343
1344impl Endpoint for UpdateIssue<'_> {
1345 fn method(&self) -> Method {
1346 Method::PUT
1347 }
1348
1349 fn endpoint(&self) -> Cow<'static, str> {
1350 format!("issues/{}.json", self.id).into()
1351 }
1352
1353 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, crate::Error> {
1354 Ok(Some((
1355 "application/json",
1356 serde_json::to_vec(&IssueWrapper::<UpdateIssue> {
1357 issue: (*self).to_owned(),
1358 })?,
1359 )))
1360 }
1361}
1362
1363#[derive(Debug, Clone, Builder)]
1365#[builder(setter(strip_option))]
1366pub struct DeleteIssue {
1367 id: u64,
1369}
1370
1371impl DeleteIssue {
1372 #[must_use]
1374 pub fn builder() -> DeleteIssueBuilder {
1375 DeleteIssueBuilder::default()
1376 }
1377}
1378
1379impl Endpoint for DeleteIssue {
1380 fn method(&self) -> Method {
1381 Method::DELETE
1382 }
1383
1384 fn endpoint(&self) -> Cow<'static, str> {
1385 format!("issues/{}.json", &self.id).into()
1386 }
1387}
1388
1389#[derive(Debug, Clone, Builder, Serialize)]
1391#[builder(setter(strip_option))]
1392pub struct AddWatcher {
1393 #[serde(skip_serializing)]
1395 issue_id: u64,
1396 user_id: u64,
1398}
1399
1400impl AddWatcher {
1401 #[must_use]
1403 pub fn builder() -> AddWatcherBuilder {
1404 AddWatcherBuilder::default()
1405 }
1406}
1407
1408impl Endpoint for AddWatcher {
1409 fn method(&self) -> Method {
1410 Method::POST
1411 }
1412
1413 fn endpoint(&self) -> Cow<'static, str> {
1414 format!("issues/{}/watchers.json", &self.issue_id).into()
1415 }
1416
1417 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, crate::Error> {
1418 Ok(Some(("application/json", serde_json::to_vec(self)?)))
1419 }
1420}
1421
1422#[derive(Debug, Clone, Builder)]
1424#[builder(setter(strip_option))]
1425pub struct RemoveWatcher {
1426 issue_id: u64,
1428 user_id: u64,
1430}
1431
1432impl RemoveWatcher {
1433 #[must_use]
1435 pub fn builder() -> RemoveWatcherBuilder {
1436 RemoveWatcherBuilder::default()
1437 }
1438}
1439
1440impl Endpoint for RemoveWatcher {
1441 fn method(&self) -> Method {
1442 Method::DELETE
1443 }
1444
1445 fn endpoint(&self) -> Cow<'static, str> {
1446 format!("issues/{}/watchers/{}.json", &self.issue_id, &self.user_id).into()
1447 }
1448}
1449
1450#[derive(Debug, Clone, PartialEq, Eq, Serialize, serde::Deserialize)]
1452pub struct IssuesWrapper<T> {
1453 pub issues: Vec<T>,
1455}
1456
1457#[derive(Debug, Clone, PartialEq, Eq, Serialize, serde::Deserialize)]
1460pub struct IssueWrapper<T> {
1461 pub issue: T,
1463}
1464
1465#[cfg(test)]
1466pub(crate) mod test {
1467 use super::*;
1468 use crate::api::ResponsePage;
1469 use crate::api::test_helpers::with_project;
1470 use pretty_assertions::assert_eq;
1471 use std::error::Error;
1472 use tokio::sync::RwLock;
1473 use tracing_test::traced_test;
1474
1475 pub static ISSUES_LOCK: RwLock<()> = RwLock::const_new(());
1478
1479 #[traced_test]
1480 #[test]
1481 fn test_list_issues_first_page() -> Result<(), Box<dyn Error>> {
1482 let _r_issues = ISSUES_LOCK.blocking_read();
1483 dotenvy::dotenv()?;
1484 let redmine = crate::api::Redmine::from_env(
1485 reqwest::blocking::Client::builder()
1486 .use_rustls_tls()
1487 .build()?,
1488 )?;
1489 let endpoint = ListIssues::builder().build()?;
1490 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1491 Ok(())
1492 }
1493
1494 #[traced_test]
1498 #[test]
1499 #[ignore]
1500 fn test_list_issues_all_pages() -> Result<(), Box<dyn Error>> {
1501 let _r_issues = ISSUES_LOCK.blocking_read();
1502 dotenvy::dotenv()?;
1503 let redmine = crate::api::Redmine::from_env(
1504 reqwest::blocking::Client::builder()
1505 .use_rustls_tls()
1506 .build()?,
1507 )?;
1508 let endpoint = ListIssues::builder().build()?;
1509 redmine.json_response_body_all_pages::<_, Issue>(&endpoint)?;
1510 Ok(())
1511 }
1512
1513 #[traced_test]
1517 #[test]
1518 #[ignore]
1519 fn test_list_issues_all_pages_iter() -> Result<(), Box<dyn Error>> {
1520 let _r_issues = ISSUES_LOCK.blocking_read();
1521 dotenvy::dotenv()?;
1522 let redmine = crate::api::Redmine::from_env(
1523 reqwest::blocking::Client::builder()
1524 .use_rustls_tls()
1525 .build()?,
1526 )?;
1527 let endpoint = ListIssues::builder().build()?;
1528 let mut i = 0;
1529 for issue in redmine.json_response_body_all_pages_iter::<_, Issue>(&endpoint) {
1530 let _issue = issue?;
1531 i += 1;
1532 }
1533 assert!(i > 0);
1534
1535 Ok(())
1536 }
1537
1538 #[traced_test]
1542 #[tokio::test]
1543 #[ignore]
1544 async fn test_list_issues_all_pages_stream() -> Result<(), Box<dyn Error>> {
1545 let _r_issues = ISSUES_LOCK.read().await;
1546 dotenvy::dotenv()?;
1547 let redmine = crate::api::RedmineAsync::from_env(
1548 reqwest::Client::builder().use_rustls_tls().build()?,
1549 )?;
1550 let endpoint = ListIssues::builder().build()?;
1551 let mut i = 0;
1552 let mut stream = redmine.json_response_body_all_pages_stream::<_, Issue>(&endpoint);
1553 while let Some(issue) = <_ as futures::stream::StreamExt>::next(&mut stream).await {
1554 let _issue = issue?;
1555 i += 1;
1556 }
1557 assert!(i > 0);
1558
1559 Ok(())
1560 }
1561
1562 #[traced_test]
1563 #[test]
1564 fn test_get_issue() -> Result<(), Box<dyn Error>> {
1565 let _r_issues = ISSUES_LOCK.blocking_read();
1566 dotenvy::dotenv()?;
1567 let redmine = crate::api::Redmine::from_env(
1568 reqwest::blocking::Client::builder()
1569 .use_rustls_tls()
1570 .build()?,
1571 )?;
1572 let endpoint = GetIssue::builder().id(40000).build()?;
1573 redmine.json_response_body::<_, IssueWrapper<Issue>>(&endpoint)?;
1574 Ok(())
1575 }
1576
1577 #[function_name::named]
1578 #[traced_test]
1579 #[test]
1580 fn test_create_issue() -> Result<(), Box<dyn Error>> {
1581 let _w_issues = ISSUES_LOCK.blocking_write();
1582 let name = format!("unittest_{}", function_name!());
1583 with_project(&name, |redmine, project_id, _| {
1584 let create_endpoint = super::CreateIssue::builder()
1585 .project_id(project_id)
1586 .subject("old test subject")
1587 .build()?;
1588 redmine.json_response_body::<_, IssueWrapper<Issue>>(&create_endpoint)?;
1589 Ok(())
1590 })?;
1591 Ok(())
1592 }
1593
1594 #[function_name::named]
1595 #[traced_test]
1596 #[test]
1597 fn test_update_issue() -> Result<(), Box<dyn Error>> {
1598 let _w_issues = ISSUES_LOCK.blocking_write();
1599 let name = format!("unittest_{}", function_name!());
1600 with_project(&name, |redmine, project_id, _name| {
1601 let create_endpoint = super::CreateIssue::builder()
1602 .project_id(project_id)
1603 .subject("old test subject")
1604 .build()?;
1605 let IssueWrapper { issue }: IssueWrapper<Issue> =
1606 redmine.json_response_body::<_, _>(&create_endpoint)?;
1607 let update_endpoint = super::UpdateIssue::builder()
1608 .id(issue.id)
1609 .subject("New test subject")
1610 .build()?;
1611 redmine.ignore_response_body::<_>(&update_endpoint)?;
1612 Ok(())
1613 })?;
1614 Ok(())
1615 }
1616
1617 #[function_name::named]
1618 #[traced_test]
1619 #[test]
1620 fn test_delete_issue() -> Result<(), Box<dyn Error>> {
1621 let _w_issues = ISSUES_LOCK.blocking_write();
1622 let name = format!("unittest_{}", function_name!());
1623 with_project(&name, |redmine, project_id, _name| {
1624 let create_endpoint = super::CreateIssue::builder()
1625 .project_id(project_id)
1626 .subject("test subject")
1627 .build()?;
1628 let IssueWrapper { issue }: IssueWrapper<Issue> =
1629 redmine.json_response_body::<_, _>(&create_endpoint)?;
1630 let delete_endpoint = super::DeleteIssue::builder().id(issue.id).build()?;
1631 redmine.ignore_response_body::<_>(&delete_endpoint)?;
1632 Ok(())
1633 })?;
1634 Ok(())
1635 }
1636
1637 #[traced_test]
1642 #[test]
1643 fn test_completeness_issue_type_first_page() -> Result<(), Box<dyn Error>> {
1644 let _r_issues = ISSUES_LOCK.blocking_read();
1645 dotenvy::dotenv()?;
1646 let redmine = crate::api::Redmine::from_env(
1647 reqwest::blocking::Client::builder()
1648 .use_rustls_tls()
1649 .build()?,
1650 )?;
1651 let endpoint = ListIssues::builder()
1652 .include(vec![
1653 IssueListInclude::Attachments,
1654 IssueListInclude::Relations,
1655 ])
1656 .build()?;
1657 let ResponsePage {
1658 values,
1659 total_count: _,
1660 offset: _,
1661 limit: _,
1662 } = redmine.json_response_body_page::<_, serde_json::Value>(&endpoint, 0, 100)?;
1663 for value in values {
1664 let o: Issue = serde_json::from_value(value.clone())?;
1665 let reserialized = serde_json::to_value(o)?;
1666 let expected_value = if let serde_json::Value::Object(obj) = value {
1667 let mut expected_obj = obj.clone();
1668 if obj
1669 .get("total_estimated_hours")
1670 .is_some_and(|v| *v == serde_json::Value::Null)
1671 {
1672 expected_obj.remove("total_estimated_hours");
1673 }
1674 serde_json::Value::Object(expected_obj)
1675 } else {
1676 value
1677 };
1678 assert_eq!(expected_value, reserialized);
1679 }
1680 Ok(())
1681 }
1682
1683 #[traced_test]
1692 #[test]
1693 #[ignore]
1694 fn test_completeness_issue_type_all_pages() -> Result<(), Box<dyn Error>> {
1695 let _r_issues = ISSUES_LOCK.blocking_read();
1696 dotenvy::dotenv()?;
1697 let redmine = crate::api::Redmine::from_env(
1698 reqwest::blocking::Client::builder()
1699 .use_rustls_tls()
1700 .build()?,
1701 )?;
1702 let endpoint = ListIssues::builder()
1703 .include(vec![
1704 IssueListInclude::Attachments,
1705 IssueListInclude::Relations,
1706 ])
1707 .build()?;
1708 let values = redmine.json_response_body_all_pages::<_, serde_json::Value>(&endpoint)?;
1709 for value in values {
1710 let o: Issue = serde_json::from_value(value.clone())?;
1711 let reserialized = serde_json::to_value(o)?;
1712 let expected_value = if let serde_json::Value::Object(obj) = value {
1713 let mut expected_obj = obj.clone();
1714 if obj
1715 .get("total_estimated_hours")
1716 .is_some_and(|v| *v == serde_json::Value::Null)
1717 {
1718 expected_obj.remove("total_estimated_hours");
1719 }
1720 serde_json::Value::Object(expected_obj)
1721 } else {
1722 value
1723 };
1724 assert_eq!(expected_value, reserialized);
1725 }
1726 Ok(())
1727 }
1728
1729 #[traced_test]
1739 #[test]
1740 #[ignore]
1741 fn test_completeness_issue_type_all_pages_all_issue_details() -> Result<(), Box<dyn Error>> {
1742 let _r_issues = ISSUES_LOCK.blocking_read();
1743 dotenvy::dotenv()?;
1744 let redmine = crate::api::Redmine::from_env(
1745 reqwest::blocking::Client::builder()
1746 .use_rustls_tls()
1747 .build()?,
1748 )?;
1749 let endpoint = ListIssues::builder()
1750 .include(vec![
1751 IssueListInclude::Attachments,
1752 IssueListInclude::Relations,
1753 ])
1754 .build()?;
1755 let issues = redmine.json_response_body_all_pages::<_, Issue>(&endpoint)?;
1756 for issue in issues {
1757 let get_endpoint = GetIssue::builder()
1758 .id(issue.id)
1759 .include(vec![
1760 IssueInclude::Attachments,
1761 IssueInclude::Children,
1762 IssueInclude::Changesets,
1763 IssueInclude::Relations,
1764 IssueInclude::Journals,
1765 IssueInclude::Watchers,
1766 ])
1767 .build()?;
1768 let IssueWrapper { issue: mut value } =
1769 redmine.json_response_body::<_, IssueWrapper<serde_json::Value>>(&get_endpoint)?;
1770 let o: Issue = serde_json::from_value(value.clone())?;
1771 let value_object = value.as_object_mut().unwrap();
1777 if value_object.get("total_estimated_hours") == Some(&serde_json::Value::Null) {
1778 value_object.remove("total_estimated_hours");
1779 }
1780 let reserialized = serde_json::to_value(o)?;
1781 assert_eq!(value, reserialized);
1782 }
1783 Ok(())
1784 }
1785
1786 #[traced_test]
1787 #[test]
1788 fn test_list_issues_created_on_filter_exact_match() -> Result<(), Box<dyn Error>> {
1789 let _r_issues = ISSUES_LOCK.blocking_read();
1790 dotenvy::dotenv()?;
1791 let redmine = crate::api::Redmine::from_env(
1792 reqwest::blocking::Client::builder()
1793 .use_rustls_tls()
1794 .build()?,
1795 )?;
1796
1797 let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
1798 let endpoint = ListIssues::builder()
1799 .created_on(DateTimeFilterPast::ExactMatch(dt))
1800 .build()?;
1801 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1802
1803 Ok(())
1804 }
1805
1806 #[traced_test]
1807 #[test]
1808 fn test_list_issues_created_on_filter_range() -> Result<(), Box<dyn Error>> {
1809 let _r_issues = ISSUES_LOCK.blocking_read();
1810 dotenvy::dotenv()?;
1811 let redmine = crate::api::Redmine::from_env(
1812 reqwest::blocking::Client::builder()
1813 .use_rustls_tls()
1814 .build()?,
1815 )?;
1816
1817 let dt_start = time::macros::datetime!(2023-01-01 00:00:00 UTC);
1818 let dt_end = time::macros::datetime!(2023-01-31 23:59:59 UTC);
1819 let endpoint = ListIssues::builder()
1820 .created_on(DateTimeFilterPast::Range(dt_start, dt_end))
1821 .build()?;
1822 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1823
1824 Ok(())
1825 }
1826
1827 #[traced_test]
1828 #[test]
1829 fn test_list_issues_created_on_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
1830 let _r_issues = ISSUES_LOCK.blocking_read();
1831 dotenvy::dotenv()?;
1832 let redmine = crate::api::Redmine::from_env(
1833 reqwest::blocking::Client::builder()
1834 .use_rustls_tls()
1835 .build()?,
1836 )?;
1837
1838 let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
1839 let endpoint = ListIssues::builder()
1840 .created_on(DateTimeFilterPast::LessThanOrEqual(dt))
1841 .build()?;
1842 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1843
1844 Ok(())
1845 }
1846
1847 #[traced_test]
1848 #[test]
1849 fn test_list_issues_created_on_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
1850 let _r_issues = ISSUES_LOCK.blocking_read();
1851 dotenvy::dotenv()?;
1852 let redmine = crate::api::Redmine::from_env(
1853 reqwest::blocking::Client::builder()
1854 .use_rustls_tls()
1855 .build()?,
1856 )?;
1857
1858 let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
1859 let endpoint = ListIssues::builder()
1860 .created_on(DateTimeFilterPast::GreaterThanOrEqual(dt))
1861 .build()?;
1862 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1863
1864 Ok(())
1865 }
1866
1867 #[traced_test]
1868 #[test]
1869 fn test_list_issues_created_on_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
1870 let _r_issues = ISSUES_LOCK.blocking_read();
1871 dotenvy::dotenv()?;
1872 let redmine = crate::api::Redmine::from_env(
1873 reqwest::blocking::Client::builder()
1874 .use_rustls_tls()
1875 .build()?,
1876 )?;
1877
1878 let endpoint = ListIssues::builder()
1879 .created_on(DateTimeFilterPast::LessThanDaysAgo(5))
1880 .build()?;
1881 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1882
1883 Ok(())
1884 }
1885
1886 #[traced_test]
1887 #[test]
1888 fn test_list_issues_created_on_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
1889 let _r_issues = ISSUES_LOCK.blocking_read();
1890 dotenvy::dotenv()?;
1891 let redmine = crate::api::Redmine::from_env(
1892 reqwest::blocking::Client::builder()
1893 .use_rustls_tls()
1894 .build()?,
1895 )?;
1896
1897 let endpoint = ListIssues::builder()
1898 .created_on(DateTimeFilterPast::MoreThanDaysAgo(10))
1899 .build()?;
1900 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1901
1902 Ok(())
1903 }
1904
1905 #[traced_test]
1906 #[test]
1907 fn test_list_issues_created_on_filter_within_past_days() -> Result<(), Box<dyn Error>> {
1908 let _r_issues = ISSUES_LOCK.blocking_read();
1909 dotenvy::dotenv()?;
1910 let redmine = crate::api::Redmine::from_env(
1911 reqwest::blocking::Client::builder()
1912 .use_rustls_tls()
1913 .build()?,
1914 )?;
1915
1916 let endpoint = ListIssues::builder()
1917 .created_on(DateTimeFilterPast::WithinPastDays(7))
1918 .build()?;
1919 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1920
1921 Ok(())
1922 }
1923
1924 #[traced_test]
1925 #[test]
1926 fn test_list_issues_created_on_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
1927 let _r_issues = ISSUES_LOCK.blocking_read();
1928 dotenvy::dotenv()?;
1929 let redmine = crate::api::Redmine::from_env(
1930 reqwest::blocking::Client::builder()
1931 .use_rustls_tls()
1932 .build()?,
1933 )?;
1934
1935 let endpoint = ListIssues::builder()
1936 .created_on(DateTimeFilterPast::ExactDaysAgo(3))
1937 .build()?;
1938 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1939
1940 Ok(())
1941 }
1942
1943 #[traced_test]
1944 #[test]
1945 fn test_list_issues_created_on_filter_today() -> Result<(), Box<dyn Error>> {
1946 let _r_issues = ISSUES_LOCK.blocking_read();
1947 dotenvy::dotenv()?;
1948 let redmine = crate::api::Redmine::from_env(
1949 reqwest::blocking::Client::builder()
1950 .use_rustls_tls()
1951 .build()?,
1952 )?;
1953
1954 let endpoint = ListIssues::builder()
1955 .created_on(DateTimeFilterPast::Today)
1956 .build()?;
1957 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1958
1959 Ok(())
1960 }
1961
1962 #[traced_test]
1963 #[test]
1964 fn test_list_issues_created_on_filter_yesterday() -> Result<(), Box<dyn Error>> {
1965 let _r_issues = ISSUES_LOCK.blocking_read();
1966 dotenvy::dotenv()?;
1967 let redmine = crate::api::Redmine::from_env(
1968 reqwest::blocking::Client::builder()
1969 .use_rustls_tls()
1970 .build()?,
1971 )?;
1972
1973 let endpoint = ListIssues::builder()
1974 .created_on(DateTimeFilterPast::Yesterday)
1975 .build()?;
1976 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1977
1978 Ok(())
1979 }
1980
1981 #[traced_test]
1982 #[test]
1983 fn test_list_issues_created_on_filter_this_week() -> Result<(), Box<dyn Error>> {
1984 let _r_issues = ISSUES_LOCK.blocking_read();
1985 dotenvy::dotenv()?;
1986 let redmine = crate::api::Redmine::from_env(
1987 reqwest::blocking::Client::builder()
1988 .use_rustls_tls()
1989 .build()?,
1990 )?;
1991
1992 let endpoint = ListIssues::builder()
1993 .created_on(DateTimeFilterPast::ThisWeek)
1994 .build()?;
1995 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
1996
1997 Ok(())
1998 }
1999
2000 #[traced_test]
2001 #[test]
2002 fn test_list_issues_created_on_filter_last_week() -> Result<(), Box<dyn Error>> {
2003 let _r_issues = ISSUES_LOCK.blocking_read();
2004 dotenvy::dotenv()?;
2005 let redmine = crate::api::Redmine::from_env(
2006 reqwest::blocking::Client::builder()
2007 .use_rustls_tls()
2008 .build()?,
2009 )?;
2010
2011 let endpoint = ListIssues::builder()
2012 .created_on(DateTimeFilterPast::LastWeek)
2013 .build()?;
2014 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2015
2016 Ok(())
2017 }
2018
2019 #[traced_test]
2020 #[test]
2021 fn test_list_issues_created_on_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
2022 let _r_issues = ISSUES_LOCK.blocking_read();
2023 dotenvy::dotenv()?;
2024 let redmine = crate::api::Redmine::from_env(
2025 reqwest::blocking::Client::builder()
2026 .use_rustls_tls()
2027 .build()?,
2028 )?;
2029
2030 let endpoint = ListIssues::builder()
2031 .created_on(DateTimeFilterPast::LastTwoWeeks)
2032 .build()?;
2033 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2034
2035 Ok(())
2036 }
2037
2038 #[traced_test]
2039 #[test]
2040 fn test_list_issues_created_on_filter_this_month() -> Result<(), Box<dyn Error>> {
2041 let _r_issues = ISSUES_LOCK.blocking_read();
2042 dotenvy::dotenv()?;
2043 let redmine = crate::api::Redmine::from_env(
2044 reqwest::blocking::Client::builder()
2045 .use_rustls_tls()
2046 .build()?,
2047 )?;
2048
2049 let endpoint = ListIssues::builder()
2050 .created_on(DateTimeFilterPast::ThisMonth)
2051 .build()?;
2052 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2053
2054 Ok(())
2055 }
2056
2057 #[traced_test]
2058 #[test]
2059 fn test_list_issues_created_on_filter_last_month() -> Result<(), Box<dyn Error>> {
2060 let _r_issues = ISSUES_LOCK.blocking_read();
2061 dotenvy::dotenv()?;
2062 let redmine = crate::api::Redmine::from_env(
2063 reqwest::blocking::Client::builder()
2064 .use_rustls_tls()
2065 .build()?,
2066 )?;
2067
2068 let endpoint = ListIssues::builder()
2069 .created_on(DateTimeFilterPast::LastMonth)
2070 .build()?;
2071 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2072
2073 Ok(())
2074 }
2075
2076 #[traced_test]
2077 #[test]
2078 fn test_list_issues_created_on_filter_this_year() -> Result<(), Box<dyn Error>> {
2079 let _r_issues = ISSUES_LOCK.blocking_read();
2080 dotenvy::dotenv()?;
2081 let redmine = crate::api::Redmine::from_env(
2082 reqwest::blocking::Client::builder()
2083 .use_rustls_tls()
2084 .build()?,
2085 )?;
2086
2087 let endpoint = ListIssues::builder()
2088 .created_on(DateTimeFilterPast::ThisYear)
2089 .build()?;
2090 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2091
2092 Ok(())
2093 }
2094
2095 #[traced_test]
2096 #[test]
2097 fn test_list_issues_created_on_filter_unset() -> Result<(), Box<dyn Error>> {
2098 let _r_issues = ISSUES_LOCK.blocking_read();
2099 dotenvy::dotenv()?;
2100 let redmine = crate::api::Redmine::from_env(
2101 reqwest::blocking::Client::builder()
2102 .use_rustls_tls()
2103 .build()?,
2104 )?;
2105
2106 let endpoint = ListIssues::builder()
2107 .created_on(DateTimeFilterPast::Unset)
2108 .build()?;
2109 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2110
2111 Ok(())
2112 }
2113
2114 #[traced_test]
2115 #[test]
2116 fn test_list_issues_created_on_filter_any() -> Result<(), Box<dyn Error>> {
2117 let _r_issues = ISSUES_LOCK.blocking_read();
2118 dotenvy::dotenv()?;
2119 let redmine = crate::api::Redmine::from_env(
2120 reqwest::blocking::Client::builder()
2121 .use_rustls_tls()
2122 .build()?,
2123 )?;
2124
2125 let endpoint = ListIssues::builder()
2126 .created_on(DateTimeFilterPast::Any)
2127 .build()?;
2128 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2129
2130 Ok(())
2131 }
2132
2133 #[traced_test]
2134 #[test]
2135 fn test_list_issues_updated_on_filter_exact_match() -> Result<(), Box<dyn Error>> {
2136 let _r_issues = ISSUES_LOCK.blocking_read();
2137 dotenvy::dotenv()?;
2138 let redmine = crate::api::Redmine::from_env(
2139 reqwest::blocking::Client::builder()
2140 .use_rustls_tls()
2141 .build()?,
2142 )?;
2143
2144 let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
2145 let endpoint = ListIssues::builder()
2146 .updated_on(DateTimeFilterPast::ExactMatch(dt))
2147 .build()?;
2148 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2149
2150 Ok(())
2151 }
2152
2153 #[traced_test]
2154 #[test]
2155 fn test_list_issues_updated_on_filter_range() -> Result<(), Box<dyn Error>> {
2156 let _r_issues = ISSUES_LOCK.blocking_read();
2157 dotenvy::dotenv()?;
2158 let redmine = crate::api::Redmine::from_env(
2159 reqwest::blocking::Client::builder()
2160 .use_rustls_tls()
2161 .build()?,
2162 )?;
2163
2164 let dt_start = time::macros::datetime!(2023-01-01 00:00:00 UTC);
2165 let dt_end = time::macros::datetime!(2023-01-31 23:59:59 UTC);
2166 let endpoint = ListIssues::builder()
2167 .updated_on(DateTimeFilterPast::Range(dt_start, dt_end))
2168 .build()?;
2169 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2170
2171 Ok(())
2172 }
2173
2174 #[traced_test]
2175 #[test]
2176 fn test_list_issues_updated_on_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
2177 let _r_issues = ISSUES_LOCK.blocking_read();
2178 dotenvy::dotenv()?;
2179 let redmine = crate::api::Redmine::from_env(
2180 reqwest::blocking::Client::builder()
2181 .use_rustls_tls()
2182 .build()?,
2183 )?;
2184
2185 let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
2186 let endpoint = ListIssues::builder()
2187 .updated_on(DateTimeFilterPast::LessThanOrEqual(dt))
2188 .build()?;
2189 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2190
2191 Ok(())
2192 }
2193
2194 #[traced_test]
2195 #[test]
2196 fn test_list_issues_updated_on_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
2197 let _r_issues = ISSUES_LOCK.blocking_read();
2198 dotenvy::dotenv()?;
2199 let redmine = crate::api::Redmine::from_env(
2200 reqwest::blocking::Client::builder()
2201 .use_rustls_tls()
2202 .build()?,
2203 )?;
2204
2205 let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
2206 let endpoint = ListIssues::builder()
2207 .updated_on(DateTimeFilterPast::GreaterThanOrEqual(dt))
2208 .build()?;
2209 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2210
2211 Ok(())
2212 }
2213
2214 #[traced_test]
2215 #[test]
2216 fn test_list_issues_updated_on_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
2217 let _r_issues = ISSUES_LOCK.blocking_read();
2218 dotenvy::dotenv()?;
2219 let redmine = crate::api::Redmine::from_env(
2220 reqwest::blocking::Client::builder()
2221 .use_rustls_tls()
2222 .build()?,
2223 )?;
2224
2225 let endpoint = ListIssues::builder()
2226 .updated_on(DateTimeFilterPast::LessThanDaysAgo(5))
2227 .build()?;
2228 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2229
2230 Ok(())
2231 }
2232
2233 #[traced_test]
2234 #[test]
2235 fn test_list_issues_updated_on_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
2236 let _r_issues = ISSUES_LOCK.blocking_read();
2237 dotenvy::dotenv()?;
2238 let redmine = crate::api::Redmine::from_env(
2239 reqwest::blocking::Client::builder()
2240 .use_rustls_tls()
2241 .build()?,
2242 )?;
2243
2244 let endpoint = ListIssues::builder()
2245 .updated_on(DateTimeFilterPast::MoreThanDaysAgo(10))
2246 .build()?;
2247 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2248
2249 Ok(())
2250 }
2251
2252 #[traced_test]
2253 #[test]
2254 fn test_list_issues_updated_on_filter_within_past_days() -> Result<(), Box<dyn Error>> {
2255 let _r_issues = ISSUES_LOCK.blocking_read();
2256 dotenvy::dotenv()?;
2257 let redmine = crate::api::Redmine::from_env(
2258 reqwest::blocking::Client::builder()
2259 .use_rustls_tls()
2260 .build()?,
2261 )?;
2262
2263 let endpoint = ListIssues::builder()
2264 .updated_on(DateTimeFilterPast::WithinPastDays(7))
2265 .build()?;
2266 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2267
2268 Ok(())
2269 }
2270
2271 #[traced_test]
2272 #[test]
2273 fn test_list_issues_updated_on_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
2274 let _r_issues = ISSUES_LOCK.blocking_read();
2275 dotenvy::dotenv()?;
2276 let redmine = crate::api::Redmine::from_env(
2277 reqwest::blocking::Client::builder()
2278 .use_rustls_tls()
2279 .build()?,
2280 )?;
2281
2282 let endpoint = ListIssues::builder()
2283 .updated_on(DateTimeFilterPast::ExactDaysAgo(3))
2284 .build()?;
2285 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2286
2287 Ok(())
2288 }
2289
2290 #[traced_test]
2291 #[test]
2292 fn test_list_issues_updated_on_filter_today() -> Result<(), Box<dyn Error>> {
2293 let _r_issues = ISSUES_LOCK.blocking_read();
2294 dotenvy::dotenv()?;
2295 let redmine = crate::api::Redmine::from_env(
2296 reqwest::blocking::Client::builder()
2297 .use_rustls_tls()
2298 .build()?,
2299 )?;
2300
2301 let endpoint = ListIssues::builder()
2302 .updated_on(DateTimeFilterPast::Today)
2303 .build()?;
2304 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2305
2306 Ok(())
2307 }
2308
2309 #[traced_test]
2310 #[test]
2311 fn test_list_issues_updated_on_filter_yesterday() -> Result<(), Box<dyn Error>> {
2312 let _r_issues = ISSUES_LOCK.blocking_read();
2313 dotenvy::dotenv()?;
2314 let redmine = crate::api::Redmine::from_env(
2315 reqwest::blocking::Client::builder()
2316 .use_rustls_tls()
2317 .build()?,
2318 )?;
2319
2320 let endpoint = ListIssues::builder()
2321 .updated_on(DateTimeFilterPast::Yesterday)
2322 .build()?;
2323 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2324
2325 Ok(())
2326 }
2327
2328 #[traced_test]
2329 #[test]
2330 fn test_list_issues_updated_on_filter_this_week() -> Result<(), Box<dyn Error>> {
2331 let _r_issues = ISSUES_LOCK.blocking_read();
2332 dotenvy::dotenv()?;
2333 let redmine = crate::api::Redmine::from_env(
2334 reqwest::blocking::Client::builder()
2335 .use_rustls_tls()
2336 .build()?,
2337 )?;
2338
2339 let endpoint = ListIssues::builder()
2340 .updated_on(DateTimeFilterPast::ThisWeek)
2341 .build()?;
2342 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2343
2344 Ok(())
2345 }
2346
2347 #[traced_test]
2348 #[test]
2349 fn test_list_issues_updated_on_filter_last_week() -> Result<(), Box<dyn Error>> {
2350 let _r_issues = ISSUES_LOCK.blocking_read();
2351 dotenvy::dotenv()?;
2352 let redmine = crate::api::Redmine::from_env(
2353 reqwest::blocking::Client::builder()
2354 .use_rustls_tls()
2355 .build()?,
2356 )?;
2357
2358 let endpoint = ListIssues::builder()
2359 .updated_on(DateTimeFilterPast::LastWeek)
2360 .build()?;
2361 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2362
2363 Ok(())
2364 }
2365
2366 #[traced_test]
2367 #[test]
2368 fn test_list_issues_updated_on_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
2369 let _r_issues = ISSUES_LOCK.blocking_read();
2370 dotenvy::dotenv()?;
2371 let redmine = crate::api::Redmine::from_env(
2372 reqwest::blocking::Client::builder()
2373 .use_rustls_tls()
2374 .build()?,
2375 )?;
2376
2377 let endpoint = ListIssues::builder()
2378 .updated_on(DateTimeFilterPast::LastTwoWeeks)
2379 .build()?;
2380 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2381
2382 Ok(())
2383 }
2384
2385 #[traced_test]
2386 #[test]
2387 fn test_list_issues_updated_on_filter_this_month() -> Result<(), Box<dyn Error>> {
2388 let _r_issues = ISSUES_LOCK.blocking_read();
2389 dotenvy::dotenv()?;
2390 let redmine = crate::api::Redmine::from_env(
2391 reqwest::blocking::Client::builder()
2392 .use_rustls_tls()
2393 .build()?,
2394 )?;
2395
2396 let endpoint = ListIssues::builder()
2397 .updated_on(DateTimeFilterPast::ThisMonth)
2398 .build()?;
2399 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2400
2401 Ok(())
2402 }
2403
2404 #[traced_test]
2405 #[test]
2406 fn test_list_issues_updated_on_filter_last_month() -> Result<(), Box<dyn Error>> {
2407 let _r_issues = ISSUES_LOCK.blocking_read();
2408 dotenvy::dotenv()?;
2409 let redmine = crate::api::Redmine::from_env(
2410 reqwest::blocking::Client::builder()
2411 .use_rustls_tls()
2412 .build()?,
2413 )?;
2414
2415 let endpoint = ListIssues::builder()
2416 .updated_on(DateTimeFilterPast::LastMonth)
2417 .build()?;
2418 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2419
2420 Ok(())
2421 }
2422
2423 #[traced_test]
2424 #[test]
2425 fn test_list_issues_updated_on_filter_this_year() -> Result<(), Box<dyn Error>> {
2426 let _r_issues = ISSUES_LOCK.blocking_read();
2427 dotenvy::dotenv()?;
2428 let redmine = crate::api::Redmine::from_env(
2429 reqwest::blocking::Client::builder()
2430 .use_rustls_tls()
2431 .build()?,
2432 )?;
2433
2434 let endpoint = ListIssues::builder()
2435 .updated_on(DateTimeFilterPast::ThisYear)
2436 .build()?;
2437 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2438
2439 Ok(())
2440 }
2441
2442 #[traced_test]
2443 #[test]
2444 fn test_list_issues_updated_on_filter_unset() -> Result<(), Box<dyn Error>> {
2445 let _r_issues = ISSUES_LOCK.blocking_read();
2446 dotenvy::dotenv()?;
2447 let redmine = crate::api::Redmine::from_env(
2448 reqwest::blocking::Client::builder()
2449 .use_rustls_tls()
2450 .build()?,
2451 )?;
2452
2453 let endpoint = ListIssues::builder()
2454 .updated_on(DateTimeFilterPast::Unset)
2455 .build()?;
2456 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2457
2458 Ok(())
2459 }
2460
2461 #[traced_test]
2462 #[test]
2463 fn test_list_issues_updated_on_filter_any() -> Result<(), Box<dyn Error>> {
2464 let _r_issues = ISSUES_LOCK.blocking_read();
2465 dotenvy::dotenv()?;
2466 let redmine = crate::api::Redmine::from_env(
2467 reqwest::blocking::Client::builder()
2468 .use_rustls_tls()
2469 .build()?,
2470 )?;
2471
2472 let endpoint = ListIssues::builder()
2473 .updated_on(DateTimeFilterPast::Any)
2474 .build()?;
2475 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2476
2477 Ok(())
2478 }
2479
2480 #[traced_test]
2481 #[test]
2482 fn test_list_issues_start_date_filter_exact_match() -> Result<(), Box<dyn Error>> {
2483 let _r_issues = ISSUES_LOCK.blocking_read();
2484 dotenvy::dotenv()?;
2485 let redmine = crate::api::Redmine::from_env(
2486 reqwest::blocking::Client::builder()
2487 .use_rustls_tls()
2488 .build()?,
2489 )?;
2490
2491 let dt = time::macros::date!(2023 - 01 - 15);
2492 let endpoint = ListIssues::builder()
2493 .start_date(DateFilter::ExactMatch(dt))
2494 .build()?;
2495 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2496
2497 Ok(())
2498 }
2499
2500 #[traced_test]
2501 #[test]
2502 fn test_list_issues_start_date_filter_range() -> Result<(), Box<dyn Error>> {
2503 let _r_issues = ISSUES_LOCK.blocking_read();
2504 dotenvy::dotenv()?;
2505 let redmine = crate::api::Redmine::from_env(
2506 reqwest::blocking::Client::builder()
2507 .use_rustls_tls()
2508 .build()?,
2509 )?;
2510
2511 let dt_start = time::macros::date!(2023 - 01 - 01);
2512 let dt_end = time::macros::date!(2023 - 01 - 31);
2513 let endpoint = ListIssues::builder()
2514 .start_date(DateFilter::Range(dt_start, dt_end))
2515 .build()?;
2516 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2517
2518 Ok(())
2519 }
2520
2521 #[traced_test]
2522 #[test]
2523 fn test_list_issues_start_date_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
2524 let _r_issues = ISSUES_LOCK.blocking_read();
2525 dotenvy::dotenv()?;
2526 let redmine = crate::api::Redmine::from_env(
2527 reqwest::blocking::Client::builder()
2528 .use_rustls_tls()
2529 .build()?,
2530 )?;
2531
2532 let dt = time::macros::date!(2023 - 01 - 15);
2533 let endpoint = ListIssues::builder()
2534 .start_date(DateFilter::LessThanOrEqual(dt))
2535 .build()?;
2536 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2537
2538 Ok(())
2539 }
2540
2541 #[traced_test]
2542 #[test]
2543 fn test_list_issues_start_date_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
2544 let _r_issues = ISSUES_LOCK.blocking_read();
2545 dotenvy::dotenv()?;
2546 let redmine = crate::api::Redmine::from_env(
2547 reqwest::blocking::Client::builder()
2548 .use_rustls_tls()
2549 .build()?,
2550 )?;
2551
2552 let dt = time::macros::date!(2023 - 01 - 15);
2553 let endpoint = ListIssues::builder()
2554 .start_date(DateFilter::GreaterThanOrEqual(dt))
2555 .build()?;
2556 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2557
2558 Ok(())
2559 }
2560
2561 #[traced_test]
2562 #[test]
2563 fn test_list_issues_start_date_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
2564 let _r_issues = ISSUES_LOCK.blocking_read();
2565 dotenvy::dotenv()?;
2566 let redmine = crate::api::Redmine::from_env(
2567 reqwest::blocking::Client::builder()
2568 .use_rustls_tls()
2569 .build()?,
2570 )?;
2571
2572 let endpoint = ListIssues::builder()
2573 .start_date(DateFilter::LessThanDaysAgo(5))
2574 .build()?;
2575 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2576
2577 Ok(())
2578 }
2579
2580 #[traced_test]
2581 #[test]
2582 fn test_list_issues_start_date_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
2583 let _r_issues = ISSUES_LOCK.blocking_read();
2584 dotenvy::dotenv()?;
2585 let redmine = crate::api::Redmine::from_env(
2586 reqwest::blocking::Client::builder()
2587 .use_rustls_tls()
2588 .build()?,
2589 )?;
2590
2591 let endpoint = ListIssues::builder()
2592 .start_date(DateFilter::MoreThanDaysAgo(10))
2593 .build()?;
2594 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2595
2596 Ok(())
2597 }
2598
2599 #[traced_test]
2600 #[test]
2601 fn test_list_issues_start_date_filter_within_past_days() -> Result<(), Box<dyn Error>> {
2602 let _r_issues = ISSUES_LOCK.blocking_read();
2603 dotenvy::dotenv()?;
2604 let redmine = crate::api::Redmine::from_env(
2605 reqwest::blocking::Client::builder()
2606 .use_rustls_tls()
2607 .build()?,
2608 )?;
2609
2610 let endpoint = ListIssues::builder()
2611 .start_date(DateFilter::WithinPastDays(7))
2612 .build()?;
2613 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2614
2615 Ok(())
2616 }
2617
2618 #[traced_test]
2619 #[test]
2620 fn test_list_issues_start_date_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
2621 let _r_issues = ISSUES_LOCK.blocking_read();
2622 dotenvy::dotenv()?;
2623 let redmine = crate::api::Redmine::from_env(
2624 reqwest::blocking::Client::builder()
2625 .use_rustls_tls()
2626 .build()?,
2627 )?;
2628
2629 let endpoint = ListIssues::builder()
2630 .start_date(DateFilter::ExactDaysAgo(3))
2631 .build()?;
2632 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2633
2634 Ok(())
2635 }
2636
2637 #[traced_test]
2638 #[test]
2639 fn test_list_issues_start_date_filter_in_less_than_days() -> Result<(), Box<dyn Error>> {
2640 let _r_issues = ISSUES_LOCK.blocking_read();
2641 dotenvy::dotenv()?;
2642 let redmine = crate::api::Redmine::from_env(
2643 reqwest::blocking::Client::builder()
2644 .use_rustls_tls()
2645 .build()?,
2646 )?;
2647
2648 let endpoint = ListIssues::builder()
2649 .start_date(DateFilter::InLessThanDays(5))
2650 .build()?;
2651 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2652
2653 Ok(())
2654 }
2655
2656 #[traced_test]
2657 #[test]
2658 fn test_list_issues_start_date_filter_in_more_than_days() -> Result<(), Box<dyn Error>> {
2659 let _r_issues = ISSUES_LOCK.blocking_read();
2660 dotenvy::dotenv()?;
2661 let redmine = crate::api::Redmine::from_env(
2662 reqwest::blocking::Client::builder()
2663 .use_rustls_tls()
2664 .build()?,
2665 )?;
2666
2667 let endpoint = ListIssues::builder()
2668 .start_date(DateFilter::InMoreThanDays(10))
2669 .build()?;
2670 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2671
2672 Ok(())
2673 }
2674
2675 #[traced_test]
2676 #[test]
2677 fn test_list_issues_start_date_filter_within_future_days() -> Result<(), Box<dyn Error>> {
2678 let _r_issues = ISSUES_LOCK.blocking_read();
2679 dotenvy::dotenv()?;
2680 let redmine = crate::api::Redmine::from_env(
2681 reqwest::blocking::Client::builder()
2682 .use_rustls_tls()
2683 .build()?,
2684 )?;
2685
2686 let endpoint = ListIssues::builder()
2687 .start_date(DateFilter::WithinFutureDays(7))
2688 .build()?;
2689 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2690
2691 Ok(())
2692 }
2693
2694 #[traced_test]
2695 #[test]
2696 fn test_list_issues_start_date_filter_in_exact_days() -> Result<(), Box<dyn Error>> {
2697 let _r_issues = ISSUES_LOCK.blocking_read();
2698 dotenvy::dotenv()?;
2699 let redmine = crate::api::Redmine::from_env(
2700 reqwest::blocking::Client::builder()
2701 .use_rustls_tls()
2702 .build()?,
2703 )?;
2704
2705 let endpoint = ListIssues::builder()
2706 .start_date(DateFilter::InExactDays(3))
2707 .build()?;
2708 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2709
2710 Ok(())
2711 }
2712
2713 #[traced_test]
2714 #[test]
2715 fn test_list_issues_start_date_filter_today() -> Result<(), Box<dyn Error>> {
2716 let _r_issues = ISSUES_LOCK.blocking_read();
2717 dotenvy::dotenv()?;
2718 let redmine = crate::api::Redmine::from_env(
2719 reqwest::blocking::Client::builder()
2720 .use_rustls_tls()
2721 .build()?,
2722 )?;
2723
2724 let endpoint = ListIssues::builder()
2725 .start_date(DateFilter::Today)
2726 .build()?;
2727 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2728
2729 Ok(())
2730 }
2731
2732 #[traced_test]
2733 #[test]
2734 fn test_list_issues_start_date_filter_yesterday() -> Result<(), Box<dyn Error>> {
2735 let _r_issues = ISSUES_LOCK.blocking_read();
2736 dotenvy::dotenv()?;
2737 let redmine = crate::api::Redmine::from_env(
2738 reqwest::blocking::Client::builder()
2739 .use_rustls_tls()
2740 .build()?,
2741 )?;
2742
2743 let endpoint = ListIssues::builder()
2744 .start_date(DateFilter::Yesterday)
2745 .build()?;
2746 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2747
2748 Ok(())
2749 }
2750
2751 #[traced_test]
2752 #[test]
2753 fn test_list_issues_start_date_filter_tomorrow() -> Result<(), Box<dyn Error>> {
2754 let _r_issues = ISSUES_LOCK.blocking_read();
2755 dotenvy::dotenv()?;
2756 let redmine = crate::api::Redmine::from_env(
2757 reqwest::blocking::Client::builder()
2758 .use_rustls_tls()
2759 .build()?,
2760 )?;
2761
2762 let endpoint = ListIssues::builder()
2763 .start_date(DateFilter::Tomorrow)
2764 .build()?;
2765 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2766
2767 Ok(())
2768 }
2769
2770 #[traced_test]
2771 #[test]
2772 fn test_list_issues_start_date_filter_this_week() -> Result<(), Box<dyn Error>> {
2773 let _r_issues = ISSUES_LOCK.blocking_read();
2774 dotenvy::dotenv()?;
2775 let redmine = crate::api::Redmine::from_env(
2776 reqwest::blocking::Client::builder()
2777 .use_rustls_tls()
2778 .build()?,
2779 )?;
2780
2781 let endpoint = ListIssues::builder()
2782 .start_date(DateFilter::ThisWeek)
2783 .build()?;
2784 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2785
2786 Ok(())
2787 }
2788
2789 #[traced_test]
2790 #[test]
2791 fn test_list_issues_start_date_filter_last_week() -> Result<(), Box<dyn Error>> {
2792 let _r_issues = ISSUES_LOCK.blocking_read();
2793 dotenvy::dotenv()?;
2794 let redmine = crate::api::Redmine::from_env(
2795 reqwest::blocking::Client::builder()
2796 .use_rustls_tls()
2797 .build()?,
2798 )?;
2799
2800 let endpoint = ListIssues::builder()
2801 .start_date(DateFilter::LastWeek)
2802 .build()?;
2803 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2804
2805 Ok(())
2806 }
2807
2808 #[traced_test]
2809 #[test]
2810 fn test_list_issues_start_date_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
2811 let _r_issues = ISSUES_LOCK.blocking_read();
2812 dotenvy::dotenv()?;
2813 let redmine = crate::api::Redmine::from_env(
2814 reqwest::blocking::Client::builder()
2815 .use_rustls_tls()
2816 .build()?,
2817 )?;
2818
2819 let endpoint = ListIssues::builder()
2820 .start_date(DateFilter::LastTwoWeeks)
2821 .build()?;
2822 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2823
2824 Ok(())
2825 }
2826
2827 #[traced_test]
2828 #[test]
2829 fn test_list_issues_start_date_filter_next_week() -> Result<(), Box<dyn Error>> {
2830 let _r_issues = ISSUES_LOCK.blocking_read();
2831 dotenvy::dotenv()?;
2832 let redmine = crate::api::Redmine::from_env(
2833 reqwest::blocking::Client::builder()
2834 .use_rustls_tls()
2835 .build()?,
2836 )?;
2837
2838 let endpoint = ListIssues::builder()
2839 .start_date(DateFilter::NextWeek)
2840 .build()?;
2841 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2842
2843 Ok(())
2844 }
2845
2846 #[traced_test]
2847 #[test]
2848 fn test_list_issues_start_date_filter_this_month() -> Result<(), Box<dyn Error>> {
2849 let _r_issues = ISSUES_LOCK.blocking_read();
2850 dotenvy::dotenv()?;
2851 let redmine = crate::api::Redmine::from_env(
2852 reqwest::blocking::Client::builder()
2853 .use_rustls_tls()
2854 .build()?,
2855 )?;
2856
2857 let endpoint = ListIssues::builder()
2858 .start_date(DateFilter::ThisMonth)
2859 .build()?;
2860 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2861
2862 Ok(())
2863 }
2864
2865 #[traced_test]
2866 #[test]
2867 fn test_list_issues_start_date_filter_last_month() -> Result<(), Box<dyn Error>> {
2868 let _r_issues = ISSUES_LOCK.blocking_read();
2869 dotenvy::dotenv()?;
2870 let redmine = crate::api::Redmine::from_env(
2871 reqwest::blocking::Client::builder()
2872 .use_rustls_tls()
2873 .build()?,
2874 )?;
2875
2876 let endpoint = ListIssues::builder()
2877 .start_date(DateFilter::LastMonth)
2878 .build()?;
2879 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2880
2881 Ok(())
2882 }
2883
2884 #[traced_test]
2885 #[test]
2886 fn test_list_issues_start_date_filter_next_month() -> Result<(), Box<dyn Error>> {
2887 let _r_issues = ISSUES_LOCK.blocking_read();
2888 dotenvy::dotenv()?;
2889 let redmine = crate::api::Redmine::from_env(
2890 reqwest::blocking::Client::builder()
2891 .use_rustls_tls()
2892 .build()?,
2893 )?;
2894
2895 let endpoint = ListIssues::builder()
2896 .start_date(DateFilter::NextMonth)
2897 .build()?;
2898 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2899
2900 Ok(())
2901 }
2902
2903 #[traced_test]
2904 #[test]
2905 fn test_list_issues_start_date_filter_this_year() -> Result<(), Box<dyn Error>> {
2906 let _r_issues = ISSUES_LOCK.blocking_read();
2907 dotenvy::dotenv()?;
2908 let redmine = crate::api::Redmine::from_env(
2909 reqwest::blocking::Client::builder()
2910 .use_rustls_tls()
2911 .build()?,
2912 )?;
2913
2914 let endpoint = ListIssues::builder()
2915 .start_date(DateFilter::ThisYear)
2916 .build()?;
2917 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2918
2919 Ok(())
2920 }
2921
2922 #[traced_test]
2923 #[test]
2924 fn test_list_issues_start_date_filter_unset() -> Result<(), Box<dyn Error>> {
2925 let _r_issues = ISSUES_LOCK.blocking_read();
2926 dotenvy::dotenv()?;
2927 let redmine = crate::api::Redmine::from_env(
2928 reqwest::blocking::Client::builder()
2929 .use_rustls_tls()
2930 .build()?,
2931 )?;
2932
2933 let endpoint = ListIssues::builder()
2934 .start_date(DateFilter::Unset)
2935 .build()?;
2936 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2937
2938 Ok(())
2939 }
2940
2941 #[traced_test]
2942 #[test]
2943 fn test_list_issues_start_date_filter_any() -> Result<(), Box<dyn Error>> {
2944 let _r_issues = ISSUES_LOCK.blocking_read();
2945 dotenvy::dotenv()?;
2946 let redmine = crate::api::Redmine::from_env(
2947 reqwest::blocking::Client::builder()
2948 .use_rustls_tls()
2949 .build()?,
2950 )?;
2951
2952 let endpoint = ListIssues::builder().start_date(DateFilter::Any).build()?;
2953 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2954
2955 Ok(())
2956 }
2957
2958 #[traced_test]
2959 #[test]
2960 fn test_list_issues_due_date_filter_exact_match() -> Result<(), Box<dyn Error>> {
2961 let _r_issues = ISSUES_LOCK.blocking_read();
2962 dotenvy::dotenv()?;
2963 let redmine = crate::api::Redmine::from_env(
2964 reqwest::blocking::Client::builder()
2965 .use_rustls_tls()
2966 .build()?,
2967 )?;
2968
2969 let dt = time::macros::date!(2023 - 01 - 15);
2970 let endpoint = ListIssues::builder()
2971 .due_date(DateFilter::ExactMatch(dt))
2972 .build()?;
2973 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2974
2975 Ok(())
2976 }
2977
2978 #[traced_test]
2979 #[test]
2980 fn test_list_issues_due_date_filter_range() -> Result<(), Box<dyn Error>> {
2981 let _r_issues = ISSUES_LOCK.blocking_read();
2982 dotenvy::dotenv()?;
2983 let redmine = crate::api::Redmine::from_env(
2984 reqwest::blocking::Client::builder()
2985 .use_rustls_tls()
2986 .build()?,
2987 )?;
2988
2989 let dt_start = time::macros::date!(2023 - 01 - 01);
2990 let dt_end = time::macros::date!(2023 - 01 - 31);
2991 let endpoint = ListIssues::builder()
2992 .due_date(DateFilter::Range(dt_start, dt_end))
2993 .build()?;
2994 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
2995
2996 Ok(())
2997 }
2998
2999 #[traced_test]
3000 #[test]
3001 fn test_list_issues_due_date_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
3002 let _r_issues = ISSUES_LOCK.blocking_read();
3003 dotenvy::dotenv()?;
3004 let redmine = crate::api::Redmine::from_env(
3005 reqwest::blocking::Client::builder()
3006 .use_rustls_tls()
3007 .build()?,
3008 )?;
3009
3010 let dt = time::macros::date!(2023 - 01 - 15);
3011 let endpoint = ListIssues::builder()
3012 .due_date(DateFilter::LessThanOrEqual(dt))
3013 .build()?;
3014 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3015
3016 Ok(())
3017 }
3018
3019 #[traced_test]
3020 #[test]
3021 fn test_list_issues_due_date_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
3022 let _r_issues = ISSUES_LOCK.blocking_read();
3023 dotenvy::dotenv()?;
3024 let redmine = crate::api::Redmine::from_env(
3025 reqwest::blocking::Client::builder()
3026 .use_rustls_tls()
3027 .build()?,
3028 )?;
3029
3030 let dt = time::macros::date!(2023 - 01 - 15);
3031 let endpoint = ListIssues::builder()
3032 .due_date(DateFilter::GreaterThanOrEqual(dt))
3033 .build()?;
3034 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3035
3036 Ok(())
3037 }
3038
3039 #[traced_test]
3040 #[test]
3041 fn test_list_issues_due_date_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
3042 let _r_issues = ISSUES_LOCK.blocking_read();
3043 dotenvy::dotenv()?;
3044 let redmine = crate::api::Redmine::from_env(
3045 reqwest::blocking::Client::builder()
3046 .use_rustls_tls()
3047 .build()?,
3048 )?;
3049
3050 let endpoint = ListIssues::builder()
3051 .due_date(DateFilter::LessThanDaysAgo(5))
3052 .build()?;
3053 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3054
3055 Ok(())
3056 }
3057
3058 #[traced_test]
3059 #[test]
3060 fn test_list_issues_due_date_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
3061 let _r_issues = ISSUES_LOCK.blocking_read();
3062 dotenvy::dotenv()?;
3063 let redmine = crate::api::Redmine::from_env(
3064 reqwest::blocking::Client::builder()
3065 .use_rustls_tls()
3066 .build()?,
3067 )?;
3068
3069 let endpoint = ListIssues::builder()
3070 .due_date(DateFilter::MoreThanDaysAgo(10))
3071 .build()?;
3072 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3073
3074 Ok(())
3075 }
3076
3077 #[traced_test]
3078 #[test]
3079 fn test_list_issues_due_date_filter_within_past_days() -> Result<(), Box<dyn Error>> {
3080 let _r_issues = ISSUES_LOCK.blocking_read();
3081 dotenvy::dotenv()?;
3082 let redmine = crate::api::Redmine::from_env(
3083 reqwest::blocking::Client::builder()
3084 .use_rustls_tls()
3085 .build()?,
3086 )?;
3087
3088 let endpoint = ListIssues::builder()
3089 .due_date(DateFilter::WithinPastDays(7))
3090 .build()?;
3091 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3092
3093 Ok(())
3094 }
3095
3096 #[traced_test]
3097 #[test]
3098 fn test_list_issues_due_date_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
3099 let _r_issues = ISSUES_LOCK.blocking_read();
3100 dotenvy::dotenv()?;
3101 let redmine = crate::api::Redmine::from_env(
3102 reqwest::blocking::Client::builder()
3103 .use_rustls_tls()
3104 .build()?,
3105 )?;
3106
3107 let endpoint = ListIssues::builder()
3108 .due_date(DateFilter::ExactDaysAgo(3))
3109 .build()?;
3110 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3111
3112 Ok(())
3113 }
3114
3115 #[traced_test]
3116 #[test]
3117 fn test_list_issues_due_date_filter_in_less_than_days() -> Result<(), Box<dyn Error>> {
3118 let _r_issues = ISSUES_LOCK.blocking_read();
3119 dotenvy::dotenv()?;
3120 let redmine = crate::api::Redmine::from_env(
3121 reqwest::blocking::Client::builder()
3122 .use_rustls_tls()
3123 .build()?,
3124 )?;
3125
3126 let endpoint = ListIssues::builder()
3127 .due_date(DateFilter::InLessThanDays(5))
3128 .build()?;
3129 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3130
3131 Ok(())
3132 }
3133
3134 #[traced_test]
3135 #[test]
3136 fn test_list_issues_due_date_filter_in_more_than_days() -> Result<(), Box<dyn Error>> {
3137 let _r_issues = ISSUES_LOCK.blocking_read();
3138 dotenvy::dotenv()?;
3139 let redmine = crate::api::Redmine::from_env(
3140 reqwest::blocking::Client::builder()
3141 .use_rustls_tls()
3142 .build()?,
3143 )?;
3144
3145 let endpoint = ListIssues::builder()
3146 .due_date(DateFilter::InMoreThanDays(10))
3147 .build()?;
3148 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3149
3150 Ok(())
3151 }
3152
3153 #[traced_test]
3154 #[test]
3155 fn test_list_issues_due_date_filter_within_future_days() -> Result<(), Box<dyn Error>> {
3156 let _r_issues = ISSUES_LOCK.blocking_read();
3157 dotenvy::dotenv()?;
3158 let redmine = crate::api::Redmine::from_env(
3159 reqwest::blocking::Client::builder()
3160 .use_rustls_tls()
3161 .build()?,
3162 )?;
3163
3164 let endpoint = ListIssues::builder()
3165 .due_date(DateFilter::WithinFutureDays(7))
3166 .build()?;
3167 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3168
3169 Ok(())
3170 }
3171
3172 #[traced_test]
3173 #[test]
3174 fn test_list_issues_due_date_filter_in_exact_days() -> Result<(), Box<dyn Error>> {
3175 let _r_issues = ISSUES_LOCK.blocking_read();
3176 dotenvy::dotenv()?;
3177 let redmine = crate::api::Redmine::from_env(
3178 reqwest::blocking::Client::builder()
3179 .use_rustls_tls()
3180 .build()?,
3181 )?;
3182
3183 let endpoint = ListIssues::builder()
3184 .due_date(DateFilter::InExactDays(3))
3185 .build()?;
3186 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3187
3188 Ok(())
3189 }
3190
3191 #[traced_test]
3192 #[test]
3193 fn test_list_issues_due_date_filter_today() -> Result<(), Box<dyn Error>> {
3194 let _r_issues = ISSUES_LOCK.blocking_read();
3195 dotenvy::dotenv()?;
3196 let redmine = crate::api::Redmine::from_env(
3197 reqwest::blocking::Client::builder()
3198 .use_rustls_tls()
3199 .build()?,
3200 )?;
3201
3202 let endpoint = ListIssues::builder().due_date(DateFilter::Today).build()?;
3203 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3204
3205 Ok(())
3206 }
3207
3208 #[traced_test]
3209 #[test]
3210 fn test_list_issues_due_date_filter_yesterday() -> Result<(), Box<dyn Error>> {
3211 let _r_issues = ISSUES_LOCK.blocking_read();
3212 dotenvy::dotenv()?;
3213 let redmine = crate::api::Redmine::from_env(
3214 reqwest::blocking::Client::builder()
3215 .use_rustls_tls()
3216 .build()?,
3217 )?;
3218
3219 let endpoint = ListIssues::builder()
3220 .due_date(DateFilter::Yesterday)
3221 .build()?;
3222 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3223
3224 Ok(())
3225 }
3226
3227 #[traced_test]
3228 #[test]
3229 fn test_list_issues_due_date_filter_tomorrow() -> Result<(), Box<dyn Error>> {
3230 let _r_issues = ISSUES_LOCK.blocking_read();
3231 dotenvy::dotenv()?;
3232 let redmine = crate::api::Redmine::from_env(
3233 reqwest::blocking::Client::builder()
3234 .use_rustls_tls()
3235 .build()?,
3236 )?;
3237
3238 let endpoint = ListIssues::builder()
3239 .due_date(DateFilter::Tomorrow)
3240 .build()?;
3241 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3242
3243 Ok(())
3244 }
3245
3246 #[traced_test]
3247 #[test]
3248 fn test_list_issues_due_date_filter_this_week() -> Result<(), Box<dyn Error>> {
3249 let _r_issues = ISSUES_LOCK.blocking_read();
3250 dotenvy::dotenv()?;
3251 let redmine = crate::api::Redmine::from_env(
3252 reqwest::blocking::Client::builder()
3253 .use_rustls_tls()
3254 .build()?,
3255 )?;
3256
3257 let endpoint = ListIssues::builder()
3258 .due_date(DateFilter::ThisWeek)
3259 .build()?;
3260 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3261
3262 Ok(())
3263 }
3264
3265 #[traced_test]
3266 #[test]
3267 fn test_list_issues_due_date_filter_last_week() -> Result<(), Box<dyn Error>> {
3268 let _r_issues = ISSUES_LOCK.blocking_read();
3269 dotenvy::dotenv()?;
3270 let redmine = crate::api::Redmine::from_env(
3271 reqwest::blocking::Client::builder()
3272 .use_rustls_tls()
3273 .build()?,
3274 )?;
3275
3276 let endpoint = ListIssues::builder()
3277 .due_date(DateFilter::LastWeek)
3278 .build()?;
3279 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3280
3281 Ok(())
3282 }
3283
3284 #[traced_test]
3285 #[test]
3286 fn test_list_issues_due_date_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
3287 let _r_issues = ISSUES_LOCK.blocking_read();
3288 dotenvy::dotenv()?;
3289 let redmine = crate::api::Redmine::from_env(
3290 reqwest::blocking::Client::builder()
3291 .use_rustls_tls()
3292 .build()?,
3293 )?;
3294
3295 let endpoint = ListIssues::builder()
3296 .due_date(DateFilter::LastTwoWeeks)
3297 .build()?;
3298 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3299
3300 Ok(())
3301 }
3302
3303 #[traced_test]
3304 #[test]
3305 fn test_list_issues_due_date_filter_next_week() -> Result<(), Box<dyn Error>> {
3306 let _r_issues = ISSUES_LOCK.blocking_read();
3307 dotenvy::dotenv()?;
3308 let redmine = crate::api::Redmine::from_env(
3309 reqwest::blocking::Client::builder()
3310 .use_rustls_tls()
3311 .build()?,
3312 )?;
3313
3314 let endpoint = ListIssues::builder()
3315 .due_date(DateFilter::NextWeek)
3316 .build()?;
3317 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3318
3319 Ok(())
3320 }
3321
3322 #[traced_test]
3323 #[test]
3324 fn test_list_issues_due_date_filter_this_month() -> Result<(), Box<dyn Error>> {
3325 let _r_issues = ISSUES_LOCK.blocking_read();
3326 dotenvy::dotenv()?;
3327 let redmine = crate::api::Redmine::from_env(
3328 reqwest::blocking::Client::builder()
3329 .use_rustls_tls()
3330 .build()?,
3331 )?;
3332
3333 let endpoint = ListIssues::builder()
3334 .due_date(DateFilter::ThisMonth)
3335 .build()?;
3336 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3337
3338 Ok(())
3339 }
3340
3341 #[traced_test]
3342 #[test]
3343 fn test_list_issues_due_date_filter_last_month() -> Result<(), Box<dyn Error>> {
3344 let _r_issues = ISSUES_LOCK.blocking_read();
3345 dotenvy::dotenv()?;
3346 let redmine = crate::api::Redmine::from_env(
3347 reqwest::blocking::Client::builder()
3348 .use_rustls_tls()
3349 .build()?,
3350 )?;
3351
3352 let endpoint = ListIssues::builder()
3353 .due_date(DateFilter::LastMonth)
3354 .build()?;
3355 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3356
3357 Ok(())
3358 }
3359
3360 #[traced_test]
3361 #[test]
3362 fn test_list_issues_due_date_filter_next_month() -> Result<(), Box<dyn Error>> {
3363 let _r_issues = ISSUES_LOCK.blocking_read();
3364 dotenvy::dotenv()?;
3365 let redmine = crate::api::Redmine::from_env(
3366 reqwest::blocking::Client::builder()
3367 .use_rustls_tls()
3368 .build()?,
3369 )?;
3370
3371 let endpoint = ListIssues::builder()
3372 .due_date(DateFilter::NextMonth)
3373 .build()?;
3374 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3375
3376 Ok(())
3377 }
3378
3379 #[traced_test]
3380 #[test]
3381 fn test_list_issues_due_date_filter_this_year() -> Result<(), Box<dyn Error>> {
3382 let _r_issues = ISSUES_LOCK.blocking_read();
3383 dotenvy::dotenv()?;
3384 let redmine = crate::api::Redmine::from_env(
3385 reqwest::blocking::Client::builder()
3386 .use_rustls_tls()
3387 .build()?,
3388 )?;
3389
3390 let endpoint = ListIssues::builder()
3391 .due_date(DateFilter::ThisYear)
3392 .build()?;
3393 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3394
3395 Ok(())
3396 }
3397
3398 #[traced_test]
3399 #[test]
3400 fn test_list_issues_due_date_filter_unset() -> Result<(), Box<dyn Error>> {
3401 let _r_issues = ISSUES_LOCK.blocking_read();
3402 dotenvy::dotenv()?;
3403 let redmine = crate::api::Redmine::from_env(
3404 reqwest::blocking::Client::builder()
3405 .use_rustls_tls()
3406 .build()?,
3407 )?;
3408
3409 let endpoint = ListIssues::builder().due_date(DateFilter::Unset).build()?;
3410 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3411
3412 Ok(())
3413 }
3414
3415 #[traced_test]
3416 #[test]
3417 fn test_list_issues_due_date_filter_any() -> Result<(), Box<dyn Error>> {
3418 let _r_issues = ISSUES_LOCK.blocking_read();
3419 dotenvy::dotenv()?;
3420 let redmine = crate::api::Redmine::from_env(
3421 reqwest::blocking::Client::builder()
3422 .use_rustls_tls()
3423 .build()?,
3424 )?;
3425
3426 let endpoint = ListIssues::builder().due_date(DateFilter::Any).build()?;
3427 redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
3428
3429 Ok(())
3430 }
3431}