Repository: Unleash/unleash-client-java
Pull request #117 (2021-01-28 08:51:17)

feat: Add support for filtering toggles on project
This will have no effect until the server is released with support for this, but it's good to be ahead of time :)

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

Coverage increased (+0.07%) to 92.089% when pulling **19b83870e99b9d4f6b936aab45f296e89ecc53a6 on project-filter** into **69aac4fd552f2764bcecc59febf2445217807b85 on master**.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee06293c..0c8761bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
 ## 4.0.0
 - feat: Switched to slf4j-api logging
 - feat: add support for filtering on project or tags
+- fix: BREAKING CHANGE UnleashConfig constructor is now private. 
+  Use UnleashConfig.Builder to get instance of UnleashConfig 
 ## 3.3.4
 - fix: follow redirect once (#115)
 - chore(deps): bump version.log4j2 from 2.11.2 to 2.13.3 (#109)
diff --git a/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java b/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java
index 0af157c2..ade3f54a 100644
--- a/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java
+++ b/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java
@@ -25,7 +25,11 @@ public final class HttpToggleFetcher implements ToggleFetcher {
 
     public HttpToggleFetcher(UnleashConfig unleashConfig) {
         this.unleashConfig = unleashConfig;
-        this.toggleUrl = unleashConfig.getUnleashURLs().getFetchTogglesURL();
+        if (unleashConfig.getProjectName() != null) {
+            this.toggleUrl = unleashConfig.getUnleashURLs().getFetchTogglesURL(unleashConfig.getProjectName());
+        } else {
+            this.toggleUrl = unleashConfig.getUnleashURLs().getFetchTogglesURL();
+        }
     }
 
     @Override
diff --git a/src/main/java/no/finn/unleash/util/UnleashConfig.java b/src/main/java/no/finn/unleash/util/UnleashConfig.java
index c343f785..91494bf8 100644
--- a/src/main/java/no/finn/unleash/util/UnleashConfig.java
+++ b/src/main/java/no/finn/unleash/util/UnleashConfig.java
@@ -26,6 +26,7 @@ public class UnleashConfig {
     private final String instanceId;
     private final String sdkVersion;
     private final String backupFile;
+    private final String projectName;
     private final long fetchTogglesInterval;
     private final long sendMetricsInterval;
     private final boolean disableMetrics;
@@ -35,7 +36,7 @@ public class UnleashConfig {
     private final UnleashScheduledExecutor unleashScheduledExecutor;
     private final UnleashSubscriber unleashSubscriber;
 
-    public UnleashConfig(
+    private UnleashConfig(
             URI unleashAPI,
             Map<String, String> customHttpHeaders,
             CustomHttpHeadersProvider customHttpHeadersProvider,
@@ -44,6 +45,7 @@ public class UnleashConfig {
             String instanceId,
             String sdkVersion,
             String backupFile,
+            String projectName,
             long fetchTogglesInterval,
             long sendMetricsInterval,
             boolean disableMetrics,
@@ -87,6 +89,7 @@ public class UnleashConfig {
         this.instanceId = instanceId;
         this.sdkVersion = sdkVersion;
         this.backupFile = backupFile;
+        this.projectName = projectName;
         this.fetchTogglesInterval = fetchTogglesInterval;
         this.sendMetricsInterval = sendMetricsInterval;
         this.disableMetrics = disableMetrics;
@@ -140,6 +143,8 @@ public class UnleashConfig {
         return sdkVersion;
     }
 
+    public String getProjectName() { return projectName; }
+
     public long getFetchTogglesInterval() {
         return fetchTogglesInterval;
     }
@@ -210,6 +215,7 @@ public class UnleashConfig {
         private String instanceId = getDefaultInstanceId();
         private String sdkVersion = getDefaultSdkVersion();
         private String backupFile;
+        private String projectName;
         private long fetchTogglesInterval = 10;
         private long sendMetricsInterval = 60;
         private boolean disableMetrics = false;
@@ -264,6 +270,11 @@ public class UnleashConfig {
             return this;
         }
 
+        public Builder projectName(String projectName) {
+            this.projectName = projectName;
+            return this;
+        }
+
         public Builder fetchTogglesInterval(long fetchTogglesInterval) {
             this.fetchTogglesInterval = fetchTogglesInterval;
             return this;
@@ -328,6 +339,7 @@ public class UnleashConfig {
                     instanceId,
                     sdkVersion,
                     getBackupFile(),
+                    projectName,
                     fetchTogglesInterval,
                     sendMetricsInterval,
                     disableMetrics,
diff --git a/src/main/java/no/finn/unleash/util/UnleashURLs.java b/src/main/java/no/finn/unleash/util/UnleashURLs.java
index 5d203a50..4b775667 100644
--- a/src/main/java/no/finn/unleash/util/UnleashURLs.java
+++ b/src/main/java/no/finn/unleash/util/UnleashURLs.java
@@ -25,6 +25,14 @@ public class UnleashURLs {
         return fetchTogglesURL;
     }
 
+    public URL getFetchTogglesURL(String projectName) {
+        try {
+            return URI.create(fetchTogglesURL.toString() + "?project=" + projectName).normalize().toURL();
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Project name [" +projectName + "] was not URL friendly.", e);
+        }
+    }
+
     public URL getClientMetricsURL() {
         return clientMetricsURL;
     }


Test patch:
diff --git a/src/test/java/no/finn/unleash/repository/HttpToggleFetcherTest.java b/src/test/java/no/finn/unleash/repository/HttpToggleFetcherTest.java
index 985259db..81e76bc3 100644
--- a/src/test/java/no/finn/unleash/repository/HttpToggleFetcherTest.java
+++ b/src/test/java/no/finn/unleash/repository/HttpToggleFetcherTest.java
@@ -284,4 +284,29 @@ public class HttpToggleFetcherTest {
 
     }
 
+    @Test
+    public void should_add_project_filter_to_toggles_url_if_config_has_it_set() throws URISyntaxException {
+        stubFor(get(urlEqualTo("/api/client/features?project=name"))
+        .withHeader("Accept", equalTo("application/json"))
+        .willReturn(aResponse()
+            .withStatus(200)
+                .withHeader("Content-Type", "application/json")
+                .withBodyFile("features-v1.json")
+        ));
+        URI uri = new URI(serverMock.baseUrl() +"/api/");
+        UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).projectName("name").build();
+        HttpToggleFetcher httpToggleFetcher = new HttpToggleFetcher(config);
+        FeatureToggleResponse response = httpToggleFetcher.fetchToggles();
+        verify(getRequestedFor(urlMatching("/api/client/features\\?project=name")));
+    }
+
+    @Test
+    public void should_throw_an_exception_if_project_name_is_not_url_friendly() throws URISyntaxException {
+        URI uri = new URI(serverMock.baseUrl() + "/api/");
+        String name = "^!#$!$?";
+        UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).projectName(name).build();
+        org.assertj.core.api.Assertions.assertThatThrownBy(() -> new HttpToggleFetcher(config))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessage("Project name [" +name  +"] was not URL friendly.");
+    }
 }