sparklepost 0.5.5

Bindings for Sparkpost email API v1
Documentation
//! Module contains Sparkpost email sending api
//!
//! # Examples
//!
//! ```
//! use sparkpost::transmission::{Transmission, Message, EmailAddress, TransmissionResponse};
//!
//! let tm = Transmission::new("api_key");
//! // to create for EU version use
//! let tm = Transmission::new_eu("api_key");
//! let mut email = Message::new(
//!     EmailAddress::new("marketing@company.com", "Example Company")
//! );
//!
//! email.add_recipient("name@domain.com")
//!      .subject("My Awesome email 😎")
//!      .html("<h1>html body of the email</h1>")
//!      .text("text body of the email");
//!
//! let result = tm.send(&email);
//!
//! match result {
//!    Ok(res) => {
//!         println!("{:?}", &res);
//!         match res {
//!             TransmissionResponse::ApiResponse(api_res) => {
//!              //   assert_eq!(1, api_res.total_accepted_recipients);
//!              //   assert_eq!(0, api_res.total_rejected_recipients);
//!             }
//!             TransmissionResponse::ApiError(errors) => {
//!                 println!("res: \n {:#?}", &errors);
//!             }
//!         }
//!     }
//!     Err(error) => {
//!         println!("error \n {:#?}", error);
//!     }
//! }
//! ```

use reqwest::{
    blocking::Client,
    header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE},
    Error,
};
use serde::Deserialize;
use std::collections::HashMap;

mod message;
mod models;

pub use self::message::*;
pub use self::models::*;

/// Reqwest Error
pub type ReqError = Error;

/// Transmission result returned by the API
///
#[derive(Debug, Deserialize)]
pub struct ApiResponse {
    pub total_rejected_recipients: usize,
    pub total_accepted_recipients: usize,
    pub id: String,
}

/// Transmission error returned by the API
/// #### Note
/// this is not http error
#[derive(Debug, Deserialize)]
pub struct ApiError {
    pub description: Option<String>,
    pub code: Option<String>,
    pub message: Option<String>,
}

/// Wrapper Enum for ApiResponse and ApiError
#[derive(Debug, Deserialize)]
pub enum TransmissionResponse {
    #[serde(rename = "results")]
    ApiResponse(ApiResponse),
    #[serde(rename = "errors")]
    ApiError(Vec<ApiError>),
}

/// Sparkpost Transmission
///
/// Currently only supports sending email message.
///
/// ```
/// use sparkpost::transmission::Transmission;
/// let tm = Transmission::new("api_key_form_env".to_string());
/// ```
///
/// For more info see <https://developers.sparkpost.com/api/transmissions>.
#[derive(Debug)]
pub struct Transmission {
    api_key: String,
    url: String,
    client: Client,
}

impl Transmission {
    /// creates new Transmission with api key for global version
    pub fn new<T: Into<String>>(api_key: T) -> Self {
        Transmission {
            api_key: api_key.into(),
            url: "https://api.sparkpost.com/api/v1/transmissions".to_owned(),
            client: Client::new(),
        }
    }

    /// creates new Transmission with api key for EU version
    pub fn new_eu<T: Into<String>>(api_key: T) -> Self {
        Transmission {
            api_key: api_key.into(),
            url: "https://api.eu.sparkpost.com/api/v1/transmissions".to_owned(),
            client: Client::new(),
        }
    }
    /// Send api request
    pub fn send(
        &self,
        message: &Message,
    ) -> Result<TransmissionResponse, ReqError> {
        self.client
            .post(&self.url)
            .headers(self.construct_headers(None))
            .json(message)
            .send()?
            .json()
    }
    /// Retrieve a Scheduled Transmission from API
    pub fn scheduled_by_id(
        &self,
        transmission_id: &str,
    ) -> Result<TransmissionResponse, ReqError> {
        let url = format!("{}/{transmission_id}", self.url);

        self.client
            .get(url)
            .headers(self.construct_headers(None))
            .send()?
            .json()
    }

