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,34 @@ 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 );
514-
515- if (result .treatment .equals (Treatments .CONTROL ) && result .label .equals (Labels .DEFINITION_NOT_FOUND ) && _gates .isSDKReady ()) {
516- _log .warn (String .format (
517- "%s: you passed \" %s\" that does not exist in this environment, " +
518- "please double check what feature flags exist in the Split user interface." , methodEnum .getMethod (), featureFlag ));
519- return SPLIT_RESULT_CONTROL ;
513+ String label = result .label ;
514+ if (result .label != null && result .label .contains (Labels .DEFINITION_NOT_FOUND )) {
515+ if (_gates .isSDKReady ()) {
516+ _log .warn (String .format (
517+ "%s: you passed \" %s\" that does not exist in this environment, " +
518+ "please double check what feature flags exist in the Split user interface." , methodEnum .getMethod (), featureFlag ));
519+ return checkFallbackTreatment (featureFlag );
520+ }
521+ label = result .label .replace (Labels .DEFINITION_NOT_FOUND , Labels .NOT_READY );
520522 }
521523
522524 recordStats (
@@ -526,7 +528,7 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
526528 start ,
527529 result .treatment ,
528530 String .format ("sdk.%s" , methodEnum .getMethod ()),
529- _config .labelsEnabled () ? result . label : null ,
531+ _config .labelsEnabled () ? label : null ,
530532 result .changeNumber ,
531533 attributes ,
532534 result .track ,
@@ -541,8 +543,17 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
541543 } catch (Exception e1 ) {
542544 // ignore
543545 }
544- return SPLIT_RESULT_CONTROL ;
546+ return checkFallbackTreatment (featureFlag );
547+ }
548+ }
549+
550+ private SplitResult checkFallbackTreatment (String featureName ) {
551+ FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator .resolve (featureName , "" );
552+ String config = null ;
553+ if (fallbackTreatment .getConfig () != null ) {
554+ config = fallbackTreatment .getConfig ();
545555 }
556+ return new SplitResult (fallbackTreatment .getTreatment (), config );
546557 }
547558
548559 private String validateProperties (Map <String , Object > properties ) {
@@ -563,6 +574,7 @@ private Map<String, SplitResult> getTreatmentsWithConfigInternal(String matching
563574 _log .error (String .format ("%s: featureFlagNames must be a non-empty array" , methodEnum .getMethod ()));
564575 return new HashMap <>();
565576 }
577+
566578 try {
567579 checkSDKReady (methodEnum , featureFlagNames );
568580 Map <String , SplitResult > result = validateBeforeEvaluate (featureFlagNames , matchingKey , methodEnum , bucketingKey );
@@ -601,47 +613,47 @@ private Map<String, SplitResult> getTreatmentsBySetsWithConfigInternal(String ma
601613 if (cleanFlagSets .isEmpty ()) {
602614 return new HashMap <>();
603615 }
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 );
616+ checkSDKReady (methodEnum );
617+ Map <String , SplitResult > result = validateBeforeEvaluateByFlagSets (matchingKey , methodEnum ,bucketingKey );
618+ if (result != null ) {
619+ return result ;
620+ }
621+ Map <String , EvaluatorImp .TreatmentLabelAndChangeNumber > evaluatorResult = _evaluator .evaluateFeaturesByFlagSets (matchingKey ,
622+ bucketingKey , new ArrayList <>(cleanFlagSets ), attributes );
613623
614- return processEvaluatorResult (evaluatorResult , methodEnum , matchingKey , bucketingKey , attributes , initTime ,
615- validateProperties (evaluationOptions .getProperties ()));
616- } catch (Exception e ) {
617- try {
624+ evaluatorResult .entrySet ().forEach (flag -> {
625+ if (flag .getValue ().label != null &&
626+ flag .getValue ().label .contains (io .split .engine .evaluator .Labels .EXCEPTION )) {
618627 _telemetryEvaluationProducer .recordException (methodEnum );
619- _log .error (CATCHALL_EXCEPTION , e );
620- } catch (Exception e1 ) {
621- // ignore
622628 }
623- return createMapControl (featureFlagNames );
624- }
629+ });
630+ return processEvaluatorResult (evaluatorResult , methodEnum , matchingKey , bucketingKey , attributes , initTime ,
631+ validateProperties (evaluationOptions .getProperties ()));
625632 }
633+
626634 private Map <String , SplitResult > processEvaluatorResult (Map <String , EvaluatorImp .TreatmentLabelAndChangeNumber > evaluatorResult ,
627635 MethodEnum methodEnum , String matchingKey , String bucketingKey , Map <String ,
628636 Object > attributes , long initTime , String properties ){
629637 List <DecoratedImpression > decoratedImpressions = new ArrayList <>();
630638 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 ()) {
634- _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 );
637- } else {
638- result .put (t , new SplitResult (evaluatorResult .get (t ).treatment , evaluatorResult .get (t ).configurations ));
639- decoratedImpressions .add (
640- 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 ));
639+ evaluatorResult .keySet ().forEach (flag -> {
640+ String label = evaluatorResult .get (flag ).label ;
641+ if (evaluatorResult .get (flag ).label != null &&
642+ evaluatorResult .get (flag ).label .contains (Labels .DEFINITION_NOT_FOUND )) {
643+ if (_gates .isSDKReady ()) {
644+ _log .warn (String .format ("%s: you passed \" %s\" that does not exist in this environment please double check " +
645+ "what feature flags exist in the Split user interface." , methodEnum .getMethod (), flag ));
646+ result .put (flag , checkFallbackTreatment (flag ));
647+ return ;
648+ }
649+ label = evaluatorResult .get (flag ).label .replace (Labels .DEFINITION_NOT_FOUND , Labels .NOT_READY );
644650 }
651+ result .put (flag , new SplitResult (evaluatorResult .get (flag ).treatment , evaluatorResult .get (flag ).configurations ));
652+ decoratedImpressions .add (
653+ new DecoratedImpression (
654+ new Impression (matchingKey , bucketingKey , flag , evaluatorResult .get (flag ).treatment , System .currentTimeMillis (),
655+ label , evaluatorResult .get (flag ).changeNumber , attributes , properties ),
656+ evaluatorResult .get (flag ).track ));
645657 });
646658 _telemetryEvaluationProducer .recordLatency (methodEnum , System .currentTimeMillis () - initTime );
647659 if (!decoratedImpressions .isEmpty ()) {
@@ -735,7 +747,7 @@ private void checkSDKReady(MethodEnum methodEnum) {
735747
736748 private Map <String , SplitResult > createMapControl (List <String > featureFlags ) {
737749 Map <String , SplitResult > result = new HashMap <>();
738- featureFlags .forEach (s -> result .put (s , SPLIT_RESULT_CONTROL ));
750+ featureFlags .forEach (s -> result .put (s , checkFallbackTreatment ( s ) ));
739751 return result ;
740752 }
741753}
0 commit comments