Skip to main content

statsig_rust/event_logging/event_queue/
queued_config_expo.rs

1use crate::{
2    evaluation::evaluation_types::ExtraExposureInfo,
3    event_logging::{
4        event_logger::ExposureTrigger,
5        exposure_sampling::{EvtSamplingDecision, ExposureSamplingKey},
6        exposure_utils::{get_metadata_with_details, get_statsig_metadata_with_sampling_decision},
7        statsig_event::StatsigEvent,
8        statsig_event_internal::{StatsigEventInternal, CONFIG_EXPOSURE_EVENT_NAME},
9    },
10    interned_string::InternedString,
11    statsig_types::DynamicConfig,
12    user::{StatsigUserInternal, StatsigUserLoggable},
13    EvaluationDetails, SecondaryExposure,
14};
15
16use super::queued_event::{EnqueueOperation, QueuedEvent, QueuedExposure};
17use crate::event_logging::statsig_event::string_metadata_to_value_metadata;
18
19pub struct EnqueueConfigExpoOp<'a> {
20    pub user: &'a StatsigUserInternal<'a, 'a>,
21    pub config: &'a DynamicConfig,
22    pub trigger: ExposureTrigger,
23    pub exposure_time: u64,
24}
25
26impl EnqueueOperation for EnqueueConfigExpoOp<'_> {
27    fn as_exposure(&self) -> Option<&impl QueuedExposure<'_>> {
28        Some(self)
29    }
30
31    fn into_queued_event(self, sampling_decision: EvtSamplingDecision) -> QueuedEvent {
32        let evaluation = self.config.__evaluation.as_ref();
33        let secondary_exposures = evaluation.map(|eval| &eval.base.secondary_exposures);
34        let exposure_info = evaluation.and_then(|eval| eval.base.exposure_info.as_ref());
35        let (version, override_config_name) = exposure_info
36            .map(|info| (info.version, info.override_config_name.clone()))
37            .unwrap_or_default();
38
39        QueuedEvent::ConfigExposure(QueuedConfigExposureEvent {
40            user: self.user.to_loggable(),
41            config_name: self.config.name.clone(),
42            rule_id: self.config.rule_id.clone(),
43            rule_passed: evaluation.map(|eval| eval.passed),
44            secondary_exposures: secondary_exposures.cloned(),
45            evaluation_details: self.config.details.clone(),
46            version,
47            exposure_trigger: self.trigger,
48            sampling_decision,
49            override_config_name,
50            exposure_time: self.exposure_time,
51        })
52    }
53}
54
55impl<'a> QueuedExposure<'a> for EnqueueConfigExpoOp<'a> {
56    fn create_exposure_sampling_key(&self) -> ExposureSamplingKey {
57        let user_data = &self.user.user_ref.data;
58        let evaluation = self.config.__evaluation.as_ref().map(|e| &e.base);
59        let passed = self.config.__evaluation.as_ref().is_some_and(|e| e.passed);
60
61        ExposureSamplingKey::new(evaluation, user_data.as_ref(), passed as u64)
62    }
63
64    fn get_rule_id_ref(&self) -> &'a str {
65        &self.config.rule_id
66    }
67
68    fn get_extra_exposure_info_ref(&self) -> Option<&'a ExtraExposureInfo> {
69        self.config
70            .__evaluation
71            .as_ref()?
72            .base
73            .exposure_info
74            .as_ref()
75    }
76}
77
78pub struct QueuedConfigExposureEvent {
79    pub user: StatsigUserLoggable,
80    pub config_name: String,
81    pub rule_id: String,
82    pub rule_passed: Option<bool>,
83    pub secondary_exposures: Option<Vec<SecondaryExposure>>,
84    pub evaluation_details: EvaluationDetails,
85    pub version: Option<u32>,
86    pub exposure_trigger: ExposureTrigger,
87    pub sampling_decision: EvtSamplingDecision,
88    pub override_config_name: Option<InternedString>,
89    pub exposure_time: u64,
90}
91
92impl QueuedConfigExposureEvent {
93    pub fn into_statsig_event_internal(self) -> StatsigEventInternal {
94        let mut metadata = get_metadata_with_details(self.evaluation_details);
95        metadata.insert("config".into(), self.config_name);
96        metadata.insert("ruleID".into(), self.rule_id);
97
98        if let Some(version) = self.version {
99            metadata.insert("configVersion".into(), version.to_string());
100        }
101
102        if let Some(rule_passed) = self.rule_passed {
103            metadata.insert("rulePassed".into(), rule_passed.to_string());
104        }
105
106        if self.exposure_trigger == ExposureTrigger::Manual {
107            metadata.insert("isManualExposure".into(), "true".into());
108        }
109
110        if let Some(override_config_name) = self.override_config_name {
111            metadata.insert(
112                "overrideConfigName".into(),
113                override_config_name.unperformant_to_string(),
114            );
115        }
116
117        let statsig_metadata = get_statsig_metadata_with_sampling_decision(self.sampling_decision);
118
119        let event = StatsigEvent {
120            event_name: CONFIG_EXPOSURE_EVENT_NAME.into(),
121            value: None,
122            metadata: Some(string_metadata_to_value_metadata(metadata)),
123            statsig_metadata: Some(statsig_metadata),
124        };
125
126        StatsigEventInternal::new(
127            self.exposure_time,
128            self.user,
129            event,
130            Some(self.secondary_exposures.unwrap_or_default()),
131        )
132    }
133}