Skip to content

Commit

Permalink
Merge pull request #1 from indexdata/MODSENDER-66
Browse files Browse the repository at this point in the history
Modsender 66
  • Loading branch information
JanisSaldabols authored Dec 11, 2023
2 parents 93fc56f + 14868ca commit 36d2490
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Request body example:
"outputFormat": "text/plain"
},
{
"deliveryChannel": "sms",
"deliveryChannel": "mail",
"header": "Your FOLIO password changed",
"body": "Dear Alex, Your password has been changed.",
"outputFormat": "text/plain"
Expand Down
6 changes: 5 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
{
"id" : "email",
"version" : "1.1"
},
{
"id" : "batch-print",
"version" : "1.0"
}
],
"provides": [
Expand All @@ -20,7 +24,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" ]
}
]
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/folio/sender/DeliveryVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -17,6 +18,8 @@ public class DeliveryVerticle extends AbstractVerticle {
public void start(Promise<Void> 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());
}
Expand Down
31 changes: 18 additions & 13 deletions src/main/java/org/folio/sender/delivery/EmailDeliveryChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Buffer> 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<Buffer> 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) {
Expand All @@ -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<Buffer> createRequestAndPrepareHeaders(JsonObject okapiHeadersJson, String urlPath, WebClient webClient) {
OkapiHeaders okapiHeaders = okapiHeadersJson.mapTo(OkapiHeaders.class);
String requestUrl = okapiHeaders.getOkapiUrl() + urlPath;
HttpRequest<Buffer> 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;
}
}
64 changes: 64 additions & 0 deletions src/main/java/org/folio/sender/delivery/MailDeliveryChannel.java
Original file line number Diff line number Diff line change
@@ -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<Buffer> 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();
}
}
}
116 changes: 115 additions & 1 deletion src/test/java/org/folio/rest/MessageDeliveryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ public static void tearDown(TestContext context) {
vertx.close(context.asyncAssertSuccess());
}


@Test
public void shouldReturn422WhenInvalidBody() {
JsonObject emptyBody = new JsonObject();
Expand Down Expand Up @@ -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("[email protected]"));

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("[email protected]"));

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("[email protected]"));

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()
Expand Down Expand Up @@ -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())));
Expand Down

0 comments on commit 36d2490

Please sign in to comment.