Fix YAML output if arrays are nested or contain objects

Fixes gh-92
This commit is contained in:
Dave Syer
2015-02-26 18:11:33 +00:00
parent 105612d30e
commit 76871fdf7c
3 changed files with 73 additions and 26 deletions

View File

@@ -21,9 +21,9 @@ import java.net.URLDecoder;
import java.security.KeyPair;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -225,7 +225,7 @@ public class EncryptionController {
Environment result = new Environment(environment.getName(),
environment.getLabel());
for (PropertySource source : environment.getPropertySources()) {
ConcurrentHashMap<Object, Object> map = new ConcurrentHashMap<Object, Object>(
Map<Object, Object> map = new LinkedHashMap<Object, Object>(
source.getSource());
for (Entry<Object,Object> entry : map.entrySet()) {
Object key = entry.getKey();

View File

@@ -7,7 +7,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.servlet.http.HttpServletResponse;
@@ -16,6 +15,8 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.cloud.configure.Environment;
import org.springframework.cloud.configure.PropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -30,7 +31,9 @@ import org.yaml.snakeyaml.Yaml;
@RestController
@RequestMapping("${spring.cloud.config.server.prefix:}")
public class EnvironmentController {
private static final String MAP_PREFIX = "map";
@Qualifier("MultipleJGitEnvironmentRepository")
private EnvironmentRepository repository;
@@ -63,7 +66,7 @@ public class EnvironmentController {
}
return environment;
}
@RequestMapping("/{name}-{profiles}.properties")
public ResponseEntity<String> properties(@PathVariable String name,
@PathVariable String profiles) throws IOException {
@@ -77,13 +80,14 @@ public class EnvironmentController {
throw new IllegalArgumentException(
"Properties output not supported for name or profiles containing hyphens");
}
Properties properties = convertToProperties(labelled(name, profiles, label));
Map<String, Object> properties = convertToProperties(labelled(name, profiles,
label));
return getSuccess(sortLines(properties));
}
private String sortLines(Properties properties) throws IOException {
private String sortLines(Map<String, Object> properties) throws IOException {
List<String> list = new ArrayList<String>();
for (Entry<Object, Object> entry : properties.entrySet()) {
for (Entry<String, Object> entry : properties.entrySet()) {
if (entry.getKey().equals("spring.profiles")) {
continue;
}
@@ -117,12 +121,18 @@ public class EnvironmentController {
LinkedHashMap<String, Object> target = new LinkedHashMap<String, Object>();
PropertiesConfigurationFactory<Map<String, Object>> factory = new PropertiesConfigurationFactory<Map<String, Object>>(
target);
Properties properties = convertToProperties(labelled(name, profiles, label));
Map<String, Object> data = convertToProperties(labelled(name, profiles,
label));
LinkedHashMap<String, Object> properties = new LinkedHashMap<String, Object>();
for (String key : data.keySet()) {
properties.put(MAP_PREFIX + "." + key, data.get(key));
}
addArrays(target, properties);
factory.setProperties(properties);
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(new MapPropertySource("properties", properties));
factory.setPropertySources(propertySources);
factory.bindPropertiesToTarget();
Map<String, Object> input = factory.getObject();
return getSuccess(new Yaml().dumpAsMap(input));
return getSuccess(new Yaml().dumpAsMap(target.get(MAP_PREFIX)));
}
@ExceptionHandler(IllegalArgumentException.class)
@@ -145,13 +155,14 @@ public class EnvironmentController {
* @param target the target Map
* @param properties the properties (with key names to check)
*/
private void addArrays(LinkedHashMap<String, Object> target, Properties properties) {
for (String key : properties.stringPropertyNames()) {
private void addArrays(LinkedHashMap<String, Object> target,
Map<String, Object> properties) {
for (String key : properties.keySet()) {
int index = key.indexOf("[");
Map<String, Object> current = target;
if (index > 0) {
String stem = key.substring(0, index);
String[] keys = StringUtils.split(stem, ".");
String[] keys = StringUtils.delimitedListToStringArray(stem, ".");
for (int i = 0; i < keys.length - 1; i++) {
if (current.get(keys[i]) == null) {
LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
@@ -174,24 +185,27 @@ public class EnvironmentController {
int position = Integer
.valueOf(key.substring(index + 1, key.indexOf("]")));
while (position >= value.size()) {
value.add("");
if (key.indexOf("].", index) > 0) {
value.add(new LinkedHashMap<String, Object>());
}
else {
value.add("");
}
}
}
}
}
private Properties convertToProperties(Environment profiles) {
Properties map = new Properties();
List<PropertySource> sources = new ArrayList<PropertySource>(profiles.getPropertySources());
private Map<String, Object> convertToProperties(Environment profiles) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
List<PropertySource> sources = new ArrayList<PropertySource>(
profiles.getPropertySources());
Collections.reverse(sources);
for (PropertySource source : sources) {
@SuppressWarnings("unchecked")
Map<String, String> value = (Map<String, String>) source.getSource();
map.putAll(value);
}
for (Entry<Object, Object> entry : map.entrySet()) {
map.put(entry.getKey(), entry.getValue().toString());
}
return map;
}

View File

@@ -28,9 +28,6 @@ import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.springframework.cloud.configure.Environment;
import org.springframework.cloud.configure.PropertySource;
import org.springframework.cloud.configure.server.EncryptionController;
import org.springframework.cloud.configure.server.EnvironmentController;
import org.springframework.cloud.configure.server.EnvironmentRepository;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@@ -65,7 +62,7 @@ public class EnvironmentControllerTests {
@Test
public void propertyOverrideInYaml() throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("a.b.c", "d");
environment.add(new PropertySource("one", map));
environment.addFirst(new PropertySource("two", Collections.singletonMap("a.b.c", "e")));
@@ -85,6 +82,42 @@ public class EnvironmentControllerTests {
assertEquals("a:\n b:\n - c\n - d\n", yaml);
}
@Test
public void arrayOfObjectInYaml() throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("a.b[0].c", "d");
map.put("a.b[0].d", "e");
map.put("a.b[1].c", "d");
environment.add(new PropertySource("one", map));
Mockito.when(repository.findOne("foo", "bar", "master")).thenReturn(environment);
String yaml = controller.yaml("foo", "bar").getBody();
assertEquals("a:\n b:\n - d: e\n c: d\n - c: d\n", yaml);
}
@Test
public void arrayOfObjectAtTopLevelInYaml() throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("b[0].c", "d");
map.put("b[0].d", "e");
map.put("b[1].c", "d");
environment.add(new PropertySource("one", map));
Mockito.when(repository.findOne("foo", "bar", "master")).thenReturn(environment);
String yaml = controller.yaml("foo", "bar").getBody();
assertEquals("b:\n- c: d\n d: e\n- c: d\n", yaml);
}
@Test
public void arrayOfObjectNestedLevelInYaml() throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("x.a.b[0].c", "d");
map.put("x.a.b[0].d", "e");
map.put("x.a.b[1].c", "d");
environment.add(new PropertySource("one", map));
Mockito.when(repository.findOne("foo", "bar", "master")).thenReturn(environment);
String yaml = controller.yaml("foo", "bar").getBody();
assertEquals("x:\n a:\n b:\n - c: d\n d: e\n - c: d\n", yaml);
}
@Test
public void mappingForEnvironment() throws Exception {
Mockito.when(repository.findOne("foo", "bar", "master")).thenReturn(environment);