Move issue creation calls into the jira client
This commit is contained in:
parent
8a7c989f48
commit
194e25dc14
14 changed files with 280 additions and 180 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -941,7 +941,6 @@ dependencies = [
|
||||||
"gray_matter",
|
"gray_matter",
|
||||||
"libjirac",
|
"libjirac",
|
||||||
"open",
|
"open",
|
||||||
"reqwest",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tabwriter",
|
"tabwriter",
|
||||||
|
@ -985,6 +984,7 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
||||||
libjirac = { path = "../libjirac" }
|
libjirac = { path = "../libjirac" }
|
||||||
|
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use crate::jira_config::JiraConfig;
|
use crate::jira_config::JiraConfig;
|
||||||
use gray_matter::engine::TOML;
|
use gray_matter::engine::TOML;
|
||||||
use gray_matter::Matter;
|
use gray_matter::Matter;
|
||||||
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
|
use libjirac::client::commands::{
|
||||||
|
IssueCreateCommand, IssueTransitionsCommand, IssueTransitionsUpdateCommand,
|
||||||
|
};
|
||||||
|
use libjirac::client::JiraClient;
|
||||||
|
use libjirac::entities::issue_request::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
@ -23,84 +27,6 @@ struct IssueMetadata {
|
||||||
extra: HashMap<String, serde_json::Value>,
|
extra: HashMap<String, serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
struct JiraIssueRequest {
|
|
||||||
fields: JiraIssueFields,
|
|
||||||
update: Option<JiraIssueUpdate>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct JiraIssueFields {
|
|
||||||
project: Project,
|
|
||||||
summary: String,
|
|
||||||
description: String,
|
|
||||||
issuetype: IssueType,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
assignee: Option<Assignee>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
status: Option<Status>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
struct JiraIssueUpdate {
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
parent: Option<Vec<ParentUpdate>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
struct ParentUpdate {
|
|
||||||
add: Parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
struct Parent {
|
|
||||||
key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct Assignee {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct Project {
|
|
||||||
key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct IssueType {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct JiraResponse {
|
|
||||||
key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct JiraSearchResponse {
|
|
||||||
issues: Vec<JiraIssue>,
|
|
||||||
total: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct JiraIssue {
|
|
||||||
key: String,
|
|
||||||
fields: JiraIssueResponseFields,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct JiraIssueResponseFields {
|
|
||||||
summary: String,
|
|
||||||
status: Status,
|
|
||||||
created: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct Status {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_issue_template(
|
fn get_issue_template(
|
||||||
issue_metadata: &IssueMetadata,
|
issue_metadata: &IssueMetadata,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
@ -216,37 +142,27 @@ async fn create_jira_issue(
|
||||||
title: &str,
|
title: &str,
|
||||||
description: &str,
|
description: &str,
|
||||||
metadata: &IssueMetadata,
|
metadata: &IssueMetadata,
|
||||||
) -> Result<JiraResponse, Box<dyn std::error::Error>> {
|
) -> Result<IssueCreateResponse, Box<dyn std::error::Error>> {
|
||||||
let client = reqwest::Client::new();
|
let client = JiraClient::from(config);
|
||||||
|
|
||||||
let mut headers = HeaderMap::new();
|
let fields = IssueFields {
|
||||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
|
||||||
|
|
||||||
let mut fields = JiraIssueFields {
|
|
||||||
project: Project {
|
project: Project {
|
||||||
key: project_key.to_string(),
|
key: project_key.to_string(),
|
||||||
},
|
},
|
||||||
summary: title.to_string(),
|
summary: title.to_string(),
|
||||||
description: description.to_string(),
|
description: description.to_string(),
|
||||||
issuetype: IssueType {
|
issue_type: IssueType {
|
||||||
name: "Task".to_string(),
|
name: "Task".to_string(),
|
||||||
},
|
},
|
||||||
assignee: None,
|
assignee: None,
|
||||||
status: None,
|
status: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add assignee if specified
|
|
||||||
if let Some(assignee) = &metadata.assignee {
|
|
||||||
fields.assignee = Some(Assignee {
|
|
||||||
name: assignee.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut update = None;
|
let mut update = None;
|
||||||
|
|
||||||
// Add parent if specified
|
// Add parent if specified
|
||||||
if let Some(parent_key) = &metadata.parent {
|
if let Some(parent_key) = &metadata.parent {
|
||||||
update = Some(JiraIssueUpdate {
|
update = Some(IssueUpdate {
|
||||||
parent: Some(vec![ParentUpdate {
|
parent: Some(vec![ParentUpdate {
|
||||||
add: Parent {
|
add: Parent {
|
||||||
key: parent_key.clone(),
|
key: parent_key.clone(),
|
||||||
|
@ -255,33 +171,24 @@ async fn create_jira_issue(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let issue = JiraIssueRequest { fields, update };
|
let issue = IssueCreateRequest { fields, update };
|
||||||
|
|
||||||
// println!("{:#?}", issue);
|
let create_cmd = IssueCreateCommand::new(issue);
|
||||||
// Ok(JiraResponse {
|
let issue_response = client.exec(create_cmd).await?;
|
||||||
// key: format!("{}-1234", project_key),
|
|
||||||
// })
|
|
||||||
|
|
||||||
let response = client
|
|
||||||
.post(format!("{}/rest/api/2/issue", config.url))
|
|
||||||
.basic_auth(&config.email, Some(&config.api_token))
|
|
||||||
.headers(headers)
|
|
||||||
.json(&issue)
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
let error_text = response.text().await?;
|
|
||||||
return Err(format!("Failed to create issue: {}", error_text).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let issue_response = response.json::<JiraResponse>().await?;
|
|
||||||
|
|
||||||
// Update status if specified (requires a separate API call)
|
// Update status if specified (requires a separate API call)
|
||||||
if let Some(status) = &metadata.status {
|
if let Some(status) = &metadata.status {
|
||||||
update_issue_status(config, &issue_response.key, status).await?;
|
update_issue_status(config, &issue_response.key, status).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update assignee if specified
|
||||||
|
if let Some(_assignee) = &metadata.assignee {
|
||||||
|
// Need to look up users by username, because the api wants an account id
|
||||||
|
// fields.assignee = Some(Assignee {
|
||||||
|
// id: assignee.clone(),
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
Ok(issue_response)
|
Ok(issue_response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,52 +197,29 @@ async fn update_issue_status(
|
||||||
issue_key: &str,
|
issue_key: &str,
|
||||||
status: &str,
|
status: &str,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let client = reqwest::Client::new();
|
let client = JiraClient::from(config);
|
||||||
|
|
||||||
let transitions = client
|
let transitions_cmd = IssueTransitionsCommand::new(issue_key);
|
||||||
.get(format!(
|
let transitions = client.exec(transitions_cmd).await?;
|
||||||
"{}/rest/api/2/issue/{}/transitions",
|
|
||||||
config.url, issue_key
|
|
||||||
))
|
|
||||||
.basic_auth(&config.email, Some(&config.api_token))
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json::<serde_json::Value>()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Find the transition ID for the desired status
|
// Find the transition ID for the desired status
|
||||||
let transition_id = transitions["transitions"]
|
let transition_id = transitions
|
||||||
.as_array()
|
.transitions
|
||||||
.and_then(|t| {
|
.iter()
|
||||||
t.iter().find(|t| {
|
.find_map(|t| {
|
||||||
t["to"]["name"]
|
if t.to.name.eq_ignore_ascii_case(status) {
|
||||||
.as_str()
|
Some(t.id.clone())
|
||||||
.map_or(false, |s| s.eq_ignore_ascii_case(status))
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
.and_then(|t| t["id"].as_str())
|
|
||||||
.ok_or_else(|| format!("No transition found for status: {}", status))?;
|
.ok_or_else(|| format!("No transition found for status: {}", status))?;
|
||||||
|
|
||||||
// Perform the transition
|
let transition_cmd = IssueTransitionsUpdateCommand::new(issue_key, &transition_id);
|
||||||
let transition_payload = serde_json::json!({
|
match client.exec(transition_cmd).await {
|
||||||
"transition": { "id": transition_id }
|
Ok(x) => Ok(x),
|
||||||
});
|
Err(reason) => Err(reason.into()),
|
||||||
|
|
||||||
let response = client
|
|
||||||
.post(format!(
|
|
||||||
"{}/rest/api/2/issue/{}/transitions",
|
|
||||||
config.url, issue_key
|
|
||||||
))
|
|
||||||
.basic_auth(&config.email, Some(&config.api_token))
|
|
||||||
.json(&transition_payload)
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
return Err(format!("Failed to update status: {}", response.text().await?).into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
|
|
|
@ -85,7 +85,7 @@ pub async fn exec(output: FormatMode, jql: &str) -> Result<(), Box<dyn std::erro
|
||||||
|
|
||||||
let client = JiraClient::from(&config);
|
let client = JiraClient::from(&config);
|
||||||
|
|
||||||
let jql_cmd = SearchCommand::new(&jql);
|
let jql_cmd = SearchCommand::new(jql);
|
||||||
let result = client.exec(jql_cmd).await?;
|
let result = client.exec(jql_cmd).await?;
|
||||||
|
|
||||||
match (output, result.issues.is_empty()) {
|
match (output, result.issues.is_empty()) {
|
||||||
|
|
|
@ -7,28 +7,6 @@ use libjirac::client::JiraClient;
|
||||||
use libjirac::entities::issue::JiraIssue;
|
use libjirac::entities::issue::JiraIssue;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
async fn fetch_issue(
|
|
||||||
config: &JiraConfig,
|
|
||||||
href: &str,
|
|
||||||
) -> Result<JiraIssue, Box<dyn std::error::Error>> {
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
|
|
||||||
let response = client
|
|
||||||
.get(href)
|
|
||||||
.basic_auth(&config.email, Some(&config.api_token))
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
let error_text = response.text().await?;
|
|
||||||
return Err(format!("Failed to fetch issue: {}", error_text).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let issue_response = response.json::<JiraIssue>().await?;
|
|
||||||
|
|
||||||
Ok(issue_response)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pretty_print(issue: &JiraIssue) -> Result<(), Box<dyn std::error::Error>> {
|
fn pretty_print(issue: &JiraIssue) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("\n== Title {:=<71}", "");
|
println!("\n== Title {:=<71}", "");
|
||||||
println!(
|
println!(
|
||||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
http = { workspace = true }
|
http = { workspace = true }
|
||||||
|
|
|
@ -47,6 +47,8 @@ pub enum JiraClientError {
|
||||||
UrlParseError(#[from] ParseError),
|
UrlParseError(#[from] ParseError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ReqwestError(#[from] reqwest::Error),
|
ReqwestError(#[from] reqwest::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
SerdeError(#[from] serde_json::Error),
|
||||||
#[error("API Error: {0}")]
|
#[error("API Error: {0}")]
|
||||||
ApiError(String),
|
ApiError(String),
|
||||||
}
|
}
|
||||||
|
@ -97,9 +99,19 @@ impl JiraClient {
|
||||||
return Err(JiraClientError::ApiError(error_text));
|
return Err(JiraClientError::ApiError(error_text));
|
||||||
}
|
}
|
||||||
|
|
||||||
match response.json::<TCommand::TResponse>().await {
|
let plain = match response.bytes().await {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(reason) => return Err(JiraClientError::ReqwestError(reason)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = match is_unit::<TCommand::TResponse>() {
|
||||||
|
true => b"null",
|
||||||
|
false => plain.as_ref(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match serde_json::from_slice::<TCommand::TResponse>(value) {
|
||||||
Ok(x) => Ok(x),
|
Ok(x) => Ok(x),
|
||||||
Err(reason) => Err(JiraClientError::ReqwestError(reason)),
|
Err(reason) => Err(JiraClientError::SerdeError(reason)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,3 +128,28 @@ impl JiraClient {
|
||||||
Ok(url.to_string())
|
Ok(url.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn is_unit<T>() -> bool {
|
||||||
|
size_of::<T>() == 0 && align_of::<T>() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_unit_type() {
|
||||||
|
assert!(is_unit::<()>());
|
||||||
|
assert!(!is_unit::<String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_unit_type() {
|
||||||
|
let value = match is_unit::<()>() {
|
||||||
|
true => b"null".as_ref(),
|
||||||
|
false => b"{}".as_ref(),
|
||||||
|
};
|
||||||
|
|
||||||
|
serde_json::from_slice::<()>(value).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
|
mod issue_create_command;
|
||||||
|
mod issue_transitions_command;
|
||||||
|
mod issue_transitions_update_command;
|
||||||
mod search_command;
|
mod search_command;
|
||||||
mod self_command;
|
mod self_command;
|
||||||
|
|
||||||
|
pub use issue_create_command::IssueCreateCommand;
|
||||||
|
pub use issue_transitions_command::IssueTransitionsCommand;
|
||||||
|
pub use issue_transitions_update_command::IssueTransitionsUpdateCommand;
|
||||||
pub use search_command::SearchCommand;
|
pub use search_command::SearchCommand;
|
||||||
pub use self_command::SelfCommand;
|
pub use self_command::SelfCommand;
|
||||||
|
|
31
crates/libjirac/src/client/commands/issue_create_command.rs
Normal file
31
crates/libjirac/src/client/commands/issue_create_command.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::client::{JiraCommand, JiraRequestType};
|
||||||
|
use crate::entities::issue_request::{IssueCreateRequest, IssueCreateResponse};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IssueCreateCommand {
|
||||||
|
issue: IssueCreateRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IssueCreateCommand {
|
||||||
|
pub fn new(issue: IssueCreateRequest) -> Self {
|
||||||
|
Self { issue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JiraCommand for IssueCreateCommand {
|
||||||
|
type TResponse = IssueCreateResponse;
|
||||||
|
type TPayload = IssueCreateRequest;
|
||||||
|
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Create;
|
||||||
|
|
||||||
|
fn endpoint(&self) -> String {
|
||||||
|
"/rest/api/2/issue".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_body(&self) -> Option<&Self::TPayload> {
|
||||||
|
Some(&self.issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_params(&self) -> Option<Vec<(String, String)>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::client::{JiraCommand, JiraRequestType};
|
||||||
|
use crate::entities::transitions::Transitions;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IssueTransitionsCommand {
|
||||||
|
issue_key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IssueTransitionsCommand {
|
||||||
|
pub fn new(issue_key: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
issue_key: issue_key.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JiraCommand for IssueTransitionsCommand {
|
||||||
|
type TResponse = Transitions;
|
||||||
|
type TPayload = ();
|
||||||
|
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Read;
|
||||||
|
|
||||||
|
fn endpoint(&self) -> String {
|
||||||
|
format!("/rest/api/3/issue/{}/transitions", self.issue_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_body(&self) -> Option<&Self::TPayload> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_params(&self) -> Option<Vec<(String, String)>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
use crate::client::{JiraCommand, JiraRequestType};
|
||||||
|
use crate::entities::transitions::{IssueTransitionUpdatePayload, IssueTransitionUpdatePayloadId};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IssueTransitionsUpdateCommand {
|
||||||
|
pub issue_key: String,
|
||||||
|
pub transition: IssueTransitionUpdatePayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IssueTransitionsUpdateCommand {
|
||||||
|
pub fn new(issue_key: &str, transition_id: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
issue_key: issue_key.to_string(),
|
||||||
|
transition: IssueTransitionUpdatePayload {
|
||||||
|
transition: IssueTransitionUpdatePayloadId {
|
||||||
|
id: transition_id.to_string(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JiraCommand for IssueTransitionsUpdateCommand {
|
||||||
|
type TResponse = ();
|
||||||
|
type TPayload = IssueTransitionUpdatePayload;
|
||||||
|
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Create;
|
||||||
|
|
||||||
|
fn endpoint(&self) -> String {
|
||||||
|
format!("/rest/api/3/issue/{}/transitions", self.issue_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_body(&self) -> Option<&Self::TPayload> {
|
||||||
|
Some(&self.transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_params(&self) -> Option<Vec<(String, String)>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,4 @@
|
||||||
pub mod issue;
|
pub mod issue;
|
||||||
|
pub mod issue_request;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
|
pub mod transitions;
|
||||||
|
|
61
crates/libjirac/src/entities/issue_request.rs
Normal file
61
crates/libjirac/src/entities/issue_request.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct IssueCreateResponse {
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct IssueCreateRequest {
|
||||||
|
pub fields: IssueFields,
|
||||||
|
pub update: Option<IssueUpdate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct IssueUpdate {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parent: Option<Vec<ParentUpdate>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct ParentUpdate {
|
||||||
|
pub add: Parent,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Parent {
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct IssueFields {
|
||||||
|
pub project: Project,
|
||||||
|
pub summary: String,
|
||||||
|
pub description: String,
|
||||||
|
#[serde(rename = "issuetype")]
|
||||||
|
pub issue_type: IssueType,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub assignee: Option<Assignee>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub status: Option<Status>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Assignee {
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Project {
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct IssueType {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Status {
|
||||||
|
pub name: String,
|
||||||
|
}
|
29
crates/libjirac/src/entities/transitions.rs
Normal file
29
crates/libjirac/src/entities/transitions.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Transitions {
|
||||||
|
pub transitions: Vec<Transition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Transition {
|
||||||
|
pub id: String,
|
||||||
|
pub to: TransitionTarget,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct TransitionTarget {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct IssueTransitionUpdatePayload {
|
||||||
|
pub transition: IssueTransitionUpdatePayloadId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct IssueTransitionUpdatePayloadId {
|
||||||
|
pub id: String,
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue