Reduce duplication by introducing RestDocumentationHandler
Previously, logic for creating an Operation, determining the snippets to call, and calling them was duplicated in both the MockMvc and REST Assured modules. This commit introduces a new core class, RestDocumentationHandler, that now does the bulk of the work in a reusable manner. The MockMvc and REST Assured modules have been updated to delegate to RestDocumentationHandler. Closes gh-194
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -32,11 +32,13 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockMultipartHttpServletRequest;
|
||||
import org.springframework.restdocs.operation.ConversionException;
|
||||
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.RequestConverter;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -44,13 +46,13 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import static org.springframework.restdocs.mockmvc.IterableEnumeration.iterable;
|
||||
|
||||
/**
|
||||
* A factory for creating an {@link OperationRequest} from a
|
||||
* A converter for creating an {@link OperationRequest} from a
|
||||
* {@link MockHttpServletRequest}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
class MockMvcOperationRequestFactory {
|
||||
class MockMvcRequestConverter implements RequestConverter<MockHttpServletRequest> {
|
||||
|
||||
private static final String SCHEME_HTTP = "http";
|
||||
|
||||
@@ -60,28 +62,29 @@ class MockMvcOperationRequestFactory {
|
||||
|
||||
private static final int STANDARD_PORT_HTTPS = 443;
|
||||
|
||||
/**
|
||||
* Creates a new {@code OperationRequest} derived from the given {@code mockRequest}.
|
||||
*
|
||||
* @param mockRequest the request
|
||||
* @return the {@code OperationRequest}
|
||||
* @throws Exception if the request could not be created
|
||||
*/
|
||||
OperationRequest createOperationRequest(MockHttpServletRequest mockRequest)
|
||||
throws Exception {
|
||||
HttpHeaders headers = extractHeaders(mockRequest);
|
||||
Parameters parameters = extractParameters(mockRequest);
|
||||
List<OperationRequestPart> parts = extractParts(mockRequest);
|
||||
String queryString = mockRequest.getQueryString();
|
||||
if (!StringUtils.hasText(queryString) && "GET".equals(mockRequest.getMethod())) {
|
||||
queryString = parameters.toQueryString();
|
||||
@Override
|
||||
public OperationRequest convert(MockHttpServletRequest mockRequest) {
|
||||
try {
|
||||
HttpHeaders headers = extractHeaders(mockRequest);
|
||||
Parameters parameters = extractParameters(mockRequest);
|
||||
List<OperationRequestPart> parts = extractParts(mockRequest);
|
||||
String queryString = mockRequest.getQueryString();
|
||||
if (!StringUtils.hasText(queryString)
|
||||
&& "GET".equals(mockRequest.getMethod())) {
|
||||
queryString = parameters.toQueryString();
|
||||
}
|
||||
return new OperationRequestFactory()
|
||||
.create(URI
|
||||
.create(getRequestUri(mockRequest)
|
||||
+ (StringUtils.hasText(queryString) ? "?"
|
||||
+ queryString : "")),
|
||||
HttpMethod.valueOf(mockRequest.getMethod()), FileCopyUtils
|
||||
.copyToByteArray(mockRequest.getInputStream()),
|
||||
headers, parameters, parts);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new ConversionException(ex);
|
||||
}
|
||||
return new OperationRequestFactory().create(
|
||||
URI.create(getRequestUri(mockRequest)
|
||||
+ (StringUtils.hasText(queryString) ? "?" + queryString : "")),
|
||||
HttpMethod.valueOf(mockRequest.getMethod()),
|
||||
FileCopyUtils.copyToByteArray(mockRequest.getInputStream()), headers,
|
||||
parameters, parts);
|
||||
}
|
||||
|
||||
private List<OperationRequestPart> extractParts(MockHttpServletRequest servletRequest)
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -21,22 +21,18 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.ResponseConverter;
|
||||
|
||||
/**
|
||||
* A factory for creating an {@link OperationResponse} derived from a
|
||||
* A converter for creating an {@link OperationResponse} derived from a
|
||||
* {@link MockHttpServletResponse}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class MockMvcOperationResponseFactory {
|
||||
class MockMvcResponseConverter implements ResponseConverter<MockHttpServletResponse> {
|
||||
|
||||
/**
|
||||
* Create a new {@code OperationResponse} derived from the given {@code mockResponse}.
|
||||
*
|
||||
* @param mockResponse the response
|
||||
* @return the {@code OperationResponse}
|
||||
*/
|
||||
OperationResponse createOperationResponse(MockHttpServletResponse mockResponse) {
|
||||
@Override
|
||||
public OperationResponse convert(MockHttpServletResponse mockResponse) {
|
||||
return new OperationResponseFactory().create(
|
||||
HttpStatus.valueOf(mockResponse.getStatus()),
|
||||
extractHeaders(mockResponse), mockResponse.getContentAsByteArray());
|
||||
@@ -51,4 +47,5 @@ class MockMvcOperationResponseFactory {
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.restdocs.mockmvc;
|
||||
|
||||
import org.springframework.restdocs.RestDocumentation;
|
||||
import org.springframework.restdocs.RestDocumentationHandler;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
|
||||
import org.springframework.restdocs.snippet.Snippet;
|
||||
@@ -32,6 +33,10 @@ import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
|
||||
*/
|
||||
public abstract class MockMvcRestDocumentation {
|
||||
|
||||
private static final MockMvcRequestConverter REQUEST_CONVERTER = new MockMvcRequestConverter();
|
||||
|
||||
private static final MockMvcResponseConverter RESPONSE_CONVERTER = new MockMvcResponseConverter();
|
||||
|
||||
private MockMvcRestDocumentation() {
|
||||
|
||||
}
|
||||
@@ -61,7 +66,8 @@ public abstract class MockMvcRestDocumentation {
|
||||
*/
|
||||
public static RestDocumentationResultHandler document(String identifier,
|
||||
Snippet... snippets) {
|
||||
return new RestDocumentationResultHandler(identifier, snippets);
|
||||
return new RestDocumentationResultHandler(new RestDocumentationHandler<>(
|
||||
identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, snippets));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,8 +84,9 @@ public abstract class MockMvcRestDocumentation {
|
||||
*/
|
||||
public static RestDocumentationResultHandler document(String identifier,
|
||||
OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) {
|
||||
return new RestDocumentationResultHandler(identifier, requestPreprocessor,
|
||||
snippets);
|
||||
return new RestDocumentationResultHandler(new RestDocumentationHandler<>(
|
||||
identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, requestPreprocessor,
|
||||
snippets));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,8 +103,9 @@ public abstract class MockMvcRestDocumentation {
|
||||
*/
|
||||
public static RestDocumentationResultHandler document(String identifier,
|
||||
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
|
||||
return new RestDocumentationResultHandler(identifier, responsePreprocessor,
|
||||
snippets);
|
||||
return new RestDocumentationResultHandler(new RestDocumentationHandler<>(
|
||||
identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, responsePreprocessor,
|
||||
snippets));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,8 +125,9 @@ public abstract class MockMvcRestDocumentation {
|
||||
public static RestDocumentationResultHandler document(String identifier,
|
||||
OperationRequestPreprocessor requestPreprocessor,
|
||||
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
|
||||
return new RestDocumentationResultHandler(identifier, requestPreprocessor,
|
||||
responsePreprocessor, snippets);
|
||||
return new RestDocumentationResultHandler(new RestDocumentationHandler<>(
|
||||
identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, requestPreprocessor,
|
||||
responsePreprocessor, snippets));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,12 +88,12 @@ public class MockMvcRestDocumentationConfigurer
|
||||
@Override
|
||||
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
|
||||
RestDocumentationContext context = this.restDocumentation.beforeOperation();
|
||||
request.setAttribute(RestDocumentationContext.class.getName(), context);
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put(MockHttpServletRequest.class.getName(), request);
|
||||
String urlTemplateAttribute = "org.springframework.restdocs.urlTemplate";
|
||||
configuration.put(urlTemplateAttribute,
|
||||
request.getAttribute(urlTemplateAttribute));
|
||||
configuration.put(RestDocumentationContext.class.getName(), context);
|
||||
request.setAttribute("org.springframework.restdocs.configuration",
|
||||
configuration);
|
||||
MockMvcRestDocumentationConfigurer.this.apply(configuration, context);
|
||||
|
||||
@@ -16,20 +16,11 @@
|
||||
|
||||
package org.springframework.restdocs.mockmvc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.RestDocumentationContext;
|
||||
import org.springframework.restdocs.config.SnippetConfigurer;
|
||||
import org.springframework.restdocs.operation.Operation;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.StandardOperation;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.restdocs.RestDocumentationHandler;
|
||||
import org.springframework.restdocs.snippet.Snippet;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultHandler;
|
||||
@@ -44,68 +35,20 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class RestDocumentationResultHandler implements ResultHandler {
|
||||
|
||||
private final String identifier;
|
||||
private final RestDocumentationHandler<MockHttpServletRequest, MockHttpServletResponse> delegate;
|
||||
|
||||
private final OperationRequestPreprocessor requestPreprocessor;
|
||||
|
||||
private final OperationResponsePreprocessor responsePreprocessor;
|
||||
|
||||
private final List<Snippet> snippets;
|
||||
|
||||
RestDocumentationResultHandler(String identifier, Snippet... snippets) {
|
||||
this(identifier, new IdentityOperationRequestPreprocessor(),
|
||||
new IdentityOperationResponsePreprocessor(), snippets);
|
||||
}
|
||||
|
||||
RestDocumentationResultHandler(String identifier,
|
||||
OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) {
|
||||
this(identifier, requestPreprocessor,
|
||||
new IdentityOperationResponsePreprocessor(), snippets);
|
||||
}
|
||||
|
||||
RestDocumentationResultHandler(String identifier,
|
||||
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
|
||||
this(identifier, new IdentityOperationRequestPreprocessor(),
|
||||
responsePreprocessor, snippets);
|
||||
}
|
||||
|
||||
RestDocumentationResultHandler(String identifier,
|
||||
OperationRequestPreprocessor requestPreprocessor,
|
||||
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
|
||||
Assert.notNull(identifier, "identifier must be non-null");
|
||||
Assert.notNull(requestPreprocessor, "requestPreprocessor must be non-null");
|
||||
Assert.notNull(responsePreprocessor, "responsePreprocessor must be non-null");
|
||||
Assert.notNull(snippets, "snippets must be non-null");
|
||||
this.identifier = identifier;
|
||||
this.requestPreprocessor = requestPreprocessor;
|
||||
this.responsePreprocessor = responsePreprocessor;
|
||||
this.snippets = new ArrayList<>(Arrays.asList(snippets));
|
||||
RestDocumentationResultHandler(
|
||||
RestDocumentationHandler<MockHttpServletRequest, MockHttpServletResponse> delegate) {
|
||||
Assert.notNull(delegate, "delegate must be non-null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MvcResult result) throws Exception {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(RestDocumentationContext.class.getName(), result.getRequest()
|
||||
.getAttribute(RestDocumentationContext.class.getName()));
|
||||
attributes.put("org.springframework.restdocs.urlTemplate", result.getRequest()
|
||||
.getAttribute("org.springframework.restdocs.urlTemplate"));
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> configuration = (Map<String, Object>) result.getRequest()
|
||||
.getAttribute("org.springframework.restdocs.configuration");
|
||||
attributes.putAll(configuration);
|
||||
|
||||
OperationRequest request = this.requestPreprocessor
|
||||
.preprocess(new MockMvcOperationRequestFactory()
|
||||
.createOperationRequest(result.getRequest()));
|
||||
|
||||
OperationResponse response = this.responsePreprocessor
|
||||
.preprocess(new MockMvcOperationResponseFactory()
|
||||
.createOperationResponse(result.getResponse()));
|
||||
Operation operation = new StandardOperation(this.identifier, request, response,
|
||||
attributes);
|
||||
for (Snippet snippet : getSnippets(result)) {
|
||||
snippet.document(operation);
|
||||
}
|
||||
this.delegate.handle(result.getRequest(), result.getResponse(), configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,41 +56,11 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
* handler is called.
|
||||
*
|
||||
* @param snippets the snippets to add
|
||||
* @return this {@code ResultDocumentationResultHandler}
|
||||
* @return this {@code RestDocumentationResultHandler}
|
||||
*/
|
||||
public RestDocumentationResultHandler snippets(Snippet... snippets) {
|
||||
this.snippets.addAll(Arrays.asList(snippets));
|
||||
this.delegate.addSnippets(snippets);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Snippet> getSnippets(MvcResult result) {
|
||||
List<Snippet> combinedSnippets = new ArrayList<>(
|
||||
(List<Snippet>) ((Map<String, Object>) result.getRequest().getAttribute(
|
||||
"org.springframework.restdocs.configuration"))
|
||||
.get(SnippetConfigurer.ATTRIBUTE_DEFAULT_SNIPPETS));
|
||||
combinedSnippets.addAll(this.snippets);
|
||||
return combinedSnippets;
|
||||
}
|
||||
|
||||
private static final class IdentityOperationRequestPreprocessor implements
|
||||
OperationRequestPreprocessor {
|
||||
|
||||
@Override
|
||||
public OperationRequest preprocess(OperationRequest request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class IdentityOperationResponsePreprocessor implements
|
||||
OperationResponsePreprocessor {
|
||||
|
||||
@Override
|
||||
public OperationResponse preprocess(OperationResponse response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -43,13 +43,13 @@ import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link MockMvcOperationRequestFactory}.
|
||||
* Tests for {@link MockMvcRequestConverter}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class MockMvcOperationRequestFactoryTests {
|
||||
public class MockMvcRequestConverterTests {
|
||||
|
||||
private final MockMvcOperationRequestFactory factory = new MockMvcOperationRequestFactory();
|
||||
private final MockMvcRequestConverter factory = new MockMvcRequestConverter();
|
||||
|
||||
@Test
|
||||
public void httpRequest() throws Exception {
|
||||
@@ -64,7 +64,7 @@ public class MockMvcOperationRequestFactoryTests {
|
||||
MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo")
|
||||
.buildRequest(new MockServletContext());
|
||||
mockRequest.setServerPort(8080);
|
||||
OperationRequest request = this.factory.createOperationRequest(mockRequest);
|
||||
OperationRequest request = this.factory.convert(mockRequest);
|
||||
assertThat(request.getUri(), is(URI.create("http://localhost:8080/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
@@ -93,7 +93,7 @@ public class MockMvcOperationRequestFactoryTests {
|
||||
.buildRequest(new MockServletContext());
|
||||
mockRequest.setScheme("https");
|
||||
mockRequest.setServerPort(443);
|
||||
OperationRequest request = this.factory.createOperationRequest(mockRequest);
|
||||
OperationRequest request = this.factory.convert(mockRequest);
|
||||
assertThat(request.getUri(), is(URI.create("https://localhost/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public class MockMvcOperationRequestFactoryTests {
|
||||
.buildRequest(new MockServletContext());
|
||||
mockRequest.setScheme("https");
|
||||
mockRequest.setServerPort(8443);
|
||||
OperationRequest request = this.factory.createOperationRequest(mockRequest);
|
||||
OperationRequest request = this.factory.convert(mockRequest);
|
||||
assertThat(request.getUri(), is(URI.create("https://localhost:8443/foo")));
|
||||
assertThat(request.getMethod(), is(HttpMethod.GET));
|
||||
}
|
||||
@@ -191,7 +191,7 @@ public class MockMvcOperationRequestFactoryTests {
|
||||
given(mockPart.getName()).willReturn("part-name");
|
||||
given(mockPart.getSubmittedFileName()).willReturn("submitted.txt");
|
||||
mockRequest.addPart(mockPart);
|
||||
OperationRequest request = this.factory.createOperationRequest(mockRequest);
|
||||
OperationRequest request = this.factory.convert(mockRequest);
|
||||
assertThat(request.getParts().size(), is(1));
|
||||
OperationRequestPart part = request.getParts().iterator().next();
|
||||
assertThat(part.getName(), is(equalTo("part-name")));
|
||||
@@ -216,7 +216,7 @@ public class MockMvcOperationRequestFactoryTests {
|
||||
given(mockPart.getSubmittedFileName()).willReturn("submitted.png");
|
||||
given(mockPart.getContentType()).willReturn("image/png");
|
||||
mockRequest.addPart(mockPart);
|
||||
OperationRequest request = this.factory.createOperationRequest(mockRequest);
|
||||
OperationRequest request = this.factory.convert(mockRequest);
|
||||
assertThat(request.getParts().size(), is(1));
|
||||
OperationRequestPart part = request.getParts().iterator().next();
|
||||
assertThat(part.getName(), is(equalTo("part-name")));
|
||||
@@ -229,8 +229,7 @@ public class MockMvcOperationRequestFactoryTests {
|
||||
|
||||
private OperationRequest createOperationRequest(MockHttpServletRequestBuilder builder)
|
||||
throws Exception {
|
||||
return this.factory.createOperationRequest(builder
|
||||
.buildRequest(new MockServletContext()));
|
||||
return this.factory.convert(builder.buildRequest(new MockServletContext()));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user