systemprompt_analytics/repository/
events.rs1use crate::Result;
7use systemprompt_identifiers::{SessionId, UserId};
8use systemprompt_traits::analytics_events::{AnalyticsEventRecord, DynAnalyticsEventStore};
9
10use crate::models::{AnalyticsEventCreated, CreateAnalyticsEventInput};
11
12#[derive(Clone, Debug)]
13pub struct AnalyticsEventsRepository {
14 event_sink: DynAnalyticsEventStore,
15}
16
17impl AnalyticsEventsRepository {
18 pub const fn new(event_sink: DynAnalyticsEventStore) -> Self {
19 Self { event_sink }
20 }
21
22 pub async fn create_event(
23 &self,
24 session_id: &SessionId,
25 user_id: &UserId,
26 input: &CreateAnalyticsEventInput,
27 ) -> Result<AnalyticsEventCreated> {
28 let event = Self::build_record(session_id, user_id, input);
29 self.event_sink
30 .persist_events(std::slice::from_ref(&event))
31 .await?;
32 Ok(AnalyticsEventCreated {
33 id: event.id,
34 event_type: event.event_type,
35 })
36 }
37
38 pub async fn create_events_batch(
39 &self,
40 session_id: &SessionId,
41 user_id: &UserId,
42 inputs: &[CreateAnalyticsEventInput],
43 ) -> Result<Vec<AnalyticsEventCreated>> {
44 if inputs.is_empty() {
45 return Ok(Vec::new());
46 }
47
48 let events: Vec<_> = inputs
49 .iter()
50 .map(|input| Self::build_record(session_id, user_id, input))
51 .collect();
52 self.event_sink.persist_events(&events).await?;
53 Ok(events
54 .into_iter()
55 .map(|event| AnalyticsEventCreated {
56 id: event.id,
57 event_type: event.event_type,
58 })
59 .collect())
60 }
61
62 fn build_record(
63 session_id: &SessionId,
64 user_id: &UserId,
65 input: &CreateAnalyticsEventInput,
66 ) -> AnalyticsEventRecord {
67 AnalyticsEventRecord {
68 id: format!("evt_{}", uuid::Uuid::new_v4()),
69 user_id: user_id.clone(),
70 session_id: session_id.clone(),
71 event_type: input.event_type.as_str().to_owned(),
72 event_category: input.event_type.category().to_owned(),
73 page_url: input.page_url.clone(),
74 event_data: Self::build_event_data(input),
75 }
76 }
77
78 fn build_event_data(input: &CreateAnalyticsEventInput) -> serde_json::Value {
79 let mut data = input.data.clone().unwrap_or(serde_json::json!({}));
80
81 if let Some(obj) = data.as_object_mut() {
82 if let Some(content_id) = &input.content_id {
83 obj.insert(
84 "content_id".to_owned(),
85 serde_json::json!(content_id.as_str()),
86 );
87 }
88 if let Some(slug) = &input.slug {
89 obj.insert("slug".to_owned(), serde_json::json!(slug));
90 }
91 if let Some(referrer) = &input.referrer {
92 obj.insert("referrer".to_owned(), serde_json::json!(referrer));
93 }
94 }
95
96 data
97 }
98}