Repository: Unleash/unleash-client-java
Pull request #295 (2025-03-12 08:11:32)

feat: add environmentEnabled property on feature defs
Restores the old `enabled` property on feature defs. I've chosen to rename this to `environmentEnabled`, which I think expresses better what this actually means

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

coverage: 79.616% (+0.2%) from 79.439%
when pulling **934f48a71b40ec01fa5b128896bd6e5e32b59127 on feat/restore-enabled-property-on-feature-defs**
into **9734138c5fa2592a8c1c6cdcd37a43b089c8b4e4 on main**.


Patch:
diff --git a/pom.xml b/pom.xml
index dcfa3dc4..32c68a58 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,7 +59,7 @@
         <dependency>
             <groupId>io.getunleash</groupId>
             <artifactId>yggdrasil-engine</artifactId>
-            <version>0.1.0</version>
+            <version>0.1.1</version>
         </dependency>
         <dependency>
             <groupId>com.google.code.gson</groupId>
diff --git a/src/main/java/io/getunleash/FakeUnleash.java b/src/main/java/io/getunleash/FakeUnleash.java
index 9eb5e1ce..dcf69e2a 100644
--- a/src/main/java/io/getunleash/FakeUnleash.java
+++ b/src/main/java/io/getunleash/FakeUnleash.java
@@ -142,7 +142,10 @@ public class FakeUnleash implements Unleash {
                     .map(
                             value ->
                                     new FeatureDefinition(
-                                            toggleName, Optional.of("experiment"), "default"));
+                                            toggleName,
+                                            Optional.of("experiment"),
+                                            "default",
+                                            true));
         }
 
         @Override
diff --git a/src/main/java/io/getunleash/FeatureDefinition.java b/src/main/java/io/getunleash/FeatureDefinition.java
index d21e7da8..125d9d2d 100644
--- a/src/main/java/io/getunleash/FeatureDefinition.java
+++ b/src/main/java/io/getunleash/FeatureDefinition.java
@@ -8,17 +8,21 @@ public class FeatureDefinition {
     private final String name;
     private final Optional<String> type;
     private final String project;
+    private final boolean environmentEnabled;
 
     public FeatureDefinition(FeatureDef source) {
         this.name = source.getName();
         this.type = source.getType();
         this.project = source.getProject();
+        this.environmentEnabled = source.isEnabled();
     }
 
-    public FeatureDefinition(String name, Optional<String> type, String project) {
+    public FeatureDefinition(
+            String name, Optional<String> type, String project, boolean environmentEnabled) {
         this.name = name;
         this.type = type;
         this.project = project;
+        this.environmentEnabled = environmentEnabled;
     }
 
     public String getName() {
@@ -32,4 +36,8 @@ public class FeatureDefinition {
     public String getProject() {
         return project;
     }
+
+    public boolean environmentEnabled() {
+        return environmentEnabled;
+    }
 }
diff --git a/src/main/java/io/getunleash/util/FeatureDefinitionAdapter.java b/src/main/java/io/getunleash/util/FeatureDefinitionAdapter.java
index b2dda449..54c12d33 100644
--- a/src/main/java/io/getunleash/util/FeatureDefinitionAdapter.java
+++ b/src/main/java/io/getunleash/util/FeatureDefinitionAdapter.java
@@ -15,6 +15,7 @@ public class FeatureDefinitionAdapter extends TypeAdapter<FeatureDefinition> {
         out.name("name").value(value.getName());
         out.name("project").value(value.getProject());
         out.name("type").value(value.getType().orElse(null));
+        out.name("enabled").value(value.environmentEnabled());
         out.endObject();
     }
 
@@ -23,6 +24,7 @@ public class FeatureDefinitionAdapter extends TypeAdapter<FeatureDefinition> {
         String name = null;
         String project = null;
         Optional<String> type = Optional.empty();
+        boolean enabled = false;
 
         in.beginObject();
         while (in.hasNext()) {
@@ -36,6 +38,9 @@ public class FeatureDefinitionAdapter extends TypeAdapter<FeatureDefinition> {
                 case "type":
                     type = Optional.of(in.nextString());
                     break;
+                case "enabled":
+                    enabled = in.nextBoolean();
+                    break;
                 default:
                     in.skipValue();
             }
@@ -46,6 +51,6 @@ public class FeatureDefinitionAdapter extends TypeAdapter<FeatureDefinition> {
             throw new IOException("Missing required field 'name'");
         }
 
-        return new FeatureDefinition(name, type, project);
+        return new FeatureDefinition(name, type, project, enabled);
     }
 }


Test patch:
diff --git a/src/test/java/io/getunleash/repository/FeatureRepositoryTest.java b/src/test/java/io/getunleash/repository/FeatureRepositoryTest.java
index 52ffcc75..19a15eac 100644
--- a/src/test/java/io/getunleash/repository/FeatureRepositoryTest.java
+++ b/src/test/java/io/getunleash/repository/FeatureRepositoryTest.java
@@ -182,6 +182,29 @@ public class FeatureRepositoryTest {
         assertEquals(5, knownToggles.size());
     }
 
+    @Test
+    public void should_return_enabled_property_on_known_toggles() {
+        when(backupHandler.read())
+                .thenReturn(Optional.of(loadMockFeatures("unleash-repo-v2.json")));
+
+        when(bootstrapHandler.read()).thenReturn(Optional.empty());
+
+        FeatureRepository featureRepository =
+                new FeatureRepositoryImpl(defaultConfig, backupHandler, new UnleashEngine());
+
+        List<FeatureDefinition> knownToggles =
+                featureRepository.listKnownToggles().collect(Collectors.toList());
+
+        assertEquals(5, knownToggles.size());
+        FeatureDefinition featureY =
+                knownToggles.stream().filter(f -> f.getName().equals("featureY")).findFirst().get();
+        assertThat(featureY.environmentEnabled()).isFalse();
+
+        FeatureDefinition featureX =
+                knownToggles.stream().filter(f -> f.getName().equals("featureX")).findFirst().get();
+        assertThat(featureX.environmentEnabled()).isTrue();
+    }
+
     @Test
     public void should_not_read_bootstrap_if_backup_was_found() {
 
diff --git a/src/test/java/io/getunleash/util/ClientFeaturesParserTest.java b/src/test/java/io/getunleash/util/ClientFeaturesParserTest.java
index c933e91e..d3e51787 100644
--- a/src/test/java/io/getunleash/util/ClientFeaturesParserTest.java
+++ b/src/test/java/io/getunleash/util/ClientFeaturesParserTest.java
@@ -66,4 +66,34 @@ public class ClientFeaturesParserTest {
             assertThat(e.getMessage()).contains("Missing required field 'name'");
         }
     }
+
+    @Test
+    public void test_enabled_property_returned_if_set() {
+        String basicFeatures =
+                "{\"features\":[{\"name\":\"featureX\",\"project\":\"default\",\"enabled\":true,\"strategies\":[{\"name\":\"default\"}]}]}";
+        List<FeatureDefinition> parsed = ClientFeaturesParser.parse(basicFeatures);
+
+        assertEquals(1, parsed.size());
+
+        FeatureDefinition feature = parsed.get(0);
+
+        assertEquals(feature.getName(), "featureX");
+        assertEquals(feature.getProject(), "default");
+        assertEquals(feature.environmentEnabled(), true);
+    }
+
+    @Test
+    public void test_enabled_property_defaults_to_false() {
+        String basicFeatures =
+                "{\"features\":[{\"name\":\"featureX\",\"project\":\"default\",\"strategies\":[{\"name\":\"default\"}]}]}";
+        List<FeatureDefinition> parsed = ClientFeaturesParser.parse(basicFeatures);
+
+        assertEquals(1, parsed.size());
+
+        FeatureDefinition feature = parsed.get(0);
+
+        assertEquals(feature.getName(), "featureX");
+        assertEquals(feature.getProject(), "default");
+        assertEquals(feature.environmentEnabled(), false);
+    }
 }