diff --git a/src/main/java/fr/wseduc/webutils/Controller.java b/src/main/java/fr/wseduc/webutils/Controller.java index 584a7eb..cb5625e 100644 --- a/src/main/java/fr/wseduc/webutils/Controller.java +++ b/src/main/java/fr/wseduc/webutils/Controller.java @@ -20,7 +20,6 @@ import fr.wseduc.webutils.http.HttpMethod; import fr.wseduc.webutils.http.Renders; import fr.wseduc.webutils.http.TraceIdContextHandler; -import fr.wseduc.webutils.request.AccessLogger; import fr.wseduc.webutils.request.AccessLoggerFactory; import fr.wseduc.webutils.request.IAccessLogger; import fr.wseduc.webutils.request.filter.SecurityHandler; @@ -33,6 +32,7 @@ import io.vertx.core.eventbus.EventBus; import io.vertx.core.eventbus.Message; import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import org.apache.commons.lang3.time.StopWatch; import org.vertx.java.core.http.RouteMatcher; @@ -44,8 +44,10 @@ import java.util.*; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; public abstract class Controller extends Renders { @@ -78,7 +80,7 @@ public Controller(Vertx vertx, JsonObject config, RouteMatcher rm, } protected void init(Vertx vertx, JsonObject config, RouteMatcher rm, - Map securedActions) + Map securedActions) { super.init(vertx, config); this.rm = rm; @@ -452,6 +454,22 @@ private String addPathPrefix(String pattern) { return pathPrefix + pattern.trim(); } + /** + * Take a json array of jsonObject, appy the mapper to enforce contract, then serialize the result back to jsonObject + * to be send on the event bus + * @param arr List of JsonObject from database + * @param mapper Mapper to convert / filter JsonObject to a specific T class + * @return JsonArray of serialized T on JsonObject + * @param + */ + public static JsonArray serializeForBus(JsonArray arr, Function mapper) { + return new JsonArray(arr.stream() + .map(JsonObject.class::cast) + .map(mapper) + .map(JsonObject::mapFrom) + .collect(Collectors.toList())); + } + public void setAccessLogger(IAccessLogger accessLogger) { this.accessLogger = accessLogger; } diff --git a/src/main/java/fr/wseduc/webutils/Utils.java b/src/main/java/fr/wseduc/webutils/Utils.java index 338aab9..538b4c4 100644 --- a/src/main/java/fr/wseduc/webutils/Utils.java +++ b/src/main/java/fr/wseduc/webutils/Utils.java @@ -18,8 +18,10 @@ import fr.wseduc.webutils.eventbus.ResultMessage; import io.vertx.core.AsyncResult; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.MultiMap; +import io.vertx.core.Promise; import io.vertx.core.eventbus.Message; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; @@ -28,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Scanner; +import java.util.function.Consumer; public class Utils { @@ -199,4 +202,35 @@ public static Handler>> handlerToAsyncHandler(Ha }; } + /** + * Bridges a legacy callback-based service method (using {@link Either}) into a Vert.x {@link Future}. + *

+ * Many service methods in this codebase accept a {@code Handler>} as their last + * argument, where a {@link Either.Left} signals failure (with an error message) and a + * {@link Either.Right} signals success (with the result value). This utility wraps such a call + * into a {@link Future} so it can be composed with other async operations. + * + *

{@code
+	 * // Given a service method:
+	 * //   void findUser(String id, Handler> handler)
+	 *
+	 * Utils.eitherToFuture(handler -> userService.findUser(userId, handler))
+	 *     .compose(user -> Utils.eitherToFuture(handler -> roleService.getRoles(user.getString("id"), handler)))
+	 *     .onSuccess(roles -> renderJson(request, roles))
+	 *     .onFailure(err -> renderError(request, err));
+	 * }
+ * + * @param call a consumer that invokes the legacy service method, passing along the provided handler + * @param the type of the success value + * @return a {@link Future} that completes with the right value on success, or fails with the left message on error + */ + public static Future eitherToFuture(Consumer>> call) { + Promise promise = Promise.promise(); + call.accept(res -> { + if (res.isRight()) promise.complete(res.right().getValue()); + else promise.fail(res.left().getValue()); + }); + return promise.future(); + } + } diff --git a/src/main/java/fr/wseduc/webutils/http/BaseController.java b/src/main/java/fr/wseduc/webutils/http/BaseController.java index 4118bad..1d28cc4 100644 --- a/src/main/java/fr/wseduc/webutils/http/BaseController.java +++ b/src/main/java/fr/wseduc/webutils/http/BaseController.java @@ -17,7 +17,6 @@ package fr.wseduc.webutils.http; import fr.wseduc.webutils.Controller; -import fr.wseduc.webutils.Server; import fr.wseduc.webutils.security.SecuredAction; import io.vertx.core.Future; import io.vertx.core.Vertx; @@ -37,7 +36,7 @@ public BaseController() { this(null, null, null, null); } - public void init(Vertx vertx, JsonObject config, RouteMatcher rm, + public void init(Vertx vertx, JsonObject config, RouteMatcher rm, Map securedActions) { super.init(vertx, config, rm, securedActions); }