Provide simple way to create ClientResponse

This commit introduces ClientResponse.Builder, an easier way to create a
ClientResponse from an existing response, or from scratch.

Issue: SPR-16553
This commit is contained in:
Arjen Poutsma
2018-03-08 15:47:48 +01:00
parent f8588e364a
commit 04c2a2990d
10 changed files with 879 additions and 9 deletions

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2002-2018 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.web.reactive.function.client;
import java.nio.charset.StandardCharsets;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import static org.junit.Assert.*;
/**
* @author Arjen Poutsma
*/
public class DefaultClientResponseBuilderTests {
private DataBufferFactory dataBufferFactory;
@Before
public void createBufferFactory() {
this.dataBufferFactory = new DefaultDataBufferFactory();
}
@Test
public void normal() {
Flux<DataBuffer> body = Flux.just("baz")
.map(s -> s.getBytes(StandardCharsets.UTF_8))
.map(dataBufferFactory::wrap);
ClientResponse response = ClientResponse.create(HttpStatus.BAD_GATEWAY, ExchangeStrategies.withDefaults())
.header("foo", "bar")
.cookie("baz", "qux")
.body(body)
.build();
assertEquals(HttpStatus.BAD_GATEWAY, response.statusCode());
HttpHeaders responseHeaders = response.headers().asHttpHeaders();
assertEquals("bar", responseHeaders.getFirst("foo"));
assertNotNull("qux", response.cookies().getFirst("baz"));
assertEquals("qux", response.cookies().getFirst("baz").getValue());
StepVerifier.create(response.bodyToFlux(String.class))
.expectNext("baz")
.verifyComplete();
}
@Test
public void from() throws Exception {
Flux<DataBuffer> otherBody = Flux.just("foo", "bar")
.map(s -> s.getBytes(StandardCharsets.UTF_8))
.map(dataBufferFactory::wrap);
ClientResponse other = ClientResponse.create(HttpStatus.BAD_REQUEST, ExchangeStrategies.withDefaults())
.header("foo", "bar")
.cookie("baz", "qux")
.body(otherBody)
.build();
Flux<DataBuffer> body = Flux.just("baz")
.map(s -> s.getBytes(StandardCharsets.UTF_8))
.map(dataBufferFactory::wrap);
ClientResponse result = ClientResponse.from(other)
.headers(httpHeaders -> httpHeaders.set("foo", "baar"))
.cookies(cookies -> cookies.set("baz", ResponseCookie.from("baz", "quux").build()))
.body(body)
.build();
assertEquals(HttpStatus.BAD_REQUEST, result.statusCode());
assertEquals(1, result.headers().asHttpHeaders().size());
assertEquals("baar", result.headers().asHttpHeaders().getFirst("foo"));
assertEquals(1, result.cookies().size());
assertEquals("quux", result.cookies().getFirst("baz").getValue());
StepVerifier.create(result.bodyToFlux(String.class))
.expectNext("baz")
.verifyComplete();
}
}

View File

