From df71e1073c261fb39f3fd6334dd9558fe7babff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sun, 17 Mar 2024 21:34:03 +0100 Subject: [PATCH] allow multiple instances of gitlab to be configured --- src/config.rs | 3 ++- src/gitlab/client.rs | 35 ++++++++++++++++++++++++++++++++++ src/gitlab/mod.rs | 1 + src/openproject/client.rs | 39 ++++++++++++++++++++++++-------------- src/openproject/root.rs | 6 +++--- src/openproject/user.rs | 6 +++--- src/openproject/work.rs | 4 ++-- src/planning/issue2work.rs | 25 +++++++++--------------- 8 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 src/gitlab/client.rs diff --git a/src/config.rs b/src/config.rs index c6ee4e9..468a551 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,13 +2,14 @@ use serde::Deserialize; #[derive(Deserialize, Debug)] pub(crate) struct Config { - pub gitlab: GitlabConfig, + pub gitlab: Vec, pub openproject: OpenProjectConfig, } #[derive(Deserialize, Debug)] pub(crate) struct GitlabConfig { pub token: String, + pub domain: String, } #[derive(Deserialize, Debug)] diff --git a/src/gitlab/client.rs b/src/gitlab/client.rs new file mode 100644 index 0000000..d84979b --- /dev/null +++ b/src/gitlab/client.rs @@ -0,0 +1,35 @@ +use crate::config::Config; +use crate::error::GeneralError; +use gitlab::AsyncGitlab; +use gitlab::GitlabBuilder; +use url::Url; + +pub trait ClientProviderTrait { + async fn client_for_url(url: &Url, config: &Config) -> Result; +} + +pub struct ClientProvider {} + +impl ClientProviderTrait for ClientProvider { + async fn client_for_url(url: &Url, config: &Config) -> Result { + for c in &config.gitlab { + if url.domain() == Some(c.domain.as_str()) { + let client = GitlabBuilder::new("gitlab.com", c.token.clone()) + .build_async() + .await; + + return match client { + Ok(new_client) => Ok(new_client), + Err(e) => { + let new_error = e.into(); + Err(new_error) + } + }; + } + } + + Err(GeneralError { + description: format!("No client available for this domain: {:?}", url.domain()), + }) + } +} diff --git a/src/gitlab/mod.rs b/src/gitlab/mod.rs index 1a94752..e278a81 100644 --- a/src/gitlab/mod.rs +++ b/src/gitlab/mod.rs @@ -1,3 +1,4 @@ +pub mod client; pub mod issue; use crate::error::GeneralError; diff --git a/src/openproject/client.rs b/src/openproject/client.rs index 540a788..0cfb407 100644 --- a/src/openproject/client.rs +++ b/src/openproject/client.rs @@ -3,22 +3,23 @@ use crate::error::GeneralError; use crate::openproject::work::{WorkPackage, WorkPackageWriter}; use reqwest::Response; use serde::Deserialize; +use std::error::Error; #[derive(Deserialize, Debug)] -pub(crate) struct Error { +pub(crate) struct OpenProjectError { pub(crate) description: String, } -impl From for Error { +impl From for OpenProjectError { fn from(value: reqwest::Error) -> Self { - Error { + OpenProjectError { description: format!("Error while connecting to openproject instance: {}", value), } } } -impl From for GeneralError { - fn from(value: Error) -> GeneralError { +impl From for GeneralError { + fn from(value: OpenProjectError) -> GeneralError { GeneralError { description: value.description, } @@ -33,7 +34,7 @@ pub(crate) struct Client { pub async fn handle_response_status serde::Deserialize<'de>>( response: Response, error_message: &str, -) -> Result { +) -> Result { if !response.status().is_success() { let status = response.status().to_string().clone(); let content = response @@ -42,7 +43,7 @@ pub async fn handle_response_status serde::Deserialize<'de>>( .unwrap_or_else(|_| "Impossible to decode".into()) .clone(); - return Err(Error { + return Err(OpenProjectError { description: format!( "{}, status: {}, content: {}", error_message, status, content @@ -50,9 +51,18 @@ pub async fn handle_response_status serde::Deserialize<'de>>( }); } - let t = response.json().await?; + let t = response.json::().await; - Ok(t) + match t { + Ok(t) => Ok(t), + Err(e) => Err(OpenProjectError { + description: format!( + "Error while decoding json: {}, source: {:?}", + e.to_string(), + e.source() + ), + }), + } } impl Client { @@ -67,21 +77,22 @@ impl Client { &self, work_package: &WorkPackageWriter, project_id: &String, - ) -> Result { + ) -> Result { let client = reqwest::Client::new(); - let work_package: WorkPackage = client + let response = client .post(format!( "{}/api/v3/projects/{}/work_packages", self.base_url, project_id )) .basic_auth("apikey", Some(&self.token)) - .json(&work_package) + .json(work_package) .send() - .await? - .json() .await?; + let work_package = + handle_response_status(response, "error while retrieving work package").await?; + Ok(work_package) } } diff --git a/src/openproject/root.rs b/src/openproject/root.rs index 4070c03..e6a3769 100644 --- a/src/openproject/root.rs +++ b/src/openproject/root.rs @@ -1,4 +1,4 @@ -use crate::openproject::client::{handle_response_status, Client, Error}; +use crate::openproject::client::{handle_response_status, Client, OpenProjectError}; use crate::openproject::hal::HalEntity; use serde::Deserialize; @@ -22,11 +22,11 @@ pub struct Root { } pub trait RootClient { - async fn root(&self) -> Result; + async fn root(&self) -> Result; } impl RootClient for Client { - async fn root(&self) -> Result { + async fn root(&self) -> Result { let client = reqwest::Client::new(); let response = client diff --git a/src/openproject/user.rs b/src/openproject/user.rs index 2f3551a..3e29a3f 100644 --- a/src/openproject/user.rs +++ b/src/openproject/user.rs @@ -1,4 +1,4 @@ -use crate::openproject::client::{handle_response_status, Client, Error}; +use crate::openproject::client::{handle_response_status, Client, OpenProjectError}; use crate::openproject::hal::Link; use crate::openproject::root::RootClient; use serde::Deserialize; @@ -19,11 +19,11 @@ pub struct User { } pub trait GetMe { - async fn me(&self) -> Result; + async fn me(&self) -> Result; } impl GetMe for Client { - async fn me(&self) -> Result { + async fn me(&self) -> Result { let r = self.root().await?; let client = reqwest::Client::new(); diff --git a/src/openproject/work.rs b/src/openproject/work.rs index 63e074a..d9aca46 100644 --- a/src/openproject/work.rs +++ b/src/openproject/work.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Debug)] pub struct WorkPackageWriterAssignee { - pub(crate) href: String, + pub(crate) href: Option, } #[derive(Serialize, Debug)] pub struct WorkPackageWriter { @@ -10,7 +10,7 @@ pub struct WorkPackageWriter { #[serde(alias = "type")] pub(crate) work_type: String, pub(crate) description: DescriptionWriter, - pub assignee: Option, + pub assignee: WorkPackageWriterAssignee, } #[derive(Serialize, Debug)] diff --git a/src/planning/issue2work.rs b/src/planning/issue2work.rs index ebe1a51..7c19cc5 100644 --- a/src/planning/issue2work.rs +++ b/src/planning/issue2work.rs @@ -1,12 +1,13 @@ use crate::cli::Issue2Work; use crate::config::Config; use crate::error::GeneralError; +use crate::gitlab::client::{ClientProvider, ClientProviderTrait}; use crate::gitlab::issue::IssueBundle; use crate::openproject::client::Client; use crate::openproject::user::{GetMe, User}; use crate::openproject::work::WorkPackageWriterAssignee; use gitlab::api::{issues, projects, AsyncQuery}; -use gitlab::{GitlabBuilder, Issue, Project}; +use gitlab::{Issue, Project}; use url::Url; #[derive(Debug)] @@ -22,17 +23,6 @@ struct Issue2WorkPackageDTO { pub assign_to: Option, } -impl From<&Issue2WorkPackageDTO> for Option { - fn from(value: &Issue2WorkPackageDTO) -> Self { - match &value.assign_to { - None => None, - Some(w) => Some(WorkPackageWriterAssignee { - href: w.clone().d_links.d_self.href, - }), - } - } -} - impl From<&Issue2WorkPackageDTO> for crate::openproject::work::WorkPackageWriter { fn from(value: &Issue2WorkPackageDTO) -> Self { crate::openproject::work::WorkPackageWriter { @@ -47,7 +37,12 @@ impl From<&Issue2WorkPackageDTO> for crate::openproject::work::WorkPackageWriter format: "markdown".into(), raw: format!("From gitlab: {}", value.issue.issue.web_url), }, - assignee: value.into(), + assignee: WorkPackageWriterAssignee { + href: match &value.assign_to { + None => None, + Some(w) => Some(w.clone().d_links.d_self.href), + }, + }, } } } @@ -56,9 +51,7 @@ pub(crate) async fn issue2work(config: Config, args: &Issue2Work) -> Result<(), let url = Url::parse(&*args.issue_url).expect("issue_url is not valid"); let data = extract_issue_info(&url).unwrap(); - let client = GitlabBuilder::new("gitlab.com", config.gitlab.token) - .build_async() - .await?; + let client = ClientProvider::client_for_url(&url, &config).await?; let endpoint = issues::ProjectIssues::builder() .iid(data.iid)