jirust_cli/runners/jira_cmd_runners/link_issue_cmd_runner.rs
1use jira_v3_openapi::apis::Error;
2use jira_v3_openapi::apis::configuration::Configuration;
3use jira_v3_openapi::apis::issue_links_api::link_issues;
4use jira_v3_openapi::models::{IssueLinkType, LinkIssueRequestJsonBean, LinkedIssue};
5use serde_json::Value;
6
7use crate::args::commands::LinkIssueArgs;
8use crate::config::config_file::{AuthData, ConfigFile};
9use crate::utils::changelog_extractor::ChangelogExtractor;
10
11/// Link issue command runner
12/// This struct is responsible for running the link issue command
13/// It uses the Jira API to perform the operations
14///
15/// # Fields
16///
17/// * `cfg` - Configuration object
18pub struct LinkIssueCmdRunner {
19 /// Configuration object
20 cfg: Configuration,
21}
22
23/// Implementation of IssueCmdRunner
24///
25/// # Methods
26///
27/// * `new` - Creates a new instance of LinkIssueCmdRunner
28/// * `link_jira_issues` - Links Jira issues
29impl LinkIssueCmdRunner {
30 /// Creates a new instance of LinkIssueCmdRunner
31 ///
32 /// # Arguments
33 ///
34 /// * `cfg_file` - Configuration file
35 ///
36 /// # Returns
37 ///
38 /// * `LinkIssueCmdRunner` - Instance of LinkIssueCmdRunner
39 ///
40 /// # Examples
41 ///
42 /// ```
43 /// use jirust_cli::config::config_file::ConfigFile;
44 /// use jirust_cli::runners::jira_cmd_runners::link_issue_cmd_runner::LinkIssueCmdRunner;
45 /// use toml::Table;
46 ///
47 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
48 ///
49 /// let link_issue_cmd_runner = LinkIssueCmdRunner::new(cfg_file);
50 /// ```
51 pub fn new(cfg_file: ConfigFile) -> LinkIssueCmdRunner {
52 let mut config = Configuration::new();
53 let auth_data = AuthData::from_base64(cfg_file.get_auth_key());
54 config.base_path = cfg_file.get_jira_url().to_string();
55 config.basic_auth = Some((auth_data.0, Some(auth_data.1)));
56 LinkIssueCmdRunner { cfg: config }
57 }
58
59 /// Links Jira issues
60 ///
61 /// # Arguments
62 ///
63 /// * `params` - LinkIssueCmdParams struct
64 ///
65 /// # Returns
66 ///
67 /// * `Result<Value, Box<dyn std::error::Error>>` - Result of the operation
68 ///
69 /// # Examples
70 ///
71 /// ```no_run
72 /// use jirust_cli::runners::jira_cmd_runners::link_issue_cmd_runner::{LinkIssueCmdRunner, LinkIssueCmdParams};
73 /// use jirust_cli::config::config_file::ConfigFile;
74 /// use toml::Table;
75 /// # use std::error::Error;
76 ///
77 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
78 /// # tokio_test::block_on(async {
79 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
80 /// let link_issue_cmd_runner = LinkIssueCmdRunner::new(cfg_file);
81 /// let mut params = LinkIssueCmdParams::new();
82 /// params.origin_issue_key = "ISSUE-1".to_string();
83 /// params.destination_issue_key = Some("ISSUE-2".to_string());
84 /// params.link_type = "Blocks".to_string();
85 ///
86 /// let result = link_issue_cmd_runner.link_jira_issues(params).await?;
87 /// # Ok(())
88 /// # })
89 /// # }
90 /// ```
91 pub async fn link_jira_issues(
92 &self,
93 params: LinkIssueCmdParams,
94 ) -> Result<Value, Box<dyn std::error::Error>> {
95 let mut link_requests: Vec<LinkIssueRequestJsonBean> = Vec::new();
96 if params.destination_issue_key.is_some() {
97 let link_request = LinkIssueRequestJsonBean {
98 comment: None,
99 inward_issue: Box::new(LinkedIssue {
100 key: Some(params.origin_issue_key),
101 id: None,
102 fields: None,
103 param_self: None,
104 }),
105 outward_issue: Box::new(LinkedIssue {
106 key: params.destination_issue_key,
107 id: None,
108 fields: None,
109 param_self: None,
110 }),
111 r#type: Box::new(IssueLinkType {
112 name: Some(params.link_type),
113 inward: None,
114 outward: None,
115 id: None,
116 param_self: None,
117 }),
118 };
119 link_requests.push(link_request);
120 } else {
121 let changelog_extractor = ChangelogExtractor::new(params.changelog_file.unwrap());
122 let version_data: Option<String> = Some(
123 changelog_extractor
124 .extract_version_changelog()
125 .unwrap_or_default(),
126 );
127 if version_data.is_some() {
128 let issues = changelog_extractor
129 .extract_issues_from_changelog(
130 version_data.clone().unwrap(),
131 params.project_key.clone().expect("Project key is required"),
132 )
133 .unwrap_or_default();
134 link_requests = issues
135 .iter()
136 .map(|issue| LinkIssueRequestJsonBean {
137 comment: None,
138 inward_issue: Box::new(LinkedIssue {
139 key: Some(params.origin_issue_key.clone()),
140 id: None,
141 fields: None,
142 param_self: None,
143 }),
144 outward_issue: Box::new(LinkedIssue {
145 key: Some(issue.clone()),
146 id: None,
147 fields: None,
148 param_self: None,
149 }),
150 r#type: Box::new(IssueLinkType {
151 name: Some(params.link_type.clone()),
152 inward: None,
153 outward: None,
154 id: None,
155 param_self: None,
156 }),
157 })
158 .collect();
159 }
160 };
161
162 let mut link_result: Value = Value::String("Linking OK".to_string());
163
164 for link_issue_request_json_bean in link_requests {
165 match link_issues(&self.cfg, link_issue_request_json_bean).await {
166 Ok(_) => {}
167 Err(Error::Serde(_)) => {}
168 Err(_) => {
169 link_result = Value::String("Linking KO".to_string());
170 }
171 };
172 }
173 Ok(link_result)
174 }
175}
176
177/// Link issue command parameters
178///
179/// # Fields
180///
181/// * `project_key` - Jira project key
182/// * `origin_issue_key` - Jira origin issue key
183/// * `destination_issue_key` - Jira destination issue key
184/// * `link_type` - Jira link type
185/// * `changelog_file` - Changelog file
186pub struct LinkIssueCmdParams {
187 /// Jira project key
188 pub project_key: Option<String>,
189 /// Jira issue key
190 pub origin_issue_key: String,
191 /// Jira issue key
192 pub destination_issue_key: Option<String>,
193 /// Jira issue fields
194 pub link_type: String,
195 /// Jira issue transition
196 pub changelog_file: Option<String>,
197}
198
199/// Implementation of LinkIssueCmdParams struct
200///
201/// # Methods
202///
203/// * `new` - Creates a new LinkIssueCmdParams instance
204impl LinkIssueCmdParams {
205 /// Creates a new LinkIssueCmdParams instance
206 ///
207 /// # Returns
208 ///
209 /// * `LinkIssueCmdParams` - Issue command parameters
210 ///
211 /// # Examples
212 ///
213 /// ```
214 /// use jirust_cli::runners::jira_cmd_runners::link_issue_cmd_runner::LinkIssueCmdParams;
215 ///
216 /// let params = LinkIssueCmdParams::new();
217 /// ```
218 pub fn new() -> LinkIssueCmdParams {
219 LinkIssueCmdParams {
220 project_key: None,
221 origin_issue_key: "".to_string(),
222 destination_issue_key: None,
223 link_type: "".to_string(),
224 changelog_file: None,
225 }
226 }
227}
228
229/// Implementation of From trait for LinkIssueCmdParams struct
230/// to convert LinkIssueArgs struct to LinkIssueCmdParams struct
231impl From<&LinkIssueArgs> for LinkIssueCmdParams {
232 /// Converts LinkIssueArgs struct to LinkIssueCmdParams struct
233 /// to create a new LinkIssueCmdParams instance
234 ///
235 /// # Arguments
236 ///
237 /// * `value` - LinkIssueArgs struct
238 ///
239 /// # Returns
240 ///
241 /// * `LinkIssueArgs` - Link issue command parameters
242 ///
243 /// # Examples
244 ///
245 /// ```
246 /// use jirust_cli::runners::jira_cmd_runners::link_issue_cmd_runner::LinkIssueCmdParams;
247 /// use jirust_cli::args::commands::{LinkIssueArgs, LinkIssueActionValues};
248 /// use std::collections::HashMap;
249 /// use serde_json::Value;
250 ///
251 /// let link_issue_args = LinkIssueArgs {
252 /// link_act: LinkIssueActionValues::Create,
253 /// project_key: Some("project_key".to_string()),
254 /// origin_issue_key: "origin_issue_key".to_string(),
255 /// destination_issue_key: Some("destination_issue_key".to_string()),
256 /// link_type: "link_type".to_string(),
257 /// changelog_file: None,
258 /// };
259 ///
260 /// let params = LinkIssueCmdParams::from(&link_issue_args);
261 ///
262 /// assert_eq!(params.project_key, Some("project_key".to_string()));
263 /// assert_eq!(params.origin_issue_key, "origin_issue_key".to_string());
264 /// assert_eq!(params.destination_issue_key, Some("destination_issue_key".to_string()));
265 /// assert_eq!(params.link_type, "link_type".to_string());
266 /// assert_eq!(params.changelog_file, None);
267 ///
268 /// ```
269 fn from(value: &LinkIssueArgs) -> Self {
270 if (value.destination_issue_key.is_none() && value.changelog_file.is_none())
271 || (value.destination_issue_key.is_some() && value.changelog_file.is_some())
272 {
273 panic!("Either destination issue key or changelog file is required");
274 }
275 if value.changelog_file.is_some() && value.project_key.is_none() {
276 panic!("Project key is required when changelog file is provided");
277 }
278 LinkIssueCmdParams {
279 project_key: value.project_key.clone(),
280 origin_issue_key: value.origin_issue_key.clone(),
281 destination_issue_key: value.destination_issue_key.clone(),
282 link_type: value.link_type.clone(),
283 changelog_file: value.changelog_file.clone(),
284 }
285 }
286}
287
288/// Default implementation for IssueCmdParams struct
289impl Default for LinkIssueCmdParams {
290 /// Creates a default LinkIssueCmdParams instance
291 ///
292 /// # Returns
293 ///
294 /// A LinkIssueCmdParams instance with default values
295 ///
296 /// # Examples
297 ///
298 /// ```
299 /// use jirust_cli::runners::jira_cmd_runners::link_issue_cmd_runner::LinkIssueCmdParams;
300 ///
301 /// let params = LinkIssueCmdParams::default();
302 ///
303 /// assert_eq!(params.project_key, None);
304 /// assert_eq!(params.origin_issue_key, "".to_string());
305 /// assert_eq!(params.destination_issue_key, None);
306 /// assert_eq!(params.link_type, "".to_string());
307 /// assert_eq!(params.changelog_file, None);
308 /// ```
309 fn default() -> Self {
310 LinkIssueCmdParams::new()
311 }
312}