Add Zuul Proxy regex serviceId to route mapping
fixes gh-693
This commit is contained in:
committed by
Spencer Gibb
parent
9f9948707a
commit
4a52a7f889
@@ -1093,6 +1093,26 @@ users:
|
||||
listOfServers: example.com,google.com
|
||||
----
|
||||
|
||||
You can provide convention between serviceId and routes using regexmapper.
|
||||
It uses regular expression named group to extract variables from serviceId and inject them
|
||||
into a route pattern.
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
zuul:
|
||||
regexMapper:
|
||||
enabled: true
|
||||
servicePattern: "(?<name>^.+)-(?<version>v.+$)"
|
||||
routePattern: "${version}/${name}"
|
||||
----
|
||||
|
||||
This means that a serviceId "myusers-v1" will be mapped to route "/v1/myusers/**".
|
||||
Any regular expression is accepted but all named group must be present in both servicePattern and routePattern.
|
||||
If servicePattern do not match a serviceId, the default behavior is used. In exemple above,
|
||||
a serviceId "myusers" will be mapped to route "/myusers/**" (no version detected)
|
||||
These feature is disable by default and is only applied to discovered services.
|
||||
|
||||
To add a prefix to all mappings, set `zuul.prefix` to a value, such as
|
||||
`/api`. The proxy prefix is stripped from the request before the
|
||||
request is forwarded by default (switch this behaviour off with
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
@@ -32,8 +33,11 @@ import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEven
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ServiceRouteMapper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.SimpleServiceRouteMapper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
|
||||
import org.springframework.cloud.netflix.zuul.filters.regex.RegExServiceRouteMapper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter;
|
||||
@@ -66,6 +70,9 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
@Autowired
|
||||
private ServerProperties server;
|
||||
|
||||
@Autowired
|
||||
private ServiceRouteMapper serviceRouteMapper;
|
||||
|
||||
@Override
|
||||
public HasFeatures zuulFeature() {
|
||||
return HasFeatures.namedFeature("Zuul (Discovery)", ZuulProxyConfiguration.class);
|
||||
@@ -75,7 +82,7 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
@Override
|
||||
public ProxyRouteLocator routeLocator() {
|
||||
return new ProxyRouteLocator(this.server.getServletPrefix(), this.discovery,
|
||||
this.zuulProperties);
|
||||
this.zuulProperties, serviceRouteMapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -86,8 +93,8 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
|
||||
// pre filters
|
||||
@Bean
|
||||
public PreDecorationFilter preDecorationFilter() {
|
||||
return new PreDecorationFilter(routeLocator(),
|
||||
public PreDecorationFilter preDecorationFilter(ProxyRouteLocator routeLocator) {
|
||||
return new PreDecorationFilter(routeLocator,
|
||||
this.zuulProperties.isAddProxyHeaders());
|
||||
}
|
||||
|
||||
@@ -117,6 +124,27 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
return new ZuulRefreshListener();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "zuul.regexMapper.enabled", matchIfMissing = false)
|
||||
protected static class RegexServiceRouteMapperConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServiceRouteMapper serviceRouteMapper(ZuulProperties props) {
|
||||
return new RegExServiceRouteMapper(props.getRegexMapper().getServicePattern(),
|
||||
props.getRegexMapper().getRoutePattern());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(ServiceRouteMapper.class)
|
||||
protected static class SimpleServiceRouteMapperConfiguration {
|
||||
@Bean
|
||||
public ServiceRouteMapper serviceRouteMapper() {
|
||||
return new SimpleServiceRouteMapper();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class RoutesEndpointConfiguration {
|
||||
|
||||
@@ -55,6 +55,8 @@ public class ProxyRouteLocator implements RouteLocator {
|
||||
|
||||
private String servletPath;
|
||||
|
||||
private ServiceRouteMapper serviceRouteMapper;
|
||||
|
||||
public ProxyRouteLocator(String servletPath, DiscoveryClient discovery,
|
||||
ZuulProperties properties) {
|
||||
if (StringUtils.hasText(servletPath)) { // a servletPath is passed explicitly
|
||||
@@ -75,11 +77,17 @@ public class ProxyRouteLocator implements RouteLocator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.serviceRouteMapper = new SimpleServiceRouteMapper();
|
||||
this.discovery = discovery;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public ProxyRouteLocator(String servletPath, DiscoveryClient discovery,
|
||||
ZuulProperties properties, ServiceRouteMapper serviceRouteMapper) {
|
||||
this(servletPath, discovery, properties);
|
||||
this.serviceRouteMapper = serviceRouteMapper;
|
||||
}
|
||||
|
||||
public void addRoute(String path, String location) {
|
||||
this.staticRoutes.put(path, new ZuulRoute(path, location));
|
||||
resetRoutes();
|
||||
@@ -196,7 +204,7 @@ public class ProxyRouteLocator implements RouteLocator {
|
||||
for (String serviceId : services) {
|
||||
// Ignore specifically ignored services and those that were manually
|
||||
// configured
|
||||
String key = "/" + serviceId + "/**";
|
||||
String key = "/" + mapRouteToService(serviceId) + "/**";
|
||||
if (staticServices.containsKey(serviceId)
|
||||
&& staticServices.get(serviceId).getUrl() == null) {
|
||||
// Explicitly configured with no URL, cannot be ignored
|
||||
@@ -238,6 +246,10 @@ public class ProxyRouteLocator implements RouteLocator {
|
||||
return values;
|
||||
}
|
||||
|
||||
protected String mapRouteToService(String serviceId) {
|
||||
return this.serviceRouteMapper.apply(serviceId);
|
||||
}
|
||||
|
||||
protected void addConfiguredRoutes(Map<String, ZuulRoute> routes) {
|
||||
Map<String, ZuulRoute> routeEntries = this.properties.getRoutes();
|
||||
for (ZuulRoute entry : routeEntries.values()) {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.cloud.netflix.zuul.filters;
|
||||
|
||||
/**
|
||||
* @author Stéphane LEROY
|
||||
*
|
||||
* Provide a way to apply convention between routes and discovered services name.
|
||||
*
|
||||
*/
|
||||
public interface ServiceRouteMapper {
|
||||
|
||||
/**
|
||||
* Take a service Id (its discovered name) and return a route path.
|
||||
*
|
||||
* @param serviceId service discovered name
|
||||
* @return route path
|
||||
*/
|
||||
String apply(String serviceId);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.springframework.cloud.netflix.zuul.filters;
|
||||
|
||||
/**
|
||||
* @author Stéphane Leroy
|
||||
*
|
||||
* A simple passthru service route mapper.
|
||||
*/
|
||||
public class SimpleServiceRouteMapper implements ServiceRouteMapper {
|
||||
@Override
|
||||
public String apply(String serviceId) {
|
||||
return serviceId;
|
||||
}
|
||||
}
|
||||
@@ -21,16 +21,15 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
@@ -57,6 +56,8 @@ public class ZuulProperties {
|
||||
|
||||
private boolean ignoreLocalService = true;
|
||||
|
||||
private RegexMapper regexMapper = new RegexMapper();
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
for (Entry<String, ZuulRoute> entry : this.routes.entrySet()) {
|
||||
@@ -73,6 +74,17 @@ public class ZuulProperties {
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class RegexMapper {
|
||||
private boolean enabled = false;
|
||||
|
||||
private String servicePattern = "(?<name>.*)-(?<version>v.*$)";
|
||||
|
||||
private String routePattern = "${version}/${name}";
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.springframework.cloud.netflix.zuul.filters.regex;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.cloud.netflix.zuul.filters.ServiceRouteMapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Stéphane Leroy
|
||||
*
|
||||
* This service route mapper use Java 7 RegEx named group feature to rewrite a discovered
|
||||
* service Id into a route.
|
||||
*
|
||||
* Ex : If we want to map service Id [rest-service-v1] to /v1/rest-service/** route
|
||||
* service pattern : "(?<name>.*)-(?<version>v.*$)" route pattern : "${version}/${name}"
|
||||
*
|
||||
* /!\ This implementation use Matcher.replaceFirst so only one match will be replace.
|
||||
*/
|
||||
public class RegExServiceRouteMapper implements ServiceRouteMapper {
|
||||
|
||||
/**
|
||||
* A RegExp Pattern that extract needed information from a service ID. Ex :
|
||||
* "(?<name>.*)-(?<version>v.*$)"
|
||||
*/
|
||||
private Pattern servicePattern;
|
||||
/**
|
||||
* A RegExp that refer to named groups define in servicePattern. Ex :
|
||||
* "${version}/${name}"
|
||||
*/
|
||||
private String routePattern;
|
||||
|
||||
public RegExServiceRouteMapper(String servicePattern, String routePattern) {
|
||||
this.servicePattern = Pattern.compile(servicePattern);
|
||||
this.routePattern = routePattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use servicePattern to extract groups and routePattern to construct the route.
|
||||
*
|
||||
* If there is no matches, the serviceId is returned.
|
||||
*
|
||||
* @param serviceId service discovered name
|
||||
* @return route path
|
||||
*/
|
||||
@Override
|
||||
public String apply(String serviceId) {
|
||||
Matcher matcher = servicePattern.matcher(serviceId);
|
||||
String route = matcher.replaceFirst(routePattern);
|
||||
route = cleanRoute(route);
|
||||
return (StringUtils.hasText(route) ? route : serviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route with regex and replace can be a bit messy when used with conditional named
|
||||
* group. We clean here first and trailing '/' and remove multiple consecutive '/'
|
||||
* @param route
|
||||
* @return
|
||||
*/
|
||||
private String cleanRoute(final String route) {
|
||||
String routeToClean = route.replaceAll("/{2,}", "/");
|
||||
if (routeToClean.startsWith("/")) {
|
||||
routeToClean = routeToClean.substring(1);
|
||||
}
|
||||
if (routeToClean.endsWith("/")) {
|
||||
routeToClean = routeToClean.substring(0, routeToClean.length() - 1);
|
||||
}
|
||||
return routeToClean;
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@@ -35,8 +27,17 @@ import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator.ProxyRouteSpec;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.cloud.netflix.zuul.filters.regex.RegExServiceRouteMapper;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
@@ -518,6 +519,34 @@ public class ProxyRouteLocatorTests {
|
||||
assertMapping(routesMap, MYSERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegExServiceRouteMapperNoServiceIdMatches() {
|
||||
given(this.discovery.getServices()).willReturn(Collections.singletonList(MYSERVICE));
|
||||
|
||||
RegExServiceRouteMapper regExServiceRouteMapper = new RegExServiceRouteMapper(properties.getRegexMapper().getServicePattern(),
|
||||
properties.getRegexMapper().getRoutePattern());
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
|
||||
this.properties, regExServiceRouteMapper);
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, MYSERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegExServiceRouteMapperServiceIdMatches() {
|
||||
given(this.discovery.getServices()).willReturn(Collections.singletonList("rest-service-v1"));
|
||||
|
||||
RegExServiceRouteMapper regExServiceRouteMapper = new RegExServiceRouteMapper(properties.getRegexMapper().getServicePattern(),
|
||||
properties.getRegexMapper().getRoutePattern());
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
|
||||
this.properties, regExServiceRouteMapper);
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, "rest-service-v1", "v1/rest-service");
|
||||
}
|
||||
|
||||
|
||||
protected void assertMapping(Map<String, String> routesMap, String serviceId) {
|
||||
assertMapping(routesMap, serviceId, serviceId);
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package org.springframework.cloud.netflix.zuul.filters.regex;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.netflix.zuul.filters.regex.RegExServiceRouteMapperIntegrationTests.SERVICE_ID;
|
||||
|
||||
/**
|
||||
* @author Stéphane Leroy
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = SampleCustomZuulProxyApplication.class)
|
||||
@WebIntegrationTest(value = { "spring.application.name=regex-test-application",
|
||||
"spring.jmx.enabled=true" }, randomPort = true)
|
||||
@TestPropertySource(properties = { "eureka.client.enabled=false",
|
||||
"zuul.regexMapper.enabled=true",
|
||||
"zuul.regexMapper.servicePattern=(?<domain>^.+)-(?<name>.+)-(?<version>v.+$)",
|
||||
"zuul.regexMapper.routePattern=${version}/${domain}/${name}" })
|
||||
@DirtiesContext
|
||||
public class RegExServiceRouteMapperIntegrationTests {
|
||||
|
||||
protected static final String SERVICE_ID = "domain-service-v1";
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
@Autowired
|
||||
private ProxyRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
|
||||
@Test
|
||||
public void getRegexMappedService() {
|
||||
endpoint.reset();
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/v1/domain/service/get/1",
|
||||
HttpMethod.GET, new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("Get 1", result.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStaticRoute() {
|
||||
this.routes.addRoute("/self/**", "http://localhost:" + this.port);
|
||||
endpoint.reset();
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/self/get/1", HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("Get 1", result.getBody());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableZuulProxy
|
||||
@RibbonClient(value = SERVICE_ID, configuration = SimpleRibbonClientConfiguration.class)
|
||||
class SampleCustomZuulProxyApplication {
|
||||
|
||||
@Bean
|
||||
public DiscoveryClient discoveryClient() {
|
||||
DiscoveryClient discoveryClient = mock(DiscoveryClient.class);
|
||||
List<String> services = new ArrayList<>();
|
||||
services.add(SERVICE_ID);
|
||||
when(discoveryClient.getServices()).thenReturn(services);
|
||||
return discoveryClient;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
|
||||
public String get(@PathVariable String id) {
|
||||
return "Get " + id;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleCustomZuulProxyApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class SimpleRibbonClientConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.springframework.cloud.netflix.zuul.filters.regex;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Stéphane Leroy
|
||||
*/
|
||||
public class RegExServiceRouteMapperTests {
|
||||
|
||||
/**
|
||||
* Service pattern that follow convention {domain}-{name}-{version}. The name is
|
||||
* optional
|
||||
*/
|
||||
public static final String SERVICE_PATTERN = "(?<domain>^\\w+)(-(?<name>\\w+)-|-)(?<version>v\\d+$)";
|
||||
public static final String ROUTE_PATTERN = "${version}/${domain}/${name}";
|
||||
|
||||
@Test
|
||||
public void test_return_mapped_route_if_serviceid_matches() {
|
||||
RegExServiceRouteMapper toTest = new RegExServiceRouteMapper(SERVICE_PATTERN,
|
||||
ROUTE_PATTERN);
|
||||
|
||||
assertEquals("service version convention", "v1/rest/service",
|
||||
toTest.apply("rest-service-v1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_return_serviceid_if_no_matches() {
|
||||
RegExServiceRouteMapper toTest = new RegExServiceRouteMapper(SERVICE_PATTERN,
|
||||
ROUTE_PATTERN);
|
||||
|
||||
// No version here
|
||||
assertEquals("No matches for this service id", "rest-service",
|
||||
toTest.apply("rest-service"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_route_should_be_cleaned_before_returned() {
|
||||
// Messy patterns
|
||||
RegExServiceRouteMapper toTest = new RegExServiceRouteMapper(SERVICE_PATTERN
|
||||
+ "(?<nevermatch>.)?", "/${version}/${nevermatch}/${domain}/${name}/");
|
||||
assertEquals("No matches for this service id", "v1/domain/service",
|
||||
toTest.apply("domain-service-v1"));
|
||||
assertEquals("No matches for this service id", "v1/domain",
|
||||
toTest.apply("domain-v1"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user