base template for IssueTrak and outgoing Jira client, some neutral work

master
Griffiths Lott 3 years ago
commit 822e4a9a7f
  1. 4
      .gitignore
  2. 1609
      Cargo.lock
  3. 12
      Cargo.toml
  4. 51
      src/issuetrak/outgoing.rs
  5. 1
      src/jira/mod.rs
  6. 26
      src/jira/outgoing.rs
  7. 10
      src/main.rs
  8. 43
      src/neutral.rs
  9. 25
      src/tests.rs

4
.gitignore vendored

@ -0,0 +1,4 @@
/target
*.json
*.env
*.sh

1609
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,12 @@
[package]
name = "it2jira"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-web = "4.2.1"
reqwest = {version = "0.11.11", features = ["blocking"]}
serde_json = "1.0.85"
futures = "0.3.24"

@ -0,0 +1,51 @@
struct IssueTrakClient<'a> {
api_key : &'a str,
req_client : reqwest::blocking::Client,
base_url : &'a str
}
impl<'a> IssueTrakClient<'a> {
pub fn new(api_key: &'a str, base_url: &'a str) -> Self {
IssueTrakClient {
api_key : api_key,
req_client : reqwest::blocking::Client::new(),
base_url : base_url
}
}
fn gen_hmac(&self, header_data: String) -> String {
let mut mac = HmacSha512::new_from_slice(self.api_key.as_bytes()).unwrap();
mac.update(header_data.as_bytes());
let res: Vec<u8> = mac.finalize().into_bytes().iter().map(|b| *b).collect();
base64::encode(res)
}
fn build_headers(&self, req_type: &str, endpoint_path: &str, body_str: &str) -> HeaderMap {
let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true);
let guid = Uuid::new_v4().to_string();
let auth_str = format!("{}\n{}\n{}\n{}\n{}\n{}",req_type, &guid, &timestamp, endpoint_path, "",body_str);
println!("auth_str = {:?}", auth_str);
let auth_hash = self.gen_hmac(auth_str);
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/json".parse().unwrap());
headers.insert("Content-Type", "application/json; charset=utf-8".parse().unwrap());
headers.insert("Accept-Language", "en-US".parse().unwrap());
headers.insert("X-IssueTrak-API-Request-ID", guid.parse().unwrap());
headers.insert("X-IssueTrak-API-Timestamp", timestamp.parse().unwrap());
headers.insert("X-IssueTrak-API-Authorization", auth_hash.parse().unwrap());
println!("headers = {:?}\n\n", headers);
headers
}
pub fn issue_by_number(&self, issue_number: i32) {
let endpoint_suffix = format!("api/v1/issues/true/{issue_number}");
let full_endpoint = format!("{}api/v1/issues/true/{}", self.base_url, issue_number);
println!("full_endpoint = {:?}\n", full_endpoint);
let header_data = self.build_headers("GET", &endpoint_suffix,"");
let res = self.req_client.get(full_endpoint)
.headers(header_data)
.send();
println!("res = {:?}", res);
}

@ -0,0 +1 @@
pub mod outgoing;

@ -0,0 +1,26 @@
use reqwest::{Client,Url};
use reqwest::header::{HeaderMap, HeaderValue};
use futures::future;
pub struct JiraClient<'a> {
http_client: Client,
basic_auth: (&'a str, &'a str),
base_url: &'a str
}
impl<'a> JiraClient<'a> {
pub fn new(base_url: &'a str, api_key: &'a str, user: &'a str) -> Self {
let mut headers = HeaderMap::new();
headers.insert("Accept", HeaderValue::from_static("application/json"));
let client = Client::new();
JiraClient { http_client: client, basic_auth: (user, api_key), base_url: base_url }
}
pub async fn get_issue(&self, issue_id: String) -> Result<serde_json::Value, Box<dyn std::error::Error>>{
let endpoint = format!("{}/rest/api/3/issue/{}",self.base_url , issue_id);
let resp = self.http_client.get(endpoint)
.basic_auth(self.basic_auth.0, Some(self.basic_auth.1))
.send().await?.text().await?;
let js: serde_json::Value = serde_json::from_str(&resp)?;
Ok(js)
}
}

@ -0,0 +1,10 @@
mod jira;
use crate::jira::outgoing;
use reqwest::{Client, ClientBuilder};
mod neutral;
mod tests;
fn main() {
println!("Hello, world!");
}

@ -0,0 +1,43 @@
use reqwest;
use serde_json::Value;
#[derive(Debug)]
pub struct TicketData {
class : String, // Type in Jira
status : String,
priority : String,
subject : String, // Summary in Jira
issue_trak_num : String,
issue_trak_link: String,
assignee : String, // Next Action in IssueTrak
comment : String, // Note in IssueTrak
// Not yet implemented Solution : String
}
impl TicketData {
pub fn from_jira_data(json_value: Value) -> Result<Self, Box<dyn std::error::Error>>{
// All of the data we need is under the fields key
let json_fields = match json_value.get("fields") {
Some(f) => {f.clone()},
_ => { return Err("No fields")?}
};
/*
Handling Jira comments is going to a nightmare. This will be implemented later
*/
// Believe it or not this is easier than dealing with creating structs for the reposonse
// if the fields are blank 'null' will be returned as a string
Ok(TicketData {
class : json_fields.get("issuetype").unwrap().get("name").unwrap().to_string(),
status: json_fields.get("status").unwrap().get("name").unwrap().to_string(),
priority: json_fields.get("priority").unwrap().get("name").unwrap().to_string(),
subject: json_fields.get("summary").unwrap().to_string(),
issue_trak_num: json_fields.get("customfield_10065").unwrap().to_string(),
issue_trak_link: json_fields.get("customfield_10060").unwrap().to_string(),
assignee: json_fields.get("assignee").unwrap().get("displayName").unwrap().to_string(),
comment: "Test".to_string()//json_fields.get("comment").unwrap().get("displayName").unwrap().to_string(),
//solution: json_fields.get("solu").unwrap().get("displayName").unwrap().to_string()
})
}
}

@ -0,0 +1,25 @@
#[cfg(test)]
mod test {
use actix_web::test;
use serde_json;
use crate::jira::outgoing;
use crate::neutral::TicketData;
use dotenv:dotenv;
use std::env::var;
#[actix_web::test]
async fn get_ticket_data_from_jira() {
let jcl = outgoing::JiraClient::new(var("JIRA_INSTANCE").except("No JIRA_INSTANCE configured in .env"), var("JIRA_USER_PW").except("No JIRA_USER_PW configured in .env"), var("JIRA_USER").except("No JIRA_USER configured in .env"));
let jit_project = "LEAF-511";
println!("Testing Jira issue = {:?}", jit_project);
let res: Result<serde_json::Value, Box<dyn std::error::Error>> = jcl.get_issue(jit_project.to_string()).await;
if !res.is_ok() {
println!("res = {:?}", res);
assert!(res.is_ok())
}
println!("res = {:?}", res);
let ticket_data = TicketData::from_jira_data(res.unwrap());
println!("ticketData = {:?}", ticket_data);
assert!(ticket_data.is_ok())
}
}
Loading…
Cancel
Save