migamake-api-cloudflare 0.2.0

A library to work with Cloudflare apis
Documentation
//! This library is a wrapper for [3 cloudflare
//! api calls](https://api.cloudflare.com).
//!
//! The 3 api calls are
//! * `list zones`
//! * `create a dns record for a domain`
//! * `delete a dns record for a domain`
//!
//! ### Authentication
//!
//! All endpoints need authentication. This library supports authentication
//! using Authorization: Bearer header.
//!
//! You need to export this key - CLOUDFLARE_API_KEY.
//!
//! ### Usage
//! ```no_run
//!     use migamake_api_cloudflare::{Cloudflare, dns};
//!
//!     let domain = "example.com".into();
//!     let zoneid = "some id";
//!     // Initialize using an environment variable CLOUDFLARE_API_KEY
//!     let cloudflare = Cloudflare::default(None);
//!
//!     // or the api key could be passed explicitly to the default method
//!     // let cloudflare = Cloudflare::default(Some("api-key").into());
//!
//!     let mut txt_dns_record = dns::TXTRecord::new();
//!     txt_dns_record.name = domain;
//!     txt_dns_record.content = "create a txt record".into();
//!     let response = cloudflare.create_dns_record(txt_dns_record, &zoneid);
//!     let res = response.unwrap();
//!     if res.success {
//!        println!("{}", "Record created");
//!     }
//!     else{
//!        println!("{:?}", res.errors);

//! }
//! ```

pub mod dns;
pub mod parameters;
pub mod response;
pub mod zone;

use crate::parameters::DnsRecordType;
use crate::zone::Zone;
use serde::Serialize;
use std::env;

use validator::{Validate, ValidationErrors};

/// Base url of Cloudflare API
pub const API_PREFIX: &str = "https://api.cloudflare.com/client/v4/";
/// This represents object to talk to cloudflare api
pub struct Cloudflare {
    auth_key: String,
}

impl Cloudflare {
    /// Initializes the struct with an api token.
    ///
    /// If the api token is not passed as a parameter then uses the
    /// environment variable `CLOUDFLARE_API_KEY` to get the api token
    pub fn default(api_key: Option<String>) -> Cloudflare {
        match api_key {
            Some(value) => Cloudflare { auth_key: value },
            None => {
                let api_key =
                    env::var("CLOUDFLARE_API_KEY").expect("CLOUDFLARE_API_KEY was not found");
                Cloudflare { auth_key: api_key }
            }
        }
    }

    /// builds a request object based on http verb with authentication headers
    fn make_request(&self, url: String, httpverb: parameters::HttpVerb) -> ureq::Request {
        let mut request = if httpverb == parameters::HttpVerb::GET {
            ureq::get(&url)
        } else if httpverb == parameters::HttpVerb::DELETE {
            ureq::delete(&url)
        } else {
            ureq::post(&url)
        };

        request
            .set("Authorization", &format!("Bearer {}", &self.auth_key))
            .set("Content-Type", "application/json");

        request
    }

    /// List zones
    ///
    /// [List Zones](https://api.cloudflare.com/#zone-list-zones)
    pub fn list(self, zone: Zone) -> crate::response::CFResponse {
        let mut url = format!("{}zones?", API_PREFIX);
        url.push_str(&format!("match={}", zone.search.to_string()));
        if let Some(i) = zone.per_page {
            url.push_str(&(format!("&per_page={:?}", i)));
        }
        if let Some(i) = zone.name {
            url.push_str(&(format!("&name={}", i)));
        }
        if let Some(i) = zone.status {
            url.push_str(&(format!("&status={}", i)));
        }

        if let Some(i) = zone.direction {
            url.push_str(&(format!("&direction={}", i)));
        }

        if let Some(i) = zone.order {
            url.push_str(&(format!("&order={}", i)));
        }

        if let Some(i) = zone.account_name {
            url.push_str(&(format!("&account.name={}", i)));
        }

        if let Some(i) = zone.page {
            url.push_str(&(format!("&page={}", i)));
        }

        if let Some(i) = zone.account_id {
            url.push_str(&(format!("&account.id={}", i)));
        }

        println!("{:?}", url);

        let response = self.make_request(url, parameters::HttpVerb::GET).call();

        println!("{:?}", response);
        response
            .into_json_deserialize::<crate::response::CFResponse>()
            .unwrap()
    }

    /// List dns record
    ///
    /// [List Zones](https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records)
    pub fn list_dns_record(
        &self,
        dns_type: DnsRecordType,
        domain_name: &str,
        zone: &str,
    ) -> crate::response::CFResponse {
        let mut url = format!("{}zones/{}/dns_records?", API_PREFIX, zone);
        url.push_str(&format!("type={}", dns_type.to_string()));
        url.push_str(&(format!("&name={}", domain_name)));

        let response = self.make_request(url, parameters::HttpVerb::GET).call();

        response
            .into_json_deserialize::<crate::response::CFResponse>()
            .unwrap()
    }

    /// Create a dns record
    ///
    /// POST zones/:zone_identifier/dns_records
    ///
    /// [Create DNS Record](https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record)
    pub fn create_dns_record<T: Validate + Serialize>(
        &self,
        record: T,
        zone: &str,
    ) -> Result<crate::response::CFCreateResponse, ValidationErrors> {
        let url = format!("{}zones/{}/dns_records", API_PREFIX, zone);

        record.validate()?;

        let response = self
            .make_request(url, parameters::HttpVerb::POST)
            .send_json(ureq::json!(record));

        Ok(response
            .into_json_deserialize::<crate::response::CFCreateResponse>()
            .unwrap())
    }

    /// Delete a dns record
    ///
    /// DELETE zones/:zone_identifier/dns_records/:identifier
    ///
    /// [Delete DNS Record ](https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record)
    pub fn delete_dns_record(
        &self,
        zone: &str,
        dns_record: &str,
    ) -> crate::response::CFDeleteResponse {
        let url = format!("{}zones/{}/dns_records/{}", API_PREFIX, zone, dns_record);

        let response = self.make_request(url, parameters::HttpVerb::DELETE).call();

        response
            .into_json_deserialize::<crate::response::CFDeleteResponse>()
            .unwrap()
    }
}