fix: Added support for multiple files in multipart REST (#8986)

* fix: Added support for multiple files in multipart REST

* Added test case

* Review comment

* Review comment
This commit is contained in:
Nidhi 2021-11-09 10:33:54 +05:30 committed by GitHub
parent f17ce968e7
commit ec34ef9a91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 17 deletions

View File

@ -26,6 +26,8 @@ import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -150,26 +152,15 @@ public class DataUtils {
if (MultipartFormDataType.TEXT.equals(multipartFormDataType)) {
bodyBuilder.part(key, property.getValue());
} else if (MultipartFormDataType.FILE.equals(multipartFormDataType)) {
MultipartFormDataDTO multipartFormDataDTO = null;
try {
multipartFormDataDTO = objectMapper.readValue(
String.valueOf(property.getValue()),
MultipartFormDataDTO.class);
populateFileTypeBodyBuilder(bodyBuilder, property, outputMessage);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new AppsmithPluginException(
AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR,
"Unable to parse content. Expected to receive an array or object of multipart data"
);
}
final MultipartFormDataDTO finalMultipartFormDataDTO = multipartFormDataDTO;
Flux<DataBuffer> data = DataBufferUtils.readInputStream(
() -> new ByteArrayInputStream(String
.valueOf(finalMultipartFormDataDTO.getData())
.getBytes(StandardCharsets.UTF_8)),
outputMessage.bufferFactory(),
4096);
bodyBuilder.asyncPart(key, data, DataBuffer.class)
.filename(multipartFormDataDTO.getName())
.contentType(MediaType.valueOf(multipartFormDataDTO.getType()));
}
}
@ -181,6 +172,38 @@ public class DataUtils {
});
}
private void populateFileTypeBodyBuilder(MultipartBodyBuilder bodyBuilder, Property property, ClientHttpRequest outputMessage)
throws JsonProcessingException {
final Object fileValue = property.getValue();
final String key = property.getKey();
List<MultipartFormDataDTO> multipartFormDataDTOs = new ArrayList<>();
try {
multipartFormDataDTOs = Arrays.asList(
objectMapper.readValue(
String.valueOf(fileValue),
MultipartFormDataDTO[].class));
} catch (JsonProcessingException e) {
e.printStackTrace();
final MultipartFormDataDTO multipartFormDataDTO = objectMapper.readValue(String.valueOf(fileValue),
MultipartFormDataDTO.class);
multipartFormDataDTOs.add(multipartFormDataDTO);
}
multipartFormDataDTOs.forEach(multipartFormDataDTO -> {
final MultipartFormDataDTO finalMultipartFormDataDTO = multipartFormDataDTO;
Flux<DataBuffer> data = DataBufferUtils.readInputStream(
() -> new ByteArrayInputStream(String
.valueOf(finalMultipartFormDataDTO.getData())
.getBytes(StandardCharsets.UTF_8)),
outputMessage.bufferFactory(),
4096);
bodyBuilder.asyncPart(key, data, DataBuffer.class)
.filename(multipartFormDataDTO.getName())
.contentType(MediaType.valueOf(multipartFormDataDTO.getType()));
});
}
/**
* Given a JSON string, we infer the top-level type of the object it represents and then parse it into that
* type. However, only `Map` and `List` top-levels are supported. Note that the map or list may contain

View File

@ -163,6 +163,41 @@ public class DataUtilsTest {
.verify();
}
@Test
public void testParseMultipartFileData_withValidMultipleFileList_returnsExpectedBody() {
List<Property> properties = new ArrayList<>();
final Property p1 = new Property("fileType", "[{\"name\": \"test1.json\", \"type\": \"application/json\", \"data\" : {}}, {\"name\": \"test2.json\", \"type\": \"application/json\", \"data\" : {}}]");
p1.setType("file");
properties.add(p1);
final BodyInserter<Object, MockClientHttpRequest> bodyInserter =
(BodyInserter<Object, MockClientHttpRequest>) dataUtils.parseMultipartFileData(properties);
MockClientHttpRequest request = new MockClientHttpRequest(HttpMethod.POST, URI.create("https://example.com"));
Mono<Void> result = bodyInserter.insert(request, this.context);
StepVerifier.create(result).expectComplete().verify();
StepVerifier.create(DataBufferUtils.join(request.getBody()))
.consumeNextWith(dataBuffer -> {
byte[] resultBytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(resultBytes);
DataBufferUtils.release(dataBuffer);
String content = new String(resultBytes, StandardCharsets.UTF_8);
Assert.assertTrue(content.contains(
"Content-Disposition: form-data; name=\"fileType\"; filename=\"test1.json\"\r\n" +
"Content-Type: application/json\r\n" +
"\r\n" +
"{}"));
Assert.assertTrue(content.contains(
"Content-Disposition: form-data; name=\"fileType\"; filename=\"test2.json\"\r\n" +
"Content-Type: application/json\r\n" +
"\r\n" +
"{}"));
})
.expectComplete()
.verify();
}
@Test
public void testParseFormData_withEncodingParamsToggleTrue_returnsEncodedString() throws UnsupportedEncodingException {
final String encoded_value = dataUtils.parseFormData(List.of(new Property("key", "valüe")),
@ -177,7 +212,7 @@ public class DataUtilsTest {
}
@Test
public void testParseFormData_withOutEncodingParamsToggleTrue_returnsEncodedString() throws UnsupportedEncodingException {
public void testParseFormData_withoutEncodingParamsToggleTrue_returnsEncodedString() throws UnsupportedEncodingException {
final String encoded_value = dataUtils.parseFormData(List.of(new Property("key", "valüe")),
false);
String expected_value = null;