119 lines
3.1 KiB
Rust
119 lines
3.1 KiB
Rust
|
pub mod commands;
|
||
|
|
||
|
use http::header::CONTENT_TYPE;
|
||
|
use http::{HeaderMap, HeaderValue};
|
||
|
use serde::de::DeserializeOwned;
|
||
|
use serde::Serialize;
|
||
|
use std::fmt::Debug;
|
||
|
use thiserror::Error;
|
||
|
use url::{ParseError, Url};
|
||
|
|
||
|
pub struct JiraClient {
|
||
|
pub url: String,
|
||
|
pub email: String,
|
||
|
pub api_token: String,
|
||
|
}
|
||
|
|
||
|
pub trait JiraCommand {
|
||
|
type TResponse: DeserializeOwned + Clone;
|
||
|
type TPayload: Serialize;
|
||
|
const REQUEST_TYPE: JiraRequestType;
|
||
|
fn endpoint(&self) -> String;
|
||
|
fn request_body(&self) -> Option<&Self::TPayload>;
|
||
|
fn query_params(&self) -> Option<Vec<(String, String)>>;
|
||
|
}
|
||
|
|
||
|
pub enum JiraRequestType {
|
||
|
Read,
|
||
|
Create,
|
||
|
Update,
|
||
|
Delete,
|
||
|
}
|
||
|
|
||
|
impl From<JiraRequestType> for http::Method {
|
||
|
fn from(value: JiraRequestType) -> Self {
|
||
|
match value {
|
||
|
JiraRequestType::Read => Self::GET,
|
||
|
JiraRequestType::Create => Self::POST,
|
||
|
JiraRequestType::Update => Self::PATCH,
|
||
|
JiraRequestType::Delete => Self::DELETE,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Error)]
|
||
|
pub enum JiraClientError {
|
||
|
#[error(transparent)]
|
||
|
UrlParseError(#[from] ParseError),
|
||
|
#[error(transparent)]
|
||
|
ReqwestError(#[from] reqwest::Error),
|
||
|
#[error("API Error: {0}")]
|
||
|
ApiError(String),
|
||
|
}
|
||
|
|
||
|
impl JiraClient {
|
||
|
pub fn new(url: &str, email: &str, api_token: &str) -> Self {
|
||
|
Self {
|
||
|
url: url.to_string(),
|
||
|
email: email.to_string(),
|
||
|
api_token: api_token.to_string(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub async fn exec<TCommand>(
|
||
|
&self,
|
||
|
cmd: TCommand,
|
||
|
) -> Result<TCommand::TResponse, JiraClientError>
|
||
|
where
|
||
|
TCommand: JiraCommand + Debug,
|
||
|
{
|
||
|
let client = reqwest::Client::new();
|
||
|
|
||
|
let mut headers = HeaderMap::new();
|
||
|
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||
|
|
||
|
let url = self.construct_url(&cmd.endpoint())?;
|
||
|
|
||
|
let mut request_builder = client
|
||
|
.request(TCommand::REQUEST_TYPE.into(), url)
|
||
|
.basic_auth(&self.email, Some(&self.api_token))
|
||
|
.headers(headers);
|
||
|
|
||
|
if let Some(params) = cmd.query_params() {
|
||
|
request_builder = request_builder.query(¶ms);
|
||
|
}
|
||
|
|
||
|
if let Some(body) = cmd.request_body() {
|
||
|
request_builder = request_builder.json(body);
|
||
|
}
|
||
|
|
||
|
let response = match request_builder.send().await {
|
||
|
Ok(x) => x,
|
||
|
Err(reason) => return Err(JiraClientError::ReqwestError(reason)),
|
||
|
};
|
||
|
|
||
|
if !response.status().is_success() {
|
||
|
let error_text = response.text().await?;
|
||
|
return Err(JiraClientError::ApiError(error_text));
|
||
|
}
|
||
|
|
||
|
match response.json::<TCommand::TResponse>().await {
|
||
|
Ok(x) => Ok(x),
|
||
|
Err(reason) => Err(JiraClientError::ReqwestError(reason)),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn construct_url(&self, endpoint: &str) -> Result<String, JiraClientError> {
|
||
|
let mut url = match Url::parse(&self.url) {
|
||
|
Ok(x) => x,
|
||
|
Err(reason) => {
|
||
|
return Err(JiraClientError::UrlParseError(reason));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
url.set_path(endpoint);
|
||
|
|
||
|
Ok(url.to_string())
|
||
|
}
|
||
|
}
|