Browse Source
As a follow-up to the recent commit 246e72 some slight modifications to MockServerExchangeMutator (renamed to ExchnageMutatorWebFilter). Aside from the name change, the main difference is that "per request" exchange processors are now simply applied via WebTestClient#filter(..). Issue: SPR-15570pull/1435/head
8 changed files with 235 additions and 309 deletions
@ -0,0 +1,112 @@
@@ -0,0 +1,112 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* http://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.test.web.reactive.server; |
||||
|
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.function.Function; |
||||
import java.util.function.UnaryOperator; |
||||
|
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
import org.springframework.web.server.WebFilter; |
||||
import org.springframework.web.server.WebFilterChain; |
||||
|
||||
|
||||
/** |
||||
* Apply {@code ServerWebExchange} transformations during "mock" server tests |
||||
* with the {@code WebTestClient}. |
||||
* |
||||
* <p>Register the {@code WebFilter} while setting up the mock server through |
||||
* one of the following: |
||||
* <ul> |
||||
* <li>{@link WebTestClient#bindToController} |
||||
* <li>{@link WebTestClient#bindToRouterFunction} |
||||
* <li>{@link WebTestClient#bindToApplicationContext} |
||||
* <li>{@link WebTestClient#bindToWebHandler} |
||||
* </ul> |
||||
* |
||||
* <p>Example usage: |
||||
* |
||||
* <pre class="code"> |
||||
* ExchangeMutatorWebFilter mutator = new ExchangeMutatorWebFilter(exchange -> ...); |
||||
* WebTestClient client = WebTestClient.bindToController(new MyController()).webFilter(mutator).build(); |
||||
* </pre> |
||||
* |
||||
* <p>It is also possible to apply "per request" transformations: |
||||
* |
||||
* <pre class="code"> |
||||
* ExchangeMutatorWebFilter mutator = new ExchangeMutatorWebFilter(exchange -> ...); |
||||
* WebTestClient client = WebTestClient.bindToController(new MyController()).webFilter(mutator).build(); |
||||
* client.filter(mutator.perClient(exchange -> ...)).get().uri("/").exchange(); |
||||
* </pre> |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.0 |
||||
*/ |
||||
public class ExchangeMutatorWebFilter implements WebFilter { |
||||
|
||||
private final Function<ServerWebExchange, ServerWebExchange> processor; |
||||
|
||||
private final Map<String, Function<ServerWebExchange, ServerWebExchange>> perRequestProcessors = |
||||
new ConcurrentHashMap<>(4); |
||||
|
||||
|
||||
public ExchangeMutatorWebFilter(UnaryOperator<ServerWebExchange> processor) { |
||||
Assert.notNull(processor, "'processor' is required"); |
||||
this.processor = processor; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { |
||||
exchange = getProcessor(exchange).apply(exchange); |
||||
return chain.filter(exchange); |
||||
} |
||||
|
||||
private Function<ServerWebExchange, ServerWebExchange> getProcessor(ServerWebExchange exchange) { |
||||
String id = getRequestId(exchange.getRequest().getHeaders()); |
||||
Function<ServerWebExchange, ServerWebExchange> clientMutator = this.perRequestProcessors.remove(id); |
||||
return (clientMutator != null ? this.processor.andThen(clientMutator) : this.processor); |
||||
} |
||||
|
||||
private String getRequestId(HttpHeaders headers) { |
||||
String id = headers.getFirst(WebTestClient.WEBTESTCLIENT_REQUEST_ID); |
||||
Assert.notNull(id, "No \"" + WebTestClient.WEBTESTCLIENT_REQUEST_ID + "\" header"); |
||||
return id; |
||||
} |
||||
|
||||
/** |
||||
* Apply the given processor only to requests performed through the client |
||||
* instance filtered with the returned filter. See class-level Javadoc for |
||||
* sample code. |
||||
* @param processor the exchange processor to use |
||||
* @return client filter for use with {@link WebTestClient#filter} |
||||
*/ |
||||
public ExchangeFilterFunction perClient(UnaryOperator<ServerWebExchange> processor) { |
||||
return (request, next) -> { |
||||
String id = getRequestId(request.headers()); |
||||
this.perRequestProcessors.compute(id, |
||||
(s, value) -> value != null ? value.andThen(processor) : processor); |
||||
return next.exchange(request); |
||||
}; |
||||
} |
||||
|
||||
} |
@ -1,126 +0,0 @@
@@ -1,126 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* http://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.test.web.reactive.server; |
||||
|
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.function.Function; |
||||
import java.util.function.UnaryOperator; |
||||
|
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
import org.springframework.web.server.WebFilter; |
||||
import org.springframework.web.server.WebFilterChain; |
||||
|
||||
/** |
||||
* Built-in {@link WebFilter} for applying {@code ServerWebExchange} |
||||
* transformations during requests from the {@code WebTestClient} to a mock |
||||
* server -- i.e. when one of the following is in use: |
||||
* <ul> |
||||
* <li>{@link WebTestClient#bindToController} |
||||
* <li>{@link WebTestClient#bindToRouterFunction} |
||||
* <li>{@link WebTestClient#bindToApplicationContext} |
||||
* <li>{@link WebTestClient#bindToWebHandler} |
||||
* </ul> |
||||
* |
||||
* <p>Example of registering a "global" transformation: |
||||
* <pre class="code"> |
||||
* |
||||
* MockServerExchangeMutator mutator = new MockServerExchangeMutator(exchange -> ...); |
||||
* WebTestClient client = WebTestClient.bindToController(new MyController()).webFilter(mutator).build() |
||||
* </pre> |
||||
* |
||||
* <p>Example of registering "per client" transformations: |
||||
* <pre class="code"> |
||||
* |
||||
* MockServerExchangeMutator mutator = new MockServerExchangeMutator(exchange -> ...); |
||||
* WebTestClient client = WebTestClient.bindToController(new MyController()).webFilter(mutator).build() |
||||
* |
||||
* WebTestClient clientA = mutator.filterClient(client, exchange -> ...); |
||||
* // Use client A...
|
||||
* |
||||
* WebTestClient clientB = mutator.filterClient(client, exchange -> ...); |
||||
* // Use client B...
|
||||
* </pre> |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.0 |
||||
*/ |
||||
public class MockServerExchangeMutator implements WebFilter { |
||||
|
||||
private final Function<ServerWebExchange, ServerWebExchange> mutator; |
||||
|
||||
private final Map<String, Function<ServerWebExchange, ServerWebExchange>> perRequestMutators = |
||||
new ConcurrentHashMap<>(4); |
||||
|
||||
|
||||
public MockServerExchangeMutator(Function<ServerWebExchange, ServerWebExchange> mutator) { |
||||
Assert.notNull(mutator, "'mutator' is required"); |
||||
this.mutator = mutator; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { |
||||
return chain.filter(getMutatorsFor(exchange).apply(exchange)); |
||||
} |
||||
|
||||
private Function<ServerWebExchange, ServerWebExchange> getMutatorsFor(ServerWebExchange exchange) { |
||||
String id = getRequestId(exchange.getRequest().getHeaders()); |
||||
Function<ServerWebExchange, ServerWebExchange> clientMutator = this.perRequestMutators.remove(id); |
||||
return (clientMutator != null ? this.mutator.andThen(clientMutator) : this.mutator); |
||||
} |
||||
|
||||
private String getRequestId(HttpHeaders headers) { |
||||
String id = headers.getFirst(WebTestClient.WEBTESTCLIENT_REQUEST_ID); |
||||
Assert.notNull(id, "No \"" + WebTestClient.WEBTESTCLIENT_REQUEST_ID + "\" header"); |
||||
return id; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Apply a filter to the given client in order to apply |
||||
* {@code ServerWebExchange} transformations only to requests executed |
||||
* through the returned client instance. See examples in the |
||||
* {@link MockServerExchangeMutator class-level Javadoc}. |
||||
* |
||||
* @param mutator the per-request mutator to use |
||||
* @param mutators additional per-request mutators to use |
||||
* @return a new client instance filtered with {@link WebTestClient#filter} |
||||
*/ |
||||
@SafeVarargs |
||||
public final WebTestClient filterClient(WebTestClient client, |
||||
UnaryOperator<ServerWebExchange> mutator, UnaryOperator<ServerWebExchange>... mutators) { |
||||
|
||||
return client.filter((request, next) -> { |
||||
String id = getRequestId(request.headers()); |
||||
registerPerRequestMutator(id, mutator); |
||||
for (UnaryOperator<ServerWebExchange> current : mutators) { |
||||
registerPerRequestMutator(id, current); |
||||
} |
||||
return next.exchange(request); |
||||
}); |
||||
} |
||||
|
||||
private void registerPerRequestMutator(String id, UnaryOperator<ServerWebExchange> m) { |
||||
this.perRequestMutators.compute(id, (s, value) -> value != null ? value.andThen(m) : m); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,100 @@
@@ -0,0 +1,100 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* http://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.test.web.reactive.server.samples; |
||||
|
||||
import java.security.Principal; |
||||
import java.util.function.UnaryOperator; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.test.web.reactive.server.ExchangeMutatorWebFilter; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
|
||||
/** |
||||
* Samples tests that demonstrate applying ServerWebExchange initialization. |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ExchangeMutatorWebFilterTests { |
||||
|
||||
private ExchangeMutatorWebFilter exchangeMutator; |
||||
|
||||
private WebTestClient webTestClient; |
||||
|
||||
|
||||
@Before |
||||
public void setUp() throws Exception { |
||||
|
||||
this.exchangeMutator = new ExchangeMutatorWebFilter(userIdentity("Pablo")); |
||||
|
||||
this.webTestClient = WebTestClient.bindToController(new TestController()) |
||||
.webFilter(this.exchangeMutator) |
||||
.build(); |
||||
} |
||||
|
||||
@Test |
||||
public void globalMutator() throws Exception { |
||||
this.webTestClient.get().uri("/userIdentity") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).isEqualTo("Hello Pablo!"); |
||||
} |
||||
|
||||
@Test |
||||
public void perRequestMutators() throws Exception { |
||||
this.webTestClient |
||||
.filter(this.exchangeMutator.perClient(userIdentity("Giovanni"))) |
||||
.get().uri("/userIdentity") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).isEqualTo("Hello Giovanni!"); |
||||
} |
||||
|
||||
|
||||
private UnaryOperator<ServerWebExchange> userIdentity(String userName) { |
||||
return exchange -> exchange.mutate().principal(Mono.just(new TestUser(userName))).build(); |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
static class TestController { |
||||
|
||||
@GetMapping("/userIdentity") |
||||
public String handle(Principal principal) { |
||||
return "Hello " + principal.getName() + "!"; |
||||
} |
||||
} |
||||
|
||||
|
||||
private static class TestUser implements Principal { |
||||
|
||||
private final String name; |
||||
|
||||
TestUser(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue