1- use std:: collections:: { BTreeMap , HashSet } ;
1+ use std:: collections:: { BTreeMap , HashMap , HashSet } ;
22
3+ use hive_router_config:: override_labels:: { LabelOverrideValue , OverrideLabelsConfig } ;
4+ use hive_router_plan_executor:: execution:: plan:: ClientRequestDetails ;
35use hive_router_query_planner:: {
46 graph:: { PlannerOverrideContext , PERCENTAGE_SCALE_FACTOR } ,
57 state:: supergraph_state:: SupergraphState ,
68} ;
79use rand:: Rng ;
10+ use vrl:: {
11+ compiler:: { compile as vrl_compile, Program as VrlProgram , TargetValue as VrlTargetValue } ,
12+ core:: Value as VrlValue ,
13+ prelude:: {
14+ state:: RuntimeState as VrlState , Context as VrlContext , ExpressionError ,
15+ TimeZone as VrlTimeZone ,
16+ } ,
17+ stdlib:: all as vrl_build_functions,
18+ value:: Secrets as VrlSecrets ,
19+ } ;
20+
21+ #[ derive( thiserror:: Error , Debug ) ]
22+ #[ error( "Failed to compile override label expression for label '{label}': {error}" ) ]
23+ pub struct OverrideLabelsCompileError {
24+ pub label : String ,
25+ pub error : String ,
26+ }
827
9- use super :: error:: PipelineError ;
28+ #[ derive( thiserror:: Error , Debug ) ]
29+ pub enum LabelEvaluationError {
30+ #[ error(
31+ "Failed to resolve VRL expression for override label '{label}'. Runtime error: {source}"
32+ ) ]
33+ ExpressionResolutionFailure {
34+ label : String ,
35+ source : ExpressionError ,
36+ } ,
37+ #[ error(
38+ "VRL expression for override label '{label}' did not evaluate to a boolean. Got: {got}"
39+ ) ]
40+ ExpressionWrongType { label : String , got : String } ,
41+ }
1042
1143/// Contains the request-specific context for progressive overrides.
1244/// This is stored in the request extensions
@@ -19,9 +51,14 @@ pub struct RequestOverrideContext {
1951}
2052
2153#[ inline]
22- pub fn request_override_context ( ) -> Result < RequestOverrideContext , PipelineError > {
23- // No active flags by default - until we implement it
24- let active_flags = HashSet :: new ( ) ;
54+ pub fn request_override_context < ' req , F > (
55+ override_labels_evaluator : & OverrideLabelsEvaluator ,
56+ get_client_request : F ,
57+ ) -> Result < RequestOverrideContext , LabelEvaluationError >
58+ where
59+ F : FnOnce ( ) -> ClientRequestDetails < ' req > ,
60+ {
61+ let active_flags = override_labels_evaluator. evaluate ( get_client_request) ?;
2562
2663 // Generate the random percentage value for this request.
2764 // Percentage is 0 - 100_000_000_000 (100*PERCENTAGE_SCALE_FACTOR)
@@ -77,3 +114,105 @@ impl StableOverrideContext {
77114 }
78115 }
79116}
117+
118+ /// Evaluator for override labels based on configuration.
119+ /// This struct compiles and evaluates the override label expressions.
120+ /// It's intended to be used as a shared state in the router.
121+ pub struct OverrideLabelsEvaluator {
122+ static_enabled_labels : HashSet < String > ,
123+ expressions : HashMap < String , VrlProgram > ,
124+ }
125+
126+ impl OverrideLabelsEvaluator {
127+ pub ( crate ) fn from_config (
128+ override_labels_config : & OverrideLabelsConfig ,
129+ ) -> Result < Self , OverrideLabelsCompileError > {
130+ let mut static_enabled_labels = HashSet :: new ( ) ;
131+ let mut expressions = HashMap :: new ( ) ;
132+ let vrl_functions = vrl_build_functions ( ) ;
133+
134+ for ( label, value) in override_labels_config. iter ( ) {
135+ match value {
136+ LabelOverrideValue :: Boolean ( true ) => {
137+ static_enabled_labels. insert ( label. clone ( ) ) ;
138+ }
139+ LabelOverrideValue :: Expression { expression } => {
140+ let compilation_result =
141+ vrl_compile ( expression, & vrl_functions) . map_err ( |diagnostics| {
142+ OverrideLabelsCompileError {
143+ label : label. clone ( ) ,
144+ error : diagnostics
145+ . errors ( )
146+ . into_iter ( )
147+ . map ( |d| d. code . to_string ( ) + ": " + & d. message )
148+ . collect :: < Vec < _ > > ( )
149+ . join ( ", " ) ,
150+ }
151+ } ) ?;
152+ expressions. insert ( label. clone ( ) , compilation_result. program ) ;
153+ }
154+ _ => { } // Skip false booleans
155+ }
156+ }
157+
158+ Ok ( Self {
159+ static_enabled_labels,
160+ expressions,
161+ } )
162+ }
163+
164+ pub ( crate ) fn evaluate < ' req , F > (
165+ & self ,
166+ get_client_request : F ,
167+ ) -> Result < HashSet < String > , LabelEvaluationError >
168+ where
169+ F : FnOnce ( ) -> ClientRequestDetails < ' req > ,
170+ {
171+ let mut active_flags = self . static_enabled_labels . clone ( ) ;
172+
173+ if self . expressions . is_empty ( ) {
174+ return Ok ( active_flags) ;
175+ }
176+
177+ let client_request = get_client_request ( ) ;
178+ let mut target = VrlTargetValue {
179+ value : VrlValue :: Object ( BTreeMap :: from ( [ (
180+ "request" . into ( ) ,
181+ ( & client_request) . into ( ) ,
182+ ) ] ) ) ,
183+ metadata : VrlValue :: Object ( BTreeMap :: new ( ) ) ,
184+ secrets : VrlSecrets :: default ( ) ,
185+ } ;
186+
187+ let mut state = VrlState :: default ( ) ;
188+ let timezone = VrlTimeZone :: default ( ) ;
189+ let mut ctx = VrlContext :: new ( & mut target, & mut state, & timezone) ;
190+
191+ for ( label, expression) in & self . expressions {
192+ match expression. resolve ( & mut ctx) {
193+ Ok ( evaluated_value) => match evaluated_value {
194+ VrlValue :: Boolean ( true ) => {
195+ active_flags. insert ( label. clone ( ) ) ;
196+ }
197+ VrlValue :: Boolean ( false ) => {
198+ // Do nothing for false
199+ }
200+ invalid_value => {
201+ return Err ( LabelEvaluationError :: ExpressionWrongType {
202+ label : label. clone ( ) ,
203+ got : format ! ( "{:?}" , invalid_value) ,
204+ } ) ;
205+ }
206+ } ,
207+ Err ( err) => {
208+ return Err ( LabelEvaluationError :: ExpressionResolutionFailure {
209+ label : label. clone ( ) ,
210+ source : err,
211+ } ) ;
212+ }
213+ }
214+ }
215+
216+ Ok ( active_flags)
217+ }
218+ }
0 commit comments