Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
/target
config.toml
Cargo.lock
15 changes: 5 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use youtrack::{Duration, IssueWorkItem};

use regex::Regex;

mod token;
mod youtrack;

mod toggl;
Expand All @@ -24,7 +23,7 @@ struct Config {
youtrack_api: ApiConfig,
}

#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
struct ApiConfig {
token: String,
}
Expand All @@ -45,7 +44,7 @@ async fn main() -> Result<(), reqwest::Error> {
let config: Config = toml::from_str(&config_content).expect("Failed to parse config file");

// Get the current youtrack user for later use
let user = youtrack::get_current_user().await.unwrap();
let user = youtrack::get_current_user(config.youtrack_api.clone()).await.unwrap();
info!("User: {:#?}", user);

// Get all toggl time entries of the last X days.
Expand Down Expand Up @@ -93,8 +92,9 @@ async fn main() -> Result<(), reqwest::Error> {
// The association is represented by a HashMap. This should happen asynchronously (slow web reqs).
let work_items_map = stream::iter(unique_issue_ids)
.filter_map(|issue_id| {
let youtrack_apiconfig_clone = config.youtrack_api.clone();
async move {
match youtrack::get_workitems(issue_id.clone()).await {
match youtrack::get_workitems(issue_id.clone(), youtrack_apiconfig_clone).await {
// If WorkItems are obtained for this issue ID, return them.
Ok(work_items) => Some((issue_id.clone(), work_items)),
// Otherwise do not include that Issue ID in the HashMap.
Expand Down Expand Up @@ -190,7 +190,7 @@ async fn main() -> Result<(), reqwest::Error> {
"Issue {} - creating work_item: {:#?}",
&entry.issue_id, &work_item
);
youtrack::create_work_item(&entry.issue_id, work_item).await
youtrack::create_work_item(&entry.issue_id, work_item, config.youtrack_api.clone()).await
} else {
log::warn!("Duration not > 0. Skipping entry: {:#?}", entry);
}
Expand All @@ -216,8 +216,3 @@ mod test {
assert_eq!(caps.get(2).unwrap().as_str(), "My Description");
}
}

#[tokio::test]
async fn test_wrong_issue_id() {
youtrack::get_workitems("ABC-123".to_string()).await;
}
2 changes: 0 additions & 2 deletions src/token.rs.template

This file was deleted.

32 changes: 20 additions & 12 deletions src/youtrack.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::token::AUTH_TOKEN;
use chrono::{serde::ts_milliseconds, DateTime, Utc};
use log::debug;
use serde::{Deserialize, Serialize};

use crate::ApiConfig;

const BASE_URL: &str = "https://systemscape.youtrack.cloud";
const WORK_ITEMS_FIELDS: &str = "author(id,login),creator(id,login),date,created(minutes),duration(minutes),id,name,text,issue(idReadable)";
//const WORK_ITEMS_FIELDS: &str = "author(id,login),creator(id,login),date,created(minutes),duration(minutes),id,name,text,type(id,name),issue(idReadable)";
Expand Down Expand Up @@ -45,14 +46,14 @@ pub struct User {
pub id: String,
}

pub async fn create_work_item(issue_id: &str, item: IssueWorkItem) {
pub async fn create_work_item(issue_id: &str, item: IssueWorkItem, config: ApiConfig) {
let client = reqwest::Client::new();

let res = client
.post(format!(
"{BASE_URL}/api/issues/{issue_id}/timeTracking/workItems?fields={WORK_ITEMS_FIELDS}"
))
.bearer_auth(AUTH_TOKEN)
.bearer_auth(config.token)
.header("Content-Type", "application/json")
.body(serde_json::to_string(&item).unwrap())
.send()
Expand All @@ -64,30 +65,30 @@ pub async fn create_work_item(issue_id: &str, item: IssueWorkItem) {
);
}

pub async fn get_workitems(issue_id: String) -> Result<Vec<IssueWorkItem>, reqwest::Error> {
pub async fn get_workitems(issue_id: String, config: ApiConfig) -> Result<Vec<IssueWorkItem>, reqwest::Error> {
debug!("get_workitems for issue_id {}", &issue_id);
let url = format!(
"{BASE_URL}/api/issues/{issue_id}/timeTracking/workItems?fields={WORK_ITEMS_FIELDS}"
);

let res = perform_request(&url).await.unwrap();
let res = perform_request(&url, config).await.unwrap();
let res = res.error_for_status()?.text().await.unwrap();
let items: Vec<IssueWorkItem> = serde_json::from_str(&res).unwrap();

Ok(items)
}

pub async fn perform_request(url: &str) -> Result<reqwest::Response, reqwest::Error> {
pub async fn perform_request(url: &str, config: ApiConfig) -> Result<reqwest::Response, reqwest::Error> {
let client = reqwest::Client::new();
client.get(url).bearer_auth(AUTH_TOKEN).send().await
client.get(url).bearer_auth(config.token).send().await
}

pub async fn get_current_user() -> Result<User, String> {
pub async fn get_current_user(config: ApiConfig) -> Result<User, String> {
let client = reqwest::Client::new();

let res = client
.get(format!("{BASE_URL}/api/users/me?fields=id,login"))
.bearer_auth(AUTH_TOKEN)
.bearer_auth(config.token)
.send()
.await;

Expand All @@ -101,16 +102,23 @@ pub async fn get_current_user() -> Result<User, String> {
#[cfg(test)]
mod test {
use log::info;

use std::fs;

use crate::Config;
use crate::youtrack;

#[tokio::test]
async fn test_serde() {
simple_logger::init().unwrap();

let user = youtrack::get_current_user().await.unwrap();
// Read the config file
let config_content = fs::read_to_string("config.toml").expect("Failed to read config file");
let config: Config = toml::from_str(&config_content).expect("Failed to parse config file");


let user = youtrack::get_current_user(config.youtrack_api.clone()).await.unwrap();
info!("User: {:#?}", user);

youtrack::get_workitems("SO-106").await;
youtrack::get_workitems("SO-106".to_string(), config.youtrack_api.clone()).await;
}
}