Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support FileUpload as multipart type in REST Client #40094

Merged
merged 2 commits into from
Apr 16, 2024
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
8 changes: 4 additions & 4 deletions docs/src/main/asciidoc/rest-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@
String sendMultipart(@RestForm File file, @RestForm String otherField);
----

Parameters specified as `File`, `Path`, `byte[]` or `Buffer` are sent as files and default to the
Parameters specified as `File`, `Path`, `byte[]`, `Buffer` or `FileUpload` are sent as files and default to the

Check warning on line 1403 in docs/src/main/asciidoc/rest-client.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/rest-client.adoc", "range": {"start": {"line": 1403, "column": 85}}}, "severity": "INFO"}
`application/octet-stream` MIME type. Other `@RestForm` parameter types default to the `text/plain`
MIME type. You can override these defaults with the `@PartType` annotation.

Expand All @@ -1421,7 +1421,7 @@
String sendMultipart(Parameters parameters);
----

Any `@RestForm` parameter of the type `File`, `Path`, `byte[]` or `Buffer`, as well as any
Any `@RestForm` parameter of the type `File`, `Path`, `byte[]`, `Buffer` or `FileUpload`, as well as any

Check warning on line 1424 in docs/src/main/asciidoc/rest-client.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/rest-client.adoc", "range": {"start": {"line": 1424, "column": 80}}}, "severity": "INFO"}
annotated with `@PartType` automatically imply a `@Consumes(MediaType.MULTIPART_FORM_DATA)`
on the method if there is no `@Consumes` present.

Expand Down Expand Up @@ -1533,15 +1533,15 @@
ClientMultipartForm multiPartForm = ClientMultipartForm.create();
multiPartForm.attribute("jsonPayload", request.getJsonPayload(), "jsonPayload"); // <2>
request.getFiles().forEach(fu -> {
multiPartForm.binaryFileUpload("file", fu.name(), fu.filePath().toString(), fu.contentType()); // <3>
multiPartForm.fileUpload(fu); // <3>
});
return multiPartForm;
}
----

<1> `Request` representing the request the server parts accepts
<2> A `jsonPayload` attribute is added directly to `ClientMultipartForm`
<3> A `binaryFileUpload` is created from the request's `FileUpload` (which is a Quarkus REST (Server) type used to represent a binary file upload)
<3> A `fileUpload` is created from the request's `FileUpload`

[NOTE]
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames;
import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult;
import org.jboss.resteasy.reactive.multipart.FileDownload;
import org.jboss.resteasy.reactive.multipart.FileUpload;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
Expand Down Expand Up @@ -190,6 +191,7 @@ public class JaxrsClientReactiveProcessor {
private static final String PATH_SIGNATURE = "L" + java.nio.file.Path.class.getName().replace('.', '/') + ";";
private static final String BUFFER_SIGNATURE = "L" + Buffer.class.getName().replace('.', '/') + ";";
private static final String BYTE_ARRAY_SIGNATURE = "[B";
private static final String FILE_UPLOAD_SIGNATURE = "L" + FileUpload.class.getName().replace('.', '/') + ";";

private static final Logger log = Logger.getLogger(JaxrsClientReactiveProcessor.class);

Expand Down Expand Up @@ -1176,6 +1178,7 @@ private boolean isMultipartRequiringType(String signature, String partType) {
|| signature.equals(BUFFER_SIGNATURE)
|| signature.equals(BYTE_ARRAY_SIGNATURE)
|| signature.equals(MULTI_BYTE_SIGNATURE)
|| signature.equals(FILE_UPLOAD_SIGNATURE)
|| partType != null);
}

Expand Down Expand Up @@ -1793,6 +1796,8 @@ private void handleMultipartField(String formParamName, String partType, String
} else if (type.equals(Path.class.getName())) {
// and so is path
addFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue);
} else if (type.equals(FileUpload.class.getName())) {
addFileUpload(fieldValue, multipartForm, methodCreator);
} else if (type.equals(InputStream.class.getName())) {
// and so is path
addInputStream(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, type);
Expand Down Expand Up @@ -1888,6 +1893,15 @@ private void addFile(BytecodeCreator methodCreator, AssignableResultHandle multi
}
}

