1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! # The client component
//!
//!

use crate::base::Connection;
use crate::jobs::Job;
use log::{debug, error, warn};
use reqwest::Client;
use std::fmt;

/// Client Error
#[derive(Debug, Clone)]
pub struct RomadClientError {
    msg: String,
}

impl RomadClientError {
    pub fn new(msg: String) -> RomadClientError {
        RomadClientError { msg: msg }
    }
}

impl fmt::Display for RomadClientError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RomadClientError: {}", self.msg)
    }
}

pub struct RomadClient {
    base_url: String,
    client: Client,
}

impl RomadClient {
    /// New
    pub fn new(
        address: &'static str,
        port: &'static str,
        token: std::option::Option<&'static str>,
    ) -> Result<RomadClient, RomadClientError> {
        let connection: Connection = Connection {
            address,
            port,
            token,
            timeout: 0,
            version: "1",
        };

        RomadClient::from_connection(connection)
    }

    /// Create client from a connection object
    pub fn from_connection(connection: Connection) -> Result<RomadClient, RomadClientError> {
        let base_url = connection.build_base_url();

        let client = match Client::builder().build() {
            Ok(client) => client,
            Err(e) => {
                return {
                    error!("Error building client: {}", e);
                    Err(RomadClientError::new(format!("Error building client")))
                }
            }
        };

        Ok(RomadClient { base_url, client })
    }
    /// Get base url
    pub fn get_base_url(&self) -> &String {
        &self.base_url
    }

    /// Get the http client for then object
    pub fn get_client(&self) -> &Client {
        &self.client
    }

    /// Method calls the [List Jobs](https://www.nomadproject.io/api-docs/jobs#list-jobs)
    /// endpoint
    /// TODO: Implement prefix and namespace filter
    pub async fn list_jobs(
        &mut self,
        prefix: Option<&String>,
        namespace: Option<&String>,
    ) -> Result<Vec<Job>, RomadClientError> {
        let client = self.get_client();

        // Construct the url from th base
        let url: String = format!("{}/jobs", self.base_url);

        match namespace {
            Some(_) => {
                warn!("Listing jobs by namespace is not yet implemented, defaulting to no specified namespace")
            }
            None => {}
        }

        match prefix {
            Some(_) => {
                warn!("Listing jobs by prefix is not yet implemented, defaulting to no prefix")
            }
            None => {}
        }

        debug!("Listing jobs using url {}", url);

        let response = match client.get(&url).send().await {
            Ok(resp) => resp,
            Err(e) => return Err(RomadClientError::new(format!("{}", e))),
        };

        debug!("List jobs response headers: {:?}", response);
        match response.text().await {
            Ok(text) => {
                println!("List jobs response: {}", text);
                let jobs: Vec<Job> = serde_json::from_str(&text).unwrap();
                Ok(jobs)
            }
            Err(e) => return Err(RomadClientError::new(format!("{}", e))),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_default_client() {
        let client: RomadClient = RomadClient::new("http://localhost", "4646", None).unwrap();

        assert_eq!(client.get_base_url(), "http://localhost:4646/v1");
    }
}