roder_usage_analytics/
sink.rs1use std::sync::Arc;
11
12use roder_api::capabilities::CapabilityRequest;
13use roder_api::events::EventEnvelope;
14use roder_api::extension::{
15 EventSink, EventSinkId, ExtensionManifest, ExtensionRegistryBuilder, ProvidedService,
16 RoderExtension,
17};
18use semver::Version;
19
20use crate::ingest::AnalyticsIngestor;
21use crate::store::AnalyticsStore;
22
23pub const ANALYTICS_EXTENSION_ID: &str = "roder-usage-analytics";
24pub const ANALYTICS_SINK_ID: &str = "usage-analytics";
25
26pub struct UsageAnalyticsSink {
27 store: Arc<AnalyticsStore>,
28}
29
30impl UsageAnalyticsSink {
31 pub fn new(store: Arc<AnalyticsStore>) -> Self {
32 Self { store }
33 }
34}
35
36#[async_trait::async_trait]
37impl EventSink for UsageAnalyticsSink {
38 fn id(&self) -> EventSinkId {
39 ANALYTICS_SINK_ID.to_string()
40 }
41
42 async fn handle_event(&self, envelope: &EventEnvelope) -> anyhow::Result<()> {
43 AnalyticsIngestor::new(&self.store).ingest_event(envelope)
44 }
45}
46
47pub struct UsageAnalyticsExtension {
50 store: Arc<AnalyticsStore>,
51}
52
53impl UsageAnalyticsExtension {
54 pub fn new(store: Arc<AnalyticsStore>) -> Self {
55 Self { store }
56 }
57}
58
59impl RoderExtension for UsageAnalyticsExtension {
60 fn manifest(&self) -> ExtensionManifest {
61 ExtensionManifest {
62 id: ANALYTICS_EXTENSION_ID.to_string(),
63 name: "Local Usage Analytics".to_string(),
64 version: Version::new(0, 1, 0),
65 api_version: "0.1.0".to_string(),
66 description: Some(
67 "Projects local runtime events into the SQLite usage-analytics store.".to_string(),
68 ),
69 provides: vec![ProvidedService::EventSink(ANALYTICS_SINK_ID.to_string())],
70 required_capabilities: vec![CapabilityRequest::new("events.read.all")],
71 }
72 }
73
74 fn install(&self, registry: &mut ExtensionRegistryBuilder) -> anyhow::Result<()> {
75 registry.event_sink(Arc::new(UsageAnalyticsSink::new(self.store.clone())));
76 Ok(())
77 }
78}