Adds BooleanOpSpec.not()
This allows to negate specific predicates rather than just the previous predicate. Fixes gh-1932
This commit is contained in:
@@ -39,6 +39,10 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
return new NegateAsyncPredicate<>(this);
|
||||
}
|
||||
|
||||
default AsyncPredicate<T> not(AsyncPredicate<? super T> other) {
|
||||
return new NegateAsyncPredicate<>(other);
|
||||
}
|
||||
|
||||
default AsyncPredicate<T> or(AsyncPredicate<? super T> other) {
|
||||
return new OrAsyncPredicate<>(this, other);
|
||||
}
|
||||
@@ -84,7 +88,7 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("!%s", this.predicate);
|
||||
return String.format("!(%s)", this.predicate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -113,6 +113,27 @@ public class BooleanSpec extends UriSpec {
|
||||
return new BooleanSpec(this.routeBuilder, this.builder);
|
||||
}
|
||||
|
||||
public BooleanSpec not(Function<PredicateSpec, BooleanSpec> fn) {
|
||||
return fn
|
||||
.apply(new NotOpSpec(this.routeBuilder, this.builder, this.operator));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class NotOpSpec extends BooleanOpSpec {
|
||||
|
||||
NotOpSpec(Route.AsyncBuilder routeBuilder, RouteLocatorBuilder.Builder builder,
|
||||
Operator operator) {
|
||||
super(routeBuilder, builder, operator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanSpec asyncPredicate(AsyncPredicate<ServerWebExchange> predicate) {
|
||||
AsyncPredicate<ServerWebExchange> negated = this.routeBuilder.getPredicate()
|
||||
.not(predicate);
|
||||
return super.asyncPredicate(negated);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.route.builder;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.cloud.gateway.handler.AsyncPredicate;
|
||||
@@ -309,4 +310,8 @@ public class PredicateSpec extends UriSpec {
|
||||
return predicate(exchange -> true);
|
||||
}
|
||||
|
||||
public BooleanSpec not(Function<PredicateSpec, BooleanSpec> fn) {
|
||||
return alwaysTrue().and().not(fn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,6 +74,20 @@ public class AsyncPredicateTest {
|
||||
right.assertTested();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negateOperatorWorks() {
|
||||
TestAsyncPredicate<Object> falsePredicate = new TestAsyncPredicate<>(o -> false);
|
||||
TestAsyncPredicate<Object> truePredicate = new TestAsyncPredicate<>(o -> true);
|
||||
Publisher<Boolean> falseNot = falsePredicate.negate().apply(new Object());
|
||||
Publisher<Boolean> trueNot = truePredicate.negate().apply(new Object());
|
||||
|
||||
StepVerifier.create(falseNot).expectNext(true).expectComplete().verify();
|
||||
StepVerifier.create(trueNot).expectNext(false).expectComplete().verify();
|
||||
|
||||
falsePredicate.assertTested();
|
||||
truePredicate.assertTested();
|
||||
}
|
||||
|
||||
/**
|
||||
* An AsyncPredicate decorator that records if the apply method was called.
|
||||
*/
|
||||
|
||||
@@ -21,14 +21,21 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
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.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
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.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
@@ -57,11 +64,51 @@ public class RoutePredicateHandlerMappingIntegrationTests extends BaseWebClientT
|
||||
.uri("/get").exchange().expectStatus().isNotFound();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void andNotWorksWithMissingParameter() {
|
||||
testClient.get().uri("/andnotquery").exchange().expectBody(String.class)
|
||||
.isEqualTo("notsupplied");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void andNotWorksWithParameter() {
|
||||
testClient.get().uri("/andnotquery?myquery=shouldnotsee").exchange()
|
||||
.expectBody(String.class).isEqualTo("hasquery");
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
@RestController
|
||||
public static class TestConfig {
|
||||
|
||||
@Value("${test.uri:http://httpbin.org:80}")
|
||||
String uri;
|
||||
|
||||
@GetMapping("/httpbin/andnotquery")
|
||||
String andnotquery(@RequestParam(name = "myquery",
|
||||
defaultValue = "notsupplied") String myquery) {
|
||||
return myquery;
|
||||
}
|
||||
|
||||
@GetMapping("/httpbin/hasquery")
|
||||
String hasquery() {
|
||||
return "hasquery";
|
||||
}
|
||||
|
||||
@Bean
|
||||
RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("and_not_missing_myquery",
|
||||
r -> r.path("/andnotquery").and().not(p -> p.query("myquery"))
|
||||
.filters(f -> f.prefixPath("/httpbin")).uri(uri))
|
||||
.route("and_not_has_myquery",
|
||||
r -> r.path("/andnotquery").and().query("myquery")
|
||||
.filters(f -> f.setPath("/httpbin/hasquery"))
|
||||
.uri(uri))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user