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