From 3bfdd4b61c23dfca68d3883eca95b88bef49a34c Mon Sep 17 00:00:00 2001 From: Ian Bondoc Date: Fri, 30 Sep 2016 02:16:26 +1300 Subject: [PATCH] Support nested arrays when translating properties to map when generating yml or json Fixes gh-518 --- .../environment/EnvironmentController.java | 238 ++++++++++++------ .../EnvironmentControllerTests.java | 234 ++++++++++++----- 2 files changed, 328 insertions(+), 144 deletions(-) diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java index 2e27743c..b5f6e53d 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java @@ -31,17 +31,12 @@ import java.util.TreeMap; import javax.servlet.http.HttpServletResponse; -import org.springframework.boot.bind.PropertiesConfigurationFactory; import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.config.environment.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; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -62,14 +57,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; * @author Rafal Zukowski * @author Ivan Corrales Solera * @author Daniel Frey + * @author Ian Bondoc * */ @RestController @RequestMapping(method = RequestMethod.GET, path = "${spring.cloud.config.server.prefix:}") public class EnvironmentController { - private static final String MAP_PREFIX = "map"; - private EnvironmentRepository repository; private ObjectMapper objectMapper; @@ -152,7 +146,7 @@ public class EnvironmentController { throws Exception { validateProfiles(profiles); Environment environment = labelled(name, profiles, label); - Map properties = convertToMap(environment, resolvePlaceholders); + Map properties = convertToMap(environment); String json = this.objectMapper.writeValueAsString(properties); if (resolvePlaceholders) { json = resolvePlaceholders(prepareEnvironment(environment), json); @@ -188,7 +182,7 @@ public class EnvironmentController { throws Exception { validateProfiles(profiles); Environment environment = labelled(name, profiles, label); - Map result = convertToMap(environment, resolvePlaceholders); + Map result = convertToMap(environment); if (this.stripDocument && result.size() == 1 && result.keySet().iterator().next().equals("document")) { Object value = result.get("document"); @@ -208,26 +202,25 @@ public class EnvironmentController { return getSuccess(yaml); } - private Map convertToMap(Environment input, boolean resolvePlaceholders) throws BindException { - Map target = new LinkedHashMap<>(); - PropertiesConfigurationFactory> factory = new PropertiesConfigurationFactory<>( - target); - if (!resolvePlaceholders) { - factory.setResolvePlaceholders(false); + /** + * Method {@code convertToMap} converts an {@code Environment} to a nested Map which represents a yml/json structure. + * + * @param input the environment to be converted + * @return the nested map containing the environment's properties + */ + private Map convertToMap(Environment input) { + // First use the current convertToProperties to get a flat Map from the environment + Map properties = convertToProperties(input); + + // The root map which holds all the first level properties + Map rootMap = new LinkedHashMap<>(); + for (Map.Entry entry : properties.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + PropertyNavigator nav = new PropertyNavigator(key); + nav.setMapValue(rootMap, value); } - Map data = convertToProperties(input); - LinkedHashMap properties = new LinkedHashMap<>(); - for (String key : data.keySet()) { - properties.put(MAP_PREFIX + "." + key, data.get(key)); - } - addArrays(target, properties); - MutablePropertySources propertySources = new MutablePropertySources(); - propertySources.addFirst(new MapPropertySource("properties", properties)); - factory.setPropertySources(propertySources); - factory.bindPropertiesToTarget(); - @SuppressWarnings("unchecked") - Map result = (Map) target.get(MAP_PREFIX); - return result == null ? new LinkedHashMap() : result; + return rootMap; } @ExceptionHandler(NoSuchLabelException.class) @@ -262,55 +255,6 @@ public class EnvironmentController { return new ResponseEntity<>(body, getHttpHeaders(mediaType), HttpStatus.OK); } - /** - * Create Lists of the right size for any YAML arrays that are going to need to be - * bound. Some of this might be do-able in RelaxedDataBinder, but we need to do it - * here for now. Only supports arrays at leaf level currently (i.e. the properties - * keys end in [*]). - * - * @param target the target Map - * @param properties the properties (with key names to check) - */ - private void addArrays(Map target, Map properties) { - for (String key : properties.keySet()) { - int index = key.indexOf("["); - Map current = target; - if (index > 0) { - String stem = key.substring(0, index); - String[] keys = StringUtils.delimitedListToStringArray(stem, "."); - for (int i = 0; i < keys.length - 1; i++) { - if (current.get(keys[i]) == null) { - LinkedHashMap map = new LinkedHashMap<>(); - current.put(keys[i], map); - current = map; - } - else { - @SuppressWarnings("unchecked") - Map map = (Map) current - .get(keys[i]); - current = map; - } - } - String name = keys[keys.length - 1]; - if (current.get(name) == null) { - current.put(name, new ArrayList<>()); - } - @SuppressWarnings("unchecked") - List value = (List) current.get(name); - int position = Integer - .valueOf(key.substring(index + 1, key.indexOf("]"))); - while (position >= value.size()) { - if (key.indexOf("].", index) > 0) { - value.add(new LinkedHashMap()); - } - else { - value.add(""); - } - } - } - } - } - private Map convertToProperties(Environment profiles) { // Map of unique keys containing full map of properties for each unique @@ -367,4 +311,144 @@ public class EnvironmentController { } } + /** + * Class {@code PropertyNavigator} is used to navigate through the property key and create necessary Maps and Lists + * making up the nested structure to finally set the property value at the leaf node. + *

+ * The following rules in yml/json are implemented: + *

+	 * 1. an array element can be:
+	 *    - a value (leaf)
+	 *    - a map
+	 *    - a nested array
+	 * 2. a map value can be:
+	 *    - a value (leaf)
+	 *    - a nested map
+	 *    - an array
+	 * 
+ */ + private static class PropertyNavigator { + + private enum NodeType {LEAF, MAP, ARRAY} + + private final String propertyKey; + private int currentPos; + private NodeType valueType; + + private PropertyNavigator(String propertyKey) { + this.propertyKey = propertyKey; + currentPos = -1; + valueType = NodeType.MAP; + } + + private void setMapValue(Map map, Object value) { + String key = getKey(); + if (NodeType.MAP.equals(valueType)) { + Map nestedMap = (Map) map.get(key); + if (nestedMap == null) { + nestedMap = new LinkedHashMap<>(); + map.put(key, nestedMap); + } + setMapValue(nestedMap, value); + } else if (NodeType.ARRAY.equals(valueType)) { + List list = (List) map.get(key); + if (list == null) { + list = new ArrayList<>(); + map.put(key, list); + } + setListValue(list, value); + } else { + map.put(key, value); + } + } + + private void setListValue(List list, Object value) { + int index = getIndex(); + // Fill missing elements if needed + while (list.size() <= index) { + list.add(null); + } + if (NodeType.MAP.equals(valueType)) { + Map map = (Map) list.get(index); + if (map == null) { + map = new LinkedHashMap<>(); + list.set(index, map); + } + setMapValue(map, value); + } else if (NodeType.ARRAY.equals(valueType)) { + List nestedList = (List) list.get(index); + if (nestedList == null) { + nestedList = new ArrayList<>(); + list.set(index, nestedList); + } + setListValue(nestedList, value); + } else { + list.set(index, value); + } + } + + private int getIndex() { + // Consider [ + int start = currentPos + 1; + + for (int i = start; i < propertyKey.length(); i++) { + char c = propertyKey.charAt(i); + if (c == ']') { + currentPos = i; + break; + } else if (!Character.isDigit(c)) { + throw new IllegalArgumentException("Invalid key: " + propertyKey); + } + } + // If no closing ] or if '[]' + if (currentPos < start || currentPos == start) { + throw new IllegalArgumentException("Invalid key: " + propertyKey); + } else { + int index = Integer.parseInt(propertyKey.substring(start, currentPos)); + // Skip the closing ] + currentPos++; + if (currentPos == propertyKey.length()) { + valueType = NodeType.LEAF; + } else { + switch (propertyKey.charAt(currentPos)) { + case '.': + valueType = NodeType.MAP; + break; + case '[': + valueType = NodeType.ARRAY; + break; + default: + throw new IllegalArgumentException("Invalid key: " + propertyKey); + } + } + return index; + } + } + + private String getKey() { + // Consider initial value or previous char '.' or '[' + int start = currentPos + 1; + for (int i = start; i < propertyKey.length(); i++) { + char currentChar = propertyKey.charAt(i); + if (currentChar == '.') { + valueType = NodeType.MAP; + currentPos = i; + break; + } else if (currentChar == '[') { + valueType = NodeType.ARRAY; + currentPos = i; + break; + } + } + // If there's no delimiter then it's a key of a leaf + if (currentPos < start) { + currentPos = propertyKey.length(); + valueType = NodeType.LEAF; + // Else if we encounter '..' or '.[' or start of the property is . or [ then it's invalid + } else if (currentPos == start) { + throw new IllegalArgumentException("Invalid key: " + propertyKey); + } + return propertyKey.substring(start, currentPos); + } + } } diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java index 98fabb77..4e492e4f 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java @@ -15,14 +15,6 @@ */ 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; import java.util.HashMap; import java.util.LinkedHashMap; @@ -34,6 +26,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mockito; + import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.config.environment.PropertySource; import org.springframework.http.MediaType; @@ -42,11 +35,21 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +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; + /** * @author Dave Syer * @author Roy Clarkson * @author Ivan Corrales Solera * @author Daniel Frey + * @author Ian Bondoc */ public class EnvironmentControllerTests { @@ -74,7 +77,8 @@ public class EnvironmentControllerTests { Map map = new HashMap(); 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); } @@ -84,8 +88,10 @@ public class EnvironmentControllerTests { Map map = new LinkedHashMap(); 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); } @@ -101,7 +107,8 @@ public class EnvironmentControllerTests { map.put("A", "Z"); map.put("S", 3); this.environment.addFirst(new PropertySource("two", 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: Z\nS: 3\nY: 0\n", yaml); } @@ -128,14 +135,16 @@ public class EnvironmentControllerTests { } @Test - public void placeholdersNotResolvedInYamlFromSystemPropertiesWhenNotFlagged() throws Exception { + public void placeholdersNotResolvedInYamlFromSystemPropertiesWhenNotFlagged() + throws Exception { whenPlaceholdersSystemProps(); String yaml = this.controller.yaml("foo", "bar", false).getBody(); assertEquals("a:\n b:\n c: ${foo}\n", yaml); } @Test - public void placeholdersNotResolvedInYamlFromSystemPropertiesWhenNotFlaggedWithDefault() throws Exception { + public void placeholdersNotResolvedInYamlFromSystemPropertiesWhenNotFlaggedWithDefault() + throws Exception { whenPlaceholdersSystemPropsWithDefault(); String yaml = this.controller.yaml("foo", "bar", false).getBody(); // If there is a default value we prevent the placeholder being resolved @@ -143,7 +152,8 @@ public class EnvironmentControllerTests { } @Test - public void placeholdersResolvedInYamlFromSystemPropertiesWhenFlagged() throws Exception { + public void placeholdersResolvedInYamlFromSystemPropertiesWhenFlagged() + throws Exception { whenPlaceholdersSystemPropsWithDefault(); String yaml = this.controller.yaml("foo", "bar", true).getBody(); // If there is a default value we do not prevent the placeholder being resolved @@ -156,7 +166,8 @@ 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); } @@ -176,7 +187,8 @@ public class EnvironmentControllerTests { twoMap.put("a.b[1]", "h"); this.environment.addFirst(new PropertySource("two", twoMap)); - Mockito.when(this.repository.findOne("foo", "bar", "two")).thenReturn(this.environment); + 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")); @@ -185,9 +197,11 @@ public class EnvironmentControllerTests { 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(0).getSource().entrySet(), + hasSize(2)); assertThat(environment.getPropertySources().get(1).getName(), equalTo("one")); - assertThat(environment.getPropertySources().get(1).getSource().entrySet(), hasSize(3)); + assertThat(environment.getPropertySources().get(1).getSource().entrySet(), + hasSize(3)); } @Test @@ -205,7 +219,8 @@ public class EnvironmentControllerTests { twoMap.put("a.b[1]", "h"); this.environment.addFirst(new PropertySource("two", twoMap)); - 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(); // Result will not contain original, extra values from oneMap @@ -217,7 +232,8 @@ public class EnvironmentControllerTests { Map map = new LinkedHashMap(); 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); } @@ -228,7 +244,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); } @@ -239,7 +256,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); } @@ -251,10 +269,61 @@ 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)); + 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)); + } + + @Test + public void nestedArraysOfObjectInYaml() throws Exception { + Map map = new LinkedHashMap(); + map.put("a.b[0].c", "x"); + map.put("a.b[2].e[0].d", "z"); + map.put("a.b[0].d[2]", "yy"); + map.put("a.b[0].d[0]", "xx"); + map.put("a.b[2].c", "y"); + map.put("a.b[3][0]", "r"); + map.put("a.b[3][1]", "s"); + this.environment.add(new PropertySource("one", map)); + Mockito.when(this.repository.findOne("foo", "bar", null)) + .thenReturn(this.environment); + String yaml = this.controller.yaml("foo", "bar", false).getBody(); + String expected = // @formatter:off + "a:\n" + + " b:\n" + + " - c: x\n" + + " d:\n" + + " - xx\n" + + " - null\n" + + " - yy\n" + + " - null\n" + + " - c: y\n" + + " e:\n" + + " - d: z\n" + + " - - r\n" + + " - s\n"; +// @formatter:on + assertThat("Wrong output: " + yaml, yaml, is(expected)); + } + + @Test + public void nestedArraysOfObjectInJson() throws Exception { + Map map = new LinkedHashMap(); + map.put("a.b[0].c", "x"); + map.put("a.b[0].d[0]", "xx"); + map.put("a.b[0].d[1]", "yy"); + map.put("a.b[1].c", "y"); + map.put("a.b[1].e[0].d", "z"); + this.environment.add(new PropertySource("one", map)); + Mockito.when(this.repository.findOne("foo", "bar", null)) + .thenReturn(this.environment); + String json = this.controller.jsonProperties("foo", "bar", false).getBody(); + System.err.println(json); + assertThat("Wrong output: " + json, json, is( + "{\"a\":{\"b\":[{\"c\":\"x\",\"d\":[\"xx\",\"yy\"]},{\"c\":\"y\",\"e\":[{\"d\":\"z\"}]}]}}")); } @Test @@ -263,7 +332,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); } @@ -274,7 +344,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); } @@ -294,21 +365,24 @@ public class EnvironmentControllerTests { } @Test - public void placeholdersNotResolvedInPropertiesFromSystemProperties() throws Exception { + public void placeholdersNotResolvedInPropertiesFromSystemProperties() + throws Exception { whenPlaceholdersSystemProps(); String text = this.controller.properties("foo", "bar", true).getBody(); assertEquals("a.b.c: ${foo}", text); } @Test - public void placeholdersNotResolvedInPropertiesFromSystemPropertiesWhenNotFlagged() throws Exception { + public void placeholdersNotResolvedInPropertiesFromSystemPropertiesWhenNotFlagged() + throws Exception { whenPlaceholdersSystemProps(); String text = this.controller.properties("foo", "bar", false).getBody(); assertEquals("a.b.c: ${foo}", text); } @Test - public void placeholdersNotResolvedInPropertiesFromSystemPropertiesWhenNotFlaggedWithDefault() throws Exception { + public void placeholdersNotResolvedInPropertiesFromSystemPropertiesWhenNotFlaggedWithDefault() + throws Exception { whenPlaceholdersSystemPropsWithDefault(); String text = this.controller.properties("foo", "bar", false).getBody(); assertEquals("a.b.c: ${foo:spam}", text); @@ -336,14 +410,16 @@ public class EnvironmentControllerTests { } @Test - public void placeholdersNotResolvedInJsonFromSystemPropertiesWhenNotFlagged() throws Exception { + public void placeholdersNotResolvedInJsonFromSystemPropertiesWhenNotFlagged() + throws Exception { whenPlaceholdersSystemProps(); String json = this.controller.jsonProperties("foo", "bar", false).getBody(); assertEquals("{\"a\":{\"b\":{\"c\":\"${foo}\"}}}", json); } @Test - public void placeholdersNotResolvedInJsonFromSystemPropertiesWhenNotFlaggedWithDefault() throws Exception { + public void placeholdersNotResolvedInJsonFromSystemPropertiesWhenNotFlaggedWithDefault() + throws Exception { whenPlaceholdersSystemPropsWithDefault(); String json = this.controller.jsonProperties("foo", "bar", false).getBody(); // If there is a default value we prevent the placeholder being resolved @@ -351,7 +427,8 @@ public class EnvironmentControllerTests { } @Test - public void placeholdersResolvedInJsonFromSystemPropertiesWhenFlagged() throws Exception { + public void placeholdersResolvedInJsonFromSystemPropertiesWhenFlagged() + throws Exception { whenPlaceholdersSystemPropsWithDefault(); String json = this.controller.jsonProperties("foo", "bar", true).getBody(); // If there is a default value we do not prevent the placeholder being resolved @@ -362,108 +439,131 @@ public class EnvironmentControllerTests { Map map = new LinkedHashMap(); 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); } private void whenPlaceholdersSystemProps() { System.setProperty("foo", "bar"); - 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); } private void whenPlaceholdersSystemPropsWithDefault() { System.setProperty("foo", "bar"); - this.environment.addFirst(new PropertySource("two", Collections.singletonMap("a.b.c", "${foo:spam}"))); - Mockito.when(this.repository.findOne("foo", "bar", null)).thenReturn(this.environment); + this.environment.addFirst(new PropertySource("two", + Collections.singletonMap("a.b.c", "${foo:spam}"))); + 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() + .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)); + mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.yml")).andExpect( + MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN)); } @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)); + mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.properties")).andExpect( + MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN)); } @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)); + mvc.perform(MockMvcRequestBuilders.get("/foo-bar.properties")).andExpect( + MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN)); } @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)); + .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)); + mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.json")).andExpect( + MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)); } @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)); + mvc.perform(MockMvcRequestBuilders.get("/foo-bar.json")).andExpect( + MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)); } @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)); + .andExpect(MockMvcResultMatchers.content() + .contentType(MediaType.APPLICATION_JSON)); }