1use std::sync::Arc;
2
3use crate::error::RovenueResult;
4use crate::transport::http_client::HttpClient;
5use crate::transport::types::HttpPostRequest;
6
7use super::EventEnvelope;
8
9pub struct EventsClient {
12 http: Arc<HttpClient>,
13}
14
15impl EventsClient {
16 pub fn new(http: Arc<HttpClient>) -> Self {
17 Self { http }
18 }
19
20 pub fn post(&self, envelope: &EventEnvelope, scope: Option<&str>) -> RovenueResult<()> {
25 let mut req = HttpPostRequest::new("/v1/events").expect_empty_body();
26 if let Some(s) = scope {
27 req = req.user_scope(s);
28 }
29 self.http
30 .post_json::<EventEnvelope, serde_json::Value>(req, envelope)?;
31 Ok(())
32 }
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use crate::events::IdentityContext;
39
40 fn envelope() -> EventEnvelope {
41 EventEnvelope {
42 version: None,
43 event_id: None,
44 event_type: "purchase".into(),
45 occurred_at: "2026-06-20T10:00:00Z".into(),
46 subscriber_id: Some("user_42".into()),
47 product_id: None,
48 amount: Some("9.99".into()),
49 currency: Some("USD".into()),
50 event_source_url: None,
51 identity_context: Some(IdentityContext {
52 email: Some("a@b.com".into()),
53 ..Default::default()
54 }),
55 funnel_context: None,
56 }
57 }
58
59 #[test]
60 fn post_sends_camelcase_envelope_and_omits_none() {
61 let mut server = mockito::Server::new();
62 let m = server
63 .mock("POST", "/v1/events")
64 .match_header("X-Rovenue-App-User-Id", "user_42")
65 .match_body(mockito::Matcher::JsonString(
66 r#"{"eventType":"purchase","occurredAt":"2026-06-20T10:00:00Z","subscriberId":"user_42","amount":"9.99","currency":"USD","identityContext":{"email":"a@b.com"}}"#.into(),
67 ))
68 .with_status(202)
69 .create();
70
71 let http = Arc::new(HttpClient::new(server.url(), "pk_test".into()).with_max_attempts(1));
72 EventsClient::new(http)
73 .post(&envelope(), Some("user_42"))
74 .expect("post ok");
75
76 m.assert();
77 }
78
79 #[test]
80 fn post_omits_scope_header_when_none() {
81 let mut server = mockito::Server::new();
82 let m = server
83 .mock("POST", "/v1/events")
84 .match_header("X-Rovenue-App-User-Id", mockito::Matcher::Missing)
85 .with_status(202)
86 .create();
87
88 let http = Arc::new(HttpClient::new(server.url(), "pk_test".into()).with_max_attempts(1));
89 EventsClient::new(http)
90 .post(&envelope(), None)
91 .expect("post ok");
92
93 m.assert();
94 }
95}