Added override handling of arrays/maps in properties and yaml endpoints

fixes #385
This commit is contained in:
Daniel Frey
2016-05-03 13:18:12 -04:00
committed by Dave Syer
parent 7430e7deda
commit 2323878eaa
2 changed files with 159 additions and 48 deletions

View File

@@ -61,6 +61,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Bartosz Wojtkiewicz
* @author Rafal Zukowski
* @author Ivan Corrales Solera
* @author Daniel Frey
*
*/
@RestController
@@ -307,17 +308,45 @@ public class EnvironmentController {
}
}
private Map<String, Object> convertToProperties(Environment environment) {
Map<String, Object> map = new TreeMap<>();
List<PropertySource> sources = new ArrayList<>(environment.getPropertySources());
private Map<String, Object> convertToProperties(Environment profiles) {
// Map of unique keys containing full map of properties for each unique key
Map<String, Map<String, Object>> map = new TreeMap<>();
List<PropertySource> sources = new ArrayList<>(profiles.getPropertySources());
Collections.reverse(sources);
for (PropertySource source : sources) {
@SuppressWarnings("unchecked")
Map<String, String> value = (Map<String, String>) source.getSource();
map.putAll(value);
Map<String, Object> value = (Map<String, Object>) source.getSource();
for (String key : value.keySet()) {
if (!key.contains("[")) {
// Not an array, add unique key to the map
map.put(key, value);
}
else {
// An existing array might have already been added to the property map
// of an unequal size
// to the current array. Replace the array key in the current map
map.put(key.substring(0, key.indexOf('[') - 1), value);
}
}
}
postProcessProperties(map);
return map;
// Combine all unique keys into a combined map
Map<String, Object> combinedMap = new TreeMap<>();
for (Entry<String, Map<String, Object>> entry : map.entrySet()) {
combinedMap.putAll(entry.getValue());
}
postProcessProperties(combinedMap);
return combinedMap;
}
private void postProcessProperties(Map<String, Object> propertiesMap) {

View File

@@ -15,7 +15,12 @@
*/
package org.springframework.cloud.config.server.environment;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
@@ -40,6 +45,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
* @author Dave Syer
* @author Roy Clarkson
* @author Ivan Corrales Solera
* @author Daniel Frey
*/
public class EnvironmentControllerTests {
@@ -62,7 +68,8 @@ public class EnvironmentControllerTests {
Map<String, Object> map = new HashMap<String, Object>();
map.put("a.b.c", "d");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("a:\n b:\n c: d\n", yaml);
}
@@ -72,9 +79,10 @@ public class EnvironmentControllerTests {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("a.b.c", "d");
this.environment.add(new PropertySource("one", map));
this.environment.addFirst(new PropertySource("two", Collections.singletonMap("a.b.c",
"e")));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
this.environment.addFirst(
new PropertySource("two", Collections.singletonMap("a.b.c", "e")));
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("a:\n b:\n c: e\n", yaml);
}
@@ -92,17 +100,74 @@ public class EnvironmentControllerTests {
map.put("a.b[0]", "c");
map.put("a.b[1]", "d");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("a:\n b:\n - c\n - d\n", yaml);
}
@Test
public void arrayOverridenInEnvironment() throws Exception {
// Add original values first source
Map<String, Object> oneMap = new LinkedHashMap<String, Object>();
oneMap.put("a.b[0]", "c");
oneMap.put("a.b[1]", "d");
oneMap.put("a.b[2]", "z");
this.environment.add(new PropertySource("one", oneMap));
// Add overridden values in second source
Map<String, Object> twoMap = new LinkedHashMap<String, Object>();
twoMap.put("a.b[0]", "f");
twoMap.put("a.b[1]", "h");
this.environment.addFirst(new PropertySource("two", twoMap));
Mockito.when(this.repository.findOne("foo", "bar", "two"))
.thenReturn(this.environment);
Environment environment = this.controller.labelled("foo", "bar", "two");
assertThat(environment, not(nullValue()));
assertThat(environment.getName(), equalTo("foo"));
assertThat(environment.getProfiles(), equalTo(new String[] { "master" }));
assertThat(environment.getLabel(), equalTo("master"));
assertThat(environment.getVersion(), nullValue());
assertThat(environment.getPropertySources(), hasSize(2));
assertThat(environment.getPropertySources().get(0).getName(), equalTo("two"));
assertThat(environment.getPropertySources().get(0).getSource().entrySet(),
hasSize(2));
assertThat(environment.getPropertySources().get(1).getName(), equalTo("one"));
assertThat(environment.getPropertySources().get(1).getSource().entrySet(),
hasSize(3));
}
@Test
public void arrayOverridenInYaml() throws Exception {
// Add original values first source
Map<String, Object> oneMap = new LinkedHashMap<String, Object>();
oneMap.put("a.b[0]", "c");
oneMap.put("a.b[1]", "d");
oneMap.put("a.b[2]", "z");
this.environment.add(new PropertySource("one", oneMap));
// Add overridden values in second source
Map<String, Object> twoMap = new LinkedHashMap<String, Object>();
twoMap.put("a.b[0]", "f");
twoMap.put("a.b[1]", "h");
this.environment.addFirst(new PropertySource("two", twoMap));
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
// Result will not contain original, extra values from oneMap
assertEquals("a:\n b:\n - f\n - h\n", yaml);
}
@Test
public void textAtTopLevelInYaml() throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("document", "blah");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("blah\n", yaml);
}
@@ -113,7 +178,8 @@ public class EnvironmentControllerTests {
map.put("document[0]", "c");
map.put("document[1]", "d");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("- c\n- d\n", yaml);
}
@@ -124,7 +190,8 @@ public class EnvironmentControllerTests {
map.put("document[0].a", "c");
map.put("document[1].a", "d");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("- a: c\n- a: d\n", yaml);
}
@@ -136,11 +203,12 @@ public class EnvironmentControllerTests {
map.put("a.b[0].d", "e");
map.put("a.b[1].c", "d");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertTrue("Wrong output: " + yaml,
"a:\n b:\n - d: e\n c: d\n - c: d\n".equals(yaml)
|| "a:\n b:\n - c: d\n d: e\n - c: d\n".equals(yaml));
|| "a:\n b:\n - c: d\n d: e\n - c: d\n".equals(yaml));
}
@Test
@@ -149,7 +217,8 @@ public class EnvironmentControllerTests {
map.put("b[0].c", "d");
map.put("b[1].c", "d");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("b:\n- c: d\n- c: d\n", yaml);
}
@@ -160,7 +229,8 @@ public class EnvironmentControllerTests {
map.put("x.a.b[0].c", "d");
map.put("x.a.b[1].c", "d");
this.environment.add(new PropertySource("one", map));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
String yaml = this.controller.yaml("foo", "bar", false).getBody();
assertEquals("x:\n a:\n b:\n - c: d\n - c: d\n", yaml);
}
@@ -183,51 +253,57 @@ public class EnvironmentControllerTests {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("foo", "bar");
this.environment.add(new PropertySource("one", map));
this.environment.addFirst(new PropertySource("two", Collections.singletonMap("a.b.c", "${foo}")));
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
this.environment.addFirst(
new PropertySource("two", Collections.singletonMap("a.b.c", "${foo}")));
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
}
@Test
public void mappingForEnvironment() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/foo/bar")).andExpect(
MockMvcResultMatchers.status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/foo/bar"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
public void mappingForLabelledEnvironment() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", "other")).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", "other"))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/foo/bar/other")).andExpect(
MockMvcResultMatchers.status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/foo/bar/other"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
public void mappingForYaml() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/foo-bar.yml"))
.andExpect(
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN))
.andExpect(
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN))
.andExpect(MockMvcResultMatchers.content().string("{}\n"));
}
@Test
public void mappingForJson() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/foo-bar.json"))
.andExpect(
MockMvcResultMatchers.content().contentType(
MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.content().string("{}"));
.andExpect(MockMvcResultMatchers.content()
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.content().string("{}"));
;
}
@Test
public void mappingForLabelledYaml() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", "other")).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", "other"))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.yml")).andExpect(
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
@@ -235,7 +311,8 @@ public class EnvironmentControllerTests {
@Test
public void mappingForLabelledProperties() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", "other")).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", "other"))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.properties")).andExpect(
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
@@ -243,7 +320,8 @@ public class EnvironmentControllerTests {
@Test
public void mappingForProperties() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/foo-bar.properties")).andExpect(
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
@@ -251,16 +329,18 @@ public class EnvironmentControllerTests {
@Test
public void mappingForLabelledYamlWithHyphen() throws Exception {
Mockito.when(this.repository.findOne("foo-bar-foo2-bar2", "spam", "other")).thenReturn(
this.environment);
Mockito.when(this.repository.findOne("foo-bar-foo2-bar2", "spam", "other"))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar-foo2-bar2-spam.yml")).andExpect(
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar-foo2-bar2-spam.yml"))
.andExpect(MockMvcResultMatchers.content()
.contentType(MediaType.TEXT_PLAIN));
}
@Test
public void mappingforLabelledJsonProperties() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", "other")).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", "other"))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.json")).andExpect(
MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON));
@@ -268,7 +348,8 @@ public class EnvironmentControllerTests {
@Test
public void mappingforJsonProperties() throws Exception {
Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment);
Mockito.when(this.repository.findOne("foo", "bar", null))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/foo-bar.json")).andExpect(
MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON));
@@ -276,11 +357,12 @@ public class EnvironmentControllerTests {
@Test
public void mappingForLabelledJsonPropertiesWithHyphen() throws Exception {
Mockito.when(this.repository.findOne("foo-bar-foo2-bar2", "spam", "other")).thenReturn(
this.environment);
Mockito.when(this.repository.findOne("foo-bar-foo2-bar2", "spam", "other"))
.thenReturn(this.environment);
MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar-foo2-bar2-spam.json")).andExpect(
MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON));
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar-foo2-bar2-spam.json"))
.andExpect(MockMvcResultMatchers.content()
.contentType(MediaType.APPLICATION_JSON));
}