@@ -175,19 +175,7 @@ public string Activate(string experimentKey, string userId, UserAttributes userA
175175 userAttributes = userAttributes . FilterNullValues ( Logger ) ;
176176 }
177177
178- var impressionEvent = EventBuilder . CreateImpressionEvent ( Config , experiment , variation . Id , userId , userAttributes ) ;
179- Logger . Log ( LogLevel . INFO , string . Format ( "Activating user {0} in experiment {1}." , userId , experimentKey ) ) ;
180- Logger . Log ( LogLevel . DEBUG , string . Format ( "Dispatching impression event to URL {0} with params {1}." ,
181- impressionEvent , impressionEvent . GetParamsAsJson ( ) ) ) ;
182-
183- try
184- {
185- EventDispatcher . DispatchEvent ( impressionEvent ) ;
186- }
187- catch ( Exception exception )
188- {
189- Logger . Log ( LogLevel . ERROR , string . Format ( "Unable to dispatch impression event. Error {0}" , exception . Message ) ) ;
190- }
178+ SendImpressionEvent ( experiment , variation . Id , userId , userAttributes ) ;
191179
192180 return variation . Key ;
193181 }
@@ -330,5 +318,249 @@ public Variation GetForcedVariation(string experimentKey, string userId)
330318
331319 return forcedVariation ;
332320 }
321+
322+ #region FeatureFlag APIs
323+
324+ /// <summary>
325+ /// Determine whether a feature is enabled.
326+ /// Send an impression event if the user is bucketed into an experiment using the feature.
327+ /// </summary>
328+ /// <param name="experimentKey">The experiment key</param>
329+ /// <param name="userId">The user ID</param>
330+ /// <param name="userAttributes">The user's attributes.</param>
331+ /// <returns>True if feature is enabled, false or null otherwise</returns>
332+ public bool ? IsFeatureEnabled ( string featureKey , string userId , UserAttributes userAttributes = null )
333+ {
334+ if ( string . IsNullOrEmpty ( userId ) )
335+ {
336+ Logger . Log ( LogLevel . ERROR , "User ID must not be empty." ) ;
337+ return null ;
338+ }
339+
340+ if ( string . IsNullOrEmpty ( featureKey ) )
341+ {
342+ Logger . Log ( LogLevel . ERROR , "Feature flag key must not be empty." ) ;
343+ return null ;
344+ }
345+
346+ var featureFlag = Config . GetFeatureFlagFromKey ( featureKey ) ;
347+ if ( string . IsNullOrEmpty ( featureFlag . Key ) )
348+ return null ;
349+
350+ if ( ! Validator . IsFeatureFlagValid ( Config , featureFlag ) )
351+ return false ;
352+
353+ var variation = DecisionService . GetVariationForFeature ( featureFlag , userId , userAttributes ) ;
354+ if ( variation == null )
355+ {
356+ Logger . Log ( LogLevel . INFO , $@ "Feature flag ""{ featureKey } "" is not enabled for user ""{ userId } "".") ;
357+ return false ;
358+ }
359+
360+ var experiment = Config . GetExperimentForVariationId ( variation . Id ) ;
361+
362+ if ( ! string . IsNullOrEmpty ( experiment . Key ) )
363+ SendImpressionEvent ( experiment , variation . Id , userId , userAttributes ) ;
364+ else
365+ Logger . Log ( LogLevel . INFO , $@ "The user ""{ userId } "" is not being experimented on feature ""{ featureKey } "".") ;
366+
367+ Logger . Log ( LogLevel . INFO , $@ "Feature flag ""{ featureKey } "" is enabled for user ""{ userId } "".") ;
368+ return true ;
369+ }
370+
371+ /// <summary>
372+ /// Gets the feature variable value for given type.
373+ /// </summary>
374+ /// <param name="featureKey">The feature flag key</param>
375+ /// <param name="variableKey">The variable key</param>
376+ /// <param name="userId">The user ID</param>
377+ /// <param name="userAttributes">The user's attributes</param>
378+ /// <param name="variableType">Variable type</param>
379+ /// <returns>string | null Feature variable value</returns>
380+ public virtual string GetFeatureVariableValueForType ( string featureKey , string variableKey , string userId ,
381+ UserAttributes userAttributes , FeatureVariable . VariableType variableType )
382+ {
383+ if ( string . IsNullOrEmpty ( featureKey ) )
384+ {
385+ Logger . Log ( LogLevel . ERROR , "Feature flag key must not be empty." ) ;
386+ return null ;
387+ }
388+
389+ if ( string . IsNullOrEmpty ( variableKey ) )
390+ {
391+ Logger . Log ( LogLevel . ERROR , "Variable key must not be empty." ) ;
392+ return null ;
393+ }
394+
395+ if ( string . IsNullOrEmpty ( userId ) )
396+ {
397+ Logger . Log ( LogLevel . ERROR , "User ID must not be empty." ) ;
398+ return null ;
399+ }
400+
401+ var featureFlag = Config . GetFeatureFlagFromKey ( featureKey ) ;
402+ if ( string . IsNullOrEmpty ( featureFlag . Key ) )
403+ return null ;
404+
405+ var featureVariable = featureFlag . GetFeatureVariableFromKey ( variableKey ) ;
406+ if ( featureVariable == null )
407+ {
408+ Logger . Log ( LogLevel . ERROR ,
409+ $@ "No feature variable was found for key ""{ variableKey } "" in feature flag ""{ featureKey } "".") ;
410+ return null ;
411+ }
412+ else if ( featureVariable . Type != variableType )
413+ {
414+ Logger . Log ( LogLevel . ERROR ,
415+ $@ "Variable is of type ""{ featureVariable . Type } "", but you requested it as type ""{ variableType } "".") ;
416+ return null ;
417+ }
418+
419+ var variableValue = featureVariable . DefaultValue ;
420+ var variation = DecisionService . GetVariationForFeature ( featureFlag , userId , userAttributes ) ;
421+
422+ if ( variation != null )
423+ {
424+ var featureVariableUsageInstance = variation . GetFeatureVariableUsageFromId ( featureVariable . Id ) ;
425+ if ( featureVariableUsageInstance != null )
426+ {
427+ variableValue = featureVariableUsageInstance . Value ;
428+ Logger . Log ( LogLevel . INFO ,
429+ $@ "Returning variable value ""{ variableValue } "" for variation ""{ variation . Key } "" of feature flag ""{ featureFlag . Key } "".") ;
430+ }
431+ else
432+ {
433+ Logger . Log ( LogLevel . INFO ,
434+ $@ "Variable ""{ variableKey } "" is not used in variation ""{ variation . Key } "", returning default value ""{ variableValue } "".") ;
435+ }
436+ }
437+ else
438+ {
439+ Logger . Log ( LogLevel . INFO ,
440+ $@ "User ""{ userId } "" is not in any variation for feature flag ""{ featureFlag . Key } "", returning default value ""{ variableValue } "".") ;
441+ }
442+
443+ return variableValue ;
444+ }
445+
446+ /// <summary>
447+ /// Gets boolean feature variable value.
448+ /// </summary>
449+ /// <param name="featureKey">The feature flag key</param>
450+ /// <param name="variableKey">The variable key</param>
451+ /// <param name="userId">The user ID</param>
452+ /// <param name="userAttributes">The user's attributes</param>
453+ /// <returns>bool | Feature variable value or null</returns>
454+ public bool ? GetFeatureVariableBoolean ( string featureKey , string variableKey , string userId , UserAttributes userAttributes )
455+ {
456+ var variableType = FeatureVariable . VariableType . BOOLEAN ;
457+ var variableValue = GetFeatureVariableValueForType ( featureKey , variableKey , userId , userAttributes , variableType ) ;
458+
459+ if ( variableValue != null )
460+ {
461+ if ( Boolean . TryParse ( variableValue , out bool booleanValue ) )
462+ return booleanValue ;
463+ else
464+ Logger . Log ( LogLevel . ERROR , $@ "Unable to cast variable value ""{ variableValue } "" to type ""{ variableType } "".") ;
465+ }
466+
467+ return null ;
468+ }
469+
470+ /// <summary>
471+ /// Gets double feature variable value.
472+ /// </summary>
473+ /// <param name="featureKey">The feature flag key</param>
474+ /// <param name="variableKey">The variable key</param>
475+ /// <param name="userId">The user ID</param>
476+ /// <param name="userAttributes">The user's attributes</param>
477+ /// <returns>double | Feature variable value or null</returns>
478+ public double ? GetFeatureVariableDouble ( string featureKey , string variableKey , string userId , UserAttributes userAttributes )
479+ {
480+ var variableType = FeatureVariable . VariableType . DOUBLE ;
481+ var variableValue = GetFeatureVariableValueForType ( featureKey , variableKey , userId , userAttributes , variableType ) ;
482+
483+ if ( variableValue != null )
484+ {
485+ if ( Double . TryParse ( variableValue , out double doubleValue ) )
486+ return doubleValue ;
487+ else
488+ Logger . Log ( LogLevel . ERROR , $@ "Unable to cast variable value ""{ variableValue } "" to type ""{ variableType } "".") ;
489+ }
490+
491+ return null ;
492+ }
493+
494+ /// <summary>
495+ /// Gets integer feature variable value.
496+ /// </summary>
497+ /// <param name="featureKey">The feature flag key</param>
498+ /// <param name="variableKey">The variable key</param>
499+ /// <param name="userId">The user ID</param>
500+ /// <param name="userAttributes">The user's attributes</param>
501+ /// <returns>int | Feature variable value or null</returns>
502+ public int ? GetFeatureVariableInteger ( string featureKey , string variableKey , string userId , UserAttributes userAttributes )
503+ {
504+ var variableType = FeatureVariable . VariableType . INTEGER ;
505+ var variableValue = GetFeatureVariableValueForType ( featureKey , variableKey , userId , userAttributes , variableType ) ;
506+
507+ if ( variableValue != null )
508+ {
509+ if ( Int32 . TryParse ( variableValue , out int intValue ) )
510+ return intValue ;
511+ else
512+ Logger . Log ( LogLevel . ERROR , $@ "Unable to cast variable value ""{ variableValue } "" to type ""{ variableType } "".") ;
513+ }
514+
515+ return null ;
516+ }
517+
518+ /// <summary>
519+ /// Gets string feature variable value.
520+ /// </summary>
521+ /// <param name="featureKey">The feature flag key</param>
522+ /// <param name="variableKey">The variable key</param>
523+ /// <param name="userId">The user ID</param>
524+ /// <param name="userAttributes">The user's attributes</param>
525+ /// <returns>string | Feature variable value or null</returns>
526+ public string GetFeatureVariableString ( string featureKey , string variableKey , string userId , UserAttributes userAttributes )
527+ {
528+ return GetFeatureVariableValueForType ( featureKey , variableKey , userId , userAttributes ,
529+ FeatureVariable . VariableType . STRING ) ;
530+ }
531+
532+ /// <summary>
533+ /// Sends impression event.
534+ /// </summary>
535+ /// <param name="experiment">The experiment</param>
536+ /// <param name="variationId">The variation Id</param>
537+ /// <param name="userId">The user ID</param>
538+ /// <param name="userAttributes">The user's attributes</param>
539+ private void SendImpressionEvent ( Experiment experiment , string variationId , string userId ,
540+ UserAttributes userAttributes )
541+ {
542+ if ( experiment . IsExperimentRunning )
543+ {
544+ var impressionEvent = EventBuilder . CreateImpressionEvent ( Config , experiment , variationId , userId , userAttributes ) ;
545+ Logger . Log ( LogLevel . INFO , string . Format ( "Activating user {0} in experiment {1}." , userId , experiment . Key ) ) ;
546+ Logger . Log ( LogLevel . DEBUG , string . Format ( "Dispatching impression event to URL {0} with params {1}." ,
547+ impressionEvent . Url , impressionEvent . GetParamsAsJson ( ) ) ) ;
548+
549+ try
550+ {
551+ EventDispatcher . DispatchEvent ( impressionEvent ) ;
552+ }
553+ catch ( Exception exception )
554+ {
555+ Logger . Log ( LogLevel . ERROR , string . Format ( "Unable to dispatch impression event. Error {0}" , exception . Message ) ) ;
556+ }
557+ }
558+ else
559+ {
560+ Logger . Log ( LogLevel . ERROR , @"Experiment has ""Launched"" status so not dispatching event during activation." ) ;
561+ }
562+ }
563+
564+ #endregion // FeatureFlag APIs
333565 }
334566}
0 commit comments