jirust_cli/runners/jira_cmd_runners/issue_cmd_runner.rs
1use jira_v3_openapi::apis::issues_api::*;
2use jira_v3_openapi::models::user::AccountType;
3use jira_v3_openapi::models::{CreatedIssue, IssueBean, IssueTransition, Transitions, User};
4use jira_v3_openapi::{apis::configuration::Configuration, models::IssueUpdateDetails};
5use serde_json::Value;
6use std::collections::HashMap;
7use std::io::Error;
8
9use crate::args::commands::TransitionArgs;
10use crate::{
11 args::commands::IssueArgs,
12 config::config_file::{AuthData, ConfigFile},
13};
14
15/// Issue command runner
16/// This struct is responsible for running the issue command
17/// It uses the Jira API to perform the operations
18///
19/// # Fields
20///
21/// * `cfg` - Configuration object
22pub struct IssueCmdRunner {
23 /// Configuration object
24 cfg: Configuration,
25}
26
27/// Implementation of IssueCmdRunner
28///
29/// # Methods
30///
31/// * `new` - Creates a new instance of IssueCmdRunner
32/// * `assign_jira_issue` - Assigns a Jira issue to a user
33/// * `create_jira_issue` - Creates a Jira issue
34/// * `delete_jira_issue` - Deletes a Jira issue
35/// * `get_jira_issue` - Gets a Jira issue
36/// * `transition_jira_issue` - Transitions a Jira issue
37/// * `update_jira_issue` - Updates a Jira issue
38/// * `get_issue_available_transitions` - Gets available transitions for a Jira issue
39impl IssueCmdRunner {
40 /// Creates a new instance of IssueCmdRunner
41 ///
42 /// # Arguments
43 ///
44 /// * `cfg_file` - Configuration file
45 ///
46 /// # Returns
47 ///
48 /// * `IssueCmdRunner` - Instance of IssueCmdRunner
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// use jirust_cli::config::config_file::ConfigFile;
54 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueCmdRunner;
55 /// use toml::Table;
56 ///
57 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
58 ///
59 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
60 /// ```
61 pub fn new(cfg_file: &ConfigFile) -> IssueCmdRunner {
62 let mut config = Configuration::new();
63 let auth_data = AuthData::from_base64(cfg_file.get_auth_key());
64 config.base_path = cfg_file.get_jira_url().to_string();
65 config.basic_auth = Some((auth_data.0, Some(auth_data.1)));
66 IssueCmdRunner { cfg: config }
67 }
68
69 /// Assigns a Jira issue to a user
70 ///
71 /// # Arguments
72 ///
73 /// * `params` - Issue command parameters
74 ///
75 /// # Returns
76 ///
77 /// * `Value` - JSON value
78 ///
79 /// # Examples
80 ///
81 /// ```no_run
82 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
83 /// use jirust_cli::config::config_file::ConfigFile;
84 /// use toml::Table;
85 ///
86 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
87 /// # tokio_test::block_on(async {
88 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
89 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
90 /// let mut params = IssueCmdParams::new();
91 /// params.assignee = Some("assignee".to_string());
92 ///
93 /// let result = issue_cmd_runner.assign_jira_issue(params).await?;
94 /// # Ok(())
95 /// # })
96 /// # }
97 /// ```
98 pub async fn assign_jira_issue(
99 &self,
100 params: IssueCmdParams,
101 ) -> Result<Value, Box<dyn std::error::Error>> {
102 let user_data = User {
103 account_id: Some(params.assignee.expect("Assignee is required")),
104 account_type: Some(AccountType::Atlassian),
105 ..Default::default()
106 };
107
108 let i_key = if let Some(key) = ¶ms.issue_key {
109 key.as_str()
110 } else {
111 return Err(Box::new(Error::other(
112 "Error assigning issue: Empty issue key".to_string(),
113 )));
114 };
115
116 Ok(assign_issue(&self.cfg, i_key, user_data).await?)
117 }
118
119 /// Creates a Jira issue
120 ///
121 /// # Arguments
122 ///
123 /// * `params` - Issue command parameters
124 ///
125 /// # Returns
126 ///
127 /// * `CreatedIssue` - Created issue
128 ///
129 /// # Examples
130 ///
131 /// ```no_run
132 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
133 /// use jirust_cli::config::config_file::ConfigFile;
134 /// use toml::Table;
135 ///
136 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
137 /// # tokio_test::block_on(async {
138 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
139 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
140 /// let params = IssueCmdParams::new();
141 ///
142 /// let result = issue_cmd_runner.create_jira_issue(params).await?;
143 /// # Ok(())
144 /// # })
145 /// # }
146 /// ```
147 pub async fn create_jira_issue(
148 &self,
149 params: IssueCmdParams,
150 ) -> Result<CreatedIssue, Box<dyn std::error::Error>> {
151 let mut issue_fields = params.issue_fields.unwrap_or_default();
152 issue_fields.insert(
153 "project".to_string(),
154 serde_json::json!({"key": params.project_key}),
155 );
156 let issue_data = IssueUpdateDetails {
157 fields: Some(issue_fields),
158 history_metadata: None,
159 properties: None,
160 transition: None,
161 update: None,
162 };
163 Ok(create_issue(&self.cfg, issue_data, None).await?)
164 }
165
166 /// Deletes a Jira issue
167 ///
168 /// # Arguments
169 ///
170 /// * `params` - Issue command parameters
171 ///
172 /// # Returns
173 ///
174 /// * `()` - Empty tuple
175 ///
176 /// # Examples
177 ///
178 /// ```no_run
179 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
180 /// use jirust_cli::config::config_file::ConfigFile;
181 /// use toml::Table;
182 ///
183 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
184 /// # tokio_test::block_on(async {
185 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
186 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
187 /// let mut params = IssueCmdParams::new();
188 /// params.issue_key = Some("issue_key".to_string());
189 ///
190 /// let result = issue_cmd_runner.delete_jira_issue(params).await?;
191 /// # Ok(())
192 /// # })
193 /// # }
194 /// ```
195 pub async fn delete_jira_issue(
196 &self,
197 params: IssueCmdParams,
198 ) -> Result<(), Box<dyn std::error::Error>> {
199 let i_key = if let Some(key) = ¶ms.issue_key {
200 key.as_str()
201 } else {
202 return Err(Box::new(Error::other(
203 "Error deleting issue: Empty issue key".to_string(),
204 )));
205 };
206
207 Ok(delete_issue(&self.cfg, i_key, Some("true")).await?)
208 }
209
210 /// Gets a Jira issue
211 ///
212 /// # Arguments
213 ///
214 /// * `params` - Issue command parameters
215 ///
216 /// # Returns
217 ///
218 /// * `IssueBean` - Jira issue
219 ///
220 /// # Examples
221 ///
222 /// ```no_run
223 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
224 /// use jirust_cli::config::config_file::ConfigFile;
225 /// use toml::Table;
226 ///
227 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
228 /// # tokio_test::block_on(async {
229 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
230 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
231 /// let mut params = IssueCmdParams::new();
232 /// params.issue_key = Some("issue_key".to_string());
233 ///
234 /// let result = issue_cmd_runner.get_jira_issue(params).await?;
235 /// # Ok(())
236 /// # })
237 /// # }
238 /// ```
239 pub async fn get_jira_issue(
240 &self,
241 params: IssueCmdParams,
242 ) -> Result<IssueBean, Box<dyn std::error::Error>> {
243 let i_key = if let Some(key) = ¶ms.issue_key {
244 key.as_str()
245 } else {
246 return Err(Box::new(Error::other(
247 "Error retrieving issue: Empty issue key".to_string(),
248 )));
249 };
250 Ok(get_issue(&self.cfg, i_key, None, None, None, None, None, None).await?)
251 }
252
253 /// Transitions a Jira issue
254 ///
255 /// # Arguments
256 ///
257 /// * `params` - Issue command parameters
258 ///
259 /// # Returns
260 ///
261 /// * `Value` - Jira issue transition
262 ///
263 /// # Examples
264 ///
265 /// ```no_run
266 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
267 /// use jirust_cli::config::config_file::ConfigFile;
268 /// use toml::Table;
269 ///
270 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
271 /// # tokio_test::block_on(async {
272 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
273 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
274 ///
275 /// let mut params = IssueCmdParams::new();
276 /// params.transition = Some("transition_id".to_string());
277 ///
278 /// let result = issue_cmd_runner.transition_jira_issue(params).await?;
279 /// # Ok(())
280 /// # })
281 /// # }
282 /// ```
283 pub async fn transition_jira_issue(
284 &self,
285 params: IssueCmdParams,
286 ) -> Result<Value, Box<dyn std::error::Error>> {
287 let i_key = if let Some(key) = ¶ms.issue_key {
288 key.as_str()
289 } else {
290 return Err(Box::new(Error::other(
291 "Error with issue transition: Empty issue key".to_string(),
292 )));
293 };
294
295 let trans = if let Some(transition) = ¶ms.transition {
296 transition.as_str()
297 } else {
298 return Err(Box::new(Error::other(
299 "Error with issue transition: Empty transition".to_string(),
300 )));
301 };
302
303 let transition = IssueTransition {
304 id: Some(trans.to_string()),
305 ..Default::default()
306 };
307 let issue_data = IssueUpdateDetails {
308 fields: params.issue_fields,
309 history_metadata: None,
310 properties: None,
311 transition: Some(transition),
312 update: None,
313 };
314 Ok(do_transition(&self.cfg, i_key, issue_data).await?)
315 }
316
317 /// Updates a Jira issue
318 ///
319 /// # Arguments
320 ///
321 /// * `params` - Issue command parameters
322 ///
323 /// # Returns
324 ///
325 /// * `Value` - Jira issue update
326 ///
327 /// # Examples
328 ///
329 /// ```no_run
330 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
331 /// use jirust_cli::config::config_file::ConfigFile;
332 /// use toml::Table;
333 ///
334 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
335 /// # tokio_test::block_on(async {
336 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
337 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
338 /// let params = IssueCmdParams::new();
339 ///
340 /// let result = issue_cmd_runner.update_jira_issue(params).await?;
341 /// # Ok(())
342 /// # })
343 /// # }
344 /// ```
345 pub async fn update_jira_issue(
346 &self,
347 params: IssueCmdParams,
348 ) -> Result<Value, Box<dyn std::error::Error>> {
349 let i_key = if let Some(key) = ¶ms.issue_key {
350 key.as_str()
351 } else {
352 return Err(Box::new(Error::other(
353 "Error updating issue: Empty issue key".to_string(),
354 )));
355 };
356
357 let issue_data = IssueUpdateDetails {
358 fields: params.issue_fields,
359 history_metadata: None,
360 properties: None,
361 transition: None,
362 update: None,
363 };
364 Ok(edit_issue(
365 &self.cfg,
366 i_key,
367 issue_data,
368 None,
369 None,
370 None,
371 Some(true),
372 None,
373 )
374 .await?)
375 }
376
377 /// Gets available transitions for a Jira issue
378 ///
379 /// # Arguments
380 ///
381 /// * `params` - Issue command parameters
382 ///
383 /// # Returns
384 ///
385 /// * `Transitions` - Jira issue transitions
386 ///
387 /// # Examples
388 ///
389 /// ```no_run
390 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueTransitionCmdParams};
391 /// use jirust_cli::config::config_file::ConfigFile;
392 /// use toml::Table;
393 ///
394 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
395 /// # tokio_test::block_on(async {
396 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
397 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
398 /// let mut params = IssueTransitionCmdParams::new();
399 /// params.issue_key = "issue_key".to_string();
400 ///
401 /// let result = issue_cmd_runner.get_issue_available_transitions(params).await?;
402 /// # Ok(())
403 /// # })
404 /// # }
405 /// ```
406 pub async fn get_issue_available_transitions(
407 &self,
408 params: IssueTransitionCmdParams,
409 ) -> Result<Transitions, Box<dyn std::error::Error>> {
410 Ok(get_transitions(
411 &self.cfg,
412 ¶ms.issue_key,
413 None,
414 None,
415 None,
416 Some(false),
417 None,
418 )
419 .await?)
420 }
421}
422
423/// Issue command parameters
424///
425/// # Fields
426///
427/// * `project_key` - Jira project key
428/// * `issue_key` - Jira issue key
429/// * `issue_fields` - Jira issue fields
430/// * `transition` - Jira issue transition
431/// * `assignee` - Jira issue assignee
432pub struct IssueCmdParams {
433 /// Jira project key
434 pub project_key: String,
435 /// Jira issue key
436 pub issue_key: Option<String>,
437 /// Jira issue fields
438 pub issue_fields: Option<HashMap<String, Value>>,
439 /// Jira issue transition
440 pub transition: Option<String>,
441 /// Jira issue assignee
442 pub assignee: Option<String>,
443}
444
445/// Implementation of IssueCmdParams struct
446///
447/// # Methods
448///
449/// * `new` - Creates a new IssueCmdParams instance
450impl IssueCmdParams {
451 /// Creates a new IssueCmdParams instance
452 ///
453 /// # Returns
454 ///
455 /// * `IssueCmdParams` - Issue command parameters
456 ///
457 /// # Examples
458 ///
459 /// ```
460 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueCmdParams;
461 ///
462 /// let params = IssueCmdParams::new();
463 /// ```
464 pub fn new() -> IssueCmdParams {
465 IssueCmdParams {
466 project_key: "".to_string(),
467 issue_key: None,
468 issue_fields: None,
469 transition: None,
470 assignee: None,
471 }
472 }
473}
474
475/// Implementation of From trait for IssueCmdParams struct
476/// to convert IssueArgs struct to IssueCmdParams struct
477impl From<&IssueArgs> for IssueCmdParams {
478 /// Converts IssueArgs struct to IssueCmdParams struct
479 /// to create a new IssueCmdParams instance
480 ///
481 /// # Arguments
482 ///
483 /// * `value` - IssueArgs struct
484 ///
485 /// # Returns
486 ///
487 /// * `IssueCmdParams` - Issue command parameters
488 ///
489 /// # Examples
490 ///
491 /// ```
492 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueCmdParams;
493 /// use jirust_cli::args::commands::{IssueArgs, PaginationArgs, OutputArgs, IssueActionValues};
494 /// use std::collections::HashMap;
495 /// use serde_json::Value;
496 ///
497 /// let issue_args = IssueArgs {
498 /// issue_act: IssueActionValues::Get,
499 /// project_key: "project_key".to_string(),
500 /// issue_key: Some("issue_key".to_string()),
501 /// issue_fields: Some(vec![("key".to_string(), r#"{ "key": "value" }"#.to_string())]),
502 /// transition_to: Some("transition_to".to_string()),
503 /// assignee: Some("assignee".to_string()),
504 /// pagination: PaginationArgs { page_size: Some(20), page_offset: None },
505 /// output: OutputArgs { output_format: None, output_type: None },
506 /// };
507 ///
508 /// let params = IssueCmdParams::from(&issue_args);
509 ///
510 /// assert_eq!(params.project_key, "project_key".to_string());
511 /// assert_eq!(params.issue_key.unwrap(), "issue_key".to_string());
512 /// assert_eq!(params.transition.unwrap(), "transition_to".to_string());
513 /// assert_eq!(params.assignee.unwrap(), "assignee".to_string());
514 /// ```
515 fn from(value: &IssueArgs) -> Self {
516 IssueCmdParams {
517 project_key: value.project_key.clone(),
518 issue_key: value.issue_key.clone(),
519 issue_fields: Some(
520 value
521 .issue_fields
522 .clone()
523 .unwrap_or_default()
524 .iter()
525 .map(|elem| {
526 (
527 elem.0.clone(),
528 serde_json::from_str(elem.1.clone().as_str()).unwrap_or(Value::Null),
529 )
530 })
531 .collect::<HashMap<_, _>>(),
532 ),
533 transition: value.transition_to.clone(),
534 assignee: value.assignee.clone(),
535 }
536 }
537}
538
539/// Issue transition command parameters
540///
541/// # Fields
542///
543/// * `issue_key` - Jira issue key
544pub struct IssueTransitionCmdParams {
545 /// Jira issue key
546 pub issue_key: String,
547}
548
549/// Implementation of IssueTransitionCmdParams struct
550///
551/// # Methods
552///
553/// * `new` - Creates a new IssueTransitionCmdParams instance
554impl IssueTransitionCmdParams {
555 /// Creates a new IssueTransitionCmdParams instance
556 ///
557 /// # Returns
558 ///
559 /// * `IssueTransitionCmdParams` - Issue transition command parameters
560 ///
561 /// # Examples
562 ///
563 /// ```
564 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueTransitionCmdParams;
565 ///
566 /// let params = IssueTransitionCmdParams::new();
567 /// ```
568 pub fn new() -> IssueTransitionCmdParams {
569 IssueTransitionCmdParams {
570 issue_key: "".to_string(),
571 }
572 }
573}
574
575/// Implementation of From trait for IssueTransitionCmdParams struct
576/// to convert TransitionArgs struct to IssueTransitionCmdParams struct
577impl From<&TransitionArgs> for IssueTransitionCmdParams {
578 /// Converts TransitionArgs struct to IssueTransitionCmdParams struct
579 /// to create a new IssueTransitionCmdParams instance
580 ///
581 /// # Arguments
582 ///
583 /// * `value` - TransitionArgs struct
584 ///
585 /// # Returns
586 ///
587 /// * `IssueTransitionCmdParams` - Issue transition command parameters
588 ///
589 /// # Examples
590 ///
591 /// ```
592 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueTransitionCmdParams;
593 /// use jirust_cli::args::commands::{TransitionArgs, TransitionActionValues, OutputArgs};
594 ///
595 /// let transition_args = TransitionArgs {
596 /// transition_act: TransitionActionValues::List,
597 /// issue_key: "issue_key".to_string(),
598 /// output: OutputArgs { output_format: None, output_type: None },
599 /// };
600 ///
601 /// let params = IssueTransitionCmdParams::from(&transition_args);
602 ///
603 /// assert_eq!(params.issue_key, "issue_key".to_string());
604 /// ```
605 fn from(value: &TransitionArgs) -> Self {
606 IssueTransitionCmdParams {
607 issue_key: value.issue_key.clone(),
608 }
609 }
610}
611
612/// Default implementation for IssueCmdParams struct
613impl Default for IssueTransitionCmdParams {
614 /// Creates a default IssueTransitionCmdParams instance
615 ///
616 /// # Returns
617 ///
618 /// A IssueTransitionCmdParams instance with default values
619 ///
620 /// # Examples
621 ///
622 /// ```
623 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueTransitionCmdParams;
624 ///
625 /// let params = IssueTransitionCmdParams::default();
626 ///
627 /// assert_eq!(params.issue_key, "".to_string());
628 /// ```
629 fn default() -> Self {
630 IssueTransitionCmdParams::new()
631 }
632}
633
634/// Default implementation for IssueCmdParams struct
635impl Default for IssueCmdParams {
636 /// Creates a default IssueCmdParams instance
637 ///
638 /// # Returns
639 ///
640 /// A IssueCmdParams instance with default values
641 ///
642 /// # Examples
643 ///
644 /// ```
645 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueCmdParams;
646 ///
647 /// let params = IssueCmdParams::default();
648 ///
649 /// assert_eq!(params.project_key, "".to_string());
650 /// assert_eq!(params.issue_key, None);
651 /// assert_eq!(params.issue_fields, None);
652 /// assert_eq!(params.transition, None);
653 /// assert_eq!(params.assignee, None);
654 /// ```
655 fn default() -> Self {
656 IssueCmdParams::new()
657 }
658}