From 8a75ff9b1b6321dad3b8cd414a05a8ca6a534304 Mon Sep 17 00:00:00 2001 From: Janis Saldabols Date: Fri, 13 Oct 2023 16:27:11 +0300 Subject: [PATCH 1/2] MODSENDER-66 Create print channel --- descriptors/ModuleDescriptor-template.json | 2 +- .../org/folio/sender/DeliveryVerticle.java | 3 + .../sender/delivery/EmailDeliveryChannel.java | 31 +++-- .../sender/delivery/MailDeliveryChannel.java | 64 ++++++++++ .../org/folio/rest/MessageDeliveryTest.java | 116 +++++++++++++++++- 5 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/folio/sender/delivery/MailDeliveryChannel.java diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index b242a70..d2518eb 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -20,7 +20,7 @@ "methods": ["POST"], "pathPattern": "/message-delivery", "permissionsRequired": ["sender.message-delivery"], - "modulePermissions": ["email.message.post", "users.item.get" ] + "modulePermissions": ["email.message.post", "mod-batch-print.entries.mail.post", "users.item.get" ] } ] } diff --git a/src/main/java/org/folio/sender/DeliveryVerticle.java b/src/main/java/org/folio/sender/DeliveryVerticle.java index 7ade491..7d9740b 100644 --- a/src/main/java/org/folio/sender/DeliveryVerticle.java +++ b/src/main/java/org/folio/sender/DeliveryVerticle.java @@ -8,6 +8,7 @@ import io.vertx.serviceproxy.ServiceBinder; import org.folio.sender.delivery.DeliveryChannel; import org.folio.sender.delivery.EmailDeliveryChannel; +import org.folio.sender.delivery.MailDeliveryChannel; public class DeliveryVerticle extends AbstractVerticle { @@ -17,6 +18,8 @@ public class DeliveryVerticle extends AbstractVerticle { public void start(Promise startPromise) throws Exception { registerDeliveryChannel("email", "delivery-channel.email.queue", vertx, new EmailDeliveryChannel(vertx, "/email")); + registerDeliveryChannel("mail", "delivery-channel.mail.queue", vertx, + new MailDeliveryChannel(vertx, "/mail")); startPromise.handle(Future.succeededFuture()); } diff --git a/src/main/java/org/folio/sender/delivery/EmailDeliveryChannel.java b/src/main/java/org/folio/sender/delivery/EmailDeliveryChannel.java index 2cd542e..417a410 100644 --- a/src/main/java/org/folio/sender/delivery/EmailDeliveryChannel.java +++ b/src/main/java/org/folio/sender/delivery/EmailDeliveryChannel.java @@ -32,19 +32,14 @@ public void deliverMessage(String notificationId, JsonObject recipientJson, JsonObject message, JsonObject okapiHeadersJson) { LOG.debug("deliverMessage:: Sending message to recipient {} with message {}", recipientJson, message); try { - User recipient = recipientJson.mapTo(User.class); - EmailEntity emailEntity = message.mapTo(EmailEntity.class); - emailEntity.setNotificationId(notificationId); - emailEntity.setTo(recipient.getPersonal().getEmail()); - - OkapiHeaders okapiHeaders = okapiHeadersJson.mapTo(OkapiHeaders.class); - String url = okapiHeaders.getOkapiUrl() + emailUrlPath; - HttpRequest request = webClient.postAbs(url); - okapiHeaders.fillRequestHeaders(request.headers()); - request.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.putHeader(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN); - - request.sendJson(emailEntity, response -> { + User recipient = recipientJson.mapTo(User.class); + EmailEntity emailEntity = message.mapTo(EmailEntity.class); + emailEntity.setNotificationId(notificationId); + emailEntity.setTo(recipient.getPersonal().getEmail()); + + HttpRequest request = createRequestAndPrepareHeaders(okapiHeadersJson, emailUrlPath, webClient); + + request.sendJson(emailEntity, response -> { if (response.failed()) { LOG.error("deliverMessage:: Error from Email module {} ", response.cause().getMessage()); } else if (response.result().statusCode() != HttpStatus.SC_OK) { @@ -57,4 +52,14 @@ public void deliverMessage(String notificationId, JsonObject recipientJson, LOG.error("deliverMessage:: Error while attempting to deliver message to recipient {} ", recipientJson, e); } } + + static HttpRequest createRequestAndPrepareHeaders(JsonObject okapiHeadersJson, String urlPath, WebClient webClient) { + OkapiHeaders okapiHeaders = okapiHeadersJson.mapTo(OkapiHeaders.class); + String requestUrl = okapiHeaders.getOkapiUrl() + urlPath; + HttpRequest request = webClient.postAbs(requestUrl); + okapiHeaders.fillRequestHeaders(request.headers()); + request.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + request.putHeader(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN + "," + MediaType.APPLICATION_JSON); + return request; + } } diff --git a/src/main/java/org/folio/sender/delivery/MailDeliveryChannel.java b/src/main/java/org/folio/sender/delivery/MailDeliveryChannel.java new file mode 100644 index 0000000..0268d04 --- /dev/null +++ b/src/main/java/org/folio/sender/delivery/MailDeliveryChannel.java @@ -0,0 +1,64 @@ +package org.folio.sender.delivery; + +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.client.HttpRequest; +import io.vertx.ext.web.client.WebClient; +import org.apache.http.HttpStatus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.rest.jaxrs.model.EmailEntity; +import org.folio.rest.jaxrs.model.User; + +public class MailDeliveryChannel implements DeliveryChannel { + + private static final Logger LOG = LogManager.getLogger(MailDeliveryChannel.class); + + private final WebClient webClient; + private final String mailUrlPath; + + public MailDeliveryChannel(Vertx vertx, String mailUrlPath) { + this.webClient = WebClient.create(vertx); + this.mailUrlPath = mailUrlPath; + } + + @Override + public void deliverMessage(String notificationId, JsonObject recipientJson, + JsonObject message, JsonObject okapiHeadersJson) { + LOG.debug("deliverMessage:: Sending message to recipient {} with message {}", + recipientJson, message); + try { + User recipient = recipientJson.mapTo(User.class); + EmailEntity emailEntity = message.mapTo(EmailEntity.class); + emailEntity.setNotificationId(notificationId); + emailEntity.setTo(getTo(recipient)); + + HttpRequest request = EmailDeliveryChannel.createRequestAndPrepareHeaders( + okapiHeadersJson, mailUrlPath, webClient); + + request.sendJson(emailEntity, response -> { + if (response.failed()) { + LOG.error("deliverMessage:: Error from Mail module {} ", response.cause().getMessage()); + } else if (response.result().statusCode() != HttpStatus.SC_OK) { + String errorMessage = String.format("deliverMessage:: Mail module responded with " + + "status '%s' and body '%s'", + response.result().statusCode(), response.result().bodyAsString()); + LOG.error(errorMessage); + } + }); + } catch (Exception e) { + LOG.error("deliverMessage:: Error while attempting to " + + "deliver message to recipient {} ", recipientJson, e); + } + } + + private String getTo(User recipient) { + if (recipient.getPersonal() != null) { + return recipient.getPersonal().getLastName() + "," + + recipient.getPersonal().getFirstName() + "," + recipient.getPersonal().getEmail(); + } else { + return recipient.getId(); + } + } +} diff --git a/src/test/java/org/folio/rest/MessageDeliveryTest.java b/src/test/java/org/folio/rest/MessageDeliveryTest.java index 7477d0b..7475201 100644 --- a/src/test/java/org/folio/rest/MessageDeliveryTest.java +++ b/src/test/java/org/folio/rest/MessageDeliveryTest.java @@ -79,7 +79,6 @@ public static void tearDown(TestContext context) { vertx.close(context.asyncAssertSuccess()); } - @Test public void shouldReturn422WhenInvalidBody() { JsonObject emptyBody = new JsonObject(); @@ -155,6 +154,116 @@ public void shouldReturnNoContentAndSendEmailWhenRequestIsValid() { WireMock.urlMatching("/users/" + mockRecipient.getId()))); } + @Test + public void shouldReturnNoContentAndSendMailWhenRequestIsValid() { + User mockRecipient = new User() + .withId(UUID.randomUUID().toString()) + .withPersonal(new Personal().withEmail("test@test.com")); + + mockUserModule(mockRecipient.getId(), mockRecipient); + mockMailModule(); + + Message mailChannel1 = new Message() + .withDeliveryChannel("mail") + .withHeader("header1") + .withBody("body1") + .withOutputFormat(MediaType.TEXT_PLAIN); + Message mailChannel2 = new Message() + .withDeliveryChannel("mail") + .withHeader("header2") + .withBody("body2") + .withOutputFormat(MediaType.TEXT_HTML); + + Notification notification = new Notification() + .withNotificationId(UUID.randomUUID().toString()) + .withRecipientUserId(mockRecipient.getId()) + .withMessages(Arrays.asList(mailChannel1, mailChannel2)); + + RestAssured.given() + .spec(spec) + .header(mockUrlHeader) + .body(toJson(notification)) + .when() + .post(MESSAGE_DELIVERY_PATH) + .then() + .statusCode(HttpStatus.SC_NO_CONTENT); + + WireMock.verify(1, WireMock.getRequestedFor( + WireMock.urlMatching("/users/" + mockRecipient.getId()))); + WireMock.verify(2, WireMock.postRequestedFor( + WireMock.urlMatching("/mail"))); + } + + @Test + public void shouldReturnNoContentWhenFailedToSend() { + User mockRecipient = new User() + .withId(UUID.randomUUID().toString()) + .withPersonal(new Personal().withEmail("test@test.com")); + + mockUserModule(mockRecipient.getId(), mockRecipient); + WireMock.stubFor(WireMock.post("/mail").willReturn(WireMock.forbidden())); + + Message mailChannel1 = new Message() + .withDeliveryChannel("mail") + .withHeader("header1") + .withBody("body1") + .withOutputFormat(MediaType.TEXT_PLAIN); + + Notification notification = new Notification() + .withNotificationId(UUID.randomUUID().toString()) + .withRecipientUserId(mockRecipient.getId()) + .withMessages(Collections.singletonList(mailChannel1)); + + RestAssured.given() + .spec(spec) + .header(mockUrlHeader) + .body(toJson(notification)) + .when() + .post(MESSAGE_DELIVERY_PATH) + .then() + .statusCode(HttpStatus.SC_NO_CONTENT); + + WireMock.verify(1, WireMock.getRequestedFor( + WireMock.urlMatching("/users/" + mockRecipient.getId()))); + WireMock.verify(1, WireMock.postRequestedFor( + WireMock.urlMatching("/mail"))); + } + + @Test + public void shouldReturnNoContentWhenUnexpectedReturnCode() { + User mockRecipient = new User() + .withId(UUID.randomUUID().toString()) + .withPersonal(new Personal().withEmail("test@test.com")); + + mockUserModule(mockRecipient.getId(), mockRecipient); + WireMock.stubFor(WireMock.post("/mail").willReturn(WireMock.created())); + + Message mailChannel1 = new Message() + .withDeliveryChannel("mail") + .withHeader("header1") + .withBody("body1") + .withOutputFormat(MediaType.TEXT_PLAIN); + + Notification notification = new Notification() + .withNotificationId(UUID.randomUUID().toString()) + .withRecipientUserId(mockRecipient.getId()) + .withMessages(Collections.singletonList(mailChannel1)); + + RestAssured.given() + .spec(spec) + .header(mockUrlHeader) + .body(toJson(notification)) + .when() + .post(MESSAGE_DELIVERY_PATH) + .then() + .statusCode(HttpStatus.SC_NO_CONTENT); + + WireMock.verify(1, WireMock.getRequestedFor( + WireMock.urlMatching("/users/" + mockRecipient.getId()))); + WireMock.verify(1, WireMock.postRequestedFor( + WireMock.urlMatching("/mail"))); + } + @Test public void shouldNotFailWhenUserContainsAdditionalProperties() { User mockRecipient = new User() @@ -260,6 +369,11 @@ private void mockEmailModule() { .willReturn(WireMock.ok())); } + private void mockMailModule() { + WireMock.stubFor(WireMock.post("/mail") + .willReturn(WireMock.ok())); + } + private void mockUserModule(String userId, User response) { WireMock.stubFor(WireMock.get("/users/" + userId) .willReturn(WireMock.okJson(JsonObject.mapFrom(response).toString()))); From 3dd2d5b55fcffb20447d16215c8b730b95a17d81 Mon Sep 17 00:00:00 2001 From: Niels Erik Nielsen Date: Fri, 29 Dec 2023 14:41:02 +0100 Subject: [PATCH 2/2] Propagate permission name change (MODSENDER-66) --- descriptors/ModuleDescriptor-template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 3e1feb2..25d0f7b 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -24,7 +24,7 @@ "methods": ["POST"], "pathPattern": "/message-delivery", "permissionsRequired": ["sender.message-delivery"], - "modulePermissions": ["email.message.post", "mod-batch-print.entries.mail.post", "users.item.get" ] + "modulePermissions": ["email.message.post", "batch-print.entries.mail.post", "users.item.get" ] } ] }