use derive_builder::Builder;
use reqwest::Method;
use std::borrow::Cow;
use crate::api::attachments::Attachment;
use crate::api::custom_fields::{CustomField, CustomFieldEssentialsWithValue};
use crate::api::enumerations::IssuePriorityEssentials;
use crate::api::groups::{Group, GroupEssentials};
use crate::api::issue_categories::IssueCategoryEssentials;
use crate::api::issue_relations::IssueRelation;
use crate::api::issue_statuses::IssueStatusEssentials;
use crate::api::projects::ProjectEssentials;
use crate::api::projects::ProjectStatusFilter;
use crate::api::trackers::TrackerEssentials;
use crate::api::users::UserEssentials;
use crate::api::versions::VersionEssentials;
use crate::api::{
CustomFieldFilter, DateFilter, DateTimeFilterPast, Endpoint, FloatFilter, IntegerFilter,
NoPagination, Pageable, QueryParams, ReturnsJsonResponse, StringFieldFilter,
};
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct AssigneeEssentials {
pub id: u64,
pub name: String,
}
impl From<UserEssentials> for AssigneeEssentials {
fn from(v: UserEssentials) -> Self {
AssigneeEssentials {
id: v.id,
name: v.name,
}
}
}
impl From<&UserEssentials> for AssigneeEssentials {
fn from(v: &UserEssentials) -> Self {
AssigneeEssentials {
id: v.id,
name: v.name.to_owned(),
}
}
}
impl From<GroupEssentials> for AssigneeEssentials {
fn from(v: GroupEssentials) -> Self {
AssigneeEssentials {
id: v.id,
name: v.name,
}
}
}
impl From<&GroupEssentials> for AssigneeEssentials {
fn from(v: &GroupEssentials) -> Self {
AssigneeEssentials {
id: v.id,
name: v.name.to_owned(),
}
}
}
impl From<Group> for AssigneeEssentials {
fn from(v: Group) -> Self {
AssigneeEssentials {
id: v.id,
name: v.name,
}
}
}
impl From<&Group> for AssigneeEssentials {
fn from(v: &Group) -> Self {
AssigneeEssentials {
id: v.id,
name: v.name.to_owned(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct IssueEssentials {
pub id: u64,
}
impl From<Issue> for IssueEssentials {
fn from(v: Issue) -> Self {
IssueEssentials { id: v.id }
}
}
impl From<&Issue> for IssueEssentials {
fn from(v: &Issue) -> Self {
IssueEssentials { id: v.id }
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RepositoryEssentials {
pub id: u64,
pub identifier: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct IssueChangeset {
revision: String,
user: UserEssentials,
comments: String,
#[serde(
serialize_with = "crate::api::serialize_rfc3339",
deserialize_with = "crate::api::deserialize_rfc3339"
)]
committed_on: time::OffsetDateTime,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum ChangePropertyType {
#[serde(rename = "attr")]
Attr,
#[serde(rename = "cf")]
Cf,
#[serde(rename = "relation")]
Relation,
#[serde(rename = "attachment")]
Attachment,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct JournalChange {
pub name: String,
pub old_value: Option<String>,
pub new_value: Option<String>,
pub property: ChangePropertyType,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Journal {
pub id: u64,
pub user: UserEssentials,
pub notes: Option<String>,
pub private_notes: bool,
#[serde(
serialize_with = "crate::api::serialize_rfc3339",
deserialize_with = "crate::api::deserialize_rfc3339"
)]
pub created_on: time::OffsetDateTime,
#[serde(
serialize_with = "crate::api::serialize_rfc3339",
deserialize_with = "crate::api::deserialize_rfc3339"
)]
pub updated_on: time::OffsetDateTime,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub updated_by: Option<UserEssentials>,
pub details: Vec<JournalChange>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ChildIssue {
pub id: u64,
pub subject: String,
pub tracker: TrackerEssentials,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<ChildIssue>>,
}
#[derive(Debug, Clone, Serialize, serde::Deserialize)]
pub struct Issue {
pub id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<IssueEssentials>,
pub project: ProjectEssentials,
pub tracker: TrackerEssentials,
pub status: IssueStatusEssentials,
pub priority: IssuePriorityEssentials,
pub author: UserEssentials,
#[serde(skip_serializing_if = "Option::is_none")]
pub assigned_to: Option<AssigneeEssentials>,
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<IssueCategoryEssentials>,
#[serde(rename = "fixed_version", skip_serializing_if = "Option::is_none")]
pub version: Option<VersionEssentials>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
pub description: Option<String>,
is_private: Option<bool>,
pub start_date: Option<time::Date>,
pub due_date: Option<time::Date>,
#[serde(
serialize_with = "crate::api::serialize_optional_rfc3339",
deserialize_with = "crate::api::deserialize_optional_rfc3339"
)]
pub closed_on: Option<time::OffsetDateTime>,
pub done_ratio: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom_fields: Option<Vec<CustomFieldEssentialsWithValue>>,
pub estimated_hours: Option<f64>,
#[serde(
serialize_with = "crate::api::serialize_rfc3339",
deserialize_with = "crate::api::deserialize_rfc3339"
)]
pub created_on: time::OffsetDateTime,
#[serde(
serialize_with = "crate::api::serialize_rfc3339",
deserialize_with = "crate::api::deserialize_rfc3339"
)]
pub updated_on: time::OffsetDateTime,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub attachments: Option<Vec<Attachment>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub relations: Option<Vec<IssueRelation>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub changesets: Option<Vec<IssueChangeset>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub journals: Option<Vec<Journal>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<ChildIssue>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub watchers: Option<Vec<UserEssentials>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub spent_hours: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_spent_hours: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_estimated_hours: Option<f64>,
}
#[derive(Debug, Clone)]
pub enum SubProjectFilter {
OnlyParentProject,
TheseSubProjects(Vec<u64>),
NotTheseSubProjects(Vec<u64>),
}
impl std::fmt::Display for SubProjectFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SubProjectFilter::OnlyParentProject => {
write!(f, "!*")
}
SubProjectFilter::TheseSubProjects(ids) => {
let s: String = ids
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
SubProjectFilter::NotTheseSubProjects(ids) => {
let s: String = ids
.iter()
.map(|e| format!("!{e}"))
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
}
}
}
#[derive(Debug, Clone)]
pub enum IssueStatusFilter {
Open,
Closed,
All,
TheseStatuses(Vec<u64>),
NotTheseStatuses(Vec<u64>),
}
impl std::fmt::Display for IssueStatusFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IssueStatusFilter::Open => {
write!(f, "open")
}
IssueStatusFilter::Closed => {
write!(f, "closed")
}
IssueStatusFilter::All => {
write!(f, "*")
}
IssueStatusFilter::TheseStatuses(ids) => {
let s: String = ids
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
IssueStatusFilter::NotTheseStatuses(ids) => {
let s: String = ids
.iter()
.map(|e| format!("!{e}"))
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
}
}
}
#[derive(Debug, Clone)]
pub enum UserFilter {
AnyUser,
Me,
NotMe,
TheseUsers(Vec<u64>),
NotTheseUsers(Vec<u64>),
}
impl std::fmt::Display for UserFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UserFilter::AnyUser => {
write!(f, "*")
}
UserFilter::Me => {
write!(f, "me")
}
UserFilter::NotMe => {
write!(f, "!me")
}
UserFilter::TheseUsers(ids) => {
let s: String = ids
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
UserFilter::NotTheseUsers(ids) => {
let s: String = ids
.iter()
.map(|e| format!("!{e}"))
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum RoleFilter {
AnyRole,
TheseRoles(Vec<u64>),
NotTheseRoles(Vec<u64>),
}
impl std::fmt::Display for RoleFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RoleFilter::AnyRole => {
write!(f, "*")
}
RoleFilter::TheseRoles(ids) => {
let s: String = ids
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
RoleFilter::NotTheseRoles(ids) => {
let s: String = ids
.iter()
.map(|e| format!("!{e}"))
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
}
}
}
#[derive(Debug, Clone)]
pub enum MemberOfGroupFilter {
AnyGroup,
TheseGroups(Vec<u64>),
NotTheseGroups(Vec<u64>),
}
impl std::fmt::Display for MemberOfGroupFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MemberOfGroupFilter::AnyGroup => {
write!(f, "*")
}
MemberOfGroupFilter::TheseGroups(ids) => {
let s: String = ids
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
MemberOfGroupFilter::NotTheseGroups(ids) => {
let s: String = ids
.iter()
.map(|e| format!("!{e}"))
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
}
}
}
#[derive(Debug, Clone)]
pub enum FixedVersionStatusFilter {
Open,
Locked,
Closed,
}
impl std::fmt::Display for FixedVersionStatusFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FixedVersionStatusFilter::Open => {
write!(f, "open")
}
FixedVersionStatusFilter::Locked => {
write!(f, "locked")
}
FixedVersionStatusFilter::Closed => {
write!(f, "closed")
}
}
}
}
#[derive(Debug, Clone)]
pub enum AssigneeFilter {
AnyAssignee,
Me,
NotMe,
TheseAssignees(Vec<u64>),
NotTheseAssignees(Vec<u64>),
NoAssignee,
}
impl std::fmt::Display for AssigneeFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AssigneeFilter::AnyAssignee => {
write!(f, "*")
}
AssigneeFilter::Me => {
write!(f, "me")
}
AssigneeFilter::NotMe => {
write!(f, "!me")
}
AssigneeFilter::TheseAssignees(ids) => {
let s: String = ids
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
AssigneeFilter::NotTheseAssignees(ids) => {
let s: String = ids
.iter()
.map(|e| format!("!{e}"))
.collect::<Vec<_>>()
.join(",");
write!(f, "{s}")
}
AssigneeFilter::NoAssignee => {
write!(f, "!*")
}
}
}
}
#[derive(Debug, Clone)]
pub enum SortByColumn {
Forward {
column_name: String,
},
Reverse {
column_name: String,
},
}
impl std::fmt::Display for SortByColumn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SortByColumn::Forward { column_name } => {
write!(f, "{column_name}")
}
SortByColumn::Reverse { column_name } => {
write!(f, "{column_name}:desc")
}
}
}
}
#[derive(Debug, Clone)]
pub enum IssueListInclude {
Relations,
TimeEntries,
}
impl std::fmt::Display for IssueListInclude {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Relations => {
write!(f, "relations")
}
Self::TimeEntries => {
write!(f, "time_entries")
}
}
}
}
#[derive(Debug, Clone, Builder)]
#[builder(setter(strip_option))]
pub struct ListIssues {
#[builder(default)]
include: Option<Vec<IssueListInclude>>,
#[builder(default)]
sort: Option<Vec<SortByColumn>>,
#[builder(default)]
issue_id: Option<Vec<u64>>,
#[builder(default)]
project_id: Option<Vec<u64>>,
#[builder(default)]
subproject_id: Option<SubProjectFilter>,
#[builder(default)]
tracker_id: Option<Vec<u64>>,
#[builder(default)]
priority_id: Option<Vec<u64>>,
#[builder(default)]
parent_id: Option<Vec<u64>>,
#[builder(default)]
category_id: Option<Vec<u64>>,
#[builder(default)]
status_id: Option<IssueStatusFilter>,
#[builder(default)]
subject: Option<StringFieldFilter>,
#[builder(default)]
description: Option<StringFieldFilter>,
#[builder(default)]
notes: Option<StringFieldFilter>,
#[builder(default)]
watcher_id: Option<Vec<u64>>,
#[builder(default)]
author: Option<UserFilter>,
#[builder(default)]
updated_by: Option<UserFilter>,
#[builder(default)]
last_updated_by: Option<UserFilter>,
#[builder(default)]
attachment: Option<bool>,
#[builder(default)]
attachment_description: Option<StringFieldFilter>,
#[builder(default)]
project_status: Option<ProjectStatusFilter>,
#[builder(default)]
any_searchable: Option<StringFieldFilter>,
#[builder(default)]
custom_field_filters: Option<Vec<CustomFieldFilter>>,
#[builder(default)]
assignee: Option<AssigneeFilter>,
#[builder(default)]
query_id: Option<u64>,
#[builder(default)]
version_id: Option<Vec<u64>>,
#[builder(default)]
created_on: Option<DateTimeFilterPast>,
#[builder(default)]
updated_on: Option<DateTimeFilterPast>,
#[builder(default)]
start_date: Option<DateFilter>,
#[builder(default)]
due_date: Option<DateFilter>,
#[builder(default)]
spent_time: Option<FloatFilter>,
#[builder(default)]
closed_on: Option<DateTimeFilterPast>,
#[builder(default)]
estimated_hours: Option<FloatFilter>,
#[builder(default)]
done_ratio: Option<IntegerFilter>,
#[builder(default)]
author_group: Option<MemberOfGroupFilter>,
#[builder(default)]
author_role: Option<RoleFilter>,
#[builder(default)]
member_of_group: Option<MemberOfGroupFilter>,
#[builder(default)]
assigned_to_role: Option<RoleFilter>,
#[builder(default)]
fixed_version_due_date: Option<DateFilter>,
#[builder(default)]
fixed_version_status: Option<FixedVersionStatusFilter>,
#[builder(default)]
child_id: Option<Vec<u64>>,
#[builder(default)]
is_private: Option<bool>,
}
impl ReturnsJsonResponse for ListIssues {}
impl Pageable for ListIssues {
fn response_wrapper_key(&self) -> String {
"issues".to_string()
}
}
impl ListIssues {
#[must_use]
pub fn builder() -> ListIssuesBuilder {
ListIssuesBuilder::default()
}
}
impl Endpoint for ListIssues {
fn method(&self) -> Method {
Method::GET
}
fn endpoint(&self) -> Cow<'static, str> {
"issues.json".into()
}
fn parameters(&self) -> QueryParams<'_> {
let mut params = QueryParams::default();
params.push_opt("include", self.include.as_ref());
params.push_opt("sort", self.sort.as_ref());
params.push_opt("issue_id", self.issue_id.as_ref());
params.push_opt("project_id", self.project_id.as_ref());
params.push_opt(
"subproject_id",
self.subproject_id.as_ref().map(|s| s.to_string()),
);
params.push_opt("tracker_id", self.tracker_id.as_ref());
params.push_opt("priority_id", self.priority_id.as_ref());
params.push_opt("parent_id", self.parent_id.as_ref());
params.push_opt("category_id", self.category_id.as_ref());
params.push_opt("status_id", self.status_id.as_ref().map(|s| s.to_string()));
params.push_opt("subject", self.subject.as_ref().map(|s| s.to_string()));
params.push_opt(
"description",
self.description.as_ref().map(|s| s.to_string()),
);
params.push_opt("notes", self.notes.as_ref().map(|s| s.to_string()));
params.push_opt("author_id", self.author.as_ref().map(|s| s.to_string()));
params.push_opt(
"updated_by_id",
self.updated_by.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"last_updated_by_id",
self.last_updated_by.as_ref().map(|s| s.to_string()),
);
params.push_opt("attachment", self.attachment);
params.push_opt(
"attachment_description",
self.attachment_description.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"project_status",
self.project_status.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"any_searchable",
self.any_searchable.as_ref().map(|s| s.to_string()),
);
if let Some(filters) = self.custom_field_filters.as_ref() {
for filter in filters {
params.push(format!("cf_{}", filter.id), filter.value.to_string());
}
}
params.push_opt(
"assigned_to_id",
self.assignee.as_ref().map(|s| s.to_string()),
);
params.push_opt("query_id", self.query_id);
params.push_opt("fixed_version_id", self.version_id.as_ref());
params.push_opt(
"created_on",
self.created_on.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"updated_on",
self.updated_on.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"start_date",
self.start_date.as_ref().map(|s| s.to_string()),
);
params.push_opt("due_date", self.due_date.as_ref().map(|s| s.to_string()));
params.push_opt(
"spent_time",
self.spent_time.as_ref().map(|s| s.to_string()),
);
params.push_opt("closed_on", self.closed_on.as_ref().map(|s| s.to_string()));
params.push_opt(
"estimated_hours",
self.estimated_hours.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"done_ratio",
self.done_ratio.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"author.group",
self.author_group.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"author.role",
self.author_role.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"member_of_group",
self.member_of_group.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"assigned_to_role",
self.assigned_to_role.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"fixed_version.due_date",
self.fixed_version_due_date.as_ref().map(|s| s.to_string()),
);
params.push_opt(
"fixed_version.status",
self.fixed_version_status.as_ref().map(|s| s.to_string()),
);
params.push_opt("child_id", self.child_id.as_ref());
params.push_opt("is_private", self.is_private);
params.push_opt(
"watcher_id",
self.watcher_id.as_ref().map(|ids| {
ids.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<String>>()
.join(",")
}),
);
params
}
}
#[derive(Debug, Clone)]
pub enum IssueInclude {
Children,
Attachments,
Relations,
Changesets,
Journals,
Watchers,
AllowedStatuses,
}
impl std::fmt::Display for IssueInclude {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Children => {
write!(f, "children")
}
Self::Attachments => {
write!(f, "attachments")
}
Self::Relations => {
write!(f, "relations")
}
Self::Changesets => {
write!(f, "changesets")
}
Self::Journals => {
write!(f, "journals")
}
Self::Watchers => {
write!(f, "watchers")
}
Self::AllowedStatuses => {
write!(f, "allowed_statuses")
}
}
}
}
#[derive(Debug, Clone, Builder)]
#[builder(setter(strip_option))]
pub struct GetIssue {
id: u64,
#[builder(default)]
include: Option<Vec<IssueInclude>>,
}
impl ReturnsJsonResponse for GetIssue {}
impl NoPagination for GetIssue {}
impl GetIssue {
#[must_use]
pub fn builder() -> GetIssueBuilder {
GetIssueBuilder::default()
}
}
impl Endpoint for GetIssue {
fn method(&self) -> Method {
Method::GET
}
fn endpoint(&self) -> Cow<'static, str> {
format!("issues/{}.json", &self.id).into()
}
fn parameters(&self) -> QueryParams<'_> {
let mut params = QueryParams::default();
params.push_opt("include", self.include.as_ref());
params
}
}
#[derive(Debug, Clone, Serialize)]
pub struct UploadedAttachment<'a> {
pub token: Cow<'a, str>,
pub filename: Cow<'a, str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<Cow<'a, str>>,
pub content_type: Cow<'a, str>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Builder, Serialize)]
#[builder(setter(strip_option))]
pub struct CreateIssue<'a> {
project_id: u64,
#[builder(default)]
tracker_id: Option<u64>,
#[builder(default)]
status_id: Option<u64>,
#[builder(default)]
priority_id: Option<u64>,
#[builder(setter(into), default)]
subject: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
description: Option<Cow<'a, str>>,
#[builder(default)]
category_id: Option<u64>,
#[builder(default, setter(name = "version"))]
fixed_version_id: Option<u64>,
#[builder(default)]
assigned_to_id: Option<u64>,
#[builder(default)]
parent_issue_id: Option<u64>,
#[builder(default)]
custom_fields: Option<Vec<CustomField<'a>>>,
#[builder(default)]
watcher_user_ids: Option<Vec<u64>>,
#[builder(default)]
is_private: Option<bool>,
#[builder(default)]
estimated_hours: Option<f64>,
#[builder(default)]
uploads: Option<Vec<UploadedAttachment<'a>>>,
}
impl<'a> CreateIssue<'a> {
#[must_use]
pub fn builder() -> CreateIssueBuilder<'a> {
CreateIssueBuilder::default()
}
}
impl ReturnsJsonResponse for CreateIssue<'_> {}
impl NoPagination for CreateIssue<'_> {}
impl Endpoint for CreateIssue<'_> {
fn method(&self) -> Method {
Method::POST
}
fn endpoint(&self) -> Cow<'static, str> {
"issues.json".into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, crate::Error> {
Ok(Some((
"application/json",
serde_json::to_vec(&IssueWrapper::<CreateIssue> {
issue: (*self).to_owned(),
})?,
)))
}
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Builder, Serialize)]
#[builder(setter(strip_option))]
pub struct UpdateIssue<'a> {
#[serde(skip_serializing)]
id: u64,
#[builder(default)]
project_id: Option<u64>,
#[builder(default)]
tracker_id: Option<u64>,
#[builder(default)]
status_id: Option<u64>,
#[builder(default)]
priority_id: Option<u64>,
#[builder(setter(into), default)]
subject: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
description: Option<Cow<'a, str>>,
#[builder(default)]
category_id: Option<u64>,
#[builder(default, setter(name = "version"))]
fixed_version_id: Option<u64>,
#[builder(default)]
assigned_to_id: Option<u64>,
#[builder(default)]
parent_issue_id: Option<u64>,
#[builder(default)]
custom_fields: Option<Vec<CustomField<'a>>>,
#[builder(default)]
watcher_user_ids: Option<Vec<u64>>,
#[builder(default)]
is_private: Option<bool>,
#[builder(default)]
estimated_hours: Option<f64>,
#[builder(default)]
notes: Option<Cow<'a, str>>,
#[builder(default)]
private_notes: Option<bool>,
#[builder(default)]
uploads: Option<Vec<UploadedAttachment<'a>>>,
}
impl<'a> UpdateIssue<'a> {
#[must_use]
pub fn builder() -> UpdateIssueBuilder<'a> {
UpdateIssueBuilder::default()
}
}
impl Endpoint for UpdateIssue<'_> {
fn method(&self) -> Method {
Method::PUT
}
fn endpoint(&self) -> Cow<'static, str> {
format!("issues/{}.json", self.id).into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, crate::Error> {
Ok(Some((
"application/json",
serde_json::to_vec(&IssueWrapper::<UpdateIssue> {
issue: (*self).to_owned(),
})?,
)))
}
}
#[derive(Debug, Clone, Builder)]
#[builder(setter(strip_option))]
pub struct DeleteIssue {
id: u64,
}
impl DeleteIssue {
#[must_use]
pub fn builder() -> DeleteIssueBuilder {
DeleteIssueBuilder::default()
}
}
impl Endpoint for DeleteIssue {
fn method(&self) -> Method {
Method::DELETE
}
fn endpoint(&self) -> Cow<'static, str> {
format!("issues/{}.json", &self.id).into()
}
}
#[derive(Debug, Clone, Builder, Serialize)]
#[builder(setter(strip_option))]
pub struct AddWatcher {
#[serde(skip_serializing)]
issue_id: u64,
user_id: u64,
}
impl AddWatcher {
#[must_use]
pub fn builder() -> AddWatcherBuilder {
AddWatcherBuilder::default()
}
}
impl Endpoint for AddWatcher {
fn method(&self) -> Method {
Method::POST
}
fn endpoint(&self) -> Cow<'static, str> {
format!("issues/{}/watchers.json", &self.issue_id).into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, crate::Error> {
Ok(Some(("application/json", serde_json::to_vec(self)?)))
}
}
#[derive(Debug, Clone, Builder)]
#[builder(setter(strip_option))]
pub struct RemoveWatcher {
issue_id: u64,
user_id: u64,
}
impl RemoveWatcher {
#[must_use]
pub fn builder() -> RemoveWatcherBuilder {
RemoveWatcherBuilder::default()
}
}
impl Endpoint for RemoveWatcher {
fn method(&self) -> Method {
Method::DELETE
}
fn endpoint(&self) -> Cow<'static, str> {
format!("issues/{}/watchers/{}.json", &self.issue_id, &self.user_id).into()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, serde::Deserialize)]
pub struct IssuesWrapper<T> {
pub issues: Vec<T>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, serde::Deserialize)]
pub struct IssueWrapper<T> {
pub issue: T,
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
use crate::api::ResponsePage;
use crate::api::test_helpers::with_project;
use pretty_assertions::assert_eq;
use std::error::Error;
use tokio::sync::RwLock;
use tracing_test::traced_test;
pub static ISSUES_LOCK: RwLock<()> = RwLock::const_new(());
#[traced_test]
#[test]
fn test_list_issues_first_page() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
#[ignore]
fn test_list_issues_all_pages() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().build()?;
redmine.json_response_body_all_pages::<_, Issue>(&endpoint)?;
Ok(())
}
#[traced_test]
#[test]
#[ignore]
fn test_list_issues_all_pages_iter() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().build()?;
let mut i = 0;
for issue in redmine.json_response_body_all_pages_iter::<_, Issue>(&endpoint) {
let _issue = issue?;
i += 1;
}
assert!(i > 0);
Ok(())
}
#[traced_test]
#[tokio::test]
#[ignore]
async fn test_list_issues_all_pages_stream() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.read().await;
dotenvy::dotenv()?;
let redmine = crate::api::RedmineAsync::from_env(
reqwest::Client::builder().tls_backend_rustls().build()?,
)?;
let endpoint = ListIssues::builder().build()?;
let mut i = 0;
let mut stream = redmine.json_response_body_all_pages_stream::<_, Issue>(&endpoint);
while let Some(issue) = <_ as futures::stream::StreamExt>::next(&mut stream).await {
let _issue = issue?;
i += 1;
}
assert!(i > 0);
Ok(())
}
#[traced_test]
#[test]
fn test_get_issue() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = GetIssue::builder().id(40000).build()?;
redmine.json_response_body::<_, IssueWrapper<Issue>>(&endpoint)?;
Ok(())
}
#[function_name::named]
#[traced_test]
#[test]
fn test_create_issue() -> Result<(), Box<dyn Error>> {
let _w_issues = ISSUES_LOCK.blocking_write();
let name = format!("unittest_{}", function_name!());
with_project(&name, |redmine, project_id, _| {
let create_endpoint = super::CreateIssue::builder()
.project_id(project_id)
.subject("old test subject")
.build()?;
redmine.json_response_body::<_, IssueWrapper<Issue>>(&create_endpoint)?;
Ok(())
})?;
Ok(())
}
#[function_name::named]
#[traced_test]
#[test]
fn test_update_issue() -> Result<(), Box<dyn Error>> {
let _w_issues = ISSUES_LOCK.blocking_write();
let name = format!("unittest_{}", function_name!());
with_project(&name, |redmine, project_id, _name| {
let create_endpoint = super::CreateIssue::builder()
.project_id(project_id)
.subject("old test subject")
.build()?;
let IssueWrapper { issue }: IssueWrapper<Issue> =
redmine.json_response_body::<_, _>(&create_endpoint)?;
let update_endpoint = super::UpdateIssue::builder()
.id(issue.id)
.subject("New test subject")
.build()?;
redmine.ignore_response_body::<_>(&update_endpoint)?;
Ok(())
})?;
Ok(())
}
#[function_name::named]
#[traced_test]
#[test]
fn test_delete_issue() -> Result<(), Box<dyn Error>> {
let _w_issues = ISSUES_LOCK.blocking_write();
let name = format!("unittest_{}", function_name!());
with_project(&name, |redmine, project_id, _name| {
let create_endpoint = super::CreateIssue::builder()
.project_id(project_id)
.subject("test subject")
.build()?;
let IssueWrapper { issue }: IssueWrapper<Issue> =
redmine.json_response_body::<_, _>(&create_endpoint)?;
let delete_endpoint = super::DeleteIssue::builder().id(issue.id).build()?;
redmine.ignore_response_body::<_>(&delete_endpoint)?;
Ok(())
})?;
Ok(())
}
#[traced_test]
#[test]
fn test_completeness_issue_type_first_page() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.include(vec![
IssueListInclude::TimeEntries,
IssueListInclude::Relations,
])
.build()?;
let ResponsePage {
values,
total_count: _,
offset: _,
limit: _,
} = redmine.json_response_body_page::<_, serde_json::Value>(&endpoint, 0, 100)?;
for value in values {
let o: Issue = serde_json::from_value(value.clone())?;
let reserialized = serde_json::to_value(o)?;
let expected_value = if let serde_json::Value::Object(obj) = value {
let mut expected_obj = obj.clone();
if obj
.get("total_estimated_hours")
.is_some_and(|v| *v == serde_json::Value::Null)
{
expected_obj.remove("total_estimated_hours");
}
serde_json::Value::Object(expected_obj)
} else {
value
};
assert_eq!(expected_value, reserialized);
}
Ok(())
}
#[traced_test]
#[test]
#[ignore]
fn test_completeness_issue_type_all_pages() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.include(vec![
IssueListInclude::TimeEntries,
IssueListInclude::Relations,
])
.build()?;
let values = redmine.json_response_body_all_pages::<_, serde_json::Value>(&endpoint)?;
for value in values {
let o: Issue = serde_json::from_value(value.clone())?;
let reserialized = serde_json::to_value(o)?;
let expected_value = if let serde_json::Value::Object(obj) = value {
let mut expected_obj = obj.clone();
if obj
.get("total_estimated_hours")
.is_some_and(|v| *v == serde_json::Value::Null)
{
expected_obj.remove("total_estimated_hours");
}
serde_json::Value::Object(expected_obj)
} else {
value
};
assert_eq!(expected_value, reserialized);
}
Ok(())
}
#[traced_test]
#[test]
#[ignore]
fn test_completeness_issue_type_all_pages_all_issue_details() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.include(vec![
IssueListInclude::TimeEntries,
IssueListInclude::Relations,
])
.build()?;
let issues = redmine.json_response_body_all_pages::<_, Issue>(&endpoint)?;
for issue in issues {
let get_endpoint = GetIssue::builder()
.id(issue.id)
.include(vec![
IssueInclude::Attachments,
IssueInclude::Children,
IssueInclude::Changesets,
IssueInclude::Relations,
IssueInclude::Journals,
IssueInclude::Watchers,
])
.build()?;
let IssueWrapper { issue: mut value } =
redmine.json_response_body::<_, IssueWrapper<serde_json::Value>>(&get_endpoint)?;
let o: Issue = serde_json::from_value(value.clone())?;
let value_object = value.as_object_mut().unwrap();
if value_object.get("total_estimated_hours") == Some(&serde_json::Value::Null) {
value_object.remove("total_estimated_hours");
}
let reserialized = serde_json::to_value(o)?;
assert_eq!(value, reserialized);
}
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_exact_match() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::ExactMatch(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_range() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt_start = time::macros::datetime!(2023-01-01 00:00:00 UTC);
let dt_end = time::macros::datetime!(2023-01-31 23:59:59 UTC);
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::Range(dt_start, dt_end))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::LessThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::GreaterThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::LessThanDaysAgo(5))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::MoreThanDaysAgo(10))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_within_past_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::WithinPastDays(7))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::ExactDaysAgo(3))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_today() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::Today)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_yesterday() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::Yesterday)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_this_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::ThisWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_last_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::LastWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::LastTwoWeeks)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_this_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::ThisMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_last_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::LastMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_this_year() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::ThisYear)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_unset() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::Unset)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_created_on_filter_any() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.created_on(DateTimeFilterPast::Any)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_exact_match() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::ExactMatch(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_range() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt_start = time::macros::datetime!(2023-01-01 00:00:00 UTC);
let dt_end = time::macros::datetime!(2023-01-31 23:59:59 UTC);
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::Range(dt_start, dt_end))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::LessThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::datetime!(2023-01-15 10:30:00 UTC);
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::GreaterThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::LessThanDaysAgo(5))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::MoreThanDaysAgo(10))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_within_past_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::WithinPastDays(7))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::ExactDaysAgo(3))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_today() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::Today)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_yesterday() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::Yesterday)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_this_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::ThisWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_last_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::LastWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::LastTwoWeeks)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_this_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::ThisMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_last_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::LastMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_this_year() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::ThisYear)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_unset() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::Unset)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_updated_on_filter_any() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.updated_on(DateTimeFilterPast::Any)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_exact_match() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::date!(2023 - 01 - 15);
let endpoint = ListIssues::builder()
.start_date(DateFilter::ExactMatch(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_range() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt_start = time::macros::date!(2023 - 01 - 01);
let dt_end = time::macros::date!(2023 - 01 - 31);
let endpoint = ListIssues::builder()
.start_date(DateFilter::Range(dt_start, dt_end))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::date!(2023 - 01 - 15);
let endpoint = ListIssues::builder()
.start_date(DateFilter::LessThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::date!(2023 - 01 - 15);
let endpoint = ListIssues::builder()
.start_date(DateFilter::GreaterThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::LessThanDaysAgo(5))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::MoreThanDaysAgo(10))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_within_past_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::WithinPastDays(7))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::ExactDaysAgo(3))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_in_less_than_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::InLessThanDays(5))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_in_more_than_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::InMoreThanDays(10))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_within_future_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::WithinFutureDays(7))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_in_exact_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::InExactDays(3))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_today() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::Today)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_yesterday() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::Yesterday)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_tomorrow() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::Tomorrow)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_this_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::ThisWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_last_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::LastWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::LastTwoWeeks)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_next_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::NextWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_this_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::ThisMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_last_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::LastMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_next_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::NextMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_this_year() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::ThisYear)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_unset() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.start_date(DateFilter::Unset)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_start_date_filter_any() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().start_date(DateFilter::Any).build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_exact_match() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::date!(2023 - 01 - 15);
let endpoint = ListIssues::builder()
.due_date(DateFilter::ExactMatch(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_range() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt_start = time::macros::date!(2023 - 01 - 01);
let dt_end = time::macros::date!(2023 - 01 - 31);
let endpoint = ListIssues::builder()
.due_date(DateFilter::Range(dt_start, dt_end))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_less_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::date!(2023 - 01 - 15);
let endpoint = ListIssues::builder()
.due_date(DateFilter::LessThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_greater_than_or_equal() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let dt = time::macros::date!(2023 - 01 - 15);
let endpoint = ListIssues::builder()
.due_date(DateFilter::GreaterThanOrEqual(dt))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_less_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::LessThanDaysAgo(5))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_more_than_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::MoreThanDaysAgo(10))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_within_past_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::WithinPastDays(7))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_exact_days_ago() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::ExactDaysAgo(3))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_in_less_than_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::InLessThanDays(5))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_in_more_than_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::InMoreThanDays(10))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_within_future_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::WithinFutureDays(7))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_in_exact_days() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::InExactDays(3))
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_today() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().due_date(DateFilter::Today).build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_yesterday() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::Yesterday)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_tomorrow() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::Tomorrow)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_this_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::ThisWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_last_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::LastWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_last_two_weeks() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::LastTwoWeeks)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_next_week() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::NextWeek)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_this_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::ThisMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_last_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::LastMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_next_month() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::NextMonth)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_this_year() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder()
.due_date(DateFilter::ThisYear)
.build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_unset() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().due_date(DateFilter::Unset).build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_due_date_filter_any() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().due_date(DateFilter::Any).build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
#[traced_test]
#[test]
fn test_list_issues_child_id_filter() -> Result<(), Box<dyn Error>> {
let _r_issues = ISSUES_LOCK.blocking_read();
dotenvy::dotenv()?;
let redmine = crate::api::Redmine::from_env(
reqwest::blocking::Client::builder()
.tls_backend_rustls()
.build()?,
)?;
let endpoint = ListIssues::builder().child_id(vec![123, 456]).build()?;
redmine.json_response_body_page::<_, Issue>(&endpoint, 0, 25)?;
Ok(())
}
}