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:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <REQ> the request type that can be handled
|
||||
* @param <RESP> the response type that can be handled
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public final class RestDocumentationHandler<REQ, RESP> {
|
||||
|
||||
private final String identifier;
|
||||
|
||||
private final OperationRequestPreprocessor requestPreprocessor;
|
||||
|
||||
private final OperationResponsePreprocessor responsePreprocessor;
|
||||
|
||||
private final List<Snippet> snippets;
|
||||
|
||||
private final RequestConverter<REQ> requestConverter;
|
||||
|
||||
private final ResponseConverter<RESP> 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<REQ> requestConverter,
|
||||
ResponseConverter<RESP> 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<REQ> requestConverter,
|
||||
ResponseConverter<RESP> 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<REQ> requestConverter,
|
||||
ResponseConverter<RESP> 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<REQ> requestConverter,
|
||||
ResponseConverter<RESP> 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<String, Object> configuration) {
|
||||
OperationRequest operationRequest = this.requestPreprocessor
|
||||
.preprocess(this.requestConverter.convert(request));
|
||||
|
||||
OperationResponse operationResponse = this.responsePreprocessor
|
||||
.preprocess(this.responseConverter.convert(response));
|
||||
Map<String, Object> 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<Snippet> getSnippets(Map<String, Object> configuration) {
|
||||
List<Snippet> combinedSnippets = new ArrayList<>(this.snippets);
|
||||
List<Snippet> defaultSnippets = (List<Snippet>) 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <R> The implementation-specific request type
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public interface RequestConverter<R> {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
@@ -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 <R> The implementation-specific response type
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public interface ResponseConverter<R> {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
@@ -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<Object> requestConverter = mock(RequestConverter.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final ResponseConverter<Object> 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<String, Object> 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<String, Object> 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<Object, Object> handler = new RestDocumentationHandler<>(
|
||||
"id", this.requestConverter, this.responseConverter, this.snippet);
|
||||
handler.addSnippets(additionalSnippet1, additionalSnippet2);
|
||||
HashMap<String, Object> 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<String, Object> attributes)
|
||||
throws IOException {
|
||||
ArgumentCaptor<Operation> 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())));
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<FilterableRequestSpecification> {
|
||||
|
||||
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),
|
||||
@@ -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<Response> {
|
||||
|
||||
OperationResponse createOperationResponse(Response response) {
|
||||
@Override
|
||||
public OperationResponse convert(Response response) {
|
||||
return new OperationResponseFactory().create(
|
||||
HttpStatus.valueOf(response.getStatusCode()), extractHeaders(response),
|
||||
extractContent(response));
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<FilterableRequestSpecification, Response> delegate;
|
||||
|
||||
private final OperationRequestPreprocessor requestPreprocessor;
|
||||
|
||||
private final OperationResponsePreprocessor responsePreprocessor;
|
||||
|
||||
private final List<Snippet> 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<FilterableRequestSpecification, Response> 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<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(RestDocumentationContext.class.getName(), documentationContext);
|
||||
attributes.put("org.springframework.restdocs.urlTemplate",
|
||||
Map<String, Object> configuration = new HashMap<>(
|
||||
context.<Map<String, Object>>getValue("org.springframework.restdocs.configuration"));
|
||||
configuration.put(RestDocumentationContext.class.getName(), context
|
||||
.<RestDocumentationContext>getValue(RestDocumentationContext.class
|
||||
.getName()));
|
||||
configuration.put("org.springframework.restdocs.urlTemplate",
|
||||
requestSpec.getUserDefinedPath());
|
||||
Map<String, Object> 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<Snippet> getSnippets(Map<String, Object> configuration) {
|
||||
List<Snippet> combinedSnippets = new ArrayList<>(
|
||||
(List<Snippet>) 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<OperationRequestPart> parts = request.getParts();
|
||||
assertThat(parts.size(), is(2));
|
||||
Iterator<OperationRequestPart> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
Reference in New Issue
Block a user