use error_stack::{Result, ResultExt};
use crate::datafile::Datafile;
use crate::error::EventApiError;
use crate::{Conversion, Decision};
pub(crate) use payload::Payload;
use snapshot::Snapshot;
pub(crate) use visitor::Visitor;
mod payload;
mod snapshot;
mod visitor;
const ENDPOINT_URL: &str = "https://logx.optimizely.com/v1/events";
const CONTENT_TYPE_KEY: &str = "content-type";
const CONTENT_TYPE_VALUE: &str = "application/json";
pub struct Request {
account_id: String,
payload: Option<Payload>,
}
impl Request {
pub fn new(datafile: &Datafile) -> Request {
Request {
account_id: datafile.account_id().to_owned(),
payload: Option::None,
}
}
fn payload(&mut self) -> &mut Payload {
self.payload.get_or_insert_with(|| {
let account_id = self.account_id.clone();
Payload::new(account_id)
})
}
pub fn add_conversion_event(&mut self, visitor: Visitor, conversion: Conversion) {
self.payload().add_conversion_event(visitor, conversion);
}
pub fn add_decision_event(&mut self, visitor: Visitor, decision: Decision) {
self.payload().add_decision_event(visitor, decision);
}
pub fn buffer_size(&self) -> usize {
match &self.payload {
Some(payload) => payload.size(),
None => 0,
}
}
pub fn send(&mut self) {
if let Some(payload) = self.payload.take() {
log::debug!("Sending request to Event API");
let body = match payload.to_string() {
Ok(body) => body,
Err(report) => {
log::error!("Failed to serialize payload");
log::error!("\n{report:?}");
return;
}
};
let response = Self::post(&body);
match response {
Ok(_) => {
log::info!("Successfully sent request to Event API");
}
Err(report) => {
log::error!("Failed to send request to Event API");
log::error!("\n{report:?}");
}
}
} else {
log::warn!("Nothing to send");
}
}
fn post(body: &str) -> Result<ureq::Response, EventApiError> {
ureq::post(ENDPOINT_URL)
.set(CONTENT_TYPE_KEY, CONTENT_TYPE_VALUE)
.send_string(body)
.change_context(EventApiError::FailedRequest)
}
}
impl Drop for Request {
fn drop(&mut self) {
log::debug!("Dropping Request");
if self.payload.is_some() {
self.send()
}
}
}