From 9619d0bd229be0824137bbcfe2bdc68c10637390 Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Sun, 21 Jul 2024 02:49:53 +0200 Subject: [PATCH 01/11] Creation of QueryParamRoutePredicateFactory A predicate that checks if a query parameter value matches criteria of a given predicate. --- .../config/GatewayAutoConfiguration.java | 8 ++ .../QueryParamRoutePredicateFactory.java | 105 ++++++++++++++++++ .../gateway/route/builder/PredicateSpec.java | 15 ++- .../QueryParamRoutePredicateFactoryTests.java | 40 +++++++ .../cloud/gateway/test/AdhocTestSuite.java | 1 + 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java create mode 100644 spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index cd5a21c059..ff4eca9c1a 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -137,6 +137,7 @@ import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; @@ -206,6 +207,7 @@ public StringToZonedDateTimeConverter stringToZonedDateTimeConverter() { * @deprecated in favour of * {@link org.springframework.cloud.gateway.support.config.KeyValueConverter} */ + @Deprecated @Bean public org.springframework.cloud.gateway.support.KeyValueConverter deprecatedKeyValueConverter() { return new org.springframework.cloud.gateway.support.KeyValueConverter(); @@ -470,6 +472,12 @@ public PathRoutePredicateFactory pathRoutePredicateFactory() { return new PathRoutePredicateFactory(); } + @Bean + @ConditionalOnEnabledPredicate + public QueryParamRoutePredicateFactory queryParamRoutePredicateFactory() { + return new QueryParamRoutePredicateFactory(); + } + @Bean @ConditionalOnEnabledPredicate public QueryRoutePredicateFactory queryRoutePredicateFactory() { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java new file mode 100644 index 0000000000..230c4f9072 --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java @@ -0,0 +1,105 @@ +/* + * Copyright 2013-2024 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 + * + * https://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.gateway.handler.predicate; + +import java.util.List; +import java.util.function.Predicate; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import org.springframework.validation.annotation.Validated; +import org.springframework.web.server.ServerWebExchange; + +/** + * A predicate that checks if a query parameter value matches criteria of a given + * predicate. + * + * @author Francesco Poli + */ +public class QueryParamRoutePredicateFactory + extends AbstractRoutePredicateFactory { + + public QueryParamRoutePredicateFactory() { + super(Config.class); + } + + @Override + public Predicate apply(Config config) { + return new GatewayPredicate() { + + @Override + public boolean test(ServerWebExchange exchange) { + List values = exchange.getRequest().getQueryParams().get(config.param); + if (values == null) { + return false; + } + for (String value : values) { + if (value != null && config.predicate.test(value)) { + return true; + } + } + return false; + } + + @Override + public Object getConfig() { + return config; + } + + @Override + public String toString() { + return String.format("QueryParam: param=%s", config.getParam()); + } + }; + } + + /** + * {@link QueryParamRoutePredicateFactory} configuration class. + * + * @author Francesco Poli + */ + @Validated + public static class Config { + + @NotEmpty + private String param; + + @NotNull + private Predicate predicate; + + public String getParam() { + return this.param; + } + + public Config setParam(String param) { + this.param = param; + return this; + } + + public Predicate getPredicate() { + return this.predicate; + } + + public Config setPredicate(Predicate predicate) { + this.predicate = predicate; + return this; + } + + } + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java index d88a7cc1ba..677438a72a 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java @@ -31,6 +31,7 @@ import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; @@ -204,6 +205,18 @@ public BooleanSpec readBody(Class inClass, Predicate predicate) { getBean(ReadBodyRoutePredicateFactory.class).applyAsync(c -> c.setPredicate(inClass, predicate))); } + /** + * A predicate that checks if a query parameter value matches criteria of a given + * predicate. + * @param param the query parameter name + * @param predicate a predicate to check the value of the param + * @return a {@link BooleanSpec} to be used to add logical operators + */ + public BooleanSpec query(String param, Predicate predicate) { + return asyncPredicate(getBean(QueryParamRoutePredicateFactory.class) + .applyAsync(c -> c.setParam(param).setPredicate(predicate))); + } + /** * A predicate that checks if a query parameter matches a regular expression. * @param param the query parameter name @@ -287,7 +300,7 @@ public BooleanSpec xForwardedRemoteAddr(String... addrs) { */ public BooleanSpec weight(String group, int weight) { return asyncPredicate(getBean(WeightRoutePredicateFactory.class) - .applyAsync(c -> c.setGroup(group).setRouteId(routeBuilder.getId()).setWeight(weight))); + .applyAsync(c -> c.setGroup(group).setRouteId(this.routeBuilder.getId()).setWeight(weight))); } public BooleanSpec cloudFoundryRouteService() { diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java new file mode 100644 index 0000000000..59d3808040 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2024 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 + * + * https://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.gateway.handler.predicate; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.test.annotation.DirtiesContext; + +import static org.junit.jupiter.api.Assertions.fail; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +@ExtendWith(OutputCaptureExtension.class) +public class QueryParamRoutePredicateFactoryTests extends BaseWebClientTests { + + @Test + void test() { + fail("Not yet implemented"); + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java index 5d1bf14fd5..ec7f43c907 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java @@ -45,6 +45,7 @@ org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.class, + org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.WeightRoutePredicateFactoryIntegrationTests.class, org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactoryTests.class, From 22db8b4a2841313c30110e0c5357ca445fedaa36 Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Tue, 23 Jul 2024 22:02:06 +0200 Subject: [PATCH 02/11] Fix predicate method --- .../cloud/gateway/route/builder/PredicateSpec.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java index 677438a72a..b72523d1e0 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java @@ -98,7 +98,7 @@ public BooleanSpec before(ZonedDateTime datetime) { */ public BooleanSpec between(ZonedDateTime datetime1, ZonedDateTime datetime2) { return asyncPredicate(getBean(BetweenRoutePredicateFactory.class) - .applyAsync(c -> c.setDatetime1(datetime1).setDatetime2(datetime2))); + .applyAsync(c -> c.setDatetime1(datetime1).setDatetime2(datetime2))); } /** @@ -189,7 +189,7 @@ public BooleanSpec path(String... patterns) { */ public BooleanSpec path(boolean matchTrailingSlash, String... patterns) { return asyncPredicate(getBean(PathRoutePredicateFactory.class) - .applyAsync(c -> c.setPatterns(Arrays.asList(patterns)).setMatchTrailingSlash(matchTrailingSlash))); + .applyAsync(c -> c.setPatterns(Arrays.asList(patterns)).setMatchTrailingSlash(matchTrailingSlash))); } /** @@ -212,9 +212,9 @@ public BooleanSpec readBody(Class inClass, Predicate predicate) { * @param predicate a predicate to check the value of the param * @return a {@link BooleanSpec} to be used to add logical operators */ - public BooleanSpec query(String param, Predicate predicate) { + public BooleanSpec queryParam(String param, Predicate predicate) { return asyncPredicate(getBean(QueryParamRoutePredicateFactory.class) - .applyAsync(c -> c.setParam(param).setPredicate(predicate))); + .applyAsync(c -> c.setParam(param).setPredicate(predicate))); } /** @@ -300,7 +300,7 @@ public BooleanSpec xForwardedRemoteAddr(String... addrs) { */ public BooleanSpec weight(String group, int weight) { return asyncPredicate(getBean(WeightRoutePredicateFactory.class) - .applyAsync(c -> c.setGroup(group).setRouteId(this.routeBuilder.getId()).setWeight(weight))); + .applyAsync(c -> c.setGroup(group).setRouteId(this.routeBuilder.getId()).setWeight(weight))); } public BooleanSpec cloudFoundryRouteService() { From 97d9d7fc79a1c7c21ff7b020a1555ccbee3f680b Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Wed, 24 Jul 2024 00:37:38 +0200 Subject: [PATCH 03/11] Factory fixes and junit test coverage --- .../QueryParamRoutePredicateFactory.java | 10 +- .../QueryParamRoutePredicateFactoryTests.java | 113 +++++++++++++++++- 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java index 230c4f9072..313aec8c40 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java @@ -63,7 +63,7 @@ public Object getConfig() { @Override public String toString() { - return String.format("QueryParam: param=%s", config.getParam()); + return String.format("QueryParam: param=%s", config.param); } }; } @@ -82,19 +82,11 @@ public static class Config { @NotNull private Predicate predicate; - public String getParam() { - return this.param; - } - public Config setParam(String param) { this.param = param; return this; } - public Predicate getPredicate() { - return this.predicate; - } - public Config setPredicate(Predicate predicate) { this.predicate = predicate; return this; diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java index 59d3808040..06f43bb7bd 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java @@ -16,25 +16,132 @@ package org.springframework.cloud.gateway.handler.predicate; +import java.util.function.Predicate; + +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactory.Config; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.cloud.gateway.support.HasConfig; import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.web.server.ServerWebExchange; -import static org.junit.jupiter.api.Assertions.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +/** + * Test class for {@link QueryParamRoutePredicateFactory}. + * + * @see QueryParamRoutePredicateFactory + * @author Francesco Poli + */ @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext @ExtendWith(OutputCaptureExtension.class) public class QueryParamRoutePredicateFactoryTests extends BaseWebClientTests { @Test - void test() { - fail("Not yet implemented"); + public void noQueryParamWorks(CapturedOutput output) { + this.testClient.get() + .uri("/get") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); + } + + @Test + public void queryParamPredicateTrue() { + this.testClient.get() + .uri("/get?foo=1234567") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "foo_query_param"); + } + + @Test + public void queryParamPredicateFalse(CapturedOutput output) { + this.testClient.get() + .uri("/get?foo=123") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); + } + + @Test + public void emptyQueryParamWorks(CapturedOutput output) { + this.testClient.get() + .uri("/get?foo") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); + } + + @Test + public void testConfig() { + Config config = new Config(); + config.setParam("query_param"); + Predicate predicate = new QueryParamRoutePredicateFactory().apply(config); + assertTrue(predicate instanceof HasConfig, "Incongruent types for predicate"); + assertSame(config, ((HasConfig) predicate).getConfig(), "Incongruent config"); + } + + @Test + public void toStringFormat() { + Config config = new Config(); + config.setParam("query_param"); + Predicate predicate = new QueryParamRoutePredicateFactory().apply(config); + assertThat(predicate.toString()).contains("QueryParam: param=query_param"); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + @Import(DefaultTestConfig.class) + public static class TestConfig { + + private static final int PARAM_LENGTH = 5; + + @Value("${test.uri}") + private String uri; + + @Bean + RouteLocator queryParamRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("foo_query_param", + r -> r.queryParam("foo", queryParamPredicate()) + .filters(f -> f.prefixPath("/httpbin")) + .uri(this.uri)) + .build(); + } + + private Predicate queryParamPredicate() { + return p -> StringUtils.length(p) > PARAM_LENGTH; + } + } } From 15a1af60827eba97e5a7f58ed96a1e2cba78a3ee Mon Sep 17 00:00:00 2001 From: polifr Date: Thu, 3 Oct 2024 11:24:13 +0200 Subject: [PATCH 04/11] Fix on predicate check for tests --- .../predicate/QueryParamRoutePredicateFactoryTests.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java index 06f43bb7bd..7c92602042 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java @@ -18,7 +18,6 @@ import java.util.function.Predicate; -import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -139,7 +138,7 @@ RouteLocator queryParamRouteLocator(RouteLocatorBuilder builder) { } private Predicate queryParamPredicate() { - return p -> StringUtils.length(p) > PARAM_LENGTH; + return p -> p == null ? false : p.length() > PARAM_LENGTH; } } From b229a0e7b1106f6ad7eb58d300d9dfe30fe53ebc Mon Sep 17 00:00:00 2001 From: polifr Date: Thu, 3 Oct 2024 12:29:22 +0200 Subject: [PATCH 05/11] Regexp management via predicate and configuration extension --- .../predicate/QueryRoutePredicateFactory.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java index 2dd37fbd7e..2c8e15cc71 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java @@ -21,7 +21,6 @@ import java.util.function.Predicate; import jakarta.validation.constraints.NotEmpty; - import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.server.ServerWebExchange; @@ -41,13 +40,18 @@ public class QueryRoutePredicateFactory extends AbstractRoutePredicateFactory shortcutFieldOrder() { - return Arrays.asList(PARAM_KEY, REGEXP_KEY); + return Arrays.asList(PARAM_KEY, REGEXP_KEY, PREDICATE_KEY); } @Override @@ -55,7 +59,7 @@ public Predicate apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { - if (!StringUtils.hasText(config.regexp)) { + if (!StringUtils.hasText(config.regexp) && config.predicate == null) { // check existence of header return exchange.getRequest().getQueryParams().containsKey(config.param); } @@ -64,8 +68,13 @@ public boolean test(ServerWebExchange exchange) { if (values == null) { return false; } + + Predicate predicate = config.predicate; + if (StringUtils.hasText(config.regexp)) { + predicate = value -> value.matches(config.regexp); + } for (String value : values) { - if (value != null && value.matches(config.regexp)) { + if (value != null && predicate.test(value)) { return true; } } @@ -92,6 +101,8 @@ public static class Config { private String regexp; + private Predicate predicate; + public String getParam() { return param; } @@ -110,6 +121,15 @@ public Config setRegexp(String regexp) { return this; } + public Predicate getPredicate() { + return predicate; + } + + public Config setPredicate(Predicate predicate) { + this.predicate = predicate; + return this; + } + } } From b4abeb8163f4ae51dffc37fd61e7f55957ebfe25 Mon Sep 17 00:00:00 2001 From: polifr Date: Mon, 7 Oct 2024 14:17:22 +0200 Subject: [PATCH 06/11] Validation enforcing - tryout --- .../predicate/QueryRoutePredicateFactory.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java index 2c8e15cc71..51c34592c4 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.List; import java.util.function.Predicate; - +import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.NotEmpty; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; @@ -130,6 +130,17 @@ public Config setPredicate(Predicate predicate) { return this; } + /** + * Enforces the validation done on predicate configuration: {@link #regexp} and + * {@link #predicate} can't be both set at runtime. + * @return false if {@link #regexp} and {@link #predicate} are both + * set in this predicate factory configuration + */ + @AssertTrue + public boolean isValid() { + return !(StringUtils.hasText(regexp) && predicate != null); + } + } } From 28e28fc2cfc57327d6990fc46c5052b3b8a5e81d Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Tue, 15 Oct 2024 23:14:02 +0200 Subject: [PATCH 07/11] Checkstyle formatting fix --- .../handler/predicate/QueryRoutePredicateFactory.java | 10 ++++++---- .../QueryParamRoutePredicateFactoryTests.java | 8 +++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java index 51c34592c4..2696baba64 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java @@ -19,8 +19,10 @@ import java.util.Arrays; import java.util.List; import java.util.function.Predicate; + import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.NotEmpty; + import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.server.ServerWebExchange; @@ -104,7 +106,7 @@ public static class Config { private Predicate predicate; public String getParam() { - return param; + return this.param; } public Config setParam(String param) { @@ -113,7 +115,7 @@ public Config setParam(String param) { } public String getRegexp() { - return regexp; + return this.regexp; } public Config setRegexp(String regexp) { @@ -122,7 +124,7 @@ public Config setRegexp(String regexp) { } public Predicate getPredicate() { - return predicate; + return this.predicate; } public Config setPredicate(Predicate predicate) { @@ -138,7 +140,7 @@ public Config setPredicate(Predicate predicate) { */ @AssertTrue public boolean isValid() { - return !(StringUtils.hasText(regexp) && predicate != null); + return !(StringUtils.hasText(this.regexp) && this.predicate != null); } } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java index 7c92602042..5a41d4df2e 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java @@ -38,15 +38,13 @@ import org.springframework.web.server.ServerWebExchange; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * Test class for {@link QueryParamRoutePredicateFactory}. * - * @see QueryParamRoutePredicateFactory * @author Francesco Poli + * @see QueryParamRoutePredicateFactory */ @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext @@ -105,8 +103,8 @@ public void testConfig() { Config config = new Config(); config.setParam("query_param"); Predicate predicate = new QueryParamRoutePredicateFactory().apply(config); - assertTrue(predicate instanceof HasConfig, "Incongruent types for predicate"); - assertSame(config, ((HasConfig) predicate).getConfig(), "Incongruent config"); + assertThat(predicate).isInstanceOf(HasConfig.class); + assertThat(config).isSameAs(((HasConfig) predicate).getConfig()); } @Test From 660b99b12a923217391ade97d231323fd052e2d9 Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Wed, 16 Oct 2024 00:09:18 +0200 Subject: [PATCH 08/11] Deletion of QueryParamRoutePredicateFactory class and test --- .../config/GatewayAutoConfiguration.java | 7 - .../QueryParamRoutePredicateFactory.java | 97 ------------ .../QueryParamRoutePredicateFactoryTests.java | 144 ------------------ 3 files changed, 248 deletions(-) delete mode 100644 spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java delete mode 100644 spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index dae617a983..8cd8b063f4 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -137,7 +137,6 @@ import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; -import org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; @@ -472,12 +471,6 @@ public PathRoutePredicateFactory pathRoutePredicateFactory() { return new PathRoutePredicateFactory(); } - @Bean - @ConditionalOnEnabledPredicate - public QueryParamRoutePredicateFactory queryParamRoutePredicateFactory() { - return new QueryParamRoutePredicateFactory(); - } - @Bean @ConditionalOnEnabledPredicate public QueryRoutePredicateFactory queryRoutePredicateFactory() { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java deleted file mode 100644 index 313aec8c40..0000000000 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013-2024 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 - * - * https://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.gateway.handler.predicate; - -import java.util.List; -import java.util.function.Predicate; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -import org.springframework.validation.annotation.Validated; -import org.springframework.web.server.ServerWebExchange; - -/** - * A predicate that checks if a query parameter value matches criteria of a given - * predicate. - * - * @author Francesco Poli - */ -public class QueryParamRoutePredicateFactory - extends AbstractRoutePredicateFactory { - - public QueryParamRoutePredicateFactory() { - super(Config.class); - } - - @Override - public Predicate apply(Config config) { - return new GatewayPredicate() { - - @Override - public boolean test(ServerWebExchange exchange) { - List values = exchange.getRequest().getQueryParams().get(config.param); - if (values == null) { - return false; - } - for (String value : values) { - if (value != null && config.predicate.test(value)) { - return true; - } - } - return false; - } - - @Override - public Object getConfig() { - return config; - } - - @Override - public String toString() { - return String.format("QueryParam: param=%s", config.param); - } - }; - } - - /** - * {@link QueryParamRoutePredicateFactory} configuration class. - * - * @author Francesco Poli - */ - @Validated - public static class Config { - - @NotEmpty - private String param; - - @NotNull - private Predicate predicate; - - public Config setParam(String param) { - this.param = param; - return this; - } - - public Config setPredicate(Predicate predicate) { - this.predicate = predicate; - return this; - } - - } - -} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java deleted file mode 100644 index 5a41d4df2e..0000000000 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryParamRoutePredicateFactoryTests.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2013-2024 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 - * - * https://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.gateway.handler.predicate; - -import java.util.function.Predicate; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactory.Config; -import org.springframework.cloud.gateway.route.RouteLocator; -import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; -import org.springframework.cloud.gateway.support.HasConfig; -import org.springframework.cloud.gateway.test.BaseWebClientTests; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.web.server.ServerWebExchange; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - -/** - * Test class for {@link QueryParamRoutePredicateFactory}. - * - * @author Francesco Poli - * @see QueryParamRoutePredicateFactory - */ -@SpringBootTest(webEnvironment = RANDOM_PORT) -@DirtiesContext -@ExtendWith(OutputCaptureExtension.class) -public class QueryParamRoutePredicateFactoryTests extends BaseWebClientTests { - - @Test - public void noQueryParamWorks(CapturedOutput output) { - this.testClient.get() - .uri("/get") - .exchange() - .expectStatus() - .isOk() - .expectHeader() - .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); - assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); - } - - @Test - public void queryParamPredicateTrue() { - this.testClient.get() - .uri("/get?foo=1234567") - .exchange() - .expectStatus() - .isOk() - .expectHeader() - .valueEquals(ROUTE_ID_HEADER, "foo_query_param"); - } - - @Test - public void queryParamPredicateFalse(CapturedOutput output) { - this.testClient.get() - .uri("/get?foo=123") - .exchange() - .expectStatus() - .isOk() - .expectHeader() - .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); - assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); - } - - @Test - public void emptyQueryParamWorks(CapturedOutput output) { - this.testClient.get() - .uri("/get?foo") - .exchange() - .expectStatus() - .isOk() - .expectHeader() - .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); - assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); - } - - @Test - public void testConfig() { - Config config = new Config(); - config.setParam("query_param"); - Predicate predicate = new QueryParamRoutePredicateFactory().apply(config); - assertThat(predicate).isInstanceOf(HasConfig.class); - assertThat(config).isSameAs(((HasConfig) predicate).getConfig()); - } - - @Test - public void toStringFormat() { - Config config = new Config(); - config.setParam("query_param"); - Predicate predicate = new QueryParamRoutePredicateFactory().apply(config); - assertThat(predicate.toString()).contains("QueryParam: param=query_param"); - } - - @EnableAutoConfiguration - @SpringBootConfiguration - @Import(DefaultTestConfig.class) - public static class TestConfig { - - private static final int PARAM_LENGTH = 5; - - @Value("${test.uri}") - private String uri; - - @Bean - RouteLocator queryParamRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("foo_query_param", - r -> r.queryParam("foo", queryParamPredicate()) - .filters(f -> f.prefixPath("/httpbin")) - .uri(this.uri)) - .build(); - } - - private Predicate queryParamPredicate() { - return p -> p == null ? false : p.length() > PARAM_LENGTH; - } - - } - -} From 085d304d70e220853a5ca6835ce5d809ebece252 Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Wed, 16 Oct 2024 00:12:16 +0200 Subject: [PATCH 09/11] Update in QueryRoutePredicateFactory creation with Predicate --- .../cloud/gateway/route/builder/PredicateSpec.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java index cc11f30f10..db971f48cd 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java @@ -31,7 +31,6 @@ import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory; -import org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; @@ -212,9 +211,9 @@ public BooleanSpec readBody(Class inClass, Predicate predicate) { * @param predicate a predicate to check the value of the param * @return a {@link BooleanSpec} to be used to add logical operators */ - public BooleanSpec queryParam(String param, Predicate predicate) { - return asyncPredicate(getBean(QueryParamRoutePredicateFactory.class) - .applyAsync(c -> c.setParam(param).setPredicate(predicate))); + public BooleanSpec query(String param, Predicate predicate) { + return asyncPredicate( + getBean(QueryRoutePredicateFactory.class).applyAsync(c -> c.setParam(param).setPredicate(predicate))); } /** @@ -300,7 +299,7 @@ public BooleanSpec xForwardedRemoteAddr(String... addrs) { */ public BooleanSpec weight(String group, int weight) { return asyncPredicate(getBean(WeightRoutePredicateFactory.class) - .applyAsync(c -> c.setGroup(group).setRouteId(routeBuilder.getId()).setWeight(weight))); + .applyAsync(c -> c.setGroup(group).setRouteId(this.routeBuilder.getId()).setWeight(weight))); } public BooleanSpec cloudFoundryRouteService() { From dbdeff6c5723ed43aa2e015dae7b4d2d713b7d3b Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Wed, 16 Oct 2024 00:12:27 +0200 Subject: [PATCH 10/11] Unit test update --- ...ryRoutePredicateFactoryPredicateTests.java | 141 ++++++++++++++++++ .../QueryRoutePredicateFactoryTests.java | 14 +- .../cloud/gateway/test/AdhocTestSuite.java | 2 +- 3 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java new file mode 100644 index 0000000000..b150e0b63f --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java @@ -0,0 +1,141 @@ +/* + * Copyright 2024 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 + * + * https://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.gateway.handler.predicate; + +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory.Config; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.cloud.gateway.support.HasConfig; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.web.server.ServerWebExchange; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * Test class for {@link QueryRoutePredicateFactory} for predicate parameter. + * + * @see QueryRoutePredicateFactory + */ +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +@ExtendWith(OutputCaptureExtension.class) +public class QueryRoutePredicateFactoryPredicateTests extends BaseWebClientTests { + + @Test + public void noQueryParamWorks(CapturedOutput output) { + this.testClient.get() + .uri("/get") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); + } + + @Test + public void queryParamPredicateTrue() { + this.testClient.get() + .uri("/get?foo=1234567") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "foo_query_param"); + } + + @Test + public void queryParamPredicateFalse(CapturedOutput output) { + this.testClient.get() + .uri("/get?foo=123") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); + } + + @Test + public void emptyQueryParamWorks(CapturedOutput output) { + this.testClient.get() + .uri("/get?foo") + .exchange() + .expectStatus() + .isOk() + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + assertThat(output).doesNotContain("Error applying predicate for route: foo_query_param"); + } + + @Test + public void testConfig() { + Config config = new Config(); + config.setParam("query_param"); + Predicate predicate = new QueryRoutePredicateFactory().apply(config); + assertThat(predicate).isInstanceOf(HasConfig.class); + assertThat(config).isSameAs(((HasConfig) predicate).getConfig()); + } + + @Test + public void toStringFormat() { + Config config = new Config(); + config.setParam("query_param"); + Predicate predicate = new QueryRoutePredicateFactory().apply(config); + assertThat(predicate.toString()).contains("Query: param=query_param"); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + @Import(DefaultTestConfig.class) + public static class TestConfig { + + private static final int PARAM_LENGTH = 5; + + @Value("${test.uri}") + private String uri; + + @Bean + RouteLocator queryRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("foo_query_param", + r -> r.query("foo", queryParamPredicate()).filters(f -> f.prefixPath("/httpbin")).uri(this.uri)) + .build(); + } + + private Predicate queryParamPredicate() { + return p -> p == null ? false : p.length() > PARAM_LENGTH; + } + + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java index 270a1a5051..2fe75c6993 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java @@ -38,6 +38,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +/** + * Test class for {@link QueryRoutePredicateFactory} for regex parameter. + * + * @see QueryRoutePredicateFactory + */ @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext @ExtendWith(OutputCaptureExtension.class) @@ -45,7 +50,7 @@ public class QueryRoutePredicateFactoryTests extends BaseWebClientTests { @Test public void noQueryParamWorks(CapturedOutput output) { - testClient.get() + this.testClient.get() .uri("/get") .exchange() .expectStatus() @@ -57,7 +62,7 @@ public void noQueryParamWorks(CapturedOutput output) { @Test public void queryParamWorks() { - testClient.get() + this.testClient.get() .uri("/get?foo=bar") .exchange() .expectStatus() @@ -68,7 +73,7 @@ public void queryParamWorks() { @Test public void emptyQueryParamWorks(CapturedOutput output) { - testClient.get() + this.testClient.get() .uri("/get?foo") .exchange() .expectStatus() @@ -98,7 +103,8 @@ public static class TestConfig { @Bean RouteLocator queryRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("foo_query_param", r -> r.query("foo", "bar").filters(f -> f.prefixPath("/httpbin")).uri(uri)) + .route("foo_query_param", + r -> r.query("foo", "bar").filters(f -> f.prefixPath("/httpbin")).uri(this.uri)) .build(); } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java index ec7f43c907..38041174a1 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java @@ -45,8 +45,8 @@ org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.class, - org.springframework.cloud.gateway.handler.predicate.QueryParamRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactoryTests.class, + org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactoryPredicateTests.class, org.springframework.cloud.gateway.handler.predicate.WeightRoutePredicateFactoryIntegrationTests.class, org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactoryTests.class, From 5ec66fe44d1bca2e6c75bfb6db352f8b8b591875 Mon Sep 17 00:00:00 2001 From: Francesco Poli Date: Mon, 23 Dec 2024 16:02:28 +0100 Subject: [PATCH 11/11] Update QueryRoutePredicateFactoryPredicateTests.java Fix copyright header comment --- .../predicate/QueryRoutePredicateFactoryPredicateTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java index b150e0b63f..6976565934 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryPredicateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2013-2024 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.