    /// Retrieve all Scheduled Transmissions from API
    ///
    /// Example
    /// ```rust
    /// use sparkpost::transmission::Transmission;
    /// use std::collections::HashMap;
    /// let tm = Transmission::new("api_key");
    ///
    /// let mut headers = HashMap::new();
    /// headers.insert("campaign_id", "your_campaingn_id");
    /// headers.insert("template_id", "your_template_id");
    ///
    /// // filter with headers
    /// let transmissions = tm.scheduled_transmissions(Some(&headers));
    ///
    /// // or for all transmissions
    /// let transmissions = tm.scheduled_transmissions(None);
    ///
    /// ```
    pub fn scheduled_transmissions(
        &self,
        header_map: Option<&HashMap<&'static str, &str>>,
    ) -> Result<TransmissionResponse, ReqError> {
        self.client
            .get(&self.url)
            .headers(self.construct_headers(header_map))
            .send()?
            .json()
    }

    fn construct_headers(
        &self,
        header_map: Option<&HashMap<&'static str, &str>>,
    ) -> HeaderMap {
        let mut headers = HeaderMap::new();
        headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
        headers
            .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
        headers.insert(
            AUTHORIZATION,
            HeaderValue::from_str(&self.api_key).unwrap(),
        );

        if let Some(header_map) = header_map {
            for (name, value) in header_map {
                headers.insert(*name, HeaderValue::from_str(value).unwrap());
            }
        }
        headers
    }
}

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

    fn get_api_key() -> String {
        use dotenvy::dotenv;
        use std::env;
        dotenv().ok();
        env::var("SPARKPOST_API_KEY").expect("SPARKPOST_API_KEY must be set")
    }

    /// actually test the api
    #[ignore]
    #[test]
    fn send_email() {
        let tm = Transmission::new_eu(get_api_key());
        let mut email: Message = Message::new(EmailAddress::new(
            "hello@email.letsorganise.app",
            "noreply",
        ));
        email
            .add_recipient("test@hgill.io")
            .subject("Testing builder email sandbox")
            .html("This is the html body of the email")
            .text("This is the text body of the email");

        //        println!("{:#?}", &email.json().to_string());
        let result = tm.send(&email);
        //        println!("{:#?}", result);
        match result {
            Ok(res) => {
                println!("{:?}", &res);
                match res {
                    TransmissionResponse::ApiResponse(api_res) => {
                        assert_eq!(1, api_res.total_accepted_recipients);
                        assert_eq!(0, api_res.total_rejected_recipients);
                    }
                    TransmissionResponse::ApiError(errors) => {
                        println!("res: \n {:#?}", &errors);
                    }
                }
            }
            Err(error) => {
                println!("error \n {:#?}", error);
            }
        }
        // attach file to email
        email.add_attachment(Attachment::from_data(
            "AnImage.png",
            "image/png",
            "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAXxJREFUOBFjvJVg84P5718WBjLAX2bmPyxMf/+xMDH8YyZDPwPDXwYGJkIaOXTNGdiUtHAqI2jA/18/GUQzGsg3gMfKg4FVQo6BiYcPqyF4XcChaczA4+DP8P//f4b/P3+SZgAzvxCDSGYjAyMjI8PvZw+AoYXdLuyiQLtE0uoZWAREwLb+fnKXQTipkngXcJu7MnACQx8G2FX1GHgs3bDGBlYX8HlFM/z9+JbhzewWhmf1CQyfti9j+PfzBwO/ZxTMTDiNmQKBfmZX1GB42V/K8P38YbDCX/dvMDAwMzPwuYbBNcIYmC4AhfjvXwx/376AqQHTf96+ZPj34xuKGIiDaQBQ8PPBTQwCoZkMjJzcYA3MgqIMAr7xDJ/3rAHzkQnGO7FWf5gZ/qLmBSZmBoHgNAZee1+Gf18/MzCyczJ83LyQ4fPetch6Gf4xMP3FbgBMGdAgJqAr/n37zABMTTBROA0ygAWUJUG5Civ4B8xwX78CpbD6FJiHmf4AAFicbTMTr5jAAAAAAElFTkSuQmCC")).subject("Email with attachment");

        let result = tm.send(&email);
        //        println!("{:#?}", result);
        match result {
            Ok(res) => {
                println!("{:?}", &res);
                match res {
                    TransmissionResponse::ApiResponse(api_res) => {
                        assert_eq!(1, api_res.total_accepted_recipients);
                        assert_eq!(0, api_res.total_rejected_recipients);
                    }
                    TransmissionResponse::ApiError(errors) => {
                        println!("res: \n {:#?}", &errors);
                    }
                }
            }
            Err(error) => {
                println!("error \n {:#?}", error);
            }
        }
    }
}