lemon_bugsnag_rs 0.1.2

A library for interacting with the BugSnag error-reporting and sessions APIs.
Documentation
use reqwest::{
    header::{HeaderMap, HeaderName, HeaderValue},
    ClientBuilder,
};

use crate::{
    common::{
        builder::{builder_options::BuilderOptions, error::BuilderError},
        cobbler::Cobbler,
        error::Error,
        traits::backend_builder_trait_async::BackendBuilderTraitAsync,
    },
    error::{
        payload::{events::Event, notifier::Notifier, Payload},
        reqwest::client::non_blocking::ReqwestErrorAsync,
    },
};

/// The client builder containing information needed to build the
/// [ClientBuilder] which sends the asynchronous request to Bugsnag's error
/// reporting API
///
/// ```
/// use lemon_bugsnag_rs::common::builder::client_builder::ClientBuilder;
/// use lemon_bugsnag_rs::common::traits::decider_trait_async::DeciderTraitAsync;
///
/// let mut error_builder = ClientBuilder::error();
///
/// let configured_builder = error_builder.configure(
///     "Fake API Key",
///     "Doctest configure example error class",
///     "It's not really an error, per se...",
///     "ErrorClientBuilder documentation test notifier",
///     "1.0.0",
///     "Doctest app type",
///     "Production, of course"
/// );
///
/// // This is the client_builder, which will be used to build the asynchronous
/// // client that sends the request to Bugsnags Error-Reporting API
/// let client_builder = configured_builder.reqwest().non_blocking();
/// ```
pub struct ClientBuilderReqwestErrorAsync {
    /// Reqwest backend client used to send asynchronous requests to Bugsnag
    /// Error reporting API
    pub client_builder: ClientBuilder,
    /// Options struct common to all BugSnag client builders in this library.
    pub(crate) options: BuilderOptions,
    /// The API Key associated with the project. Informs Bugsnag which project
    /// has generated this error.
    pub(crate) api_key: Option<String>,
    /// The version number of the payload. The Bugsnag-Payload-Version header
    /// should be included as well, for compatibility reasons.
    pub(crate) payload_version: String,
    /// Describes the notifier itself. These properties are used within Bugsnag
    /// to track error rates from a notifier.
    pub(crate) notifier: Option<Notifier>,
    /// An array of error events Bugsnag is notified of. A notifier can choose
    /// to group notices into an array to minimize network traffic, or notifies
    /// Bugsnag each time an event occurs. Should contain at least 1 event.
    pub(crate) events: Option<Vec<Event>>,
}

impl BackendBuilderTraitAsync<ReqwestErrorAsync, Error>
    for ClientBuilderReqwestErrorAsync
{
    /// Builds the reqwest client that sends asyncronous requests after checking
    /// for missing fields needed to properly send the requests.
    ///
    /// ```
    /// use lemon_bugsnag_rs::common::bundle::*;
    ///
    /// let mut error_builder = ClientBuilder::error();
    ///
    /// let configured_builder = error_builder.configure(
    ///     "Fake API Key",
    ///     "Doctest configure example error class",
    ///     "It's not really an error, per se...",
    ///     "ErrorClientBuilder documentation test notifier",
    ///     "1.0.0",
    ///     "Doctest app type",
    ///     "Production, of course"
    /// );
    ///
    /// let client_builder = configured_builder.reqwest().non_blocking().build().unwrap();
    /// ```
    fn build(mut self) -> Result<ReqwestErrorAsync, Error> {
        let mut missing_fields: Vec<String> = Vec::new();

        let headers = self.options.headers.as_mut().unwrap();
        if self.api_key.is_some() {
            headers.insert(
                "Bugsnag-Api-Key".to_string(),
                self.api_key.clone().unwrap(),
            );
        }

        headers.insert(
            "Bugsnag-Payload-Version".into(),
            self.payload_version.clone(),
        );

        // We check the headers and not self.api_key because it is possible for
        // the user to have directly set the API key in the headers, bypassing
        // set_api_key(). By this point in the code, the API key will be in the
        // headers no matter which was it was set, assuming it was set at all.
        if !headers.contains_key("Bugsnag-Api-Key") {
            missing_fields.push("api_key".to_string());
        }

        if self.notifier.is_none() {
            missing_fields.push("notifier".to_string());
        }

        if self.events.is_none() {
            missing_fields.push("events".to_string());
        }

        // Self::configure_client(&self.options, &mut self.client_builder);
        if missing_fields.len() == 0 {
            let client = self
                .client_builder
                .default_headers(
                    self.options
                        .headers
                        .as_ref()
                        .unwrap()
                        .into_iter()
                        .map(|(header_name, header_value)| {
                            (
                                HeaderName::from_bytes(header_name.as_bytes())
                                    .unwrap(),
                                HeaderValue::from_str(&header_value).unwrap(),
                            )
                        })
                        .collect::<HeaderMap>(),
                )
                .build()?;

            let payload = Payload {
                api_key: self.api_key,
                payload_version: self.payload_version,
                notifier: self.notifier.unwrap(),
                events: self.events.unwrap(),
            };

            let url = Cobbler::cobble(self.options)?;

            Ok(ReqwestErrorAsync { url, client, payload })
        } else {
            Err(BuilderError::MissingField {
                context: "BackendBuilderTrait<ReqwestErrorAsync, Error>"
                    .to_string(),
                fields: missing_fields,
            }
            .into())
        }
    }
}