jirust_cli/runners/jira_cmd_runners/
issue_cmd_runner.rs

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