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