diff --git a/src/main/java/com/chavaillaz/client/common/vertx/AbstractVertxHttpClient.java b/src/main/java/com/chavaillaz/client/common/vertx/AbstractVertxHttpClient.java index e055681..6523efe 100644 --- a/src/main/java/com/chavaillaz/client/common/vertx/AbstractVertxHttpClient.java +++ b/src/main/java/com/chavaillaz/client/common/vertx/AbstractVertxHttpClient.java @@ -8,14 +8,12 @@ import com.chavaillaz.client.common.AbstractHttpClient; import com.chavaillaz.client.common.security.Authentication; import com.fasterxml.jackson.databind.JavaType; -import io.vertx.core.Vertx; +import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpMethod; import io.vertx.ext.web.client.HttpRequest; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; -import io.vertx.ext.web.client.WebClientOptions; import lombok.extern.slf4j.Slf4j; /** @@ -29,38 +27,25 @@ public class AbstractVertxHttpClient extends AbstractHttpClient implements AutoC /** * Creates a new abstract client based on Vert.x HTTP client. * - * @param vertx The Vert.x instance to create the web client - * @param options The web client options + * @param client The web client to use * @param baseUrl The base URL of endpoints * @param authentication The authentication information */ - protected AbstractVertxHttpClient(Vertx vertx, WebClientOptions options, String baseUrl, Authentication authentication) { + protected AbstractVertxHttpClient(WebClient client, String baseUrl, Authentication authentication) { super(baseUrl, authentication); - this.client = WebClient.create(vertx, options); - } - - /** - * Creates a new abstract client based on Vert.x HTTP client. - * - * @param httpClient The HTTP client to wrap in the Vert.x web client - * @param baseUrl The base URL of endpoints - * @param authentication The authentication information - */ - protected AbstractVertxHttpClient(HttpClient httpClient, String baseUrl, Authentication authentication) { - super(baseUrl, authentication); - this.client = WebClient.wrap(httpClient); + this.client = client; } /** * Creates a request based on the given URL and replaces the parameters in it by the given ones. * - * @param method The HTTP method for the HTTP request to build + * @param method The HTTP method for the request to build * @param url The URL with possible parameters in it (using braces) * @param parameters The parameters value to replace in the URL (in the right order) - * @return The request having the URL and authorization header set + * @return The request having the URL and authentication set */ protected HttpRequest requestBuilder(HttpMethod method, String url, Object... parameters) { - var request = client.request(method, url(url, parameters).toString()) + var request = client.requestAbs(method, url(url, parameters).toString()) .putHeader(HEADER_CONTENT_TYPE, HEADER_CONTENT_JSON); getAuthentication().fillHeaders(request::putHeader); getCookieHeader(getAuthentication()).ifPresent(value -> request.putHeader(HEADER_COOKIE, value)); @@ -68,49 +53,63 @@ protected HttpRequest requestBuilder(HttpMethod method, String url, Obje } /** - * Sends a request and returns a domain object. + * Creates a body buffer containing the given object serialized as JSON. * - * @param request The request + * @param object The object to serialize + * @return The corresponding buffer for the request + */ + protected Buffer body(Object object) { + return Buffer.buffer(serialize(object)); + } + + /** + * Handles the request sent and returns a domain object. + * + * @param future The future response * @param returnType The domain object type class * @param The domain object type * @return A {@link CompletableFuture} with the deserialized domain object */ - protected CompletableFuture sendAsync(HttpRequest request, Class returnType) { - return sendAsync(request, objectMapper.constructType(returnType)); + protected CompletableFuture handleAsync(Future> future, Class returnType) { + return handleAsync(future, objectMapper.constructType(returnType)); } /** - * Sends a request and returns a domain object. + * Handles the request sent and returns a domain object. * - * @param request The request + * @param future The future response * @param returnType The domain object type class * @param The domain object type * @return A {@link CompletableFuture} with the deserialized domain object */ - protected CompletableFuture sendAsync(HttpRequest request, JavaType returnType) { + protected CompletableFuture handleAsync(Future> future, JavaType returnType) { CompletableFuture> completableFuture = new CompletableFuture<>(); - request.send() - .onSuccess(response -> handleResponse(response, completableFuture)) + future.onSuccess(response -> handleResponse(response, completableFuture)) .onFailure(completableFuture::completeExceptionally); return completableFuture.thenApply(HttpResponse::bodyAsString) .thenApply(body -> deserialize(body, returnType)); } /** - * Sends a request and returns an input stream. + * Handles the request sent and returns an input stream. * - * @param request The request + * @param future The future response * @return A {@link CompletableFuture} with the input stream */ - protected CompletableFuture sendAsync(HttpRequest request) { + protected CompletableFuture handleAsync(Future> future) { CompletableFuture> completableFuture = new CompletableFuture<>(); - request.send() - .onSuccess(response -> handleResponse(response, completableFuture)) + future.onSuccess(response -> handleResponse(response, completableFuture)) .onFailure(completableFuture::completeExceptionally); return completableFuture.thenApply(HttpResponse::body) .thenApply(VertxInputStream::new); } + /** + * Handles the response by transmitting its state to the given {@link CompletableFuture}. + * + * @param response The HTTP response + * @param completableFuture The completable future to update + */ protected void handleResponse(HttpResponse response, CompletableFuture> completableFuture) { if (response.statusCode() >= 400) { completableFuture.completeExceptionally(responseException(response.statusCode(), response.bodyAsString())); diff --git a/src/main/java/com/chavaillaz/client/common/vertx/VertxInputStream.java b/src/main/java/com/chavaillaz/client/common/vertx/VertxInputStream.java index d2b05d5..75b91c4 100644 --- a/src/main/java/com/chavaillaz/client/common/vertx/VertxInputStream.java +++ b/src/main/java/com/chavaillaz/client/common/vertx/VertxInputStream.java @@ -6,6 +6,9 @@ import io.vertx.core.buffer.Buffer; import lombok.RequiredArgsConstructor; +/** + * Input stream reading the content of a Vert.x {@link Buffer}. + */ @RequiredArgsConstructor public class VertxInputStream extends InputStream { diff --git a/src/main/java/com/chavaillaz/client/common/vertx/VertxUtils.java b/src/main/java/com/chavaillaz/client/common/vertx/VertxUtils.java index 0be1183..d598ceb 100644 --- a/src/main/java/com/chavaillaz/client/common/vertx/VertxUtils.java +++ b/src/main/java/com/chavaillaz/client/common/vertx/VertxUtils.java @@ -1,10 +1,15 @@ package com.chavaillaz.client.common.vertx; +import static java.nio.file.Files.probeContentType; + +import java.io.File; import java.util.Optional; import com.chavaillaz.client.common.utility.ProxyConfiguration; import io.vertx.core.net.ProxyOptions; import io.vertx.ext.web.client.WebClientOptions; +import io.vertx.ext.web.multipart.MultipartForm; +import lombok.SneakyThrows; import lombok.experimental.UtilityClass; /** @@ -30,4 +35,19 @@ public static WebClientOptions newWebClientOptions(ProxyConfiguration proxy) { .setIdleTimeout(30_000); } + /** + * Generates a new multipart form with the given files. + * + * @param files The list of files to include + * @return The multipart form + */ + @SneakyThrows + public static MultipartForm multipartWithFiles(File... files) { + MultipartForm form = MultipartForm.create(); + for (File file : files) { + form.binaryFileUpload("file", file.getName(), file.getAbsolutePath(), probeContentType(file.toPath())); + } + return form; + } + }