Repository: Unleash/unleash-client-java (java)

Issue:
Update API endoint paths for Unleash 3.x
The next version of Unleash has changed the API endpoint for the client calls. Next version of the client should use the client path. 

https://github.com/Unleash/unleash/tree/master/docs/api/client

Pull request description:
Update API endoint paths for Unleash 3.x
closes #40

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 42a83dd0..79316fa1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,9 @@
 # Changelog
 
 ## 3.0.0 (unreleased)
+- This version requires `unleash-server` v3 or higher. 
 - Switch hashing to MurmurHash (https://github.com/Unleash/unleash/issues/247)
+- Update API endoint paths for Unleash 3.x (https://github.com/Unleash/unleash-client-java/issues/40)
 
 ## 2.1.3
 - Add sdkVersion in client-register call
diff --git a/README.md b/README.md
index 6ed4a072..d972b8cf 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,9 @@
 [![Build Status](https://travis-ci.org/Unleash/unleash-client-java.svg?branch=master)](https://travis-ci.org/Unleash/unleash-client-java)
 [![Coverage Status](https://coveralls.io/repos/github/Unleash/unleash-client-java/badge.svg?branch=master)](https://coveralls.io/github/Unleash/unleash-client-java?branch=master)
 
+This is the node client for Unleash. Read more about the [Unleash project](https://github.com/finn-no/unleash)
+**Version 3.x of the client requires `unleash-server` v3.x or higher. 
+
 ## Getting started
 You will require unleash on your class path, pop it in to your pom:
 
diff --git a/src/main/java/no/finn/unleash/util/UnleashURLs.java b/src/main/java/no/finn/unleash/util/UnleashURLs.java
index 4f316971..5d203a50 100644
--- a/src/main/java/no/finn/unleash/util/UnleashURLs.java
+++ b/src/main/java/no/finn/unleash/util/UnleashURLs.java
@@ -12,7 +12,7 @@ public class UnleashURLs {
     public UnleashURLs(URI unleashAPI) {
         try {
             String unleashAPIstr = unleashAPI.toString();
-            fetchTogglesURL = URI.create(unleashAPIstr + "/features").normalize().toURL();
+            fetchTogglesURL = URI.create(unleashAPIstr + "/client/features").normalize().toURL();
             clientMetricsURL = URI.create(unleashAPIstr + "/client/metrics").normalize().toURL();
             clientRegisterURL = URI.create(unleashAPIstr + "/client/register").normalize().toURL();
 


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 296eb921..81aebaa5 100644
--- a/src/test/java/no/finn/unleash/repository/HttpToggleFetcherTest.java
+++ b/src/test/java/no/finn/unleash/repository/HttpToggleFetcherTest.java
@@ -42,14 +42,14 @@ public class HttpToggleFetcherTest {
 
     @Test
     public void happy_path_test_version0() throws URISyntaxException {
-        stubFor(get(urlEqualTo("/features"))
+        stubFor(get(urlEqualTo("/api/client/features"))
                 .withHeader("Accept", equalTo("application/json"))
                 .willReturn(aResponse()
                         .withStatus(200)
                         .withHeader("Content-Type", "application/json")
                         .withBodyFile("features-v0.json")));
 
-        URI uri = new URI("http://localhost:"+wireMockRule.port());
+        URI uri = new URI("http://localhost:"+wireMockRule.port() + "/api/");
         UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).build();
         HttpToggleFetcher httpToggleFetcher = new HttpToggleFetcher(config);
         FeatureToggleResponse response = httpToggleFetcher.fetchToggles();
@@ -57,20 +57,20 @@ public class HttpToggleFetcherTest {
 
         assertTrue(featureX.isEnabled());
 
-        verify(getRequestedFor(urlMatching("/features"))
+        verify(getRequestedFor(urlMatching("/api/client/features"))
                 .withHeader("Content-Type", matching("application/json")));
     }
 
     @Test
     public void happy_path_test_version1() throws URISyntaxException {
-        stubFor(get(urlEqualTo("/features"))
+        stubFor(get(urlEqualTo("/api/client/features"))
                 .withHeader("Accept", equalTo("application/json"))
                 .willReturn(aResponse()
                         .withStatus(200)
                         .withHeader("Content-Type", "application/json")
                         .withBodyFile("features-v1.json")));
 
-        URI uri = new URI("http://localhost:"+wireMockRule.port());
+        URI uri = new URI("http://localhost:"+wireMockRule.port() + "/api/");
         UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).build();
         HttpToggleFetcher httpToggleFetcher = new HttpToggleFetcher(config);
         FeatureToggleResponse response = httpToggleFetcher.fetchToggles();
@@ -78,64 +78,64 @@ public class HttpToggleFetcherTest {
 
         assertTrue(featureX.isEnabled());
 
-        verify(getRequestedFor(urlMatching("/features"))
+        verify(getRequestedFor(urlMatching("/api/client/features"))
                 .withHeader("Content-Type", matching("application/json")));
     }
 
 
     @Test
     public void given_empty_body() throws URISyntaxException {
-        stubFor(get(urlEqualTo("/features"))
+        stubFor(get(urlEqualTo("/api/client/features"))
                 .withHeader("Accept", equalTo("application/json"))
                 .willReturn(aResponse()
                         .withStatus(200)
                         .withHeader("Content-Type", "application/json")));
 
-        URI uri = new URI("http://localhost:"+wireMockRule.port());
+        URI uri = new URI("http://localhost:"+wireMockRule.port() + "/api/");
         UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).build();
         HttpToggleFetcher httpToggleFetcher = new HttpToggleFetcher(config);
         exception.expect(UnleashException.class);
         httpToggleFetcher.fetchToggles();
 
 
-        verify(getRequestedFor(urlMatching("/features"))
+        verify(getRequestedFor(urlMatching("/api/client/features"))
                 .withHeader("Content-Type", matching("application/json")));
     }
 
     @Test
     public void given_json_without_feature_field() throws Exception {
-        stubFor(get(urlEqualTo("/features"))
+        stubFor(get(urlEqualTo("/api/client/features"))
                 .withHeader("Accept", equalTo("application/json"))
                 .willReturn(aResponse()
                         .withStatus(200)
                         .withHeader("Content-Type", "application/json")
                         .withBody("{}")));
 
-        URI uri = new URI("http://localhost:"+wireMockRule.port());
+        URI uri = new URI("http://localhost:"+wireMockRule.port() + "/api/");
         UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).build();
         HttpToggleFetcher httpToggleFetcher = new HttpToggleFetcher(config);
         exception.expect(UnleashException.class);
         httpToggleFetcher.fetchToggles();
 
-        verify(getRequestedFor(urlMatching("/features"))
+        verify(getRequestedFor(urlMatching("/api/client/features"))
                 .withHeader("Content-Type", matching("application/json")));
     }
 
     @Test
     public void should_handle_not_changed() throws URISyntaxException {
-        stubFor(get(urlEqualTo("/features"))
+        stubFor(get(urlEqualTo("/api/client/features"))
                 .withHeader("Accept", equalTo("application/json"))
                 .willReturn(aResponse()
                         .withStatus(302)
                         .withHeader("Content-Type", "application/json")));
 
-        URI uri = new URI("http://localhost:"+wireMockRule.port());
+        URI uri = new URI("http://localhost:"+wireMockRule.port() + "/api/");
         UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).build();
         HttpToggleFetcher httpToggleFetcher = new HttpToggleFetcher(config);
         FeatureToggleResponse response = httpToggleFetcher.fetchToggles();
         assertEquals("Should return status NOT_CHANGED", response.getStatus(), FeatureToggleResponse.Status.NOT_CHANGED);
 
-        verify(getRequestedFor(urlMatching("/features"))
+        verify(getRequestedFor(urlMatching("/api/client/features"))
                 .withHeader("Content-Type", matching("application/json")));
 
     }
@@ -144,19 +144,19 @@ public class HttpToggleFetcherTest {
     public void should_handle_errors() throws URISyntaxException {
         int httpCodes[] = {400,401,403,404,500,503};
         for(int httpCode:httpCodes) {
-            stubFor(get(urlEqualTo("/features"))
+            stubFor(get(urlEqualTo("/api/client/features"))
                     .withHeader("Accept", equalTo("application/json"))
                     .willReturn(aResponse()
                             .withStatus(httpCode)
                             .withHeader("Content-Type", "application/json")));
 
-            URI uri = new URI("http://localhost:" + wireMockRule.port());
+            URI uri = new URI("http://localhost:" + wireMockRule.port() + "/api/");
             UnleashConfig config = UnleashConfig.builder().appName("test").unleashAPI(uri).build();
             HttpToggleFetcher httpToggleFetcher = new HttpToggleFetcher(config);
             FeatureToggleResponse response = httpToggleFetcher.fetchToggles();
             assertEquals("Should return status NOT_CHANGED", response.getStatus(), FeatureToggleResponse.Status.NOT_CHANGED);
 
-            verify(getRequestedFor(urlMatching("/features"))
+            verify(getRequestedFor(urlMatching("/api/client/features"))
                     .withHeader("Content-Type", matching("application/json")));
         }
 
diff --git a/src/test/java/no/finn/unleash/util/UnleashURLsTest.java b/src/test/java/no/finn/unleash/util/UnleashURLsTest.java
index 3dfb0de3..629fa1c1 100644
--- a/src/test/java/no/finn/unleash/util/UnleashURLsTest.java
+++ b/src/test/java/no/finn/unleash/util/UnleashURLsTest.java
@@ -12,26 +12,26 @@ public class UnleashURLsTest {
 
     @Test
     public void should_handle_additional_slash() {
-        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com/"));
-        assertThat(urls.getFetchTogglesURL().toString(), is("http://unleash.com/features"));
+        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com/api/"));
+        assertThat(urls.getFetchTogglesURL().toString(), is("http://unleash.com/api/client/features"));
     }
 
     @Test
     public void should_set_correct_client_register_url() {
-        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com"));
-        assertThat(urls.getClientRegisterURL().toString(), is("http://unleash.com/client/register"));
+        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com/api/"));
+        assertThat(urls.getClientRegisterURL().toString(), is("http://unleash.com/api/client/register"));
     }
 
     @Test
     public void should_set_correct_client_metrics_url() {
-        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com"));
-        assertThat(urls.getClientMetricsURL().toString(), is("http://unleash.com/client/metrics"));
+        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com/api/"));
+        assertThat(urls.getClientMetricsURL().toString(), is("http://unleash.com/api/client/metrics"));
     }
 
     @Test
     public void should_set_correct_fetch_url() {
-        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com"));
-        assertThat(urls.getFetchTogglesURL().toString(), is("http://unleash.com/features"));
+        UnleashURLs urls = new UnleashURLs(URI.create("http://unleash.com/api/"));
+        assertThat(urls.getFetchTogglesURL().toString(), is("http://unleash.com/api/client/features"));
     }
 
     @Test(expected = IllegalArgumentException.class)