gitlab_butler_lib/
issues.rs1use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET};
2
3use anyhow::Result;
4use log::{debug, error};
5
6use crate::client::Client;
7use crate::entities::{CreateIssue, Issue, IssueState, Project, ProjectMilestone};
8
9impl Issue {
10 pub fn create(client: &Client, issue: &CreateIssue) -> Result<Issue> {
11 let api_path = {
13 let project_name_or_id =
14 utf8_percent_encode(&issue.project_id, PATH_SEGMENT_ENCODE_SET).to_string();
15 format!("api/v4/projects/{}/issues", project_name_or_id)
16 };
17 let response = client.post_json(&api_path, &issue)?;
18 let de = &mut serde_json::Deserializer::from_reader(response);
19 let result: Result<Issue, _> = serde_path_to_error::deserialize(de);
20 match result {
21 Ok(issue) => Ok(issue),
22 Err(err) => {
23 dbg!(&api_path);
24 dbg!(err.path().to_string());
25 dbg!(client.get(&api_path)?.text()?);
26 Err(err.into_inner().into())
27 }
28 }
29 }
30 pub fn issue_from_branch(
31 client: &Client,
32 project: &str,
33 branch: &str,
34 ) -> Option<Result<Issue>> {
35 let prefix = branch.split('-').next();
36 let issue_iid = prefix.and_then(|iid| iid.parse::<usize>().ok());
37 issue_iid.map(|issue_iid| Issue::single(&client, &project, issue_iid))
38 }
39 pub fn single(client: &Client, project: &str, issue_iid: usize) -> Result<Issue> {
40 let api_path = {
42 let project_name_or_id =
43 utf8_percent_encode(project, PATH_SEGMENT_ENCODE_SET).to_string();
44 format!(
45 "api/v4/projects/{}/issues/{}",
46 project_name_or_id, issue_iid
47 )
48 };
49 let response = client.get(&api_path)?;
50 let de = &mut serde_json::Deserializer::from_reader(response);
51 let result: Result<Issue, _> = serde_path_to_error::deserialize(de);
52 match result {
53 Ok(issue) => Ok(issue),
54 Err(err) => {
55 error!("Path: {}", err.path().to_string());
56 error!("Body: {:?}", client.get(&api_path)?.bytes()?);
57 Err(err.into_inner().into())
58 }
59 }
60 }
61 pub fn list(
62 client: &Client,
63 project: &Option<String>,
64 state: &IssueState,
65 labels: &Option<String>,
66 milestone: &Option<String>,
67 limit: usize,
68 ) -> Result<Vec<Issue>> {
69 let prefix = if let Some(aproject) = project {
70 let project_name_or_id =
71 utf8_percent_encode(aproject, PATH_SEGMENT_ENCODE_SET).to_string();
72 format!("api/v4/projects/{}", project_name_or_id)
73 } else {
74 "api/v4".to_string()
75 };
76 let scope = {
77 if project.is_some() {
78 "all"
79 } else {
80 "assigned_to_me" }
82 };
83 let mut api_path = format!("{}/issues?state={}&scope={}", prefix, state.name(), scope);
84 if let Some(labels) = labels {
85 api_path = format!("{}&labels={}", api_path, labels);
86 }
87 if let Some(milestone) = milestone {
88 api_path = format!("{}&milestone={}", api_path, milestone);
89 }
90 debug!("Ready to query {:?}", api_path);
91 let mut all_issues: Vec<Issue> = vec![];
92 for issues in client.get_paginated(&api_path) {
93 let issues = issues?;
94 let de = &mut serde_json::Deserializer::from_reader(issues);
95 let result: Result<Vec<Issue>, _> = serde_path_to_error::deserialize(de);
96 let issues = match result {
97 Ok(issues) => issues,
98 Err(err) => {
99 dbg!(&api_path);
100 dbg!(err.path().to_string());
101 dbg!(client.get(&api_path)?.text()?);
102 return Err(err.into_inner().into());
103 }
104 };
105 all_issues.extend(issues);
106 if all_issues.len() >= limit {
107 break;
108 }
109 }
110 Ok(all_issues)
111 }
112}
113
114impl ProjectMilestone {
115 pub fn in_project_by_title(
116 client: &Client,
117 project: &str,
118 title: &str,
119 ) -> Result<Option<ProjectMilestone>> {
120 ProjectMilestone::_in_project_by_title(client, project, Some(title)).map(|mut m| m.pop())
122 }
123 pub fn in_project(client: &Client, project: &str) -> Result<Vec<ProjectMilestone>> {
124 ProjectMilestone::_in_project_by_title(client, project, None)
126 }
127 fn _in_project_by_title(
128 client: &Client,
129 project: &str,
130 title: Option<&str>,
131 ) -> Result<Vec<ProjectMilestone>> {
132 let prefix = {
133 let project_name_or_id =
134 utf8_percent_encode(project, PATH_SEGMENT_ENCODE_SET).to_string();
135 format!("api/v4/projects/{}", project_name_or_id)
136 };
137 let mut api_path = format!(
138 "{}/milestones?state=active&include_parent_milestones=true",
139 prefix
140 );
141 if let Some(title) = title {
142 api_path = format!("{}&title={}", api_path, title);
143 }
144 debug!("Ready to query {:?}", api_path);
145 let response = client.get(&api_path)?;
146 let de = &mut serde_json::Deserializer::from_reader(response);
147 let result: Result<Vec<ProjectMilestone>, _> = serde_path_to_error::deserialize(de);
148 let milestones = match result {
149 Ok(milestones) => milestones,
150 Err(err) => {
151 dbg!(&api_path);
152 dbg!(err.path().to_string());
153 dbg!(client.get(&api_path)?.text()?);
154 return Err(err.into_inner().into());
155 }
156 };
157 Ok(milestones)
158 }
159}
160
161impl Project {
162 pub fn list(client: &Client, search: &Option<String>, limit: usize) -> Result<Vec<Project>> {
163 let mut api_path = "api/v4/projects?simple=true&oder_by=id".to_string();
164 if let Some(search) = search {
165 api_path = format!("{}&search={}", api_path, search);
166 }
167 debug!("Ready to query {:?}", api_path);
168 let mut all_projects = vec![];
169 for projects in client.get_paginated(&api_path) {
170 let response = projects?;
171 let de = &mut serde_json::Deserializer::from_reader(response);
172 let result: Result<Vec<Project>, _> = serde_path_to_error::deserialize(de);
173 let projects = match result {
174 Ok(projects) => projects,
175 Err(err) => {
176 dbg!(&api_path);
177 dbg!(err.path().to_string());
178 return Err(err.into_inner().into());
179 }
180 };
181 all_projects.extend(projects);
182 if all_projects.len() >= limit {
183 break;
184 }
185 }
186 Ok(all_projects)
187 }
188 pub fn single(client: &Client, name_or_id: &str) -> Result<Project> {
189 let api_path = {
190 let name_or_id = utf8_percent_encode(name_or_id, PATH_SEGMENT_ENCODE_SET).to_string();
191 format!("api/v4/projects/{}", name_or_id)
192 };
193 let response = client.get(&api_path)?;
194 let de = &mut serde_json::Deserializer::from_reader(response);
195 let result: Result<Project, _> = serde_path_to_error::deserialize(de);
196 match result {
197 Ok(project) => Ok(project),
198 Err(err) => {
199 dbg!(&api_path);
200 dbg!(err.path().to_string());
201 dbg!(client.get(&api_path)?.text()?);
202 Err(err.into_inner().into())
203 }
204 }
205 }
206}