Adds Visitor pattern to visit each node of Predicate. (#2423)
This allows users to inspect the configuration of predicates and perform work on them.
This commit is contained in:
@@ -23,13 +23,15 @@ import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
|
||||
import org.springframework.cloud.gateway.support.HasConfig;
|
||||
import org.springframework.cloud.gateway.support.Visitor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>>, HasConfig {
|
||||
|
||||
default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
|
||||
return new AndAsyncPredicate<>(this, other);
|
||||
@@ -47,6 +49,10 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
return new OrAsyncPredicate<>(this, other);
|
||||
}
|
||||
|
||||
default void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
static AsyncPredicate<ServerWebExchange> from(Predicate<? super ServerWebExchange> predicate) {
|
||||
return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
|
||||
}
|
||||
@@ -69,6 +75,14 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
return this.delegate.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
if (delegate instanceof GatewayPredicate) {
|
||||
GatewayPredicate gatewayPredicate = (GatewayPredicate) delegate;
|
||||
gatewayPredicate.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NegateAsyncPredicate<T> implements AsyncPredicate<T> {
|
||||
@@ -110,6 +124,12 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
return Mono.from(left.apply(t)).flatMap(result -> !result ? Mono.just(false) : Mono.from(right.apply(t)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
left.accept(visitor);
|
||||
right.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s && %s)", this.left, this.right);
|
||||
@@ -135,6 +155,12 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
return Mono.from(left.apply(t)).flatMap(result -> result ? Mono.just(true) : Mono.from(right.apply(t)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
left.accept(visitor);
|
||||
right.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s || %s)", this.left, this.right);
|
||||
|
||||
@@ -53,6 +53,11 @@ public class AfterRoutePredicateFactory extends AbstractRoutePredicateFactory<Af
|
||||
return now.isAfter(config.getDatetime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("After: %s", config.getDatetime());
|
||||
|
||||
@@ -51,6 +51,11 @@ public class BeforeRoutePredicateFactory extends AbstractRoutePredicateFactory<B
|
||||
return now.isBefore(config.getDatetime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Before: %s", config.getDatetime());
|
||||
|
||||
@@ -63,6 +63,11 @@ public class BetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<
|
||||
return now.isAfter(config.getDatetime1()) && now.isBefore(config.getDatetime2());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Between: %s and %s", config.getDatetime1(), config.getDatetime2());
|
||||
|
||||
@@ -67,6 +67,11 @@ public class CookieRoutePredicateFactory extends AbstractRoutePredicateFactory<C
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Cookie: name=%s regexp=%s", config.name, config.regexp);
|
||||
|
||||
@@ -18,10 +18,12 @@ package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.cloud.gateway.support.HasConfig;
|
||||
import org.springframework.cloud.gateway.support.Visitor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
public interface GatewayPredicate extends Predicate<ServerWebExchange> {
|
||||
public interface GatewayPredicate extends Predicate<ServerWebExchange>, HasConfig {
|
||||
|
||||
@Override
|
||||
default Predicate<ServerWebExchange> and(Predicate<? super ServerWebExchange> other) {
|
||||
@@ -38,6 +40,10 @@ public interface GatewayPredicate extends Predicate<ServerWebExchange> {
|
||||
return new OrGatewayPredicate(this, wrapIfNeeded(other));
|
||||
}
|
||||
|
||||
default void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
static GatewayPredicate wrapIfNeeded(Predicate<? super ServerWebExchange> other) {
|
||||
GatewayPredicate right;
|
||||
|
||||
@@ -64,6 +70,14 @@ public interface GatewayPredicate extends Predicate<ServerWebExchange> {
|
||||
return this.delegate.test(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
if (delegate instanceof GatewayPredicate) {
|
||||
GatewayPredicate gatewayPredicate = (GatewayPredicate) delegate;
|
||||
gatewayPredicate.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.delegate.getClass().getSimpleName();
|
||||
@@ -85,6 +99,11 @@ public interface GatewayPredicate extends Predicate<ServerWebExchange> {
|
||||
return !this.predicate.test(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
predicate.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("!%s", this.predicate);
|
||||
@@ -110,6 +129,12 @@ public interface GatewayPredicate extends Predicate<ServerWebExchange> {
|
||||
return (this.left.test(t) && this.right.test(t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
left.accept(visitor);
|
||||
right.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s && %s)", this.left, this.right);
|
||||
@@ -135,6 +160,12 @@ public interface GatewayPredicate extends Predicate<ServerWebExchange> {
|
||||
return (this.left.test(t) || this.right.test(t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
left.accept(visitor);
|
||||
right.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s || %s)", this.left, this.right);
|
||||
|
||||
@@ -79,6 +79,11 @@ public class HeaderRoutePredicateFactory extends AbstractRoutePredicateFactory<H
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Header: %s regexp=%s", config.header, config.regexp);
|
||||
|
||||
@@ -78,6 +78,11 @@ public class HostRoutePredicateFactory extends AbstractRoutePredicateFactory<Hos
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Hosts: %s", config.getPatterns());
|
||||
|
||||
@@ -109,6 +109,11 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<Pat
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Paths: %s, match trailing slash: %b", config.getPatterns(),
|
||||
|
||||
@@ -72,6 +72,11 @@ public class QueryRoutePredicateFactory extends AbstractRoutePredicateFactory<Qu
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Query: param=%s regexp=%s", config.getParam(), config.getRegexp());
|
||||
|
||||
@@ -97,6 +97,11 @@ public class ReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ReadBody: %s", config.getInClass());
|
||||
|
||||
@@ -94,6 +94,11 @@ public class RemoteAddrRoutePredicateFactory
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("RemoteAddrs: %s", config.getSources());
|
||||
|
||||
@@ -110,6 +110,11 @@ public class WeightRoutePredicateFactory extends AbstractRoutePredicateFactory<W
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Weight: %s %s", config.getGroup(), config.getWeight());
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.support;
|
||||
|
||||
public interface HasConfig {
|
||||
|
||||
default Object getConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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.support;
|
||||
|
||||
/**
|
||||
* Simple visitor interface that allows users to inspect a graph of objects.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Visitor {
|
||||
|
||||
void visit(HasConfig hasConfig);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.ArrayList;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.gateway.handler.AsyncPredicate;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class GatewayPredicateVisitorTests {
|
||||
|
||||
@Test
|
||||
public void asyncPredicateVisitVisitsEachNode() {
|
||||
PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory();
|
||||
HostRoutePredicateFactory hostRoutePredicateFactory = new HostRoutePredicateFactory();
|
||||
ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory1 = new ReadBodyRoutePredicateFactory();
|
||||
ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory2 = new ReadBodyRoutePredicateFactory();
|
||||
AsyncPredicate<ServerWebExchange> predicate = AsyncPredicate.from(pathRoutePredicateFactory.apply(pathRoutePredicateFactory.newConfig()))
|
||||
.and(AsyncPredicate.from(hostRoutePredicateFactory.apply(hostRoutePredicateFactory.newConfig())))
|
||||
.and(readBodyRoutePredicateFactory1.applyAsync(readBodyRoutePredicateFactory1.newConfig()))
|
||||
.and(readBodyRoutePredicateFactory2.applyAsync(readBodyRoutePredicateFactory2.newConfig()));
|
||||
|
||||
Route route = Route.async().id("git").uri("http://myuri").asyncPredicate(predicate).build();
|
||||
ArrayList<Object> configs = new ArrayList<>();
|
||||
route.getPredicate().accept(p -> configs.add(p.getConfig()));
|
||||
|
||||
assertThat(configs).hasSize(4).hasExactlyElementsOfTypes(PathRoutePredicateFactory.Config.class,
|
||||
HostRoutePredicateFactory.Config.class, ReadBodyRoutePredicateFactory.Config.class,
|
||||
ReadBodyRoutePredicateFactory.Config.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void predicateVisitVisitsEachNode() {
|
||||
PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory();
|
||||
HostRoutePredicateFactory hostRoutePredicateFactory = new HostRoutePredicateFactory();
|
||||
Predicate<ServerWebExchange> predicate = pathRoutePredicateFactory.apply(pathRoutePredicateFactory.newConfig())
|
||||
.and(hostRoutePredicateFactory.apply(hostRoutePredicateFactory.newConfig()));
|
||||
|
||||
Route route = Route.async().id("git").uri("http://myuri").predicate(predicate).build();
|
||||
ArrayList<Object> configs = new ArrayList<>();
|
||||
route.getPredicate().accept(p -> configs.add(p.getConfig()));
|
||||
|
||||
assertThat(configs).hasSize(2).hasExactlyElementsOfTypes(PathRoutePredicateFactory.Config.class,
|
||||
HostRoutePredicateFactory.Config.class);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user