Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Replacement for binary content of multipart requests #369

Merged
merged 2 commits into from
Jan 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package capital.scalable.restdocs.response;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The headers above the new files are missing but I can add them as well. We have a Maven plugin configured that adds them automatically.


import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
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.preprocess.OperationPreprocessorAdapter;

import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;

public class MultipartContentOperationPreprocessor extends OperationPreprocessorAdapter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you are extending OperationPreprocessorAdapter instead of implementing ContentModifier like the BinaryReplacementContentModifier? Implementing ContentModifier makes the code a bit simpler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extending OperationPreprocessorAdapter because ContentModifier takes original content (look at ContentModifyingOperationPreprocessor. In case of multipart/form-data content is empty byte array and all binary data holds in parts field.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this makes sense.

private static final byte[] BINARY_REPLACEMENT = "<binary>".getBytes(UTF_8);

private final OperationRequestPartFactory partFactory = new OperationRequestPartFactory();
private final OperationRequestFactory requestFactory = new OperationRequestFactory();

@Override
public OperationRequest preprocess(OperationRequest request) {
if (isMultipart(request)) {
List<OperationRequestPart> parts = request.getParts().stream()
.map(this::replaceBinary)
.collect(toList());
return requestFactory.create(request.getUri(),
request.getMethod(), request.getContent(), request.getHeaders(), request.getParameters()
, parts);
}
return request;
}

private boolean isMultipart(OperationRequest request) {
List<String> contentTypes = request.getHeaders().get(HttpHeaders.CONTENT_TYPE);
return contentTypes != null && contentTypes.contains(MediaType.MULTIPART_FORM_DATA_VALUE);
}

private OperationRequestPart replaceBinary(OperationRequestPart part) {
return partFactory.create(part.getName(), part.getSubmittedFileName()
, BINARY_REPLACEMENT, part.getHeaders());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ public static OperationPreprocessor replaceBinaryContent() {
return new ContentModifyingOperationPreprocessor(new BinaryReplacementContentModifier());
}

/**
* For binary content of multipart/form-data requests, replaces value with "&lt;binary&gt;".
*
* @return a preprocessor replacing binary content of multipart/form-data requests
*/
public static OperationPreprocessor replaceMultipartBinaryContent() {
return new MultipartContentOperationPreprocessor();
}

/**
* For JSON content, cuts the length of all JSON arrays in the response to 3 elements.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package capital.scalable.restdocs.response;

import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
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 java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

public class MultipartContentOperationPreprocessorTest {
private static final byte[] EXPECTED_CONTENT = "<binary>".getBytes(StandardCharsets.UTF_8);
private static final byte[] BINARY_CONTENT = new byte[]{1, 2, 3, 3, 3};
private MultipartContentOperationPreprocessor preprocessor;
private OperationRequestFactory requestFactory;
private OperationRequestPartFactory partFactory;

@Before
public void setUp() {
preprocessor = new MultipartContentOperationPreprocessor();
requestFactory = new OperationRequestFactory();
partFactory = new OperationRequestPartFactory();
}

@Test
public void shouldReplaceContent() throws URISyntaxException {
OperationRequest request = createRequestWithContentType(MediaType.MULTIPART_FORM_DATA_VALUE);

OperationRequest processedRequest = preprocessor.preprocess(request);
assertArrayEquals(request.getContent(), processedRequest.getContent());
assertEquals(request.getHeaders(), processedRequest.getHeaders());
assertEquals(request.getMethod(), processedRequest.getMethod());
assertEquals(request.getUri(), processedRequest.getUri());
assertEquals(request.getParameters(), processedRequest.getParameters());
for (OperationRequestPart part : processedRequest.getParts()) {
assertArrayEquals(EXPECTED_CONTENT, part.getContent());
}
}

@Test
public void shouldNotReplaceWhenNotMultipart() throws URISyntaxException {
OperationRequest request = createRequestWithContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);

OperationRequest processedRequest = preprocessor.preprocess(request);
assertArrayEquals(request.getContent(), processedRequest.getContent());
assertEquals(request.getHeaders(), processedRequest.getHeaders());
assertEquals(request.getMethod(), processedRequest.getMethod());
assertEquals(request.getUri(), processedRequest.getUri());
assertEquals(request.getParameters(), processedRequest.getParameters());

OperationRequestPart[] expectedParts = request.getParts().toArray(new OperationRequestPart[0]);
OperationRequestPart[] operationRequestParts = processedRequest.getParts().toArray(new OperationRequestPart[0]);
for (int i = 0; i < operationRequestParts.length; i++) {
assertEquals(expectedParts[i], operationRequestParts[i]);
}
}

private OperationRequest createRequestWithContentType(String contentType) throws URISyntaxException {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, contentType);
Parameters parameters = new Parameters();
parameters.add("parameter", "value");
List<OperationRequestPart> requestParts = new ArrayList<>();
requestParts.add(partFactory.create("first", "file1", BINARY_CONTENT, new HttpHeaders()));
return requestFactory.create(new URI("http://localhost"), HttpMethod.POST, BINARY_CONTENT, headers, parameters, requestParts);
}
}
1 change: 1 addition & 0 deletions spring-auto-restdocs-docs/other.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ See example project for link:{example-dir}/src/main/asciidoc/index.adoc#overview
Preprocessors are available for use in order to improve quality of endpoint examples.

- link:{core-package}/response/BinaryReplacementContentModifier.java[binary replacement]: replaces content with `<binary>` for common mime types
- link:{core-package}/response/MultipartContentOperationPreprocessor.java[multipart binary replacement]: replaces content with `<binary>` for multipart/form-data requests
- link:{core-package}/response/ArrayLimitingJsonContentModifier.java[limit JSON array length]: limits all JSON arrays to 3 items

For a list of standard preprocessors see link:{restdocs-package}/operation/preprocess/Preprocessors.java[Preprocessors].
Expand Down