From b62cbb22d9dbd497e699de07e8563cd9dd12c19a Mon Sep 17 00:00:00 2001 From: liran2000 Date: Thu, 7 Sep 2023 17:42:46 +0300 Subject: [PATCH 01/17] add Unleash provider Signed-off-by: liran2000 --- pom.xml | 1 + providers/unleash/CHANGELOG.md | 0 providers/unleash/README.md | 42 ++++ providers/unleash/lombok.config | 5 + providers/unleash/pom.xml | 40 ++++ .../providers/unleash/UnleashOptions.java | 28 +++ .../providers/unleash/UnleashProvider.java | 195 +++++++++++++++ .../unleash/UnleashSubscriberWrapper.java | 117 +++++++++ .../unleash/UnleashProviderTest.java | 223 ++++++++++++++++++ providers/unleash/version.txt | 1 + 10 files changed, 652 insertions(+) create mode 100644 providers/unleash/CHANGELOG.md create mode 100644 providers/unleash/README.md create mode 100644 providers/unleash/lombok.config create mode 100644 providers/unleash/pom.xml create mode 100644 providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java create mode 100644 providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java create mode 100644 providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java create mode 100644 providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java create mode 100644 providers/unleash/version.txt diff --git a/pom.xml b/pom.xml index 9622b426f..320f6147f 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ providers/go-feature-flag providers/jsonlogic-eval-provider providers/env-var + providers/unleash diff --git a/providers/unleash/CHANGELOG.md b/providers/unleash/CHANGELOG.md new file mode 100644 index 000000000..e69de29bb diff --git a/providers/unleash/README.md b/providers/unleash/README.md new file mode 100644 index 000000000..66942b132 --- /dev/null +++ b/providers/unleash/README.md @@ -0,0 +1,42 @@ +# Unofficial Unleash OpenFeature Provider for Java + +Unleash OpenFeature Provider can provide usage for Unleash via OpenFeature Java SDK. + +## Installation + + + +```xml + + + dev.openfeature.contrib.providers + unleash + 0.0.1 + +``` + + + +## Usage +Unleash OpenFeature Provider is using Unleash Java SDK. + +### Usage Example + +``` +FeatureProvider featureProvider = new UnleashProvider(unleashOptions); +OpenFeatureAPI.getInstance().setProviderAndWait(unleashProvider); +boolean featureEnabled = client.getBooleanValue(FLAG_NAME, false); + +UnleashContext unleashContext = UnleashContext.builder().userId("1").build(); +EvaluationContext evaluationContext = UnleashProvider.transform(unleashContext); +featureEnabled = client.getBooleanValue(FLAG_NAME, false, evaluationContext); +``` + +See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information. + +## Caveats / Limitations + +* Unleash OpenFeature Provider only supports boolean feature flags. + +## References +* [Unleash](https://getunleash.io) diff --git a/providers/unleash/lombok.config b/providers/unleash/lombok.config new file mode 100644 index 000000000..bcd1afdae --- /dev/null +++ b/providers/unleash/lombok.config @@ -0,0 +1,5 @@ +# This file is needed to avoid errors throw by findbugs when working with lombok. +lombok.addSuppressWarnings = true +lombok.addLombokGeneratedAnnotation = true +config.stopBubbling = true +lombok.extern.findbugs.addSuppressFBWarnings = true diff --git a/providers/unleash/pom.xml b/providers/unleash/pom.xml new file mode 100644 index 000000000..c0d7f477d --- /dev/null +++ b/providers/unleash/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + dev.openfeature.contrib + parent + 0.1.0 + ../../pom.xml + + dev.openfeature.contrib.providers + unleash + 0.0.1 + + unleash + unleash provider for Java + https://www.getunleash.io/ + + + + io.getunleash + unleash-client-java + 8.3.1 + + + + org.slf4j + slf4j-api + 2.0.9 + + + + org.apache.logging.log4j + log4j-slf4j2-impl + 2.20.0 + test + + + + diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java new file mode 100644 index 000000000..44b0785c1 --- /dev/null +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java @@ -0,0 +1,28 @@ +package dev.openfeature.contrib.providers.unleash; + +import io.getunleash.UnleashContextProvider; +import io.getunleash.event.EventDispatcher; +import io.getunleash.metric.UnleashMetricService; +import io.getunleash.repository.IFeatureRepository; +import io.getunleash.strategy.Strategy; +import io.getunleash.util.UnleashConfig; +import lombok.Builder; +import lombok.Getter; + +import javax.annotation.Nullable; +import java.util.Map; + +/** + * Options for initializing Unleash provider. + */ +@Getter +@Builder +public class UnleashOptions { + private UnleashConfig.Builder unleashConfigBuilder; + @Nullable private IFeatureRepository featureRepository; + @Nullable private Map strategyMap; + @Nullable private UnleashContextProvider contextProvider; + @Nullable private EventDispatcher eventDispatcher; + @Nullable private UnleashMetricService metricService; + @Nullable private boolean failOnMultipleInstantiations; +} diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java new file mode 100644 index 000000000..5d13cb727 --- /dev/null +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -0,0 +1,195 @@ +package dev.openfeature.contrib.providers.unleash; + +import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.EventProvider; +import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.Metadata; +import dev.openfeature.sdk.ProviderEvaluation; +import dev.openfeature.sdk.ProviderEventDetails; +import dev.openfeature.sdk.ProviderState; +import dev.openfeature.sdk.Reason; +import dev.openfeature.sdk.Value; +import dev.openfeature.sdk.exceptions.GeneralError; +import dev.openfeature.sdk.exceptions.ProviderNotReadyError; +import dev.openfeature.sdk.exceptions.TypeMismatchError; +import io.getunleash.DefaultUnleash; +import io.getunleash.Unleash; +import io.getunleash.UnleashContext; +import io.getunleash.util.UnleashConfig; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +/** + * Provider implementation for Unleash. + */ +@Slf4j +public class UnleashProvider extends EventProvider { + + @Getter + private static final String NAME = "Unleash Provider"; + public static final String NOT_IMPLEMENTED = + "Not implemented - provider does not support this type. Only boolean is supported."; + public static final String CONTEXT_APP_NAME = "appName"; + public static final String CONTEXT_USER_ID = "userId"; + public static final String CONTEXT_ENVIRONMENT = "environment"; + public static final String CONTEXT_REMOTE_ADDRESS = "remoteAddress"; + public static final String CONTEXT_SESSION_ID = "sessionId"; + public static final String CONTEXT_CURRENT_TIME = "currentTime"; + + private UnleashOptions unleashOptions; + + @Getter + private Unleash unleash; + + @Getter + private ProviderState state = ProviderState.NOT_READY; + + /** + * Constructor. + * @param unleashOptions UnleashOptions + */ + public UnleashProvider(UnleashOptions unleashOptions) { + this.unleashOptions = unleashOptions; + } + + /** + * Initialize the provider. + * @param evaluationContext evaluation context + * @throws Exception on error + */ + @Override + public void initialize(EvaluationContext evaluationContext) throws Exception { + super.initialize(evaluationContext); + + UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( + unleashOptions.getUnleashConfigBuilder().build().getSubscriber(), this); + unleashOptions.getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); + UnleashConfig unleashConfig = unleashOptions.getUnleashConfigBuilder().build(); + unleash = new DefaultUnleash(unleashConfig, + unleashOptions.getFeatureRepository(), + unleashOptions.getStrategyMap(), + unleashOptions.getContextProvider(), + unleashOptions.getEventDispatcher(), + unleashOptions.getMetricService(), + unleashOptions.isFailOnMultipleInstantiations()); + + // else, state will be changed via UnleashSubscriberWrapper events + if (unleashConfig.isSynchronousFetchOnInitialisation()) { + state = ProviderState.READY; + } else { + log.info("ready state will be changed via UnleashSubscriberWrapper events"); + } + + log.info("finished initializing provider, state: {}", state); + } + + @Override + public Metadata getMetadata() { + return () -> NAME; + } + + @Override + public void emitProviderReady(ProviderEventDetails details) { + super.emitProviderReady(details); + state = ProviderState.READY; + } + + @Override + public void emitProviderError(ProviderEventDetails details) { + super.emitProviderError(details); + state = ProviderState.ERROR; + } + + @Override + public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { + if (!ProviderState.READY.equals(state)) { + if (ProviderState.NOT_READY.equals(state)) { + throw new ProviderNotReadyError("provider not yet initialized"); + } + throw new GeneralError("unknown error"); + } + UnleashContext context = ctx == null ? UnleashContext.builder().build() : transform(ctx); + boolean featureBooleanValue = ctx == null + ? unleash.isEnabled(key, defaultValue) + : unleash.isEnabled(key, context, defaultValue); + return ProviderEvaluation.builder() + .value(featureBooleanValue) + .reason(Reason.TARGETING_MATCH.name()) + .build(); + } + + protected static UnleashContext transform(EvaluationContext ctx) { + UnleashContext.Builder unleashContextBuilder = new UnleashContext.Builder(); + ctx.asObjectMap().forEach((k, v) -> { + switch (k) { + case CONTEXT_APP_NAME: + unleashContextBuilder.appName(String.valueOf(v)); + break; + case CONTEXT_USER_ID: + unleashContextBuilder.userId(String.valueOf(v)); + break; + case CONTEXT_ENVIRONMENT: + unleashContextBuilder.environment(String.valueOf(v)); + break; + case CONTEXT_REMOTE_ADDRESS: + unleashContextBuilder.remoteAddress(String.valueOf(v)); + break; + case CONTEXT_SESSION_ID: + unleashContextBuilder.sessionId(String.valueOf(v)); + break; + case CONTEXT_CURRENT_TIME: + unleashContextBuilder.currentTime(ZonedDateTime.parse(String.valueOf(v))); + break; + default: + unleashContextBuilder.addProperty(k, String.valueOf(v)); + break; + } + }); + return unleashContextBuilder.build(); + } + + /** + * Transform UnleashContext to EvaluationContext. + * @param unleashContext the UnleashContext + * @return transformed EvaluationContext + */ + public static EvaluationContext transform(UnleashContext unleashContext) { + Map attributes = new HashMap<>(); + unleashContext.getAppName().ifPresent(o -> attributes.put(CONTEXT_APP_NAME, Value.objectToValue(o))); + unleashContext.getUserId().ifPresent(o -> attributes.put(CONTEXT_USER_ID, Value.objectToValue(o))); + unleashContext.getEnvironment().ifPresent(o -> attributes.put(CONTEXT_ENVIRONMENT, Value.objectToValue(o))); + unleashContext.getSessionId().ifPresent(o -> attributes.put(CONTEXT_SESSION_ID, Value.objectToValue(o))); + unleashContext.getRemoteAddress().ifPresent(o -> attributes.put( + CONTEXT_REMOTE_ADDRESS, Value.objectToValue(o))); + unleashContext.getCurrentTime().ifPresent(o -> attributes.put(CONTEXT_CURRENT_TIME, Value.objectToValue(o))); + + unleashContext.getProperties().forEach((k, v) -> { + attributes.put(k, Value.objectToValue(v)); + }); + return new ImmutableContext(attributes); + } + + @Override + public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { + throw new TypeMismatchError(NOT_IMPLEMENTED); + } + + @Override + public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { + throw new TypeMismatchError(NOT_IMPLEMENTED); + } + + @Override + public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { + throw new TypeMismatchError(NOT_IMPLEMENTED); + } + + @Override + public ProviderEvaluation getObjectEvaluation(String s, Value value, EvaluationContext evaluationContext) { + throw new TypeMismatchError(NOT_IMPLEMENTED); + } +} diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java new file mode 100644 index 000000000..2bcf4ca0b --- /dev/null +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java @@ -0,0 +1,117 @@ +package dev.openfeature.contrib.providers.unleash; + +import dev.openfeature.sdk.EventProvider; +import dev.openfeature.sdk.ImmutableMetadata; +import dev.openfeature.sdk.ProviderEventDetails; +import io.getunleash.UnleashException; +import io.getunleash.event.ImpressionEvent; +import io.getunleash.event.NoOpSubscriber; +import io.getunleash.event.ToggleEvaluated; +import io.getunleash.event.UnleashEvent; +import io.getunleash.event.UnleashReady; +import io.getunleash.event.UnleashSubscriber; +import io.getunleash.metric.ClientMetrics; +import io.getunleash.metric.ClientRegistration; +import io.getunleash.repository.FeatureCollection; +import io.getunleash.repository.FeatureToggleResponse; +import io.getunleash.repository.ToggleCollection; + +import javax.annotation.Nullable; + +/** + * UnleashSubscriber wrapper for emitting event provider events. + */ +public class UnleashSubscriberWrapper implements UnleashSubscriber { + + private UnleashSubscriber unleashSubscriber; + private EventProvider eventProvider; + + /** + * Constructor. + * @param unleashSubscriber subscriber + * @param eventProvider events provider for emitting events. + */ + public UnleashSubscriberWrapper(@Nullable UnleashSubscriber unleashSubscriber, EventProvider eventProvider) { + this.unleashSubscriber = unleashSubscriber; + if (this.unleashSubscriber == null) { + this.unleashSubscriber = new NoOpSubscriber(); + } + this.eventProvider = eventProvider; + } + + @Override + public void onError(UnleashException unleashException) { + unleashSubscriber.onError(unleashException); + eventProvider.emitProviderError(ProviderEventDetails.builder() + .message(unleashException.getMessage()) + .build()); + } + + @Override + public void on(UnleashEvent unleashEvent) { + unleashSubscriber.on(unleashEvent); + } + + @Override + public void onReady(UnleashReady unleashReady) { + unleashSubscriber.onReady(unleashReady); + eventProvider.emitProviderReady(ProviderEventDetails.builder() + .eventMetadata(ImmutableMetadata.builder() + .build()).build()); + } + + @Override + public void toggleEvaluated(ToggleEvaluated toggleEvaluated) { + unleashSubscriber.toggleEvaluated(toggleEvaluated); + } + + @Override + public void togglesFetched(FeatureToggleResponse toggleResponse) { + unleashSubscriber.togglesFetched(toggleResponse); + } + + @Override + public void clientMetrics(ClientMetrics clientMetrics) { + unleashSubscriber.clientMetrics(clientMetrics); + } + + @Override + public void clientRegistered(ClientRegistration clientRegistration) { + unleashSubscriber.clientRegistered(clientRegistration); + } + + @Override + public void togglesBackedUp(ToggleCollection toggleCollection) { + unleashSubscriber.togglesBackedUp(toggleCollection); + } + + @Override + public void toggleBackupRestored(ToggleCollection toggleCollection) { + unleashSubscriber.toggleBackupRestored(toggleCollection); + } + + @Override + public void togglesBootstrapped(ToggleCollection toggleCollection) { + unleashSubscriber.togglesBootstrapped(toggleCollection); + } + + @Override + public void featuresBootstrapped(FeatureCollection featureCollection) { + unleashSubscriber.featuresBootstrapped(featureCollection); + } + + @Override + public void featuresBackedUp(FeatureCollection featureCollection) { + unleashSubscriber.featuresBackedUp(featureCollection); + } + + @Override + public void featuresBackupRestored(FeatureCollection featureCollection) { + unleashSubscriber.featuresBackupRestored(featureCollection); + } + + @Override + public void impression(ImpressionEvent impressionEvent) { + unleashSubscriber.impression(impressionEvent); + } +} diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java new file mode 100644 index 000000000..e52ebe003 --- /dev/null +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -0,0 +1,223 @@ +package dev.openfeature.contrib.providers.unleash; + +import dev.openfeature.sdk.Client; +import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.OpenFeatureAPI; +import dev.openfeature.sdk.ProviderEventDetails; +import dev.openfeature.sdk.ProviderState; +import dev.openfeature.sdk.Value; +import dev.openfeature.sdk.exceptions.ProviderNotReadyError; +import dev.openfeature.sdk.exceptions.TypeMismatchError; +import io.getunleash.ActivationStrategy; +import io.getunleash.FeatureToggle; +import io.getunleash.UnleashContext; +import io.getunleash.UnleashContextProvider; +import io.getunleash.UnleashException; +import io.getunleash.event.EventDispatcher; +import io.getunleash.event.ToggleEvaluated; +import io.getunleash.event.UnleashEvent; +import io.getunleash.event.UnleashReady; +import io.getunleash.event.UnleashSubscriber; +import io.getunleash.metric.UnleashMetricService; +import io.getunleash.repository.FeatureRepository; +import io.getunleash.repository.FeatureToggleResponse; +import io.getunleash.strategy.DefaultStrategy; +import io.getunleash.strategy.Strategy; +import io.getunleash.strategy.UserWithIdStrategy; +import io.getunleash.util.UnleashConfig; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * UnleashProvider Test. + * Inspired by Unleash tests. + */ +class UnleashProviderTest { + + private static final String FLAG_NAME = "flagName"; + private FeatureRepository featureRepository; + private UnleashContextProvider contextProvider; + private EventDispatcher eventDispatcher; + private UnleashMetricService metricService; + private TestSubscriber testSubscriber; + private UnleashProvider unleashProvider; + private Client client; + + @BeforeEach + void setUp() { + testSubscriber = new TestSubscriber(); + unleashProvider = buildUnleashProvider(true); + OpenFeatureAPI.getInstance().setProviderAndWait("sync", unleashProvider); + client = OpenFeatureAPI.getInstance().getClient("sync"); + } + + private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation) { + TestSubscriber testSubscriber = new TestSubscriber(); + UnleashConfig.Builder unleashConfigBuilder = + UnleashConfig.builder() + .unleashAPI("http://fakeAPI") + .appName("fakeApp") + .subscriber(testSubscriber) + .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); + featureRepository = mock(FeatureRepository.class); + when(featureRepository.getToggle(FLAG_NAME)) + .thenReturn( + new FeatureToggle( + FLAG_NAME, true, asList(new ActivationStrategy("default", null)))); + Map strategyMap = new HashMap<>(); + strategyMap.put("default", new DefaultStrategy()); + // Set up a toggleName using UserWithIdStrategy + Map params = new HashMap<>(); + UserWithIdStrategy userWithIdStrategy = new UserWithIdStrategy(); + strategyMap.put(userWithIdStrategy.getName(), userWithIdStrategy); + contextProvider = mock(UnleashContextProvider.class); + eventDispatcher = mock(EventDispatcher.class); + metricService = mock(UnleashMetricService.class); + when(contextProvider.getContext()).thenReturn(UnleashContext.builder().build()); + + UnleashOptions unleashOptions = UnleashOptions.builder() + .unleashConfigBuilder(unleashConfigBuilder) + .featureRepository(featureRepository) + .strategyMap(strategyMap) + .contextProvider(contextProvider) + .eventDispatcher(eventDispatcher) + .metricService(metricService).build(); + return new UnleashProvider(unleashOptions); + } + + @Test + void getBooleanEvaluation() { + assertEquals(true, unleashProvider.getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()).getValue()); + assertEquals(true, client.getBooleanValue(FLAG_NAME, false)); + assertEquals(false, unleashProvider.getBooleanEvaluation("non-existing", false, new ImmutableContext()).getValue()); + assertEquals(false, client.getBooleanValue("non-existing", false)); + } + + @Test + void getBooleanEvaluationByUser() { + + // Set up a toggleName using UserWithIdStrategy + Map params = new HashMap<>(); + params.put("userIds", "1"); + ActivationStrategy strategy = new ActivationStrategy("userWithId", params); + String flagName = "testByUserId"; + FeatureToggle featureToggle = new FeatureToggle(flagName, true, asList(strategy)); + when(featureRepository.getToggle(flagName)).thenReturn(featureToggle); + + UnleashContext unleashContext = UnleashContext.builder().userId("1").build(); + EvaluationContext evaluationContext = UnleashProvider.transform(unleashContext); + assertEquals(true, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); + assertEquals(true, client.getBooleanValue(flagName, false, evaluationContext)); + unleashContext = UnleashContext.builder().userId("2").build(); + evaluationContext = UnleashProvider.transform(unleashContext); + assertEquals(false, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); + assertEquals(false, client.getBooleanValue(flagName, false, evaluationContext)); + } + + @Test + void typeMismatch() { + assertThrows(TypeMismatchError.class, () -> { + unleashProvider.getStringEvaluation("test", "default_value", new ImmutableContext()); + }); + } + + @SneakyThrows + @Test + void asyncInitTest() { + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false); + OpenFeatureAPI.getInstance().setProvider("async", asyncInitUnleashProvider); + Client asyncClient = OpenFeatureAPI.getInstance().getClient("async"); + assertEquals(ProviderState.NOT_READY, asyncInitUnleashProvider.getState()); + + // ErrorCode.PROVIDER_NOT_READY should be returned when evaluated via the client + assertThrows(ProviderNotReadyError.class, ()-> asyncInitUnleashProvider.getBooleanEvaluation("fail_not_initialized", false, new ImmutableContext())); + + asyncInitUnleashProvider.initialize(new ImmutableContext()); + asyncInitUnleashProvider.emitProviderReady(ProviderEventDetails.builder().build()); + + assertEquals(ProviderState.READY, asyncInitUnleashProvider.getState()); + assertEquals(false, asyncInitUnleashProvider.getBooleanEvaluation("non-existing", false, new ImmutableContext()).getValue()); + assertEquals(true, unleashProvider.getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()).getValue()); + assertEquals(true, client.getBooleanValue(FLAG_NAME, false)); + + asyncInitUnleashProvider.emitProviderError(ProviderEventDetails.builder().build()); + assertEquals(ProviderState.ERROR, asyncInitUnleashProvider.getState()); + } + + @SneakyThrows + @Test + void contextTransformTest() { + Map values = new HashMap<>(); + String appNameValue = "appName_value"; + values.put("appName", Value.objectToValue(appNameValue)); + String userIdValue = "userId_value"; + values.put("userId", Value.objectToValue(userIdValue)); + String environmentValue = "environment_value"; + values.put("environment", Value.objectToValue(environmentValue)); + String remoteAddressValue = "remoteAddress_value"; + values.put("remoteAddress", Value.objectToValue(remoteAddressValue)); + String sessionIdValue = "sessionId_value"; + values.put("sessionId", Value.objectToValue(sessionIdValue)); + ZonedDateTime currentTimeValue = ZonedDateTime.now(); + values.put("currentTime", Value.objectToValue(currentTimeValue.toString())); + String customPropertyValue = "customProperty_value"; + String customPropertyKey = "customProperty"; + values.put(customPropertyKey, Value.objectToValue(customPropertyValue)); + + EvaluationContext evaluationContext = new ImmutableContext(values); + UnleashContext transformedUnleashContext = UnleashProvider.transform(evaluationContext); + assertEquals(appNameValue, transformedUnleashContext.getAppName().get()); + assertEquals(userIdValue, transformedUnleashContext.getUserId().get()); + assertEquals(environmentValue, transformedUnleashContext.getEnvironment().get()); + assertEquals(remoteAddressValue, transformedUnleashContext.getRemoteAddress().get()); + assertEquals(sessionIdValue, transformedUnleashContext.getSessionId().get()); + assertEquals(currentTimeValue, transformedUnleashContext.getCurrentTime().get()); + assertEquals(customPropertyValue, transformedUnleashContext.getProperties().get(customPropertyKey)); + } + + private class TestSubscriber implements UnleashSubscriber { + + private FeatureToggleResponse.Status status; + + private String toggleName; + private boolean toggleEnabled; + + private List events = new ArrayList<>(); + private List errors = new ArrayList<>(); + + @Override + public void on(UnleashEvent unleashEvent) { + this.events.add(unleashEvent); + } + + @Override + public void onError(UnleashException unleashException) { + this.errors.add(unleashException); + } + + @Override + public void toggleEvaluated(ToggleEvaluated toggleEvaluated) { + this.toggleName = toggleEvaluated.getToggleName(); + this.toggleEnabled = toggleEvaluated.isEnabled(); + } + + @Override + public void togglesFetched(FeatureToggleResponse toggleResponse) { + this.status = toggleResponse.getStatus(); + } + } +} \ No newline at end of file diff --git a/providers/unleash/version.txt b/providers/unleash/version.txt new file mode 100644 index 000000000..8acdd82b7 --- /dev/null +++ b/providers/unleash/version.txt @@ -0,0 +1 @@ +0.0.1 From 8ef55ff49350ae9c4ff1c07ea93dd0579374f4ec Mon Sep 17 00:00:00 2001 From: liran2000 Date: Thu, 7 Sep 2023 20:35:42 +0300 Subject: [PATCH 02/17] add getVariant and getStringEvaluation support Signed-off-by: liran2000 --- providers/unleash/README.md | 8 +- .../providers/unleash/UnleashOptions.java | 8 -- .../providers/unleash/UnleashProvider.java | 47 +++++++--- .../unleash/TestUnleashProvider.java | 94 +++++++++++++++++++ .../UnleashProviderIntegrationTest.java | 94 +++++++++++++++++++ .../unleash/UnleashProviderTest.java | 65 ++++--------- .../src/test/resources/log4j2-test.xml | 13 +++ 7 files changed, 257 insertions(+), 72 deletions(-) create mode 100644 providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java create mode 100644 providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java create mode 100644 providers/unleash/src/test/resources/log4j2-test.xml diff --git a/providers/unleash/README.md b/providers/unleash/README.md index 66942b132..46095f344 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -17,6 +17,10 @@ Unleash OpenFeature Provider can provide usage for Unleash via OpenFeature Java +## Concepts +* Boolean evaluation gets feature enabled status. +* String evaluation gets feature variant value. + ## Usage Unleash OpenFeature Provider is using Unleash Java SDK. @@ -36,7 +40,9 @@ See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers ## Caveats / Limitations -* Unleash OpenFeature Provider only supports boolean feature flags. +* Unleash OpenFeature Provider only supports boolean and string evaluation. +* Unleash OpenFeature Provider only supports string variant type. +* Evaluation reason is currently UNKNOWN. ## References * [Unleash](https://getunleash.io) diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java index 44b0785c1..f20598073 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java @@ -1,9 +1,5 @@ package dev.openfeature.contrib.providers.unleash; -import io.getunleash.UnleashContextProvider; -import io.getunleash.event.EventDispatcher; -import io.getunleash.metric.UnleashMetricService; -import io.getunleash.repository.IFeatureRepository; import io.getunleash.strategy.Strategy; import io.getunleash.util.UnleashConfig; import lombok.Builder; @@ -19,10 +15,6 @@ @Builder public class UnleashOptions { private UnleashConfig.Builder unleashConfigBuilder; - @Nullable private IFeatureRepository featureRepository; @Nullable private Map strategyMap; - @Nullable private UnleashContextProvider contextProvider; - @Nullable private EventDispatcher eventDispatcher; - @Nullable private UnleashMetricService metricService; @Nullable private boolean failOnMultipleInstantiations; } diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 5d13cb727..0bb24c13c 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -15,8 +15,13 @@ import io.getunleash.DefaultUnleash; import io.getunleash.Unleash; import io.getunleash.UnleashContext; +import io.getunleash.Variant; +import io.getunleash.strategy.Strategy; import io.getunleash.util.UnleashConfig; +import io.getunleash.variant.Payload; +import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.time.ZonedDateTime; @@ -39,12 +44,17 @@ public class UnleashProvider extends EventProvider { public static final String CONTEXT_REMOTE_ADDRESS = "remoteAddress"; public static final String CONTEXT_SESSION_ID = "sessionId"; public static final String CONTEXT_CURRENT_TIME = "currentTime"; + public static final String PROVIDER_NOT_YET_INITIALIZED = "provider not yet initialized"; + public static final String UNKNOWN_ERROR = "unknown error"; + @Getter(AccessLevel.PROTECTED) private UnleashOptions unleashOptions; + @Setter(AccessLevel.PROTECTED) @Getter private Unleash unleash; + @Setter(AccessLevel.PROTECTED) @Getter private ProviderState state = ProviderState.NOT_READY; @@ -64,18 +74,12 @@ public UnleashProvider(UnleashOptions unleashOptions) { @Override public void initialize(EvaluationContext evaluationContext) throws Exception { super.initialize(evaluationContext); - UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( - unleashOptions.getUnleashConfigBuilder().build().getSubscriber(), this); + unleashOptions.getUnleashConfigBuilder().build().getSubscriber(), this); unleashOptions.getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); UnleashConfig unleashConfig = unleashOptions.getUnleashConfigBuilder().build(); unleash = new DefaultUnleash(unleashConfig, - unleashOptions.getFeatureRepository(), - unleashOptions.getStrategyMap(), - unleashOptions.getContextProvider(), - unleashOptions.getEventDispatcher(), - unleashOptions.getMetricService(), - unleashOptions.isFailOnMultipleInstantiations()); + unleashOptions.getStrategyMap().values().toArray(new Strategy[unleashOptions.getStrategyMap().size()])); // else, state will be changed via UnleashSubscriberWrapper events if (unleashConfig.isSynchronousFetchOnInitialisation()) { @@ -108,17 +112,15 @@ public void emitProviderError(ProviderEventDetails details) { public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { if (!ProviderState.READY.equals(state)) { if (ProviderState.NOT_READY.equals(state)) { - throw new ProviderNotReadyError("provider not yet initialized"); + throw new ProviderNotReadyError(PROVIDER_NOT_YET_INITIALIZED); } - throw new GeneralError("unknown error"); + throw new GeneralError(UNKNOWN_ERROR); } UnleashContext context = ctx == null ? UnleashContext.builder().build() : transform(ctx); - boolean featureBooleanValue = ctx == null - ? unleash.isEnabled(key, defaultValue) - : unleash.isEnabled(key, context, defaultValue); + boolean featureBooleanValue = unleash.isEnabled(key, context, defaultValue); return ProviderEvaluation.builder() .value(featureBooleanValue) - .reason(Reason.TARGETING_MATCH.name()) + .reason(Reason.UNKNOWN.name()) .build(); } @@ -175,7 +177,22 @@ public static EvaluationContext transform(UnleashContext unleashContext) { @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { - throw new TypeMismatchError(NOT_IMPLEMENTED); + if (!ProviderState.READY.equals(state)) { + if (ProviderState.NOT_READY.equals(state)) { + throw new ProviderNotReadyError(PROVIDER_NOT_YET_INITIALIZED); + } + throw new GeneralError(UNKNOWN_ERROR); + } + UnleashContext context = ctx == null ? UnleashContext.builder().build() : transform(ctx); + Payload defaultVariantPayload = new Payload("string", String.valueOf(defaultValue)); + Variant defaultVariant = new Variant("default_fallback", defaultVariantPayload, true); + Variant evaluatedVariant = unleash.getVariant(key, context, defaultVariant); + Payload evaluatedVariantPayload = evaluatedVariant.getPayload().orElse(defaultVariantPayload); + String evaluatedVariantPayloadValue = evaluatedVariantPayload.getValue(); + return ProviderEvaluation.builder() + .value(evaluatedVariantPayloadValue) + .reason(Reason.UNKNOWN.name()) + .build(); } @Override diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java new file mode 100644 index 000000000..35d716452 --- /dev/null +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java @@ -0,0 +1,94 @@ +package dev.openfeature.contrib.providers.unleash; + +import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.ProviderState; +import io.getunleash.ActivationStrategy; +import io.getunleash.DefaultUnleash; +import io.getunleash.FeatureToggle; +import io.getunleash.Unleash; +import io.getunleash.UnleashContextProvider; +import io.getunleash.event.EventDispatcher; +import io.getunleash.metric.UnleashMetricService; +import io.getunleash.repository.FeatureRepository; +import io.getunleash.strategy.DefaultStrategy; +import io.getunleash.strategy.Strategy; +import io.getunleash.strategy.UserWithIdStrategy; +import io.getunleash.util.UnleashConfig; +import io.getunleash.variant.Payload; +import io.getunleash.variant.VariantDefinition; +import lombok.extern.slf4j.Slf4j; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static dev.openfeature.contrib.providers.unleash.UnleashProviderTest.FLAG_NAME; +import static dev.openfeature.contrib.providers.unleash.UnleashProviderTest.VARIANT_1; +import static dev.openfeature.contrib.providers.unleash.UnleashProviderTest.VARIANT_1_VALUE; +import static java.util.Arrays.asList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Slf4j +public class TestUnleashProvider extends UnleashProvider { + /** + * Constructor. + * + * @param unleashOptions UnleashOptions + */ + public TestUnleashProvider(UnleashOptions unleashOptions) { + super(unleashOptions); + } + + @Override + public void initialize(EvaluationContext evaluationContext) { + UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( + getUnleashOptions().getUnleashConfigBuilder().build().getSubscriber(), this); + getUnleashOptions().getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); + UnleashConfig unleashConfig = getUnleashOptions().getUnleashConfigBuilder().build(); + + UnleashContextProvider contextProvider = mock(UnleashContextProvider.class); + EventDispatcher eventDispatcher = mock(EventDispatcher.class); + UnleashMetricService metricService = mock(UnleashMetricService.class); + FeatureRepository featureRepository = mock(FeatureRepository.class); + VariantDefinition v1 = + new VariantDefinition( + VARIANT_1, 100, new Payload("string", VARIANT_1_VALUE), Collections.emptyList()); + when(featureRepository.getToggle(FLAG_NAME)) + .thenReturn( + new FeatureToggle( + FLAG_NAME, true, asList(new ActivationStrategy("default", null)), Arrays.asList(v1))); + + Map params = new HashMap<>(); + params.put("userIds", "1"); + ActivationStrategy strategy = new ActivationStrategy("userWithId", params); + String flagName = "testByUserId"; + FeatureToggle featureToggle = new FeatureToggle(flagName, true, asList(strategy)); + when(featureRepository.getToggle(flagName)).thenReturn(featureToggle); + + Map strategyMap = new HashMap<>(); + strategyMap.put("default", new DefaultStrategy()); + // Set up a toggleName using UserWithIdStrategy + UserWithIdStrategy userWithIdStrategy = new UserWithIdStrategy(); + strategyMap.put(userWithIdStrategy.getName(), userWithIdStrategy); + + Unleash unleash = new DefaultUnleash(unleashConfig, + featureRepository, + strategyMap, + contextProvider, + eventDispatcher, + metricService, + false); + setUnleash(unleash); + + // else, state will be changed via UnleashSubscriberWrapper events + if (unleashConfig.isSynchronousFetchOnInitialisation()) { + setState(ProviderState.READY); + } else { + log.info("ready state will be changed via UnleashSubscriberWrapper events"); + } + + log.info("finished initializing provider, state: {}", getState()); + } +} diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java new file mode 100644 index 000000000..62172537f --- /dev/null +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java @@ -0,0 +1,94 @@ +package dev.openfeature.contrib.providers.unleash; + +import dev.openfeature.sdk.Client; +import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.OpenFeatureAPI; +import io.getunleash.strategy.DefaultStrategy; +import io.getunleash.strategy.Strategy; +import io.getunleash.strategy.UserWithIdStrategy; +import io.getunleash.util.UnleashConfig; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * UnleashProvider Integration test via live Unleash instance. + * To trigger manually only. + * To test it, set API_KEY and other values accordingly. + */ +@Slf4j +class UnleashProviderIntegrationTest { + + private static final String FLAG_NAME = "open-feature_flag1"; + public static final String API_KEY = null; + public static final String API_URL = "https://app.unleash-hosted.com/demo/api/"; + public static final String APP_NAME = "open-feature"; + public static final String PROJECT_NAME = "open-feature"; + private UnleashProvider unleashProvider; + private Client client; + + @BeforeEach + void setUp() { + if (API_KEY == null) { + log.debug("tests disabled"); + return; + } + unleashProvider = buildUnleashProvider(true); + OpenFeatureAPI.getInstance().setProviderAndWait("sync", unleashProvider); + client = OpenFeatureAPI.getInstance().getClient("sync"); + } + + private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation) { + UnleashConfig.Builder unleashConfigBuilder = + UnleashConfig.builder() + .unleashAPI(API_URL) + .appName(APP_NAME) + .apiKey(API_KEY) + .projectName(PROJECT_NAME) + .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); + Map strategyMap = new HashMap<>(); + strategyMap.put("default", new DefaultStrategy()); + // Set up a toggleName using UserWithIdStrategy + Map params = new HashMap<>(); + UserWithIdStrategy userWithIdStrategy = new UserWithIdStrategy(); + strategyMap.put(userWithIdStrategy.getName(), userWithIdStrategy); + + UnleashOptions unleashOptions = UnleashOptions.builder() + .unleashConfigBuilder(unleashConfigBuilder) + .strategyMap(strategyMap) + .build(); + return new UnleashProvider(unleashOptions); + } + + @Test + void getBooleanEvaluation() { + if (API_KEY == null) { + log.debug("test disabled"); + return; + } + assertEquals(true, unleashProvider.getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()).getValue()); + assertEquals(true, client.getBooleanValue(FLAG_NAME, false)); + assertEquals(false, unleashProvider.getBooleanEvaluation("non-existing", false, new ImmutableContext()).getValue()); + assertEquals(false, client.getBooleanValue("non-existing", false)); + } + + @Test + void getStringVariantEvaluation() { + if (API_KEY == null) { + log.debug("test disabled"); + return; + } + assertEquals("default_variant_value", unleashProvider.getStringEvaluation(FLAG_NAME, "", + new ImmutableContext()).getValue()); + assertEquals("default_variant_value", client.getStringValue(FLAG_NAME, "")); + assertEquals("fallback_str", unleashProvider.getStringEvaluation("non-existing", + "fallback_str", new ImmutableContext()).getValue()); + assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); + } + +} \ No newline at end of file diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index e52ebe003..c76b916dc 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -9,22 +9,12 @@ import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; import dev.openfeature.sdk.exceptions.TypeMismatchError; -import io.getunleash.ActivationStrategy; -import io.getunleash.FeatureToggle; import io.getunleash.UnleashContext; -import io.getunleash.UnleashContextProvider; import io.getunleash.UnleashException; -import io.getunleash.event.EventDispatcher; import io.getunleash.event.ToggleEvaluated; import io.getunleash.event.UnleashEvent; -import io.getunleash.event.UnleashReady; import io.getunleash.event.UnleashSubscriber; -import io.getunleash.metric.UnleashMetricService; -import io.getunleash.repository.FeatureRepository; import io.getunleash.repository.FeatureToggleResponse; -import io.getunleash.strategy.DefaultStrategy; -import io.getunleash.strategy.Strategy; -import io.getunleash.strategy.UserWithIdStrategy; import io.getunleash.util.UnleashConfig; import lombok.SneakyThrows; import org.junit.jupiter.api.BeforeEach; @@ -36,11 +26,8 @@ import java.util.List; import java.util.Map; -import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; /** * UnleashProvider Test. @@ -48,11 +35,9 @@ */ class UnleashProviderTest { - private static final String FLAG_NAME = "flagName"; - private FeatureRepository featureRepository; - private UnleashContextProvider contextProvider; - private EventDispatcher eventDispatcher; - private UnleashMetricService metricService; + public static final String FLAG_NAME = "flagName"; + public static final String VARIANT_1 = "variant1"; + public static final String VARIANT_1_VALUE = "variant1_value"; private TestSubscriber testSubscriber; private UnleashProvider unleashProvider; private Client client; @@ -65,6 +50,7 @@ void setUp() { client = OpenFeatureAPI.getInstance().getClient("sync"); } + @SneakyThrows private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation) { TestSubscriber testSubscriber = new TestSubscriber(); UnleashConfig.Builder unleashConfigBuilder = @@ -73,30 +59,11 @@ private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialis .appName("fakeApp") .subscriber(testSubscriber) .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); - featureRepository = mock(FeatureRepository.class); - when(featureRepository.getToggle(FLAG_NAME)) - .thenReturn( - new FeatureToggle( - FLAG_NAME, true, asList(new ActivationStrategy("default", null)))); - Map strategyMap = new HashMap<>(); - strategyMap.put("default", new DefaultStrategy()); - // Set up a toggleName using UserWithIdStrategy - Map params = new HashMap<>(); - UserWithIdStrategy userWithIdStrategy = new UserWithIdStrategy(); - strategyMap.put(userWithIdStrategy.getName(), userWithIdStrategy); - contextProvider = mock(UnleashContextProvider.class); - eventDispatcher = mock(EventDispatcher.class); - metricService = mock(UnleashMetricService.class); - when(contextProvider.getContext()).thenReturn(UnleashContext.builder().build()); UnleashOptions unleashOptions = UnleashOptions.builder() .unleashConfigBuilder(unleashConfigBuilder) - .featureRepository(featureRepository) - .strategyMap(strategyMap) - .contextProvider(contextProvider) - .eventDispatcher(eventDispatcher) - .metricService(metricService).build(); - return new UnleashProvider(unleashOptions); + .build(); + return new TestUnleashProvider(unleashOptions); } @Test @@ -108,16 +75,18 @@ void getBooleanEvaluation() { } @Test - void getBooleanEvaluationByUser() { + void getStringVariantEvaluation() { + assertEquals(VARIANT_1_VALUE, unleashProvider.getStringEvaluation(FLAG_NAME, "", + new ImmutableContext()).getValue()); + assertEquals(VARIANT_1_VALUE, client.getStringValue(FLAG_NAME, "")); + assertEquals("fallback_str", unleashProvider.getStringEvaluation("non-existing", + "fallback_str", new ImmutableContext()).getValue()); + assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); + } - // Set up a toggleName using UserWithIdStrategy - Map params = new HashMap<>(); - params.put("userIds", "1"); - ActivationStrategy strategy = new ActivationStrategy("userWithId", params); + @Test + void getBooleanEvaluationByUser() { String flagName = "testByUserId"; - FeatureToggle featureToggle = new FeatureToggle(flagName, true, asList(strategy)); - when(featureRepository.getToggle(flagName)).thenReturn(featureToggle); - UnleashContext unleashContext = UnleashContext.builder().userId("1").build(); EvaluationContext evaluationContext = UnleashProvider.transform(unleashContext); assertEquals(true, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); @@ -131,7 +100,7 @@ void getBooleanEvaluationByUser() { @Test void typeMismatch() { assertThrows(TypeMismatchError.class, () -> { - unleashProvider.getStringEvaluation("test", "default_value", new ImmutableContext()); + unleashProvider.getIntegerEvaluation("test", 1, new ImmutableContext()); }); } diff --git a/providers/unleash/src/test/resources/log4j2-test.xml b/providers/unleash/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..223d21a89 --- /dev/null +++ b/providers/unleash/src/test/resources/log4j2-test.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file From e97a55eaf74cb85bb5b74337679df4e556ef583f Mon Sep 17 00:00:00 2001 From: liran2000 Date: Thu, 7 Sep 2023 20:48:20 +0300 Subject: [PATCH 03/17] update readme with getStringValue usage Signed-off-by: liran2000 --- providers/unleash/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/providers/unleash/README.md b/providers/unleash/README.md index 46095f344..ffddff46a 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -34,6 +34,8 @@ boolean featureEnabled = client.getBooleanValue(FLAG_NAME, false); UnleashContext unleashContext = UnleashContext.builder().userId("1").build(); EvaluationContext evaluationContext = UnleashProvider.transform(unleashContext); featureEnabled = client.getBooleanValue(FLAG_NAME, false, evaluationContext); + +String variantValue = client.getStringValue(FLAG_NAME, ""); ``` See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information. From 3a0084780d72a75637422f5a2b3c3f84dfe047c3 Mon Sep 17 00:00:00 2001 From: liran2000 Date: Fri, 8 Sep 2023 18:56:35 +0300 Subject: [PATCH 04/17] set variant and some refactor Signed-off-by: liran2000 --- providers/unleash/README.md | 1 - .../providers/unleash/ContextTransformer.java | 74 +++++++++++++++++++ .../providers/unleash/UnleashProvider.java | 71 +----------------- .../unleash/UnleashSubscriberWrapper.java | 5 ++ .../unleash/UnleashProviderTest.java | 6 +- 5 files changed, 86 insertions(+), 71 deletions(-) create mode 100644 providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java diff --git a/providers/unleash/README.md b/providers/unleash/README.md index ffddff46a..c775a1d2e 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -44,7 +44,6 @@ See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers * Unleash OpenFeature Provider only supports boolean and string evaluation. * Unleash OpenFeature Provider only supports string variant type. -* Evaluation reason is currently UNKNOWN. ## References * [Unleash](https://getunleash.io) diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java new file mode 100644 index 000000000..898256563 --- /dev/null +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java @@ -0,0 +1,74 @@ +package dev.openfeature.contrib.providers.unleash; + +import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.Value; +import io.getunleash.UnleashContext; + +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +/** + * Transformer from Unleash context to OpenFeature context and vice versa. + */ +public class ContextTransformer { + + public static final String CONTEXT_APP_NAME = "appName"; + public static final String CONTEXT_USER_ID = "userId"; + public static final String CONTEXT_ENVIRONMENT = "environment"; + public static final String CONTEXT_REMOTE_ADDRESS = "remoteAddress"; + public static final String CONTEXT_SESSION_ID = "sessionId"; + public static final String CONTEXT_CURRENT_TIME = "currentTime"; + + protected static UnleashContext transform(EvaluationContext ctx) { + UnleashContext.Builder unleashContextBuilder = new UnleashContext.Builder(); + ctx.asObjectMap().forEach((k, v) -> { + switch (k) { + case CONTEXT_APP_NAME: + unleashContextBuilder.appName(String.valueOf(v)); + break; + case CONTEXT_USER_ID: + unleashContextBuilder.userId(String.valueOf(v)); + break; + case CONTEXT_ENVIRONMENT: + unleashContextBuilder.environment(String.valueOf(v)); + break; + case CONTEXT_REMOTE_ADDRESS: + unleashContextBuilder.remoteAddress(String.valueOf(v)); + break; + case CONTEXT_SESSION_ID: + unleashContextBuilder.sessionId(String.valueOf(v)); + break; + case CONTEXT_CURRENT_TIME: + unleashContextBuilder.currentTime(ZonedDateTime.parse(String.valueOf(v))); + break; + default: + unleashContextBuilder.addProperty(k, String.valueOf(v)); + break; + } + }); + return unleashContextBuilder.build(); + } + + /** + * Transform UnleashContext to EvaluationContext. + * @param unleashContext the UnleashContext + * @return transformed EvaluationContext + */ + public static EvaluationContext transform(UnleashContext unleashContext) { + Map attributes = new HashMap<>(); + unleashContext.getAppName().ifPresent(o -> attributes.put(CONTEXT_APP_NAME, Value.objectToValue(o))); + unleashContext.getUserId().ifPresent(o -> attributes.put(CONTEXT_USER_ID, Value.objectToValue(o))); + unleashContext.getEnvironment().ifPresent(o -> attributes.put(CONTEXT_ENVIRONMENT, Value.objectToValue(o))); + unleashContext.getSessionId().ifPresent(o -> attributes.put(CONTEXT_SESSION_ID, Value.objectToValue(o))); + unleashContext.getRemoteAddress().ifPresent(o -> attributes.put( + CONTEXT_REMOTE_ADDRESS, Value.objectToValue(o))); + unleashContext.getCurrentTime().ifPresent(o -> attributes.put(CONTEXT_CURRENT_TIME, Value.objectToValue(o))); + + unleashContext.getProperties().forEach((k, v) -> { + attributes.put(k, Value.objectToValue(v)); + }); + return new ImmutableContext(attributes); + } +} diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 0bb24c13c..08b0f709e 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -2,12 +2,10 @@ import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; -import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.Metadata; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.ProviderState; -import dev.openfeature.sdk.Reason; import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; @@ -24,10 +22,6 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.Map; - /** * Provider implementation for Unleash. */ @@ -38,12 +32,7 @@ public class UnleashProvider extends EventProvider { private static final String NAME = "Unleash Provider"; public static final String NOT_IMPLEMENTED = "Not implemented - provider does not support this type. Only boolean is supported."; - public static final String CONTEXT_APP_NAME = "appName"; - public static final String CONTEXT_USER_ID = "userId"; - public static final String CONTEXT_ENVIRONMENT = "environment"; - public static final String CONTEXT_REMOTE_ADDRESS = "remoteAddress"; - public static final String CONTEXT_SESSION_ID = "sessionId"; - public static final String CONTEXT_CURRENT_TIME = "currentTime"; + public static final String PROVIDER_NOT_YET_INITIALIZED = "provider not yet initialized"; public static final String UNKNOWN_ERROR = "unknown error"; @@ -116,65 +105,13 @@ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defa } throw new GeneralError(UNKNOWN_ERROR); } - UnleashContext context = ctx == null ? UnleashContext.builder().build() : transform(ctx); + UnleashContext context = ctx == null ? UnleashContext.builder().build() : ContextTransformer.transform(ctx); boolean featureBooleanValue = unleash.isEnabled(key, context, defaultValue); return ProviderEvaluation.builder() .value(featureBooleanValue) - .reason(Reason.UNKNOWN.name()) .build(); } - protected static UnleashContext transform(EvaluationContext ctx) { - UnleashContext.Builder unleashContextBuilder = new UnleashContext.Builder(); - ctx.asObjectMap().forEach((k, v) -> { - switch (k) { - case CONTEXT_APP_NAME: - unleashContextBuilder.appName(String.valueOf(v)); - break; - case CONTEXT_USER_ID: - unleashContextBuilder.userId(String.valueOf(v)); - break; - case CONTEXT_ENVIRONMENT: - unleashContextBuilder.environment(String.valueOf(v)); - break; - case CONTEXT_REMOTE_ADDRESS: - unleashContextBuilder.remoteAddress(String.valueOf(v)); - break; - case CONTEXT_SESSION_ID: - unleashContextBuilder.sessionId(String.valueOf(v)); - break; - case CONTEXT_CURRENT_TIME: - unleashContextBuilder.currentTime(ZonedDateTime.parse(String.valueOf(v))); - break; - default: - unleashContextBuilder.addProperty(k, String.valueOf(v)); - break; - } - }); - return unleashContextBuilder.build(); - } - - /** - * Transform UnleashContext to EvaluationContext. - * @param unleashContext the UnleashContext - * @return transformed EvaluationContext - */ - public static EvaluationContext transform(UnleashContext unleashContext) { - Map attributes = new HashMap<>(); - unleashContext.getAppName().ifPresent(o -> attributes.put(CONTEXT_APP_NAME, Value.objectToValue(o))); - unleashContext.getUserId().ifPresent(o -> attributes.put(CONTEXT_USER_ID, Value.objectToValue(o))); - unleashContext.getEnvironment().ifPresent(o -> attributes.put(CONTEXT_ENVIRONMENT, Value.objectToValue(o))); - unleashContext.getSessionId().ifPresent(o -> attributes.put(CONTEXT_SESSION_ID, Value.objectToValue(o))); - unleashContext.getRemoteAddress().ifPresent(o -> attributes.put( - CONTEXT_REMOTE_ADDRESS, Value.objectToValue(o))); - unleashContext.getCurrentTime().ifPresent(o -> attributes.put(CONTEXT_CURRENT_TIME, Value.objectToValue(o))); - - unleashContext.getProperties().forEach((k, v) -> { - attributes.put(k, Value.objectToValue(v)); - }); - return new ImmutableContext(attributes); - } - @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { if (!ProviderState.READY.equals(state)) { @@ -183,7 +120,7 @@ public ProviderEvaluation getStringEvaluation(String key, String default } throw new GeneralError(UNKNOWN_ERROR); } - UnleashContext context = ctx == null ? UnleashContext.builder().build() : transform(ctx); + UnleashContext context = ctx == null ? UnleashContext.builder().build() : ContextTransformer.transform(ctx); Payload defaultVariantPayload = new Payload("string", String.valueOf(defaultValue)); Variant defaultVariant = new Variant("default_fallback", defaultVariantPayload, true); Variant evaluatedVariant = unleash.getVariant(key, context, defaultVariant); @@ -191,7 +128,7 @@ public ProviderEvaluation getStringEvaluation(String key, String default String evaluatedVariantPayloadValue = evaluatedVariantPayload.getValue(); return ProviderEvaluation.builder() .value(evaluatedVariantPayloadValue) - .reason(Reason.UNKNOWN.name()) + .variant(evaluatedVariant.getName()) .build(); } diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java index 2bcf4ca0b..a701a1859 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java @@ -68,6 +68,11 @@ public void toggleEvaluated(ToggleEvaluated toggleEvaluated) { @Override public void togglesFetched(FeatureToggleResponse toggleResponse) { unleashSubscriber.togglesFetched(toggleResponse); + if (FeatureToggleResponse.Status.CHANGED.equals(toggleResponse.getStatus())) { + eventProvider.emitProviderConfigurationChanged(ProviderEventDetails.builder() + .eventMetadata(ImmutableMetadata.builder() + .build()).build()); + } } @Override diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index c76b916dc..af33cc49e 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -88,11 +88,11 @@ void getStringVariantEvaluation() { void getBooleanEvaluationByUser() { String flagName = "testByUserId"; UnleashContext unleashContext = UnleashContext.builder().userId("1").build(); - EvaluationContext evaluationContext = UnleashProvider.transform(unleashContext); + EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); assertEquals(true, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); assertEquals(true, client.getBooleanValue(flagName, false, evaluationContext)); unleashContext = UnleashContext.builder().userId("2").build(); - evaluationContext = UnleashProvider.transform(unleashContext); + evaluationContext = ContextTransformer.transform(unleashContext); assertEquals(false, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); assertEquals(false, client.getBooleanValue(flagName, false, evaluationContext)); } @@ -148,7 +148,7 @@ void contextTransformTest() { values.put(customPropertyKey, Value.objectToValue(customPropertyValue)); EvaluationContext evaluationContext = new ImmutableContext(values); - UnleashContext transformedUnleashContext = UnleashProvider.transform(evaluationContext); + UnleashContext transformedUnleashContext = ContextTransformer.transform(evaluationContext); assertEquals(appNameValue, transformedUnleashContext.getAppName().get()); assertEquals(userIdValue, transformedUnleashContext.getUserId().get()); assertEquals(environmentValue, transformedUnleashContext.getEnvironment().get()); From 5eef0e20fbf3e820203da54b1ce82ab4bfe1ea7a Mon Sep 17 00:00:00 2001 From: liran2000 Date: Sat, 9 Sep 2023 20:37:10 +0300 Subject: [PATCH 05/17] - some fixes - test using WireMock Signed-off-by: liran2000 --- .release-please-manifest.json | 3 +- providers/unleash/README.md | 8 + providers/unleash/pom.xml | 8 + .../providers/unleash/ContextTransformer.java | 5 +- .../providers/unleash/UnleashOptions.java | 5 - .../providers/unleash/UnleashProvider.java | 26 +- .../unleash/UnleashSubscriberWrapper.java | 8 +- .../unleash/TestUnleashProvider.java | 94 - .../UnleashProviderIntegrationTest.java | 13 - .../unleash/UnleashProviderTest.java | 89 +- .../unleash/src/test/resources/features.json | 4509 +++++++++++++++++ release-please-config.json | 11 + 12 files changed, 4624 insertions(+), 155 deletions(-) delete mode 100644 providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java create mode 100644 providers/unleash/src/test/resources/features.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 91844553f..a25d50a87 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -4,5 +4,6 @@ "providers/go-feature-flag": "0.2.11", "providers/flagsmith": "0.0.8", "providers/env-var": "0.0.4", - "providers/jsonlogic-eval-provider": "1.0.0" + "providers/jsonlogic-eval-provider": "1.0.0", + "providers/unleash": "0.0.1" } \ No newline at end of file diff --git a/providers/unleash/README.md b/providers/unleash/README.md index c775a1d2e..f7bb02460 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -45,5 +45,13 @@ See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers * Unleash OpenFeature Provider only supports boolean and string evaluation. * Unleash OpenFeature Provider only supports string variant type. +## Unleash Provider Tests Strategies + +* Unit test based on Unleash instance with Unleash features schema file, with WireMock for API mocking. +See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information. +* Integration Test based on Unleash instance connected to a live server. +This test is disabled by default, and meant for manual triggering only. +See [UnleashProviderIntegrationTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java) for more information. + ## References * [Unleash](https://getunleash.io) diff --git a/providers/unleash/pom.xml b/providers/unleash/pom.xml index c0d7f477d..cdbf7783c 100644 --- a/providers/unleash/pom.xml +++ b/providers/unleash/pom.xml @@ -29,6 +29,14 @@ 2.0.9 + + org.wiremock + wiremock + 3.0.3 + test + + + org.apache.logging.log4j log4j-slf4j2-impl diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java index 898256563..d1791959c 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java @@ -63,8 +63,9 @@ public static EvaluationContext transform(UnleashContext unleashContext) { unleashContext.getEnvironment().ifPresent(o -> attributes.put(CONTEXT_ENVIRONMENT, Value.objectToValue(o))); unleashContext.getSessionId().ifPresent(o -> attributes.put(CONTEXT_SESSION_ID, Value.objectToValue(o))); unleashContext.getRemoteAddress().ifPresent(o -> attributes.put( - CONTEXT_REMOTE_ADDRESS, Value.objectToValue(o))); - unleashContext.getCurrentTime().ifPresent(o -> attributes.put(CONTEXT_CURRENT_TIME, Value.objectToValue(o))); + CONTEXT_REMOTE_ADDRESS, Value.objectToValue(o))); + unleashContext.getCurrentTime().ifPresent( + o -> attributes.put(CONTEXT_CURRENT_TIME, Value.objectToValue(o.toString()))); unleashContext.getProperties().forEach((k, v) -> { attributes.put(k, Value.objectToValue(v)); diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java index f20598073..f0266e42f 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java @@ -1,12 +1,9 @@ package dev.openfeature.contrib.providers.unleash; -import io.getunleash.strategy.Strategy; import io.getunleash.util.UnleashConfig; import lombok.Builder; import lombok.Getter; -import javax.annotation.Nullable; -import java.util.Map; /** * Options for initializing Unleash provider. @@ -15,6 +12,4 @@ @Builder public class UnleashOptions { private UnleashConfig.Builder unleashConfigBuilder; - @Nullable private Map strategyMap; - @Nullable private boolean failOnMultipleInstantiations; } diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 08b0f709e..459fcecd8 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -14,14 +14,14 @@ import io.getunleash.Unleash; import io.getunleash.UnleashContext; import io.getunleash.Variant; -import io.getunleash.strategy.Strategy; import io.getunleash.util.UnleashConfig; -import io.getunleash.variant.Payload; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import static io.getunleash.Variant.DISABLED_VARIANT; + /** * Provider implementation for Unleash. */ @@ -67,8 +67,7 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { unleashOptions.getUnleashConfigBuilder().build().getSubscriber(), this); unleashOptions.getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); UnleashConfig unleashConfig = unleashOptions.getUnleashConfigBuilder().build(); - unleash = new DefaultUnleash(unleashConfig, - unleashOptions.getStrategyMap().values().toArray(new Strategy[unleashOptions.getStrategyMap().size()])); + unleash = new DefaultUnleash(unleashConfig); // else, state will be changed via UnleashSubscriberWrapper events if (unleashConfig.isSynchronousFetchOnInitialisation()) { @@ -121,14 +120,19 @@ public ProviderEvaluation getStringEvaluation(String key, String default throw new GeneralError(UNKNOWN_ERROR); } UnleashContext context = ctx == null ? UnleashContext.builder().build() : ContextTransformer.transform(ctx); - Payload defaultVariantPayload = new Payload("string", String.valueOf(defaultValue)); - Variant defaultVariant = new Variant("default_fallback", defaultVariantPayload, true); - Variant evaluatedVariant = unleash.getVariant(key, context, defaultVariant); - Payload evaluatedVariantPayload = evaluatedVariant.getPayload().orElse(defaultVariantPayload); - String evaluatedVariantPayloadValue = evaluatedVariantPayload.getValue(); + Variant evaluatedVariant = unleash.getVariant(key, context); + String variantName; + String value; + if (DISABLED_VARIANT.equals(evaluatedVariant)) { + variantName = null; + value = defaultValue; + } else { + variantName = evaluatedVariant.getName(); + value = evaluatedVariant.getPayload().get().getValue(); + } return ProviderEvaluation.builder() - .value(evaluatedVariantPayloadValue) - .variant(evaluatedVariant.getName()) + .value(value) + .variant(variantName) .build(); } diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java index a701a1859..4a7ca448d 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java @@ -15,12 +15,14 @@ import io.getunleash.repository.FeatureCollection; import io.getunleash.repository.FeatureToggleResponse; import io.getunleash.repository.ToggleCollection; +import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; /** * UnleashSubscriber wrapper for emitting event provider events. */ +@Slf4j public class UnleashSubscriberWrapper implements UnleashSubscriber { private UnleashSubscriber unleashSubscriber; @@ -42,9 +44,9 @@ public UnleashSubscriberWrapper(@Nullable UnleashSubscriber unleashSubscriber, E @Override public void onError(UnleashException unleashException) { unleashSubscriber.onError(unleashException); - eventProvider.emitProviderError(ProviderEventDetails.builder() - .message(unleashException.getMessage()) - .build()); + log.info("unleashException: ", unleashException); + + // Not emitting provider error, since some unleashException not expects to change provider state to error } @Override diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java deleted file mode 100644 index 35d716452..000000000 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/TestUnleashProvider.java +++ /dev/null @@ -1,94 +0,0 @@ -package dev.openfeature.contrib.providers.unleash; - -import dev.openfeature.sdk.EvaluationContext; -import dev.openfeature.sdk.ProviderState; -import io.getunleash.ActivationStrategy; -import io.getunleash.DefaultUnleash; -import io.getunleash.FeatureToggle; -import io.getunleash.Unleash; -import io.getunleash.UnleashContextProvider; -import io.getunleash.event.EventDispatcher; -import io.getunleash.metric.UnleashMetricService; -import io.getunleash.repository.FeatureRepository; -import io.getunleash.strategy.DefaultStrategy; -import io.getunleash.strategy.Strategy; -import io.getunleash.strategy.UserWithIdStrategy; -import io.getunleash.util.UnleashConfig; -import io.getunleash.variant.Payload; -import io.getunleash.variant.VariantDefinition; -import lombok.extern.slf4j.Slf4j; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static dev.openfeature.contrib.providers.unleash.UnleashProviderTest.FLAG_NAME; -import static dev.openfeature.contrib.providers.unleash.UnleashProviderTest.VARIANT_1; -import static dev.openfeature.contrib.providers.unleash.UnleashProviderTest.VARIANT_1_VALUE; -import static java.util.Arrays.asList; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@Slf4j -public class TestUnleashProvider extends UnleashProvider { - /** - * Constructor. - * - * @param unleashOptions UnleashOptions - */ - public TestUnleashProvider(UnleashOptions unleashOptions) { - super(unleashOptions); - } - - @Override - public void initialize(EvaluationContext evaluationContext) { - UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( - getUnleashOptions().getUnleashConfigBuilder().build().getSubscriber(), this); - getUnleashOptions().getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); - UnleashConfig unleashConfig = getUnleashOptions().getUnleashConfigBuilder().build(); - - UnleashContextProvider contextProvider = mock(UnleashContextProvider.class); - EventDispatcher eventDispatcher = mock(EventDispatcher.class); - UnleashMetricService metricService = mock(UnleashMetricService.class); - FeatureRepository featureRepository = mock(FeatureRepository.class); - VariantDefinition v1 = - new VariantDefinition( - VARIANT_1, 100, new Payload("string", VARIANT_1_VALUE), Collections.emptyList()); - when(featureRepository.getToggle(FLAG_NAME)) - .thenReturn( - new FeatureToggle( - FLAG_NAME, true, asList(new ActivationStrategy("default", null)), Arrays.asList(v1))); - - Map params = new HashMap<>(); - params.put("userIds", "1"); - ActivationStrategy strategy = new ActivationStrategy("userWithId", params); - String flagName = "testByUserId"; - FeatureToggle featureToggle = new FeatureToggle(flagName, true, asList(strategy)); - when(featureRepository.getToggle(flagName)).thenReturn(featureToggle); - - Map strategyMap = new HashMap<>(); - strategyMap.put("default", new DefaultStrategy()); - // Set up a toggleName using UserWithIdStrategy - UserWithIdStrategy userWithIdStrategy = new UserWithIdStrategy(); - strategyMap.put(userWithIdStrategy.getName(), userWithIdStrategy); - - Unleash unleash = new DefaultUnleash(unleashConfig, - featureRepository, - strategyMap, - contextProvider, - eventDispatcher, - metricService, - false); - setUnleash(unleash); - - // else, state will be changed via UnleashSubscriberWrapper events - if (unleashConfig.isSynchronousFetchOnInitialisation()) { - setState(ProviderState.READY); - } else { - log.info("ready state will be changed via UnleashSubscriberWrapper events"); - } - - log.info("finished initializing provider, state: {}", getState()); - } -} diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java index 62172537f..8dc909e45 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java @@ -3,17 +3,11 @@ import dev.openfeature.sdk.Client; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.OpenFeatureAPI; -import io.getunleash.strategy.DefaultStrategy; -import io.getunleash.strategy.Strategy; -import io.getunleash.strategy.UserWithIdStrategy; import io.getunleash.util.UnleashConfig; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.HashMap; -import java.util.Map; - import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -51,16 +45,9 @@ private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialis .apiKey(API_KEY) .projectName(PROJECT_NAME) .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); - Map strategyMap = new HashMap<>(); - strategyMap.put("default", new DefaultStrategy()); - // Set up a toggleName using UserWithIdStrategy - Map params = new HashMap<>(); - UserWithIdStrategy userWithIdStrategy = new UserWithIdStrategy(); - strategyMap.put(userWithIdStrategy.getName(), userWithIdStrategy); UnleashOptions unleashOptions = UnleashOptions.builder() .unleashConfigBuilder(unleashConfigBuilder) - .strategyMap(strategyMap) .build(); return new UnleashProvider(unleashOptions); } diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index af33cc49e..3491dbe41 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -1,12 +1,13 @@ package dev.openfeature.contrib.providers.unleash; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import dev.openfeature.sdk.Client; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.ProviderState; -import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; import dev.openfeature.sdk.exceptions.TypeMismatchError; import io.getunleash.UnleashContext; @@ -20,12 +21,22 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -33,29 +44,48 @@ * UnleashProvider Test. * Inspired by Unleash tests. */ +@WireMockTest class UnleashProviderTest { - public static final String FLAG_NAME = "flagName"; - public static final String VARIANT_1 = "variant1"; - public static final String VARIANT_1_VALUE = "variant1_value"; + public static final String FLAG_NAME = "Demo"; + public static final String VARIANT_FLAG_NAME = "new-api"; + public static final String VARIANT_FLAG_VALUE = "v1"; private TestSubscriber testSubscriber; private UnleashProvider unleashProvider; private Client client; @BeforeEach - void setUp() { + void setUp(WireMockRuntimeInfo wmRuntimeInfo) { testSubscriber = new TestSubscriber(); - unleashProvider = buildUnleashProvider(true); + stubFor(any(anyUrl()).willReturn(aResponse() + .withStatus(200) + .withBody("{}"))); + String unleashAPI = "http://localhost:" + wmRuntimeInfo.getHttpPort() + "/api/"; + String backupFileContent = readBackupFile(); + mockUnleashAPI(backupFileContent); + unleashProvider = buildUnleashProvider(true, unleashAPI, backupFileContent); OpenFeatureAPI.getInstance().setProviderAndWait("sync", unleashProvider); client = OpenFeatureAPI.getInstance().getClient("sync"); } + private void mockUnleashAPI(String backupFileContent) { + stubFor( + get(urlEqualTo("/api/client/features")) + .withHeader("Accept", equalTo("application/json")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(backupFileContent))); + stubFor(post(urlEqualTo("/api/client/register")).willReturn(aResponse().withStatus(200))); + } + @SneakyThrows - private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation) { + private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation, String unleashAPI, String backupFileContent) { TestSubscriber testSubscriber = new TestSubscriber(); UnleashConfig.Builder unleashConfigBuilder = UnleashConfig.builder() - .unleashAPI("http://fakeAPI") + .unleashAPI(new URI(unleashAPI)) .appName("fakeApp") .subscriber(testSubscriber) .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); @@ -63,7 +93,13 @@ private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialis UnleashOptions unleashOptions = UnleashOptions.builder() .unleashConfigBuilder(unleashConfigBuilder) .build(); - return new TestUnleashProvider(unleashOptions); + return new UnleashProvider(unleashOptions); + } + + @SneakyThrows + private String readBackupFile() { + URL url = getClass().getResource("/features.json"); + return new String(Files.readAllBytes(Paths.get(url.toURI()))); } @Test @@ -76,9 +112,9 @@ void getBooleanEvaluation() { @Test void getStringVariantEvaluation() { - assertEquals(VARIANT_1_VALUE, unleashProvider.getStringEvaluation(FLAG_NAME, "", + assertEquals(VARIANT_FLAG_VALUE, unleashProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", new ImmutableContext()).getValue()); - assertEquals(VARIANT_1_VALUE, client.getStringValue(FLAG_NAME, "")); + assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(VARIANT_FLAG_NAME, "")); assertEquals("fallback_str", unleashProvider.getStringEvaluation("non-existing", "fallback_str", new ImmutableContext()).getValue()); assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); @@ -86,8 +122,8 @@ void getStringVariantEvaluation() { @Test void getBooleanEvaluationByUser() { - String flagName = "testByUserId"; - UnleashContext unleashContext = UnleashContext.builder().userId("1").build(); + String flagName = "by-users"; + UnleashContext unleashContext = UnleashContext.builder().userId("111").build(); EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); assertEquals(true, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); assertEquals(true, client.getBooleanValue(flagName, false, evaluationContext)); @@ -107,9 +143,8 @@ void typeMismatch() { @SneakyThrows @Test void asyncInitTest() { - UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false); + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", ""); OpenFeatureAPI.getInstance().setProvider("async", asyncInitUnleashProvider); - Client asyncClient = OpenFeatureAPI.getInstance().getClient("async"); assertEquals(ProviderState.NOT_READY, asyncInitUnleashProvider.getState()); // ErrorCode.PROVIDER_NOT_READY should be returned when evaluated via the client @@ -130,24 +165,26 @@ void asyncInitTest() { @SneakyThrows @Test void contextTransformTest() { - Map values = new HashMap<>(); String appNameValue = "appName_value"; - values.put("appName", Value.objectToValue(appNameValue)); String userIdValue = "userId_value"; - values.put("userId", Value.objectToValue(userIdValue)); String environmentValue = "environment_value"; - values.put("environment", Value.objectToValue(environmentValue)); String remoteAddressValue = "remoteAddress_value"; - values.put("remoteAddress", Value.objectToValue(remoteAddressValue)); String sessionIdValue = "sessionId_value"; - values.put("sessionId", Value.objectToValue(sessionIdValue)); ZonedDateTime currentTimeValue = ZonedDateTime.now(); - values.put("currentTime", Value.objectToValue(currentTimeValue.toString())); String customPropertyValue = "customProperty_value"; String customPropertyKey = "customProperty"; - values.put(customPropertyKey, Value.objectToValue(customPropertyValue)); - EvaluationContext evaluationContext = new ImmutableContext(values); + UnleashContext unleashContext = UnleashContext.builder() + .userId(userIdValue) + .currentTime(currentTimeValue) + .sessionId(sessionIdValue) + .remoteAddress(remoteAddressValue) + .environment(environmentValue) + .appName(appNameValue) + .addProperty(customPropertyKey, customPropertyValue) + .build(); + EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); + UnleashContext transformedUnleashContext = ContextTransformer.transform(evaluationContext); assertEquals(appNameValue, transformedUnleashContext.getAppName().get()); assertEquals(userIdValue, transformedUnleashContext.getUserId().get()); diff --git a/providers/unleash/src/test/resources/features.json b/providers/unleash/src/test/resources/features.json new file mode 100644 index 000000000..4223e483c --- /dev/null +++ b/providers/unleash/src/test/resources/features.json @@ -0,0 +1,4509 @@ +{ + "version": 1, + "features": [ + { + "name": "083db41c-c6c8-427b-815b-f735c3c6da60", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 49, + "stickiness": "default", + "groupId": "083db41c-c6c8-427b-815b-f735c3c6da60" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 50, + "stickiness": "default", + "groupId": "083db41c-6c8-427b-815b-f735c3c6da60" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "asdfasdf", + "weight": 500, + "weightType": "variable", + "overrides": [], + "stickiness": "default" + }, + { + "name": "agasdgaegagse", + "weight": 500, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "21", + "type": "permission", + "enabled": true, + "stale": true, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "asd,asda,1223" }, + "constraints": [] + }, + { + "name": "as", + "parameters": { "asd": "123", "asdasd": 45, "isProd": "true" }, + "constraints": [] + } + ], + "variants": [ + { + "name": "v1", + "weight": 1000, + "weightType": "variable", + "overrides": [], + "stickiness": "default" + } + ] + }, + { + "name": "2afc3854-6157-4abf-a87b-e59510bc96e9", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "OPR_CODE", + "parameters": { "OPR_CODE": "" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "A123", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "CustomId", + "parameters": { "CustomerId": "a,b,c,e" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "aaa", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "foobar", + "parameters": { "bar_p": "100", "ENVIRONMENT": "DEV" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "aaaasdw", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "aasas", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "abc", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + } + ], + "variants": [] + }, + { + "name": "abca", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "" } } + ], + "variants": [] + }, + { + "name": "accounts.reset-password.random.rollout", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "ActivityLog", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "", + "QA": "", + "Test": "", + "Training": "", + "Prod": "" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "type_1", + "weight": 500, + "payload": { "type": "string", "value": "opt_1" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "type_2", + "weight": 500, + "payload": { "type": "string", "value": "opt_2" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "ActivityLogTesting", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "gradualRolloutSessionId", + "parameters": { + "percentage": 50, + "groupId": "ActivityLogTesting" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 75, + "stickiness": "default", + "groupId": "ActivityLogTesting" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "a-demo", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "82", + "stickiness": "default", + "groupId": "a-demo" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "adsda", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "random", + "groupId": "adsda" + }, + "constraints": [] + }, + { + "name": "forClient", + "parameters": { "clientName": "Android,IOS" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "aerronpro", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "agasdfasdf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "34", + "stickiness": "default", + "groupId": "agasdfasdf" + }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "foobar", + "parameters": { "ENVIRONMENT": "" }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "userWithId", + "parameters": { "userIds": "" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "alloe", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Aname", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "de" }, + "constraints": [] + }, + { + "name": "userWithId", + "parameters": { "userIds": "de_23456,fr_5667789" }, + "constraints": [] + } + ], + "variants": [ + { + "name": "v1", + "weight": 1000, + "payload": { "type": "string", "value": "test" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "A-new.one", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "", + "Test": "", + "Training": "", + "Prod": "" + } + }, + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [ + { + "name": "foobar", + "weight": 1000, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "another-toggle", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "anotta-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "aoeu", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "app", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "46", + "stickiness": "default", + "groupId": "app" + } + } + ], + "variants": [] + }, + { + "name": "app.main", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "laci" } } + ], + "variants": [] + }, + { + "name": "app.ToggleX", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Aruba-Switch", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "asasas", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "test1", + "parameters": { "codes": "sas,{sas: \"\"sa sasasas\" }" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "ASASASAS", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "OPR_CODE", + "parameters": { "OPR_CODE": "CUST" }, + "constraints": [] + }, + { + "name": "userWithId", + "parameters": { "userIds": "" }, + "constraints": [] + }, + { + "name": "userWithId", + "parameters": { "userIds": "123,4,5" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "41", + "stickiness": "default", + "groupId": "ASASASAS" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "v1", + "weight": 1000, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "asd", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "asd" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "asd22", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "default", + "groupId": "asd22" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "asd22dsf", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "asd2asd", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": 33, + "stickiness": "default", + "groupId": "asd2asd" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 46, + "stickiness": "default", + "groupId": "asd2asd" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "asdasd", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "asdasd" + } + } + ], + "variants": [] + }, + { + "name": "asdasd2", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "asdf", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "asdfasdfasdf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "asdqddq", + "type": "permission", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "rollout_schemas", + "parameters": { "schemas": "let" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Asdqwe1we", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "aaaa", + "weight": 1000, + "payload": { "type": "json", "value": "{ a: 1 }" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "asmallname", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "version", "parameters": { "version code": "15" } } + ], + "variants": [ + { + "name": "apilevel", + "weight": 1000, + "payload": { "type": "string", "value": "100" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "auto-grading", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "default", + "groupId": "auto-grading" + }, + "constraints": [] + }, + { + "name": "userWithId", + "parameters": { "userIds": "123,1234,4441" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "AwesomeFeature", + "type": "permission", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [] + }, + { + "name": "AwesomeFF", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "47", + "stickiness": "userId", + "groupId": "AwesomeFF" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "bbbbb", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "hhhhh", "parameters": { "8": "58" } }], + "variants": [] + }, + { + "name": "bbbbbbb", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "bccnj", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "bfg_feature", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userParam", "parameters": { "userType": "basic" } } + ], + "variants": [] + }, + { + "name": "bla", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "demo", "parameters": { "demoGroup": "" } } + ], + "variants": [] + }, + { + "name": "bnmbmnb", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": 75, + "stickiness": "default", + "groupId": "" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "bookable_authorized", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "20" } } + ], + "variants": [] + }, + { + "name": "Booyeah-Who-Is-The-King", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "v1", + "weight": 1000, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "by-users", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "111,234" } } + ], + "variants": [] + }, + { + "name": "c79422b2-5c8e-407a-a389-77bdb5b6ad0a", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "cf", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "CID-SFDC", + "type": "operational", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "cid-strategy", + "parameters": { "solution": "VCF,SYN" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "CoffeeShop", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "covid-19", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "create", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "cross_featrure", + "type": "operational", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "dark-theme", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "1,2,3" }, + "constraints": [] + }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "" }, + "constraints": [] + }, + { "name": "remoteAddress", "parameters": { "IPs": "" } } + ], + "variants": [] + }, + { + "name": "dashboard_enabled", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "DateExample", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Demo", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "52", + "stickiness": "default", + "groupId": "Demo" + }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [ + { + "name": "small", + "weight": 500, + "weightType": "variable", + "payload": { "type": "string", "value": "35" }, + "overrides": [], + "stickiness": "default" + }, + { + "name": "medium", + "weight": 500, + "payload": { "type": "string", "value": "55" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "Demo123", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Demo22", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Demo33", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "24", + "stickiness": "default", + "groupId": "Demo33" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "25", + "stickiness": "default", + "groupId": "Demo33" + }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [ + { + "name": "dfsd", + "weight": 500, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "asdasd", + "weight": 500, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "Demo43", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": 56, + "stickiness": "default", + "groupId": "" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 49, + "stickiness": "default", + "groupId": "Demo43" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Demo44", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "de_sro" }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "47", + "stickiness": "default", + "groupId": "Demo44" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "default", + "groupId": "Demo44" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Demo.Slack", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Demossss", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "test1", + "parameters": { "services": "", "solutions": "" } + } + ], + "variants": [] + }, + { + "name": "Devtools", + "type": "permission", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "dfgfdg", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "" }, + "constraints": [] + }, + { + "name": "remoteAddress", + "parameters": { "IPs": "tyhtyh" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "dghjm", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "dinesh-t", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "1234" } } + ], + "variants": [] + }, + { + "name": "Doemaariets", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Domain", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "true", + "Test": "", + "Training": "", + "Prod": "" + } + }, + { + "name": "customerWithId", + "parameters": { "CustomerId": "123,321,123321" } + } + ], + "variants": [] + }, + { + "name": "eee", + "type": "permission", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "TestGroup", + "parameters": { "Group": "kjherkjqwehrkew" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "eeee", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "example-experiment", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "domain.com" } + } + ], + "variants": [] + }, + { + "name": "ExampleFeature", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "nishtha.kakkar.8@gmail.com" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Experiment", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "forClient", + "parameters": { "clientName": "bob,yay" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Experiment-", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Experiment-Test", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "blue", + "weight": 800, + "weightType": "variable", + "payload": { "type": "string", "value": "blueblue" }, + "overrides": [] + }, + { + "name": "red", + "weight": 200, + "weightType": "fix", + "overrides": [] + } + ] + }, + { + "name": "experiment-toggle", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "123456" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "userId", + "groupId": "experiment-toggle" + } + } + ], + "variants": [] + }, + { + "name": "fadfafaf", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Feature12", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [] + }, + { + "name": "feature1.subfeature1", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "var1", + "weight": 1000, + "payload": { "type": "json", "value": "test" }, + "overrides": [{ "contextName": "userId", "values": ["test2"] }], + "weightType": "variable" + } + ] + }, + { + "name": "feature-2", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "", + "Test": "", + "Training": "", + "Prod": "" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "featureFlagYeah", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "FeatureToggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "featureX", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "23", + "stickiness": "default", + "groupId": "featureX" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "fetch-2-feature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "fetch-2-feature-2", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "fetch-toggle", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "fe-test-1", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "FF", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "fff", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "remoteAddress", "parameters": { "IPs": "ffff,fff" } } + ], + "variants": [] + }, + { + "name": "Fffff", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "finish-him", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "15", + "stickiness": "random", + "groupId": "finish-him" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "first", + "type": "kill-switch", + "enabled": false, + "stale": true, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "first" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "Demo", + "weight": 1000, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "fix-invalid-date", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "foo2", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "foobar", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + } + ], + "variants": [] + }, + { + "name": "foobar2", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "foobar-shazam", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "foobar" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "foobar-toogle", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "foobar", "parameters": { "bar_p": "47" } } + ], + "variants": [] + }, + { + "name": "for-min-egen-del", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "" }, + "constraints": [] + }, + { + "name": "demo", + "parameters": { "demoGroup": "test" }, + "constraints": [] + } + ], + "variants": [ + { + "name": "myvariant", + "weight": 1000, + "payload": { "type": "string", "value": "test" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "fvjnfgjh", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": 61, + "stickiness": "userId", + "groupId": "fvjnfgjh" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "gdgfd", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "gerg", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "gjhbjhb", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "GoPower", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "hack", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "hackweek-22-test-feature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [] + }, + { + "name": "hc-testing", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "hello", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "cooks", "parameters": { "Ttt": "" } }], + "variants": [] + }, + { + "name": "Hello", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "hello_there", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "11", + "stickiness": "default", + "groupId": "hello_there" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "blue", + "weight": 500, + "payload": { "type": "string", "value": "blue" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "green", + "weight": 500, + "payload": { "type": "string", "value": "xx" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "hello-world", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "HelloWorld", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + } + ], + "variants": [] + }, + { + "name": "helloworldtoggle", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "hello-world-two-toggle", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "hgfhgf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "", + "QA": "", + "Test": "", + "Training": "", + "Prod": "true" + } + } + ], + "variants": [] + }, + { + "name": "hggfhf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "MyStrategy", + "parameters": { "param1": "jhghf", "param2": "kjlhkgjfhf" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "hjfghgfhf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "", + "QA": "", + "Test": "", + "Training": "", + "Prod": "" + } + } + ], + "variants": [] + }, + { + "name": "hjkhjkhjkhjk", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "default", + "groupId": "hjkhjkhjkhjk" + } + } + ], + "variants": [] + }, + { + "name": "hjklhghj", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "jhkjhkj", + "Test": "hjkhjkhkj", + "Training": "jkhjkhjk", + "Prod": "hjkhkj" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "hock", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Hola", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "HookbasedSytem", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "hot-instead-of-cold-salad", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Httgghghg", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "H" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "Hghg", + "weight": 500, + "weightType": "fix", + "payload": { "type": "string", "value": "Red" }, + "overrides": [], + "stickiness": "default" + }, + { + "name": "Blue", + "weight": 500, + "weightType": "variable", + "payload": { "type": "string", "value": "blue" }, + "overrides": [], + "stickiness": "default" + } + ] + }, + { + "name": "InativarProdutos", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "jeroku.test", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "jjjjjjj", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "MyStrategy", + "parameters": { "param1": "8", "param2": "9" } + } + ], + "variants": [] + }, + { + "name": "jknkjbhkjvjhk", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "jknkjbhkjvjhk" + } + }, + { "name": "cid-strategy", "parameters": { "solution": "kjn" } } + ], + "variants": [] + }, + { + "name": "Jorge", + "type": "permission", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "remoteAddress", + "parameters": { "IPs": "tttt" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "josh-unique-flag", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "killswitch", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "1234" } + }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "12345" } + } + ], + "variants": [] + }, + { + "name": "KillWiFi", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "13", + "stickiness": "random", + "groupId": "KillWiFi" + } + } + ], + "variants": [] + }, + { + "name": "KS", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "lala", + "type": "experiment", + "enabled": true, + "stale": true, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "liroy", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "variant1", + "weight": 550, + "weightType": "fix", + "payload": { "type": "string", "value": "value1" }, + "overrides": [], + "stickiness": "default" + }, + { + "name": "variant2", + "weight": 150, + "payload": { "type": "string", "value": "value2" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "variant3", + "weight": 300, + "weightType": "fix", + "payload": { "type": "string", "value": "value3" }, + "overrides": [], + "stickiness": "default" + } + ] + }, + { + "name": "lkmasd", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "lkmasd" + } + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "lkmasd" + } + } + ], + "variants": [] + }, + { + "name": "LLCampaign", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "location", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "LocationTags", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "awsomeUserStategy", + "parameters": { "userId": "23" }, + "constraints": [] + } + ], + "variants": [ + { + "name": "color", + "weight": 500, + "weightType": "variable", + "payload": { "type": "string", "value": "red" }, + "overrides": [ + { "contextName": "appName", "values": ["appppp"] } + ], + "stickiness": "default" + }, + { + "name": "color2", + "weight": 500, + "payload": { "type": "string", "value": "123" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "LodeKaSarKaarHai", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "lof", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "ly_test", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "mahshad-pyladies", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "123456" } } + ], + "variants": [] + }, + { + "name": "mahshad-second-test", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "0", + "stickiness": "default", + "groupId": "mahshad-second-test" + }, + "constraints": [] + }, + { + "name": "userWithId", + "parameters": { "userIds": "1,2,3,sss,sssss" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "mahshad-unleash-test", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "mahshad-unleash-test" + } + }, + { "name": "userWithId", "parameters": { "userIds": "" } } + ], + "variants": [] + }, + { + "name": "max-feature", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "mcast", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "roleBasedStrategy", + "parameters": { "roleId": "admin" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 14, + "stickiness": "random", + "groupId": "mcast" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "mklasdm", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": 30, + "stickiness": "default", + "groupId": "mklasdm" + }, + "constraints": [] + }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "mehome" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 30, + "stickiness": "default", + "groupId": "mklasdm" + }, + "constraints": [] + }, + { + "name": "gradualRolloutRandom", + "parameters": { "percentage": 46 }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "mmmm", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "userWithId", + "parameters": { "userIds": "1" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "mobile", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "mockTimeRecordingData", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "RuntimeEnvironmentBased", + "parameters": { "runtimeEnvironmentId": "TEST" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "my-application.hack", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-debug-feature", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "My_feature", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "home", + "weight": 500, + "weightType": "variable", + "overrides": [], + "stickiness": "default" + }, + { + "name": "type", + "weight": 500, + "payload": { "type": "string", "value": "abc" }, + "overrides": [ + { "contextName": "userId", "values": ["1"] }, + { "contextName": "userId", "values": ["2"] } + ], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "myFeatureToggle", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "MyGreatAlgorithm", + "type": "permission", + "enabled": false, + "stale": true, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "MyGreatAlgorithm" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "56", + "stickiness": "default", + "groupId": "MyGreatAlgorithm" + }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [] + }, + { + "name": "my-hero-comp", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "shan.dev.com" } + } + ], + "variants": [] + }, + { + "name": "mykey", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "default", + "groupId": "mykey" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "my-new-feature", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-new-feature-1", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-new-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "mynew-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "mytest", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-test-boyz", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "9", + "stickiness": "sessionId", + "groupId": "my-test-boyz" + }, + "constraints": [] + }, + { + "name": "Specialgroup", + "parameters": { "mail": "ciao" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "my-test-feature", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "123" } } + ], + "variants": [] + }, + { + "name": "my-testing-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-test-toggle", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-test-toggle2", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-test-toggle-2", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "My-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": 48, + "stickiness": "sessionId", + "groupId": "My-toggle" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "my-unique-test-name", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "random", + "groupId": "my-unique-test-name" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "my-unique-toggel", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "my-very-unique-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 84, + "stickiness": "default", + "groupId": "my-very-unique-toggle" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "asdfasdf", + "weight": 500, + "weightType": "variable", + "overrides": [], + "stickiness": "default" + }, + { + "name": "agasdgaegagse", + "weight": 500, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "name-tester", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "ok" } } + ], + "variants": [ + { + "name": "T1", + "weight": 500, + "weightType": "variable", + "payload": { "type": "string", "value": "T1" }, + "overrides": [], + "stickiness": "default" + }, + { + "name": "CC", + "weight": 500, + "weightType": "variable", + "payload": { "type": "string", "value": "C" }, + "overrides": [], + "stickiness": "default" + } + ] + }, + { + "name": "NarendraPatil", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { "name": "remoteAddress", "parameters": { "IPs": "" } }, + { "name": "default", "parameters": {} }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + }, + { "name": "demo", "parameters": { "demoGroup": "" } }, + { "name": "userWithId", "parameters": { "userIds": "" } } + ], + "variants": [] + }, + { + "name": "NarendraTesting", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "nar,rus" } } + ], + "variants": [] + }, + { + "name": "needs-improvement", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "new-api", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [ + { + "name": "v1", + "weight": 1000, + "weightType": "fix", + "payload": { "type": "string", "value": "v1" }, + "overrides": [], + "stickiness": "default" + }, + { + "name": "v2", + "weight": 0, + "weightType": "variable", + "payload": { "type": "string", "value": "v2" }, + "overrides": [{ "contextName": "userId", "values": ["me"] }], + "stickiness": "default" + } + ] + }, + { + "name": "new-feature-815", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "newFeatureCustomerId", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "customerWithId", + "parameters": { "CustomerId": "104057243,104067509" }, + "constraints": [] + }, + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "true", + "Test": "false", + "Training": "true", + "Prod": "" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "newFeatureDiscount", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "HugeDiscount", + "weight": 500, + "weightType": "variable", + "payload": { "type": "string", "value": "50" }, + "overrides": [ + { "contextName": "userId", "values": ["kvajars"] } + ], + "stickiness": "default" + }, + { + "name": "SmallDiscount", + "weight": 500, + "weightType": "variable", + "payload": { "type": "string", "value": "5" }, + "overrides": [ + { "contextName": "userId", "values": ["aksoles"] } + ], + "stickiness": "default" + } + ] + }, + { + "name": "newFeatureToggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "server1" }, + "constraints": [] + }, + { + "name": "remoteAddress", + "parameters": { "IPs": "172.10.0.0" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "newFeatureUserId", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "userId", + "groupId": "newFeatureUserId" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "NewFet", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "newKillSwitch", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "PayloadVariant", + "weight": 1000, + "weightType": "variable", + "payload": { + "type": "string", + "value": "TFF er for tiden utilgjengelig. Det jobbes med saken og vi forventer at det vil være løst innen 15:00." + }, + "overrides": [], + "stickiness": "default" + } + ] + }, + { + "name": "OW", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "permissiontoggle", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "plannified-down-time", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "poc-forms-hub", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [] + }, + { + "name": "qweqweawe", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "", + "Test": "", + "Training": "true", + "Prod": "" + }, + "constraints": [] + }, + { + "name": "generaltest", + "parameters": { + "rollout": 49, + "userIds": "TH,ERT", + "On/off": "true" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "RC", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Reallycoolfeature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "cid-strategy", "parameters": { "solution": "" } } + ], + "variants": [] + }, + { + "name": "RecruiterPortalFeedback", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "RecruiterPortalFeedbackswsss", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "regra", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + } + ], + "variants": [] + }, + { + "name": "relaseWhenReady", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "ResponseFeature", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "ResponseFeature2", + "weight": 1000, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "RiccardoTest", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "RiccardoTest" + } + } + ], + "variants": [] + }, + { + "name": "robert", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "", + "QA": "", + "Test": "", + "Training": "", + "Prod": "" + } + } + ], + "variants": [] + }, + { + "name": "Roman", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [] + }, + { + "name": "rs-fs-1929", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "10", + "stickiness": "userId", + "groupId": "rs-fs-1929" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "S3Conversion", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "CustomId", "parameters": { "CustomerId": "123" } }, + { + "name": "CustomId", + "parameters": { "CustomerId": "321" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "sad", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "sadasd", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "sadasd" + }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "sadasd" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "sadasdsa", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "dasda" } + } + ], + "variants": [] + }, + { + "name": "SCID", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "foobar", + "parameters": { "ENVIRONMENT": "DEV" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "ScreeningQ", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "sdasd", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + }, + { "name": "default", "parameters": {} } + ], + "variants": [] + }, + { + "name": "sdasdasd", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "default", + "groupId": "sdasdasd" + } + } + ], + "variants": [] + }, + { + "name": "sdfgsdfg", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "sdfsdf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "sdfsdfdfdfd", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "gradualRolloutRandom", + "parameters": { "percentage": 41 }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "sdfsdfsdf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "sadasd,sad" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "63", + "stickiness": "default", + "groupId": "sdfsdfsdf" + }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "68", + "stickiness": "default", + "groupId": "sdfsdfsdf" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "sdsf", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "sdfbbbbb", + "weight": 1000, + "weightType": "variable", + "payload": { "type": "string", "value": "ffff" }, + "overrides": [ + { "contextName": "userId", "values": ["gggg"] }, + { "contextName": "userId", "values": ["v1"] } + ] + } + ] + }, + { + "name": "secure-installation", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "1,2" } } + ], + "variants": [] + }, + { + "name": "Shop", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "shutdown", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Some-new-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "41", + "stickiness": "default", + "groupId": "Some-new-toggle" + } + } + ], + "variants": [] + }, + { + "name": "Something", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Somethingcool", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "spring-variants", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "75", + "stickiness": "default", + "groupId": "spring-variants" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "test", + "weight": 800, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "this-is-fun", + "weight": 200, + "weightType": "fix", + "overrides": [], + "stickiness": "default" + } + ] + }, + { + "name": "sss", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "SuperAwesomeFeature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "superfeature", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "super-truper", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "Implicit", "parameters": { "macro": "6" } } + ], + "variants": [] + }, + { + "name": "sweetness", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "75", + "stickiness": "default", + "groupId": "sweetness" + } + } + ], + "variants": [] + }, + { + "name": "SwitchFeature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "SwitchOnline", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Taco", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "123,1" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Test", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "TEST", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-01", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "foruser", + "weight": 1000, + "overrides": [{ "contextName": "userId", "values": ["1"] }], + "weightType": "variable" + } + ] + }, + { + "name": "test1", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "test1", + "parameters": { "codes": "" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test-11111-kill-feature-ever-released", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "70", + "stickiness": "default", + "groupId": "test-11111-kill-feature-ever-released" + } + } + ], + "variants": [] + }, + { + "name": "test123", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-123", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "RuntimeEnvironmentBased", + "parameters": { "runtimeEnvironmentId": "" } + }, + { "name": "test1", "parameters": { "codes": "" } } + ], + "variants": [] + }, + { + "name": "Test-123", + "type": "release", + "enabled": true, + "stale": true, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "jjjjjjj" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test1231231", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test1234214", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Test234", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "true", + "Test": "", + "Training": "", + "Prod": "false" + } + } + ], + "variants": [] + }, + { + "name": "test2345", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "32", + "stickiness": "default", + "groupId": "test2345" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test256", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "sdzxfsdf" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": 38, + "stickiness": "default", + "groupId": "test256" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test.3.12.0", + "type": "experiment", + "enabled": true, + "stale": true, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "47", + "stickiness": "default", + "groupId": "test.3.12.0" + }, + "constraints": [] + }, + { + "name": "test-env", + "parameters": { "prod": "true", "dev": "true" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Test33", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "50", + "stickiness": "userId", + "groupId": "Test33" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test777", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test8878687", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "test", "parameters": { "users": "tttt,hhjgj" } } + ], + "variants": [] + }, + { + "name": "test-cid", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "cid-strategy", + "parameters": { "solution": "VCF,SYN,VCF_4.10" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test.create", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Test.create", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "demo", + "parameters": { "demoGroup": "" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test.create.adddon", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "teste", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "teste1", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "test1", "parameters": {}, "constraints": [] } + ], + "variants": [] + }, + { + "name": "teste123123", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "asd,teste,tesasd" } + } + ], + "variants": [] + }, + { + "name": "Tester", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-feature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { "name": "userWithId", "parameters": { "userIds": "" } } + ], + "variants": [] + }, + { + "name": "testFeature", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-feature-1", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "selected-schools", + "parameters": { "schoolId": "bla" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test-feature-99999", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Test-FF", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "demo", + "parameters": { "demoGroup": "abc" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test-for-me", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "TestGroup", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "TestGroup", "parameters": { "Group": "Foo" } }, + { "name": "TestGroup", "parameters": { "Group": "Bar" } } + ], + "variants": [] + }, + { + "name": "testgtasdas", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + } + ], + "variants": [] + }, + { + "name": "Testing", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "" } + } + ], + "variants": [] + }, + { + "name": "testing123", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "testing-toggle-12", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "testlog", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-new-feature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "userWithId", "parameters": { "userIds": "5" } } + ], + "variants": [] + }, + { + "name": "Test_Permission1", + "type": "permission", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "test1", + "parameters": { "codes": "code1,code2" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "test.ping", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "testproduct", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-raf", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-release-flag", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "endpoint", + "weight": 1000, + "payload": { "type": "string", "value": "this_is_the_endpoint" }, + "overrides": [], + "weightType": "variable" + } + ] + }, + { + "name": "TestSwitch1", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-t", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "testtertogle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "testtest", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { "name": "dfazza", "parameters": { "asdfasdf": "afasd" } } + ], + "variants": [] + }, + { + "name": "test-test", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "test,test1,test2" }, + "constraints": [] + }, + { "name": "default", "parameters": {}, "constraints": [] } + ], + "variants": [ + { + "name": "t1", + "weight": 750, + "weightType": "fix", + "payload": { "type": "string", "value": "t1" }, + "overrides": [{ "contextName": "userId", "values": ["abcdef"] }], + "stickiness": "default" + }, + { + "name": "t2", + "weight": 250, + "payload": { "type": "string", "value": "t2" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "test-test-and-some-more-test", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "", + "QA": "", + "Test": "", + "Training": "", + "Prod": "true" + } + } + ], + "variants": [] + }, + { + "name": "TestTesting", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "default", "parameters": {} }, + { + "name": "asd", + "parameters": { "asd": "asd", "sdf": "" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "testtoggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test-toggle", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "testtt", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "test_variant", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "version", + "weight": 1000, + "payload": { "type": "string", "value": "v1" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "test-w", + "type": "operational", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Testymctestface", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "true", + "Test": "", + "Training": "", + "Prod": "true" + } + } + ], + "variants": [] + }, + { + "name": "tgtg", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "the-awesome-feature", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Theone", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "ticket", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "demo", "parameters": { "demoGroup": "dsfasfd" } } + ], + "variants": [ + { + "name": "bool", + "weight": 1000, + "weightType": "variable", + "payload": { "type": "string", "value": "valuess" }, + "overrides": [], + "stickiness": "default" + } + ] + }, + { + "name": "TotesMcGoats", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "trololo", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "tryion.dev.auth", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "Ttt", + "type": "operational", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "uname", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "unique", + "type": "kill-switch", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "UniqueCreateContractFeature", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "unleaseh-my-mama", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "demo", "parameters": { "demoGroup": "you" } } + ], + "variants": [] + }, + { + "name": "unleash_aws_s3", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "gradualRolloutRandom", + "parameters": { "percentage": "60" } + } + ], + "variants": [ + { + "name": "s3", + "weight": 333, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "gcp", + "weight": 333, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "azure", + "weight": 333, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "unleash-demo.avansert", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "testbruker" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "47", + "stickiness": "default", + "groupId": "unleash-demo.avansert" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "unleash-demo.hjemmelagd", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "byEnhet", + "parameters": { "valgtEnhet": "456" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "unleash-demo.scheduler", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "unleash-demo.scheduler.default-pa", + "type": "release", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "user.pornFilter", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "UserID", + "parameters": { "AD_GRP_1, AD_GRP_2": "asdf,asdf2,asdf3,asdf4" } + } + ], + "variants": [] + }, + { + "name": "vartest", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "v1", + "weight": 333, + "payload": { "type": "string", "value": "v1" }, + "overrides": [], + "weightType": "variable" + }, + { + "name": "v2", + "weight": 333, + "payload": { "type": "string", "value": "v2" }, + "overrides": [], + "weightType": "variable" + }, + { + "name": "v3", + "weight": 333, + "payload": { "type": "string", "value": "v3" }, + "overrides": [], + "weightType": "variable" + } + ] + }, + { + "name": "vcvcvc-test-feature", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { "name": "foobar", "parameters": { "ENVIRONMENT": "DEV" } }, + { + "name": "applicationHostname", + "parameters": { "hostNames": "test" }, + "constraints": [] + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "49", + "stickiness": "default", + "groupId": "vcvcvc-test-feature" + }, + "constraints": [] + }, + { + "name": "remoteAddress", + "parameters": { "IPs": "1.1.1.1" }, + "constraints": [] + }, + { + "name": "Environment", + "parameters": { + "Dev": "true", + "QA": "", + "Test": "", + "Training": "", + "Prod": "" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "Very.cool.toggle", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "49", + "stickiness": "default", + "groupId": "Very.cool.toggle" + } + }, + { + "name": "flexibleRollout", + "parameters": { + "rollout": "25", + "stickiness": "default", + "groupId": "Very.cool.toggle" + }, + "constraints": [] + } + ], + "variants": [ + { + "name": "xc", + "weight": 500, + "payload": { "type": "string", "value": "sdf" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + }, + { + "name": "dsfsdf", + "weight": 500, + "payload": { "type": "string", "value": "sdf" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "vlad", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "wasd", + "type": "experiment", + "enabled": false, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "51", + "stickiness": "default", + "groupId": "wasd" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "whitelabel", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "whitelist", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { "userIds": "juan@gmail.com" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "wwwwwwww", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "39", + "stickiness": "default", + "groupId": "wwwwwwww" + }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "xcvh", + "type": "kill-switch", + "enabled": false, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "xxx", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "flexibleRollout", + "parameters": { + "rollout": "100", + "stickiness": "userId", + "groupId": "xxx" + } + } + ], + "variants": [] + }, + { + "name": "yest", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [] + }, + { + "name": "zxc", + "type": "operational", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "trunk,testing" }, + "constraints": [] + } + ], + "variants": [] + }, + { + "name": "zXVa", + "type": "release", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "applicationHostname", + "parameters": { "hostNames": "dsagfa,asdfadf" }, + "constraints": [] + } + ], + "variants": [] + } + ] +} diff --git a/release-please-config.json b/release-please-config.json index f4c12abfe..b1bf18a8b 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -56,6 +56,17 @@ "README.md" ] }, + "providers/unleash": { + "package-name": "dev.openfeature.contrib.providers.unleash", + "release-type": "simple", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": true, + "versioning": "default", + "extra-files": [ + "pom.xml", + "README.md" + ] + }, "hooks/open-telemetry": { "package-name": "dev.openfeature.contrib.hooks.otel", "release-type": "simple", From f67742ab65d732ac6531d4ca965b305f87b9c1fd Mon Sep 17 00:00:00 2001 From: liran2000 Date: Sat, 9 Sep 2023 21:01:37 +0300 Subject: [PATCH 06/17] update WireMock version to match SDK Java version Signed-off-by: liran2000 --- providers/unleash/pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/providers/unleash/pom.xml b/providers/unleash/pom.xml index cdbf7783c..7f1d43420 100644 --- a/providers/unleash/pom.xml +++ b/providers/unleash/pom.xml @@ -30,13 +30,12 @@ - org.wiremock - wiremock - 3.0.3 + com.github.tomakehurst + wiremock-jre8 + 2.35.1 test - org.apache.logging.log4j log4j-slf4j2-impl From 92e3a62673a9a89fd14cf4bcfc7beb1030c7af3f Mon Sep 17 00:00:00 2001 From: liran2000 Date: Sun, 10 Sep 2023 10:43:43 +0300 Subject: [PATCH 07/17] add shutdown method Signed-off-by: liran2000 --- .../providers/unleash/UnleashProvider.java | 18 ++++++++++ .../UnleashProviderIntegrationTest.java | 17 +++++++-- .../unleash/UnleashProviderTest.java | 35 +++++++++---------- .../src/test/resources/log4j2-test.xml | 4 +-- 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 459fcecd8..2a0ca7f1e 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -20,6 +20,8 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.atomic.AtomicBoolean; + import static io.getunleash.Variant.DISABLED_VARIANT; /** @@ -47,6 +49,8 @@ public class UnleashProvider extends EventProvider { @Getter private ProviderState state = ProviderState.NOT_READY; + private AtomicBoolean isInitialized = new AtomicBoolean(false); + /** * Constructor. * @param unleashOptions UnleashOptions @@ -62,6 +66,10 @@ public UnleashProvider(UnleashOptions unleashOptions) { */ @Override public void initialize(EvaluationContext evaluationContext) throws Exception { + boolean initialized = isInitialized.getAndSet(true); + if (initialized) { + throw new GeneralError("already initialized"); + } super.initialize(evaluationContext); UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( unleashOptions.getUnleashConfigBuilder().build().getSubscriber(), this); @@ -150,4 +158,14 @@ public ProviderEvaluation getDoubleEvaluation(String key, Double default public ProviderEvaluation getObjectEvaluation(String s, Value value, EvaluationContext evaluationContext) { throw new TypeMismatchError(NOT_IMPLEMENTED); } + + @Override + public void shutdown() { + super.shutdown(); + log.info("shutdown"); + if (unleash != null) { + unleash.shutdown(); + } + state = ProviderState.NOT_READY; + } } diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java index 8dc909e45..60900da5a 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java @@ -5,8 +5,11 @@ import dev.openfeature.sdk.OpenFeatureAPI; import io.getunleash.util.UnleashConfig; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,6 +18,7 @@ * To trigger manually only. * To test it, set API_KEY and other values accordingly. */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) @Slf4j class UnleashProviderIntegrationTest { @@ -26,10 +30,10 @@ class UnleashProviderIntegrationTest { private UnleashProvider unleashProvider; private Client client; - @BeforeEach + @BeforeAll void setUp() { if (API_KEY == null) { - log.debug("tests disabled"); + log.debug("init: tests disabled"); return; } unleashProvider = buildUnleashProvider(true); @@ -37,6 +41,15 @@ void setUp() { client = OpenFeatureAPI.getInstance().getClient("sync"); } + @AfterAll + public void shutdown() { + if (API_KEY == null) { + log.debug("shutdown: tests disabled"); + return; + } + unleashProvider.shutdown(); + } + private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation) { UnleashConfig.Builder unleashConfigBuilder = UnleashConfig.builder() diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index 3491dbe41..de6ac6d69 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -6,7 +6,6 @@ import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.OpenFeatureAPI; -import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.ProviderState; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; import dev.openfeature.sdk.exceptions.TypeMismatchError; @@ -18,8 +17,10 @@ import io.getunleash.repository.FeatureToggleResponse; import io.getunleash.util.UnleashConfig; import lombok.SneakyThrows; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import java.net.URI; import java.net.URL; @@ -45,16 +46,17 @@ * Inspired by Unleash tests. */ @WireMockTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class UnleashProviderTest { public static final String FLAG_NAME = "Demo"; public static final String VARIANT_FLAG_NAME = "new-api"; public static final String VARIANT_FLAG_VALUE = "v1"; - private TestSubscriber testSubscriber; - private UnleashProvider unleashProvider; - private Client client; + private static TestSubscriber testSubscriber; + private static UnleashProvider unleashProvider; + private static Client client; - @BeforeEach + @BeforeAll void setUp(WireMockRuntimeInfo wmRuntimeInfo) { testSubscriber = new TestSubscriber(); stubFor(any(anyUrl()).willReturn(aResponse() @@ -68,6 +70,11 @@ void setUp(WireMockRuntimeInfo wmRuntimeInfo) { client = OpenFeatureAPI.getInstance().getClient("sync"); } + @AfterAll + public void shutdown() { + unleashProvider.shutdown(); + } + private void mockUnleashAPI(String backupFileContent) { stubFor( get(urlEqualTo("/api/client/features")) @@ -142,24 +149,14 @@ void typeMismatch() { @SneakyThrows @Test - void asyncInitTest() { - UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", ""); - OpenFeatureAPI.getInstance().setProvider("async", asyncInitUnleashProvider); + void shouldThrowIfNotInitialized() { + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", "{}"); assertEquals(ProviderState.NOT_READY, asyncInitUnleashProvider.getState()); // ErrorCode.PROVIDER_NOT_READY should be returned when evaluated via the client assertThrows(ProviderNotReadyError.class, ()-> asyncInitUnleashProvider.getBooleanEvaluation("fail_not_initialized", false, new ImmutableContext())); - asyncInitUnleashProvider.initialize(new ImmutableContext()); - asyncInitUnleashProvider.emitProviderReady(ProviderEventDetails.builder().build()); - - assertEquals(ProviderState.READY, asyncInitUnleashProvider.getState()); - assertEquals(false, asyncInitUnleashProvider.getBooleanEvaluation("non-existing", false, new ImmutableContext()).getValue()); - assertEquals(true, unleashProvider.getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()).getValue()); - assertEquals(true, client.getBooleanValue(FLAG_NAME, false)); - - asyncInitUnleashProvider.emitProviderError(ProviderEventDetails.builder().build()); - assertEquals(ProviderState.ERROR, asyncInitUnleashProvider.getState()); + asyncInitUnleashProvider.shutdown(); } @SneakyThrows diff --git a/providers/unleash/src/test/resources/log4j2-test.xml b/providers/unleash/src/test/resources/log4j2-test.xml index 223d21a89..aced30f8a 100644 --- a/providers/unleash/src/test/resources/log4j2-test.xml +++ b/providers/unleash/src/test/resources/log4j2-test.xml @@ -1,12 +1,12 @@ - + - + From 8a6316705d8ac7779ad2f00db4b18710120de7d0 Mon Sep 17 00:00:00 2001 From: liran2000 Date: Sun, 10 Sep 2023 16:04:08 +0300 Subject: [PATCH 08/17] tests updates Signed-off-by: liran2000 --- .github/component_owners.yml | 2 + providers/unleash/README.md | 5 +- .../unleash/UnleashSubscriberWrapper.java | 6 +- .../UnleashProviderIntegrationTest.java | 94 ------------------- .../unleash/UnleashProviderTest.java | 60 ++++++++++-- 5 files changed, 58 insertions(+), 109 deletions(-) delete mode 100644 providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java diff --git a/.github/component_owners.yml b/.github/component_owners.yml index 4372c0a2a..0fd67db3f 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -19,6 +19,8 @@ components: - thomaspoignant providers/jsonlogic-eval-provider: - justinabrahms + providers/unleash: + - liran2000 ignored-authors: - renovate-bot \ No newline at end of file diff --git a/providers/unleash/README.md b/providers/unleash/README.md index f7bb02460..1ea9993d3 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -47,11 +47,8 @@ See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers ## Unleash Provider Tests Strategies -* Unit test based on Unleash instance with Unleash features schema file, with WireMock for API mocking. +Unit test based on Unleash instance with Unleash features schema file, with WireMock for API mocking. See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information. -* Integration Test based on Unleash instance connected to a live server. -This test is disabled by default, and meant for manual triggering only. -See [UnleashProviderIntegrationTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java) for more information. ## References * [Unleash](https://getunleash.io) diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java index 4a7ca448d..93e9e945a 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java @@ -5,7 +5,6 @@ import dev.openfeature.sdk.ProviderEventDetails; import io.getunleash.UnleashException; import io.getunleash.event.ImpressionEvent; -import io.getunleash.event.NoOpSubscriber; import io.getunleash.event.ToggleEvaluated; import io.getunleash.event.UnleashEvent; import io.getunleash.event.UnleashReady; @@ -15,6 +14,7 @@ import io.getunleash.repository.FeatureCollection; import io.getunleash.repository.FeatureToggleResponse; import io.getunleash.repository.ToggleCollection; +import lombok.Generated; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; @@ -23,6 +23,7 @@ * UnleashSubscriber wrapper for emitting event provider events. */ @Slf4j +@Generated public class UnleashSubscriberWrapper implements UnleashSubscriber { private UnleashSubscriber unleashSubscriber; @@ -35,9 +36,6 @@ public class UnleashSubscriberWrapper implements UnleashSubscriber { */ public UnleashSubscriberWrapper(@Nullable UnleashSubscriber unleashSubscriber, EventProvider eventProvider) { this.unleashSubscriber = unleashSubscriber; - if (this.unleashSubscriber == null) { - this.unleashSubscriber = new NoOpSubscriber(); - } this.eventProvider = eventProvider; } diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java deleted file mode 100644 index 60900da5a..000000000 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderIntegrationTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package dev.openfeature.contrib.providers.unleash; - -import dev.openfeature.sdk.Client; -import dev.openfeature.sdk.ImmutableContext; -import dev.openfeature.sdk.OpenFeatureAPI; -import io.getunleash.util.UnleashConfig; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * UnleashProvider Integration test via live Unleash instance. - * To trigger manually only. - * To test it, set API_KEY and other values accordingly. - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@Slf4j -class UnleashProviderIntegrationTest { - - private static final String FLAG_NAME = "open-feature_flag1"; - public static final String API_KEY = null; - public static final String API_URL = "https://app.unleash-hosted.com/demo/api/"; - public static final String APP_NAME = "open-feature"; - public static final String PROJECT_NAME = "open-feature"; - private UnleashProvider unleashProvider; - private Client client; - - @BeforeAll - void setUp() { - if (API_KEY == null) { - log.debug("init: tests disabled"); - return; - } - unleashProvider = buildUnleashProvider(true); - OpenFeatureAPI.getInstance().setProviderAndWait("sync", unleashProvider); - client = OpenFeatureAPI.getInstance().getClient("sync"); - } - - @AfterAll - public void shutdown() { - if (API_KEY == null) { - log.debug("shutdown: tests disabled"); - return; - } - unleashProvider.shutdown(); - } - - private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation) { - UnleashConfig.Builder unleashConfigBuilder = - UnleashConfig.builder() - .unleashAPI(API_URL) - .appName(APP_NAME) - .apiKey(API_KEY) - .projectName(PROJECT_NAME) - .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); - - UnleashOptions unleashOptions = UnleashOptions.builder() - .unleashConfigBuilder(unleashConfigBuilder) - .build(); - return new UnleashProvider(unleashOptions); - } - - @Test - void getBooleanEvaluation() { - if (API_KEY == null) { - log.debug("test disabled"); - return; - } - assertEquals(true, unleashProvider.getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()).getValue()); - assertEquals(true, client.getBooleanValue(FLAG_NAME, false)); - assertEquals(false, unleashProvider.getBooleanEvaluation("non-existing", false, new ImmutableContext()).getValue()); - assertEquals(false, client.getBooleanValue("non-existing", false)); - } - - @Test - void getStringVariantEvaluation() { - if (API_KEY == null) { - log.debug("test disabled"); - return; - } - assertEquals("default_variant_value", unleashProvider.getStringEvaluation(FLAG_NAME, "", - new ImmutableContext()).getValue()); - assertEquals("default_variant_value", client.getStringValue(FLAG_NAME, "")); - assertEquals("fallback_str", unleashProvider.getStringEvaluation("non-existing", - "fallback_str", new ImmutableContext()).getValue()); - assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); - } - -} \ No newline at end of file diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index de6ac6d69..565fd5b1e 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -6,7 +6,10 @@ import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.OpenFeatureAPI; +import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.ProviderState; +import dev.openfeature.sdk.Value; +import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; import dev.openfeature.sdk.exceptions.TypeMismatchError; import io.getunleash.UnleashContext; @@ -42,7 +45,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; /** - * UnleashProvider Test. + * UnleashProvider test, based on APIs mocking. * Inspired by Unleash tests. */ @WireMockTest @@ -52,20 +55,18 @@ class UnleashProviderTest { public static final String FLAG_NAME = "Demo"; public static final String VARIANT_FLAG_NAME = "new-api"; public static final String VARIANT_FLAG_VALUE = "v1"; - private static TestSubscriber testSubscriber; private static UnleashProvider unleashProvider; private static Client client; @BeforeAll void setUp(WireMockRuntimeInfo wmRuntimeInfo) { - testSubscriber = new TestSubscriber(); stubFor(any(anyUrl()).willReturn(aResponse() .withStatus(200) .withBody("{}"))); String unleashAPI = "http://localhost:" + wmRuntimeInfo.getHttpPort() + "/api/"; String backupFileContent = readBackupFile(); mockUnleashAPI(backupFileContent); - unleashProvider = buildUnleashProvider(true, unleashAPI, backupFileContent); + unleashProvider = buildUnleashProvider(true, unleashAPI, backupFileContent, new TestSubscriber()); OpenFeatureAPI.getInstance().setProviderAndWait("sync", unleashProvider); client = OpenFeatureAPI.getInstance().getClient("sync"); } @@ -88,8 +89,7 @@ private void mockUnleashAPI(String backupFileContent) { } @SneakyThrows - private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation, String unleashAPI, String backupFileContent) { - TestSubscriber testSubscriber = new TestSubscriber(); + private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation, String unleashAPI, String backupFileContent, TestSubscriber testSubscriber) { UnleashConfig.Builder unleashConfigBuilder = UnleashConfig.builder() .unleashAPI(new URI(unleashAPI)) @@ -145,16 +145,41 @@ void typeMismatch() { assertThrows(TypeMismatchError.class, () -> { unleashProvider.getIntegerEvaluation("test", 1, new ImmutableContext()); }); + assertThrows(TypeMismatchError.class, () -> { + unleashProvider.getDoubleEvaluation("test", 1.0, new ImmutableContext()); + }); + assertThrows(TypeMismatchError.class, () -> { + unleashProvider.getObjectEvaluation("test", new Value(), new ImmutableContext()); + }); } @SneakyThrows @Test void shouldThrowIfNotInitialized() { - UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", "{}"); + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", "{}", new TestSubscriber()); assertEquals(ProviderState.NOT_READY, asyncInitUnleashProvider.getState()); // ErrorCode.PROVIDER_NOT_READY should be returned when evaluated via the client assertThrows(ProviderNotReadyError.class, ()-> asyncInitUnleashProvider.getBooleanEvaluation("fail_not_initialized", false, new ImmutableContext())); + assertThrows(ProviderNotReadyError.class, ()-> asyncInitUnleashProvider.getStringEvaluation("fail_not_initialized", "", new ImmutableContext())); + + asyncInitUnleashProvider.initialize(null); + assertThrows(GeneralError.class, ()-> asyncInitUnleashProvider.initialize(null)); + + asyncInitUnleashProvider.shutdown(); + } + + @SneakyThrows + @Test + void shouldThrowIfErrorEvent() { + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", "{}", null); + asyncInitUnleashProvider.initialize(new ImmutableContext()); + + asyncInitUnleashProvider.emitProviderError(ProviderEventDetails.builder().build()); + + // ErrorCode.PROVIDER_NOT_READY should be returned when evaluated via the client + assertThrows(GeneralError.class, ()-> asyncInitUnleashProvider.getBooleanEvaluation("fail", false, new ImmutableContext())); + assertThrows(GeneralError.class, ()-> asyncInitUnleashProvider.getStringEvaluation("fail", "", new ImmutableContext())); asyncInitUnleashProvider.shutdown(); } @@ -192,6 +217,27 @@ void contextTransformTest() { assertEquals(customPropertyValue, transformedUnleashContext.getProperties().get(customPropertyKey)); } + @SneakyThrows + @Test + void subscriberWrapperTest() { + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, + "http://fakeAPI", "{}", null); + UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( + new TestSubscriber(), asyncInitUnleashProvider); + unleashSubscriberWrapper.clientMetrics(null); + unleashSubscriberWrapper.clientRegistered(null); + unleashSubscriberWrapper.featuresBackedUp(null); + unleashSubscriberWrapper.featuresBackupRestored(null); + unleashSubscriberWrapper.featuresBootstrapped(null); + unleashSubscriberWrapper.impression(null); + unleashSubscriberWrapper.toggleEvaluated(new ToggleEvaluated("dummy", false)); + unleashSubscriberWrapper.togglesFetched(new FeatureToggleResponse(FeatureToggleResponse.Status.NOT_CHANGED, +200)); + unleashSubscriberWrapper.toggleBackupRestored(null); + unleashSubscriberWrapper.togglesBackedUp(null); + unleashSubscriberWrapper.togglesBootstrapped(null); + } + private class TestSubscriber implements UnleashSubscriber { private FeatureToggleResponse.Status status; From 99dd6a7200f63bebe9362665a9a231eb9fc58fca Mon Sep 17 00:00:00 2001 From: liran2000 Date: Tue, 12 Sep 2023 19:44:46 +0300 Subject: [PATCH 09/17] - focused test features - update name - object evaluation Signed-off-by: liran2000 --- providers/unleash/README.md | 4 +- .../providers/unleash/UnleashProvider.java | 54 +- .../unleash/UnleashProviderTest.java | 41 +- .../unleash/src/test/resources/features.json | 4471 +---------------- 4 files changed, 80 insertions(+), 4490 deletions(-) diff --git a/providers/unleash/README.md b/providers/unleash/README.md index 1ea9993d3..1265af001 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -42,8 +42,8 @@ See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers ## Caveats / Limitations -* Unleash OpenFeature Provider only supports boolean and string evaluation. -* Unleash OpenFeature Provider only supports string variant type. +* json/csv payloads are evaluated via object evaluation as what returned from Unleash - string, wrapped with Value. +* integer/double evaluation currently not supported. ## Unleash Provider Tests Strategies diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 2a0ca7f1e..8fc3de184 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -31,7 +31,7 @@ public class UnleashProvider extends EventProvider { @Getter - private static final String NAME = "Unleash Provider"; + private static final String NAME = "Unleash"; public static final String NOT_IMPLEMENTED = "Not implemented - provider does not support this type. Only boolean is supported."; @@ -77,13 +77,8 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { UnleashConfig unleashConfig = unleashOptions.getUnleashConfigBuilder().build(); unleash = new DefaultUnleash(unleashConfig); - // else, state will be changed via UnleashSubscriberWrapper events - if (unleashConfig.isSynchronousFetchOnInitialisation()) { - state = ProviderState.READY; - } else { - log.info("ready state will be changed via UnleashSubscriberWrapper events"); - } - + // Unleash is per definition ready after it is initialized. + state = ProviderState.READY; log.info("finished initializing provider, state: {}", state); } @@ -121,6 +116,28 @@ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defa @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { + ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); + return ProviderEvaluation.builder() + .value(valueProviderEvaluation.getValue().asString()) + .variant(valueProviderEvaluation.getVariant()) + .errorCode(valueProviderEvaluation.getErrorCode()) + .reason(valueProviderEvaluation.getReason()) + .flagMetadata(valueProviderEvaluation.getFlagMetadata()) + .build(); + } + + @Override + public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { + throw new TypeMismatchError(NOT_IMPLEMENTED); + } + + @Override + public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { + throw new TypeMismatchError(NOT_IMPLEMENTED); + } + + @Override + public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, EvaluationContext ctx) { if (!ProviderState.READY.equals(state)) { if (ProviderState.NOT_READY.equals(state)) { throw new ProviderNotReadyError(PROVIDER_NOT_YET_INITIALIZED); @@ -130,35 +147,20 @@ public ProviderEvaluation getStringEvaluation(String key, String default UnleashContext context = ctx == null ? UnleashContext.builder().build() : ContextTransformer.transform(ctx); Variant evaluatedVariant = unleash.getVariant(key, context); String variantName; - String value; + Value value; if (DISABLED_VARIANT.equals(evaluatedVariant)) { variantName = null; value = defaultValue; } else { variantName = evaluatedVariant.getName(); - value = evaluatedVariant.getPayload().get().getValue(); + value = evaluatedVariant.getPayload().map(p -> new Value(p.getValue())).orElse(null); } - return ProviderEvaluation.builder() + return ProviderEvaluation.builder() .value(value) .variant(variantName) .build(); } - @Override - public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { - throw new TypeMismatchError(NOT_IMPLEMENTED); - } - - @Override - public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { - throw new TypeMismatchError(NOT_IMPLEMENTED); - } - - @Override - public ProviderEvaluation getObjectEvaluation(String s, Value value, EvaluationContext evaluationContext) { - throw new TypeMismatchError(NOT_IMPLEMENTED); - } - @Override public void shutdown() { super.shutdown(); diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index 565fd5b1e..d3cfe97d3 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -52,9 +52,14 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class UnleashProviderTest { - public static final String FLAG_NAME = "Demo"; - public static final String VARIANT_FLAG_NAME = "new-api"; + public static final String FLAG_NAME = "variant-flag"; + public static final String VARIANT_FLAG_NAME = "variant-flag"; public static final String VARIANT_FLAG_VALUE = "v1"; + public static final String USERS_FLAG_NAME = "users-flag"; + public static final String JSON_VARIANT_FLAG_NAME = "json-flag"; + public static final String JSON_VARIANT_FLAG_VALUE = "{ a: 1 }"; + public static final String CSV_VARIANT_FLAG_NAME = "csv-flag"; + public static final String CSV_VARIANT_FLAG_VALUE = "a,b,c"; private static UnleashProvider unleashProvider; private static Client client; @@ -127,17 +132,36 @@ void getStringVariantEvaluation() { assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); } + @Test + void getJsonVariantEvaluation() { + assertEquals(JSON_VARIANT_FLAG_VALUE, unleashProvider.getObjectEvaluation(JSON_VARIANT_FLAG_NAME, new Value(""), + new ImmutableContext()).getValue().asString()); + assertEquals(new Value(JSON_VARIANT_FLAG_VALUE), client.getObjectValue(JSON_VARIANT_FLAG_NAME, new Value(""))); + assertEquals("fallback_str", unleashProvider.getObjectEvaluation("non-existing", + new Value("fallback_str"), new ImmutableContext()).getValue().asString()); + assertEquals(new Value("fallback_str"), client.getObjectValue("non-existing", new Value("fallback_str"))); + } + + @Test + void getCSVVariantEvaluation() { + assertEquals(CSV_VARIANT_FLAG_VALUE, unleashProvider.getObjectEvaluation(CSV_VARIANT_FLAG_NAME, new Value(""), + new ImmutableContext()).getValue().asString()); + assertEquals(new Value(CSV_VARIANT_FLAG_VALUE), client.getObjectValue(CSV_VARIANT_FLAG_NAME, new Value(""))); + assertEquals("fallback_str", unleashProvider.getObjectEvaluation("non-existing", + new Value("fallback_str"), new ImmutableContext()).getValue().asString()); + assertEquals(new Value("fallback_str"), client.getObjectValue("non-existing", new Value("fallback_str"))); + } + @Test void getBooleanEvaluationByUser() { - String flagName = "by-users"; UnleashContext unleashContext = UnleashContext.builder().userId("111").build(); EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); - assertEquals(true, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); - assertEquals(true, client.getBooleanValue(flagName, false, evaluationContext)); + assertEquals(true, unleashProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals(true, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); unleashContext = UnleashContext.builder().userId("2").build(); evaluationContext = ContextTransformer.transform(unleashContext); - assertEquals(false, unleashProvider.getBooleanEvaluation(flagName, false, evaluationContext).getValue()); - assertEquals(false, client.getBooleanValue(flagName, false, evaluationContext)); + assertEquals(false, unleashProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals(false, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); } @Test @@ -148,9 +172,6 @@ void typeMismatch() { assertThrows(TypeMismatchError.class, () -> { unleashProvider.getDoubleEvaluation("test", 1.0, new ImmutableContext()); }); - assertThrows(TypeMismatchError.class, () -> { - unleashProvider.getObjectEvaluation("test", new Value(), new ImmutableContext()); - }); } @SneakyThrows diff --git a/providers/unleash/src/test/resources/features.json b/providers/unleash/src/test/resources/features.json index 4223e483c..241b0410c 100644 --- a/providers/unleash/src/test/resources/features.json +++ b/providers/unleash/src/test/resources/features.json @@ -2,338 +2,53 @@ "version": 1, "features": [ { - "name": "083db41c-c6c8-427b-815b-f735c3c6da60", + "name": "variant-flag", "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 49, - "stickiness": "default", - "groupId": "083db41c-c6c8-427b-815b-f735c3c6da60" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 50, - "stickiness": "default", - "groupId": "083db41c-6c8-427b-815b-f735c3c6da60" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "asdfasdf", - "weight": 500, - "weightType": "variable", - "overrides": [], - "stickiness": "default" - }, - { - "name": "agasdgaegagse", - "weight": 500, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "21", - "type": "permission", "enabled": true, - "stale": true, + "stale": false, "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "asd,asda,1223" }, - "constraints": [] - }, - { - "name": "as", - "parameters": { "asd": "123", "asdasd": 45, "isProd": "true" }, - "constraints": [] - } + { "name": "default", "parameters": {}, "constraints": [] } ], "variants": [ { "name": "v1", "weight": 1000, - "weightType": "variable", - "overrides": [], - "stickiness": "default" - } - ] - }, - { - "name": "2afc3854-6157-4abf-a87b-e59510bc96e9", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "OPR_CODE", - "parameters": { "OPR_CODE": "" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "A123", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "CustomId", - "parameters": { "CustomerId": "a,b,c,e" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "aaa", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "foobar", - "parameters": { "bar_p": "100", "ENVIRONMENT": "DEV" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "aaaasdw", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "aasas", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "abc", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - } - ], - "variants": [] - }, - { - "name": "abca", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "" } } - ], - "variants": [] - }, - { - "name": "accounts.reset-password.random.rollout", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "ActivityLog", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "", - "QA": "", - "Test": "", - "Training": "", - "Prod": "" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "type_1", - "weight": 500, - "payload": { "type": "string", "value": "opt_1" }, + "weightType": "fix", + "payload": { "type": "string", "value": "v1" }, "overrides": [], - "weightType": "variable", "stickiness": "default" }, { - "name": "type_2", - "weight": 500, - "payload": { "type": "string", "value": "opt_2" }, - "overrides": [], + "name": "v2", + "weight": 0, "weightType": "variable", + "payload": { "type": "string", "value": "v2" }, + "overrides": [{ "contextName": "userId", "values": ["me"] }], "stickiness": "default" } ] }, { - "name": "ActivityLogTesting", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "gradualRolloutSessionId", - "parameters": { - "percentage": 50, - "groupId": "ActivityLogTesting" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 75, - "stickiness": "default", - "groupId": "ActivityLogTesting" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "a-demo", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "82", - "stickiness": "default", - "groupId": "a-demo" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "adsda", + "name": "users-flag", "type": "release", "enabled": true, "stale": false, "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "random", - "groupId": "adsda" - }, - "constraints": [] - }, - { - "name": "forClient", - "parameters": { "clientName": "Android,IOS" }, - "constraints": [] - } + { "name": "userWithId", "parameters": { "userIds": "111,234" } } ], "variants": [] }, { - "name": "aerronpro", + "name": "json-flag", "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "agasdfasdf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "34", - "stickiness": "default", - "groupId": "agasdfasdf" - }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "foobar", - "parameters": { "ENVIRONMENT": "" }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "userWithId", - "parameters": { "userIds": "" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "alloe", - "type": "release", "enabled": true, "stale": false, "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Aname", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "de" }, - "constraints": [] - }, - { - "name": "userWithId", - "parameters": { "userIds": "de_23456,fr_5667789" }, - "constraints": [] - } - ], "variants": [ { - "name": "v1", + "name": "aaaa", "weight": 1000, - "payload": { "type": "string", "value": "test" }, + "payload": { "type": "json", "value": "{ a: 1 }" }, "overrides": [], "weightType": "variable", "stickiness": "default" @@ -341,4169 +56,21 @@ ] }, { - "name": "A-new.one", + "name": "csv-flag", "type": "experiment", - "enabled": false, + "enabled": true, "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "", - "Test": "", - "Training": "", - "Prod": "" - } - }, - { "name": "default", "parameters": {}, "constraints": [] } - ], + "strategies": [{ "name": "default", "parameters": {} }], "variants": [ { - "name": "foobar", + "name": "aaaa", "weight": 1000, + "payload": { "type": "csv", "value": "a,b,c" }, "overrides": [], "weightType": "variable", "stickiness": "default" } ] - }, - { - "name": "another-toggle", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "anotta-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "aoeu", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "app", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "46", - "stickiness": "default", - "groupId": "app" - } - } - ], - "variants": [] - }, - { - "name": "app.main", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "laci" } } - ], - "variants": [] - }, - { - "name": "app.ToggleX", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Aruba-Switch", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "asasas", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "test1", - "parameters": { "codes": "sas,{sas: \"\"sa sasasas\" }" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "ASASASAS", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "OPR_CODE", - "parameters": { "OPR_CODE": "CUST" }, - "constraints": [] - }, - { - "name": "userWithId", - "parameters": { "userIds": "" }, - "constraints": [] - }, - { - "name": "userWithId", - "parameters": { "userIds": "123,4,5" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "41", - "stickiness": "default", - "groupId": "ASASASAS" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "v1", - "weight": 1000, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "asd", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "asd" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "asd22", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "default", - "groupId": "asd22" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "asd22dsf", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "asd2asd", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": 33, - "stickiness": "default", - "groupId": "asd2asd" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 46, - "stickiness": "default", - "groupId": "asd2asd" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "asdasd", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "asdasd" - } - } - ], - "variants": [] - }, - { - "name": "asdasd2", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "asdf", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "asdfasdfasdf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "asdqddq", - "type": "permission", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "rollout_schemas", - "parameters": { "schemas": "let" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Asdqwe1we", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "aaaa", - "weight": 1000, - "payload": { "type": "json", "value": "{ a: 1 }" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "asmallname", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "version", "parameters": { "version code": "15" } } - ], - "variants": [ - { - "name": "apilevel", - "weight": 1000, - "payload": { "type": "string", "value": "100" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "auto-grading", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "default", - "groupId": "auto-grading" - }, - "constraints": [] - }, - { - "name": "userWithId", - "parameters": { "userIds": "123,1234,4441" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "AwesomeFeature", - "type": "permission", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [] - }, - { - "name": "AwesomeFF", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "47", - "stickiness": "userId", - "groupId": "AwesomeFF" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "bbbbb", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "hhhhh", "parameters": { "8": "58" } }], - "variants": [] - }, - { - "name": "bbbbbbb", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "bccnj", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "bfg_feature", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userParam", "parameters": { "userType": "basic" } } - ], - "variants": [] - }, - { - "name": "bla", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "demo", "parameters": { "demoGroup": "" } } - ], - "variants": [] - }, - { - "name": "bnmbmnb", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": 75, - "stickiness": "default", - "groupId": "" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "bookable_authorized", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "20" } } - ], - "variants": [] - }, - { - "name": "Booyeah-Who-Is-The-King", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "v1", - "weight": 1000, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "by-users", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "111,234" } } - ], - "variants": [] - }, - { - "name": "c79422b2-5c8e-407a-a389-77bdb5b6ad0a", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "cf", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "CID-SFDC", - "type": "operational", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "cid-strategy", - "parameters": { "solution": "VCF,SYN" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "CoffeeShop", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "covid-19", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "create", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "cross_featrure", - "type": "operational", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "dark-theme", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "1,2,3" }, - "constraints": [] - }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "" }, - "constraints": [] - }, - { "name": "remoteAddress", "parameters": { "IPs": "" } } - ], - "variants": [] - }, - { - "name": "dashboard_enabled", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "DateExample", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Demo", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "52", - "stickiness": "default", - "groupId": "Demo" - }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [ - { - "name": "small", - "weight": 500, - "weightType": "variable", - "payload": { "type": "string", "value": "35" }, - "overrides": [], - "stickiness": "default" - }, - { - "name": "medium", - "weight": 500, - "payload": { "type": "string", "value": "55" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "Demo123", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Demo22", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Demo33", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "24", - "stickiness": "default", - "groupId": "Demo33" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "25", - "stickiness": "default", - "groupId": "Demo33" - }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [ - { - "name": "dfsd", - "weight": 500, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - }, - { - "name": "asdasd", - "weight": 500, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "Demo43", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": 56, - "stickiness": "default", - "groupId": "" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 49, - "stickiness": "default", - "groupId": "Demo43" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Demo44", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "de_sro" }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "47", - "stickiness": "default", - "groupId": "Demo44" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "default", - "groupId": "Demo44" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Demo.Slack", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Demossss", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "test1", - "parameters": { "services": "", "solutions": "" } - } - ], - "variants": [] - }, - { - "name": "Devtools", - "type": "permission", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "dfgfdg", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "" }, - "constraints": [] - }, - { - "name": "remoteAddress", - "parameters": { "IPs": "tyhtyh" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "dghjm", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "dinesh-t", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "1234" } } - ], - "variants": [] - }, - { - "name": "Doemaariets", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Domain", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "true", - "Test": "", - "Training": "", - "Prod": "" - } - }, - { - "name": "customerWithId", - "parameters": { "CustomerId": "123,321,123321" } - } - ], - "variants": [] - }, - { - "name": "eee", - "type": "permission", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "TestGroup", - "parameters": { "Group": "kjherkjqwehrkew" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "eeee", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "example-experiment", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "domain.com" } - } - ], - "variants": [] - }, - { - "name": "ExampleFeature", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "nishtha.kakkar.8@gmail.com" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Experiment", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "forClient", - "parameters": { "clientName": "bob,yay" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Experiment-", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Experiment-Test", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "blue", - "weight": 800, - "weightType": "variable", - "payload": { "type": "string", "value": "blueblue" }, - "overrides": [] - }, - { - "name": "red", - "weight": 200, - "weightType": "fix", - "overrides": [] - } - ] - }, - { - "name": "experiment-toggle", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "123456" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "userId", - "groupId": "experiment-toggle" - } - } - ], - "variants": [] - }, - { - "name": "fadfafaf", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Feature12", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [] - }, - { - "name": "feature1.subfeature1", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "var1", - "weight": 1000, - "payload": { "type": "json", "value": "test" }, - "overrides": [{ "contextName": "userId", "values": ["test2"] }], - "weightType": "variable" - } - ] - }, - { - "name": "feature-2", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "", - "Test": "", - "Training": "", - "Prod": "" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "featureFlagYeah", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "FeatureToggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "featureX", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "23", - "stickiness": "default", - "groupId": "featureX" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "fetch-2-feature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "fetch-2-feature-2", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "fetch-toggle", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "fe-test-1", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "FF", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "fff", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "remoteAddress", "parameters": { "IPs": "ffff,fff" } } - ], - "variants": [] - }, - { - "name": "Fffff", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "finish-him", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "15", - "stickiness": "random", - "groupId": "finish-him" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "first", - "type": "kill-switch", - "enabled": false, - "stale": true, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "first" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "Demo", - "weight": 1000, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "fix-invalid-date", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "foo2", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "foobar", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - } - ], - "variants": [] - }, - { - "name": "foobar2", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "foobar-shazam", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "foobar" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "foobar-toogle", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "foobar", "parameters": { "bar_p": "47" } } - ], - "variants": [] - }, - { - "name": "for-min-egen-del", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "" }, - "constraints": [] - }, - { - "name": "demo", - "parameters": { "demoGroup": "test" }, - "constraints": [] - } - ], - "variants": [ - { - "name": "myvariant", - "weight": 1000, - "payload": { "type": "string", "value": "test" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "fvjnfgjh", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": 61, - "stickiness": "userId", - "groupId": "fvjnfgjh" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "gdgfd", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "gerg", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "gjhbjhb", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "GoPower", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "hack", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "hackweek-22-test-feature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [] - }, - { - "name": "hc-testing", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "hello", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "cooks", "parameters": { "Ttt": "" } }], - "variants": [] - }, - { - "name": "Hello", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "hello_there", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "11", - "stickiness": "default", - "groupId": "hello_there" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "blue", - "weight": 500, - "payload": { "type": "string", "value": "blue" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - }, - { - "name": "green", - "weight": 500, - "payload": { "type": "string", "value": "xx" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "hello-world", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "HelloWorld", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - } - ], - "variants": [] - }, - { - "name": "helloworldtoggle", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "hello-world-two-toggle", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "hgfhgf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "", - "QA": "", - "Test": "", - "Training": "", - "Prod": "true" - } - } - ], - "variants": [] - }, - { - "name": "hggfhf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "MyStrategy", - "parameters": { "param1": "jhghf", "param2": "kjlhkgjfhf" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "hjfghgfhf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "", - "QA": "", - "Test": "", - "Training": "", - "Prod": "" - } - } - ], - "variants": [] - }, - { - "name": "hjkhjkhjkhjk", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "default", - "groupId": "hjkhjkhjkhjk" - } - } - ], - "variants": [] - }, - { - "name": "hjklhghj", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "jhkjhkj", - "Test": "hjkhjkhkj", - "Training": "jkhjkhjk", - "Prod": "hjkhkj" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "hock", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Hola", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "HookbasedSytem", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "hot-instead-of-cold-salad", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Httgghghg", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "H" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "Hghg", - "weight": 500, - "weightType": "fix", - "payload": { "type": "string", "value": "Red" }, - "overrides": [], - "stickiness": "default" - }, - { - "name": "Blue", - "weight": 500, - "weightType": "variable", - "payload": { "type": "string", "value": "blue" }, - "overrides": [], - "stickiness": "default" - } - ] - }, - { - "name": "InativarProdutos", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "jeroku.test", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "jjjjjjj", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "MyStrategy", - "parameters": { "param1": "8", "param2": "9" } - } - ], - "variants": [] - }, - { - "name": "jknkjbhkjvjhk", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "jknkjbhkjvjhk" - } - }, - { "name": "cid-strategy", "parameters": { "solution": "kjn" } } - ], - "variants": [] - }, - { - "name": "Jorge", - "type": "permission", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "remoteAddress", - "parameters": { "IPs": "tttt" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "josh-unique-flag", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "killswitch", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "1234" } - }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "12345" } - } - ], - "variants": [] - }, - { - "name": "KillWiFi", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "13", - "stickiness": "random", - "groupId": "KillWiFi" - } - } - ], - "variants": [] - }, - { - "name": "KS", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "lala", - "type": "experiment", - "enabled": true, - "stale": true, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "liroy", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "variant1", - "weight": 550, - "weightType": "fix", - "payload": { "type": "string", "value": "value1" }, - "overrides": [], - "stickiness": "default" - }, - { - "name": "variant2", - "weight": 150, - "payload": { "type": "string", "value": "value2" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - }, - { - "name": "variant3", - "weight": 300, - "weightType": "fix", - "payload": { "type": "string", "value": "value3" }, - "overrides": [], - "stickiness": "default" - } - ] - }, - { - "name": "lkmasd", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "lkmasd" - } - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "lkmasd" - } - } - ], - "variants": [] - }, - { - "name": "LLCampaign", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "location", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "LocationTags", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "awsomeUserStategy", - "parameters": { "userId": "23" }, - "constraints": [] - } - ], - "variants": [ - { - "name": "color", - "weight": 500, - "weightType": "variable", - "payload": { "type": "string", "value": "red" }, - "overrides": [ - { "contextName": "appName", "values": ["appppp"] } - ], - "stickiness": "default" - }, - { - "name": "color2", - "weight": 500, - "payload": { "type": "string", "value": "123" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "LodeKaSarKaarHai", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "lof", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "ly_test", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "mahshad-pyladies", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "123456" } } - ], - "variants": [] - }, - { - "name": "mahshad-second-test", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "0", - "stickiness": "default", - "groupId": "mahshad-second-test" - }, - "constraints": [] - }, - { - "name": "userWithId", - "parameters": { "userIds": "1,2,3,sss,sssss" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "mahshad-unleash-test", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "mahshad-unleash-test" - } - }, - { "name": "userWithId", "parameters": { "userIds": "" } } - ], - "variants": [] - }, - { - "name": "max-feature", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "mcast", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "roleBasedStrategy", - "parameters": { "roleId": "admin" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 14, - "stickiness": "random", - "groupId": "mcast" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "mklasdm", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": 30, - "stickiness": "default", - "groupId": "mklasdm" - }, - "constraints": [] - }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "mehome" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 30, - "stickiness": "default", - "groupId": "mklasdm" - }, - "constraints": [] - }, - { - "name": "gradualRolloutRandom", - "parameters": { "percentage": 46 }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "mmmm", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "userWithId", - "parameters": { "userIds": "1" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "mobile", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "mockTimeRecordingData", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "RuntimeEnvironmentBased", - "parameters": { "runtimeEnvironmentId": "TEST" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "my-application.hack", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-debug-feature", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "My_feature", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "home", - "weight": 500, - "weightType": "variable", - "overrides": [], - "stickiness": "default" - }, - { - "name": "type", - "weight": 500, - "payload": { "type": "string", "value": "abc" }, - "overrides": [ - { "contextName": "userId", "values": ["1"] }, - { "contextName": "userId", "values": ["2"] } - ], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "myFeatureToggle", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "MyGreatAlgorithm", - "type": "permission", - "enabled": false, - "stale": true, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "MyGreatAlgorithm" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "56", - "stickiness": "default", - "groupId": "MyGreatAlgorithm" - }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [] - }, - { - "name": "my-hero-comp", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "shan.dev.com" } - } - ], - "variants": [] - }, - { - "name": "mykey", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "default", - "groupId": "mykey" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "my-new-feature", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-new-feature-1", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-new-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "mynew-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "mytest", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-test-boyz", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "9", - "stickiness": "sessionId", - "groupId": "my-test-boyz" - }, - "constraints": [] - }, - { - "name": "Specialgroup", - "parameters": { "mail": "ciao" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "my-test-feature", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "123" } } - ], - "variants": [] - }, - { - "name": "my-testing-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-test-toggle", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-test-toggle2", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-test-toggle-2", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "My-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": 48, - "stickiness": "sessionId", - "groupId": "My-toggle" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "my-unique-test-name", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "random", - "groupId": "my-unique-test-name" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "my-unique-toggel", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "my-very-unique-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 84, - "stickiness": "default", - "groupId": "my-very-unique-toggle" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "asdfasdf", - "weight": 500, - "weightType": "variable", - "overrides": [], - "stickiness": "default" - }, - { - "name": "agasdgaegagse", - "weight": 500, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "name-tester", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "ok" } } - ], - "variants": [ - { - "name": "T1", - "weight": 500, - "weightType": "variable", - "payload": { "type": "string", "value": "T1" }, - "overrides": [], - "stickiness": "default" - }, - { - "name": "CC", - "weight": 500, - "weightType": "variable", - "payload": { "type": "string", "value": "C" }, - "overrides": [], - "stickiness": "default" - } - ] - }, - { - "name": "NarendraPatil", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { "name": "remoteAddress", "parameters": { "IPs": "" } }, - { "name": "default", "parameters": {} }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - }, - { "name": "demo", "parameters": { "demoGroup": "" } }, - { "name": "userWithId", "parameters": { "userIds": "" } } - ], - "variants": [] - }, - { - "name": "NarendraTesting", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "nar,rus" } } - ], - "variants": [] - }, - { - "name": "needs-improvement", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "new-api", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [ - { - "name": "v1", - "weight": 1000, - "weightType": "fix", - "payload": { "type": "string", "value": "v1" }, - "overrides": [], - "stickiness": "default" - }, - { - "name": "v2", - "weight": 0, - "weightType": "variable", - "payload": { "type": "string", "value": "v2" }, - "overrides": [{ "contextName": "userId", "values": ["me"] }], - "stickiness": "default" - } - ] - }, - { - "name": "new-feature-815", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "newFeatureCustomerId", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "customerWithId", - "parameters": { "CustomerId": "104057243,104067509" }, - "constraints": [] - }, - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "true", - "Test": "false", - "Training": "true", - "Prod": "" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "newFeatureDiscount", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "HugeDiscount", - "weight": 500, - "weightType": "variable", - "payload": { "type": "string", "value": "50" }, - "overrides": [ - { "contextName": "userId", "values": ["kvajars"] } - ], - "stickiness": "default" - }, - { - "name": "SmallDiscount", - "weight": 500, - "weightType": "variable", - "payload": { "type": "string", "value": "5" }, - "overrides": [ - { "contextName": "userId", "values": ["aksoles"] } - ], - "stickiness": "default" - } - ] - }, - { - "name": "newFeatureToggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "server1" }, - "constraints": [] - }, - { - "name": "remoteAddress", - "parameters": { "IPs": "172.10.0.0" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "newFeatureUserId", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "userId", - "groupId": "newFeatureUserId" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "NewFet", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "newKillSwitch", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "PayloadVariant", - "weight": 1000, - "weightType": "variable", - "payload": { - "type": "string", - "value": "TFF er for tiden utilgjengelig. Det jobbes med saken og vi forventer at det vil være løst innen 15:00." - }, - "overrides": [], - "stickiness": "default" - } - ] - }, - { - "name": "OW", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "permissiontoggle", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "plannified-down-time", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "poc-forms-hub", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [] - }, - { - "name": "qweqweawe", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "", - "Test": "", - "Training": "true", - "Prod": "" - }, - "constraints": [] - }, - { - "name": "generaltest", - "parameters": { - "rollout": 49, - "userIds": "TH,ERT", - "On/off": "true" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "RC", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Reallycoolfeature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "cid-strategy", "parameters": { "solution": "" } } - ], - "variants": [] - }, - { - "name": "RecruiterPortalFeedback", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "RecruiterPortalFeedbackswsss", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "regra", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - } - ], - "variants": [] - }, - { - "name": "relaseWhenReady", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "ResponseFeature", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "ResponseFeature2", - "weight": 1000, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "RiccardoTest", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "RiccardoTest" - } - } - ], - "variants": [] - }, - { - "name": "robert", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "", - "QA": "", - "Test": "", - "Training": "", - "Prod": "" - } - } - ], - "variants": [] - }, - { - "name": "Roman", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [] - }, - { - "name": "rs-fs-1929", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "10", - "stickiness": "userId", - "groupId": "rs-fs-1929" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "S3Conversion", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "CustomId", "parameters": { "CustomerId": "123" } }, - { - "name": "CustomId", - "parameters": { "CustomerId": "321" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "sad", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "sadasd", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "sadasd" - }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "sadasd" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "sadasdsa", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "dasda" } - } - ], - "variants": [] - }, - { - "name": "SCID", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "foobar", - "parameters": { "ENVIRONMENT": "DEV" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "ScreeningQ", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "sdasd", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - }, - { "name": "default", "parameters": {} } - ], - "variants": [] - }, - { - "name": "sdasdasd", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "default", - "groupId": "sdasdasd" - } - } - ], - "variants": [] - }, - { - "name": "sdfgsdfg", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "sdfsdf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "sdfsdfdfdfd", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "gradualRolloutRandom", - "parameters": { "percentage": 41 }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "sdfsdfsdf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "sadasd,sad" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "63", - "stickiness": "default", - "groupId": "sdfsdfsdf" - }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "68", - "stickiness": "default", - "groupId": "sdfsdfsdf" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "sdsf", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "sdfbbbbb", - "weight": 1000, - "weightType": "variable", - "payload": { "type": "string", "value": "ffff" }, - "overrides": [ - { "contextName": "userId", "values": ["gggg"] }, - { "contextName": "userId", "values": ["v1"] } - ] - } - ] - }, - { - "name": "secure-installation", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "1,2" } } - ], - "variants": [] - }, - { - "name": "Shop", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "shutdown", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Some-new-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "41", - "stickiness": "default", - "groupId": "Some-new-toggle" - } - } - ], - "variants": [] - }, - { - "name": "Something", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Somethingcool", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "spring-variants", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "75", - "stickiness": "default", - "groupId": "spring-variants" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "test", - "weight": 800, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - }, - { - "name": "this-is-fun", - "weight": 200, - "weightType": "fix", - "overrides": [], - "stickiness": "default" - } - ] - }, - { - "name": "sss", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "SuperAwesomeFeature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "superfeature", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "super-truper", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "Implicit", "parameters": { "macro": "6" } } - ], - "variants": [] - }, - { - "name": "sweetness", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "75", - "stickiness": "default", - "groupId": "sweetness" - } - } - ], - "variants": [] - }, - { - "name": "SwitchFeature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "SwitchOnline", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Taco", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "123,1" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Test", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "TEST", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-01", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "foruser", - "weight": 1000, - "overrides": [{ "contextName": "userId", "values": ["1"] }], - "weightType": "variable" - } - ] - }, - { - "name": "test1", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "test1", - "parameters": { "codes": "" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test-11111-kill-feature-ever-released", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "70", - "stickiness": "default", - "groupId": "test-11111-kill-feature-ever-released" - } - } - ], - "variants": [] - }, - { - "name": "test123", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-123", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "RuntimeEnvironmentBased", - "parameters": { "runtimeEnvironmentId": "" } - }, - { "name": "test1", "parameters": { "codes": "" } } - ], - "variants": [] - }, - { - "name": "Test-123", - "type": "release", - "enabled": true, - "stale": true, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "jjjjjjj" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test1231231", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test1234214", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Test234", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "true", - "Test": "", - "Training": "", - "Prod": "false" - } - } - ], - "variants": [] - }, - { - "name": "test2345", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "32", - "stickiness": "default", - "groupId": "test2345" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test256", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "sdzxfsdf" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": 38, - "stickiness": "default", - "groupId": "test256" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test.3.12.0", - "type": "experiment", - "enabled": true, - "stale": true, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "47", - "stickiness": "default", - "groupId": "test.3.12.0" - }, - "constraints": [] - }, - { - "name": "test-env", - "parameters": { "prod": "true", "dev": "true" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Test33", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "50", - "stickiness": "userId", - "groupId": "Test33" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test777", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test8878687", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "test", "parameters": { "users": "tttt,hhjgj" } } - ], - "variants": [] - }, - { - "name": "test-cid", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "cid-strategy", - "parameters": { "solution": "VCF,SYN,VCF_4.10" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test.create", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Test.create", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "demo", - "parameters": { "demoGroup": "" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test.create.adddon", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "teste", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "teste1", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "test1", "parameters": {}, "constraints": [] } - ], - "variants": [] - }, - { - "name": "teste123123", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "asd,teste,tesasd" } - } - ], - "variants": [] - }, - { - "name": "Tester", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-feature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { "name": "userWithId", "parameters": { "userIds": "" } } - ], - "variants": [] - }, - { - "name": "testFeature", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-feature-1", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "selected-schools", - "parameters": { "schoolId": "bla" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test-feature-99999", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Test-FF", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "demo", - "parameters": { "demoGroup": "abc" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test-for-me", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "TestGroup", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "TestGroup", "parameters": { "Group": "Foo" } }, - { "name": "TestGroup", "parameters": { "Group": "Bar" } } - ], - "variants": [] - }, - { - "name": "testgtasdas", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - } - ], - "variants": [] - }, - { - "name": "Testing", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "" } - } - ], - "variants": [] - }, - { - "name": "testing123", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "testing-toggle-12", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "testlog", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-new-feature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "userWithId", "parameters": { "userIds": "5" } } - ], - "variants": [] - }, - { - "name": "Test_Permission1", - "type": "permission", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "test1", - "parameters": { "codes": "code1,code2" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "test.ping", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "testproduct", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-raf", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-release-flag", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "endpoint", - "weight": 1000, - "payload": { "type": "string", "value": "this_is_the_endpoint" }, - "overrides": [], - "weightType": "variable" - } - ] - }, - { - "name": "TestSwitch1", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-t", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "testtertogle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "testtest", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { "name": "dfazza", "parameters": { "asdfasdf": "afasd" } } - ], - "variants": [] - }, - { - "name": "test-test", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "test,test1,test2" }, - "constraints": [] - }, - { "name": "default", "parameters": {}, "constraints": [] } - ], - "variants": [ - { - "name": "t1", - "weight": 750, - "weightType": "fix", - "payload": { "type": "string", "value": "t1" }, - "overrides": [{ "contextName": "userId", "values": ["abcdef"] }], - "stickiness": "default" - }, - { - "name": "t2", - "weight": 250, - "payload": { "type": "string", "value": "t2" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "test-test-and-some-more-test", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "", - "QA": "", - "Test": "", - "Training": "", - "Prod": "true" - } - } - ], - "variants": [] - }, - { - "name": "TestTesting", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "default", "parameters": {} }, - { - "name": "asd", - "parameters": { "asd": "asd", "sdf": "" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "testtoggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test-toggle", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "testtt", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "test_variant", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "version", - "weight": 1000, - "payload": { "type": "string", "value": "v1" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "test-w", - "type": "operational", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Testymctestface", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "true", - "Test": "", - "Training": "", - "Prod": "true" - } - } - ], - "variants": [] - }, - { - "name": "tgtg", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "the-awesome-feature", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Theone", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "ticket", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "demo", "parameters": { "demoGroup": "dsfasfd" } } - ], - "variants": [ - { - "name": "bool", - "weight": 1000, - "weightType": "variable", - "payload": { "type": "string", "value": "valuess" }, - "overrides": [], - "stickiness": "default" - } - ] - }, - { - "name": "TotesMcGoats", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "trololo", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "tryion.dev.auth", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "Ttt", - "type": "operational", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "uname", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "unique", - "type": "kill-switch", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "UniqueCreateContractFeature", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "unleaseh-my-mama", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "demo", "parameters": { "demoGroup": "you" } } - ], - "variants": [] - }, - { - "name": "unleash_aws_s3", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "gradualRolloutRandom", - "parameters": { "percentage": "60" } - } - ], - "variants": [ - { - "name": "s3", - "weight": 333, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - }, - { - "name": "gcp", - "weight": 333, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - }, - { - "name": "azure", - "weight": 333, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "unleash-demo.avansert", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "testbruker" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "47", - "stickiness": "default", - "groupId": "unleash-demo.avansert" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "unleash-demo.hjemmelagd", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "byEnhet", - "parameters": { "valgtEnhet": "456" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "unleash-demo.scheduler", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "unleash-demo.scheduler.default-pa", - "type": "release", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "user.pornFilter", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "UserID", - "parameters": { "AD_GRP_1, AD_GRP_2": "asdf,asdf2,asdf3,asdf4" } - } - ], - "variants": [] - }, - { - "name": "vartest", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [ - { - "name": "v1", - "weight": 333, - "payload": { "type": "string", "value": "v1" }, - "overrides": [], - "weightType": "variable" - }, - { - "name": "v2", - "weight": 333, - "payload": { "type": "string", "value": "v2" }, - "overrides": [], - "weightType": "variable" - }, - { - "name": "v3", - "weight": 333, - "payload": { "type": "string", "value": "v3" }, - "overrides": [], - "weightType": "variable" - } - ] - }, - { - "name": "vcvcvc-test-feature", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { "name": "foobar", "parameters": { "ENVIRONMENT": "DEV" } }, - { - "name": "applicationHostname", - "parameters": { "hostNames": "test" }, - "constraints": [] - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "49", - "stickiness": "default", - "groupId": "vcvcvc-test-feature" - }, - "constraints": [] - }, - { - "name": "remoteAddress", - "parameters": { "IPs": "1.1.1.1" }, - "constraints": [] - }, - { - "name": "Environment", - "parameters": { - "Dev": "true", - "QA": "", - "Test": "", - "Training": "", - "Prod": "" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "Very.cool.toggle", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "49", - "stickiness": "default", - "groupId": "Very.cool.toggle" - } - }, - { - "name": "flexibleRollout", - "parameters": { - "rollout": "25", - "stickiness": "default", - "groupId": "Very.cool.toggle" - }, - "constraints": [] - } - ], - "variants": [ - { - "name": "xc", - "weight": 500, - "payload": { "type": "string", "value": "sdf" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - }, - { - "name": "dsfsdf", - "weight": 500, - "payload": { "type": "string", "value": "sdf" }, - "overrides": [], - "weightType": "variable", - "stickiness": "default" - } - ] - }, - { - "name": "vlad", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "wasd", - "type": "experiment", - "enabled": false, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "51", - "stickiness": "default", - "groupId": "wasd" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "whitelabel", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "whitelist", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "userWithId", - "parameters": { "userIds": "juan@gmail.com" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "wwwwwwww", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "39", - "stickiness": "default", - "groupId": "wwwwwwww" - }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "xcvh", - "type": "kill-switch", - "enabled": false, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "xxx", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "flexibleRollout", - "parameters": { - "rollout": "100", - "stickiness": "userId", - "groupId": "xxx" - } - } - ], - "variants": [] - }, - { - "name": "yest", - "type": "experiment", - "enabled": true, - "stale": false, - "strategies": [{ "name": "default", "parameters": {} }], - "variants": [] - }, - { - "name": "zxc", - "type": "operational", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "trunk,testing" }, - "constraints": [] - } - ], - "variants": [] - }, - { - "name": "zXVa", - "type": "release", - "enabled": true, - "stale": false, - "strategies": [ - { - "name": "applicationHostname", - "parameters": { "hostNames": "dsagfa,asdfadf" }, - "constraints": [] - } - ], - "variants": [] } ] } From 8f93bbb12917848e097bdd401005d96ada0eec38 Mon Sep 17 00:00:00 2001 From: liran2000 Date: Wed, 13 Sep 2023 13:40:27 +0300 Subject: [PATCH 10/17] enhancements - int,double evaluation - flag metadata Signed-off-by: liran2000 --- providers/unleash/README.md | 8 ++- .../providers/unleash/UnleashProvider.java | 47 +++++++++++++++-- .../unleash/UnleashProviderTest.java | 51 +++++++++++++++---- .../unleash/src/test/resources/features.json | 34 +++++++++++++ 4 files changed, 124 insertions(+), 16 deletions(-) diff --git a/providers/unleash/README.md b/providers/unleash/README.md index 1265af001..d6eebb2ce 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -40,10 +40,14 @@ String variantValue = client.getStringValue(FLAG_NAME, ""); See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information. -## Caveats / Limitations +### Additional Usage Details +* When default value is used and returned, default variant is not used and variant name is not set. * json/csv payloads are evaluated via object evaluation as what returned from Unleash - string, wrapped with Value. -* integer/double evaluation currently not supported. +* Additional evaluation data can be received via flag metadata, such as: + * *enabled* - boolean + * *variant-stickiness* - string + * *payload-type* - string, optional ## Unleash Provider Tests Strategies diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 8fc3de184..4f64ddb63 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -2,6 +2,7 @@ import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; +import dev.openfeature.sdk.ImmutableMetadata; import dev.openfeature.sdk.Metadata; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.ProviderEventDetails; @@ -9,7 +10,6 @@ import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; -import dev.openfeature.sdk.exceptions.TypeMismatchError; import io.getunleash.DefaultUnleash; import io.getunleash.Unleash; import io.getunleash.UnleashContext; @@ -128,12 +128,46 @@ public ProviderEvaluation getStringEvaluation(String key, String default @Override public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { - throw new TypeMismatchError(NOT_IMPLEMENTED); + ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); + Integer value = getIntegerValue(valueProviderEvaluation, defaultValue); + return ProviderEvaluation.builder() + .value(value) + .variant(valueProviderEvaluation.getVariant()) + .errorCode(valueProviderEvaluation.getErrorCode()) + .reason(valueProviderEvaluation.getReason()) + .flagMetadata(valueProviderEvaluation.getFlagMetadata()) + .build(); + } + + private static Integer getIntegerValue(ProviderEvaluation valueProviderEvaluation, Integer defaultValue) { + String valueStr = valueProviderEvaluation.getValue().asObject().toString(); + try { + return Integer.parseInt(valueStr); + } catch (NumberFormatException ex) { + return defaultValue; + } } @Override public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { - throw new TypeMismatchError(NOT_IMPLEMENTED); + ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); + Double value = getDoubleValue(valueProviderEvaluation, defaultValue); + return ProviderEvaluation.builder() + .value(value) + .variant(valueProviderEvaluation.getVariant()) + .errorCode(valueProviderEvaluation.getErrorCode()) + .reason(valueProviderEvaluation.getReason()) + .flagMetadata(valueProviderEvaluation.getFlagMetadata()) + .build(); + } + + private static Double getDoubleValue(ProviderEvaluation valueProviderEvaluation, Double defaultValue) { + String valueStr = valueProviderEvaluation.getValue().asObject().toString(); + try { + return Double.parseDouble(valueStr); + } catch (NumberFormatException ex) { + return defaultValue; + } } @Override @@ -155,9 +189,16 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa variantName = evaluatedVariant.getName(); value = evaluatedVariant.getPayload().map(p -> new Value(p.getValue())).orElse(null); } + ImmutableMetadata.ImmutableMetadataBuilder flagMetadataBuilder = ImmutableMetadata.builder() + .addString("variant-stickiness", evaluatedVariant.getStickiness()); + flagMetadataBuilder.addBoolean("enabled", evaluatedVariant.isEnabled()); + if (evaluatedVariant.getPayload().isPresent()) { + flagMetadataBuilder.addString("payload-type", evaluatedVariant.getPayload().get().getType()); + } return ProviderEvaluation.builder() .value(value) .variant(variantName) + .flagMetadata(flagMetadataBuilder.build()) .build(); } diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index d3cfe97d3..1e0cf6c46 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -5,13 +5,14 @@ import dev.openfeature.sdk.Client; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.ImmutableMetadata; import dev.openfeature.sdk.OpenFeatureAPI; +import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.ProviderState; import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; -import dev.openfeature.sdk.exceptions.TypeMismatchError; import io.getunleash.UnleashContext; import io.getunleash.UnleashException; import io.getunleash.event.ToggleEvaluated; @@ -55,6 +56,10 @@ class UnleashProviderTest { public static final String FLAG_NAME = "variant-flag"; public static final String VARIANT_FLAG_NAME = "variant-flag"; public static final String VARIANT_FLAG_VALUE = "v1"; + public static final String INT_FLAG_NAME = "int-flag"; + public static final Integer INT_FLAG_VALUE = 123; + public static final String DOUBLE_FLAG_NAME = "double-flag"; + public static final Double DOUBLE_FLAG_VALUE = 1.23; public static final String USERS_FLAG_NAME = "users-flag"; public static final String JSON_VARIANT_FLAG_NAME = "json-flag"; public static final String JSON_VARIANT_FLAG_VALUE = "{ a: 1 }"; @@ -96,8 +101,7 @@ private void mockUnleashAPI(String backupFileContent) { @SneakyThrows private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation, String unleashAPI, String backupFileContent, TestSubscriber testSubscriber) { UnleashConfig.Builder unleashConfigBuilder = - UnleashConfig.builder() - .unleashAPI(new URI(unleashAPI)) + UnleashConfig.builder().unleashAPI(new URI(unleashAPI)) .appName("fakeApp") .subscriber(testSubscriber) .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); @@ -128,10 +132,36 @@ void getStringVariantEvaluation() { new ImmutableContext()).getValue()); assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(VARIANT_FLAG_NAME, "")); assertEquals("fallback_str", unleashProvider.getStringEvaluation("non-existing", - "fallback_str", new ImmutableContext()).getValue()); + "fallback_str", new ImmutableContext()).getValue()); assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); } + @Test + void getIntegerEvaluation() { + UnleashContext unleashContext = UnleashContext.builder().userId("int").build(); + EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); + assertEquals(INT_FLAG_VALUE, unleashProvider.getIntegerEvaluation(INT_FLAG_NAME, 1, + evaluationContext).getValue()); + assertEquals(INT_FLAG_VALUE, client.getIntegerValue(INT_FLAG_NAME, 1)); + assertEquals(1, client.getIntegerValue("non-existing", 1)); + + // non-number flag value + assertEquals(1, client.getIntegerValue(VARIANT_FLAG_NAME, 1)); + } + + @Test + void getDoubleEvaluation() { + UnleashContext unleashContext = UnleashContext.builder().userId("double").build(); + EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); + assertEquals(DOUBLE_FLAG_VALUE, unleashProvider.getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, + evaluationContext).getValue()); + assertEquals(DOUBLE_FLAG_VALUE, client.getDoubleValue(DOUBLE_FLAG_NAME, 1.1)); + assertEquals(1.1, client.getDoubleValue("non-existing", 1.1)); + + // non-number flag value + assertEquals(1.1, client.getDoubleValue(VARIANT_FLAG_NAME, 1.1)); + } + @Test void getJsonVariantEvaluation() { assertEquals(JSON_VARIANT_FLAG_VALUE, unleashProvider.getObjectEvaluation(JSON_VARIANT_FLAG_NAME, new Value(""), @@ -165,13 +195,12 @@ void getBooleanEvaluationByUser() { } @Test - void typeMismatch() { - assertThrows(TypeMismatchError.class, () -> { - unleashProvider.getIntegerEvaluation("test", 1, new ImmutableContext()); - }); - assertThrows(TypeMismatchError.class, () -> { - unleashProvider.getDoubleEvaluation("test", 1.0, new ImmutableContext()); - }); + void getEvaluationMetadataTest() { + ProviderEvaluation stringEvaluation = unleashProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", + new ImmutableContext()); + ImmutableMetadata flagMetadata = stringEvaluation.getFlagMetadata(); + assertEquals("default", flagMetadata.getString("variant-stickiness")); + assertEquals("string", flagMetadata.getString("payload-type")); } @SneakyThrows diff --git a/providers/unleash/src/test/resources/features.json b/providers/unleash/src/test/resources/features.json index 241b0410c..5b78c82b3 100644 --- a/providers/unleash/src/test/resources/features.json +++ b/providers/unleash/src/test/resources/features.json @@ -71,6 +71,40 @@ "stickiness": "default" } ] + }, + { + "name": "int-flag", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "aaaa", + "weight": 1000, + "payload": { "type": "number", "value": "123" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] + }, + { + "name": "double-flag", + "type": "experiment", + "enabled": true, + "stale": false, + "strategies": [{ "name": "default", "parameters": {} }], + "variants": [ + { + "name": "aaaa", + "weight": 1000, + "payload": { "type": "number", "value": "1.23" }, + "overrides": [], + "weightType": "variable", + "stickiness": "default" + } + ] } ] } From 1ddfd61a1c91331ad33c3d1199a2eb1e92fcdc11 Mon Sep 17 00:00:00 2001 From: liran2000 Date: Wed, 13 Sep 2023 13:47:30 +0300 Subject: [PATCH 11/17] enhance test Signed-off-by: liran2000 --- .../contrib/providers/unleash/UnleashProviderTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index 1e0cf6c46..f5f09eed1 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -201,6 +201,10 @@ void getEvaluationMetadataTest() { ImmutableMetadata flagMetadata = stringEvaluation.getFlagMetadata(); assertEquals("default", flagMetadata.getString("variant-stickiness")); assertEquals("string", flagMetadata.getString("payload-type")); + assertEquals(true, flagMetadata.getBoolean("enabled")); + ProviderEvaluation nonExistingFlagEvaluation = unleashProvider.getStringEvaluation("non-existing", + "", new ImmutableContext()); + assertEquals(false, nonExistingFlagEvaluation.getFlagMetadata().getBoolean("enabled")); } @SneakyThrows From 9b2b7b8434348a0e28d31c8b7420bc1bd23ac65c Mon Sep 17 00:00:00 2001 From: liran2000 Date: Wed, 20 Sep 2023 19:58:23 +0300 Subject: [PATCH 12/17] Remove dependencies on Unleash and Unleash provider from client usage Signed-off-by: liran2000 --- providers/unleash/README.md | 11 ++++-- .../providers/unleash/ContextTransformer.java | 25 ------------- .../unleash/UnleashProviderTest.java | 35 +++++++++---------- 3 files changed, 25 insertions(+), 46 deletions(-) diff --git a/providers/unleash/README.md b/providers/unleash/README.md index d6eebb2ce..38130f68f 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -31,8 +31,15 @@ FeatureProvider featureProvider = new UnleashProvider(unleashOptions); OpenFeatureAPI.getInstance().setProviderAndWait(unleashProvider); boolean featureEnabled = client.getBooleanValue(FLAG_NAME, false); -UnleashContext unleashContext = UnleashContext.builder().userId("1").build(); -EvaluationContext evaluationContext = UnleashProvider.transform(unleashContext); +// Context parameters are optional, not mandatory to fill all parameters +MutableContext evaluationContext = new MutableContext(); +evaluationContext.add("userId", userIdValue); +evaluationContext.add("currentTime", String.valueOf(currentTimeValue)); +evaluationContext.add("sessionId", sessionIdValue); +evaluationContext.add("remoteAddress", remoteAddressValue); +evaluationContext.add("environment", environmentValue); +evaluationContext.add("appName", appNameValue); +evaluationContext.add(customPropertyKey, customPropertyValue); featureEnabled = client.getBooleanValue(FLAG_NAME, false, evaluationContext); String variantValue = client.getStringValue(FLAG_NAME, ""); diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java index d1791959c..2e8f68806 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java @@ -1,13 +1,9 @@ package dev.openfeature.contrib.providers.unleash; import dev.openfeature.sdk.EvaluationContext; -import dev.openfeature.sdk.ImmutableContext; -import dev.openfeature.sdk.Value; import io.getunleash.UnleashContext; import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.Map; /** * Transformer from Unleash context to OpenFeature context and vice versa. @@ -51,25 +47,4 @@ protected static UnleashContext transform(EvaluationContext ctx) { return unleashContextBuilder.build(); } - /** - * Transform UnleashContext to EvaluationContext. - * @param unleashContext the UnleashContext - * @return transformed EvaluationContext - */ - public static EvaluationContext transform(UnleashContext unleashContext) { - Map attributes = new HashMap<>(); - unleashContext.getAppName().ifPresent(o -> attributes.put(CONTEXT_APP_NAME, Value.objectToValue(o))); - unleashContext.getUserId().ifPresent(o -> attributes.put(CONTEXT_USER_ID, Value.objectToValue(o))); - unleashContext.getEnvironment().ifPresent(o -> attributes.put(CONTEXT_ENVIRONMENT, Value.objectToValue(o))); - unleashContext.getSessionId().ifPresent(o -> attributes.put(CONTEXT_SESSION_ID, Value.objectToValue(o))); - unleashContext.getRemoteAddress().ifPresent(o -> attributes.put( - CONTEXT_REMOTE_ADDRESS, Value.objectToValue(o))); - unleashContext.getCurrentTime().ifPresent( - o -> attributes.put(CONTEXT_CURRENT_TIME, Value.objectToValue(o.toString()))); - - unleashContext.getProperties().forEach((k, v) -> { - attributes.put(k, Value.objectToValue(v)); - }); - return new ImmutableContext(attributes); - } } diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index f5f09eed1..a4bffe9dd 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -3,9 +3,9 @@ import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import dev.openfeature.sdk.Client; -import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.ImmutableMetadata; +import dev.openfeature.sdk.MutableContext; import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.ProviderEventDetails; @@ -138,8 +138,8 @@ void getStringVariantEvaluation() { @Test void getIntegerEvaluation() { - UnleashContext unleashContext = UnleashContext.builder().userId("int").build(); - EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); + MutableContext evaluationContext = new MutableContext(); + evaluationContext.add("userId", "int"); assertEquals(INT_FLAG_VALUE, unleashProvider.getIntegerEvaluation(INT_FLAG_NAME, 1, evaluationContext).getValue()); assertEquals(INT_FLAG_VALUE, client.getIntegerValue(INT_FLAG_NAME, 1)); @@ -151,8 +151,8 @@ void getIntegerEvaluation() { @Test void getDoubleEvaluation() { - UnleashContext unleashContext = UnleashContext.builder().userId("double").build(); - EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); + MutableContext evaluationContext = new MutableContext(); + evaluationContext.add("userId", "double"); assertEquals(DOUBLE_FLAG_VALUE, unleashProvider.getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, evaluationContext).getValue()); assertEquals(DOUBLE_FLAG_VALUE, client.getDoubleValue(DOUBLE_FLAG_NAME, 1.1)); @@ -184,12 +184,11 @@ void getCSVVariantEvaluation() { @Test void getBooleanEvaluationByUser() { - UnleashContext unleashContext = UnleashContext.builder().userId("111").build(); - EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); + MutableContext evaluationContext = new MutableContext(); + evaluationContext.add("userId", "111"); assertEquals(true, unleashProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); assertEquals(true, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); - unleashContext = UnleashContext.builder().userId("2").build(); - evaluationContext = ContextTransformer.transform(unleashContext); + evaluationContext.add("userId", "2"); assertEquals(false, unleashProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); assertEquals(false, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); } @@ -250,16 +249,14 @@ void contextTransformTest() { String customPropertyValue = "customProperty_value"; String customPropertyKey = "customProperty"; - UnleashContext unleashContext = UnleashContext.builder() - .userId(userIdValue) - .currentTime(currentTimeValue) - .sessionId(sessionIdValue) - .remoteAddress(remoteAddressValue) - .environment(environmentValue) - .appName(appNameValue) - .addProperty(customPropertyKey, customPropertyValue) - .build(); - EvaluationContext evaluationContext = ContextTransformer.transform(unleashContext); + MutableContext evaluationContext = new MutableContext(); + evaluationContext.add("userId", userIdValue); + evaluationContext.add("currentTime", String.valueOf(currentTimeValue)); + evaluationContext.add("sessionId", sessionIdValue); + evaluationContext.add("remoteAddress", remoteAddressValue); + evaluationContext.add("environment", environmentValue); + evaluationContext.add("appName", appNameValue); + evaluationContext.add(customPropertyKey, customPropertyValue); UnleashContext transformedUnleashContext = ContextTransformer.transform(evaluationContext); assertEquals(appNameValue, transformedUnleashContext.getAppName().get()); From 2d7839be03bf7147ccb83d98cb2a959dcd7746d2 Mon Sep 17 00:00:00 2001 From: liran2000 Date: Tue, 26 Sep 2023 08:29:04 +0300 Subject: [PATCH 13/17] Update readme Signed-off-by: liran2000 --- providers/unleash/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/providers/unleash/README.md b/providers/unleash/README.md index 38130f68f..dac64f04d 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -1,6 +1,6 @@ # Unofficial Unleash OpenFeature Provider for Java -Unleash OpenFeature Provider can provide usage for Unleash via OpenFeature Java SDK. +[Unleash](https://getunleash.io) OpenFeature Provider can provide usage for Unleash via OpenFeature Java SDK. ## Installation @@ -60,6 +60,3 @@ See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers Unit test based on Unleash instance with Unleash features schema file, with WireMock for API mocking. See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information. - -## References -* [Unleash](https://getunleash.io) From 769be63492408cceb1ebf3f067a13810ed2573fd Mon Sep 17 00:00:00 2001 From: liran2000 Date: Tue, 26 Sep 2023 09:17:56 +0300 Subject: [PATCH 14/17] Rename config Signed-off-by: liran2000 --- providers/unleash/README.md | 2 +- .../contrib/providers/unleash/UnleashProvider.java | 14 +++++++------- ...eashOptions.java => UnleashProviderConfig.java} | 2 +- .../providers/unleash/UnleashProviderTest.java | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) rename providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/{UnleashOptions.java => UnleashProviderConfig.java} (87%) diff --git a/providers/unleash/README.md b/providers/unleash/README.md index dac64f04d..39658e7bf 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -27,7 +27,7 @@ Unleash OpenFeature Provider is using Unleash Java SDK. ### Usage Example ``` -FeatureProvider featureProvider = new UnleashProvider(unleashOptions); +FeatureProvider featureProvider = new UnleashProvider(unleashProviderConfig); OpenFeatureAPI.getInstance().setProviderAndWait(unleashProvider); boolean featureEnabled = client.getBooleanValue(FLAG_NAME, false); diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 4f64ddb63..380a3979c 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -39,7 +39,7 @@ public class UnleashProvider extends EventProvider { public static final String UNKNOWN_ERROR = "unknown error"; @Getter(AccessLevel.PROTECTED) - private UnleashOptions unleashOptions; + private UnleashProviderConfig unleashProviderConfig; @Setter(AccessLevel.PROTECTED) @Getter @@ -53,10 +53,10 @@ public class UnleashProvider extends EventProvider { /** * Constructor. - * @param unleashOptions UnleashOptions + * @param unleashProviderConfig UnleashProviderConfig */ - public UnleashProvider(UnleashOptions unleashOptions) { - this.unleashOptions = unleashOptions; + public UnleashProvider(UnleashProviderConfig unleashProviderConfig) { + this.unleashProviderConfig = unleashProviderConfig; } /** @@ -72,9 +72,9 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { } super.initialize(evaluationContext); UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( - unleashOptions.getUnleashConfigBuilder().build().getSubscriber(), this); - unleashOptions.getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); - UnleashConfig unleashConfig = unleashOptions.getUnleashConfigBuilder().build(); + unleashProviderConfig.getUnleashConfigBuilder().build().getSubscriber(), this); + unleashProviderConfig.getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); + UnleashConfig unleashConfig = unleashProviderConfig.getUnleashConfigBuilder().build(); unleash = new DefaultUnleash(unleashConfig); // Unleash is per definition ready after it is initialized. diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProviderConfig.java similarity index 87% rename from providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java rename to providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProviderConfig.java index f0266e42f..39cb9b87e 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashOptions.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProviderConfig.java @@ -10,6 +10,6 @@ */ @Getter @Builder -public class UnleashOptions { +public class UnleashProviderConfig { private UnleashConfig.Builder unleashConfigBuilder; } diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index a4bffe9dd..2afe33c69 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -106,10 +106,10 @@ private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialis .subscriber(testSubscriber) .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); - UnleashOptions unleashOptions = UnleashOptions.builder() + UnleashProviderConfig unleashProviderConfig = UnleashProviderConfig.builder() .unleashConfigBuilder(unleashConfigBuilder) .build(); - return new UnleashProvider(unleashOptions); + return new UnleashProvider(unleashProviderConfig); } @SneakyThrows From c0ddb56e2d3b29f8d81c887b986005d6ac445b21 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 27 Sep 2023 08:36:56 -0400 Subject: [PATCH 15/17] Update providers/unleash/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ivar Conradi Ă˜sthus Signed-off-by: Todd Baert --- providers/unleash/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/unleash/README.md b/providers/unleash/README.md index 39658e7bf..e9259a17d 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -27,7 +27,7 @@ Unleash OpenFeature Provider is using Unleash Java SDK. ### Usage Example ``` -FeatureProvider featureProvider = new UnleashProvider(unleashProviderConfig); +FeatureProvider unleashProvider = new UnleashProvider(unleashProviderConfig); OpenFeatureAPI.getInstance().setProviderAndWait(unleashProvider); boolean featureEnabled = client.getBooleanValue(FLAG_NAME, false); From 4a61e9c8f1d4f155a843e9de62caa70bd862d409 Mon Sep 17 00:00:00 2001 From: liran2000 Date: Wed, 27 Sep 2023 18:05:25 +0300 Subject: [PATCH 16/17] update owners and test Signed-off-by: liran2000 --- .github/component_owners.yml | 1 + .../contrib/providers/unleash/UnleashProviderTest.java | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/component_owners.yml b/.github/component_owners.yml index 0fd67db3f..f8af4ddf6 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -21,6 +21,7 @@ components: - justinabrahms providers/unleash: - liran2000 + - sighphyre ignored-authors: - renovate-bot \ No newline at end of file diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index 2afe33c69..915b38e56 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -76,7 +76,7 @@ void setUp(WireMockRuntimeInfo wmRuntimeInfo) { String unleashAPI = "http://localhost:" + wmRuntimeInfo.getHttpPort() + "/api/"; String backupFileContent = readBackupFile(); mockUnleashAPI(backupFileContent); - unleashProvider = buildUnleashProvider(true, unleashAPI, backupFileContent, new TestSubscriber()); + unleashProvider = buildUnleashProvider(true, unleashAPI, new TestSubscriber()); OpenFeatureAPI.getInstance().setProviderAndWait("sync", unleashProvider); client = OpenFeatureAPI.getInstance().getClient("sync"); } @@ -99,7 +99,7 @@ private void mockUnleashAPI(String backupFileContent) { } @SneakyThrows - private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation, String unleashAPI, String backupFileContent, TestSubscriber testSubscriber) { + private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation, String unleashAPI, TestSubscriber testSubscriber) { UnleashConfig.Builder unleashConfigBuilder = UnleashConfig.builder().unleashAPI(new URI(unleashAPI)) .appName("fakeApp") @@ -209,7 +209,7 @@ void getEvaluationMetadataTest() { @SneakyThrows @Test void shouldThrowIfNotInitialized() { - UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", "{}", new TestSubscriber()); + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", new TestSubscriber()); assertEquals(ProviderState.NOT_READY, asyncInitUnleashProvider.getState()); // ErrorCode.PROVIDER_NOT_READY should be returned when evaluated via the client @@ -225,7 +225,7 @@ void shouldThrowIfNotInitialized() { @SneakyThrows @Test void shouldThrowIfErrorEvent() { - UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", "{}", null); + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", null); asyncInitUnleashProvider.initialize(new ImmutableContext()); asyncInitUnleashProvider.emitProviderError(ProviderEventDetails.builder().build()); @@ -272,7 +272,7 @@ void contextTransformTest() { @Test void subscriberWrapperTest() { UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, - "http://fakeAPI", "{}", null); + "http://fakeAPI", null); UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( new TestSubscriber(), asyncInitUnleashProvider); unleashSubscriberWrapper.clientMetrics(null); From e50eaffceaa10096d85545b1ae5f5ea61c8d8960 Mon Sep 17 00:00:00 2001 From: liran2000 Date: Wed, 27 Sep 2023 20:48:02 +0300 Subject: [PATCH 17/17] update version to alpha Signed-off-by: liran2000 --- .release-please-manifest.json | 2 +- providers/unleash/README.md | 2 +- providers/unleash/pom.xml | 2 +- providers/unleash/version.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 88a5a3dae..ea06aa0cd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -5,5 +5,5 @@ "providers/flagsmith": "0.0.8", "providers/env-var": "0.0.4", "providers/jsonlogic-eval-provider": "1.0.0", - "providers/unleash": "0.0.1" + "providers/unleash": "0.0.1-alpha" } \ No newline at end of file diff --git a/providers/unleash/README.md b/providers/unleash/README.md index e9259a17d..5b0e53b3e 100644 --- a/providers/unleash/README.md +++ b/providers/unleash/README.md @@ -11,7 +11,7 @@ dev.openfeature.contrib.providers unleash - 0.0.1 + 0.0.1-alpha ``` diff --git a/providers/unleash/pom.xml b/providers/unleash/pom.xml index 7f1d43420..6085aeeed 100644 --- a/providers/unleash/pom.xml +++ b/providers/unleash/pom.xml @@ -10,7 +10,7 @@ dev.openfeature.contrib.providers unleash - 0.0.1 + 0.0.1-alpha unleash unleash provider for Java diff --git a/providers/unleash/version.txt b/providers/unleash/version.txt index 8acdd82b7..68e4b0375 100644 --- a/providers/unleash/version.txt +++ b/providers/unleash/version.txt @@ -1 +1 @@ -0.0.1 +0.0.1-alpha