From a9c9bad15ef03e4a0b72d6c11c6725a9cc140be5 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 4 Feb 2016 11:33:40 +0000 Subject: [PATCH] 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 --- .../restdocs/RestDocumentationException.java | 47 ++++ .../restdocs/RestDocumentationHandler.java | 232 ++++++++++++++++++ .../operation/ConversionException.java | 50 ++++ .../restdocs/operation/RequestConverter.java | 37 +++ .../restdocs/operation/ResponseConverter.java | 37 +++ .../RestDocumentationHandlerTests.java | 130 ++++++++++ ...tory.java => MockMvcRequestConverter.java} | 51 ++-- ...ory.java => MockMvcResponseConverter.java} | 17 +- .../mockmvc/MockMvcRestDocumentation.java | 23 +- .../MockMvcRestDocumentationConfigurer.java | 2 +- .../RestDocumentationResultHandler.java | 109 +------- ...java => MockMvcRequestConverterTests.java} | 21 +- ....java => RestAssuredRequestConverter.java} | 9 +- ...java => RestAssuredResponseConverter.java} | 8 +- .../RestAssuredRestDocumentation.java | 19 +- .../restassured/RestDocumentationFilter.java | 116 ++------- ... => RestAssuredRequestConverterTests.java} | 34 +-- 17 files changed, 662 insertions(+), 280 deletions(-) create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationException.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationHandler.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ConversionException.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestConverter.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseConverter.java create mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationHandlerTests.java rename spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/{MockMvcOperationRequestFactory.java => MockMvcRequestConverter.java} (83%) rename spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/{MockMvcOperationResponseFactory.java => MockMvcResponseConverter.java} (77%) rename spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/{MockMvcOperationRequestFactoryTests.java => MockMvcRequestConverterTests.java} (92%) rename spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/{RestAssuredOperationRequestFactory.java => RestAssuredRequestConverter.java} (92%) rename spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/{RestAssuredOperationResponseFactory.java => RestAssuredResponseConverter.java} (84%) rename spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/{RestAssuredOperationRequestFactoryTests.java => RestAssuredRequestConverterTests.java} (85%) diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationException.java new file mode 100644 index 00000000..5bdf27c1 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationException.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-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. + * 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; + +/** + * An exception that can be thrown when a failure occurs during REST documentation + * generation. + * + * @author Andy Wilkinson + */ +public class RestDocumentationException extends RuntimeException { + + /** + * Creates a new {@code RestDocumentationException} with the given {@code cause}. + * + * @param cause the cause + */ + public RestDocumentationException(Throwable cause) { + super(cause); + } + + /** + * Creates a new {@code RestDocumentationException} with the given {@code message} and + * {@code cause}. + * + * @param message the message + * @param cause the cause + */ + public RestDocumentationException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationHandler.java new file mode 100644 index 00000000..f8be483b --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationHandler.java @@ -0,0 +1,232 @@ +/* + * Copyright 2012-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. + * 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; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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.RequestConverter; +import org.springframework.restdocs.operation.ResponseConverter; +import org.springframework.restdocs.operation.StandardOperation; +import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; +import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; +import org.springframework.restdocs.snippet.Snippet; +import org.springframework.util.Assert; + +/** + * A {@code RestDocumentationHandler} is used to produce documentation snippets from the + * request and response of an operation performed on a service. + * + * @param the request type that can be handled + * @param the response type that can be handled + * @author Andy Wilkinson + */ +public final class RestDocumentationHandler { + + private final String identifier; + + private final OperationRequestPreprocessor requestPreprocessor; + + private final OperationResponsePreprocessor responsePreprocessor; + + private final List snippets; + + private final RequestConverter requestConverter; + + private final ResponseConverter responseConverter; + + /** + * Creates a new {@code RestDocumentationHandler} for the operation identified by the + * given {@code identifier}. The given {@code requestConverter} and + * {@code responseConverter} are used to convert the operation's request and response + * into generic {@code OperationRequest} and {@code OperationResponse} instances that + * can then be documented. The given documentation {@code snippets} will be produced. + * + * @param identifier the identifier for the operation + * @param requestConverter the request converter + * @param responseConverter the response converter + * @param snippets the snippets + */ + public RestDocumentationHandler(String identifier, + RequestConverter requestConverter, + ResponseConverter responseConverter, Snippet... snippets) { + this(identifier, requestConverter, responseConverter, + new IdentityOperationRequestPreprocessor(), + new IdentityOperationResponsePreprocessor(), snippets); + } + + /** + * Creates a new {@code RestDocumentationHandler} for the operation identified by the + * given {@code identifier}. The given {@code requestConverter} and + * {@code responseConverter} are used to convert the operation's request and response + * into generic {@code OperationRequest} and {@code OperationResponse} instances that + * can then be documented. The given {@code requestPreprocessor} is applied to the + * request before it is documented. The given documentation {@code snippets} will be + * produced. + * + * @param identifier the identifier for the operation + * @param requestConverter the request converter + * @param responseConverter the response converter + * @param requestPreprocessor the request preprocessor + * @param snippets the snippets + */ + public RestDocumentationHandler(String identifier, + RequestConverter requestConverter, + ResponseConverter responseConverter, + OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) { + this(identifier, requestConverter, responseConverter, requestPreprocessor, + new IdentityOperationResponsePreprocessor(), snippets); + } + + /** + * Creates a new {@code RestDocumentationHandler} for the operation identified by the + * given {@code identifier}. The given {@code requestConverter} and + * {@code responseConverter} are used to convert the operation's request and response + * into generic {@code OperationRequest} and {@code OperationResponse} instances that + * can then be documented. The given {@code responsePreprocessor} is applied to the + * response before it is documented. The given documentation {@code snippets} will be + * produced. + * + * @param identifier the identifier for the operation + * @param requestConverter the request converter + * @param responseConverter the response converter + * @param responsePreprocessor the response preprocessor + * @param snippets the snippets + */ + public RestDocumentationHandler(String identifier, + RequestConverter requestConverter, + ResponseConverter responseConverter, + OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { + this(identifier, requestConverter, responseConverter, + new IdentityOperationRequestPreprocessor(), responsePreprocessor, + snippets); + } + + /** + * Creates a new {@code RestDocumentationHandler} for the operation identified by the + * given {@code identifier}. The given {@code requestConverter} and + * {@code responseConverter} are used to convert the operation's request and response + * into generic {@code OperationRequest} and {@code OperationResponse} instances that + * can then be documented. The given {@code requestPreprocessor} and + * {@code responsePreprocessor} are applied to the request and response before they + * are documented. The given documentation {@code snippets} will be produced. + * + * @param identifier the identifier for the operation + * @param requestConverter the request converter + * @param responseConverter the response converter + * @param requestPreprocessor the request preprocessor + * @param responsePreprocessor the response preprocessor + * @param snippets the snippets + */ + public RestDocumentationHandler(String identifier, + RequestConverter requestConverter, + ResponseConverter responseConverter, + OperationRequestPreprocessor requestPreprocessor, + OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { + Assert.notNull(identifier, "identifier must be non-null"); + Assert.notNull(requestConverter, "requestConverter must be non-null"); + Assert.notNull(responseConverter, "responseConverter must be non-null"); + 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.requestConverter = requestConverter; + this.responseConverter = responseConverter; + this.requestPreprocessor = requestPreprocessor; + this.responsePreprocessor = responsePreprocessor; + this.snippets = new ArrayList<>(Arrays.asList(snippets)); + } + + /** + * Handles the given {@code request} and {@code response}, producing documentation + * snippets for them using the given {@code configuration}. + * + * @param request the request + * @param response the request + * @param configuration the configuration + * @throws RestDocumentationException if a failure occurs during handling + */ + public void handle(REQ request, RESP response, Map configuration) { + OperationRequest operationRequest = this.requestPreprocessor + .preprocess(this.requestConverter.convert(request)); + + OperationResponse operationResponse = this.responsePreprocessor + .preprocess(this.responseConverter.convert(response)); + Map attributes = new HashMap<>(configuration); + Operation operation = new StandardOperation(this.identifier, operationRequest, + operationResponse, attributes); + try { + for (Snippet snippet : getSnippets(attributes)) { + snippet.document(operation); + } + } + catch (IOException ex) { + throw new RestDocumentationException(ex); + } + } + + /** + * Adds the given {@code snippets} such that they are documented when this handler is + * called. + * + * @param snippets the snippets to add + */ + public void addSnippets(Snippet... snippets) { + this.snippets.addAll(Arrays.asList(snippets)); + } + + @SuppressWarnings("unchecked") + private List getSnippets(Map configuration) { + List combinedSnippets = new ArrayList<>(this.snippets); + List defaultSnippets = (List) configuration + .get(SnippetConfigurer.ATTRIBUTE_DEFAULT_SNIPPETS); + if (defaultSnippets != null) { + combinedSnippets.addAll(defaultSnippets); + } + 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; + } + + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ConversionException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ConversionException.java new file mode 100644 index 00000000..397f79d9 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ConversionException.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-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. + * 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.operation; + +/** + * An exception that can be thrown by {@link RequestConverter} and + * {@link ResponseConverter} implementations to indicate that a failure has occurred + * during conversion. + * + * @author Andy Wilkinson + * @see RequestConverter#convert(Object) + * @see ResponseConverter#convert(Object) + */ +public class ConversionException extends RuntimeException { + + /** + * Creates a new {@code ConversionException} with the given {@code cause}. + * + * @param cause the cause + */ + public ConversionException(Throwable cause) { + super(cause); + } + + /** + * Creates a new {@code ConversionException} with the given {@code message} and + * {@code cause}. + * + * @param message the message + * @param cause the cause + */ + public ConversionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestConverter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestConverter.java new file mode 100644 index 00000000..5c2377d5 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestConverter.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-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. + * 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.operation; + +/** + * A {@code RequestConverter} is used to convert an implementation-specific request into + * an {@link OperationRequest}. + * + * @param The implementation-specific request type + * @author Andy Wilkinson + */ +public interface RequestConverter { + + /** + * Converts the given {@code request} into an {@code OperationRequest}. + * + * @param request the request + * @return the operation request + * @throws ConversionException if the conversion fails + */ + OperationRequest convert(R request); + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseConverter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseConverter.java new file mode 100644 index 00000000..48a76be2 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseConverter.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-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. + * 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.operation; + +/** + * A {@code ResponseConverter} is used to convert an implementation-specific response into + * an {@link OperationResponse}. + * + * @param The implementation-specific response type + * @author Andy Wilkinson + */ +public interface ResponseConverter { + + /** + * Converts the given {@code response} into an {@code OperationResponse}. + * + * @param response the response + * @return the operation response + * @throws ConversionException if the conversion fails + */ + OperationResponse convert(R response); + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationHandlerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationHandlerTests.java new file mode 100644 index 00000000..abfdafa5 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationHandlerTests.java @@ -0,0 +1,130 @@ +/* + * Copyright 2012-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. + * 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; + +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.http.HttpHeaders; +import org.springframework.restdocs.config.SnippetConfigurer; +import org.springframework.restdocs.operation.Operation; +import org.springframework.restdocs.operation.OperationRequest; +import org.springframework.restdocs.operation.OperationRequestFactory; +import org.springframework.restdocs.operation.OperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; +import org.springframework.restdocs.operation.RequestConverter; +import org.springframework.restdocs.operation.ResponseConverter; +import org.springframework.restdocs.snippet.Snippet; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link RestDocumentationHandler}. + * + * @author Andy Wilkinson + */ +public class RestDocumentationHandlerTests { + + @SuppressWarnings("unchecked") + private final RequestConverter requestConverter = mock(RequestConverter.class); + + @SuppressWarnings("unchecked") + private final ResponseConverter responseConverter = mock(ResponseConverter.class); + + private final Object request = new Object(); + + private final Object response = new Object(); + + private final OperationRequest operationRequest = new OperationRequestFactory() + .create(URI.create("http://localhost:8080"), null, null, new HttpHeaders(), + null, null); + + private final OperationResponse operationResponse = new OperationResponseFactory() + .create(null, null, null); + + private final Snippet snippet = mock(Snippet.class); + + @Test + public void basicHandling() throws IOException { + given(this.requestConverter.convert(this.request)).willReturn( + this.operationRequest); + given(this.responseConverter.convert(this.response)).willReturn( + this.operationResponse); + HashMap configuration = new HashMap<>(); + new RestDocumentationHandler<>("id", this.requestConverter, + this.responseConverter, this.snippet).handle(this.request, this.response, + configuration); + verifySnippetInvocation(this.snippet, configuration); + } + + @Test + public void defaultSnippetsAreCalled() throws IOException { + given(this.requestConverter.convert(this.request)).willReturn( + this.operationRequest); + given(this.responseConverter.convert(this.response)).willReturn( + this.operationResponse); + HashMap configuration = new HashMap<>(); + Snippet defaultSnippet1 = mock(Snippet.class); + Snippet defaultSnippet2 = mock(Snippet.class); + configuration.put(SnippetConfigurer.ATTRIBUTE_DEFAULT_SNIPPETS, + Arrays.asList(defaultSnippet1, defaultSnippet2)); + new RestDocumentationHandler<>("id", this.requestConverter, + this.responseConverter, this.snippet).handle(this.request, this.response, + configuration); + verifySnippetInvocation(this.snippet, configuration); + verifySnippetInvocation(defaultSnippet1, configuration); + verifySnippetInvocation(defaultSnippet2, configuration); + } + + @Test + public void additionalSnippetsAreCalled() throws IOException { + given(this.requestConverter.convert(this.request)).willReturn( + this.operationRequest); + given(this.responseConverter.convert(this.response)).willReturn( + this.operationResponse); + Snippet additionalSnippet1 = mock(Snippet.class); + Snippet additionalSnippet2 = mock(Snippet.class); + RestDocumentationHandler handler = new RestDocumentationHandler<>( + "id", this.requestConverter, this.responseConverter, this.snippet); + handler.addSnippets(additionalSnippet1, additionalSnippet2); + HashMap configuration = new HashMap<>(); + handler.handle(this.request, this.response, configuration); + verifySnippetInvocation(this.snippet, configuration); + verifySnippetInvocation(additionalSnippet1, configuration); + verifySnippetInvocation(additionalSnippet2, configuration); + } + + private void verifySnippetInvocation(Snippet snippet, Map attributes) + throws IOException { + ArgumentCaptor operation = ArgumentCaptor.forClass(Operation.class); + verify(snippet).document(operation.capture()); + assertThat(this.operationRequest, is(equalTo(operation.getValue().getRequest()))); + assertThat(this.operationResponse, + is(equalTo(operation.getValue().getResponse()))); + assertThat(attributes, is(equalTo(operation.getValue().getAttributes()))); + } +} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactory.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverter.java similarity index 83% rename from spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactory.java rename to spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverter.java index 9449354a..6ad0c553 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactory.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverter.java @@ -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 { 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 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 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 extractParts(MockHttpServletRequest servletRequest) diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationResponseFactory.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java similarity index 77% rename from spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationResponseFactory.java rename to spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java index 4e4f765f..d0677cce 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationResponseFactory.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java @@ -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 { - /** - * 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; } + } diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java index 541f380d..64825f7e 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java @@ -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)); } } diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java index a402487f..7921cab8 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java @@ -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 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); diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java index 075c162f..3194e826 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java @@ -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 delegate; - private final OperationRequestPreprocessor requestPreprocessor; - - private final OperationResponsePreprocessor responsePreprocessor; - - private final List 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 delegate) { + Assert.notNull(delegate, "delegate must be non-null"); + this.delegate = delegate; } @Override public void handle(MvcResult result) throws Exception { - Map 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 configuration = (Map) 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 getSnippets(MvcResult result) { - List combinedSnippets = new ArrayList<>( - (List) ((Map) 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; - } - - } - } diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactoryTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java similarity index 92% rename from spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactoryTests.java rename to spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java index 6770d31b..5c5c2aea 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactoryTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java @@ -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())); } } diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactory.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRequestConverter.java similarity index 92% rename from spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactory.java rename to spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRequestConverter.java index c3ef0b20..cfc19677 100644 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactory.java +++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRequestConverter.java @@ -30,20 +30,23 @@ 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 com.jayway.restassured.response.Header; import com.jayway.restassured.specification.FilterableRequestSpecification; import com.jayway.restassured.specification.MultiPartSpecification; /** - * A factory for creating an {@link OperationRequest} derived from a REST Assured + * A converter for creating an {@link OperationRequest} from a REST Assured * {@link FilterableRequestSpecification}. * * @author Andy Wilkinson */ -class RestAssuredOperationRequestFactory { +class RestAssuredRequestConverter implements + RequestConverter { - OperationRequest createOperationRequest(FilterableRequestSpecification requestSpec) { + @Override + public OperationRequest convert(FilterableRequestSpecification requestSpec) { return new OperationRequestFactory().create(URI.create(requestSpec.getURI()), HttpMethod.valueOf(requestSpec.getMethod().name()), extractContent(requestSpec), extractHeaders(requestSpec), diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationResponseFactory.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredResponseConverter.java similarity index 84% rename from spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationResponseFactory.java rename to spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredResponseConverter.java index bbcd251e..425fc96f 100644 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationResponseFactory.java +++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredResponseConverter.java @@ -20,19 +20,21 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.restdocs.operation.OperationResponse; import org.springframework.restdocs.operation.OperationResponseFactory; +import org.springframework.restdocs.operation.ResponseConverter; import com.jayway.restassured.response.Header; import com.jayway.restassured.response.Response; /** - * A factory for creating an {@link OperationResponse} derived from a REST Assured + * A converter for creating an {@link OperationResponse} from a REST Assured * {@link Response}. * * @author Andy Wilkinson */ -class RestAssuredOperationResponseFactory { +class RestAssuredResponseConverter implements ResponseConverter { - OperationResponse createOperationResponse(Response response) { + @Override + public OperationResponse convert(Response response) { return new OperationResponseFactory().create( HttpStatus.valueOf(response.getStatusCode()), extractHeaders(response), extractContent(response)); diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java index 447ba1f5..0e47cbd2 100644 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java +++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java @@ -17,6 +17,7 @@ package org.springframework.restdocs.restassured; 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; @@ -28,6 +29,10 @@ import org.springframework.restdocs.snippet.Snippet; */ public abstract class RestAssuredRestDocumentation { + private static final RestAssuredRequestConverter REQUEST_CONVERTER = new RestAssuredRequestConverter(); + + private static final RestAssuredResponseConverter RESPONSE_CONVERTER = new RestAssuredResponseConverter(); + private RestAssuredRestDocumentation() { } @@ -41,7 +46,8 @@ public abstract class RestAssuredRestDocumentation { * @return a {@link RestDocumentationFilter} that will produce the documentation */ public static RestDocumentationFilter document(String identifier, Snippet... snippets) { - return new RestDocumentationFilter(identifier, snippets); + return new RestDocumentationFilter(new RestDocumentationHandler<>(identifier, + REQUEST_CONVERTER, RESPONSE_CONVERTER, snippets)); } /** @@ -56,7 +62,8 @@ public abstract class RestAssuredRestDocumentation { */ public static RestDocumentationFilter document(String identifier, OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) { - return new RestDocumentationFilter(identifier, requestPreprocessor, snippets); + return new RestDocumentationFilter(new RestDocumentationHandler<>(identifier, + REQUEST_CONVERTER, RESPONSE_CONVERTER, requestPreprocessor, snippets)); } /** @@ -71,7 +78,8 @@ public abstract class RestAssuredRestDocumentation { */ public static RestDocumentationFilter document(String identifier, OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - return new RestDocumentationFilter(identifier, responsePreprocessor, snippets); + return new RestDocumentationFilter(new RestDocumentationHandler<>(identifier, + REQUEST_CONVERTER, RESPONSE_CONVERTER, responsePreprocessor, snippets)); } /** @@ -89,8 +97,9 @@ public abstract class RestAssuredRestDocumentation { public static RestDocumentationFilter document(String identifier, OperationRequestPreprocessor requestPreprocessor, OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - return new RestDocumentationFilter(identifier, requestPreprocessor, - responsePreprocessor, snippets); + return new RestDocumentationFilter(new RestDocumentationHandler<>(identifier, + REQUEST_CONVERTER, RESPONSE_CONVERTER, requestPreprocessor, + responsePreprocessor, snippets)); } /** diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java index 3460a99a..f588ed16 100644 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java +++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java @@ -16,22 +16,13 @@ package org.springframework.restdocs.restassured; -import java.io.IOException; -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.restdocs.RestDocumentationHandler; import org.springframework.restdocs.snippet.Snippet; +import org.springframework.util.Assert; import com.jayway.restassured.filter.Filter; import com.jayway.restassured.filter.FilterContext; @@ -46,40 +37,12 @@ import com.jayway.restassured.specification.FilterableResponseSpecification; */ public final class RestDocumentationFilter implements Filter { - private final String identifier; + private final RestDocumentationHandler delegate; - private final OperationRequestPreprocessor requestPreprocessor; - - private final OperationResponsePreprocessor responsePreprocessor; - - private final List snippets; - - RestDocumentationFilter(String identifier, Snippet... snippets) { - this(identifier, new IdentityOperationRequestPreprocessor(), - new IdentityOperationResponsePreprocessor(), snippets); - } - - RestDocumentationFilter(String identifier, - OperationRequestPreprocessor operationRequestPreprocessor, - Snippet... snippets) { - this(identifier, operationRequestPreprocessor, - new IdentityOperationResponsePreprocessor(), snippets); - } - - RestDocumentationFilter(String identifier, - OperationResponsePreprocessor operationResponsePreprocessor, - Snippet... snippets) { - this(identifier, new IdentityOperationRequestPreprocessor(), - operationResponsePreprocessor, snippets); - } - - RestDocumentationFilter(String identifier, - OperationRequestPreprocessor requestPreprocessor, - OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - this.identifier = identifier; - this.requestPreprocessor = requestPreprocessor; - this.responsePreprocessor = responsePreprocessor; - this.snippets = new ArrayList<>(Arrays.asList(snippets)); + RestDocumentationFilter( + RestDocumentationHandler delegate) { + Assert.notNull(delegate, "delegate must be non-null"); + this.delegate = delegate; } @Override @@ -87,35 +50,15 @@ public final class RestDocumentationFilter implements Filter { FilterableResponseSpecification responseSpec, FilterContext context) { Response response = context.next(requestSpec, responseSpec); - OperationRequest operationRequest = this.requestPreprocessor - .preprocess(new RestAssuredOperationRequestFactory() - .createOperationRequest(requestSpec)); - OperationResponse operationResponse = this.responsePreprocessor - .preprocess(new RestAssuredOperationResponseFactory() - .createOperationResponse(response)); - - RestDocumentationContext documentationContext = context - .getValue(RestDocumentationContext.class.getName()); - - Map attributes = new HashMap<>(); - attributes.put(RestDocumentationContext.class.getName(), documentationContext); - attributes.put("org.springframework.restdocs.urlTemplate", + Map configuration = new HashMap<>( + context.>getValue("org.springframework.restdocs.configuration")); + configuration.put(RestDocumentationContext.class.getName(), context + .getValue(RestDocumentationContext.class + .getName())); + configuration.put("org.springframework.restdocs.urlTemplate", requestSpec.getUserDefinedPath()); - Map configuration = context - .getValue("org.springframework.restdocs.configuration"); - attributes.putAll(configuration); - Operation operation = new StandardOperation(this.identifier, operationRequest, - operationResponse, attributes); - - try { - for (Snippet snippet : getSnippets(configuration)) { - snippet.document(operation); - } - } - catch (IOException ex) { - throw new RuntimeException(ex); - } + this.delegate.handle(requestSpec, response, configuration); return response; } @@ -128,37 +71,8 @@ public final class RestDocumentationFilter implements Filter { * @return this {@code RestDocumentationFilter} */ public RestDocumentationFilter snippets(Snippet... snippets) { - this.snippets.addAll(Arrays.asList(snippets)); + this.delegate.addSnippets(snippets); return this; } - @SuppressWarnings("unchecked") - private List getSnippets(Map configuration) { - List combinedSnippets = new ArrayList<>( - (List) 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; - } - - } - } diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactoryTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java similarity index 85% rename from spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactoryTests.java rename to spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java index bb532559..f39b15b2 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactoryTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java @@ -36,7 +36,7 @@ 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.restdocs.restassured.RestAssuredOperationRequestFactoryTests.TestApplication; +import org.springframework.restdocs.restassured.RestAssuredRequestConverterTests.TestApplication; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.web.bind.annotation.RequestMapping; @@ -51,7 +51,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; /** - * Tests for {@link RestAssuredOperationRequestFactory}. + * Tests for {@link RestAssuredRequestConverter}. * * @author Andy Wilkinson */ @@ -59,12 +59,12 @@ import static org.junit.Assert.assertThat; @SpringApplicationConfiguration(classes = TestApplication.class) @WebAppConfiguration @IntegrationTest("server.port=0") -public class RestAssuredOperationRequestFactoryTests { +public class RestAssuredRequestConverterTests { @Rule public final ExpectedException thrown = ExpectedException.none(); - private final RestAssuredOperationRequestFactory factory = new RestAssuredOperationRequestFactory(); + private final RestAssuredRequestConverter factory = new RestAssuredRequestConverter(); @Value("${local.server.port}") private int port; @@ -74,7 +74,7 @@ public class RestAssuredOperationRequestFactoryTests { RequestSpecification requestSpec = RestAssured.given().port(this.port); requestSpec.get("/foo/bar"); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getUri(), is(equalTo(URI.create("http://localhost:" + this.port + "/foo/bar")))); } @@ -84,7 +84,7 @@ public class RestAssuredOperationRequestFactoryTests { RequestSpecification requestSpec = RestAssured.given().port(this.port); requestSpec.head("/foo/bar"); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getMethod(), is(equalTo(HttpMethod.HEAD))); } @@ -94,7 +94,7 @@ public class RestAssuredOperationRequestFactoryTests { .queryParam("foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getParameters().size(), is(1)); assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar")))); } @@ -104,7 +104,7 @@ public class RestAssuredOperationRequestFactoryTests { RequestSpecification requestSpec = RestAssured.given().port(this.port); requestSpec.get("/?foo=bar"); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getParameters().size(), is(1)); assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar")))); } @@ -115,7 +115,7 @@ public class RestAssuredOperationRequestFactoryTests { .formParameter("foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getParameters().size(), is(1)); assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar")))); } @@ -126,7 +126,7 @@ public class RestAssuredOperationRequestFactoryTests { .parameter("foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getParameters().size(), is(1)); assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar")))); } @@ -137,7 +137,7 @@ public class RestAssuredOperationRequestFactoryTests { .header("Foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getHeaders().toString(), request.getHeaders().size(), is(3)); assertThat(request.getHeaders().get("Foo"), is(equalTo(Arrays.asList("bar")))); assertThat(request.getHeaders().get("Accept"), is(equalTo(Arrays.asList("*/*")))); @@ -152,7 +152,7 @@ public class RestAssuredOperationRequestFactoryTests { .multiPart("b", new ObjectBody("bar"), "application/json"); requestSpec.post().then().statusCode(200); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); Collection parts = request.getParts(); assertThat(parts.size(), is(2)); Iterator iterator = parts.iterator(); @@ -174,7 +174,7 @@ public class RestAssuredOperationRequestFactoryTests { RequestSpecification requestSpec = RestAssured.given().body("body".getBytes()) .port(this.port); requestSpec.post(); - this.factory.createOperationRequest((FilterableRequestSpecification) requestSpec); + this.factory.convert((FilterableRequestSpecification) requestSpec); } @Test @@ -183,7 +183,7 @@ public class RestAssuredOperationRequestFactoryTests { .port(this.port); requestSpec.post(); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getContentAsString(), is(equalTo("body"))); } @@ -193,7 +193,7 @@ public class RestAssuredOperationRequestFactoryTests { .body(new ObjectBody("bar")).port(this.port); requestSpec.post(); OperationRequest request = this.factory - .createOperationRequest((FilterableRequestSpecification) requestSpec); + .convert((FilterableRequestSpecification) requestSpec); assertThat(request.getContentAsString(), is(equalTo("{\"foo\":\"bar\"}"))); } @@ -205,7 +205,7 @@ public class RestAssuredOperationRequestFactoryTests { requestSpec.post(); this.thrown .expectMessage(equalTo("Unsupported request content: java.io.ByteArrayInputStream")); - this.factory.createOperationRequest((FilterableRequestSpecification) requestSpec); + this.factory.convert((FilterableRequestSpecification) requestSpec); } @Test @@ -214,7 +214,7 @@ public class RestAssuredOperationRequestFactoryTests { .body(new File("src/test/resources/body.txt")).port(this.port); requestSpec.post(); this.thrown.expectMessage(equalTo("Unsupported request content: java.io.File")); - this.factory.createOperationRequest((FilterableRequestSpecification) requestSpec); + this.factory.convert((FilterableRequestSpecification) requestSpec); } /**