diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/EventStreamObserver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/EventStreamObserver.java index e6e7c18e0..ea9b6cf3c 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/EventStreamObserver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/EventStreamObserver.java @@ -41,7 +41,9 @@ public void onNext(EventStreamResponse value) { @Override public void onError(Throwable t) { log.error("event stream", t); - this.cache.clear(); + if (this.cache.getEnabled()) { + this.cache.clear(); + } this.callback.setEventStreamAlive(false); try { this.callback.restartEventStream(); @@ -52,7 +54,9 @@ public void onError(Throwable t) { @Override public void onCompleted() { - this.cache.clear(); + if (this.cache.getEnabled()) { + this.cache.clear(); + } this.callback.setEventStreamAlive(false); } @@ -76,7 +80,9 @@ private void handleConfigurationChangeEvent(EventStreamResponse value) { } private void handleProviderReadyEvent() { - this.cache.clear(); this.callback.setEventStreamAlive(true); + if (this.cache.getEnabled()) { + this.cache.clear(); + } } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdCache.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdCache.java index 458b3cfe0..15a10c13f 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdCache.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdCache.java @@ -12,12 +12,16 @@ */ public class FlagdCache { private Map> store; - private Boolean enabled; + private Boolean enabled = false; static final String LRU_CACHE = "lru"; static final String DISABLED = "disabled"; FlagdCache(String cache, int maxCacheSize) { + if (cache == null) { + return; + } + switch (cache) { case DISABLED: return; diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java index f4bc45429..854d5107d 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java @@ -625,4 +625,110 @@ private void do_resolvers_cache_responses(String reason, Boolean eventStreamAliv assertEquals(OBJECT_VARIANT, objectDetails.getVariant()); assertEquals(expectedReason, objectDetails.getReason()); } -} \ No newline at end of file + + @Test + void disabled_cache() { + ResolveBooleanResponse booleanResponse = ResolveBooleanResponse.newBuilder() + .setValue(true) + .setVariant(BOOL_VARIANT) + .setReason(FlagdProvider.STATIC_REASON) + .build(); + + ResolveStringResponse stringResponse = ResolveStringResponse.newBuilder() + .setValue(STRING_VALUE) + .setVariant(STRING_VARIANT) + .setReason(FlagdProvider.STATIC_REASON) + .build(); + + ResolveIntResponse intResponse = ResolveIntResponse.newBuilder() + .setValue(INT_VALUE) + .setVariant(INT_VARIANT) + .setReason(FlagdProvider.STATIC_REASON) + .build(); + + ResolveFloatResponse floatResponse = ResolveFloatResponse.newBuilder() + .setValue(DOUBLE_VALUE) + .setVariant(DOUBLE_VARIANT) + .setReason(FlagdProvider.STATIC_REASON) + .build(); + + ResolveObjectResponse objectResponse = ResolveObjectResponse.newBuilder() + .setValue(PROTOBUF_STRUCTURE_VALUE) + .setVariant(OBJECT_VARIANT) + .setReason(FlagdProvider.STATIC_REASON) + .build(); + + + ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); + ServiceStub serviceStubMock = mock(ServiceStub.class); + when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) + .thenReturn(serviceBlockingStubMock); + when(serviceBlockingStubMock + .resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))).thenReturn(booleanResponse); + when(serviceBlockingStubMock + .resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))).thenReturn(floatResponse); + when(serviceBlockingStubMock + .resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))).thenReturn(intResponse); + when(serviceBlockingStubMock + .resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))).thenReturn(stringResponse); + when(serviceBlockingStubMock + .resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))).thenReturn(objectResponse); + + FlagdProvider provider = new FlagdProvider(serviceBlockingStubMock, serviceStubMock, null, 0, 1); + ArgumentCaptor> streamObserverCaptor = ArgumentCaptor.forClass(StreamObserver.class); + verify(serviceStubMock).eventStream(any(EventStreamRequest.class), streamObserverCaptor.capture()); + + provider.setEventStreamAlive(true); + OpenFeatureAPI.getInstance().setProvider(provider); + + HashMap flagsMap = new HashMap(); + HashMap structMap = new HashMap(); + + flagsMap.put("foo", com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); // assert that a configuration_change event works + + structMap.put("flags", com.google.protobuf.Value.newBuilder(). + setStructValue(Struct.newBuilder().putAllFields(flagsMap)).build()); + + EventStreamResponse eResponse = EventStreamResponse.newBuilder() + .setType("configuration_change") + .setData(Struct.newBuilder().putAllFields(structMap).build()) + .build(); + + // should not cache results + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); + FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, "wrong"); + FlagEvaluationDetails intDetails = api.getClient().getIntegerDetails(FLAG_KEY_INTEGER, 0); + FlagEvaluationDetails floatDetails = api.getClient().getDoubleDetails(FLAG_KEY_DOUBLE, 0.1); + FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); + + // should not cause a change of state + streamObserverCaptor.getValue().onNext(eResponse); + + // assert values are not cached + booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); + assertTrue(booleanDetails.getValue()); + assertEquals(BOOL_VARIANT, booleanDetails.getVariant()); + assertEquals(FlagdProvider.STATIC_REASON, booleanDetails.getReason()); + + stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, "wrong"); + assertEquals(STRING_VALUE, stringDetails.getValue()); + assertEquals(STRING_VARIANT, stringDetails.getVariant()); + assertEquals(FlagdProvider.STATIC_REASON, stringDetails.getReason()); + + intDetails = api.getClient().getIntegerDetails(FLAG_KEY_INTEGER, 0); + assertEquals(INT_VALUE, intDetails.getValue()); + assertEquals(INT_VARIANT, intDetails.getVariant()); + assertEquals(FlagdProvider.STATIC_REASON, intDetails.getReason()); + + floatDetails = api.getClient().getDoubleDetails(FLAG_KEY_DOUBLE, 0.1); + assertEquals(DOUBLE_VALUE, floatDetails.getValue()); + assertEquals(DOUBLE_VARIANT, floatDetails.getVariant()); + assertEquals(FlagdProvider.STATIC_REASON, floatDetails.getReason()); + + objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); + assertEquals(INNER_STRUCT_VALUE, objectDetails.getValue().asStructure() + .asMap().get(INNER_STRUCT_KEY).asString()); + assertEquals(OBJECT_VARIANT, objectDetails.getVariant()); + assertEquals(FlagdProvider.STATIC_REASON, objectDetails.getReason()); + } +}