fractal-api 0.0.2

Fractal Global Credits API client library
Documentation
//! Fractal Global Credits API.

// #![forbid(missing_docs, warnings)]
#![deny(deprecated, drop_with_repr_extern, improper_ctypes,
        non_shorthand_field_patterns, overflowing_literals, plugin_as_library,
        private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion,
        unknown_lints, unused, unused_allocation, unused_attributes,
        unused_comparisons, unused_features, unused_parens, while_true)]
#![warn(missing_docs, trivial_casts, trivial_numeric_casts, unused, unused_extern_crates,
        unused_import_braces, unused_qualifications, unused_results, variant_size_differences)]

extern crate hyper;
extern crate chrono;
extern crate rustc_serialize;
extern crate fractal_dto as dto;

use std::time::Duration;
use std::io::Read;

use hyper::Client as HyperClient;
use hyper::client::IntoUrl;
use hyper::header::{Headers, Authorization, Accept, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::status::StatusCode;
use rustc_serialize::base64::FromBase64;
use rustc_serialize::json;
use dto::FromDTO;

pub mod error;
pub mod types;
pub mod oauth;

use error::{Result, Error};
use types::User;
use oauth::{AccessToken, Scope};

/// Application's secret length.
pub const SECRET_LEN: usize = 32;

const FRACTAL_SERVER: &'static str = "https://api.fractal.global/";
const FRACTAL_DEV_SERVER: &'static str = "https://dev.fractal.global/";

/// The client struct.
///
/// This struct will be in charge of connections to the Fractal Global Credits API.
pub struct ClientV1 {
    client: HyperClient,
    url: String,
}

impl ClientV1 {
    /// Creates a new Fractal Global Credits API client.
    pub fn new() -> ClientV1 {
        ClientV1 {
            client: hyper::Client::new(),
            url: format!("{}/v1/", FRACTAL_SERVER),
        }
    }

    /// Creates a new Fractal Global Credits API client.
    pub fn new_with_url<S: AsRef<str>>(url: S) -> ClientV1 {
        ClientV1 {
            client: hyper::Client::new(),
            url: format!("{}/v1/", url.as_ref()),
        }
    }

    /// Creates a new Fractal Global Credits API development client.
    pub fn new_dev() -> ClientV1 {
        ClientV1 {
            client: hyper::Client::new(),
            url: format!("{}/v1/", FRACTAL_DEV_SERVER),
        }
    }

    /// Sets the read timeout for requests.
    pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
        self.client.set_read_timeout(timeout);
    }

    /// Sets the write timeout for requests.
    pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
        self.client.set_write_timeout(timeout);
    }

    /// Gets a token from the API.
    pub fn token<S: AsRef<str>>(&self, app_id: S, secret: S) -> Result<AccessToken> {
        match secret.as_ref().from_base64() {
            Ok(b) => {
                if b.len() == SECRET_LEN {
                    let mut headers = Headers::new();
                    headers.set(
                        Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ])
                    );
                    headers.set(
                        Authorization(format!("Basic {}:{}", app_id.as_ref(), secret.as_ref()))
                    );
                    let mut response = try!(self.client
                        .get(&format!("{}/token", self.url))
                        .body("grant_type=client_credentials")
                        .headers(headers).send());
                    match response.status {
                        StatusCode::Ok => {
                            let mut response_str = String::new();
                            try!(response.read_to_string(&mut response_str));
                            Ok(try!(AccessToken::from_dto(try!(json::decode(&response_str)))))
                        },
                        _ => Err(Error::Unauthorized),
                    }
                } else {
                    Err(Error::InvalidSecret)
                }
            },
            Err(_) => Err(Error::InvalidSecret),
        }
    }

    /// Gets all users from the database.
    pub fn get_all_users(&self, access_token: AccessToken) -> Result<Vec<User>> {
        if access_token.scopes().any(|s| s == &Scope::Admin) && !access_token.has_expired() {
            let response = try!(self.client
                .get(&format!("{}/get_all_users", self.url))
                .header(Authorization(format!("{} {}",
                                              access_token.get_token_type(),
                                              access_token.as_str())))
                .send());
            unimplemented!()
        } else {
            Err(Error::Unauthorized)
        }
    }
}