bitgo_api 0.2.1

Rust implimentation for Bitgo api
Documentation
use std::fs::File;

use crate::config::Config;
use crate::error::{Error, Result};
use log::trace;
use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};
use reqwest::{Certificate, Client, ClientBuilder, RequestBuilder};
use std::io::Read;

#[derive(Debug, Clone)]
pub struct BitGoClient {
    pub endpoint: String,
    pub token: String,
    pub bitgo_cert_path: Option<String>,
}

impl BitGoClient {
    pub fn new(endpoint: String, token: String, bitgo_cert_path: Option<String>) -> Result<Self> {
        Ok(BitGoClient {
            endpoint,
            token,
            bitgo_cert_path,
        })
    }

    pub async fn get_api<T: serde::Serialize>(
        &self,
        request_url: &str,
        params: &T,
    ) -> Result<serde_json::Value> {
        log::trace!("request url {:?}", request_url);
        let cert = match self.bitgo_cert_path.as_ref() {
            Some(path) => path.to_string(),
            None => "".to_string(),
        };
        let builder;
        if !cert.is_empty() {
            let client = BitGoClient::get_ssl_certificate(cert)?;
            builder = ClientBuilder::build(client)?.get(request_url);
        } else {
            builder = reqwest::Client::new().get(request_url);
        }

        self.call_api(builder, params).await
    }

    pub async fn post_api<T: serde::Serialize>(
        &self,
        request_url: &str,
        params: &T,
    ) -> Result<serde_json::Value> {
        log::trace!("request url {:?}", request_url);
        let cert = match self.bitgo_cert_path.as_ref() {
            Some(path) => path.to_string(),
            None => "".to_string(),
        };
        let builder;
        if !cert.is_empty() {
            let client = BitGoClient::get_ssl_certificate(cert)?;
            builder = ClientBuilder::build(client)?.post(request_url);
        } else {
            builder = reqwest::Client::new().post(request_url);
        }
        self.call_api(builder, params).await
    }

    pub async fn delete_api<T: serde::Serialize>(
        &self,
        request_url: &str,
        params: &T,
    ) -> Result<serde_json::Value> {
        log::trace!("request url {:?}", request_url);
        let cert = match self.bitgo_cert_path.as_ref() {
            Some(path) => path.to_string(),
            None => "".to_string(),
        };
        let builder;
        if !cert.is_empty() {
            let client = BitGoClient::get_ssl_certificate(cert)?;
            builder = ClientBuilder::build(client)?.delete(request_url);
        } else {
            builder = reqwest::Client::new().delete(request_url);
        }

        self.call_api(builder, params).await
    }

    async fn call_api<T: serde::Serialize>(
        &self,
        builder: RequestBuilder,
        params: &T,
    ) -> Result<serde_json::Value> {
        let response = builder
            .header(CONTENT_TYPE, "application/json")
            .header(AUTHORIZATION, format!("Bearer {}", self.token))
            .json(params)
            .send()
            .await?;
        if response.status().is_success() {
            let response_json: serde_json::Value = response.json().await?;
            trace!("bitgo api response {:?}", response_json);
            Ok(response_json)
        } else {
            let err_json: serde_json::Value = response.json().await?;
            let msg = match err_json.get("message") {
                Some(value) => value.to_string(),
                None => "Unknown Error".to_string(),
            };
            Err(Error::BitgoError { msg })
        }
    }

    pub fn get_ssl_certificate(path: String) -> Result<ClientBuilder> {
        let mut buf = Vec::new();
        let mut f = File::open(path).expect("Unable to open file");
        f.read_to_end(&mut buf).expect("Unable to read data");
        let cert = reqwest::Certificate::from_pem(&buf)?;
        let client = ClientBuilder::new().add_root_certificate(cert);
        Ok(client)
    }

    pub fn from_config(config: &Config) -> Result<Self> {
        BitGoClient::new(
            config.endpoint.clone(),
            config.token.clone(),
            config.bitgo_cert_path.clone(),
        )
    }
}