use super::*;
#[derive(Debug, Default, Clone, Copy)]
pub struct RequestFilter<'a> {
pub status: Option<&'a str>,
pub method: Option<&'a str>,
pub path: Option<&'a str>,
pub limit: Option<usize>,
}
impl RequestFilter<'_> {
pub fn query(&self) -> String {
let mut pairs: Vec<String> = Vec::new();
for (name, value) in [
("status", self.status),
("method", self.method),
("path", self.path),
] {
if let Some(value) = value.map(str::trim).filter(|value| !value.is_empty()) {
pairs.push(format!(
"{name}={}",
utf8_percent_encode(value, NON_ALPHANUMERIC)
));
}
}
if let Some(limit) = self.limit {
pairs.push(format!("limit={limit}"));
}
if pairs.is_empty() {
String::new()
} else {
format!("?{}", pairs.join("&"))
}
}
}
impl ApiClient {
pub async fn requests(
&self,
service_environment_id: &str,
filter: RequestFilter<'_>,
) -> Result<Answer<RequestWindow>> {
self.answer(
Method::GET,
&format!(
"/api/infra/services/{service_environment_id}/requests{}",
filter.query()
),
None::<&()>,
)
.await
}
pub async fn stream_requests<F>(
&self,
service_environment_id: &str,
filter: RequestFilter<'_>,
mut on_request: F,
) -> Result<()>
where
F: FnMut(InspectedRequest),
{
let path = format!(
"/api/infra/services/{service_environment_id}/requests/stream{}",
filter.query()
);
self.read_events(&path, |event| {
if let Some(request) = parse_request_event(event) {
on_request(request);
}
ControlFlow::Continue(())
})
.await
}
}
fn parse_request_event(event: &str) -> Option<InspectedRequest> {
if !event.lines().any(|line| line == "event: request") {
return None;
}
serde_json::from_str::<InspectedRequest>(&payload_of(event)?).ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_should_ask_for_nothing_without_a_filter() {
assert_eq!(RequestFilter::default().query(), "");
}
#[test]
fn it_should_encode_the_piece_of_path() {
let filter = RequestFilter {
status: Some("5xx"),
path: Some("/api?x"),
limit: Some(20),
..RequestFilter::default()
};
assert_eq!(filter.query(), "?status=5xx&path=%2Fapi%3Fx&limit=20");
}
#[test]
fn it_should_read_a_request_from_its_event() {
let event = "id: 7\nevent: request\ndata: {\"sequence\":7,\"at\":\"2026-10-16T10:00:00.000Z\",\"method\":\"GET\",\"path\":\"/\",\"status\":200,\"durationMillis\":12,\"clientAddress\":null,\"userAgent\":null,\"cached\":false,\"woke\":false,\"webSocket\":false,\"blockedBy\":null}";
assert_eq!(
parse_request_event(event).map(|request| request.status),
Some(200)
);
}
#[test]
fn it_should_ignore_a_keep_alive() {
assert!(parse_request_event(": keep-alive").is_none());
}
}