Add support for using WebFlux's WebTestClient to document an API
Closes gh-384
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.restdocs.config.OperationPreprocessorsConfigurer;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
|
||||
/**
|
||||
* A configurer that can be used to configure the operation preprocessors.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class WebTestClientOperationPreprocessorsConfigurer extends
|
||||
OperationPreprocessorsConfigurer<WebTestClientRestDocumentationConfigurer, WebTestClientOperationPreprocessorsConfigurer>
|
||||
implements ExchangeFilterFunction {
|
||||
|
||||
WebTestClientOperationPreprocessorsConfigurer(WebTestClientRestDocumentationConfigurer parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
|
||||
return and().filter(request, next);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.http.codec.FormHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.FilePart;
|
||||
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
import org.springframework.restdocs.operation.OperationRequestFactory;
|
||||
import org.springframework.restdocs.operation.OperationRequestPart;
|
||||
import org.springframework.restdocs.operation.OperationRequestPartFactory;
|
||||
import org.springframework.restdocs.operation.Parameters;
|
||||
import org.springframework.restdocs.operation.QueryStringParser;
|
||||
import org.springframework.restdocs.operation.RequestConverter;
|
||||
import org.springframework.restdocs.operation.RequestCookie;
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* A {@link RequestConverter} for creating an {@link OperationRequest} derived from an
|
||||
* {@link ExchangeResult}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class WebTestClientRequestConverter implements RequestConverter<ExchangeResult> {
|
||||
|
||||
private static final ResolvableType FORM_DATA_TYPE = ResolvableType
|
||||
.forClassWithGenerics(MultiValueMap.class, String.class, String.class);
|
||||
|
||||
private final QueryStringParser queryStringParser = new QueryStringParser();
|
||||
|
||||
private final FormHttpMessageReader formDataReader = new FormHttpMessageReader();
|
||||
|
||||
@Override
|
||||
public OperationRequest convert(ExchangeResult result) {
|
||||
return new OperationRequestFactory().create(result.getUrl(), result.getMethod(),
|
||||
result.getRequestBodyContent(), extractRequestHeaders(result),
|
||||
extractParameters(result), extractRequestParts(result),
|
||||
extractCookies(result));
|
||||
}
|
||||
|
||||
private HttpHeaders extractRequestHeaders(ExchangeResult result) {
|
||||
HttpHeaders extracted = new HttpHeaders();
|
||||
extracted.putAll(result.getRequestHeaders());
|
||||
extracted.remove(WebTestClient.WEBTESTCLIENT_REQUEST_ID);
|
||||
return extracted;
|
||||
}
|
||||
|
||||
private Parameters extractParameters(ExchangeResult result) {
|
||||
if (result.getMethod() == HttpMethod.GET) {
|
||||
return this.queryStringParser.parse(result.getUrl());
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
if (result.getRequestHeaders().getContentType()
|
||||
.equals(MediaType.APPLICATION_FORM_URLENCODED)) {
|
||||
parameters.addAll(this.formDataReader
|
||||
.readMono(FORM_DATA_TYPE,
|
||||
new ExchangeResultReactiveHttpInputMessage(result), null)
|
||||
.block());
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private List<OperationRequestPart> extractRequestParts(ExchangeResult result) {
|
||||
if (!ClassUtils.isPresent(
|
||||
"org.synchronoss.cloud.nio.multipart.NioMultipartParserListener",
|
||||
getClass().getClassLoader())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return new MultipartHttpMessageReader(new SynchronossPartHttpMessageReader())
|
||||
.readMono(null, new ExchangeResultReactiveHttpInputMessage(result), null)
|
||||
.onErrorReturn(new LinkedMultiValueMap<>()).block().values().stream()
|
||||
.flatMap((parts) -> parts.stream().map(this::createOperationRequestPart))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private OperationRequestPart createOperationRequestPart(Part part) {
|
||||
ByteArrayOutputStream content = readPartBodyContent(part);
|
||||
return new OperationRequestPartFactory().create(part.name(),
|
||||
part instanceof FilePart ? ((FilePart) part).filename() : null,
|
||||
content.toByteArray(), part.headers());
|
||||
}
|
||||
|
||||
private ByteArrayOutputStream readPartBodyContent(Part part) {
|
||||
ByteArrayOutputStream contentStream = new ByteArrayOutputStream();
|
||||
DataBufferUtils.write(part.content(), contentStream).blockFirst();
|
||||
return contentStream;
|
||||
}
|
||||
|
||||
private Collection<RequestCookie> extractCookies(ExchangeResult result) {
|
||||
// Cookies are not available. See https://jira.spring.io/browse/SPR-16124.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private final class ExchangeResultReactiveHttpInputMessage
|
||||
implements ReactiveHttpInputMessage {
|
||||
|
||||
private final ExchangeResult result;
|
||||
|
||||
private ExchangeResultReactiveHttpInputMessage(ExchangeResult result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.result.getRequestHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<DataBuffer> getBody() {
|
||||
DefaultDataBuffer buffer = new DefaultDataBufferFactory().allocateBuffer();
|
||||
buffer.write(this.result.getRequestBodyContent());
|
||||
return Flux.fromArray(new DataBuffer[] { buffer });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.ResponseConverter;
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult;
|
||||
|
||||
/**
|
||||
* A {@link ResponseConverter} for creating an {@link OperationResponse} derived from an
|
||||
* {@link ExchangeResult}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class WebTestClientResponseConverter implements ResponseConverter<ExchangeResult> {
|
||||
|
||||
@Override
|
||||
public OperationResponse convert(ExchangeResult result) {
|
||||
return new OperationResponseFactory().create(result.getStatus(),
|
||||
result.getResponseHeaders(), result.getResponseBodyContent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.restdocs.RestDocumentationContextProvider;
|
||||
import org.springframework.restdocs.generate.RestDocumentationGenerator;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
|
||||
import org.springframework.restdocs.snippet.Snippet;
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient.BodyContentSpec;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient.BodySpec;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient.Builder;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
|
||||
/**
|
||||
* Static factory methods for documenting RESTful APIs using WebFlux's
|
||||
* {@link WebTestClient}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class WebTestClientRestDocumentation {
|
||||
|
||||
private static final WebTestClientRequestConverter REQUEST_CONVERTER = new WebTestClientRequestConverter();
|
||||
|
||||
private static final WebTestClientResponseConverter RESPONSE_CONVERTER = new WebTestClientResponseConverter();
|
||||
|
||||
private WebTestClientRestDocumentation() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to a {@link ExchangeFilterFunction} that can be used to configure a
|
||||
* {@link WebTestClient} instance using the given {@code contextProvider}.
|
||||
*
|
||||
* @param contextProvider the context provider
|
||||
* @return the configurer
|
||||
* @see Builder#filter(ExchangeFilterFunction)
|
||||
*/
|
||||
public static WebTestClientRestDocumentationConfigurer documentationConfiguration(
|
||||
RestDocumentationContextProvider contextProvider) {
|
||||
return new WebTestClientRestDocumentationConfigurer(contextProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Consumer} that, when called, documents the API call with the given
|
||||
* {@code identifier} using the given {@code snippets} in addition to any default
|
||||
* snippets.
|
||||
*
|
||||
* @param identifier an identifier for the API call that is being documented
|
||||
* @param snippets the snippets
|
||||
* @param <T> the type of {@link ExchangeResult} that will be consumed
|
||||
* @return the {@link Consumer} that will document the API call represented by the
|
||||
* {@link ExchangeResult}.
|
||||
* @see BodySpec#consumeWith(Consumer)
|
||||
* @see BodyContentSpec#consumeWith(Consumer)
|
||||
*/
|
||||
public static <T extends ExchangeResult> Consumer<T> document(String identifier,
|
||||
Snippet... snippets) {
|
||||
return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER,
|
||||
RESPONSE_CONVERTER, snippets).handle(result, result,
|
||||
retrieveConfiguration(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Documents the API call with the given {@code identifier} using the given
|
||||
* {@code snippets} in addition to any default snippets. The given
|
||||
* {@code requestPreprocessor} is applied to the request before it is documented.
|
||||
*
|
||||
* @param identifier an identifier for the API call that is being documented
|
||||
* @param requestPreprocessor the request preprocessor
|
||||
* @param snippets the snippets
|
||||
* @param <T> the type of {@link ExchangeResult} that will be consumed
|
||||
* @return the {@link Consumer} that will document the API call represented by the
|
||||
* {@link ExchangeResult}.
|
||||
*/
|
||||
public static <T extends ExchangeResult> Consumer<T> document(String identifier,
|
||||
OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) {
|
||||
return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER,
|
||||
RESPONSE_CONVERTER, requestPreprocessor, snippets).handle(result, result,
|
||||
retrieveConfiguration(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Documents the API call with the given {@code identifier} using the given
|
||||
* {@code snippets} in addition to any default snippets. The given
|
||||
* {@code responsePreprocessor} is applied to the request before it is documented.
|
||||
*
|
||||
* @param identifier an identifier for the API call that is being documented
|
||||
* @param responsePreprocessor the response preprocessor
|
||||
* @param snippets the snippets
|
||||
* @param <T> the type of {@link ExchangeResult} that will be consumed
|
||||
* @return the {@link Consumer} that will document the API call represented by the
|
||||
* {@link ExchangeResult}.
|
||||
*/
|
||||
public static <T extends ExchangeResult> Consumer<T> document(String identifier,
|
||||
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
|
||||
return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER,
|
||||
RESPONSE_CONVERTER, responsePreprocessor, snippets).handle(result, result,
|
||||
retrieveConfiguration(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Documents the API call with the given {@code identifier} using the given
|
||||
* {@code snippets} in addition to any default snippets. The given
|
||||
* {@code requestPreprocessor} and {@code responsePreprocessor} are applied to the
|
||||
* request and response respectively before they are documented.
|
||||
*
|
||||
* @param identifier an identifier for the API call that is being documented
|
||||
* @param requestPreprocessor the request preprocessor
|
||||
* @param responsePreprocessor the response preprocessor
|
||||
* @param snippets the snippets
|
||||
* @param <T> the type of {@link ExchangeResult} that will be consumed
|
||||
* @return the {@link Consumer} that will document the API call represented by the
|
||||
* {@link ExchangeResult}.
|
||||
*/
|
||||
public static <T extends ExchangeResult> Consumer<T> document(String identifier,
|
||||
OperationRequestPreprocessor requestPreprocessor,
|
||||
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
|
||||
return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER,
|
||||
RESPONSE_CONVERTER, requestPreprocessor, responsePreprocessor, snippets)
|
||||
.handle(result, result, retrieveConfiguration(result));
|
||||
}
|
||||
|
||||
private static Map<String, Object> retrieveConfiguration(ExchangeResult result) {
|
||||
Map<String, Object> configuration = new HashMap<>(
|
||||
WebTestClientRestDocumentationConfigurer
|
||||
.retrieveConfiguration(result.getRequestHeaders()));
|
||||
configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE,
|
||||
result.getUriTemplate());
|
||||
return configuration;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.restdocs.RestDocumentationContext;
|
||||
import org.springframework.restdocs.RestDocumentationContextProvider;
|
||||
import org.springframework.restdocs.config.RestDocumentationConfigurer;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
|
||||
/**
|
||||
* A WebFlux-specific {@link RestDocumentationConfigurer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class WebTestClientRestDocumentationConfigurer extends
|
||||
RestDocumentationConfigurer<WebTestClientSnippetConfigurer, WebTestClientOperationPreprocessorsConfigurer, WebTestClientRestDocumentationConfigurer>
|
||||
implements ExchangeFilterFunction {
|
||||
|
||||
private final WebTestClientSnippetConfigurer snippetConfigurer = new WebTestClientSnippetConfigurer(
|
||||
this);
|
||||
|
||||
private static final Map<String, Map<String, Object>> configurations = new ConcurrentHashMap<>();
|
||||
|
||||
private final WebTestClientOperationPreprocessorsConfigurer operationPreprocessorsConfigurer = new WebTestClientOperationPreprocessorsConfigurer(
|
||||
this);
|
||||
|
||||
private final RestDocumentationContextProvider contextProvider;
|
||||
|
||||
WebTestClientRestDocumentationConfigurer(RestDocumentationContextProvider contextProvider) {
|
||||
this.contextProvider = contextProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClientSnippetConfigurer snippets() {
|
||||
return this.snippetConfigurer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClientOperationPreprocessorsConfigurer operationPreprocessors() {
|
||||
return this.operationPreprocessorsConfigurer;
|
||||
}
|
||||
|
||||
private Map<String, Object> createConfiguration() {
|
||||
RestDocumentationContext context = this.contextProvider.beforeOperation();
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put(RestDocumentationContext.class.getName(), context);
|
||||
apply(configuration, context);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
static Map<String, Object> retrieveConfiguration(HttpHeaders headers) {
|
||||
String requestId = headers.getFirst(WebTestClient.WEBTESTCLIENT_REQUEST_ID);
|
||||
return configurations.remove(requestId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
|
||||
String index = request.headers().getFirst(WebTestClient.WEBTESTCLIENT_REQUEST_ID);
|
||||
configurations.put(index, createConfiguration());
|
||||
return next.exchange(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.restdocs.config.SnippetConfigurer;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
|
||||
/**
|
||||
* A {@link SnippetConfigurer} for WebFlux that can be used to configure the generated
|
||||
* documentation snippets.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class WebTestClientSnippetConfigurer extends
|
||||
SnippetConfigurer<WebTestClientRestDocumentationConfigurer, WebTestClientSnippetConfigurer>
|
||||
implements ExchangeFilterFunction {
|
||||
|
||||
WebTestClientSnippetConfigurer(WebTestClientRestDocumentationConfigurer parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
|
||||
return and().filter(request, next);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core classes for using Spring REST Docs with Spring Framework's WebTestClient.
|
||||
*/
|
||||
package org.springframework.restdocs.webtestclient;
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
import org.springframework.restdocs.operation.OperationRequestPart;
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebTestClientRequestConverter}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class WebTestClientRequestConverterTests {
|
||||
|
||||
private final WebTestClientRequestConverter converter = new WebTestClientRequestConverter();
|
||||
|
||||
@Test
|
||||
public void httpRequest() {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null))
|
||||
.configureClient().baseUrl("http://localhost").build().get().uri("/foo")
|
||||
.exchange().expectBody().returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void httpRequestWithCustomPort() {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null))
|
||||
.configureClient().baseUrl("http://localhost:8080").build().get()
|
||||
.uri("/foo").exchange().expectBody().returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("http://localhost:8080/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithHeaders() {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/"), (req) -> null))
|
||||
.configureClient().baseUrl("http://localhost").build().get().uri("/foo")
|
||||
.header("a", "alpha", "apple").header("b", "bravo").exchange()
|
||||
.expectBody().returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
assertThat(request.getHeaders(), hasEntry("a", Arrays.asList("alpha", "apple")));
|
||||
assertThat(request.getHeaders(), hasEntry("b", Arrays.asList("bravo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void httpsRequest() {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null))
|
||||
.configureClient().baseUrl("https://localhost").build().get().uri("/foo")
|
||||
.exchange().expectBody().returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("https://localhost/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void httpsRequestWithCustomPort() {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null))
|
||||
.configureClient().baseUrl("https://localhost:8443").build().get()
|
||||
.uri("/foo").exchange().expectBody().returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("https://localhost:8443/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestWithQueryStringPopulatesParameters() throws Exception {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null))
|
||||
.configureClient().baseUrl("http://localhost").build().get()
|
||||
.uri("/foo?a=alpha&b=bravo").exchange().expectBody().returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(),
|
||||
is(URI.create("http://localhost/foo?a=alpha&b=bravo")));
|
||||
assertThat(request.getParameters().size(), is(2));
|
||||
assertThat(request.getParameters(), hasEntry("a", Arrays.asList("alpha")));
|
||||
assertThat(request.getParameters(), hasEntry("b", Arrays.asList("bravo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithParameters() throws Exception {
|
||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
|
||||
parameters.addAll("a", Arrays.asList("alpha", "apple"));
|
||||
parameters.addAll("b", Arrays.asList("br&vo"));
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> {
|
||||
req.body(BodyExtractors.toFormData()).block();
|
||||
return null;
|
||||
})).configureClient().baseUrl("http://localhost").build().post()
|
||||
.uri("/foo").body(BodyInserters.fromFormData(parameters)).exchange()
|
||||
.expectBody().returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.POST));
|
||||
assertThat(request.getParameters().size(), is(2));
|
||||
assertThat(request.getParameters(),
|
||||
hasEntry("a", Arrays.asList("alpha", "apple")));
|
||||
assertThat(request.getParameters(), hasEntry("b", Arrays.asList("br&vo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartUpload() throws Exception {
|
||||
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
|
||||
multipartData.add("file", new byte[] { 1, 2, 3, 4 });
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> {
|
||||
req.body(BodyExtractors.toMultipartData()).block();
|
||||
return null;
|
||||
})).configureClient().baseUrl("http://localhost").build().post()
|
||||
.uri("/foo").syncBody(multipartData).exchange().expectBody()
|
||||
.returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.POST));
|
||||
assertThat(request.getParts().size(), is(1));
|
||||
OperationRequestPart part = request.getParts().iterator().next();
|
||||
assertThat(part.getName(), is(equalTo("file")));
|
||||
assertThat(part.getSubmittedFileName(), is(nullValue()));
|
||||
assertThat(part.getHeaders().size(), is(2));
|
||||
assertThat(part.getHeaders().getContentLength(), is(4L));
|
||||
assertThat(part.getHeaders().getContentDisposition().getName(),
|
||||
is(equalTo("file")));
|
||||
assertThat(part.getContent(), is(equalTo(new byte[] { 1, 2, 3, 4 })));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartUploadFromResource() throws Exception {
|
||||
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
|
||||
multipartData.add("file", new ByteArrayResource(new byte[] { 1, 2, 3, 4 }) {
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "image.png";
|
||||
}
|
||||
|
||||
});
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> {
|
||||
req.body(BodyExtractors.toMultipartData()).block();
|
||||
return null;
|
||||
})).configureClient().baseUrl("http://localhost").build().post()
|
||||
.uri("/foo").syncBody(multipartData).exchange().expectBody()
|
||||
.returnResult();
|
||||
OperationRequest request = this.converter.convert(result);
|
||||
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.POST));
|
||||
assertThat(request.getParts().size(), is(1));
|
||||
OperationRequestPart part = request.getParts().iterator().next();
|
||||
assertThat(part.getName(), is(equalTo("file")));
|
||||
assertThat(part.getSubmittedFileName(), is(equalTo("image.png")));
|
||||
assertThat(part.getHeaders().size(), is(3));
|
||||
assertThat(part.getHeaders().getContentLength(), is(4L));
|
||||
ContentDisposition contentDisposition = part.getHeaders().getContentDisposition();
|
||||
assertThat(contentDisposition.getName(), is(equalTo("file")));
|
||||
assertThat(contentDisposition.getFilename(), is(equalTo("image.png")));
|
||||
assertThat(part.getHeaders().getContentType(), is(equalTo(MediaType.IMAGE_PNG)));
|
||||
assertThat(part.getContent(), is(equalTo(new byte[] { 1, 2, 3, 4 })));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebTestClientResponseConverter}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class WebTestClientResponseConverterTests {
|
||||
|
||||
private final WebTestClientResponseConverter converter = new WebTestClientResponseConverter();
|
||||
|
||||
@Test
|
||||
public void basicResponse() {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/foo"),
|
||||
(req) -> ServerResponse.ok().syncBody("Hello, World!")))
|
||||
.configureClient().baseUrl("http://localhost").build().get().uri("/foo")
|
||||
.exchange().expectBody().returnResult();
|
||||
OperationResponse response = this.converter.convert(result);
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK));
|
||||
assertThat(response.getContentAsString(), is(equalTo("Hello, World!")));
|
||||
assertThat(response.getHeaders().getContentType(),
|
||||
is(MediaType.parseMediaType("text/plain;charset=UTF-8")));
|
||||
assertThat(response.getHeaders().getContentLength(), is(13L));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.restdocs.JUnitRestDocumentation;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebTestClientRestDocumentationConfigurer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class WebTestClientRestDocumentationConfigurerTests {
|
||||
|
||||
@Rule
|
||||
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
||||
|
||||
private final WebTestClientRestDocumentationConfigurer configurer = new WebTestClientRestDocumentationConfigurer(
|
||||
this.restDocumentation);
|
||||
|
||||
@Test
|
||||
public void configurationCanBeRetrievedButOnlyOnce() {
|
||||
ClientRequest request = mock(ClientRequest.class);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1");
|
||||
given(request.headers()).willReturn(headers);
|
||||
this.configurer.filter(request, mock(ExchangeFunction.class));
|
||||
Map<String, Object> configuration = WebTestClientRestDocumentationConfigurer
|
||||
.retrieveConfiguration(headers);
|
||||
assertThat(configuration, notNullValue());
|
||||
assertThat(
|
||||
WebTestClientRestDocumentationConfigurer.retrieveConfiguration(headers),
|
||||
nullValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2014-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.restdocs.webtestclient;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.restdocs.JUnitRestDocumentation;
|
||||
import org.springframework.restdocs.templates.TemplateFormats;
|
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.requestParts;
|
||||
import static org.springframework.restdocs.templates.TemplateFormats.asciidoctor;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.snippet;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithTitleAndHeader;
|
||||
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
|
||||
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
|
||||
|
||||
/**
|
||||
* Integration tests for using Spring REST Docs with Spring Framework's WebTestClient.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class WebTestClientRestDocumentationIntegrationTests {
|
||||
|
||||
@Rule
|
||||
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
RouterFunction<ServerResponse> route = RouterFunctions
|
||||
.route(RequestPredicates.GET("/"),
|
||||
(request) -> ServerResponse.status(HttpStatus.OK)
|
||||
.body(fromObject(new Person("Jane", "Doe"))))
|
||||
.andRoute(RequestPredicates.GET("/{foo}/{bar}"),
|
||||
(request) -> ServerResponse.status(HttpStatus.OK)
|
||||
.body(fromObject(new Person("Jane", "Doe"))))
|
||||
.andRoute(RequestPredicates.POST("/upload"), (request) -> {
|
||||
return request.body(BodyExtractors.toMultipartData()).map((parts) -> {
|
||||
return ServerResponse.status(HttpStatus.OK).build().block();
|
||||
});
|
||||
});
|
||||
this.webTestClient = WebTestClient.bindToRouterFunction(route).configureClient()
|
||||
.baseUrl("https://api.example.com")
|
||||
.filter(documentationConfiguration(this.restDocumentation)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultSnippetGeneration() {
|
||||
File outputDir = new File("build/generated-snippets/default-snippets");
|
||||
FileSystemUtils.deleteRecursively(outputDir);
|
||||
this.webTestClient.get().uri("/").exchange().expectStatus().isOk().expectBody()
|
||||
.consumeWith(document("default-snippets"));
|
||||
assertExpectedSnippetFilesExist(outputDir, "http-request.adoc",
|
||||
"http-response.adoc", "curl-request.adoc", "httpie-request.adoc",
|
||||
"request-body.adoc", "response-body.adoc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathParametersSnippet() {
|
||||
this.webTestClient.get().uri("/{foo}/{bar}", "1", "2").exchange().expectStatus()
|
||||
.isOk().expectBody()
|
||||
.consumeWith(document("path-parameters", pathParameters(
|
||||
parameterWithName("foo").description("Foo description"),
|
||||
parameterWithName("bar").description("Bar description"))));
|
||||
assertThat(
|
||||
new File("build/generated-snippets/path-parameters/path-parameters.adoc"),
|
||||
is(snippet(asciidoctor()).withContents(
|
||||
tableWithTitleAndHeader(TemplateFormats.asciidoctor(),
|
||||
"/{foo}/{bar}", "Parameter", "Description")
|
||||
.row("`foo`", "Foo description")
|
||||
.row("`bar`", "Bar description"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestParametersSnippet() {
|
||||
this.webTestClient.get().uri("/?a=alpha&b=bravo").exchange().expectStatus().isOk()
|
||||
.expectBody()
|
||||
.consumeWith(document("request-parameters", requestParameters(
|
||||
parameterWithName("a").description("Alpha description"),
|
||||
parameterWithName("b").description("Bravo description"))));
|
||||
assertThat(
|
||||
new File(
|
||||
"build/generated-snippets/request-parameters/request-parameters.adoc"),
|
||||
is(snippet(asciidoctor()).withContents(
|
||||
tableWithHeader(TemplateFormats.asciidoctor(), "Parameter",
|
||||
"Description").row("`a`", "Alpha description").row("`b`",
|
||||
"Bravo description"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipart() throws Exception {
|
||||
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
|
||||
multipartData.add("a", "alpha");
|
||||
multipartData.add("b", "bravo");
|
||||
Consumer<EntityExchangeResult<byte[]>> documentation = document("multipart",
|
||||
requestParts(partWithName("a").description("Part a"),
|
||||
partWithName("b").description("Part b")));
|
||||
this.webTestClient.post().uri("/upload").syncBody(multipartData).exchange()
|
||||
.expectStatus().isOk().expectBody().consumeWith(documentation);
|
||||
assertThat(new File("build/generated-snippets/multipart/request-parts.adoc"),
|
||||
is(snippet(asciidoctor())
|
||||
.withContents(tableWithHeader(TemplateFormats.asciidoctor(),
|
||||
"Part", "Description").row("`a`", "Part a").row("`b`",
|
||||
"Part b"))));
|
||||
}
|
||||
|
||||
private void assertExpectedSnippetFilesExist(File directory, String... snippets) {
|
||||
Set<File> actual = new HashSet<>(Arrays.asList(directory.listFiles()));
|
||||
Set<File> expected = Stream.of(snippets)
|
||||
.map((snippet) -> new File(directory, snippet))
|
||||
.collect(Collectors.toSet());
|
||||
assertThat(actual, equalTo(expected));
|
||||
}
|
||||
|
||||
/**
|
||||
* A person.
|
||||
*/
|
||||
static class Person {
|
||||
|
||||
private final String firstName;
|
||||
|
||||
private final String lastName;
|
||||
|
||||
Person(String firstName, String lastName) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return this.firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return this.lastName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user