From 8c821197af8b79bfa9c54724722375e7dfb75a84 Mon Sep 17 00:00:00 2001 From: Ignacio Lozano Date: Tue, 31 Oct 2023 16:53:57 +0100 Subject: [PATCH] Correct the order when routes are refreshed by group (#3112) --- .../gateway/route/CachingRouteLocator.java | 5 +- .../GatewayControllerEndpointTests.java | 57 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java index 301e1e4cf..bce31e31a 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java @@ -88,8 +88,9 @@ public class CachingRouteLocator .onErrorResume(s -> Mono.just(List.of())); scopedRoutes.subscribe(scopedRoutesList -> { - Flux.concat(Flux.fromIterable(scopedRoutesList), getNonScopedRoutes(event)).materialize() - .collect(Collectors.toList()).subscribe(signals -> { + Flux.concat(Flux.fromIterable(scopedRoutesList), getNonScopedRoutes(event)) + .sort(AnnotationAwareOrderComparator.INSTANCE).materialize().collect(Collectors.toList()) + .subscribe(signals -> { applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this)); cache.put(CACHE_KEY, signals); }, this::handleRefreshError); diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java index 17337172a..46763c944 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Predicate; +import java.util.stream.Collectors; import org.assertj.core.util.Maps; import org.junit.jupiter.api.Assertions; @@ -193,6 +194,62 @@ public class GatewayControllerEndpointTests { }); } + @Test + public void testOrderOfRefreshByGroup() { + RouteDefinition testRouteDefinition = new RouteDefinition(); + testRouteDefinition.setUri(URI.create("http://example.org")); + testRouteDefinition.setOrder(1000); + String group1 = "group-1_" + UUID.randomUUID(); + testRouteDefinition.setMetadata(Map.of("groupBy", group1)); + + String routeId1 = "route-1_" + UUID.randomUUID(); + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1) + .accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange() + .expectStatus().isCreated(); + + RouteDefinition testRouteDefinition2 = new RouteDefinition(); + testRouteDefinition2.setUri(URI.create("http://example.org")); + testRouteDefinition2.setOrder(0); + String group2 = "group-2_" + UUID.randomUUID(); + testRouteDefinition2.setMetadata(Map.of("groupBy", group2)); + String routeId2 = "route-2_" + UUID.randomUUID(); + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2) + .accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition2)).exchange() + .expectStatus().isCreated(); + + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1) + .exchange().expectStatus().isOk(); + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2) + .exchange().expectStatus().isOk(); + + testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk() + .expectBodyList(Map.class).consumeWith(result -> { + List responseBody = result.getResponseBody(); + + List ids = responseBody.stream() + .map(route -> route.get("route_id")) + .filter(id -> id.equals(routeId1) || id.equals(routeId2)) + .collect(Collectors.toList()); + assertThat(ids).containsExactly(routeId2, routeId1); + }); + + testRouteDefinition2.setOrder(testRouteDefinition.getOrder() + 1); + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2) + .accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition2)).exchange() + .expectStatus().isCreated(); + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2) + .exchange().expectStatus().isOk(); + testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk() + .expectBodyList(Map.class).consumeWith(result -> { + List responseBody = result.getResponseBody(); + List ids = responseBody.stream() + .map(route -> route.get("route_id")) + .filter(id -> id.equals(routeId1) || id.equals(routeId2)) + .collect(Collectors.toList()); + assertThat(ids).containsExactly(routeId1, routeId2); + }); + } + @Test public void testRefreshByGroup_whenRouteDefinitionsAreDeleted() { RouteDefinition testRouteDefinition = new RouteDefinition();