Move issue creation calls into the jira client

This commit is contained in:
Daan Boerlage 2025-01-22 01:27:00 +01:00
parent 8a7c989f48
commit 194e25dc14
Signed by: daan
GPG key ID: FCE070E1E4956606
14 changed files with 280 additions and 180 deletions

View file

@ -5,6 +5,7 @@ edition = "2021"
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
chrono = { workspace = true }
reqwest = { workspace = true }
http = { workspace = true }

View file

@ -47,6 +47,8 @@ pub enum JiraClientError {
UrlParseError(#[from] ParseError),
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
#[error(transparent)]
SerdeError(#[from] serde_json::Error),
#[error("API Error: {0}")]
ApiError(String),
}
@ -97,9 +99,19 @@ impl JiraClient {
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),
Err(reason) => Err(JiraClientError::ReqwestError(reason)),
Err(reason) => Err(JiraClientError::SerdeError(reason)),
}
}
@ -116,3 +128,28 @@ impl JiraClient {
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();
}
}

View file

@ -1,5 +1,11 @@
mod issue_create_command;
mod issue_transitions_command;
mod issue_transitions_update_command;
mod search_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 self_command::SelfCommand;

View 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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -1,2 +1,4 @@
pub mod issue;
pub mod issue_request;
pub mod search;
pub mod transitions;

View 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,
}

View 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,
}