Repository: spring-cloud/spring-cloud-consul
Issue: Consul ConfigWatch not working for FILES config format

Watching Consul for config changes stopped working with release 1.1 in case of using FILES config format. I ran into this as part of upgrading to Spring Camden.RELEASE (that updates from spring-cloud-consul 1.0.2 to 1.1.0).

I did some debugging and think I know what the root cause is:
- ConfigWatch always a trailing slash to the context paths looked at in Consul
- This essentially means that ConfigWatch always looks at folders - this is conceptually incorrect  'FILES' mode - this actually is the problem root cause
- Watching this incorrect path always leads to a HTTP 404 from Consul (since the file is no folder but a leaf in the Consul tree)
- This still worked with Brixton / 1.0.2 by accident since 1.02 did not even look at the response code and only at the index returned by Consul
- Release 1.1 added code in ConfigWatch that looks at the Consul response value and ignores HTTP 404 responses (which is correct)
- Any changes to the config files are now skipped with Camden / 1.1.0 and do not lead to config change events

The fix would require to look at the configured config format `spring.cloud.consul.config.format` and avoid implicitly adding a trailing slash in case `FILES` is desired.


Changed files:
spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java

diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
index d9622bfd..d7e174d3 100644
--- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
+++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
@@ -36,6 +36,8 @@ import lombok.Data;
 import lombok.extern.apachecommons.CommonsLog;
 import org.springframework.util.ReflectionUtils;
 
+import static org.springframework.cloud.consul.config.ConsulConfigProperties.Format.FILES;
+
 /**
  * @author Spencer Gibb
  */
@@ -70,7 +72,9 @@ public class ConfigWatch implements Closeable, ApplicationEventPublisherAware {
 	public void watchConfigKeyValues() {
 		if (this.running.get()) {
 			for (String context : this.contexts) {
-				if (!context.endsWith("/")) {
+
+				// turn the context into a Consul folder path (unless our config format are FILES)
+				if (properties.getFormat() != FILES && !context.endsWith("/")) {
 					context = context + "/";
 				}
 
diff --git a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
index 37d2f0b2..5c271bda 100644
--- a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
+++ b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
@@ -20,6 +20,7 @@ import com.ecwid.consul.v1.ConsulClient;
 import com.ecwid.consul.v1.QueryParams;
 import com.ecwid.consul.v1.Response;
 import com.ecwid.consul.v1.kv.model.GetValue;
+import org.junit.Before;
 import org.junit.Test;
 import org.springframework.cloud.endpoint.event.RefreshEvent;
 import org.springframework.context.ApplicationEventPublisher;
@@ -34,17 +35,25 @@ import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.springframework.cloud.consul.config.ConsulConfigProperties.Format.FILES;
 
 /**
  * @author Spencer Gibb
  */
 public class ConfigWatchTests {
 
+	private ConsulConfigProperties configProperties;
+
+	@Before
+	public void setUp() throws Exception {
+		configProperties = new ConsulConfigProperties();
+	}
+
 	@Test
 	public void watchPublishesEvent() {
 		ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);
 
-		setupWatch(eventPublisher, new GetValue());
+		setupWatch(eventPublisher, new GetValue(), configProperties, "/app/");
 
 		verify(eventPublisher, times(1)).publishEvent(any(RefreshEvent.class));
 	}
@@ -53,12 +62,22 @@ public class ConfigWatchTests {
 	public void watchWithNullValueDoesNotPublishEvent() {
 		ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);
 
-		setupWatch(eventPublisher, null);
+		setupWatch(eventPublisher, null, configProperties, "/app/");
 
 		verify(eventPublisher, never()).publishEvent(any(RefreshEvent.class));
 	}
 
-	private void setupWatch(ApplicationEventPublisher eventPublisher, GetValue getValue) {
+	@Test
+	public void watchForFileFormatPublishesEvent() {
+		ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);
+
+		configProperties.setFormat(FILES);
+		setupWatch(eventPublisher, new GetValue(), configProperties, "/config/app.yml" );
+
+		verify(eventPublisher, times(1)).publishEvent(any(RefreshEvent.class));
+	}
+
+	private void setupWatch(ApplicationEventPublisher eventPublisher, GetValue getValue, ConsulConfigProperties configProperties, String context ) {
 		ConsulClient consul = mock(ConsulClient.class);
 		List<GetValue> getValues = null;
 
@@ -67,11 +86,11 @@ public class ConfigWatchTests {
 		}
 
 		Response<List<GetValue>> response = new Response<>(getValues, 1L, false, 1L);
-		when(consul.getKVValues(eq("/app/"), any(QueryParams.class))).thenReturn(response);
+		when(consul.getKVValues(eq(context), any(QueryParams.class))).thenReturn(response);
 
-		ConfigWatch watch = new ConfigWatch(new ConsulConfigProperties(), Arrays.asList("/app/"), consul);
+		ConfigWatch watch = new ConfigWatch(configProperties, Arrays.asList(context), consul);
 		watch.setApplicationEventPublisher(eventPublisher);
-		watch.getConsulIndexes().put("/app/", 0L);
+		watch.getConsulIndexes().put(context, 0L);
 		watch.start();
 
 		watch.watchConfigKeyValues();