diff options
author | Trumeet <yuuta@yuuta.moe> | 2021-03-29 18:45:28 -0700 |
---|---|---|
committer | Trumeet <yuuta@yuuta.moe> | 2021-03-29 18:45:28 -0700 |
commit | 1e75349b33e478d9e83d322a5d65de1b7b63753c (patch) | |
tree | cbd6296b1ef869d31746332ac713d4606ca2ffa5 | |
parent | ce7703ca44a8fb7a65cbffb7c258d043d7cd94e4 (diff) | |
download | dn42peering-1e75349b33e478d9e83d322a5d65de1b7b63753c.tar dn42peering-1e75349b33e478d9e83d322a5d65de1b7b63753c.tar.gz dn42peering-1e75349b33e478d9e83d322a5d65de1b7b63753c.tar.bz2 dn42peering-1e75349b33e478d9e83d322a5d65de1b7b63753c.zip |
refactor(central): move ASN frontend logic to a separate web API service
8 files changed, 274 insertions, 143 deletions
diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 6620e8f..02f9838 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -29,8 +29,14 @@ <processorPath useClasspath="false"> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-sql-client-templates/4.0.3/bbb167fc181ab7e793485da00b370610a6230fdf/vertx-sql-client-templates-4.0.3.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-codegen/4.0.3/416429ea05c4c43afcf4104ab488bda10f43ba87/vertx-codegen-4.0.3-processor.jar" /> + <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-web-api-service/4.0.3/15b6af169e6e1987822448f3a330b4a023d701b1/vertx-web-api-service-4.0.3.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-service-proxy/4.0.3/df93b096a38ee0120e06943a8f7e3d0627c97418/vertx-service-proxy-4.0.3.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-sql-client/4.0.3/2bb64597f96e27a4a431967bea77592286d35462/vertx-sql-client-4.0.3.jar" /> + <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-web-validation/4.0.3/2f819dd0b8082664c339410cff6e40da3ae31536/vertx-web-validation-4.0.3.jar" /> + <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-web/4.0.3/5d0cec868aadac3e098b1ab6409dc2cebae83930/vertx-web-4.0.3.jar" /> + <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-json-schema/4.0.3/fb31dae192398722a07b23892e9f261a7b6c4a85/vertx-json-schema-4.0.3.jar" /> + <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-web-common/4.0.3/274167d895a6170335a5fd7e71d4519b9608d275/vertx-web-common-4.0.3.jar" /> + <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-auth-common/4.0.3/177afb713c00713eff76a6cd83a8090c601af474/vertx-auth-common-4.0.3.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-core/4.0.3/40c496ede9a948ec673df8cf5ab227ff853dd061/vertx-core-4.0.3.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.11.3/c2351800432bdbdd8284c3f5a7f0782a352aa84a/jackson-core-2.11.3.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.netty/netty-handler-proxy/4.1.60.Final/2352f12826400e5db64b36fd951508ce9a61c196/netty-handler-proxy-4.1.60.Final.jar" /> @@ -45,6 +51,7 @@ <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.netty/netty-buffer/4.1.60.Final/9d213d090deeca2541ad6827eb3345bcd6e1e701/netty-buffer-4.1.60.Final.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.netty/netty-resolver/4.1.60.Final/caba5004618d27386ee9d5ee8b23b09b6548fb0b/netty-resolver-4.1.60.Final.jar" /> <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.netty/netty-common/4.1.60.Final/44540113f7148f1014be879663501db8da1c37b0/netty-common-4.1.60.Final.jar" /> + <entry name="$USER_HOME$/.local/share/gradle/caches/modules-2/files-2.1/io.vertx/vertx-bridge-common/4.0.3/423970a10bdc4fa14ce4ad18ef3f45d703e53420/vertx-bridge-common-4.0.3.jar" /> </processorPath> <module name="dn42peering.central.main" /> </profile> diff --git a/central/build.gradle b/central/build.gradle index c0ed922..d95951d 100644 --- a/central/build.gradle +++ b/central/build.gradle @@ -37,6 +37,8 @@ dependencies { annotationProcessor "io.vertx:vertx-service-proxy:${project.vertxVersion}" compileOnly "io.vertx:vertx-codegen:${project.vertxVersion}" implementation "io.vertx:vertx-grpc:${project.vertxVersion}" + implementation "io.vertx:vertx-web-api-service:${project.vertxVersion}" + annotationProcessor "io.vertx:vertx-web-api-service:${project.vertxVersion}" implementation project(':rpc-common') } diff --git a/central/src/main/java/moe/yuuta/dn42peering/Main.java b/central/src/main/java/moe/yuuta/dn42peering/Main.java index 5fa6690..8def1df 100644 --- a/central/src/main/java/moe/yuuta/dn42peering/Main.java +++ b/central/src/main/java/moe/yuuta/dn42peering/Main.java @@ -4,6 +4,7 @@ import io.vertx.core.*; import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.JsonObject; +import moe.yuuta.dn42peering.asn.ASNHttpVerticle; import moe.yuuta.dn42peering.asn.ASNVerticle; import moe.yuuta.dn42peering.node.NodeVerticle; import moe.yuuta.dn42peering.peer.PeerVerticle; @@ -14,6 +15,7 @@ import moe.yuuta.dn42peering.whois.WhoisVerticle; import javax.annotation.Nonnull; import java.io.FileInputStream; import java.io.InputStream; +import java.util.Arrays; public class Main { public static void main(@Nonnull String... args) throws Throwable { @@ -36,14 +38,15 @@ public class Main { .setConfig(config) .setInstances(Runtime.getRuntime().availableProcessors() * 2); Logger logger = LoggerFactory.getLogger("Main"); - CompositeFuture.all( + CompositeFuture.all(Arrays.asList( Future.<String>future(f -> vertx.deployVerticle(PeerVerticle.class.getName(), options, f)), Future.<String>future(f -> vertx.deployVerticle(WhoisVerticle.class.getName(), options, f)), Future.<String>future(f -> vertx.deployVerticle(ASNVerticle.class.getName(), options, f)), + Future.<String>future(f -> vertx.deployVerticle(ASNHttpVerticle.class.getName(), options, f)), Future.<String>future(f -> vertx.deployVerticle(NodeVerticle.class.getName(), options, f)), Future.<String>future(f -> vertx.deployVerticle(ProvisionVerticle.class.getName(), options, f)), Future.<String>future(f -> vertx.deployVerticle(HTTPPortalVerticle.class.getName(), options, f)) - ).onComplete(res -> { + )).onComplete(res -> { if (res.succeeded()) { logger.info("The server started."); } else { diff --git a/central/src/main/java/moe/yuuta/dn42peering/asn/ASNHandler.java b/central/src/main/java/moe/yuuta/dn42peering/asn/ASNHandler.java index 7abc27e..0b88540 100644 --- a/central/src/main/java/moe/yuuta/dn42peering/asn/ASNHandler.java +++ b/central/src/main/java/moe/yuuta/dn42peering/asn/ASNHandler.java @@ -11,6 +11,7 @@ import io.vertx.ext.mail.MailConfig; import io.vertx.ext.mail.MailMessage; import io.vertx.ext.mail.MailResult; import io.vertx.ext.web.Router; +import io.vertx.ext.web.api.service.RouteToEBServiceHandler; import io.vertx.ext.web.common.template.TemplateEngine; import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.templ.freemarker.FreeMarkerTemplateEngine; @@ -45,28 +46,10 @@ public class ASNHandler implements ISubRouter { @Nonnull @Override public Router mount(@Nonnull Vertx vertx) { - final IASNService asnService = IASNService.createProxy(vertx, IASNService.ADDRESS); - final IWhoisService whoisService = IWhoisService.createProxy(vertx, IWhoisService.ADDRESS); - // Preserve. We need to get email address out of it later. - final JsonObject mailConfig = vertx.getOrCreateContext().config().getJsonObject("mail"); - final MailClient mailClient = MailClient.create(vertx, new MailConfig(mailConfig)); - - final TemplateEngine engine = FreeMarkerTemplateEngine.create(vertx, "ftlh"); final Router router = Router.router(vertx); router.get("/") .produces("text/html") - .handler(ctx -> { - renderIndex(engine, null, null, res -> { - if(res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - ctx.fail(res.cause()); - logger.error("Cannot render /asn.", res.cause()); - } - }); - }); + .handler(RouteToEBServiceHandler.build(vertx.eventBus(), IASNHttpService.ADDRESS, "index")); final ObjectSchemaBuilder registerSchema = objectSchema() .allowAdditionalProperties(false) @@ -81,129 +64,8 @@ public class ASNHandler implements ISubRouter { .body(Bodies.formUrlEncoded(registerSchema)) .predicate(RequestPredicate.BODY_REQUIRED) .build()) - .handler(ctx -> { - final JsonObject parameters = ctx.<RequestParameters>get(ValidationHandler.REQUEST_CONTEXT_KEY) - .body().getJsonObject(); - final String upperASN = parameters.getString("asn").toUpperCase(); - // Start: Check if the ASN exists. - Future.<Void>future(f -> asnService.exists(upperASN, true, true, ar -> { - if(ar.succeeded()) { - if(ar.result()) { - f.fail(new FormException("This ASN exists in our records. Please login instead of registering.")); - } else { - f.complete(); - } - } else { - f.fail(ar.cause()); - } - })) - // Lookup ASN - .<WhoisObject>compose(exists -> - Future.future(f -> whoisService.query(upperASN, f))) - // Lookup emails - .<List<String>>compose(asnLookup -> { - if(asnLookup == null) { - return Future.failedFuture(new FormException("The ASN is not found in the DN42 registry.")); - } else { - return Future.future(f -> asnService.lookupEmails(asnLookup, ar -> { - if(ar.succeeded()) { - if(ar.result().isEmpty()) { - f.fail(new FormException("The tech-c contact for this ASN does not have emails.")); - } else { - f.complete(ar.result()); - } - } else { - f.fail(ar.cause()); - } - })); - } - }) - // Generate random password and register. - .<Pair<String /* Random password */, List<String> /* Emails */>>compose(emails -> Future.future(f -> { - final String randomPassword = new RandomStringGenerator.Builder() - .withinRange('a', 'z') - .build() - .generate(15); - asnService.registerOrChangePassword(upperASN, randomPassword, ar -> { - if(ar.succeeded()) { - f.complete(new Pair<>(randomPassword, emails)); - } else { - f.fail(ar.cause()); - } - }); - })) - // Send mails. - .compose(pair -> CompositeFuture.any(Stream.of(pair.b) - .map(mail -> new MailMessage() - .setFrom(mailConfig.getString("from")) - .setTo(mail) - .setText(String.format("Hi %s! Welcome to dn42 peering! Your peering initial password is %s. Make sure to change it.", - upperASN, - pair.a)) - .setSubject("Peering initial password")) - .map(message -> Future.<MailResult>future(f -> mailClient.sendMail(message, f))) - .collect(Collectors.toList()))) - // Render HTML or report errors - .onComplete(ar -> { - if(ar.succeeded()) { - // Get MailResult's out of the future. - final List<MailResult> sendRes = ar.result().list(); - renderSuccess(engine, sendRes, res -> { - if(res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - ctx.fail(res.cause()); - logger.error("Cannot render /asn (success).", res.cause()); - } - }); - } else { - if(ar.cause() instanceof HTTPException) { - ctx.response().setStatusCode(((HTTPException) ar.cause()).code).end(); - } else if(ar.cause() instanceof FormException) { - renderIndex(engine, - Arrays.asList(((FormException) ar.cause()).errors.clone()), - upperASN, - res -> { - if(res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - ctx.fail(res.cause()); - logger.error("Cannot render /asn (with errors).", res.cause()); - } - }); - } else { - logger.error(String.format("Cannot register ASN %s.", upperASN), ar.cause()); - ctx.fail(ar.cause()); - } - } - }); - }); + .handler(RouteToEBServiceHandler.build(vertx.eventBus(), IASNHttpService.ADDRESS, "register")); return router; } - - private void renderIndex(@Nonnull TemplateEngine engine, - @Nullable List<String> errors, - @Nullable String asn, - @Nonnull Handler<AsyncResult<Buffer>> handler) { - final Map<String, Object> root = new HashMap<>(); - root.put("input_asn", asn == null ? "" : asn); - root.put("errors", errors); - engine.render(root, "asn/index.ftlh", handler); - } - - private void renderSuccess(@Nonnull TemplateEngine engine, - @Nonnull List<MailResult> sendRes, - @Nonnull Handler<AsyncResult<Buffer>> handler) { - final Map<String, Object> root = new HashMap<>(); - root.put("emails", sendRes.stream() - .filter(Objects::nonNull) // Nulls mean failures. - .flatMap(res -> res.getRecipients().stream()) - .collect(Collectors.toList())); - engine.render(root, "asn/success.ftlh", handler); - } } diff --git a/central/src/main/java/moe/yuuta/dn42peering/asn/ASNHttpVerticle.java b/central/src/main/java/moe/yuuta/dn42peering/asn/ASNHttpVerticle.java new file mode 100644 index 0000000..32f1a24 --- /dev/null +++ b/central/src/main/java/moe/yuuta/dn42peering/asn/ASNHttpVerticle.java @@ -0,0 +1,41 @@ +package moe.yuuta.dn42peering.asn; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.eventbus.MessageConsumer; +import io.vertx.core.impl.logging.Logger; +import io.vertx.core.impl.logging.LoggerFactory; +import io.vertx.core.json.JsonObject; +import io.vertx.serviceproxy.ServiceBinder; + +public class ASNHttpVerticle extends AbstractVerticle { + private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName()); + + private MessageConsumer<JsonObject> consumer; + + @Override + public void start(Promise<Void> startPromise) throws Exception { + consumer = new ServiceBinder(vertx) + .setAddress(IASNHttpService.ADDRESS) + .register(IASNHttpService.class, new IASNHttpServiceImpl(vertx)); + consumer.completionHandler(ar -> { + if (ar.succeeded()) { + startPromise.complete(); + } else { + startPromise.fail(ar.cause()); + } + }); + } + + @Override + public void stop(Promise<Void> stopPromise) throws Exception { + Future.future(f -> consumer.unregister(ar -> { + if (ar.succeeded()) f.complete(); + else f.fail(ar.cause()); + })).onComplete(ar -> { + if (ar.succeeded()) stopPromise.complete(); + else stopPromise.fail(ar.cause()); + }); + } +} diff --git a/central/src/main/java/moe/yuuta/dn42peering/asn/IASNHttpService.java b/central/src/main/java/moe/yuuta/dn42peering/asn/IASNHttpService.java new file mode 100644 index 0000000..aaa6913 --- /dev/null +++ b/central/src/main/java/moe/yuuta/dn42peering/asn/IASNHttpService.java @@ -0,0 +1,22 @@ +package moe.yuuta.dn42peering.asn; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.api.service.ServiceRequest; +import io.vertx.ext.web.api.service.ServiceResponse; +import io.vertx.ext.web.api.service.WebApiServiceGen; + +import javax.annotation.Nonnull; + +@WebApiServiceGen +public interface IASNHttpService { + String ADDRESS = IASNHttpService.class.getName(); + + void index(@Nonnull ServiceRequest context, + @Nonnull Handler<AsyncResult<ServiceResponse>> handler); + + void register(@Nonnull JsonObject body, + @Nonnull ServiceRequest context, + @Nonnull Handler<AsyncResult<ServiceResponse>> handler); +} diff --git a/central/src/main/java/moe/yuuta/dn42peering/asn/IASNHttpServiceImpl.java b/central/src/main/java/moe/yuuta/dn42peering/asn/IASNHttpServiceImpl.java new file mode 100644 index 0000000..b2105a4 --- /dev/null +++ b/central/src/main/java/moe/yuuta/dn42peering/asn/IASNHttpServiceImpl.java @@ -0,0 +1,168 @@ +package moe.yuuta.dn42peering.asn; + +import io.vertx.core.*; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpHeaders; +import io.vertx.core.impl.logging.Logger; +import io.vertx.core.impl.logging.LoggerFactory; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.mail.MailClient; +import io.vertx.ext.mail.MailConfig; +import io.vertx.ext.mail.MailMessage; +import io.vertx.ext.mail.MailResult; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.api.service.ServiceRequest; +import io.vertx.ext.web.api.service.ServiceResponse; +import io.vertx.ext.web.common.template.TemplateEngine; +import io.vertx.ext.web.templ.freemarker.FreeMarkerTemplateEngine; +import moe.yuuta.dn42peering.jaba.Pair; +import moe.yuuta.dn42peering.portal.FormException; +import moe.yuuta.dn42peering.portal.HTTPException; +import moe.yuuta.dn42peering.portal.RenderingUtils; +import moe.yuuta.dn42peering.whois.IWhoisService; +import moe.yuuta.dn42peering.whois.WhoisObject; +import org.apache.commons.text.RandomStringGenerator; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class IASNHttpServiceImpl implements IASNHttpService { + private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName()); + + private final IASNService asnService; + private final IWhoisService whoisService; + private final MailClient mailClient; + private final JsonObject mailConfig; + private final TemplateEngine templateEngine; + + // Testing API + public IASNHttpServiceImpl(@Nonnull IASNService asnService, + @Nonnull IWhoisService whoisService, + @Nonnull MailClient mailClient, + @Nonnull JsonObject mailConfig, + @Nonnull TemplateEngine templateEngine) { + this.asnService = asnService; + this.whoisService = whoisService; + this.mailClient = mailClient; + this.mailConfig = mailConfig; + this.templateEngine = templateEngine; + } + + public IASNHttpServiceImpl(@Nonnull Vertx vertx) { + this.asnService = IASNService.createProxy(vertx, IASNService.ADDRESS); + this.whoisService = IWhoisService.createProxy(vertx, IWhoisService.ADDRESS); + mailConfig = vertx.getOrCreateContext().config().getJsonObject("mail"); + mailClient = MailClient.create(vertx, new MailConfig(mailConfig)); + templateEngine = FreeMarkerTemplateEngine.create(vertx, "ftlh"); + } + + @Override + public void index(@Nonnull ServiceRequest context, @Nonnull Handler<AsyncResult<ServiceResponse>> handler) { + renderIndex(null, null, handler); + } + + @Override + public void register(@Nonnull JsonObject parameters, + @Nonnull ServiceRequest context, + @Nonnull Handler<AsyncResult<ServiceResponse>> handler) { + final String upperASN = parameters.getString("asn").toUpperCase(); + // Start: Check if the ASN exists. + Future.<Void>future(f -> asnService.exists(upperASN, true, true, ar -> { + if (ar.succeeded()) { + if (ar.result()) { + f.fail(new FormException("This ASN exists in our records. Please login instead of registering.")); + } else { + f.complete(); + } + } else { + f.fail(ar.cause()); + } + })) + // Lookup ASN + .<WhoisObject>compose(exists -> + Future.future(f -> whoisService.query(upperASN, f))) + // Lookup emails + .<List<String>>compose(asnLookup -> { + if (asnLookup == null) { + return Future.failedFuture(new FormException("The ASN is not found in the DN42 registry.")); + } else { + return Future.future(f -> asnService.lookupEmails(asnLookup, ar -> { + if (ar.succeeded()) { + if (ar.result().isEmpty()) { + f.fail(new FormException("The tech-c contact for this ASN does not have emails.")); + } else { + f.complete(ar.result()); + } + } else { + f.fail(ar.cause()); + } + })); + } + }) + // Generate random password and register. + .<Pair<String /* Random password */, List<String> /* Emails */>>compose(emails -> Future.future(f -> { + final String randomPassword = new RandomStringGenerator.Builder() + .withinRange('a', 'z') + .build() + .generate(15); + asnService.registerOrChangePassword(upperASN, randomPassword, ar -> { + if (ar.succeeded()) { + f.complete(new Pair<>(randomPassword, emails)); + } else { + f.fail(ar.cause()); + } + }); + })) + // Send mails. + .compose(pair -> CompositeFuture.any(Stream.of(pair.b) + .map(mail -> new MailMessage() + .setFrom(mailConfig.getString("from")) + .setTo(mail) + .setText(String.format("Hi %s! Welcome to dn42 peering! Your peering initial password is %s. Make sure to change it.", + upperASN, + pair.a)) + .setSubject("Peering initial password")) + .map(message -> Future.<MailResult>future(f -> mailClient.sendMail(message, f))) + .collect(Collectors.toList()))) + // Render HTML or report errors + .onSuccess(res -> { + // Get MailResult's out of the future. + final List<MailResult> sendRes = res.list(); + renderSuccess(sendRes, handler); + }) + .onFailure(err -> { + if (err instanceof HTTPException) { + handler.handle(Future.succeededFuture(new ServiceResponse(((HTTPException) err).code, null, null, null))); + } else if (err instanceof FormException) { + renderIndex(Arrays.asList(((FormException) err).errors.clone()), + upperASN, + handler); + } else { + logger.error(String.format("Cannot register ASN %s.", upperASN), err); + handler.handle(Future.failedFuture(err)); + } + }); + } + + private void renderIndex(@Nullable List<String> errors, + @Nullable String asn, + @Nonnull Handler<AsyncResult<ServiceResponse>> handler) { + final Map<String, Object> root = new HashMap<>(); + root.put("input_asn", asn == null ? "" : asn); + root.put("errors", errors); + templateEngine.render(root, "asn/index.ftlh", RenderingUtils.getGeneralRenderingHandler(handler)); + } + + private void renderSuccess(@Nonnull List<MailResult> sendRes, + @Nonnull Handler<AsyncResult<ServiceResponse>> handler) { + final Map<String, Object> root = new HashMap<>(); + root.put("emails", sendRes.stream() + .filter(Objects::nonNull) // Nulls mean failures. + .flatMap(res -> res.getRecipients().stream()) + .collect(Collectors.toList())); + templateEngine.render(root, "asn/success.ftlh", RenderingUtils.getGeneralRenderingHandler(handler)); + } +} diff --git a/central/src/main/java/moe/yuuta/dn42peering/portal/RenderingUtils.java b/central/src/main/java/moe/yuuta/dn42peering/portal/RenderingUtils.java new file mode 100644 index 0000000..a8514f3 --- /dev/null +++ b/central/src/main/java/moe/yuuta/dn42peering/portal/RenderingUtils.java @@ -0,0 +1,26 @@ +package moe.yuuta.dn42peering.portal; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpHeaders; +import io.vertx.ext.web.api.service.ServiceResponse; + +import javax.annotation.Nonnull; + +public class RenderingUtils { + public static Handler<AsyncResult<Buffer>> getGeneralRenderingHandler(@Nonnull Handler<AsyncResult<ServiceResponse>> handler) { + return res -> { + if (res.succeeded()) { + handler.handle(Future.succeededFuture(new ServiceResponse(200, + null, + res.result(), + MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.CONTENT_TYPE, "text/html")))); + } else { + handler.handle(Future.failedFuture(res.cause())); + } + }; + } +} |