Repository: Unleash/unleash-client-java
Pull request #82 (2019-07-01 08:25:13)

Add support for static context fields
Added support for two new static context fields:
- environment
- appName

These are set in the configuration when the client is
initialised and should be inserted in to the UnleashContext.

This is needed in order to provide support for [Strategy Constraints](https://github.com/Unleash/unleash/issues/442#issuecomment-507168912)

coveralls: 
[![Coverage Status](https://coveralls.io/builds/24296226/badge)](https://coveralls.io/builds/24296226)

Coverage decreased (-0.002%) to 93.612% when pulling **65ade64035e0543562051e97e39476daeda9904f on feat/static_context_fields** into **b5f74a050ed184e5307c27def38ff55f9b44586d on master**.


Patch:
diff --git a/src/main/java/no/finn/unleash/DefaultUnleash.java b/src/main/java/no/finn/unleash/DefaultUnleash.java
index ee8a21de..6590f244 100644
--- a/src/main/java/no/finn/unleash/DefaultUnleash.java
+++ b/src/main/java/no/finn/unleash/DefaultUnleash.java
@@ -45,6 +45,7 @@ public final class DefaultUnleash implements Unleash {
     private final Map<String, Strategy> strategyMap;
     private final UnleashContextProvider contextProvider;
     private final EventDispatcher eventDispatcher;
+    private final UnleashConfig config;
 
 
     private static FeatureToggleRepository defaultToggleRepository(UnleashConfig unleashConfig) {
@@ -60,6 +61,7 @@ public final class DefaultUnleash implements Unleash {
     }
 
     public DefaultUnleash(UnleashConfig unleashConfig, ToggleRepository toggleRepository, Strategy... strategies) {
+        this.config = unleashConfig;
         this.toggleRepository = toggleRepository;
         this.strategyMap = buildStrategyMap(strategies);
         this.contextProvider = unleashConfig.getContextProvider();
@@ -96,8 +98,9 @@ public final class DefaultUnleash implements Unleash {
         } else if(featureToggle.getStrategies().size() == 0) {
             return true;
         } else {
+            UnleashContext enhancedContext = context.applyStaticFields(config);
             enabled = featureToggle.getStrategies().stream()
-                    .anyMatch(as -> getStrategy(as.getName()).isEnabled(as.getParameters(), context));
+                    .anyMatch(as -> getStrategy(as.getName()).isEnabled(as.getParameters(), enhancedContext));
         }
         return enabled;
     }
diff --git a/src/main/java/no/finn/unleash/UnleashContext.java b/src/main/java/no/finn/unleash/UnleashContext.java
index ac6036f2..7a2b67f9 100644
--- a/src/main/java/no/finn/unleash/UnleashContext.java
+++ b/src/main/java/no/finn/unleash/UnleashContext.java
@@ -4,7 +4,11 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 
+import no.finn.unleash.util.UnleashConfig;
+
 public class UnleashContext {
+    private final Optional<String> appName;
+    private final Optional<String> environment;
     private final Optional<String> userId;
     private final Optional<String> sessionId;
     private final Optional<String> remoteAddress;
@@ -12,6 +16,12 @@ public class UnleashContext {
     private final Map<String, String> properties;
 
     public UnleashContext(String userId, String sessionId, String remoteAddress, Map<String, String> properties) {
+        this(null, null, userId, sessionId, remoteAddress, properties);
+    }
+
+    public UnleashContext(String appName, String environment, String userId, String sessionId, String remoteAddress, Map<String, String> properties) {
+        this.appName = Optional.ofNullable(appName);
+        this.environment = Optional.ofNullable(environment);
         this.userId = Optional.ofNullable(userId);
         this.sessionId = Optional.ofNullable(sessionId);
         this.remoteAddress = Optional.ofNullable(remoteAddress);
@@ -34,17 +44,59 @@ public class UnleashContext {
         return properties;
     }
 
+    public Optional<String> getAppName() {
+        return appName;
+    }
+
+    public Optional<String> getEnvironment() {
+        return environment;
+    }
+
+    public UnleashContext applyStaticFields(UnleashConfig config) {
+        Builder builder = new Builder(this);
+        if(!this.environment.isPresent()) {
+            builder.environment(config.getEnvironment());
+        }
+        if(!this.appName.isPresent()) {
+            builder.appName(config.getAppName());
+        }
+        return builder.build();
+    }
+
     public static Builder builder() {
         return new Builder();
     }
 
     public static class Builder {
+        private String appName;
+        private String environment;
         private String userId;
         private String sessionId;
         private String remoteAddress;
 
         private final Map<String, String> properties = new HashMap<>();
 
+        public Builder() { }
+
+        public Builder(UnleashContext context) {
+            context.appName.ifPresent(val -> this.appName = val);
+            context.environment.ifPresent(val -> this.environment = val);
+            context.userId.ifPresent(val -> this.userId = val);
+            context.sessionId.ifPresent(val -> this.sessionId = val);
+            context.remoteAddress.ifPresent(val -> this.remoteAddress = val);
+            context.properties.forEach(this.properties::put);
+        }
+
+        public Builder appName(String appName) {
+            this.appName = appName;
+            return this;
+        }
+
+        public Builder environment(String environment) {
+            this.environment = environment;
+            return this;
+        }
+
         public Builder userId(String userId) {
             this.userId = userId;
             return this;
@@ -66,7 +118,7 @@ public class UnleashContext {
         }
 
         public UnleashContext build() {
-            return new UnleashContext(userId, sessionId, remoteAddress, properties);
+            return new UnleashContext(appName, environment, userId, sessionId, remoteAddress, properties);
         }
     }
 
diff --git a/src/main/java/no/finn/unleash/util/UnleashConfig.java b/src/main/java/no/finn/unleash/util/UnleashConfig.java
index 17b5ce73..2330f6f6 100644
--- a/src/main/java/no/finn/unleash/util/UnleashConfig.java
+++ b/src/main/java/no/finn/unleash/util/UnleashConfig.java
@@ -21,6 +21,7 @@ public class UnleashConfig {
     private final UnleashURLs unleashURLs;
     private final Map<String, String> customHttpHeaders;
     private final String appName;
+    private final String environment;
     private final String instanceId;
     private final String sdkVersion;
     private final String backupFile;
@@ -36,6 +37,7 @@ public class UnleashConfig {
             URI unleashAPI,
             Map<String, String> customHttpHeaders,
             String appName,
+            String environment,
             String instanceId,
             String sdkVersion,
             String backupFile,
@@ -73,6 +75,7 @@ public class UnleashConfig {
         this.customHttpHeaders = customHttpHeaders;
         this.unleashURLs = new UnleashURLs(unleashAPI);
         this.appName = appName;
+        this.environment = environment;
         this.instanceId = instanceId;
         this.sdkVersion = sdkVersion;
         this.backupFile = backupFile;
@@ -97,6 +100,10 @@ public class UnleashConfig {
         return appName;
     }
 
+    public String getEnvironment() {
+        return environment;
+    }
+
     public String getInstanceId() {
         return instanceId;
     }
@@ -156,6 +163,7 @@ public class UnleashConfig {
         private URI unleashAPI;
         private Map<String, String> customHttpHeaders = new HashMap<>();
         private String appName;
+        private String environment = "default";
         private String instanceId = getDefaultInstanceId();
         private String sdkVersion = getDefaultSdkVersion();
         private String backupFile;
@@ -197,6 +205,11 @@ public class UnleashConfig {
             return this;
         }
 
+        public Builder environment(String environment) {
+            this.environment = environment;
+            return this;
+        }
+
         public Builder instanceId(String instanceId) {
             this.instanceId = instanceId;
             return this;
@@ -256,6 +269,7 @@ public class UnleashConfig {
                     unleashAPI,
                     customHttpHeaders,
                     appName,
+                    environment,
                     instanceId,
                     sdkVersion,
                     getBackupFile(),


Test patch:
diff --git a/src/test/java/no/finn/unleash/UnleashContextTest.java b/src/test/java/no/finn/unleash/UnleashContextTest.java
new file mode 100644
index 00000000..1f1f9e08
--- /dev/null
+++ b/src/test/java/no/finn/unleash/UnleashContextTest.java
@@ -0,0 +1,100 @@
+package no.finn.unleash;
+
+
+import no.finn.unleash.util.UnleashConfig;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class UnleashContextTest {
+
+    @Test
+    public void should_generate_default_context() {
+        UnleashContext context = UnleashContext.builder().build();
+        assertThat(context.getUserId().isPresent(), is(false));
+        assertThat(context.getSessionId().isPresent(), is(false));
+        assertThat(context.getRemoteAddress().isPresent(), is(false));
+        assertThat(context.getProperties().size(), is(0));
+    }
+
+    @Test
+    public void should_get_context_with_userId() {
+        UnleashContext context = UnleashContext.builder()
+                .userId("test@mail.com")
+                .build();
+        assertThat(context.getUserId().get(), is("test@mail.com"));
+    }
+
+    @Test
+    public void should_get_context_with_fields_set() {
+        UnleashContext context = UnleashContext.builder()
+                .userId("test@mail.com")
+                .sessionId("123")
+                .remoteAddress("127.0.0.1")
+                .environment("prod")
+                .appName("myapp")
+                .addProperty("test", "me")
+                .build();
+
+        assertThat(context.getUserId().get(), is("test@mail.com"));
+        assertThat(context.getSessionId().get(), is("123"));
+        assertThat(context.getRemoteAddress().get(), is("127.0.0.1"));
+        assertThat(context.getEnvironment().get(), is("prod"));
+        assertThat(context.getAppName().get(), is("myapp"));
+        assertThat(context.getProperties().get("test"), is("me"));
+    }
+
+    @Test
+    public void should_apply_context_fields() {
+        UnleashContext context = UnleashContext.builder()
+                .userId("test@mail.com")
+                .sessionId("123")
+                .remoteAddress("127.0.0.1")
+                .addProperty("test", "me")
+                .build();
+
+        UnleashConfig config = UnleashConfig.builder()
+                .unleashAPI("http://test.com")
+                .appName("someApp")
+                .environment("stage")
+                .build();
+
+        UnleashContext enhanced = context.applyStaticFields(config);
+
+        assertThat(enhanced.getUserId().get(), is("test@mail.com"));
+        assertThat(enhanced.getSessionId().get(), is("123"));
+        assertThat(enhanced.getRemoteAddress().get(), is("127.0.0.1"));
+
+        assertThat(enhanced.getEnvironment().get(), is("stage"));
+        assertThat(enhanced.getAppName().get(), is("someApp"));
+    }
+
+    @Test
+    public void should_not_ovveride_static_context_fields() {
+        UnleashContext context = UnleashContext.builder()
+                .userId("test@mail.com")
+                .sessionId("123")
+                .remoteAddress("127.0.0.1")
+                .environment("env")
+                .appName("myApp")
+                .addProperty("test", "me")
+                .build();
+
+        UnleashConfig config = UnleashConfig.builder()
+                .unleashAPI("http://test.com")
+                .appName("someApp")
+                .environment("stage")
+                .build();
+
+
+        UnleashContext enhanced = context.applyStaticFields(config);
+
+        assertThat(enhanced.getUserId().get(), is("test@mail.com"));
+        assertThat(enhanced.getSessionId().get(), is("123"));
+        assertThat(enhanced.getRemoteAddress().get(), is("127.0.0.1"));
+        assertThat(enhanced.getEnvironment().get(), is("env"));
+        assertThat(enhanced.getAppName().get(), is("myApp"));
+    }
+
+}
diff --git a/src/test/java/no/finn/unleash/util/UnleashConfigTest.java b/src/test/java/no/finn/unleash/util/UnleashConfigTest.java
index 37ef465a..c6c8deb3 100644
--- a/src/test/java/no/finn/unleash/util/UnleashConfigTest.java
+++ b/src/test/java/no/finn/unleash/util/UnleashConfigTest.java
@@ -80,6 +80,28 @@ public class UnleashConfigTest {
         assertThat(config.getSdkVersion(), is("unleash-client-java:development"));
     }
 
+    @Test
+    public void should_set_environment_to_default() {
+        UnleashConfig config = UnleashConfig.builder()
+                .appName("my-app")
+                .unleashAPI("http://unleash.org")
+                .build();
+
+        assertThat(config.getEnvironment(), is("default"));
+    }
+
+    @Test
+    public void should_set_environment() {
+        String env = "prod";
+        UnleashConfig config = UnleashConfig.builder()
+                .appName("my-app")
+                .environment(env)
+                .unleashAPI("http://unleash.org")
+                .build();
+
+        assertThat(config.getEnvironment(), is(env));
+    }
+
     @Test
     public void should_add_app_name_and_instance_id_and_user_agent_to_connection() throws IOException {
         String appName = "my-app";