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 ¶ms.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}