use chrono::{DateTime, Utc};
use reqwest::blocking::RequestBuilder;
use super::{
common::{apply_before_send_hooks, apply_capture_defaults, apply_runtime_context},
BeforeSendHook, CaptureDefaults, ClientOptions,
};
use crate::endpoints::Endpoint;
use crate::error::Error;
use crate::event::{BatchRequest, Event, InnerEvent};
pub(crate) fn prepare_event(event: &mut Event, defaults: &CaptureDefaults) {
apply_capture_defaults(event, defaults);
apply_runtime_context(event);
event.prepare_for_v0();
}
pub(crate) fn build_batch_payload(
events: Vec<Event>,
api_key: String,
historical_migration: bool,
sent_at: DateTime<Utc>,
defaults: &CaptureDefaults,
before_send: &[BeforeSendHook],
) -> Result<Option<(String, usize)>, Error> {
let inner_events: Vec<InnerEvent> = events
.into_iter()
.filter_map(|mut event| {
prepare_event(&mut event, defaults);
apply_before_send_hooks(before_send, event).map(InnerEvent::new_for_batch)
})
.collect();
if inner_events.is_empty() {
return Ok(None);
}
let kept = inner_events.len();
let batch_request = BatchRequest {
api_key,
historical_migration,
sent_at: sent_at.to_rfc3339(),
batch: inner_events,
};
serde_json::to_string(&batch_request)
.map(|json| Some((json, kept)))
.map_err(|e| Error::Serialization(e.to_string()))
}
pub(crate) fn encode_body(
options: &ClientOptions,
json: String,
) -> (Vec<u8>, Option<&'static str>) {
match options.capture_compression {
Some(algo) => match crate::compression::compress(algo, json.as_bytes()) {
Some((bytes, encoding)) => (bytes, Some(encoding)),
None => (json.into_bytes(), None),
},
None => (json.into_bytes(), None),
}
}
pub(crate) struct PreparedV0 {
pub(crate) url: String,
pub(crate) body: Vec<u8>,
pub(crate) encoding: Option<&'static str>,
pub(crate) kept: usize,
}
pub(crate) fn prepare_immediate(
options: &ClientOptions,
events: Vec<Event>,
historical_migration: bool,
) -> Result<Option<PreparedV0>, Error> {
let defaults = options.capture_defaults();
let Some((json, kept)) = build_batch_payload(
events,
options.api_key.clone(),
historical_migration,
Utc::now(),
&defaults,
&options.before_send,
)?
else {
return Ok(None);
};
let base_url = options.endpoints().build_url(Endpoint::Batch);
let (body, encoding) = encode_body(options, json);
let url = match encoding {
Some(token) => format!("{base_url}?compression={token}"),
None => base_url,
};
Ok(Some(PreparedV0 {
url,
body,
encoding,
kept,
}))
}
pub(crate) fn apply_extra_headers(
#[allow(unused_variables)] options: &ClientOptions,
#[allow(unused_mut)] mut request: RequestBuilder,
) -> RequestBuilder {
#[cfg(feature = "test-harness")]
if let Some(ref extra) = options.extra_capture_headers {
for (k, v) in extra {
request = request.header(k.as_str(), v.as_str());
}
}
request
}