Add .properties and .yml endpoint
This commit is contained in:
@@ -1,17 +1,36 @@
|
||||
|
||||
package org.springframework.cloud.config.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
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;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.bind.PropertiesConfigurationFactory;
|
||||
import org.springframework.cloud.config.Environment;
|
||||
import org.springframework.cloud.config.PropertySource;
|
||||
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.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
@RestController
|
||||
public class EnvironmentController {
|
||||
|
||||
private EnvironmentRepository repository;
|
||||
|
||||
|
||||
private EncryptionController encryption;
|
||||
|
||||
@Autowired
|
||||
@@ -22,14 +41,142 @@ public class EnvironmentController {
|
||||
this.encryption = encryption;
|
||||
}
|
||||
|
||||
@RequestMapping("/{name}/{env}")
|
||||
public Environment master(@PathVariable String name, @PathVariable String env) {
|
||||
return properties(name, env, "master");
|
||||
@RequestMapping("/{name}/{profiles:.*[^-].*}")
|
||||
public Environment master(@PathVariable String name, @PathVariable String profiles) {
|
||||
return labelled(name, profiles, "master");
|
||||
}
|
||||
|
||||
@RequestMapping("/{name}/{env}/{label}")
|
||||
public Environment properties(@PathVariable String name, @PathVariable String env, @PathVariable String label) {
|
||||
return encryption.decrypt(repository.findOne(name, env, label));
|
||||
@RequestMapping("/{name}/{profiles}/{label}")
|
||||
public Environment labelled(@PathVariable String name, @PathVariable String profiles,
|
||||
@PathVariable String label) {
|
||||
return encryption.decrypt(repository.findOne(name, profiles, label));
|
||||
}
|
||||
|
||||
@RequestMapping("/{name}-{profiles}.properties")
|
||||
public ResponseEntity<String> properties(@PathVariable String name,
|
||||
@PathVariable String profiles) throws IOException {
|
||||
return labelledProperties(name, profiles, "master");
|
||||
}
|
||||
|
||||
@RequestMapping("/{label}/{name}-{profiles}.properties")
|
||||
public ResponseEntity<String> labelledProperties(@PathVariable String name,
|
||||
@PathVariable String profiles, @PathVariable String label) throws IOException {
|
||||
if (name.contains("-") || profiles.contains("-")) {
|
||||
throw new IllegalArgumentException("Properties output not supported for name or profiles containing hyphens");
|
||||
}
|
||||
Properties properties = convertToProperties(labelled(name, profiles, label));
|
||||
return getSuccess(sortLines(properties));
|
||||
}
|
||||
|
||||
private String sortLines(Properties properties) throws IOException {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (Entry<Object, Object> entry : properties.entrySet()) {
|
||||
if (entry.getKey().equals("spring.profiles")) {
|
||||
continue;
|
||||
}
|
||||
String line = entry.getKey() + ": " + entry.getValue();
|
||||
list.add(line);
|
||||
}
|
||||
Collections.sort(list);
|
||||
StringBuilder output = new StringBuilder();
|
||||
for (String item : list) {
|
||||
if (output.length() > 0) {
|
||||
output.append("\n");
|
||||
}
|
||||
output.append(item);
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
@RequestMapping({ "/{name}-{profiles}.yml", "/{name}-{profiles}.yaml" })
|
||||
public ResponseEntity<String> yaml(@PathVariable String name, @PathVariable String profiles)
|
||||
throws Exception {
|
||||
return labelledYaml(name, profiles, "master");
|
||||
}
|
||||
|
||||
@RequestMapping({ "/{label}/{name}-{profiles}.yml", "/{label}/{name}-{profiles}.yaml" })
|
||||
public ResponseEntity<String> labelledYaml(@PathVariable String name,
|
||||
@PathVariable String profiles, @PathVariable String label) throws Exception {
|
||||
if (name.contains("-") || profiles.contains("-")) {
|
||||
throw new IllegalArgumentException("YAML output not supported for name or profiles containing hyphens");
|
||||
}
|
||||
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));
|
||||
addArrays(target, properties);
|
||||
factory.setProperties(properties);
|
||||
factory.bindPropertiesToTarget();
|
||||
Map<String, Object> input = factory.getObject();
|
||||
return getSuccess(new Yaml().dumpAsMap(input));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public void illegalArgument(HttpServletResponse response) throws IOException {
|
||||
response.sendError(HttpStatus.BAD_REQUEST.value());
|
||||
}
|
||||
|
||||
private ResponseEntity<String> getSuccess(String body) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
return new ResponseEntity<String>(body, headers, 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(LinkedHashMap<String, Object> target, Properties properties) {
|
||||
for (String key : properties.stringPropertyNames()) {
|
||||
int index = key.indexOf("[");
|
||||
Map<String, Object> current = target;
|
||||
if (index > 0) {
|
||||
String stem = key.substring(0, index);
|
||||
String[] keys = StringUtils.split(stem, ".");
|
||||
for (int i = 0; i < keys.length - 1; i++) {
|
||||
if (current.get(keys[i]) == null) {
|
||||
LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
current.put(keys[i], map);
|
||||
current = map;
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) current
|
||||
.get(keys[i]);
|
||||
current = map;
|
||||
}
|
||||
}
|
||||
String name = keys[keys.length - 1];
|
||||
if (current.get(name) == null) {
|
||||
current.put(name, new ArrayList<Object>());
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> value = (List<Object>) current.get(name);
|
||||
int position = Integer
|
||||
.valueOf(key.substring(index + 1, key.indexOf("]")));
|
||||
while (position >= value.size()) {
|
||||
value.add("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Properties convertToProperties(Environment profiles) {
|
||||
Properties map = new Properties();
|
||||
for (PropertySource source : profiles.getPropertySources()) {
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.config.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.cloud.config.Environment;
|
||||
import org.springframework.cloud.config.PropertySource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EnvironmentControllerTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
private EnvironmentRepository repository = Mockito.mock(EnvironmentRepository.class);
|
||||
|
||||
private EnvironmentController controller = new EnvironmentController(repository,
|
||||
new EncryptionController());
|
||||
|
||||
private Environment environment = new Environment("foo", "master");
|
||||
|
||||
@Test
|
||||
public void vanillaYaml() throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("a.b.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 c: d\n", yaml);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayInYaml() throws Exception {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
map.put("a.b[0]", "c");
|
||||
map.put("a.b[1]", "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 - c\n - d\n", yaml);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingForEnvironment() throws Exception {
|
||||
Mockito.when(repository.findOne("foo", "bar", "master")).thenReturn(environment);
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
mvc.perform(MockMvcRequestBuilders.get("/foo/bar")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingForLabelledEnvironment() throws Exception {
|
||||
Mockito.when(repository.findOne("foo", "bar", "other")).thenReturn(environment);
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
mvc.perform(MockMvcRequestBuilders.get("/foo/bar/other")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingForYaml() throws Exception {
|
||||
Mockito.when(repository.findOne("foo", "bar", "master")).thenReturn(environment);
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
mvc.perform(MockMvcRequestBuilders.get("/foo-bar.yml")).andExpect(
|
||||
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingForLabelledYaml() throws Exception {
|
||||
Mockito.when(repository.findOne("foo", "bar", "other")).thenReturn(environment);
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.yml")).andExpect(
|
||||
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingForLabelledProperties() throws Exception {
|
||||
Mockito.when(repository.findOne("foo", "bar", "other")).thenReturn(environment);
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar.properties")).andExpect(
|
||||
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingForProperties() throws Exception {
|
||||
Mockito.when(repository.findOne("foo", "bar", "master")).thenReturn(environment);
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
mvc.perform(MockMvcRequestBuilders.get("/foo-bar.properties")).andExpect(
|
||||
MockMvcResultMatchers.content().contentType(MediaType.TEXT_PLAIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingForLabelledYamlWithHyphen() throws Exception {
|
||||
Mockito.when(repository.findOne("foo", "bar-spam", "other")).thenReturn(environment);
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
mvc.perform(MockMvcRequestBuilders.get("/other/foo-bar-spam.yml")).andExpect(
|
||||
MockMvcResultMatchers.status().isBadRequest());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user