jirust_cli/runners/jira_cmd_runners/issue_cmd_runner.rs
1use async_trait::async_trait;
2use jira_v3_openapi::apis::issue_search_api::search_and_reconsile_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, SearchAndReconcileRequestBean, 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) = ¶ms.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) = ¶ms.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) = ¶ms.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: SearchAndReconcileRequestBean = SearchAndReconcileRequestBean {
294 fields: Some(vec!["*navigable".to_string(), "-comment".to_string()]),
295 jql: params.query,
296 ..Default::default()
297 };
298 match search_and_reconsile_issues_using_jql_post(&self.cfg, search_params).await {
299 Ok(result) => {
300 if let Some(issues) = result.issues {
301 Ok(issues)
302 } else {
303 Ok(vec![])
304 }
305 }
306 Err(e) => Err(Box::new(e)),
307 }
308 }
309
310 /// Transitions a Jira issue
311 ///
312 /// # Arguments
313 ///
314 /// * `params` - Issue command parameters
315 ///
316 /// # Returns
317 ///
318 /// * `Value` - Jira issue transition
319 ///
320 /// # Examples
321 ///
322 /// ```no_run
323 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
324 /// use jirust_cli::config::config_file::ConfigFile;
325 /// use toml::Table;
326 ///
327 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
328 /// # tokio_test::block_on(async {
329 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
330 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
331 ///
332 /// let mut params = IssueCmdParams::new();
333 /// params.transition = Some("transition_id".to_string());
334 ///
335 /// let result = issue_cmd_runner.transition_jira_issue(params).await?;
336 /// # Ok(())
337 /// # })
338 /// # }
339 /// ```
340 pub async fn transition_jira_issue(
341 &self,
342 params: IssueCmdParams,
343 ) -> Result<Value, Box<dyn std::error::Error>> {
344 let i_key = if let Some(key) = ¶ms.issue_key {
345 key.as_str()
346 } else {
347 return Err(Box::new(Error::other(
348 "Error with issue transition: Empty issue key".to_string(),
349 )));
350 };
351
352 let trans = if let Some(transition) = ¶ms.transition {
353 transition.as_str()
354 } else {
355 return Err(Box::new(Error::other(
356 "Error with issue transition: Empty transition".to_string(),
357 )));
358 };
359
360 let transition = IssueTransition {
361 id: Some(trans.to_string()),
362 ..Default::default()
363 };
364 let issue_data = IssueUpdateDetails {
365 fields: params.issue_fields,
366 history_metadata: None,
367 properties: None,
368 transition: Some(transition),
369 update: None,
370 };
371 Ok(do_transition(&self.cfg, i_key, issue_data).await?)
372 }
373
374 /// Updates a Jira issue
375 ///
376 /// # Arguments
377 ///
378 /// * `params` - Issue command parameters
379 ///
380 /// # Returns
381 ///
382 /// * `Value` - Jira issue update
383 ///
384 /// # Examples
385 ///
386 /// ```no_run
387 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueCmdParams};
388 /// use jirust_cli::config::config_file::ConfigFile;
389 /// use toml::Table;
390 ///
391 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
392 /// # tokio_test::block_on(async {
393 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
394 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
395 /// let params = IssueCmdParams::new();
396 ///
397 /// let result = issue_cmd_runner.update_jira_issue(params).await?;
398 /// # Ok(())
399 /// # })
400 /// # }
401 /// ```
402 pub async fn update_jira_issue(
403 &self,
404 params: IssueCmdParams,
405 ) -> Result<Value, Box<dyn std::error::Error>> {
406 let i_key = if let Some(key) = ¶ms.issue_key {
407 key.as_str()
408 } else {
409 return Err(Box::new(Error::other(
410 "Error updating issue: Empty issue key".to_string(),
411 )));
412 };
413
414 let issue_data = IssueUpdateDetails {
415 fields: params.issue_fields,
416 history_metadata: None,
417 properties: None,
418 transition: None,
419 update: None,
420 };
421 Ok(edit_issue(
422 &self.cfg,
423 i_key,
424 issue_data,
425 None,
426 None,
427 None,
428 Some(true),
429 None,
430 )
431 .await?)
432 }
433
434 /// Gets available transitions for a Jira issue
435 ///
436 /// # Arguments
437 ///
438 /// * `params` - Issue command parameters
439 ///
440 /// # Returns
441 ///
442 /// * `Transitions` - Jira issue transitions
443 ///
444 /// # Examples
445 ///
446 /// ```no_run
447 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::{IssueCmdRunner, IssueTransitionCmdParams};
448 /// use jirust_cli::config::config_file::ConfigFile;
449 /// use toml::Table;
450 ///
451 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
452 /// # tokio_test::block_on(async {
453 /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
454 /// let issue_cmd_runner = IssueCmdRunner::new(&cfg_file);
455 /// let mut params = IssueTransitionCmdParams::new();
456 /// params.issue_key = "issue_key".to_string();
457 ///
458 /// let result = issue_cmd_runner.get_issue_available_transitions(params).await?;
459 /// # Ok(())
460 /// # })
461 /// # }
462 /// ```
463 pub async fn get_issue_available_transitions(
464 &self,
465 params: IssueTransitionCmdParams,
466 ) -> Result<Transitions, Box<dyn std::error::Error>> {
467 Ok(get_transitions(
468 &self.cfg,
469 ¶ms.issue_key,
470 None,
471 None,
472 None,
473 Some(false),
474 None,
475 )
476 .await?)
477 }
478}
479
480/// Issue command parameters
481///
482/// # Fields
483///
484/// * `project_key` - Jira project key
485/// * `issue_key` - Jira issue key
486/// * `issue_fields` - Jira issue fields
487/// * `transition` - Jira issue transition
488/// * `assignee` - Jira issue assignee
489/// * `query` - Jira issue query
490pub struct IssueCmdParams {
491 /// Jira project key
492 pub project_key: Option<String>,
493 /// Jira issue key
494 pub issue_key: Option<String>,
495 /// Jira issue fields
496 pub issue_fields: Option<HashMap<String, Value>>,
497 /// Jira issue transition
498 pub transition: Option<String>,
499 /// Jira issue assignee
500 pub assignee: Option<String>,
501 /// Jira issue query
502 pub query: Option<String>,
503}
504
505/// Implementation of IssueCmdParams struct
506///
507/// # Methods
508///
509/// * `new` - Creates a new IssueCmdParams instance
510impl IssueCmdParams {
511 /// Creates a new IssueCmdParams instance
512 ///
513 /// # Returns
514 ///
515 /// * `IssueCmdParams` - Issue command parameters
516 ///
517 /// # Examples
518 ///
519 /// ```
520 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueCmdParams;
521 ///
522 /// let params = IssueCmdParams::new();
523 /// ```
524 pub fn new() -> IssueCmdParams {
525 IssueCmdParams {
526 project_key: Some("".to_string()),
527 issue_key: None,
528 issue_fields: None,
529 transition: None,
530 assignee: None,
531 query: None,
532 }
533 }
534}
535
536/// Implementation of From trait for IssueCmdParams struct
537/// to convert IssueArgs struct to IssueCmdParams struct
538impl From<&IssueArgs> for IssueCmdParams {
539 /// Converts IssueArgs struct to IssueCmdParams struct
540 /// to create a new IssueCmdParams instance
541 ///
542 /// # Arguments
543 ///
544 /// * `value` - IssueArgs struct
545 ///
546 /// # Returns
547 ///
548 /// * `IssueCmdParams` - Issue command parameters
549 ///
550 /// # Examples
551 ///
552 /// ```
553 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueCmdParams;
554 /// use jirust_cli::args::commands::{IssueArgs, PaginationArgs, OutputArgs, IssueActionValues};
555 /// use std::collections::HashMap;
556 /// use serde_json::Value;
557 ///
558 /// let issue_args = IssueArgs {
559 /// issue_act: IssueActionValues::Get,
560 /// project_key: Some("project_key".to_string()),
561 /// issue_key: Some("issue_key".to_string()),
562 /// issue_fields: Some(vec![("key".to_string(), r#"{ "key": "value" }"#.to_string())]),
563 /// transition_to: Some("transition_to".to_string()),
564 /// assignee: Some("assignee".to_string()),
565 /// query: None,
566 /// pagination: PaginationArgs { page_size: Some(20), page_offset: None },
567 /// output: OutputArgs { output_format: None, output_type: None },
568 /// };
569 ///
570 /// let params = IssueCmdParams::from(&issue_args);
571 ///
572 /// assert_eq!(params.project_key, Some("project_key".to_string()));
573 /// assert_eq!(params.issue_key.unwrap(), "issue_key".to_string());
574 /// assert_eq!(params.transition.unwrap(), "transition_to".to_string());
575 /// assert_eq!(params.assignee.unwrap(), "assignee".to_string());
576 /// ```
577 fn from(value: &IssueArgs) -> Self {
578 IssueCmdParams {
579 project_key: value.project_key.clone(),
580 issue_key: value.issue_key.clone(),
581 issue_fields: Some(
582 value
583 .issue_fields
584 .clone()
585 .unwrap_or_default()
586 .iter()
587 .map(|elem| {
588 (
589 elem.0.clone(),
590 serde_json::from_str(elem.1.clone().as_str()).unwrap_or(Value::Null),
591 )
592 })
593 .collect::<HashMap<_, _>>(),
594 ),
595 transition: value.transition_to.clone(),
596 assignee: value.assignee.clone(),
597 query: value.query.clone(),
598 }
599 }
600}
601
602/// Issue transition command parameters
603///
604/// # Fields
605///
606/// * `issue_key` - Jira issue key
607pub struct IssueTransitionCmdParams {
608 /// Jira issue key
609 pub issue_key: String,
610}
611
612/// Implementation of IssueTransitionCmdParams struct
613///
614/// # Methods
615///
616/// * `new` - Creates a new IssueTransitionCmdParams instance
617impl IssueTransitionCmdParams {
618 /// Creates a new IssueTransitionCmdParams instance
619 ///
620 /// # Returns
621 ///
622 /// * `IssueTransitionCmdParams` - Issue transition command parameters
623 ///
624 /// # Examples
625 ///
626 /// ```
627 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueTransitionCmdParams;
628 ///
629 /// let params = IssueTransitionCmdParams::new();
630 /// ```
631 pub fn new() -> IssueTransitionCmdParams {
632 IssueTransitionCmdParams {
633 issue_key: "".to_string(),
634 }
635 }
636}
637
638/// Implementation of From trait for IssueTransitionCmdParams struct
639/// to convert TransitionArgs struct to IssueTransitionCmdParams struct
640impl From<&TransitionArgs> for IssueTransitionCmdParams {
641 /// Converts TransitionArgs struct to IssueTransitionCmdParams struct
642 /// to create a new IssueTransitionCmdParams instance
643 ///
644 /// # Arguments
645 ///
646 /// * `value` - TransitionArgs struct
647 ///
648 /// # Returns
649 ///
650 /// * `IssueTransitionCmdParams` - Issue transition command parameters
651 ///
652 /// # Examples
653 ///
654 /// ```
655 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueTransitionCmdParams;
656 /// use jirust_cli::args::commands::{TransitionArgs, TransitionActionValues, OutputArgs};
657 ///
658 /// let transition_args = TransitionArgs {
659 /// transition_act: TransitionActionValues::List,
660 /// issue_key: "issue_key".to_string(),
661 /// output: OutputArgs { output_format: None, output_type: None },
662 /// };
663 ///
664 /// let params = IssueTransitionCmdParams::from(&transition_args);
665 ///
666 /// assert_eq!(params.issue_key, "issue_key".to_string());
667 /// ```
668 fn from(value: &TransitionArgs) -> Self {
669 IssueTransitionCmdParams {
670 issue_key: value.issue_key.clone(),
671 }
672 }
673}
674
675/// Default implementation for IssueCmdParams struct
676impl Default for IssueTransitionCmdParams {
677 /// Creates a default IssueTransitionCmdParams instance
678 ///
679 /// # Returns
680 ///
681 /// A IssueTransitionCmdParams instance with default values
682 ///
683 /// # Examples
684 ///
685 /// ```
686 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueTransitionCmdParams;
687 ///
688 /// let params = IssueTransitionCmdParams::default();
689 ///
690 /// assert_eq!(params.issue_key, "".to_string());
691 /// ```
692 fn default() -> Self {
693 IssueTransitionCmdParams::new()
694 }
695}
696
697/// Default implementation for IssueCmdParams struct
698impl Default for IssueCmdParams {
699 /// Creates a default IssueCmdParams instance
700 ///
701 /// # Returns
702 ///
703 /// A IssueCmdParams instance with default values
704 ///
705 /// # Examples
706 ///
707 /// ```
708 /// use jirust_cli::runners::jira_cmd_runners::issue_cmd_runner::IssueCmdParams;
709 ///
710 /// let params = IssueCmdParams::default();
711 ///
712 /// assert_eq!(params.project_key, Some("".to_string()));
713 /// assert_eq!(params.issue_key, None);
714 /// assert_eq!(params.issue_fields, None);
715 /// assert_eq!(params.transition, None);
716 /// assert_eq!(params.assignee, None);
717 /// ```
718 fn default() -> Self {
719 IssueCmdParams::new()
720 }
721}
722
723/// API contract for running Jira issue operations.
724#[cfg_attr(test, automock)]
725#[async_trait(?Send)]
726pub trait IssueCmdRunnerApi: Send + Sync {
727 /// Assigns a Jira issue to a user or placeholder account.
728 async fn assign_jira_issue(
729 &self,
730 params: IssueCmdParams,
731 ) -> Result<Value, Box<dyn std::error::Error>>;
732
733 /// Creates a Jira issue using the provided parameters.
734 async fn create_jira_issue(
735 &self,
736 params: IssueCmdParams,
737 ) -> Result<CreatedIssue, Box<dyn std::error::Error>>;
738
739 /// Deletes a Jira issue by key.
740 async fn delete_jira_issue(
741 &self,
742 params: IssueCmdParams,
743 ) -> Result<(), Box<dyn std::error::Error>>;
744
745 /// Retrieves a Jira issue by key.
746 async fn get_jira_issue(
747 &self,
748 params: IssueCmdParams,
749 ) -> Result<IssueBean, Box<dyn std::error::Error>>;
750
751 /// Executes a search query and returns the matching issues.
752 async fn search_jira_issues(
753 &self,
754 params: IssueCmdParams,
755 ) -> Result<Vec<IssueBean>, Box<dyn std::error::Error>>;
756
757 /// Transitions an issue to a new status.
758 async fn transition_jira_issue(
759 &self,
760 params: IssueCmdParams,
761 ) -> Result<Value, Box<dyn std::error::Error>>;
762
763 /// Updates an issue fields payload.
764 async fn update_jira_issue(
765 &self,
766 params: IssueCmdParams,
767 ) -> Result<Value, Box<dyn std::error::Error>>;
768
769 /// Fetches the available transitions for an issue.
770 async fn get_issue_available_transitions(
771 &self,
772 params: IssueTransitionCmdParams,
773 ) -> Result<Transitions, Box<dyn std::error::Error>>;
774}
775
776#[async_trait(?Send)]
777impl IssueCmdRunnerApi for IssueCmdRunner {
778 async fn assign_jira_issue(
779 &self,
780 params: IssueCmdParams,
781 ) -> Result<Value, Box<dyn std::error::Error>> {
782 IssueCmdRunner::assign_jira_issue(self, params).await
783 }
784
785 async fn create_jira_issue(
786 &self,
787 params: IssueCmdParams,
788 ) -> Result<CreatedIssue, Box<dyn std::error::Error>> {
789 IssueCmdRunner::create_jira_issue(self, params).await
790 }
791
792 async fn delete_jira_issue(
793 &self,
794 params: IssueCmdParams,
795 ) -> Result<(), Box<dyn std::error::Error>> {
796 IssueCmdRunner::delete_jira_issue(self, params).await
797 }
798
799 async fn get_jira_issue(
800 &self,
801 params: IssueCmdParams,
802 ) -> Result<IssueBean, Box<dyn std::error::Error>> {
803 IssueCmdRunner::get_jira_issue(self, params).await
804 }
805
806 async fn search_jira_issues(
807 &self,
808 params: IssueCmdParams,
809 ) -> Result<Vec<IssueBean>, Box<dyn std::error::Error>> {
810 IssueCmdRunner::search_jira_issues(self, params).await
811 }
812
813 async fn transition_jira_issue(
814 &self,
815 params: IssueCmdParams,
816 ) -> Result<Value, Box<dyn std::error::Error>> {
817 IssueCmdRunner::transition_jira_issue(self, params).await
818 }
819
820 async fn update_jira_issue(
821 &self,
822 params: IssueCmdParams,
823 ) -> Result<Value, Box<dyn std::error::Error>> {
824 IssueCmdRunner::update_jira_issue(self, params).await
825 }
826
827 async fn get_issue_available_transitions(
828 &self,
829 params: IssueTransitionCmdParams,
830 ) -> Result<Transitions, Box<dyn std::error::Error>> {
831 IssueCmdRunner::get_issue_available_transitions(self, params).await
832 }
833}