33import com .google .gson .GsonBuilder ;
44import io .split .client .api .Key ;
55import io .split .client .api .SplitResult ;
6- import io .split .client .dtos .DecoratedImpression ;
7- import io .split .client .dtos .EvaluationOptions ;
8- import io .split .client .dtos .Event ;
6+ import io .split .client .dtos .*;
97import io .split .client .events .EventsStorageProducer ;
108import io .split .client .impressions .Impression ;
119import io .split .client .impressions .ImpressionsManager ;
1412import io .split .engine .evaluator .Evaluator ;
1513import io .split .engine .evaluator .EvaluatorImp ;
1614import io .split .engine .evaluator .Labels ;
17- import io .split .grammar .Treatments ;
1815import io .split .inputValidation .EventsValidator ;
1916import io .split .inputValidation .KeyValidator ;
2017import io .split .inputValidation .SplitNameValidator ;
4946 * @author adil
5047 */
5148public final class SplitClientImpl implements SplitClient {
52- public static final SplitResult SPLIT_RESULT_CONTROL = new SplitResult (Treatments .CONTROL , null );
5349 private static final String CLIENT_DESTROY = "Client has already been destroyed - no calls possible" ;
5450 private static final String CATCHALL_EXCEPTION = "CatchAll Exception" ;
5551 private static final String MATCHING_KEY = "matchingKey" ;
@@ -66,6 +62,7 @@ public final class SplitClientImpl implements SplitClient {
6662 private final TelemetryEvaluationProducer _telemetryEvaluationProducer ;
6763 private final TelemetryConfigProducer _telemetryConfigProducer ;
6864 private final FlagSetsFilter _flagSetsFilter ;
65+ private final FallbackTreatmentCalculator _fallbackTreatmentCalculator ;
6966
7067 public SplitClientImpl (SplitFactory container ,
7168 SplitCacheConsumer splitCacheConsumer ,
@@ -76,7 +73,8 @@ public SplitClientImpl(SplitFactory container,
7673 Evaluator evaluator ,
7774 TelemetryEvaluationProducer telemetryEvaluationProducer ,
7875 TelemetryConfigProducer telemetryConfigProducer ,
79- FlagSetsFilter flagSetsFilter ) {
76+ FlagSetsFilter flagSetsFilter ,
77+ FallbackTreatmentCalculator fallbackTreatmentCalculator ) {
8078 _container = container ;
8179 _splitCacheConsumer = checkNotNull (splitCacheConsumer );
8280 _impressionManager = checkNotNull (impressionManager );
@@ -87,6 +85,7 @@ public SplitClientImpl(SplitFactory container,
8785 _telemetryEvaluationProducer = checkNotNull (telemetryEvaluationProducer );
8886 _telemetryConfigProducer = checkNotNull (telemetryConfigProducer );
8987 _flagSetsFilter = flagSetsFilter ;
88+ _fallbackTreatmentCalculator = fallbackTreatmentCalculator ;
9089 }
9190
9291 @ Override
@@ -492,31 +491,31 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
492491
493492 if (_container .isDestroyed ()) {
494493 _log .error (CLIENT_DESTROY );
495- return SPLIT_RESULT_CONTROL ;
494+ return checkFallbackTreatment ( featureFlag ) ;
496495 }
497496
498497 if (!KeyValidator .isValid (matchingKey , MATCHING_KEY , _config .maxStringLength (), methodEnum .getMethod ())) {
499- return SPLIT_RESULT_CONTROL ;
498+ return checkFallbackTreatment ( featureFlag ) ;
500499 }
501500
502501 if (!KeyValidator .bucketingKeyIsValid (bucketingKey , _config .maxStringLength (), methodEnum .getMethod ())) {
503- return SPLIT_RESULT_CONTROL ;
502+ return checkFallbackTreatment ( featureFlag ) ;
504503 }
505504
506505 Optional <String > splitNameResult = SplitNameValidator .isValid (featureFlag , methodEnum .getMethod ());
507506 if (!splitNameResult .isPresent ()) {
508- return SPLIT_RESULT_CONTROL ;
507+ return checkFallbackTreatment ( featureFlag ) ;
509508 }
510509 featureFlag = splitNameResult .get ();
511510 long start = System .currentTimeMillis ();
512511
513512 EvaluatorImp .TreatmentLabelAndChangeNumber result = _evaluator .evaluateFeature (matchingKey , bucketingKey , featureFlag , attributes );
514513
515- if (result .treatment . equals ( Treatments . CONTROL ) && result .label .equals (Labels .DEFINITION_NOT_FOUND ) && _gates .isSDKReady ()) {
514+ if (result .label != null && result .label .contains (Labels .DEFINITION_NOT_FOUND ) && _gates .isSDKReady ()) {
516515 _log .warn (String .format (
517516 "%s: you passed \" %s\" that does not exist in this environment, " +
518517 "please double check what feature flags exist in the Split user interface." , methodEnum .getMethod (), featureFlag ));
519- return SPLIT_RESULT_CONTROL ;
518+ return checkFallbackTreatment ( featureFlag ) ;
520519 }
521520
522521 recordStats (
@@ -541,10 +540,19 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
541540 } catch (Exception e1 ) {
542541 // ignore
543542 }
544- return SPLIT_RESULT_CONTROL ;
543+ return checkFallbackTreatment ( featureFlag ) ;
545544 }
546545 }
547546
547+ private SplitResult checkFallbackTreatment (String featureName ) {
548+ FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator .resolve (featureName , "" );
549+ String config = null ;
550+ if (fallbackTreatment .getConfig () != null ) {
551+ config = fallbackTreatment .getConfig ();
552+ }
553+ return new SplitResult (fallbackTreatment .getTreatment (), config );
554+ }
555+
548556 private String validateProperties (Map <String , Object > properties ) {
549557 if (properties == null ){
550558 return null ;
@@ -563,6 +571,7 @@ private Map<String, SplitResult> getTreatmentsWithConfigInternal(String matching
563571 _log .error (String .format ("%s: featureFlagNames must be a non-empty array" , methodEnum .getMethod ()));
564572 return new HashMap <>();
565573 }
574+
566575 try {
567576 checkSDKReady (methodEnum , featureFlagNames );
568577 Map <String , SplitResult > result = validateBeforeEvaluate (featureFlagNames , matchingKey , methodEnum , bucketingKey );
@@ -601,46 +610,43 @@ private Map<String, SplitResult> getTreatmentsBySetsWithConfigInternal(String ma
601610 if (cleanFlagSets .isEmpty ()) {
602611 return new HashMap <>();
603612 }
604- List <String > featureFlagNames = new ArrayList <>();
605- try {
606- checkSDKReady (methodEnum );
607- Map <String , SplitResult > result = validateBeforeEvaluateByFlagSets (matchingKey , methodEnum ,bucketingKey );
608- if (result != null ) {
609- return result ;
610- }
611- Map <String , EvaluatorImp .TreatmentLabelAndChangeNumber > evaluatorResult = _evaluator .evaluateFeaturesByFlagSets (matchingKey ,
612- bucketingKey , new ArrayList <>(cleanFlagSets ), attributes );
613+ checkSDKReady (methodEnum );
614+ Map <String , SplitResult > result = validateBeforeEvaluateByFlagSets (matchingKey , methodEnum ,bucketingKey );
615+ if (result != null ) {
616+ return result ;
617+ }
618+ Map <String , EvaluatorImp .TreatmentLabelAndChangeNumber > evaluatorResult = _evaluator .evaluateFeaturesByFlagSets (matchingKey ,
619+ bucketingKey , new ArrayList <>(cleanFlagSets ), attributes );
613620
614- return processEvaluatorResult (evaluatorResult , methodEnum , matchingKey , bucketingKey , attributes , initTime ,
615- validateProperties (evaluationOptions .getProperties ()));
616- } catch (Exception e ) {
617- try {
621+ evaluatorResult .entrySet ().forEach (flag -> {
622+ if (flag .getValue ().label != null &&
623+ flag .getValue ().label .contains (io .split .engine .evaluator .Labels .EXCEPTION )) {
618624 _telemetryEvaluationProducer .recordException (methodEnum );
619- _log .error (CATCHALL_EXCEPTION , e );
620- } catch (Exception e1 ) {
621- // ignore
622625 }
623- return createMapControl (featureFlagNames );
624- }
626+ });
627+ return processEvaluatorResult (evaluatorResult , methodEnum , matchingKey , bucketingKey , attributes , initTime ,
628+ validateProperties (evaluationOptions .getProperties ()));
625629 }
630+
626631 private Map <String , SplitResult > processEvaluatorResult (Map <String , EvaluatorImp .TreatmentLabelAndChangeNumber > evaluatorResult ,
627632 MethodEnum methodEnum , String matchingKey , String bucketingKey , Map <String ,
628633 Object > attributes , long initTime , String properties ){
629634 List <DecoratedImpression > decoratedImpressions = new ArrayList <>();
630635 Map <String , SplitResult > result = new HashMap <>();
631- evaluatorResult .keySet ().forEach (t -> {
632- if (evaluatorResult .get (t ).treatment .equals (Treatments .CONTROL ) && evaluatorResult .get (t ).label .
633- equals (Labels .DEFINITION_NOT_FOUND ) && _gates .isSDKReady ()) {
636+ evaluatorResult .keySet ().forEach (flag -> {
637+ if (evaluatorResult .get (flag ).label != null &&
638+ evaluatorResult .get (flag ).label .contains (Labels .DEFINITION_NOT_FOUND ) &&
639+ _gates .isSDKReady ()) {
634640 _log .warn (String .format ("%s: you passed \" %s\" that does not exist in this environment please double check " +
635- "what feature flags exist in the Split user interface." , methodEnum .getMethod (), t ));
636- result .put (t , SPLIT_RESULT_CONTROL );
641+ "what feature flags exist in the Split user interface." , methodEnum .getMethod (), flag ));
642+ result .put (flag , checkFallbackTreatment ( flag ) );
637643 } else {
638- result .put (t , new SplitResult (evaluatorResult .get (t ).treatment , evaluatorResult .get (t ).configurations ));
644+ result .put (flag , new SplitResult (evaluatorResult .get (flag ).treatment , evaluatorResult .get (flag ).configurations ));
639645 decoratedImpressions .add (
640646 new DecoratedImpression (
641- new Impression (matchingKey , bucketingKey , t , evaluatorResult .get (t ).treatment , System .currentTimeMillis (),
642- evaluatorResult .get (t ).label , evaluatorResult .get (t ).changeNumber , attributes , properties ),
643- evaluatorResult .get (t ).track ));
647+ new Impression (matchingKey , bucketingKey , flag , evaluatorResult .get (flag ).treatment , System .currentTimeMillis (),
648+ evaluatorResult .get (flag ).label , evaluatorResult .get (flag ).changeNumber , attributes , properties ),
649+ evaluatorResult .get (flag ).track ));
644650 }
645651 });
646652 _telemetryEvaluationProducer .recordLatency (methodEnum , System .currentTimeMillis () - initTime );
@@ -735,7 +741,7 @@ private void checkSDKReady(MethodEnum methodEnum) {
735741
736742 private Map <String , SplitResult > createMapControl (List <String > featureFlags ) {
737743 Map <String , SplitResult > result = new HashMap <>();
738- featureFlags .forEach (s -> result .put (s , SPLIT_RESULT_CONTROL ));
744+ featureFlags .forEach (s -> result .put (s , checkFallbackTreatment ( s ) ));
739745 return result ;
740746 }
741747}
0 commit comments