Add a new endpoint POST /routes (#2915)
* Add a new endpoint POST /routes A list of RouteDefinition can be sent to /routes using POST method to persist multiple routes by a single request. Validation is done before persisting any RouteDefinition
This commit is contained in:
@@ -2952,11 +2952,17 @@ The following table describes the structure of the response:
|
||||
|
||||
|===
|
||||
|
||||
=== Creating and Deleting a Particular Route
|
||||
=== Creating and Deleting a Particular Route Definition
|
||||
|
||||
To create a route, make a `POST` request to `/gateway/routes/{id_route_to_create}` with a JSON body that specifies the fields of the route (see <<gateway-retrieving-information-about-a-particular-route>>).
|
||||
To create a route definition, make a `POST` request to `/gateway/routes/{id_route_to_create}` with a JSON body that specifies the fields of the route (see <<gateway-retrieving-information-about-a-particular-route>>).
|
||||
|
||||
To delete a route, make a `DELETE` request to `/gateway/routes/{id_route_to_delete}`.
|
||||
To delete a route definition, make a `DELETE` request to `/gateway/routes/{id_route_to_delete}`.
|
||||
|
||||
=== Creating multiple Route Definitions
|
||||
|
||||
To create multiple route definitions in a single request, make a `POST` request to `/gateway/routes` with a JSON body that specifies the fields of the route, including the route id (see <<gateway-retrieving-information-about-a-particular-route>>).
|
||||
|
||||
The route definitions will be discarded if any route raises an error during the creation of the routes.
|
||||
|
||||
=== Recap: The List of All endpoints
|
||||
|
||||
|
||||
@@ -145,6 +145,28 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
}
|
||||
|
||||
@PostMapping("/routes")
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<ResponseEntity<Object>> save(@RequestBody List<RouteDefinition> routes) {
|
||||
routes.stream().forEach(routeDef -> {
|
||||
validateRouteDefinition(routeDef);
|
||||
validateRouteId(routeDef);
|
||||
});
|
||||
|
||||
return Flux.fromIterable(routes)
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
log.debug("Saving route: " + routeDefinition);
|
||||
return r;
|
||||
}))).then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
}
|
||||
|
||||
private void validateRouteId(RouteDefinition routeDefinition) {
|
||||
if(routeDefinition.getId() == null) {
|
||||
handleError("Saving multiple routes require specifying the ID for every route");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRouteDefinition(RouteDefinition routeDefinition) {
|
||||
Set<String> unavailableFilterDefinitions = routeDefinition.getFilters().stream().filter(rd -> !isAvailable(rd))
|
||||
.map(FilterDefinition::getName).collect(Collectors.toSet());
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.assertj.core.util.Maps;
|
||||
@@ -141,7 +142,6 @@ public class GatewayControllerEndpointTests {
|
||||
|
||||
@Test
|
||||
public void testPostValidRouteDefinition() {
|
||||
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(URI.create("http://example.org"));
|
||||
|
||||
@@ -162,6 +162,91 @@ public class GatewayControllerEndpointTests {
|
||||
.expectStatus().isCreated();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostMultipleValidRouteDefinitions() {
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(URI.create("http://example.org"));
|
||||
String routeId1 = UUID.randomUUID().toString();
|
||||
testRouteDefinition.setId(routeId1);
|
||||
|
||||
FilterDefinition prefixPathFilterDefinition = new FilterDefinition("PrefixPath=/test-path");
|
||||
FilterDefinition redirectToFilterDefinition = new FilterDefinition("RemoveResponseHeader=Sensitive-Header");
|
||||
FilterDefinition testFilterDefinition = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition.setFilters(
|
||||
Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition("Host=myhost.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition("Method=GET");
|
||||
PredicateDefinition testPredicateDefinition = new PredicateDefinition("Test=value");
|
||||
testRouteDefinition.setPredicates(
|
||||
Arrays.asList(hostRoutePredicateDefinition, methodRoutePredicateDefinition, testPredicateDefinition));
|
||||
|
||||
RouteDefinition testRouteDefinition2 = new RouteDefinition();
|
||||
testRouteDefinition2.setUri(URI.create("http://example-2.org"));
|
||||
String routeId2 = UUID.randomUUID().toString();
|
||||
testRouteDefinition2.setId(routeId2);
|
||||
|
||||
FilterDefinition prefixPathFilterDefinition2 = new FilterDefinition("PrefixPath=/test-path-2");
|
||||
FilterDefinition redirectToFilterDefinition2 = new FilterDefinition("RemoveResponseHeader=Sensitive-Header-2");
|
||||
FilterDefinition testFilterDefinition2 = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition2.setFilters(
|
||||
Arrays.asList(prefixPathFilterDefinition2, redirectToFilterDefinition2, testFilterDefinition2));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition2 = new PredicateDefinition("Host=myhost-2.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition2 = new PredicateDefinition("Method=GET");
|
||||
PredicateDefinition testPredicateDefinition2 = new PredicateDefinition("Test=value-2");
|
||||
testRouteDefinition2.setPredicates(
|
||||
Arrays.asList(hostRoutePredicateDefinition2, methodRoutePredicateDefinition2, testPredicateDefinition2)
|
||||
);
|
||||
|
||||
List<RouteDefinition> multipleRouteDefs = List.of(testRouteDefinition, testRouteDefinition2);
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(multipleRouteDefs)).exchange()
|
||||
.expectStatus().isOk();
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routedefinitions")
|
||||
.accept(MediaType.APPLICATION_JSON).exchange()
|
||||
.expectBody()
|
||||
.jsonPath("[?(@.id in ['%s','%s'])].id".formatted(routeId1, routeId2)).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostMultipleRoutesWithOneWrong_doesntPersistRouteDefinitions() {
|
||||
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(URI.create("http://example.org"));
|
||||
String routeId1 = UUID.randomUUID().toString();
|
||||
testRouteDefinition.setId(routeId1);
|
||||
|
||||
FilterDefinition prefixPathFilterDefinition = new FilterDefinition("PrefixPath=/test-path");
|
||||
FilterDefinition redirectToFilterDefinition = new FilterDefinition("RemoveResponseHeader=Sensitive-Header");
|
||||
FilterDefinition testFilterDefinition = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition.setFilters(
|
||||
Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition("Host=myhost.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition("Method=GET");
|
||||
PredicateDefinition testPredicateDefinition = new PredicateDefinition("Test=value");
|
||||
testRouteDefinition.setPredicates(
|
||||
Arrays.asList(hostRoutePredicateDefinition, methodRoutePredicateDefinition, testPredicateDefinition));
|
||||
|
||||
RouteDefinition testRouteDefinition2 = new RouteDefinition();
|
||||
testRouteDefinition2.setUri(URI.create("this-is-wrong"));
|
||||
String routeId2 = UUID.randomUUID().toString();
|
||||
testRouteDefinition2.setId(routeId2);
|
||||
|
||||
List<RouteDefinition> multipleRouteDefs = List.of(testRouteDefinition, testRouteDefinition2);
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(multipleRouteDefs)).exchange()
|
||||
.expectStatus().is4xxClientError();
|
||||
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routedefinitions")
|
||||
.accept(MediaType.APPLICATION_JSON).exchange()
|
||||
.expectBody()
|
||||
.jsonPath("[?(@.id in ['%s','%s'])].id".formatted(routeId1, routeId2)).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostValidShortcutRouteDefinition() {
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
|
||||
Reference in New Issue
Block a user