Skip to content

Commit

Permalink
RESTEasy Reactive: support multipart form parameters quarkusio#22205
Browse files Browse the repository at this point in the history
  • Loading branch information
FroMage committed Oct 10, 2022
1 parent 06c4344 commit ff76b7b
Show file tree
Hide file tree
Showing 15 changed files with 387 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package io.quarkus.resteasy.reactive.jackson.deployment.test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.jboss.resteasy.reactive.MultipartForm;
import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.multipart.FileUpload;

import io.smallrye.common.annotation.Blocking;

Expand All @@ -35,4 +40,38 @@ public Map<String, Object> greeting(@Valid @MultipartForm FormData formData) {
result.put("persons2", formData.persons2);
return result;
}

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
@Path("/param/json")
public Map<String, Object> greeting(
@RestForm @PartType(MediaType.APPLICATION_JSON) Map<String, Object> map,

@FormParam("names") @PartType(MediaType.TEXT_PLAIN) List<String> names,

@RestForm @PartType(MediaType.TEXT_PLAIN) int[] numbers,

@RestForm @PartType(MediaType.TEXT_PLAIN) List<Integer> numbers2,

@RestForm @PartType(MediaType.APPLICATION_JSON) @Valid Person person,

@RestForm @PartType(MediaType.APPLICATION_JSON) Person[] persons,

@RestForm @PartType(MediaType.APPLICATION_JSON) List<Person> persons2,

@RestForm("htmlFile") FileUpload htmlPart) {
Map<String, Object> result = new HashMap<>(map);
result.put("person", person);
result.put("htmlFileSize", htmlPart.size());
result.put("htmlFilePath", htmlPart.uploadedFile().toAbsolutePath().toString());
result.put("htmlFileContentType", htmlPart.contentType());
result.put("names", names);
result.put("numbers", numbers);
result.put("numbers2", numbers2);
result.put("persons", persons);
result.put("persons2", persons2);
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,56 @@ public void testValid() throws IOException {
.body("persons2[1].last", equalTo("Last2"));
}

@Test
public void testValidParam() throws IOException {
RestAssured.given()
.multiPart("map",
"{\n" +
" \"foo\": \"bar\",\n" +
" \"sub\": {\n" +
" \"foo2\": \"bar2\"\n" +
" }\n" +
"}")
.multiPart("person", "{\"first\": \"Bob\", \"last\": \"Builder\"}", "application/json")
.multiPart("htmlFile", HTML_FILE, "text/html")
.multiPart("names", "name1")
.multiPart("names", "name2")
.multiPart("numbers", 1)
.multiPart("numbers", 2)
.multiPart("numbers2", 1)
.multiPart("numbers2", 2)
.multiPart("persons", "{\"first\": \"First1\", \"last\": \"Last1\"}", "application/json")
.multiPart("persons", "{\"first\": \"First2\", \"last\": \"Last2\"}", "application/json")
.multiPart("persons2", "{\"first\": \"First1\", \"last\": \"Last1\"}", "application/json")
.multiPart("persons2", "{\"first\": \"First2\", \"last\": \"Last2\"}", "application/json")
.accept("application/json")
.when()
.post("/multipart/param/json")
.then()
.statusCode(200)
.body("foo", equalTo("bar"))
.body("sub.foo2", equalTo("bar2"))
.body("person.first", equalTo("Bob"))
.body("person.last", equalTo("Builder"))
.body("htmlFileSize", equalTo(Files.readAllBytes(HTML_FILE.toPath()).length))
.body("htmlFilePath", not(equalTo(HTML_FILE.toPath().toAbsolutePath().toString())))
.body("htmlFileContentType", equalTo("text/html"))
.body("names[0]", equalTo("name1"))
.body("names[1]", equalTo("name2"))
.body("numbers[0]", equalTo(1))
.body("numbers[1]", equalTo(2))
.body("numbers2[0]", equalTo(1))
.body("numbers2[1]", equalTo(2))
.body("persons[0].first", equalTo("First1"))
.body("persons[0].last", equalTo("Last1"))
.body("persons[1].first", equalTo("First2"))
.body("persons[1].last", equalTo("Last2"))
.body("persons2[0].first", equalTo("First1"))
.body("persons2[0].last", equalTo("Last1"))
.body("persons2[1].first", equalTo("First2"))
.body("persons2[1].last", equalTo("Last2"));
}

@Test
public void testInvalid() {
RestAssured.given()
Expand All @@ -106,4 +156,27 @@ public void testInvalid() {
.then()
.statusCode(400);
}

@Test
public void testInvalidParam() {
RestAssured.given()
.multiPart("map",
"{\n" +
" \"foo\": \"bar\",\n" +
" \"sub\": {\n" +
" \"foo2\": \"bar2\"\n" +
" }\n" +
"}")
.multiPart("person", "{\"first\": \"Bob\"}", "application/json")
.multiPart("htmlFile", HTML_FILE, "text/html")
.multiPart("names", "name1")
.multiPart("names", "name2")
.multiPart("numbers", 1)
.multiPart("numbers", 2)
.accept("application/json")
.when()
.post("/multipart/param/json")
.then()
.statusCode(400);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ public void testInput() {
assertThat(response).isEqualTo("John-Divino Pastor");
}

@Test
public void testInputParam() {
String response = RestAssured
.given()
.multiPart("name", "John")
.multiPart("school", SCHOOL, MediaType.APPLICATION_XML)
.post("/multipart/param/input")
.then()
.statusCode(200)
.extract().asString();

assertThat(response).isEqualTo("John-Divino Pastor");
}

private void assertContains(String response, String name, String contentType, Object value) {
String[] lines = response.split("--");
assertThat(lines).anyMatch(line -> line.contains(String.format(EXPECTED_CONTENT_DISPOSITION_PART, name))
Expand Down Expand Up @@ -97,6 +111,13 @@ public String input(@MultipartForm MultipartInput input) {
return input.name + "-" + input.school.name;
}

@POST
@Path("/param/input")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String input(@RestForm String name,
@RestForm @PartType(MediaType.APPLICATION_XML) School school) {
return name + "-" + school.name;
}
}

private static class MultipartOutputResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,27 @@ public void testSimple() {
Assertions.assertEquals(3, uploadDir.toFile().listFiles().length);
}

@Test
public void testSimpleParam() {
RestAssured.given()
.multiPart("name", "Alice")
.multiPart("active", "true")
.multiPart("num", "25")
.multiPart("status", "WORKING")
.multiPart("htmlFile", HTML_FILE, "text/html")
.multiPart("xmlFile", XML_FILE, "text/xml")
.multiPart("txtFile", TXT_FILE, "text/plain")
.accept("text/plain")
.when()
.post("/multipart/param/simple/2")
.then()
.statusCode(200)
.body(equalTo("Alice - true - 50 - WORKING - text/html - true - true"));

// ensure that the 3 uploaded files where created on disk
Assertions.assertEquals(3, uploadDir.toFile().listFiles().length);
}

@Test
public void testBlocking() throws IOException {
RestAssured.given()
Expand Down Expand Up @@ -144,6 +165,26 @@ public void testSameName() {
Assertions.assertEquals(4, uploadDir.toFile().listFiles().length);
}

@Test
public void testSameNameParam() {
RestAssured.given()
.multiPart("active", "false")
.multiPart("status", "EATING")
.multiPart("htmlFile", HTML_FILE, "text/html")
.multiPart("htmlFile", HTML_FILE2, "text/html")
.multiPart("xmlFile", XML_FILE, "text/xml")
.multiPart("txtFile", TXT_FILE, "text/plain")
.accept("text/plain")
.when()
.post("/multipart/param/same-name")
.then()
.statusCode(200)
.body(equalTo("EATING - 2 - 1 - 1"));

// ensure that the 3 uploaded files where created on disk
Assertions.assertEquals(4, uploadDir.toFile().listFiles().length);
}

private String filePath(File file) {
return file.toPath().toAbsolutePath().toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,25 @@ public void testSimple() throws IOException {
// ensure that the 3 uploaded files where created on disk
Assertions.assertEquals(3, uploadDir.toFile().listFiles().length);
}

@Test
public void testSimpleParam() throws IOException {
RestAssured.given()
.multiPart("name", "Alice")
.multiPart("active", "true")
.multiPart("num", "25")
.multiPart("status", "WORKING")
.multiPart("htmlFile", HTML_FILE, "text/html")
.multiPart("xmlFile", XML_FILE, "text/xml")
.multiPart("txtFile", TXT_FILE, "text/plain")
.accept("text/plain")
.when()
.post("/multipart-all/param/simple/2")
.then()
.statusCode(200)
.body(equalTo("Alice - true - 50 - WORKING - 3 - text/plain"));

// ensure that the 3 uploaded files where created on disk
Assertions.assertEquals(3, uploadDir.toFile().listFiles().length);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.quarkus.resteasy.reactive.server.test.multipart;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
Expand All @@ -12,7 +14,10 @@
import javax.ws.rs.core.Response;

import org.jboss.resteasy.reactive.MultipartForm;
import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestQuery;
import org.jboss.resteasy.reactive.multipart.FileUpload;

import io.quarkus.runtime.BlockingOperationControl;
import io.smallrye.common.annotation.Blocking;
Expand All @@ -36,6 +41,30 @@ public String simple(@MultipartForm FormData formData, Integer times) {
+ formData.txtFile.exists();
}

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/param/simple/{times}")
@NonBlocking
public String simple(
// don't set a part type, use the default
@RestForm String name,
@RestForm @PartType(MediaType.TEXT_PLAIN) Status status,
@RestForm("htmlFile") FileUpload htmlPart,
@RestForm("xmlFile") java.nio.file.Path xmlPart,
@RestForm File txtFile,
@RestForm @PartType(MediaType.TEXT_PLAIN) boolean active,
@RestForm @PartType(MediaType.TEXT_PLAIN) int num,
Integer times) {
if (BlockingOperationControl.isBlockingAllowed()) {
throw new RuntimeException("should not have dispatched");
}
return name + " - " + active + " - " + times * num + " - " + status
+ " - "
+ htmlPart.contentType() + " - " + Files.exists(xmlPart) + " - "
+ txtFile.exists();
}

@POST
@Blocking
@Produces(MediaType.TEXT_PLAIN)
Expand Down Expand Up @@ -67,6 +96,21 @@ public String sameName(FormDataSameFileName formData) {
+ formData.xmlFiles.size();
}

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/param/same-name")
public String sameName(@RestForm @PartType(MediaType.TEXT_PLAIN) Status status,
@RestForm("htmlFile") List<File> htmlFiles,
@RestForm("txtFile") List<FileUpload> txtFiles,
@RestForm("xmlFile") List<java.nio.file.Path> xmlFiles) {
if (!BlockingOperationControl.isBlockingAllowed()) {
throw new RuntimeException("should have dispatched");
}
return status + " - " + htmlFiles.size() + " - " + txtFiles.size() + " - "
+ xmlFiles.size();
}

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package io.quarkus.resteasy.reactive.server.test.multipart;

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.jboss.resteasy.reactive.MultipartForm;
import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.multipart.FileUpload;

import io.quarkus.runtime.BlockingOperationControl;
Expand All @@ -29,4 +33,25 @@ public String simple(@MultipartForm FormDataWithAllUploads formData, Integer tim
+ " - " + formData.getUploads().size() + " - " + txtFile.contentType();
}

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@NonBlocking
@Path("/param/simple/{times}")
public String simple(
@RestForm
// don't set a part type, use the default
String name,
@RestForm @PartType(MediaType.TEXT_PLAIN) Status status,
@RestForm(FileUpload.ALL) List<FileUpload> uploads,
@RestForm @PartType(MediaType.TEXT_PLAIN) boolean active,
@RestForm @PartType(MediaType.TEXT_PLAIN) int num,
Integer times) {
if (BlockingOperationControl.isBlockingAllowed()) {
throw new RuntimeException("should not have dispatched");
}
FileUpload txtFile = uploads.stream().filter(f -> f.name().equals("txtFile")).findFirst().get();
return name + " - " + active + " - " + times * num + " - " + status
+ " - " + uploads.size() + " - " + txtFile.contentType();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ protected MethodParameter createMethodParameter(ClassInfo currentClassInfo, Clas
return new MethodParameter(name,
elementType, declaredTypes.getDeclaredType(), declaredTypes.getDeclaredUnresolvedType(), signature, type,
single,
defaultValue, parameterResult.isObtainedAsCollection(), parameterResult.isOptional(), encoded);
defaultValue, parameterResult.isObtainedAsCollection(), parameterResult.isOptional(), encoded,
// FIXME: mime type from any @PartType annotation
null);
}

@Override
Expand Down
Loading

0 comments on commit ff76b7b

Please sign in to comment.