diff --git a/src/main/java/no/finn/unleash/DefaultUnleash.java b/src/main/java/no/finn/unleash/DefaultUnleash.java index f7ee726c2..c63ebcf0c 100644 --- a/src/main/java/no/finn/unleash/DefaultUnleash.java +++ b/src/main/java/no/finn/unleash/DefaultUnleash.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiFunction; import no.finn.unleash.event.EventDispatcher; import no.finn.unleash.event.ToggleEvaluated; @@ -40,7 +41,6 @@ public final class DefaultUnleash implements Unleash { private final EventDispatcher eventDispatcher; private final UnleashConfig config; - private static FeatureToggleRepository defaultToggleRepository(UnleashConfig unleashConfig) { return new FeatureToggleRepository( unleashConfig, @@ -74,24 +74,35 @@ public boolean isEnabled(final String toggleName, final boolean defaultSetting) } @Override - public boolean isEnabled(final String toggleName, final UnleashContext context ,final boolean defaultSetting) { - FeatureToggle featureToggle = toggleRepository.getToggle(toggleName); - boolean enabled = isEnabled(featureToggle, context, defaultSetting); + public boolean isEnabled(final String toggleName, final UnleashContext context, final boolean defaultSetting) { + return isEnabled(toggleName, context, (n, c) -> defaultSetting); + } + + @Override + public boolean isEnabled(final String toggleName, final BiFunction fallbackAction) { + return isEnabled(toggleName, contextProvider.getContext(), fallbackAction); + } + + @Override + public boolean isEnabled(String toggleName, UnleashContext context, BiFunction fallbackAction) { + boolean enabled = checkEnabled(toggleName, context, fallbackAction); count(toggleName, enabled); - eventDispatcher.dispatch(new ToggleEvaluated(toggleName,enabled)); + eventDispatcher.dispatch(new ToggleEvaluated(toggleName, enabled)); return enabled; } - private boolean isEnabled(FeatureToggle featureToggle, UnleashContext context, boolean defaultSetting) { + private boolean checkEnabled(String toggleName, UnleashContext context, BiFunction fallbackAction) { + FeatureToggle featureToggle = toggleRepository.getToggle(toggleName); boolean enabled; + UnleashContext enhancedContext = context.applyStaticFields(config); + if (featureToggle == null) { - enabled = defaultSetting; + enabled = fallbackAction.apply(toggleName, enhancedContext); } else if(!featureToggle.isEnabled()) { enabled = false; } else if(featureToggle.getStrategies().size() == 0) { return true; } else { - UnleashContext enhancedContext = context.applyStaticFields(config); enabled = featureToggle.getStrategies().stream() .anyMatch(as -> getStrategy(as.getName()).isEnabled(as.getParameters(), enhancedContext, as.getConstraints())); } @@ -106,7 +117,7 @@ public Variant getVariant(String toggleName, UnleashContext context) { @Override public Variant getVariant(String toggleName, UnleashContext context, Variant defaultValue) { FeatureToggle featureToggle = toggleRepository.getToggle(toggleName); - boolean enabled = isEnabled(featureToggle, context, false); + boolean enabled = checkEnabled(toggleName, context, (n, c) -> false); Variant variant = enabled ? selectVariant(featureToggle, context, defaultValue) : defaultValue; metricService.countVariant(toggleName, variant.getName()); return variant; diff --git a/src/main/java/no/finn/unleash/FakeUnleash.java b/src/main/java/no/finn/unleash/FakeUnleash.java index ea6d0105b..3b6a8a4de 100644 --- a/src/main/java/no/finn/unleash/FakeUnleash.java +++ b/src/main/java/no/finn/unleash/FakeUnleash.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.BiFunction; public final class FakeUnleash implements Unleash { private boolean enableAll = false; @@ -27,6 +28,19 @@ public boolean isEnabled(String toggleName, boolean defaultSetting) { } } + @Override + public boolean isEnabled(String toggleName, UnleashContext context, BiFunction fallbackAction) { + return isEnabled(toggleName, fallbackAction); + } + + @Override + public boolean isEnabled(String toggleName, BiFunction fallbackAction) { + if(!features.containsKey(toggleName)) { + return fallbackAction.apply(toggleName, UnleashContext.builder().build()); + } + return isEnabled(toggleName); + } + @Override public Variant getVariant(String toggleName, UnleashContext context) { return getVariant(toggleName, Variant.DISABLED_VARIANT); diff --git a/src/main/java/no/finn/unleash/Unleash.java b/src/main/java/no/finn/unleash/Unleash.java index fe28dacdb..3bd04b80d 100644 --- a/src/main/java/no/finn/unleash/Unleash.java +++ b/src/main/java/no/finn/unleash/Unleash.java @@ -1,6 +1,7 @@ package no.finn.unleash; import java.util.List; +import java.util.function.BiFunction; public interface Unleash { boolean isEnabled(String toggleName); @@ -15,6 +16,14 @@ default boolean isEnabled(String toggleName, UnleashContext context, boolean def return isEnabled(toggleName, defaultSetting); } + default boolean isEnabled(String toggleName, BiFunction fallbackAction) { + return isEnabled(toggleName, false); + } + + default boolean isEnabled(String toggleName, UnleashContext context, BiFunction fallbackAction) { + return isEnabled(toggleName, context, false); + } + Variant getVariant(final String toggleName, final UnleashContext context); Variant getVariant(final String toggleName, final UnleashContext context, final Variant defaultValue); diff --git a/src/test/java/no/finn/unleash/UnleashTest.java b/src/test/java/no/finn/unleash/UnleashTest.java index 8f9f64303..ead646bd8 100644 --- a/src/test/java/no/finn/unleash/UnleashTest.java +++ b/src/test/java/no/finn/unleash/UnleashTest.java @@ -7,6 +7,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; import no.finn.unleash.repository.ToggleRepository; import no.finn.unleash.strategy.Strategy; @@ -15,21 +17,18 @@ import no.finn.unleash.variant.Payload; import no.finn.unleash.variant.VariantDefinition; +import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyMap; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; public class UnleashTest { @@ -81,6 +80,49 @@ public void unknown_feature_should_use_default_setting() { assertThat(unleash.isEnabled("test", true), is(true)); } + @Test + public void fallback_function_should_be_invoked_and_return_true() { + when(toggleRepository.getToggle("test")).thenReturn(null); + BiFunction fallbackAction = mock(BiFunction.class); + when(fallbackAction.apply(eq("test"), any(UnleashContext.class))).thenReturn(true); + + assertThat(unleash.isEnabled("test", fallbackAction), is(true)); + verify(fallbackAction, times(1)).apply(anyString(), any(UnleashContext.class)); + } + + @Test + public void fallback_function_should_be_invoked_also_with_context() { + when(toggleRepository.getToggle("test")).thenReturn(null); + BiFunction fallbackAction = mock(BiFunction.class); + when(fallbackAction.apply(eq("test"), any(UnleashContext.class))).thenReturn(true); + + UnleashContext context = UnleashContext.builder().userId("123").build(); + + assertThat(unleash.isEnabled("test", context, fallbackAction), is(true)); + verify(fallbackAction, times(1)).apply(anyString(), any(UnleashContext.class)); + } + + @Test + void fallback_function_should_be_invoked_and_return_false() { + when(toggleRepository.getToggle("test")).thenReturn(null); + BiFunction fallbackAction = mock(BiFunction.class); + when(fallbackAction.apply(eq("test"), any(UnleashContext.class))).thenReturn(false); + + assertThat(unleash.isEnabled("test", fallbackAction), is(false)); + verify(fallbackAction, times(1)).apply(anyString(), any(UnleashContext.class)); + } + + @Test + void fallback_function_should_not_be_called_when_toggle_is_defined() { + when(toggleRepository.getToggle("test")).thenReturn(new FeatureToggle("test", true, asList(new ActivationStrategy("default", null)))); + + BiFunction fallbackAction = mock(BiFunction.class); + when(fallbackAction.apply(eq("test"), any(UnleashContext.class))).thenReturn(false); + + assertThat(unleash.isEnabled("test", fallbackAction), is(true)); + verify(fallbackAction, never()).apply(anyString(), any(UnleashContext.class)); + } + @Test public void should_register_custom_strategies() { //custom strategy