@@ -8,13 +8,14 @@ import { IFeatureFlagProvider } from "./featureProvider.js";
88import { TargetingFilter } from "./filter/TargetingFilter.js" ;
99import { Variant } from "./variant/Variant.js" ;
1010import { IFeatureManager } from "./IFeatureManager.js" ;
11- import { ITargetingContext } from "./common/ITargetingContext .js" ;
11+ import { ITargetingContext , TargetingContextAccessor } from "./common/targetingContext .js" ;
1212import { isTargetedGroup , isTargetedPercentile , isTargetedUser } from "./common/targetingEvaluator.js" ;
1313
1414export class FeatureManager implements IFeatureManager {
1515 #provider: IFeatureFlagProvider ;
1616 #featureFilters: Map < string , IFeatureFilter > = new Map ( ) ;
1717 #onFeatureEvaluated?: ( event : EvaluationResult ) => void ;
18+ #targetingContextAccessor?: TargetingContextAccessor ;
1819
1920 constructor ( provider : IFeatureFlagProvider , options ?: FeatureManagerOptions ) {
2021 this . #provider = provider ;
@@ -27,6 +28,7 @@ export class FeatureManager implements IFeatureManager {
2728 }
2829
2930 this . #onFeatureEvaluated = options ?. onFeatureEvaluated ;
31+ this . #targetingContextAccessor = options ?. targetingContextAccessor ;
3032 }
3133
3234 async listFeatureNames ( ) : Promise < string [ ] > {
@@ -102,11 +104,19 @@ export class FeatureManager implements IFeatureManager {
102104 for ( const clientFilter of clientFilters ) {
103105 const matchedFeatureFilter = this . #featureFilters. get ( clientFilter . name ) ;
104106 const contextWithFeatureName = { featureName : featureFlag . id , parameters : clientFilter . parameters } ;
107+ let clientFilterEvaluationResult : boolean ;
105108 if ( matchedFeatureFilter === undefined ) {
106109 console . warn ( `Feature filter ${ clientFilter . name } is not found.` ) ;
107- return false ;
110+ clientFilterEvaluationResult = false ;
108111 }
109- if ( await matchedFeatureFilter . evaluate ( contextWithFeatureName , context ) === shortCircuitEvaluationResult ) {
112+ else {
113+ let appContext = context ;
114+ if ( clientFilter . name === "Microsoft.Targeting" && this . #targetingContextAccessor !== undefined ) {
115+ appContext = this . #targetingContextAccessor( ) ;
116+ }
117+ clientFilterEvaluationResult = await matchedFeatureFilter . evaluate ( contextWithFeatureName , appContext ) ;
118+ }
119+ if ( clientFilterEvaluationResult === shortCircuitEvaluationResult ) {
110120 return shortCircuitEvaluationResult ;
111121 }
112122 }
@@ -130,7 +140,10 @@ export class FeatureManager implements IFeatureManager {
130140 // Evaluate if the feature is enabled.
131141 result . enabled = await this . #isEnabled( featureFlag , context ) ;
132142
133- const targetingContext = context as ITargetingContext ;
143+ let targetingContext = context as ITargetingContext ;
144+ if ( this . #targetingContextAccessor !== undefined ) {
145+ targetingContext = this . #targetingContextAccessor( ) ;
146+ }
134147 result . targetingId = targetingContext ?. userId ;
135148
136149 // Determine Variant
@@ -151,7 +164,7 @@ export class FeatureManager implements IFeatureManager {
151164 }
152165 } else {
153166 // enabled, assign based on allocation
154- if ( context !== undefined && featureFlag . allocation !== undefined ) {
167+ if ( targetingContext !== undefined && featureFlag . allocation !== undefined ) {
155168 const variantAndReason = await this . #assignVariant( featureFlag , targetingContext ) ;
156169 variantDef = variantAndReason . variant ;
157170 reason = variantAndReason . reason ;
@@ -202,6 +215,11 @@ export interface FeatureManagerOptions {
202215 * The callback function is called only when telemetry is enabled for the feature flag.
203216 */
204217 onFeatureEvaluated ?: ( event : EvaluationResult ) => void ;
218+
219+ /**
220+ * The accessor function that provides the @see ITargetingContext for targeting evaluation.
221+ */
222+ targetingContextAccessor ?: TargetingContextAccessor ;
205223}
206224
207225export class EvaluationResult {
0 commit comments