@@ -0,0 +1,163 @@
/*
* Copyright 2002-2018 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.web.reactive.function.client.support;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.client.ClientResponse;
import static java.util.Collections.singletonList;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Arjen Poutsma
*/
public class ClientResponseWrapperTests {
private ClientResponse mockResponse;
private ClientResponseWrapper wrapper;
@Before
public void createWrapper() {
this.mockResponse = mock(ClientResponse.class);
this.wrapper = new ClientResponseWrapper(mockResponse);
}
@Test
public void response() throws Exception {
assertSame(mockResponse, wrapper.response());
}
@Test
public void statusCode() throws Exception {
HttpStatus status = HttpStatus.BAD_REQUEST;
when(mockResponse.statusCode()).thenReturn(status);
assertSame(status, wrapper.statusCode());
}
@Test
public void headers() throws Exception {
ClientResponse.Headers headers = mock(ClientResponse.Headers.class);
when(mockResponse.headers()).thenReturn(headers);
assertSame(headers, wrapper.headers());
}
@Test
public void cookies() throws Exception {
MultiValueMap<String, ResponseCookie> cookies = mock(MultiValueMap.class);
when(mockResponse.cookies()).thenReturn(cookies);
assertSame(cookies, wrapper.cookies());
}
@Test
public void bodyExtractor() throws Exception {
Mono<String> result = Mono.just("foo");
BodyExtractor<Mono<String>, ReactiveHttpInputMessage> extractor = BodyExtractors.toMono(String.class);
when(mockResponse.body(extractor)).thenReturn(result);
assertSame(result, wrapper.body(extractor));
}
@Test
public void bodyToMonoClass() throws Exception {
Mono<String> result = Mono.just("foo");
when(mockResponse.bodyToMono(String.class)).thenReturn(result);
assertSame(result, wrapper.bodyToMono(String.class));
}
@Test
public void bodyToMonoParameterizedTypeReference() throws Exception {
Mono<String> result = Mono.just("foo");
ParameterizedTypeReference<String> reference = new ParameterizedTypeReference<String>() {};
when(mockResponse.bodyToMono(reference)).thenReturn(result);
assertSame(result, wrapper.bodyToMono(reference));
}
@Test
public void bodyToFluxClass() throws Exception {
Flux<String> result = Flux.just("foo");
when(mockResponse.bodyToFlux(String.class)).thenReturn(result);
assertSame(result, wrapper.bodyToFlux(String.class));
}
@Test
public void bodyToFluxParameterizedTypeReference() throws Exception {
Flux<String> result = Flux.just("foo");
ParameterizedTypeReference<String> reference = new ParameterizedTypeReference<String>() {};
when(mockResponse.bodyToFlux(reference)).thenReturn(result);
assertSame(result, wrapper.bodyToFlux(reference));
}
@Test
public void toEntityClass() throws Exception {
Mono<ResponseEntity<String>> result = Mono.just(new ResponseEntity<>("foo", HttpStatus.OK));
when(mockResponse.toEntity(String.class)).thenReturn(result);
assertSame(result, wrapper.toEntity(String.class));
}
@Test
public void toEntityParameterizedTypeReference() throws Exception {
Mono<ResponseEntity<String>> result = Mono.just(new ResponseEntity<>("foo", HttpStatus.OK));
ParameterizedTypeReference<String> reference = new ParameterizedTypeReference<String>() {};
when(mockResponse.toEntity(reference)).thenReturn(result);
assertSame(result, wrapper.toEntity(reference));
}
@Test
public void toEntityListClass() throws Exception {
Mono<ResponseEntity<List<String>>> result = Mono.just(new ResponseEntity<>(singletonList("foo"), HttpStatus.OK));
when(mockResponse.toEntityList(String.class)).thenReturn(result);
assertSame(result, wrapper.toEntityList(String.class));
}
@Test
public void toEntityListParameterizedTypeReference() throws Exception {
Mono<ResponseEntity<List<String>>> result = Mono.just(new ResponseEntity<>(singletonList("foo"), HttpStatus.OK));
ParameterizedTypeReference<String> reference = new ParameterizedTypeReference<String>() {};
when(mockResponse.toEntityList(reference)).thenReturn(result);
assertSame(result, wrapper.toEntityList(reference));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -23,10 +23,17 @@ import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpMethod;
import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.server.ServerRequest;
import static org.junit.Assert.*;
@@ -128,4 +135,55 @@ public class ServerRequestWrapperTests {
assertSame(pathVariables, wrapper.pathVariables());
}
@Test
public void cookies() throws Exception {
MultiValueMap<String, HttpCookie> cookies = mock(MultiValueMap.class);
when(mockRequest.cookies()).thenReturn(cookies);
assertSame(cookies, wrapper.cookies());
}
@Test
public void bodyExtractor() throws Exception {
Mono<String> result = Mono.just("foo");
BodyExtractor<Mono<String>, ReactiveHttpInputMessage> extractor = BodyExtractors.toMono(String.class);
when(mockRequest.body(extractor)).thenReturn(result);
assertSame(result, wrapper.body(extractor));
}
@Test
public void bodyToMonoClass() throws Exception {
Mono<String> result = Mono.just("foo");
when(mockRequest.bodyToMono(String.class)).thenReturn(result);
assertSame(result, wrapper.bodyToMono(String.class));
}
@Test
public void bodyToMonoParameterizedTypeReference() throws Exception {
Mono<String> result = Mono.just("foo");
ParameterizedTypeReference<String> reference = new ParameterizedTypeReference<String>() {};
when(mockRequest.bodyToMono(reference)).thenReturn(result);
assertSame(result, wrapper.bodyToMono(reference));
}
@Test
public void bodyToFluxClass() throws Exception {
Flux<String> result = Flux.just("foo");
when(mockRequest.bodyToFlux(String.class)).thenReturn(result);
assertSame(result, wrapper.bodyToFlux(String.class));
}
@Test
public void bodyToFluxParameterizedTypeReference() throws Exception {
Flux<String> result = Flux.just("foo");
ParameterizedTypeReference<String> reference = new ParameterizedTypeReference<String>() {};
when(mockRequest.bodyToFlux(reference)).thenReturn(result);
assertSame(result, wrapper.bodyToFlux(reference));
}
}