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