private void addFileUpload(ResultHandle fieldValue, AssignableResultHandle multipartForm,
BytecodeCreator methodCreator) {
// MultipartForm#fileUpload(FileUpload fileUpload);
methodCreator.invokeVirtualMethod(
MethodDescriptor.ofMethod(ClientMultipartForm.class, "fileUpload",
ClientMultipartForm.class, FileUpload.class),
multipartForm, fieldValue);
}

private ResultHandle primitiveToString(BytecodeCreator methodCreator, ResultHandle fieldValue, FieldInfo field) {
PrimitiveType primitiveType = field.type().asPrimitiveType();
switch (primitiveType.primitive()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,39 @@ void shouldCallImplicitEndpoints() throws IOException {
.isEqualTo(file.getName() + " file Hello");
assertThat(client.postMultipartEntityImplicit(file.getName(), person))
.isEqualTo(file.getName() + " Stef:Epardaud");

assertThat(client.postMultipartImplicitFileUpload("Foo", new FileUpload() {
@Override
public String name() {
return "file";
}

@Override
public java.nio.file.Path filePath() {
return file.toPath();
}

@Override
public String fileName() {
return file.getName();
}

@Override
public long size() {
return -1;
}

@Override
public String contentType() {
return "application/octet-stream";
}

@Override
public String charSet() {
return "";
}
}))
.isEqualTo("Foo " + file.getName() + " Hello");
}

@Path("form")
Expand Down Expand Up @@ -142,6 +175,10 @@ String postMultipartEntityImplicit(@RestForm String name,
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartExplicit(@RestForm String name, @RestForm File file);

@Path("multipart")
@POST
String postMultipartImplicitFileUpload(@RestForm String name, @RestForm FileUpload file);

@Path("urlencoded")
@POST
String postUrlencodedImplicit(@RestForm String name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,49 @@ void shouldPassOriginalFileName() throws IOException {
assertThat(client.postMultipart(form)).isEqualTo(file.getName());
}

@Test
void shouldWorkWithFileUpload() throws IOException {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);

File file = File.createTempFile("MultipartTest", ".txt");
file.deleteOnExit();

ClientFormUsingFileUpload form = new ClientFormUsingFileUpload();
form.file = new FileUpload() {

@Override
public String name() {
return "myFile";
}

@Override
public java.nio.file.Path filePath() {
return file.toPath();
}

@Override
public String fileName() {
return file.getName();
}

@Override
public long size() {
return 0;
}

@Override
public String contentType() {
return "application/octet-stream";
}

@Override
public String charSet() {
return "";
}
};
assertThat(client.postMultipartFileUpload(form)).isEqualTo(file.getName());
}

@Test
void shouldUseFileNameFromAnnotation() throws IOException {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
Expand Down Expand Up @@ -244,6 +287,10 @@ public interface Client {
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipart(@MultipartForm ClientForm clientForm);

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartFileUpload(ClientFormUsingFileUpload clientForm);

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartWithPartFilename(@MultipartForm ClientFormUsingFile clientForm);
Expand Down Expand Up @@ -324,6 +371,11 @@ public static class ClientForm {
public File file;
}

public static class ClientFormUsingFileUpload {
@RestForm
public FileUpload file;
}

public static class ClientFormUsingFile {
@FormParam("myFile")
@PartType(APPLICATION_OCTET_STREAM)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartForm;
import org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartFormDataPart;
import org.jboss.resteasy.reactive.multipart.FileUpload;

import io.smallrye.mutiny.Multi;
import io.vertx.core.buffer.Buffer;
Expand Down Expand Up @@ -86,4 +87,9 @@ public ClientMultipartForm multiAsTextFileUpload(String name, String filename, M
return this;
}

public ClientMultipartForm fileUpload(FileUpload fileUpload) {
binaryFileUpload(fileUpload.name(), fileUpload.fileName(), fileUpload.filePath().toString(), fileUpload.contentType());
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* Represent a file that has been uploaded.
* <p>
* WARNING: This type is currently only supported on the server
* This type is usually used on server, but it is also supported in the REST Client.
*/
public interface FileUpload extends FilePart {

Expand Down
Loading