jirust_cli/runners/jira_cmd_runners/
project_cmd_runner.rs

1use std::io::Error;
2
3use crate::args::commands::ProjectArgs;
4use crate::config::config_file::{AuthData, ConfigFile};
5use jira_v3_openapi::apis::configuration::Configuration;
6use jira_v3_openapi::apis::issues_api::{
7    get_create_issue_meta_issue_type_id, get_create_issue_meta_issue_types,
8};
9use jira_v3_openapi::apis::projects_api::{create_project, search_projects};
10use jira_v3_openapi::models::create_project_details::{AssigneeType, ProjectTypeKey};
11use jira_v3_openapi::models::{CreateProjectDetails, ProjectIdentifiers};
12use jira_v3_openapi::models::{
13    FieldCreateMetadata, IssueTypeIssueCreateMetadata, project::Project,
14};
15
16/// Project command runner struct.
17///
18/// This struct is responsible for holding the project commands parameters
19/// and it is used to pass the parameters to the project commands runner.
20pub struct ProjectCmdRunner {
21    cfg: Configuration,
22}
23
24/// Project command runner implementation.
25///
26/// # Methods
27///
28/// * `new` - Creates a new instance of the ProjectCmdRunner struct.
29/// * `list_jira_projects` - Lists Jira projects.
30/// * `get_jira_project_issue_types` - Gets Jira project issue types.
31/// * `get_jira_project_issue_type_id` - Gets Jira project issue fields by issue type ID.
32impl ProjectCmdRunner {
33    /// Creates a new instance of the ProjectCmdRunner struct.
34    ///
35    /// # Arguments
36    ///
37    /// * `cfg_file` - A ConfigFile struct.
38    ///
39    /// # Returns
40    ///
41    /// * A new instance of the ProjectCmdRunner struct.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use jirust_cli::config::config_file::ConfigFile;
47    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::ProjectCmdRunner;
48    /// use toml::Table;
49    ///
50    /// let cfg_file = ConfigFile::new("dXNlcm5hbWU6YXBpX2tleQ==".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
51    ///
52    /// let project_cmd_runner = ProjectCmdRunner::new(&cfg_file);
53    /// ```
54    pub fn new(cfg_file: &ConfigFile) -> ProjectCmdRunner {
55        let mut config = Configuration::new();
56        let auth_data = AuthData::from_base64(cfg_file.get_auth_key());
57        config.base_path = cfg_file.get_jira_url().to_string();
58        config.basic_auth = Some((auth_data.0, Some(auth_data.1)));
59        ProjectCmdRunner { cfg: config }
60    }
61
62    /// Create a new Jira project using the provided parameters.
63    ///
64    /// # Arguments
65    /// * `params` - The parameters for creating the project.
66    ///
67    /// # Returns
68    /// A `Result` containing the project identifiers if successful, or an error if failed.
69    ///
70    /// # Examples
71    ///
72    /// ```no_run
73    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::{ProjectCmdRunner, ProjectCmdParams};
74    /// use jirust_cli::config::config_file::ConfigFile;
75    /// use toml::Table;
76    ///
77    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
78    /// # tokio_test::block_on(async {
79    /// let cfg_file = ConfigFile::new("auth_token".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
80    /// let project_cmd_runner = ProjectCmdRunner::new(&cfg_file);
81    ///
82    /// let mut params = ProjectCmdParams::new();
83    /// params.project_key = Some("TEST".to_string());
84    /// params.project_name = Some("Test Project".to_string());
85    /// params.project_description = Some("This is a test project".to_string());
86    /// params.project_field_configuration_id = Some(12345);
87    /// params.project_issue_security_scheme_id = Some(67890);
88    /// params.project_issue_type_scheme_id = Some(54321);
89    ///
90    /// let projects = project_cmd_runner.create_jira_project(params).await?;
91    ///
92    /// # Ok(())
93    /// # })
94    /// # }
95    /// ```
96    pub async fn create_jira_project(
97        &self,
98        params: ProjectCmdParams,
99    ) -> Result<ProjectIdentifiers, Box<dyn std::error::Error>> {
100        let p_key = params
101            .project_key
102            .as_deref()
103            .ok_or_else(|| Box::new(Error::other("Error creating project: Empty project key")))?;
104        let p_name = params
105            .project_name
106            .as_deref()
107            .ok_or_else(|| Box::new(Error::other("Error creating project: Empty project name")))?;
108
109        let mut project_data = CreateProjectDetails::new(p_key.to_string(), p_name.to_string());
110        project_data.description = params.project_description;
111        project_data.field_configuration_scheme = params.project_field_configuration_id;
112        project_data.issue_security_scheme = params.project_issue_security_scheme_id;
113        project_data.issue_type_scheme = params.project_issue_type_scheme_id;
114        project_data.issue_type_screen_scheme = params.project_issue_type_screen_scheme_id;
115        project_data.notification_scheme = params.project_notification_scheme_id;
116        project_data.permission_scheme = params.project_permission_scheme_id;
117        project_data.workflow_scheme = params.project_workflow_scheme_id;
118        project_data.lead_account_id = params.project_lead_account_id;
119        project_data.assignee_type = if params
120            .project_assignee_type
121            .unwrap_or("unassigned".to_string())
122            == "lead"
123        {
124            Some(AssigneeType::ProjectLead)
125        } else {
126            Some(AssigneeType::Unassigned)
127        };
128        project_data.project_type_key = Some(ProjectTypeKey::Software);
129        Ok(create_project(&self.cfg, project_data).await?)
130    }
131
132    /// Lists Jira projects.
133    ///
134    /// # Arguments
135    ///
136    /// * `params` - A ProjectCmdParams struct.
137    ///
138    /// # Returns
139    ///
140    /// * A Result with a vector of Project structs or an error message.
141    ///
142    /// # Examples
143    ///
144    /// ```no_run
145    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::{ProjectCmdRunner, ProjectCmdParams};
146    /// use jirust_cli::config::config_file::ConfigFile;
147    /// use toml::Table;
148    ///
149    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
150    /// # tokio_test::block_on(async {
151    /// let cfg_file = ConfigFile::new("auth_token".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
152    /// let project_cmd_runner = ProjectCmdRunner::new(&cfg_file);
153    /// let params = ProjectCmdParams::new();
154    ///
155    /// let projects = project_cmd_runner.list_jira_projects(params).await?;
156    /// # Ok(())
157    /// # })
158    /// # }
159    /// ```
160    pub async fn list_jira_projects(
161        &self,
162        params: ProjectCmdParams,
163    ) -> Result<Vec<Project>, Box<dyn std::error::Error>> {
164        let page_size = Some(params.projects_page_size.unwrap_or(10));
165        let page_offset = Some(i64::from(params.projects_page_offset.unwrap_or(0)));
166        match search_projects(
167            &self.cfg,
168            page_offset,
169            page_size,
170            None,
171            None,
172            None,
173            None,
174            None,
175            None,
176            None,
177            None,
178            None,
179            None,
180            None,
181        )
182        .await?
183        .values
184        {
185            Some(values) => Ok(values),
186            None => Ok(vec![]),
187        }
188    }
189
190    /// Gets Jira project issue types.
191    ///
192    /// # Arguments
193    ///
194    /// * `params` - A ProjectCmdParams struct.
195    ///
196    /// # Returns
197    /// * A Result with a vector of IssueTypeIssueCreateMetadata structs or an error message.
198    ///
199    /// # Examples
200    ///
201    /// ```no_run
202    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::{ProjectCmdRunner, ProjectCmdParams};
203    /// use jirust_cli::config::config_file::ConfigFile;
204    /// use toml::Table;
205    ///
206    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
207    /// # tokio_test::block_on(async {
208    /// let cfg_file = ConfigFile::new("auth_token".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
209    /// let project_cmd_runner = ProjectCmdRunner::new(&cfg_file);
210    /// let params = ProjectCmdParams::new();
211    ///
212    /// let issue_types = project_cmd_runner.get_jira_project_issue_types(params).await?;
213    /// # Ok(())
214    /// # })
215    /// # }
216    /// ```
217    pub async fn get_jira_project_issue_types(
218        &self,
219        params: ProjectCmdParams,
220    ) -> Result<Vec<IssueTypeIssueCreateMetadata>, Box<dyn std::error::Error>> {
221        let p_key = if let Some(key) = &params.project_key {
222            key.as_str()
223        } else {
224            return Err(Box::new(Error::other(
225                "Error retrieving project issue types: Empty project key".to_string(),
226            )));
227        };
228        let page_size = Some(params.projects_page_size.unwrap_or(10));
229        let page_offset = Some(params.projects_page_offset.unwrap_or(0));
230        match get_create_issue_meta_issue_types(&self.cfg, p_key, page_offset, page_size)
231            .await?
232            .issue_types
233        {
234            Some(issue_types) => Ok(issue_types),
235            None => Ok(vec![]),
236        }
237    }
238
239    /// Gets Jira project issue fields by issue type id.
240    ///
241    /// # Arguments
242    ///
243    /// * `params` - A ProjectCmdParams struct.
244    ///
245    /// # Returns
246    ///
247    /// * A Result with a vector of FieldCreateMetadata structs or an error message.
248    ///
249    /// # Examples
250    ///
251    /// ```no_run
252    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::{ProjectCmdRunner, ProjectCmdParams};
253    /// use jirust_cli::config::config_file::ConfigFile;
254    /// use toml::Table;
255    ///
256    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
257    /// # tokio_test::block_on(async {
258    /// let cfg_file = ConfigFile::new("auth_token".to_string(), "jira_url".to_string(), "standard_resolution".to_string(), "standard_resolution_comment".to_string(), Table::new());
259    /// let project_cmd_runner = ProjectCmdRunner::new(&cfg_file);
260    /// let params = ProjectCmdParams::new();
261    ///
262    /// let issue_fields = project_cmd_runner.get_jira_project_issue_type_id(params).await?;
263    /// # Ok(())
264    /// # })
265    /// # }
266    /// ```
267    pub async fn get_jira_project_issue_type_id(
268        &self,
269        params: ProjectCmdParams,
270    ) -> Result<Vec<FieldCreateMetadata>, Box<dyn std::error::Error>> {
271        let p_key = if let Some(key) = &params.project_key {
272            key.as_str()
273        } else {
274            return Err(Box::new(Error::other(
275                "Error retrieving project issue types ids: Empty project key".to_string(),
276            )));
277        };
278        let issue_type = if let Some(key) = &params.project_issue_type {
279            key.as_str()
280        } else {
281            return Err(Box::new(Error::other(
282                "Error retrieving project issue types ids: Empty project issue type key"
283                    .to_string(),
284            )));
285        };
286        let page_size = Some(params.projects_page_size.unwrap_or(10));
287        let page_offset = Some(params.projects_page_offset.unwrap_or(0));
288        match get_create_issue_meta_issue_type_id(
289            &self.cfg,
290            p_key,
291            issue_type,
292            page_offset,
293            page_size,
294        )
295        .await?
296        .fields
297        {
298            Some(id) => Ok(id),
299            None => Ok(vec![]),
300        }
301    }
302}
303
304/// This struct defines the parameters for the Project commands
305///
306/// # Fields
307///
308/// * `project_key` - The project key, **required** for get project issue types and issue fields commands.
309/// * `project_issue_type` - The project issue type, **required** for get issue fields command.
310/// * `projects_page_size` - The page size for the project command, optional.
311/// * `projects_page_offset` - The page offset for the project command, optional.
312pub struct ProjectCmdParams {
313    pub project_key: Option<String>,
314    pub project_issue_type: Option<String>,
315    pub project_name: Option<String>,
316    pub project_description: Option<String>,
317    pub project_field_configuration_id: Option<i64>,
318    pub project_issue_security_scheme_id: Option<i64>,
319    pub project_issue_type_scheme_id: Option<i64>,
320    pub project_issue_type_screen_scheme_id: Option<i64>,
321    pub project_notification_scheme_id: Option<i64>,
322    pub project_permission_scheme_id: Option<i64>,
323    pub project_workflow_scheme_id: Option<i64>,
324    pub project_lead_account_id: Option<String>,
325    pub project_assignee_type: Option<String>,
326    pub projects_page_size: Option<i32>,
327    pub projects_page_offset: Option<i32>,
328}
329
330/// Implementation of the ProjectCmdParams struct
331///
332/// # Methods
333///
334/// * `new` - Creates a new ProjectCmdParams struct.
335///
336impl ProjectCmdParams {
337    /// Creates a new ProjectCmdParams struct instance.
338    ///
339    /// # Returns
340    ///
341    /// * A ProjectCmdParams struct instance.
342    ///
343    /// # Examples
344    ///
345    /// ```
346    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::ProjectCmdParams;
347    ///
348    /// let params = ProjectCmdParams::new();
349    /// ```
350    pub fn new() -> ProjectCmdParams {
351        ProjectCmdParams {
352            project_key: None,
353            project_issue_type: None,
354            project_name: None,
355            project_description: None,
356            project_field_configuration_id: None,
357            project_issue_security_scheme_id: None,
358            project_issue_type_scheme_id: None,
359            project_issue_type_screen_scheme_id: None,
360            project_notification_scheme_id: None,
361            project_permission_scheme_id: None,
362            project_workflow_scheme_id: None,
363            project_lead_account_id: None,
364            project_assignee_type: None,
365            projects_page_size: None,
366            projects_page_offset: None,
367        }
368    }
369}
370
371/// Implementation of the From trait for the ProjectCmdParams struct
372/// to convert from ProjectArgs to ProjectCmdParams.
373impl From<&ProjectArgs> for ProjectCmdParams {
374    /// Converts from ProjectArgs to ProjectCmdParams.
375    ///
376    /// # Arguments
377    ///
378    /// * `value` - A ProjectArgs struct.
379    ///
380    /// # Returns
381    ///
382    /// * A ProjectCmdParams struct instance.
383    ///
384    /// # Examples
385    ///
386    /// ```
387    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::ProjectCmdParams;
388    /// use jirust_cli::args::commands::{ProjectArgs, ProjectActionValues, PaginationArgs, OutputArgs};
389    ///
390    /// let project_args = ProjectArgs {
391    ///     project_act: ProjectActionValues::GetIssueTypeFields,
392    ///     project_key: Some("project_key".to_string()),
393    ///     project_issue_type: Some("project_issue_type".to_string()),
394    ///     project_name: None,
395    ///     project_description: None,
396    ///     project_field_configuration_id: None,
397    ///     project_issue_security_scheme_id: None,
398    ///     project_issue_type_scheme_id: None,
399    ///     project_issue_type_screen_scheme_id: None,
400    ///     project_notification_scheme_id: None,
401    ///     project_permission_scheme_id: None,
402    ///     project_workflow_scheme_id: None,
403    ///     project_lead_account_id: None,
404    ///     project_assignee_type: None,
405    ///     pagination: PaginationArgs { page_size: Some(10), page_offset: None },
406    ///     output: OutputArgs { output_format: None, output_type: None },
407    /// };
408    ///
409    /// let params = ProjectCmdParams::from(&project_args);
410    ///
411    /// assert_eq!(params.project_key, Some("project_key".to_string()));
412    /// assert_eq!(params.project_issue_type, Some("project_issue_type".to_string()));
413    /// assert_eq!(params.projects_page_size, Some(10));
414    /// assert_eq!(params.projects_page_offset, Some(0));
415    /// ```
416    fn from(value: &ProjectArgs) -> Self {
417        ProjectCmdParams {
418            project_key: value.project_key.clone(),
419            project_issue_type: value.project_issue_type.clone(),
420            project_name: value.project_name.clone(),
421            project_description: value.project_description.clone(),
422            project_field_configuration_id: value.project_field_configuration_id,
423            project_issue_security_scheme_id: value.project_issue_security_scheme_id,
424            project_issue_type_scheme_id: value.project_issue_type_scheme_id,
425            project_issue_type_screen_scheme_id: value.project_issue_type_screen_scheme_id,
426            project_notification_scheme_id: value.project_notification_scheme_id,
427            project_permission_scheme_id: value.project_permission_scheme_id,
428            project_workflow_scheme_id: value.project_workflow_scheme_id,
429            project_lead_account_id: value.project_lead_account_id.clone(),
430            project_assignee_type: value.project_assignee_type.clone(),
431            projects_page_size: value.pagination.page_size,
432            projects_page_offset: Some(
433                i32::try_from(value.pagination.page_offset.unwrap_or(0))
434                    .expect("Invalid page offset, should fit an i32!"),
435            ),
436        }
437    }
438}
439
440/// Implementation of the Default trait for the ProjectCmdParams struct
441impl Default for ProjectCmdParams {
442    /// Creates a default ProjectCmdParams struct instance.
443    ///
444    /// # Returns
445    ///
446    /// * A ProjectCmdParams struct instance with default values.
447    ///
448    /// # Examples
449    ///
450    /// ```
451    /// use jirust_cli::runners::jira_cmd_runners::project_cmd_runner::ProjectCmdParams;
452    ///
453    /// let params = ProjectCmdParams::default();
454    ///
455    /// assert_eq!(params.project_key, None);
456    /// assert_eq!(params.project_issue_type, None);
457    /// assert_eq!(params.projects_page_size, None);
458    /// assert_eq!(params.projects_page_offset, None);
459    /// ```
460    fn default() -> Self {
461        ProjectCmdParams::new()
462    }
463}