Skip to content

Commit f8eb286

Browse files
authored
Merge pull request #75 from 23prime/feature/43-priority-resolution-list
feat: bl priority list + bl resolution list
2 parents e498fa5 + 95d1283 commit f8eb286

11 files changed

Lines changed: 428 additions & 4 deletions

File tree

src/api/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ pub mod disk_usage;
1111
pub mod issue;
1212
pub mod licence;
1313
pub mod notification;
14+
pub mod priority;
1415
pub mod project;
16+
pub mod resolution;
1517
pub mod space;
1618
pub mod space_notification;
1719
pub mod team;
@@ -27,10 +29,12 @@ use issue::{
2729
};
2830
use licence::Licence;
2931
use notification::{Notification, NotificationCount};
32+
use priority::Priority;
3033
use project::{
3134
Project, ProjectCategory, ProjectDiskUsage, ProjectIssueType, ProjectStatus, ProjectUser,
3235
ProjectVersion,
3336
};
37+
use resolution::Resolution;
3438
use space::Space;
3539
use space_notification::SpaceNotification;
3640
use team::Team;
@@ -289,6 +293,12 @@ pub trait BacklogApi {
289293
fn put_space_notification(&self, _content: &str) -> Result<SpaceNotification> {
290294
unimplemented!()
291295
}
296+
fn get_priorities(&self) -> Result<Vec<Priority>> {
297+
unimplemented!()
298+
}
299+
fn get_resolutions(&self) -> Result<Vec<Resolution>> {
300+
unimplemented!()
301+
}
292302
fn get_watchings(&self, _user_id: u64, _params: &[(String, String)]) -> Result<Vec<Watching>> {
293303
unimplemented!()
294304
}
@@ -611,6 +621,12 @@ impl BacklogApi for BacklogClient {
611621
fn put_space_notification(&self, content: &str) -> Result<SpaceNotification> {
612622
self.put_space_notification(content)
613623
}
624+
fn get_priorities(&self) -> Result<Vec<Priority>> {
625+
self.get_priorities()
626+
}
627+
fn get_resolutions(&self) -> Result<Vec<Resolution>> {
628+
self.get_resolutions()
629+
}
614630
fn get_watchings(&self, user_id: u64, params: &[(String, String)]) -> Result<Vec<Watching>> {
615631
self.get_watchings(user_id, params)
616632
}

src/api/priority.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use anyhow::Result;
2+
use serde::{Deserialize, Serialize};
3+
4+
use super::BacklogClient;
5+
6+
#[derive(Debug, Clone, Serialize, Deserialize)]
7+
pub struct Priority {
8+
pub id: u64,
9+
pub name: String,
10+
}
11+
12+
impl BacklogClient {
13+
pub fn get_priorities(&self) -> Result<Vec<Priority>> {
14+
let value = self.get_with_query("/priorities", &[])?;
15+
serde_json::from_value(value.clone()).map_err(|e| {
16+
anyhow::anyhow!(
17+
"Failed to deserialize response: {}\nRaw JSON:\n{}",
18+
e,
19+
serde_json::to_string_pretty(&value).unwrap_or_else(|_| value.to_string())
20+
)
21+
})
22+
}
23+
}
24+
25+
#[cfg(test)]
26+
mod tests {
27+
use super::*;
28+
use httpmock::prelude::*;
29+
30+
#[test]
31+
fn get_priorities_parses_response() {
32+
let server = MockServer::start();
33+
server.mock(|when, then| {
34+
when.method(GET).path("/priorities");
35+
then.status(200)
36+
.header("content-type", "application/json")
37+
.json_body(serde_json::json!([
38+
{"id": 2, "name": "High"},
39+
{"id": 3, "name": "Normal"},
40+
{"id": 4, "name": "Low"}
41+
]));
42+
});
43+
let client = BacklogClient::new_with(&server.base_url(), "test-key").unwrap();
44+
let result = client.get_priorities().unwrap();
45+
assert_eq!(result.len(), 3);
46+
assert_eq!(result[0].id, 2);
47+
assert_eq!(result[0].name, "High");
48+
}
49+
}

src/api/resolution.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use anyhow::Result;
2+
use serde::{Deserialize, Serialize};
3+
4+
use super::BacklogClient;
5+
6+
#[derive(Debug, Clone, Serialize, Deserialize)]
7+
pub struct Resolution {
8+
pub id: u64,
9+
pub name: String,
10+
}
11+
12+
impl BacklogClient {
13+
pub fn get_resolutions(&self) -> Result<Vec<Resolution>> {
14+
let value = self.get_with_query("/resolutions", &[])?;
15+
serde_json::from_value(value.clone()).map_err(|e| {
16+
anyhow::anyhow!(
17+
"Failed to deserialize response: {}\nRaw JSON:\n{}",
18+
e,
19+
serde_json::to_string_pretty(&value).unwrap_or_else(|_| value.to_string())
20+
)
21+
})
22+
}
23+
}
24+
25+
#[cfg(test)]
26+
mod tests {
27+
use super::*;
28+
use httpmock::prelude::*;
29+
30+
#[test]
31+
fn get_resolutions_parses_response() {
32+
let server = MockServer::start();
33+
server.mock(|when, then| {
34+
when.method(GET).path("/resolutions");
35+
then.status(200)
36+
.header("content-type", "application/json")
37+
.json_body(serde_json::json!([
38+
{"id": 0, "name": "Fixed"},
39+
{"id": 1, "name": "Won't Fix"},
40+
{"id": 2, "name": "Invalid"}
41+
]));
42+
});
43+
let client = BacklogClient::new_with(&server.base_url(), "test-key").unwrap();
44+
let result = client.get_resolutions().unwrap();
45+
assert_eq!(result.len(), 3);
46+
assert_eq!(result[0].id, 0);
47+
assert_eq!(result[0].name, "Fixed");
48+
}
49+
}

src/cmd/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ pub mod auth;
22
pub mod banner;
33
pub mod issue;
44
pub mod notification;
5+
pub mod priority;
56
pub mod project;
7+
pub mod resolution;
68
pub mod space;
79
pub mod team;
810
pub mod user;

src/cmd/priority/list.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use anstream::println;
2+
use anyhow::{Context, Result};
3+
4+
use crate::api::{BacklogApi, BacklogClient};
5+
6+
pub struct PriorityListArgs {
7+
json: bool,
8+
}
9+
10+
impl PriorityListArgs {
11+
pub fn new(json: bool) -> Self {
12+
Self { json }
13+
}
14+
}
15+
16+
pub fn list(args: &PriorityListArgs) -> Result<()> {
17+
let client = BacklogClient::from_config()?;
18+
list_with(args, &client)
19+
}
20+
21+
pub fn list_with(args: &PriorityListArgs, api: &dyn BacklogApi) -> Result<()> {
22+
let priorities = api.get_priorities()?;
23+
if args.json {
24+
println!(
25+
"{}",
26+
serde_json::to_string_pretty(&priorities).context("Failed to serialize JSON")?
27+
);
28+
} else {
29+
for p in &priorities {
30+
println!("[{}] {}", p.id, p.name);
31+
}
32+
}
33+
Ok(())
34+
}
35+
36+
#[cfg(test)]
37+
mod tests {
38+
use super::*;
39+
use crate::api::priority::Priority;
40+
use anyhow::anyhow;
41+
42+
struct MockApi {
43+
priorities: Option<Vec<Priority>>,
44+
}
45+
46+
impl crate::api::BacklogApi for MockApi {
47+
fn get_priorities(&self) -> anyhow::Result<Vec<Priority>> {
48+
self.priorities
49+
.clone()
50+
.ok_or_else(|| anyhow!("no priorities"))
51+
}
52+
}
53+
54+
fn sample_priorities() -> Vec<Priority> {
55+
vec![
56+
Priority {
57+
id: 2,
58+
name: "High".to_string(),
59+
},
60+
Priority {
61+
id: 3,
62+
name: "Normal".to_string(),
63+
},
64+
Priority {
65+
id: 4,
66+
name: "Low".to_string(),
67+
},
68+
]
69+
}
70+
71+
fn args(json: bool) -> PriorityListArgs {
72+
PriorityListArgs::new(json)
73+
}
74+
75+
#[test]
76+
fn list_with_text_output_succeeds() {
77+
let api = MockApi {
78+
priorities: Some(sample_priorities()),
79+
};
80+
assert!(list_with(&args(false), &api).is_ok());
81+
}
82+
83+
#[test]
84+
fn list_with_json_output_succeeds() {
85+
let api = MockApi {
86+
priorities: Some(sample_priorities()),
87+
};
88+
assert!(list_with(&args(true), &api).is_ok());
89+
}
90+
91+
#[test]
92+
fn list_with_propagates_api_error() {
93+
let api = MockApi { priorities: None };
94+
let err = list_with(&args(false), &api).unwrap_err();
95+
assert!(err.to_string().contains("no priorities"));
96+
}
97+
}

src/cmd/priority/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod list;
2+
3+
pub use list::{PriorityListArgs, list};

src/cmd/resolution/list.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use anstream::println;
2+
use anyhow::{Context, Result};
3+
4+
use crate::api::{BacklogApi, BacklogClient};
5+
6+
pub struct ResolutionListArgs {
7+
json: bool,
8+
}
9+
10+
impl ResolutionListArgs {
11+
pub fn new(json: bool) -> Self {
12+
Self { json }
13+
}
14+
}
15+
16+
pub fn list(args: &ResolutionListArgs) -> Result<()> {
17+
let client = BacklogClient::from_config()?;
18+
list_with(args, &client)
19+
}
20+
21+
pub fn list_with(args: &ResolutionListArgs, api: &dyn BacklogApi) -> Result<()> {
22+
let resolutions = api.get_resolutions()?;
23+
if args.json {
24+
println!(
25+
"{}",
26+
serde_json::to_string_pretty(&resolutions).context("Failed to serialize JSON")?
27+
);
28+
} else {
29+
for r in &resolutions {
30+
println!("[{}] {}", r.id, r.name);
31+
}
32+
}
33+
Ok(())
34+
}
35+
36+
#[cfg(test)]
37+
mod tests {
38+
use super::*;
39+
use crate::api::resolution::Resolution;
40+
use anyhow::anyhow;
41+
42+
struct MockApi {
43+
resolutions: Option<Vec<Resolution>>,
44+
}
45+
46+
impl crate::api::BacklogApi for MockApi {
47+
fn get_resolutions(&self) -> anyhow::Result<Vec<Resolution>> {
48+
self.resolutions
49+
.clone()
50+
.ok_or_else(|| anyhow!("no resolutions"))
51+
}
52+
}
53+
54+
fn sample_resolutions() -> Vec<Resolution> {
55+
vec![
56+
Resolution {
57+
id: 0,
58+
name: "Fixed".to_string(),
59+
},
60+
Resolution {
61+
id: 1,
62+
name: "Won't Fix".to_string(),
63+
},
64+
Resolution {
65+
id: 2,
66+
name: "Invalid".to_string(),
67+
},
68+
]
69+
}
70+
71+
fn args(json: bool) -> ResolutionListArgs {
72+
ResolutionListArgs::new(json)
73+
}
74+
75+
#[test]
76+
fn list_with_text_output_succeeds() {
77+
let api = MockApi {
78+
resolutions: Some(sample_resolutions()),
79+
};
80+
assert!(list_with(&args(false), &api).is_ok());
81+
}
82+
83+
#[test]
84+
fn list_with_json_output_succeeds() {
85+
let api = MockApi {
86+
resolutions: Some(sample_resolutions()),
87+
};
88+
assert!(list_with(&args(true), &api).is_ok());
89+
}
90+
91+
#[test]
92+
fn list_with_propagates_api_error() {
93+
let api = MockApi { resolutions: None };
94+
let err = list_with(&args(false), &api).unwrap_err();
95+
assert!(err.to_string().contains("no resolutions"));
96+
}
97+
}

src/cmd/resolution/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod list;
2+
3+
pub use list::{ResolutionListArgs, list};

0 commit comments

Comments
 (0)