statsig_rust/event_logging/event_queue/
queued_config_expo.rs1use 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 let unit_id_type = self
61 .config
62 .__evaluation
63 .as_ref()
64 .and_then(|e| e.id_type.as_ref())
65 .map(|id| id.as_str());
66 let additional_hash = passed as u64;
67
68 ExposureSamplingKey::new(evaluation, user_data, additional_hash, unit_id_type)
69 }
70
71 fn get_rule_id_ref(&self) -> &'a str {
72 &self.config.rule_id
73 }
74
75 fn get_extra_exposure_info_ref(&self) -> Option<&'a ExtraExposureInfo> {
76 self.config
77 .__evaluation
78 .as_ref()?
79 .base
80 .exposure_info
81 .as_ref()
82 }
83}
84
85pub struct QueuedConfigExposureEvent {
86 pub user: StatsigUserLoggable,
87 pub config_name: String,
88 pub rule_id: String,
89 pub rule_passed: Option<bool>,
90 pub secondary_exposures: Option<Vec<SecondaryExposure>>,
91 pub evaluation_details: EvaluationDetails,
92 pub version: Option<u32>,
93 pub exposure_trigger: ExposureTrigger,
94 pub sampling_decision: EvtSamplingDecision,
95 pub override_config_name: Option<InternedString>,
96 pub exposure_time: u64,
97}
98
99impl QueuedConfigExposureEvent {
100 pub fn into_statsig_event_internal(self) -> StatsigEventInternal {
101 let mut metadata = get_metadata_with_details(self.evaluation_details);
102 metadata.insert("config".into(), self.config_name);
103 metadata.insert("ruleID".into(), self.rule_id);
104
105 if let Some(version) = self.version {
106 metadata.insert("configVersion".into(), version.to_string());
107 }
108
109 if let Some(rule_passed) = self.rule_passed {
110 metadata.insert("rulePassed".into(), rule_passed.to_string());
111 }
112
113 if self.exposure_trigger == ExposureTrigger::Manual {
114 metadata.insert("isManualExposure".into(), "true".into());
115 }
116
117 if let Some(override_config_name) = self.override_config_name {
118 metadata.insert(
119 "overrideConfigName".into(),
120 override_config_name.unperformant_to_string(),
121 );
122 }
123
124 let statsig_metadata = get_statsig_metadata_with_sampling_decision(self.sampling_decision);
125
126 let event = StatsigEvent {
127 event_name: CONFIG_EXPOSURE_EVENT_NAME.into(),
128 value: None,
129 metadata: Some(string_metadata_to_value_metadata(metadata)),
130 statsig_metadata: Some(statsig_metadata),
131 };
132
133 StatsigEventInternal::new(
134 self.exposure_time,
135 self.user,
136 event,
137 Some(self.secondary_exposures.unwrap_or_default()),
138 )
139 }
140}