diff --git a/client/src/main/java/io/split/client/SplitClientImpl.java b/client/src/main/java/io/split/client/SplitClientImpl.java index 32b68b09..38ab5e6b 100644 --- a/client/src/main/java/io/split/client/SplitClientImpl.java +++ b/client/src/main/java/io/split/client/SplitClientImpl.java @@ -510,12 +510,15 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu long start = System.currentTimeMillis(); EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(matchingKey, bucketingKey, featureFlag, attributes); - - if (result.label != null && result.label.contains(Labels.DEFINITION_NOT_FOUND) && _gates.isSDKReady()) { - _log.warn(String.format( - "%s: you passed \"%s\" that does not exist in this environment, " + - "please double check what feature flags exist in the Split user interface.", methodEnum.getMethod(), featureFlag)); - return checkFallbackTreatment(featureFlag); + String label = result.label; + if (result.label != null && result.label.contains(Labels.DEFINITION_NOT_FOUND)) { + if (_gates.isSDKReady()) { + _log.warn(String.format( + "%s: you passed \"%s\" that does not exist in this environment, " + + "please double check what feature flags exist in the Split user interface.", methodEnum.getMethod(), featureFlag)); + return checkFallbackTreatment(featureFlag); + } + label = result.label.replace(Labels.DEFINITION_NOT_FOUND, Labels.NOT_READY); } recordStats( @@ -525,7 +528,7 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu start, result.treatment, String.format("sdk.%s", methodEnum.getMethod()), - _config.labelsEnabled() ? result.label : null, + _config.labelsEnabled() ? label : null, result.changeNumber, attributes, result.track, @@ -634,20 +637,23 @@ private Map processEvaluatorResult(Map decoratedImpressions = new ArrayList<>(); Map result = new HashMap<>(); evaluatorResult.keySet().forEach(flag -> { + String label = evaluatorResult.get(flag).label; if (evaluatorResult.get(flag).label != null && - evaluatorResult.get(flag).label.contains(Labels.DEFINITION_NOT_FOUND) && - _gates.isSDKReady()) { - _log.warn(String.format("%s: you passed \"%s\" that does not exist in this environment please double check " + - "what feature flags exist in the Split user interface.", methodEnum.getMethod(), flag)); - result.put(flag, checkFallbackTreatment(flag)); - } else { - result.put(flag, new SplitResult(evaluatorResult.get(flag).treatment, evaluatorResult.get(flag).configurations)); - decoratedImpressions.add( - new DecoratedImpression( - new Impression(matchingKey, bucketingKey, flag, evaluatorResult.get(flag).treatment, System.currentTimeMillis(), - evaluatorResult.get(flag).label, evaluatorResult.get(flag).changeNumber, attributes, properties), - evaluatorResult.get(flag).track)); + evaluatorResult.get(flag).label.contains(Labels.DEFINITION_NOT_FOUND)) { + if (_gates.isSDKReady()) { + _log.warn(String.format("%s: you passed \"%s\" that does not exist in this environment please double check " + + "what feature flags exist in the Split user interface.", methodEnum.getMethod(), flag)); + result.put(flag, checkFallbackTreatment(flag)); + return; + } + label = evaluatorResult.get(flag).label.replace(Labels.DEFINITION_NOT_FOUND, Labels.NOT_READY); } + result.put(flag, new SplitResult(evaluatorResult.get(flag).treatment, evaluatorResult.get(flag).configurations)); + decoratedImpressions.add( + new DecoratedImpression( + new Impression(matchingKey, bucketingKey, flag, evaluatorResult.get(flag).treatment, System.currentTimeMillis(), + label, evaluatorResult.get(flag).changeNumber, attributes, properties), + evaluatorResult.get(flag).track)); }); _telemetryEvaluationProducer.recordLatency(methodEnum, System.currentTimeMillis() - initTime); if (!decoratedImpressions.isEmpty()) { diff --git a/client/src/main/java/io/split/engine/evaluator/Labels.java b/client/src/main/java/io/split/engine/evaluator/Labels.java index 9bda16a8..28966d51 100644 --- a/client/src/main/java/io/split/engine/evaluator/Labels.java +++ b/client/src/main/java/io/split/engine/evaluator/Labels.java @@ -8,4 +8,5 @@ public class Labels { public static final String EXCEPTION = "exception"; public static final String UNSUPPORTED_MATCHER = "targeting rule type unsupported by sdk"; public static final String PREREQUISITES_NOT_MET = "prerequisites not met"; + public static final String NOT_READY = "not ready"; } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index 04229039..4bdbc598 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -1393,6 +1393,83 @@ public MockResponse dispatch(RecordedRequest request) { Assert.assertTrue(check1); } + @Test + public void FallbackTreatmentNotReadyTest() throws Exception { + String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/splits_imp_toggle.json")), StandardCharsets.UTF_8); + List allRequests = new ArrayList<>(); + Dispatcher dispatcher = new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + allRequests.add(request); + switch (request.getPath()) { + case "/api/splitChanges?s=1.3&since=-1&rbSince=-1": + Thread.sleep(1000); + return new MockResponse().setResponseCode(200).setBody(splits); + case "/api/splitChanges?s=1.3&since=1602796638344&rbSince=-1": + return new MockResponse().setResponseCode(200).setBody("{\"ff\":{\"d\":[], \"s\":1602796638344, \"t\":1602796638344}, \"rbs\":{\"d\":[],\"s\":-1,\"t\":-1}}"); + case "/api/testImpressions/bulk": + return new MockResponse().setResponseCode(200); + case "/api/testImpressions/count": + return new MockResponse().setResponseCode(200); + case "/v1/keys/ss": + return new MockResponse().setResponseCode(200); + case "/v1/metrics/usage": + return new MockResponse().setResponseCode(200); + case "/v1/metrics/config": + return new MockResponse().setResponseCode(200); + } + return new MockResponse().setResponseCode(404); + } + }; + + MockWebServer server = new MockWebServer(); + server.setDispatcher(dispatcher); + + server.start(); + String serverURL = String.format("http://%s:%s", server.getHostName(), server.getPort()); + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-fallback", "{\"prop1\", \"val1\"}"), + null); + + SplitClientConfig config = SplitClientConfig.builder() + .setBlockUntilReadyTimeout(10000) + .endpoint(serverURL, serverURL) + .telemetryURL(serverURL + "/v1") + .authServiceURL(String.format("%s/api/auth/enabled", serverURL)) + .streamingEnabled(false) + .featuresRefreshRate(5) + .impressionsMode(ImpressionsManager.Mode.DEBUG) + .fallbackTreatments(fallbackTreatmentsConfiguration) + .build(); + + SplitFactory factory = SplitFactoryBuilder.build("fake-api-token", config); + SplitClient client = factory.client(); + + Assert.assertEquals("on-fallback", client.getTreatment("user1", "without_impression_toggle")); + Assert.assertEquals("on-fallback", client.getTreatment("user2", "feature")); + client.blockUntilReady(); + + client.destroy(); + boolean check1 = false, check2 = false; + for (int i=0; i < allRequests.size(); i++ ) { + if (allRequests.get(i).getPath().equals("/api/testImpressions/bulk") ) { + String body = allRequests.get(i).getBody().readUtf8(); + if (body.contains("user2")) { + check1 = true; + Assert.assertTrue(body.contains("feature")); + Assert.assertTrue(body.contains("fallback - not ready")); + } + if (body.contains("user1")) { + check2 = true; + Assert.assertTrue(body.contains("without_impression_toggle")); + Assert.assertTrue(body.contains("fallback - not ready")); + } + } + } + server.shutdown(); + Assert.assertTrue(check1); + Assert.assertTrue(check2); + } + private SSEMockServer buildSSEMockServer(SSEMockServer.SseEventQueue eventQueue) { return new SSEMockServer(eventQueue, (token, version, channel) -> { if (!"1.1".equals(version)) {