Support for git labels with slashes ("/")
Spring MVC can't match a pattern with a slash in it so we support a special character sequence "(_)" which is legal in a URL but hopefully rare in a git label. Fixes gh-154
This commit is contained in:
@@ -88,7 +88,12 @@ filesystem repository, so that the server can clone it and use a local
|
||||
working copy as a cache.
|
||||
|
||||
This repository implementation maps the `{label}` parameter of the
|
||||
HTTP resource to a git label (commit id, branch name or tag).
|
||||
HTTP resource to a git label (commit id, branch name or tag). If the
|
||||
git branch or tag name contains a slash ("/") then the label in the
|
||||
HTTP URL should be specified with the special string "(_)" instead (to
|
||||
avoid ambiguity with other URL paths). Be careful with the brackets in
|
||||
the URL if you are using a command line client like curl (e.g. escape
|
||||
them from the shell with quotes '').
|
||||
|
||||
Spring Cloud Config Server supports a single or multiple git
|
||||
repositories:
|
||||
|
||||
@@ -92,18 +92,25 @@ public class EnvironmentController {
|
||||
@RequestMapping("/{name}/{profiles:.*[^-].*}")
|
||||
public Environment defaultLabel(@PathVariable String name,
|
||||
@PathVariable String profiles) {
|
||||
return labelled(name, profiles, defaultLabel);
|
||||
return labelled(name, profiles, this.defaultLabel);
|
||||
}
|
||||
|
||||
@RequestMapping("/{name}/{profiles}/{label:.*}")
|
||||
public Environment labelled(@PathVariable String name, @PathVariable String profiles,
|
||||
@PathVariable String label) {
|
||||
Environment environment = repository.findOne(name, profiles, label);
|
||||
if (environmentEncryptor != null) {
|
||||
environment = environmentEncryptor.decrypt(environment);
|
||||
if (label==null) {
|
||||
label = this.defaultLabel;
|
||||
}
|
||||
if (!overrides.isEmpty()) {
|
||||
environment.addFirst(new PropertySource("overrides", overrides));
|
||||
if (label!=null && label.contains("(_)")) {
|
||||
// "(_)" is uncommon in a git branch name, but "/" cannot be matched by Spring MVC
|
||||
label = label.replace("(_)", "/");
|
||||
}
|
||||
Environment environment = this.repository.findOne(name, profiles, label);
|
||||
if (this.environmentEncryptor != null) {
|
||||
environment = this.environmentEncryptor.decrypt(environment);
|
||||
}
|
||||
if (!this.overrides.isEmpty()) {
|
||||
environment.addFirst(new PropertySource("overrides", this.overrides));
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
@@ -111,7 +118,7 @@ public class EnvironmentController {
|
||||
@RequestMapping("/{name}-{profiles}.properties")
|
||||
public ResponseEntity<String> properties(@PathVariable String name,
|
||||
@PathVariable String profiles) throws IOException {
|
||||
return labelledProperties(name, profiles, defaultLabel);
|
||||
return labelledProperties(name, profiles, this.defaultLabel);
|
||||
}
|
||||
|
||||
@RequestMapping("/{label}/{name}-{profiles}.properties")
|
||||
@@ -126,7 +133,7 @@ public class EnvironmentController {
|
||||
@RequestMapping("{name}-{profiles}.json")
|
||||
public ResponseEntity<Map<String, Object>> jsonProperties(@PathVariable String name,
|
||||
@PathVariable String profiles) throws Exception {
|
||||
return labelledJsonProperties(name, profiles, defaultLabel);
|
||||
return labelledJsonProperties(name, profiles, this.defaultLabel);
|
||||
}
|
||||
|
||||
@RequestMapping("/{label}/{name}-{profiles}.json")
|
||||
@@ -153,7 +160,7 @@ public class EnvironmentController {
|
||||
@RequestMapping({ "/{name}-{profiles}.yml", "/{name}-{profiles}.yaml" })
|
||||
public ResponseEntity<String> yaml(@PathVariable String name,
|
||||
@PathVariable String profiles) throws Exception {
|
||||
return labelledYaml(name, profiles, defaultLabel);
|
||||
return labelledYaml(name, profiles, this.defaultLabel);
|
||||
}
|
||||
|
||||
@RequestMapping({ "/{label}/{name}-{profiles}.yml", "/{label}/{name}-{profiles}.yaml" })
|
||||
@@ -251,7 +258,7 @@ public class EnvironmentController {
|
||||
else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) current
|
||||
.get(keys[i]);
|
||||
.get(keys[i]);
|
||||
current = map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,31 +16,48 @@
|
||||
|
||||
package org.springframework.cloud.config.server;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.server.EnvironmentControllerIntegrationTests.ControllerConfiguration;
|
||||
import org.springframework.cloud.config.server.encryption.CipherEnvironmentEncryptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
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;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = ControllerConfiguration.class)
|
||||
@WebAppConfiguration
|
||||
public class EnvironmentControllerIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
private MockMvc mvc;
|
||||
private EnvironmentRepository repository = Mockito.mock(EnvironmentRepository.class);;
|
||||
@Autowired
|
||||
private EnvironmentRepository repository;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
Mockito.when(this.repository.getDefaultLabel()).thenReturn("master");
|
||||
this.mvc = MockMvcBuilders.standaloneSetup(
|
||||
new EnvironmentController(this.repository,
|
||||
new CipherEnvironmentEncryptor(null))).build();
|
||||
Mockito.reset(this.repository);
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -49,6 +66,34 @@ public class EnvironmentControllerIntegrationTests {
|
||||
new Environment("foo", "default"));
|
||||
this.mvc.perform(MockMvcRequestBuilders.get("/foo/default")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
Mockito.verify(this.repository).findOne("foo", "default", "master");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesNoLabel() throws Exception {
|
||||
Mockito.when(this.repository.findOne("foo", "default", "master")).thenReturn(
|
||||
new Environment("foo", "default"));
|
||||
this.mvc.perform(MockMvcRequestBuilders.get("/foo-default.properties")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
Mockito.verify(this.repository).findOne("foo", "default", "master");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesLabel() throws Exception {
|
||||
Mockito.when(this.repository.findOne("foo", "default", "label")).thenReturn(
|
||||
new Environment("foo", "default"));
|
||||
this.mvc.perform(MockMvcRequestBuilders.get("/label/foo-default.properties")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
Mockito.verify(this.repository).findOne("foo", "default", "label");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesLabelWithSlash() throws Exception {
|
||||
Mockito.when(this.repository.findOne("foo", "default", "label/spam")).thenReturn(
|
||||
new Environment("foo", "default"));
|
||||
this.mvc.perform(MockMvcRequestBuilders.get("/label(_)spam/foo-default.properties")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
Mockito.verify(this.repository).findOne("foo", "default", "label/spam");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,4 +112,33 @@ public class EnvironmentControllerIntegrationTests {
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentWithLabelContainingSlash() throws Exception {
|
||||
Mockito.when(this.repository.findOne("foo", "default", "feature/puff"))
|
||||
.thenReturn(new Environment("foo", "default"));
|
||||
this.mvc.perform(MockMvcRequestBuilders.get("/foo/default/feature(_)puff"))
|
||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("\"propertySources\":")));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@Import(PropertyPlaceholderAutoConfiguration.class)
|
||||
public static class ControllerConfiguration {
|
||||
|
||||
@Bean
|
||||
public EnvironmentRepository environmentRepository() {
|
||||
EnvironmentRepository repository = Mockito.mock(EnvironmentRepository.class);
|
||||
Mockito.when(repository.getDefaultLabel()).thenReturn("master");
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EnvironmentController controller() {
|
||||
return new EnvironmentController(environmentRepository(),
|
||||
new CipherEnvironmentEncryptor(null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user