From db7363fac66a65805640e89eee2e2607b292d7de Mon Sep 17 00:00:00 2001 From: Nayan Date: Thu, 3 Jun 2021 12:14:46 +0600 Subject: [PATCH 1/7] -Issue #4706 - send email when new comment added or comment thread resolved --- .../server/controllers/CommentController.java | 8 +- .../appsmith/server/helpers/CommentUtils.java | 43 +++ .../server/notifications/EmailSender.java | 4 +- .../server/services/CommentService.java | 6 +- .../server/services/CommentServiceImpl.java | 42 ++- .../server/services/NotificationService.java | 9 +- .../services/NotificationServiceImpl.java | 163 +++++++++- .../services/OrganizationServiceImpl.java | 3 +- .../resources/email/commentAddedTemplate.html | 278 ++++++++++++++++++ .../email/commentResolvedTemplate.html | 274 +++++++++++++++++ .../email/userTaggedInCommentTemplate.html | 278 ++++++++++++++++++ .../server/services/CommentServiceTest.java | 8 +- .../services/NotificationServiceImplTest.java | 74 +++++ 13 files changed, 1156 insertions(+), 34 deletions(-) create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java create mode 100644 app/server/appsmith-server/src/main/resources/email/commentAddedTemplate.html create mode 100644 app/server/appsmith-server/src/main/resources/email/commentResolvedTemplate.html create mode 100644 app/server/appsmith-server/src/main/resources/email/userTaggedInCommentTemplate.html create mode 100644 app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java index 7d37d09b08..bd7cd7f896 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java @@ -41,7 +41,7 @@ public class CommentController extends BaseController new ResponseDTO<>(HttpStatus.CREATED.value(), created, null)); } @@ -50,7 +50,7 @@ public class CommentController extends BaseController> createThread(@Valid @RequestBody CommentThread resource, ServerWebExchange exchange) { log.debug("Going to create resource {}", resource.getClass().getName()); - return service.createThread(resource) + return service.createThread(resource, exchange.getRequest().getHeaders().getOrigin()) .map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null)); } @@ -63,10 +63,10 @@ public class CommentController extends BaseController> updateThread( @Valid @RequestBody CommentThread resource, - @PathVariable String threadId + @PathVariable String threadId, ServerWebExchange exchange ) { log.debug("Going to update resource {}", resource.getClass().getName()); - return service.updateThread(threadId, resource) + return service.updateThread(threadId, resource, exchange.getRequest().getHeaders().getOrigin()) .map(updated -> new ResponseDTO<>(HttpStatus.ACCEPTED.value(), updated, null)); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java new file mode 100644 index 0000000000..ff603c7dec --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java @@ -0,0 +1,43 @@ +package com.appsmith.server.helpers; + +import com.appsmith.server.domains.Comment; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +public class CommentUtils { + /** + * Checks whether provided user has been mentioned in the comment. Returns true if yes and false otherwise. + * @param comment Comment objects + * @param userEmail email address of the user + * @return true or false based on the condition + */ + public static boolean isUserMentioned(Comment comment, String userEmail) { + if(comment.getBody() != null && comment.getBody().getEntityMap() != null) { + for(String key : comment.getBody().getEntityMap().keySet()) { + Comment.Entity commentEntity = comment.getBody().getEntityMap().get(key); + if(commentEntity.getType().equals("mention")) { + // this comment has a mention, check the provided user is mentioned or not + if(commentEntity.getData() != null) { + Comment.EntityData.Mention mention = commentEntity.getData().getMention(); + if(mention != null && mention.getUser().getUsername().equals(userEmail)) { + return true; + } + } + } + } + } + return false; + } + + public static List getCommentBody(Comment comment) { + List commentLines = new ArrayList<>(); + if(comment.getBody() != null && comment.getBody().getBlocks() != null) { + for (Comment.Block block : comment.getBody().getBlocks()) { + commentLines.add(block.getText()); + } + } + return commentLines; + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/notifications/EmailSender.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/notifications/EmailSender.java index 00e320d92e..c90ad40cf8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/notifications/EmailSender.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/notifications/EmailSender.java @@ -43,7 +43,7 @@ public class EmailSender { REPLY_TO = makeReplyTo(); } - public Mono sendMail(String to, String subject, String text, Map params) { + public Mono sendMail(String to, String subject, String text, Map params) { /** * Creating a publisher which sends email in a blocking fashion, subscribing on the bounded elastic @@ -122,7 +122,7 @@ public class EmailSender { * @return Template string with Mustache replacements applied. * @throws IOException bubbled from Mustache renderer. */ - private String replaceEmailTemplate(String template, Map params) throws IOException { + private String replaceEmailTemplate(String template, Map params) throws IOException { MustacheFactory mf = new DefaultMustacheFactory(); StringWriter stringWriter = new StringWriter(); Mustache mustache = mf.compile(template); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentService.java index 4960633140..f20289c7ea 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentService.java @@ -8,11 +8,11 @@ import java.util.List; public interface CommentService extends CrudService { - Mono create(String threadId, Comment organization); + Mono create(String threadId, Comment organization, String originHeader); - Mono createThread(CommentThread commentThread); + Mono createThread(CommentThread commentThread, String originHeader); - Mono updateThread(String threadId, CommentThread commentThread); + Mono updateThread(String threadId, CommentThread commentThread, String originHeader); Mono> getThreadsByApplicationId(String applicationId); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java index 95de56d12c..74110cdb15 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java @@ -79,11 +79,11 @@ public class CommentServiceImpl extends BaseService create(String threadId, Comment comment) { - return create(threadId, comment, true); + public Mono create(String threadId, Comment comment, String originHeader) { + return create(threadId, comment, originHeader, true); } - public Mono create(String threadId, Comment comment, boolean shouldCreateNotification) { + private Mono create(String threadId, Comment comment, String originHeader, boolean shouldCreateNotification) { if (StringUtils.isWhitespace(comment.getAuthorName())) { // Error: User can't explicitly set the author name. It will be the currently logged in user. return Mono.empty(); @@ -125,36 +125,39 @@ public class CommentServiceImpl extends BaseService { final User user = tuple.getT1(); - final Comment savedComment = tuple.getT2(); - + CommentThread commentThread = tuple.getT2(); + final Comment savedComment = tuple.getT3(); + Mono sendEmailForComment = notificationService.sendEmailForComment( + comment.getAuthorUsername(), commentThread.getApplicationId(), comment, originHeader + ); if (shouldCreateNotification) { final Set usernames = policyUtils.findUsernamesWithPermission( savedComment.getPolicies(), AclPermission.READ_COMMENT); - - List> monos = new ArrayList<>(); + List> notificationMonos = new ArrayList<>(); for (String username : usernames) { if (!username.equals(user.getUsername())) { final CommentNotification notification = new CommentNotification(); notification.setComment(savedComment); notification.setForUsername(username); - monos.add(notificationService.create(notification)); + Mono notificationMono = notificationService.create(notification); + notificationMonos.add(notificationMono); } } - - return Flux.concat(monos).then(Mono.just(savedComment)); + return Flux.concat(notificationMonos).then(sendEmailForComment).then(Mono.just(savedComment)); } else { - return Mono.just(savedComment); + return sendEmailForComment.thenReturn(savedComment); } }); } @Override - public Mono createThread(CommentThread commentThread) { + public Mono createThread(CommentThread commentThread, String originHeader) { // 1. Check if this user has permission on the application given by `commentThread.applicationId`. // 2. Save the comment thread and get it's id. This is the `threadId`. // 3. Pull the comment out of the list of comments, set it's `threadId` and save it separately. @@ -211,7 +214,7 @@ public class CommentServiceImpl extends BaseService updateThread(String threadId, CommentThread commentThread) { + public Mono updateThread(String threadId, CommentThread commentThread, String originHeader) { return Mono.zip( sessionUserService.getCurrentUser(), // Resolving, pinning and marking as read don't need manage permission on the thread. @@ -301,7 +303,15 @@ public class CommentServiceImpl extends BaseService { updatedThread.setIsViewed(true); - return Mono.just(updatedThread); + // send email if comment thread is resolved + CommentThread.CommentThreadState resolvedState = commentThread.getResolvedState(); + if(resolvedState != null && resolvedState.getActive()) { + return notificationService.sendEmailForComment(user.getUsername(), + updatedThread.getApplicationId(), updatedThread, originHeader + ).thenReturn(updatedThread); + } else { + return Mono.just(updatedThread); + } }); }); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java index cce58ac380..e7b3e314a9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java @@ -1,7 +1,14 @@ package com.appsmith.server.services; +import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.Comment; import com.appsmith.server.domains.Notification; +import com.appsmith.server.domains.Organization; +import reactor.core.publisher.Mono; + +import java.util.List; public interface NotificationService extends CrudService { - +// Mono sendEmailForComment(Comment comment, String originHeader, String applicationId); + Mono sendEmailForComment(String authorUserName, String applicationId, E commentDomain, String originHeader); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java index 614e68fddc..21c8c55fd0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java @@ -1,7 +1,12 @@ package com.appsmith.server.services; -import com.appsmith.server.domains.Notification; +import com.appsmith.external.models.BaseDomain; +import com.appsmith.server.domains.*; +import com.appsmith.server.helpers.CommentUtils; +import com.appsmith.server.notifications.EmailSender; +import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.NotificationRepository; +import com.appsmith.server.repositories.OrganizationRepository; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; @@ -13,6 +18,10 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import javax.validation.Validator; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @Slf4j @Service @@ -21,6 +30,13 @@ public class NotificationServiceImpl implements NotificationService { private final SessionUserService sessionUserService; + private final EmailSender emailSender; + private final OrganizationRepository organizationRepository; + private final ApplicationRepository applicationRepository; + + private static final String COMMENT_ADDED_EMAIL_TEMPLATE = "email/commentAddedTemplate.html"; + private static final String USER_MENTIONED_EMAIL_TEMPLATE = "email/userTaggedInCommentTemplate.html"; + private static final String THREAD_RESOLVED_EMAIL_TEMPLATE = "email/commentResolvedTemplate.html"; public NotificationServiceImpl( Scheduler scheduler, @@ -29,10 +45,13 @@ public class NotificationServiceImpl ReactiveMongoTemplate reactiveMongoTemplate, NotificationRepository repository, AnalyticsService analyticsService, - SessionUserService sessionUserService - ) { + SessionUserService sessionUserService, + EmailSender emailSender, OrganizationRepository organizationRepository, ApplicationRepository applicationRepository) { super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService); this.sessionUserService = sessionUserService; + this.emailSender = emailSender; + this.organizationRepository = organizationRepository; + this.applicationRepository = applicationRepository; } @Override @@ -58,4 +77,142 @@ public class NotificationServiceImpl .flatMapMany(user -> repository.findByForUsername(user.getUsername())); } +// @Override +// public Mono sendEmailForComment(Comment comment, String originHeader, String applicationId) { +// return applicationRepository.findById(applicationId).flatMap(application -> { +// return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { +// List> emailMonos = new ArrayList<>(); +// for (UserRole userRole : organization.getUserRoles()) { +// if(!comment.getAuthorUsername().equals(userRole.getUsername())) { +// String receiverName = StringUtils.isEmpty(userRole.getName()) ? "User" : userRole.getName(); +// String receiverEmail = userRole.getUsername(); +// +// Map templateParams = new HashMap<>(); +// templateParams.put("App_User_Name", receiverName); +// templateParams.put("Commenter_Name", comment.getAuthorName()); +// templateParams.put("Application_Name", application.getName()); +// templateParams.put("Organization_Name", organization.getName()); +// templateParams.put("Comment_Body", CommentUtils.getCommentBody(comment)); +// templateParams.put("inviteUrl", originHeader); +// +// String emailTemplate = COMMENT_ADDED_EMAIL_TEMPLATE; +// String emailSubject = String.format( +// "New comment from %s in %s", comment.getAuthorName(), application.getName() +// ); +// +// // check if user has been mentioned in the comment +// if(CommentUtils.isUserMentioned(comment, receiverEmail)) { +// emailTemplate = USER_MENTIONED_EMAIL_TEMPLATE; +// emailSubject = String.format("New comment for you from %s", comment.getAuthorName()); +// } +// emailMonos.add( +// emailSender.sendMail(receiverEmail, emailSubject, emailTemplate, templateParams) +// ); +// } +// } +// return Flux.concat(emailMonos).then(); +// }); +// }); +// } +// +// public Mono sendEmailForResolveCommentThread(CommentThread commentThread, String originHeader, +// String applicationId) { +// return applicationRepository.findById(applicationId).flatMap(application -> { +// return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { +// List> emailMonos = new ArrayList<>(); +// CommentThread.CommentThreadState resolvedState = commentThread.getResolvedState(); +// for (UserRole userRole : organization.getUserRoles()) { +// if(!resolvedState.getAuthorUsername().equals(userRole.getUsername())) { +// String receiverName = StringUtils.isEmpty(userRole.getName()) ? "User" : userRole.getName(); +// String receiverEmail = userRole.getUsername(); +// +// Map templateParams = new HashMap<>(); +// templateParams.put("App_User_Name", receiverName); +// templateParams.put("Commenter_Name", resolvedState.getAuthorName()); +// templateParams.put("Application_Name", application.getName()); +// templateParams.put("Organization_Name", organization.getName()); +// templateParams.put("inviteUrl", originHeader); +// +// String emailSubject = String.format( +// "%s has resolved comment in %s", resolvedState.getAuthorName(), application.getName() +// ); +// emailMonos.add( +// emailSender.sendMail(receiverEmail, emailSubject, THREAD_RESOLVED_EMAIL_TEMPLATE, templateParams) +// ); +// } +// } +// return Flux.concat(emailMonos).then(); +// }); +// }); +// } + + private Mono getEmailSenderMono(UserRole receiverUserRole, CommentThread commentThread, + String originHeader, Application application, Organization organization) { + String receiverName = StringUtils.isEmpty(receiverUserRole.getName()) ? "User" : receiverUserRole.getName(); + String receiverEmail = receiverUserRole.getUsername(); + CommentThread.CommentThreadState resolvedState = commentThread.getResolvedState(); + Map templateParams = new HashMap<>(); + templateParams.put("App_User_Name", receiverName); + templateParams.put("Commenter_Name", resolvedState.getAuthorName()); + templateParams.put("Application_Name", application.getName()); + templateParams.put("Organization_Name", organization.getName()); + templateParams.put("inviteUrl", originHeader); + + String emailSubject = String.format( + "%s has resolved comment in %s", resolvedState.getAuthorName(), application.getName() + ); + return emailSender.sendMail(receiverEmail, emailSubject, THREAD_RESOLVED_EMAIL_TEMPLATE, templateParams); + } + + private Mono getEmailSenderMono(UserRole receiverUserRole, Comment comment, String originHeader, + Application application, Organization organization) { + String receiverName = StringUtils.isEmpty(receiverUserRole.getName()) ? "User" : receiverUserRole.getName(); + String receiverEmail = receiverUserRole.getUsername(); + + Map templateParams = new HashMap<>(); + templateParams.put("App_User_Name", receiverName); + templateParams.put("Commenter_Name", comment.getAuthorName()); + templateParams.put("Application_Name", application.getName()); + templateParams.put("Organization_Name", organization.getName()); + templateParams.put("Comment_Body", CommentUtils.getCommentBody(comment)); + templateParams.put("inviteUrl", originHeader); + + String emailTemplate = COMMENT_ADDED_EMAIL_TEMPLATE; + String emailSubject = String.format( + "New comment from %s in %s", comment.getAuthorName(), application.getName() + ); + + // check if user has been mentioned in the comment + if(CommentUtils.isUserMentioned(comment, receiverEmail)) { + emailTemplate = USER_MENTIONED_EMAIL_TEMPLATE; + emailSubject = String.format("New comment for you from %s", comment.getAuthorName()); + } + return emailSender.sendMail(receiverEmail, emailSubject, emailTemplate, templateParams); + } + + @Override + public Mono sendEmailForComment(String authorUserName, String applicationId, E commentDomain, + String originHeader) { + return applicationRepository.findById(applicationId).flatMap(application -> { + return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { + List> emailMonos = new ArrayList<>(); + for (UserRole userRole : organization.getUserRoles()) { + if(!authorUserName.equals(userRole.getUsername())) { + if(commentDomain instanceof Comment) { + Comment comment = (Comment)commentDomain; + emailMonos.add( + getEmailSenderMono(userRole, comment, originHeader, application, organization) + ); + } else if(commentDomain instanceof CommentThread) { + CommentThread commentThread = (CommentThread) commentDomain; + emailMonos.add( + getEmailSenderMono(userRole, commentThread, originHeader, application, organization) + ); + } + } + } + return Flux.concat(emailMonos).then(); + }); + }); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationServiceImpl.java index d8659fe97b..efd0f10899 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationServiceImpl.java @@ -48,7 +48,8 @@ import static com.appsmith.server.acl.AclPermission.USER_MANAGE_ORGANIZATIONS; @Slf4j @Service -public class OrganizationServiceImpl extends BaseService implements OrganizationService { +public class OrganizationServiceImpl extends BaseService + implements OrganizationService { private final PluginRepository pluginRepository; private final SessionUserService sessionUserService; diff --git a/app/server/appsmith-server/src/main/resources/email/commentAddedTemplate.html b/app/server/appsmith-server/src/main/resources/email/commentAddedTemplate.html new file mode 100644 index 0000000000..55cd64b88d --- /dev/null +++ b/app/server/appsmith-server/src/main/resources/email/commentAddedTemplate.html @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + +
+
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+ Hi {{App_User_Name}}, +
+
+ {{Commenter_Name}} just added a new comment in {{Application_Name}} in the {{Organization_Name}} organization. +
+
+ {{#Comment_Body}} +
{{ . }}
+ {{/Comment_Body}} +
+
+ Please follow this link to view and respond to the comment. +
+
+
+
+ + + + + + +
+ + + + + + +
+ Go to Comment +
+
+
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/app/server/appsmith-server/src/main/resources/email/commentResolvedTemplate.html b/app/server/appsmith-server/src/main/resources/email/commentResolvedTemplate.html new file mode 100644 index 0000000000..5c28f11d85 --- /dev/null +++ b/app/server/appsmith-server/src/main/resources/email/commentResolvedTemplate.html @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + +
+
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+ Hi {{App_User_Name}}, +
+
+ {{Commenter_Name}} has resolved the comment in {{Application_Name}} in the {{Organization_Name}} organization. +
+ +
+ Please follow this link to view and re-open the comment. +
+
+
+
+ + + + + + +
+ + + + + + +
+ Go to Comment +
+
+
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/app/server/appsmith-server/src/main/resources/email/userTaggedInCommentTemplate.html b/app/server/appsmith-server/src/main/resources/email/userTaggedInCommentTemplate.html new file mode 100644 index 0000000000..541e1348f7 --- /dev/null +++ b/app/server/appsmith-server/src/main/resources/email/userTaggedInCommentTemplate.html @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + +
+
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+ Hi {{App_User_Name}}, +
+
+ {{Commenter_Name}} tagged you in a comment in {{Application_Name}} in the {{Organization_Name}} organization. +
+
+ {{#Comment_Body}} +
{{ . }}
+ {{/Comment_Body}} +
+
+ Please follow this link to view and respond to the comment. +
+
+
+
+ + + + + + +
+ + + + + + +
+ Go to Comment +
+
+
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/CommentServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/CommentServiceTest.java index 7c29eb748d..2a46b7195d 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/CommentServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/CommentServiceTest.java @@ -57,7 +57,7 @@ public class CommentServiceTest { thread.setComments(List.of( makePlainTextComment("comment one") )); - return commentService.createThread(thread); + return commentService.createThread(thread, "https://app.appsmith.com"); }) .zipWhen(thread -> commentService.getThreadsByApplicationId(thread.getApplicationId())); @@ -128,7 +128,7 @@ public class CommentServiceTest { thread.setComments(List.of( makePlainTextComment("Test Comment") )); - return commentService.createThread(thread); + return commentService.createThread(thread, "https://app.appsmith.com"); }) .flatMap(commentThread -> Mono.just(commentThread.getComments().get(0))) .cache(); @@ -163,7 +163,7 @@ public class CommentServiceTest { final CommentThread thread = new CommentThread(); thread.setApplicationId(application.getId()); thread.setComments(List.of(makePlainTextComment("Test Comment"))); - return commentService.createThread(thread); + return commentService.createThread(thread, "https://app.appsmith.com"); }) .flatMap(commentThread -> Mono.just(commentThread.getComments().get(0))) .flatMap(comment -> { @@ -202,7 +202,7 @@ public class CommentServiceTest { final CommentThread thread = new CommentThread(); thread.setApplicationId(application.getId()); thread.setComments(List.of(makePlainTextComment("Test Comment"))); - return commentService.createThread(thread); + return commentService.createThread(thread, "https://app.appsmith.com"); }) .flatMap(commentThread -> Mono.just(commentThread.getComments().get(0))) .flatMap(comment -> { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java new file mode 100644 index 0000000000..e248b39457 --- /dev/null +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java @@ -0,0 +1,74 @@ +package com.appsmith.server.services; + +import com.appsmith.server.domains.Comment; +import com.appsmith.server.notifications.EmailSender; +import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.repositories.NotificationRepository; +import com.appsmith.server.repositories.OrganizationRepository; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.data.mongodb.core.convert.MongoConverter; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; + +import javax.validation.Validator; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +public class NotificationServiceImplTest { + private static final String COMMENT_ADDED_EMAIL_TEMPLATE = "email/commentAddedTemplate.html"; + + @MockBean SessionUserService sessionUserService; + @MockBean Scheduler scheduler; + @MockBean MongoConverter mongoConverter; + @MockBean ReactiveMongoTemplate reactiveMongoTemplate; + @MockBean NotificationRepository notificationRepository; + @MockBean Validator validator; + @MockBean AnalyticsService analyticsService; + @MockBean EmailSender emailSender; + @MockBean OrganizationRepository organizationRepository; + @MockBean ApplicationRepository applicationRepository; + + private NotificationService notificationService; + + @Before + public void setUp() { + notificationService = new NotificationServiceImpl( + scheduler, validator, mongoConverter, reactiveMongoTemplate, notificationRepository, analyticsService, + sessionUserService, emailSender, + organizationRepository, applicationRepository); + } + + @Test + public void sendEmailForComment_WhenMailSenderReturnsTrue_ReturnsTrue() { + String fromUserEmail = "nayan@appsmith.com"; + String emailReceiver = "rafiqnayan@appsmith.com"; + String originHeader = "https://example.com"; + + Map emailTemplateParams = new HashMap<>(); + emailTemplateParams.put("Commenter_User_Name", fromUserEmail); + emailTemplateParams.put("inviteUrl", originHeader); + Mockito.when( + emailSender.sendMail( + emailReceiver, "New comment", COMMENT_ADDED_EMAIL_TEMPLATE, emailTemplateParams + ) + ).thenReturn(Mono.just(Boolean.TRUE)); + + Comment comment = new Comment(); + comment.setAuthorUsername(fromUserEmail); + +// StepVerifier.create(notificationService.sendEmailForComment(emailReceiver, comment, originHeader)) +// .assertNext(t -> { +// assertEquals(Boolean.TRUE, t); +// }) +// .verifyComplete(); + } +} \ No newline at end of file From f9493a4bd23cce03f1b6436619b97dd768b5c211 Mon Sep 17 00:00:00 2001 From: Nayan Date: Mon, 7 Jun 2021 13:00:46 +0600 Subject: [PATCH 2/7] -add unit test for utils --- .../appsmith/server/helpers/CommentUtils.java | 2 +- .../server/helpers/CommentUtilsTest.java | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java index ff603c7dec..e62bf832f9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java @@ -21,7 +21,7 @@ public class CommentUtils { // this comment has a mention, check the provided user is mentioned or not if(commentEntity.getData() != null) { Comment.EntityData.Mention mention = commentEntity.getData().getMention(); - if(mention != null && mention.getUser().getUsername().equals(userEmail)) { + if(mention.getUser().getUsername().equals(userEmail)) { return true; } } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java new file mode 100644 index 0000000000..0b8312577b --- /dev/null +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java @@ -0,0 +1,93 @@ +package com.appsmith.server.helpers; + +import com.appsmith.server.domains.Comment; +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class CommentUtilsTest { + @Test + void getCommentBody_WhenBodyIsNull_ReturnsEmptyList() { + Comment comment = new Comment(); + Assert.assertEquals(0, CommentUtils.getCommentBody(comment).size()); + } + + @Test + void getCommentBody_WhenBodyHasMultipleBlocks_ReturnsValidBodies() { + Comment.Block commentBlock1 = new Comment.Block(); + commentBlock1.setText("First line"); + Comment.Block commentBlock2 = new Comment.Block(); + commentBlock1.setText("Second line"); + + Comment.Body commentBody = new Comment.Body(); + commentBody.setBlocks(List.of(commentBlock1, commentBlock2)); + + Comment comment = new Comment(); + comment.setBody(commentBody); + + Assert.assertEquals(2, CommentUtils.getCommentBody(comment).size()); + Assert.assertEquals(commentBlock1.getText(), CommentUtils.getCommentBody(comment).get(0)); + Assert.assertEquals(commentBlock2.getText(), CommentUtils.getCommentBody(comment).get(1)); + } + + @Test + public void isUserMentioned_WhenBodyIsNull_ReturnsFalse() { + Comment comment = new Comment(); + Assert.assertFalse(CommentUtils.isUserMentioned(comment, "user@abc.com")); + } + + @Test + public void isUserMentioned_WhenBodyHasNoMention_ReturnsFalse() { + Comment.Body body = new Comment.Body(); + Comment comment = new Comment(); + comment.setBody(body); + Assert.assertFalse(CommentUtils.isUserMentioned(comment, "user@abc.com")); + + Comment.Entity entity = new Comment.Entity(); + Map entityMap = new HashMap<>(); + entityMap.put("abc", entity); + body.setEntityMap(entityMap); + comment.setBody(body); + Assert.assertFalse(CommentUtils.isUserMentioned(comment, "user@abc.com")); + } + + private Map createEntityMapForUsers(List mentionedUserNames) { + Map entityMap = new HashMap<>(); + for (String username: mentionedUserNames) { + Comment.EntityData.EntityUser entityUser = new Comment.EntityData.EntityUser(); + entityUser.setUsername(username); + Comment.EntityData.Mention mention = new Comment.EntityData.Mention(); + mention.setUser(entityUser); + + Comment.EntityData entityData = new Comment.EntityData(); + entityData.setMention(mention); + + Comment.Entity entity = new Comment.Entity(); + entity.setType("mention"); + entity.setData(entityData); + entityMap.put(username, entity); + } + return entityMap; + } + + @Test + public void isUserMentioned_WhenSomeoneIsMentioned_ReturnsCorrectValue() { + Map entityMap = this.createEntityMapForUsers( + List.of("1", "2", "3") + ); + Comment.Body body = new Comment.Body(); + body.setEntityMap(entityMap); + Comment comment = new Comment(); + comment.setBody(body); + + Assert.assertTrue(CommentUtils.isUserMentioned(comment, "1")); + Assert.assertTrue(CommentUtils.isUserMentioned(comment, "2")); + Assert.assertTrue(CommentUtils.isUserMentioned(comment, "3")); + Assert.assertFalse(CommentUtils.isUserMentioned(comment, "4")); + } +} \ No newline at end of file From 5550c9310fe80abc5a78338701f670897af9fc9e Mon Sep 17 00:00:00 2001 From: Nayan Date: Tue, 8 Jun 2021 03:00:40 +0600 Subject: [PATCH 3/7] -moved email sender to background task --- .../server/events/AbstractCommentEvent.java | 13 ++ .../server/events/CommentAddedEvent.java | 16 ++ .../events/CommentThreadClosedEvent.java | 16 ++ .../server/services/CommentServiceImpl.java | 21 ++- .../server/services/NotificationService.java | 2 +- .../services/NotificationServiceImpl.java | 153 +---------------- .../server/solutions/EmailEventHandler.java | 160 ++++++++++++++++++ .../services/NotificationServiceImplTest.java | 10 +- 8 files changed, 222 insertions(+), 169 deletions(-) create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/events/AbstractCommentEvent.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentAddedEvent.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentThreadClosedEvent.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/events/AbstractCommentEvent.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/AbstractCommentEvent.java new file mode 100644 index 0000000000..003947218f --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/AbstractCommentEvent.java @@ -0,0 +1,13 @@ +package com.appsmith.server.events; + +import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.Organization; +import lombok.Data; + +@Data +public abstract class AbstractCommentEvent { + private final String authorUserName; + private final Organization organization; + private final Application application; + private final String originHeader; +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentAddedEvent.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentAddedEvent.java new file mode 100644 index 0000000000..441702d790 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentAddedEvent.java @@ -0,0 +1,16 @@ +package com.appsmith.server.events; + +import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.Comment; +import com.appsmith.server.domains.Organization; +import lombok.Getter; + +@Getter +public class CommentAddedEvent extends AbstractCommentEvent { + private final Comment comment; + + public CommentAddedEvent(String authorUserName, Organization organization, Application application, String originHeader, Comment comment) { + super(authorUserName, organization, application, originHeader); + this.comment = comment; + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentThreadClosedEvent.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentThreadClosedEvent.java new file mode 100644 index 0000000000..6c2fc7bea1 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/CommentThreadClosedEvent.java @@ -0,0 +1,16 @@ +package com.appsmith.server.events; + +import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.CommentThread; +import com.appsmith.server.domains.Organization; +import lombok.Getter; + +@Getter +public class CommentThreadClosedEvent extends AbstractCommentEvent { + private final CommentThread commentThread; + + public CommentThreadClosedEvent(String authorUserName, Organization organization, Application application, String originHeader, CommentThread commentThread) { + super(authorUserName, organization, application, originHeader); + this.commentThread = commentThread; + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java index 74110cdb15..7643c784bb 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommentServiceImpl.java @@ -11,11 +11,14 @@ import com.appsmith.server.domains.CommentThread; import com.appsmith.server.domains.CommentThreadNotification; import com.appsmith.server.domains.Notification; import com.appsmith.server.domains.User; +import com.appsmith.server.events.CommentAddedEvent; +import com.appsmith.server.events.CommentThreadClosedEvent; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.PolicyUtils; import com.appsmith.server.repositories.CommentRepository; import com.appsmith.server.repositories.CommentThreadRepository; +import com.appsmith.server.solutions.EmailEventHandler; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -52,6 +55,7 @@ public class CommentServiceImpl extends BaseService sendEmailForComment = notificationService.sendEmailForComment( + Mono publishEmailMono = emailEventHandler.publish( comment.getAuthorUsername(), commentThread.getApplicationId(), comment, originHeader ); + if (shouldCreateNotification) { final Set usernames = policyUtils.findUsernamesWithPermission( savedComment.getPolicies(), AclPermission.READ_COMMENT); @@ -149,9 +155,9 @@ public class CommentServiceImpl extends BaseService { // Mono sendEmailForComment(Comment comment, String originHeader, String applicationId); - Mono sendEmailForComment(String authorUserName, String applicationId, E commentDomain, String originHeader); +// Mono sendEmailForComment(String authorUserName, String applicationId, E commentDomain, String originHeader); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java index 21c8c55fd0..2d091a84ce 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java @@ -1,6 +1,5 @@ package com.appsmith.server.services; -import com.appsmith.external.models.BaseDomain; import com.appsmith.server.domains.*; import com.appsmith.server.helpers.CommentUtils; import com.appsmith.server.notifications.EmailSender; @@ -30,13 +29,6 @@ public class NotificationServiceImpl implements NotificationService { private final SessionUserService sessionUserService; - private final EmailSender emailSender; - private final OrganizationRepository organizationRepository; - private final ApplicationRepository applicationRepository; - - private static final String COMMENT_ADDED_EMAIL_TEMPLATE = "email/commentAddedTemplate.html"; - private static final String USER_MENTIONED_EMAIL_TEMPLATE = "email/userTaggedInCommentTemplate.html"; - private static final String THREAD_RESOLVED_EMAIL_TEMPLATE = "email/commentResolvedTemplate.html"; public NotificationServiceImpl( Scheduler scheduler, @@ -45,13 +37,9 @@ public class NotificationServiceImpl ReactiveMongoTemplate reactiveMongoTemplate, NotificationRepository repository, AnalyticsService analyticsService, - SessionUserService sessionUserService, - EmailSender emailSender, OrganizationRepository organizationRepository, ApplicationRepository applicationRepository) { + SessionUserService sessionUserService) { super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService); this.sessionUserService = sessionUserService; - this.emailSender = emailSender; - this.organizationRepository = organizationRepository; - this.applicationRepository = applicationRepository; } @Override @@ -76,143 +64,4 @@ public class NotificationServiceImpl return sessionUserService.getCurrentUser() .flatMapMany(user -> repository.findByForUsername(user.getUsername())); } - -// @Override -// public Mono sendEmailForComment(Comment comment, String originHeader, String applicationId) { -// return applicationRepository.findById(applicationId).flatMap(application -> { -// return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { -// List> emailMonos = new ArrayList<>(); -// for (UserRole userRole : organization.getUserRoles()) { -// if(!comment.getAuthorUsername().equals(userRole.getUsername())) { -// String receiverName = StringUtils.isEmpty(userRole.getName()) ? "User" : userRole.getName(); -// String receiverEmail = userRole.getUsername(); -// -// Map templateParams = new HashMap<>(); -// templateParams.put("App_User_Name", receiverName); -// templateParams.put("Commenter_Name", comment.getAuthorName()); -// templateParams.put("Application_Name", application.getName()); -// templateParams.put("Organization_Name", organization.getName()); -// templateParams.put("Comment_Body", CommentUtils.getCommentBody(comment)); -// templateParams.put("inviteUrl", originHeader); -// -// String emailTemplate = COMMENT_ADDED_EMAIL_TEMPLATE; -// String emailSubject = String.format( -// "New comment from %s in %s", comment.getAuthorName(), application.getName() -// ); -// -// // check if user has been mentioned in the comment -// if(CommentUtils.isUserMentioned(comment, receiverEmail)) { -// emailTemplate = USER_MENTIONED_EMAIL_TEMPLATE; -// emailSubject = String.format("New comment for you from %s", comment.getAuthorName()); -// } -// emailMonos.add( -// emailSender.sendMail(receiverEmail, emailSubject, emailTemplate, templateParams) -// ); -// } -// } -// return Flux.concat(emailMonos).then(); -// }); -// }); -// } -// -// public Mono sendEmailForResolveCommentThread(CommentThread commentThread, String originHeader, -// String applicationId) { -// return applicationRepository.findById(applicationId).flatMap(application -> { -// return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { -// List> emailMonos = new ArrayList<>(); -// CommentThread.CommentThreadState resolvedState = commentThread.getResolvedState(); -// for (UserRole userRole : organization.getUserRoles()) { -// if(!resolvedState.getAuthorUsername().equals(userRole.getUsername())) { -// String receiverName = StringUtils.isEmpty(userRole.getName()) ? "User" : userRole.getName(); -// String receiverEmail = userRole.getUsername(); -// -// Map templateParams = new HashMap<>(); -// templateParams.put("App_User_Name", receiverName); -// templateParams.put("Commenter_Name", resolvedState.getAuthorName()); -// templateParams.put("Application_Name", application.getName()); -// templateParams.put("Organization_Name", organization.getName()); -// templateParams.put("inviteUrl", originHeader); -// -// String emailSubject = String.format( -// "%s has resolved comment in %s", resolvedState.getAuthorName(), application.getName() -// ); -// emailMonos.add( -// emailSender.sendMail(receiverEmail, emailSubject, THREAD_RESOLVED_EMAIL_TEMPLATE, templateParams) -// ); -// } -// } -// return Flux.concat(emailMonos).then(); -// }); -// }); -// } - - private Mono getEmailSenderMono(UserRole receiverUserRole, CommentThread commentThread, - String originHeader, Application application, Organization organization) { - String receiverName = StringUtils.isEmpty(receiverUserRole.getName()) ? "User" : receiverUserRole.getName(); - String receiverEmail = receiverUserRole.getUsername(); - CommentThread.CommentThreadState resolvedState = commentThread.getResolvedState(); - Map templateParams = new HashMap<>(); - templateParams.put("App_User_Name", receiverName); - templateParams.put("Commenter_Name", resolvedState.getAuthorName()); - templateParams.put("Application_Name", application.getName()); - templateParams.put("Organization_Name", organization.getName()); - templateParams.put("inviteUrl", originHeader); - - String emailSubject = String.format( - "%s has resolved comment in %s", resolvedState.getAuthorName(), application.getName() - ); - return emailSender.sendMail(receiverEmail, emailSubject, THREAD_RESOLVED_EMAIL_TEMPLATE, templateParams); - } - - private Mono getEmailSenderMono(UserRole receiverUserRole, Comment comment, String originHeader, - Application application, Organization organization) { - String receiverName = StringUtils.isEmpty(receiverUserRole.getName()) ? "User" : receiverUserRole.getName(); - String receiverEmail = receiverUserRole.getUsername(); - - Map templateParams = new HashMap<>(); - templateParams.put("App_User_Name", receiverName); - templateParams.put("Commenter_Name", comment.getAuthorName()); - templateParams.put("Application_Name", application.getName()); - templateParams.put("Organization_Name", organization.getName()); - templateParams.put("Comment_Body", CommentUtils.getCommentBody(comment)); - templateParams.put("inviteUrl", originHeader); - - String emailTemplate = COMMENT_ADDED_EMAIL_TEMPLATE; - String emailSubject = String.format( - "New comment from %s in %s", comment.getAuthorName(), application.getName() - ); - - // check if user has been mentioned in the comment - if(CommentUtils.isUserMentioned(comment, receiverEmail)) { - emailTemplate = USER_MENTIONED_EMAIL_TEMPLATE; - emailSubject = String.format("New comment for you from %s", comment.getAuthorName()); - } - return emailSender.sendMail(receiverEmail, emailSubject, emailTemplate, templateParams); - } - - @Override - public Mono sendEmailForComment(String authorUserName, String applicationId, E commentDomain, - String originHeader) { - return applicationRepository.findById(applicationId).flatMap(application -> { - return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { - List> emailMonos = new ArrayList<>(); - for (UserRole userRole : organization.getUserRoles()) { - if(!authorUserName.equals(userRole.getUsername())) { - if(commentDomain instanceof Comment) { - Comment comment = (Comment)commentDomain; - emailMonos.add( - getEmailSenderMono(userRole, comment, originHeader, application, organization) - ); - } else if(commentDomain instanceof CommentThread) { - CommentThread commentThread = (CommentThread) commentDomain; - emailMonos.add( - getEmailSenderMono(userRole, commentThread, originHeader, application, organization) - ); - } - } - } - return Flux.concat(emailMonos).then(); - }); - }); - } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java new file mode 100644 index 0000000000..f5214c132f --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java @@ -0,0 +1,160 @@ +package com.appsmith.server.solutions; + +import com.appsmith.server.domains.*; +import com.appsmith.server.events.CommentAddedEvent; +import com.appsmith.server.events.CommentThreadClosedEvent; +import com.appsmith.server.helpers.CommentUtils; +import com.appsmith.server.notifications.EmailSender; +import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.repositories.OrganizationRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +import java.util.ArrayList; +import java.util.HashMap; + +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +@Slf4j +public class EmailEventHandler { + private static final String COMMENT_ADDED_EMAIL_TEMPLATE = "email/commentAddedTemplate.html"; + private static final String USER_MENTIONED_EMAIL_TEMPLATE = "email/userTaggedInCommentTemplate.html"; + private static final String THREAD_RESOLVED_EMAIL_TEMPLATE = "email/commentResolvedTemplate.html"; + + private final ApplicationEventPublisher applicationEventPublisher; + private final EmailSender emailSender; + private final OrganizationRepository organizationRepository; + private final ApplicationRepository applicationRepository; + + public Mono publish(String authorUserName, String applicationId, Comment comment, String originHeader) { + return applicationRepository.findById(applicationId).flatMap(application -> { + return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { + applicationEventPublisher.publishEvent( + new CommentAddedEvent(authorUserName, organization, application, originHeader, comment) + ); + return Mono.just(organization); + }); + }).thenReturn(Boolean.TRUE); + } + + public Mono publish(String authorUserName, String applicationId, CommentThread thread, String originHeader) { + return applicationRepository.findById(applicationId).flatMap(application -> { + return organizationRepository.findById(application.getOrganizationId()).flatMap(organization -> { + applicationEventPublisher.publishEvent( + new CommentThreadClosedEvent(authorUserName, organization, application, originHeader, thread) + ); + return Mono.just(organization); + }); + }).thenReturn(Boolean.TRUE); + } + + @Async + @EventListener + public void handle(CommentAddedEvent event) { + this.sendEmailForComment( + event.getAuthorUserName(), event.getApplication(), event.getOrganization(), + event.getComment(), event.getOriginHeader() + ).subscribeOn(Schedulers.elastic()) + .subscribe(); + } + + @Async + @EventListener + public void handle(CommentThreadClosedEvent event) { + this.sendEmailForComment( + event.getAuthorUserName(), event.getApplication(), event.getOrganization(), + event.getCommentThread(), event.getOriginHeader() + ) + .subscribeOn(Schedulers.elastic()) + .subscribe(); + } + + private Mono getEmailSenderMono(UserRole receiverUserRole, CommentThread commentThread, + String originHeader, Application application, Organization organization) { + String receiverName = StringUtils.isEmpty(receiverUserRole.getName()) ? "User" : receiverUserRole.getName(); + String receiverEmail = receiverUserRole.getUsername(); + CommentThread.CommentThreadState resolvedState = commentThread.getResolvedState(); + Map templateParams = new HashMap<>(); + templateParams.put("App_User_Name", receiverName); + templateParams.put("Commenter_Name", resolvedState.getAuthorName()); + templateParams.put("Application_Name", application.getName()); + templateParams.put("Organization_Name", organization.getName()); + templateParams.put("inviteUrl", originHeader); + + String emailSubject = String.format( + "%s has resolved comment in %s", resolvedState.getAuthorName(), application.getName() + ); + return emailSender.sendMail(receiverEmail, emailSubject, THREAD_RESOLVED_EMAIL_TEMPLATE, templateParams); + } + + private Mono getEmailSenderMono(UserRole receiverUserRole, Comment comment, String originHeader, + Application application, Organization organization) { + String receiverName = StringUtils.isEmpty(receiverUserRole.getName()) ? "User" : receiverUserRole.getName(); + String receiverEmail = receiverUserRole.getUsername(); + + Map templateParams = new HashMap<>(); + templateParams.put("App_User_Name", receiverName); + templateParams.put("Commenter_Name", comment.getAuthorName()); + templateParams.put("Application_Name", application.getName()); + templateParams.put("Organization_Name", organization.getName()); + templateParams.put("Comment_Body", CommentUtils.getCommentBody(comment)); + templateParams.put("inviteUrl", originHeader); + + String emailTemplate = COMMENT_ADDED_EMAIL_TEMPLATE; + String emailSubject = String.format( + "New comment from %s in %s", comment.getAuthorName(), application.getName() + ); + + // check if user has been mentioned in the comment + if(CommentUtils.isUserMentioned(comment, receiverEmail)) { + emailTemplate = USER_MENTIONED_EMAIL_TEMPLATE; + emailSubject = String.format("New comment for you from %s", comment.getAuthorName()); + } + return emailSender.sendMail(receiverEmail, emailSubject, emailTemplate, templateParams); + } + + private Mono sendEmailForComment(String authorUserName, Application application, Organization organization, + E commentDomain, String originHeader) { + +// for (UserRole userRole : organization.getUserRoles()) { +// if (!authorUserName.equals(userRole.getUsername())) { +// if (commentDomain instanceof Comment) { +// Comment comment = (Comment) commentDomain; +// getEmailSenderMono(userRole, comment, originHeader, application, organization).block(); +// } else if (commentDomain instanceof CommentThread) { +// CommentThread commentThread = (CommentThread) commentDomain; +// getEmailSenderMono(userRole, commentThread, originHeader, application, organization).block(); +// } +// } +// } + + List> emailMonos = new ArrayList<>(); + for (UserRole userRole : organization.getUserRoles()) { + if(!authorUserName.equals(userRole.getUsername())) { + if(commentDomain instanceof Comment) { + Comment comment = (Comment)commentDomain; + emailMonos.add( + getEmailSenderMono(userRole, comment, originHeader, application, organization) + ); + } else if(commentDomain instanceof CommentThread) { + CommentThread commentThread = (CommentThread) commentDomain; + emailMonos.add( + getEmailSenderMono(userRole, commentThread, originHeader, application, organization) + ); + } + } + } + return Flux.concat(emailMonos).then(Mono.just(Boolean.TRUE)); + } +} diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java index e248b39457..18e84acd76 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java @@ -43,8 +43,8 @@ public class NotificationServiceImplTest { public void setUp() { notificationService = new NotificationServiceImpl( scheduler, validator, mongoConverter, reactiveMongoTemplate, notificationRepository, analyticsService, - sessionUserService, emailSender, - organizationRepository, applicationRepository); + sessionUserService + ); } @Test @@ -64,11 +64,5 @@ public class NotificationServiceImplTest { Comment comment = new Comment(); comment.setAuthorUsername(fromUserEmail); - -// StepVerifier.create(notificationService.sendEmailForComment(emailReceiver, comment, originHeader)) -// .assertNext(t -> { -// assertEquals(Boolean.TRUE, t); -// }) -// .verifyComplete(); } } \ No newline at end of file From 280155ac898ecc2de61a6dee85352cdf04f9cf34 Mon Sep 17 00:00:00 2001 From: Nayan Date: Tue, 8 Jun 2021 14:38:41 +0600 Subject: [PATCH 4/7] -added test for the email event handler --- .../server/domains/CommentThread.java | 2 + .../server/events/UserChangedEvent.java | 1 - .../server/solutions/EmailEventHandler.java | 12 -- .../server/helpers/CommentUtilsTest.java | 2 +- .../services/NotificationServiceImplTest.java | 68 ------ .../services/OrganizationServiceUnitTest.java | 5 - .../solutions/EmailEventHandlerTest.java | 194 ++++++++++++++++++ 7 files changed, 197 insertions(+), 87 deletions(-) delete mode 100644 app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java create mode 100644 app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java index 26deb328aa..595bd7eea9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java @@ -4,6 +4,8 @@ import com.appsmith.external.models.BaseDomain; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; import org.springframework.data.annotation.Transient; import org.springframework.data.mongodb.core.mapping.Document; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java index ea6c2a0bec..b3af1c582c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java @@ -5,7 +5,6 @@ import lombok.Data; import lombok.RequiredArgsConstructor; @Data -@RequiredArgsConstructor public class UserChangedEvent { private final User user; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java index f5214c132f..bacb4ea9e4 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java @@ -127,18 +127,6 @@ public class EmailEventHandler { private Mono sendEmailForComment(String authorUserName, Application application, Organization organization, E commentDomain, String originHeader) { -// for (UserRole userRole : organization.getUserRoles()) { -// if (!authorUserName.equals(userRole.getUsername())) { -// if (commentDomain instanceof Comment) { -// Comment comment = (Comment) commentDomain; -// getEmailSenderMono(userRole, comment, originHeader, application, organization).block(); -// } else if (commentDomain instanceof CommentThread) { -// CommentThread commentThread = (CommentThread) commentDomain; -// getEmailSenderMono(userRole, commentThread, originHeader, application, organization).block(); -// } -// } -// } - List> emailMonos = new ArrayList<>(); for (UserRole userRole : organization.getUserRoles()) { if(!authorUserName.equals(userRole.getUsername())) { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java index 0b8312577b..0ccc8f0cff 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java @@ -77,7 +77,7 @@ class CommentUtilsTest { @Test public void isUserMentioned_WhenSomeoneIsMentioned_ReturnsCorrectValue() { - Map entityMap = this.createEntityMapForUsers( + Map entityMap = createEntityMapForUsers( List.of("1", "2", "3") ); Comment.Body body = new Comment.Body(); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java deleted file mode 100644 index 18e84acd76..0000000000 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NotificationServiceImplTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.appsmith.server.services; - -import com.appsmith.server.domains.Comment; -import com.appsmith.server.notifications.EmailSender; -import com.appsmith.server.repositories.ApplicationRepository; -import com.appsmith.server.repositories.NotificationRepository; -import com.appsmith.server.repositories.OrganizationRepository; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Scheduler; - -import javax.validation.Validator; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@RunWith(SpringJUnit4ClassRunner.class) -public class NotificationServiceImplTest { - private static final String COMMENT_ADDED_EMAIL_TEMPLATE = "email/commentAddedTemplate.html"; - - @MockBean SessionUserService sessionUserService; - @MockBean Scheduler scheduler; - @MockBean MongoConverter mongoConverter; - @MockBean ReactiveMongoTemplate reactiveMongoTemplate; - @MockBean NotificationRepository notificationRepository; - @MockBean Validator validator; - @MockBean AnalyticsService analyticsService; - @MockBean EmailSender emailSender; - @MockBean OrganizationRepository organizationRepository; - @MockBean ApplicationRepository applicationRepository; - - private NotificationService notificationService; - - @Before - public void setUp() { - notificationService = new NotificationServiceImpl( - scheduler, validator, mongoConverter, reactiveMongoTemplate, notificationRepository, analyticsService, - sessionUserService - ); - } - - @Test - public void sendEmailForComment_WhenMailSenderReturnsTrue_ReturnsTrue() { - String fromUserEmail = "nayan@appsmith.com"; - String emailReceiver = "rafiqnayan@appsmith.com"; - String originHeader = "https://example.com"; - - Map emailTemplateParams = new HashMap<>(); - emailTemplateParams.put("Commenter_User_Name", fromUserEmail); - emailTemplateParams.put("inviteUrl", originHeader); - Mockito.when( - emailSender.sendMail( - emailReceiver, "New comment", COMMENT_ADDED_EMAIL_TEMPLATE, emailTemplateParams - ) - ).thenReturn(Mono.just(Boolean.TRUE)); - - Comment comment = new Comment(); - comment.setAuthorUsername(fromUserEmail); - } -} \ No newline at end of file diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/OrganizationServiceUnitTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/OrganizationServiceUnitTest.java index 452745efbf..076af9e999 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/OrganizationServiceUnitTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/OrganizationServiceUnitTest.java @@ -3,7 +3,6 @@ package com.appsmith.server.services; import com.appsmith.server.acl.RoleGraph; import com.appsmith.server.constants.FieldName; import com.appsmith.server.domains.Organization; -import com.appsmith.server.domains.User; import com.appsmith.server.domains.UserRole; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.repositories.AssetRepository; @@ -15,18 +14,15 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import reactor.test.StepVerifier; import javax.validation.Validator; -import java.util.ArrayList; import java.util.List; import static com.appsmith.server.acl.AclPermission.ORGANIZATION_INVITE_USERS; @@ -56,7 +52,6 @@ public class OrganizationServiceUnitTest { organizationRepository, analyticsService, pluginRepository, sessionUserService, userOrganizationService, userRepository, roleGraph, assetRepository, assetService ); - MockitoAnnotations.initMocks(this); } @Test diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java new file mode 100644 index 0000000000..9d86d70122 --- /dev/null +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java @@ -0,0 +1,194 @@ +package com.appsmith.server.solutions; + +import com.appsmith.server.domains.*; +import com.appsmith.server.events.CommentAddedEvent; +import com.appsmith.server.events.CommentThreadClosedEvent; +import com.appsmith.server.notifications.EmailSender; +import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.repositories.OrganizationRepository; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.eq; + +@RunWith(SpringJUnit4ClassRunner.class) +public class EmailEventHandlerTest { + + private static final String COMMENT_ADDED_EMAIL_TEMPLATE = "email/commentAddedTemplate.html"; + private static final String USER_MENTIONED_EMAIL_TEMPLATE = "email/userTaggedInCommentTemplate.html"; + private static final String THREAD_RESOLVED_EMAIL_TEMPLATE = "email/commentResolvedTemplate.html"; + + @MockBean + private ApplicationEventPublisher applicationEventPublisher; + @MockBean + private EmailSender emailSender; + @MockBean + private OrganizationRepository organizationRepository; + @MockBean + private ApplicationRepository applicationRepository; + + EmailEventHandler emailEventHandler; + private Application application; + private Organization organization; + + String authorUserName = "abc"; + String originHeader = "efg"; + String applicationId = "application-id"; + String organizationId = "organization-id"; + String emailReceiverUsername = "email-receiver"; + + @Before + public void setUp() { + emailEventHandler = new EmailEventHandler( + applicationEventPublisher, emailSender, organizationRepository, applicationRepository + ); + application = new Application(); + application.setName("Test application for comment"); + application.setOrganizationId(organizationId); + organization = new Organization(); + + // add a role with email receiver username + UserRole userRole = new UserRole(); + userRole.setUsername(emailReceiverUsername); + organization.setUserRoles(List.of(userRole)); + + Mockito.when(applicationRepository.findById(applicationId)).thenReturn(Mono.just(application)); + Mockito.when(organizationRepository.findById(organizationId)).thenReturn(Mono.just(organization)); + } + + @Test + public void publish_WhenValidCommentProvided_ReturnsTrue() { + Comment comment = new Comment(); + CommentAddedEvent commentAddedEvent = new CommentAddedEvent( + authorUserName, organization, application, originHeader, comment + ); + + Mockito.doNothing().when(applicationEventPublisher).publishEvent(commentAddedEvent); + + Mono booleanMono = emailEventHandler.publish(authorUserName, applicationId, comment, originHeader); + StepVerifier.create(booleanMono).assertNext(aBoolean -> { + Assert.assertEquals(Boolean.TRUE, aBoolean); + }).verifyComplete(); + } + + @Test + public void publish_WhenValidCommentThreadProvided_ReturnsTrue() { + CommentThread commentThread = new CommentThread(); + CommentThreadClosedEvent commentThreadClosedEvent = new CommentThreadClosedEvent( + authorUserName, organization, application, originHeader, commentThread + ); + Mockito.doNothing().when(applicationEventPublisher).publishEvent(commentThreadClosedEvent); + + Mono booleanMono = emailEventHandler.publish(authorUserName, applicationId, commentThread, originHeader); + StepVerifier.create(booleanMono).assertNext(aBoolean -> { + Assert.assertEquals(Boolean.TRUE, aBoolean); + }).verifyComplete(); + } + + @Test + public void handle_WhenValidCommentAddedEvent_ReturnsTrue() { + Comment sampleComment = new Comment(); + sampleComment.setAuthorUsername(authorUserName); + sampleComment.setAuthorName("Test Author"); + + // send the event + CommentAddedEvent commentAddedEvent = new CommentAddedEvent( + authorUserName, organization, application, originHeader, sampleComment + ); + emailEventHandler.handle(commentAddedEvent); + + String expectedEmailSubject = String.format( + "New comment from %s in %s", sampleComment.getAuthorName(), application.getName() + ); + // check email sender was called with expected template and subject + Mockito.verify(emailSender, Mockito.times(1)).sendMail( + eq(emailReceiverUsername), eq(expectedEmailSubject), eq(COMMENT_ADDED_EMAIL_TEMPLATE), Mockito.anyMap() + ); + } + + private Map createEntityMapForUsers(List mentionedUserNames) { + Map entityMap = new HashMap<>(); + for (String username: mentionedUserNames) { + Comment.EntityData.EntityUser entityUser = new Comment.EntityData.EntityUser(); + entityUser.setUsername(username); + Comment.EntityData.Mention mention = new Comment.EntityData.Mention(); + mention.setUser(entityUser); + + Comment.EntityData entityData = new Comment.EntityData(); + entityData.setMention(mention); + + Comment.Entity entity = new Comment.Entity(); + entity.setType("mention"); + entity.setData(entityData); + entityMap.put(username, entity); + } + return entityMap; + } + + @Test + public void handle_WhenUserMentionedEvent_ReturnsTrue() { + Comment sampleComment = new Comment(); + sampleComment.setAuthorUsername(authorUserName); + sampleComment.setAuthorName("Test Author"); + + // mention the emailReceiverUsername in the sample comment + Map entityMap = createEntityMapForUsers(List.of(emailReceiverUsername)); + Comment.Body body = new Comment.Body(); + body.setEntityMap(entityMap); + sampleComment.setBody(body); + + // send the event + CommentAddedEvent commentAddedEvent = new CommentAddedEvent( + authorUserName, organization, application, originHeader, sampleComment + ); + emailEventHandler.handle(commentAddedEvent); + + // check if expectation meets + String expectedEmailSubject = String.format("New comment for you from %s", sampleComment.getAuthorName()); + + // check email sender was called with expected template and subject + Mockito.verify(emailSender, Mockito.times(1)).sendMail( + eq(emailReceiverUsername), eq(expectedEmailSubject), eq(USER_MENTIONED_EMAIL_TEMPLATE), Mockito.anyMap() + ); + } + + @Test + public void handle_WhenThreadClosed_ReturnsTrue() { + // add comment thread with a resolved state where resolver is `authorUserName` + String resolverName = "Test Author"; + CommentThread.CommentThreadState resolveState = new CommentThread.CommentThreadState(); + resolveState.setAuthorUsername(authorUserName); + resolveState.setAuthorName(resolverName); + resolveState.setActive(true); + + CommentThread commentThread = new CommentThread(); + commentThread.setResolvedState(resolveState); + + // send the event + CommentThreadClosedEvent commentAddedEvent = new CommentThreadClosedEvent( + authorUserName, organization, application, originHeader, commentThread + ); + emailEventHandler.handle(commentAddedEvent); + + // check if expectation meets + String expectedEmailSubject = String.format( + "%s has resolved comment in %s", resolveState.getAuthorName(), application.getName() + ); + // check email sender was called with expected template and subject + Mockito.verify(emailSender, Mockito.times(1)).sendMail( + eq(emailReceiverUsername), eq(expectedEmailSubject), eq(THREAD_RESOLVED_EMAIL_TEMPLATE), Mockito.anyMap() + ); + } +} \ No newline at end of file From ba4ea3a61be91b3fc54530f0779f70c0ac16522d Mon Sep 17 00:00:00 2001 From: Nayan Date: Tue, 8 Jun 2021 15:34:10 +0600 Subject: [PATCH 5/7] -fixed issue in unit test --- .../main/java/com/appsmith/server/helpers/CommentUtils.java | 4 ++-- .../java/com/appsmith/server/helpers/CommentUtilsTest.java | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java index e62bf832f9..50230fb998 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommentUtils.java @@ -1,7 +1,6 @@ package com.appsmith.server.helpers; import com.appsmith.server.domains.Comment; -import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; @@ -17,7 +16,8 @@ public class CommentUtils { if(comment.getBody() != null && comment.getBody().getEntityMap() != null) { for(String key : comment.getBody().getEntityMap().keySet()) { Comment.Entity commentEntity = comment.getBody().getEntityMap().get(key); - if(commentEntity.getType().equals("mention")) { + if(commentEntity != null && commentEntity.getType() != null + && commentEntity.getType().equals("mention")) { // this comment has a mention, check the provided user is mentioned or not if(commentEntity.getData() != null) { Comment.EntityData.Mention mention = commentEntity.getData().getMention(); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java index 0ccc8f0cff..4c549c3c79 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/CommentUtilsTest.java @@ -8,8 +8,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.junit.jupiter.api.Assertions.*; - class CommentUtilsTest { @Test void getCommentBody_WhenBodyIsNull_ReturnsEmptyList() { From b3bde4eb9560acd586c9a97d6bd43cd588795d06 Mon Sep 17 00:00:00 2001 From: Nayan Date: Tue, 8 Jun 2021 15:52:25 +0600 Subject: [PATCH 6/7] -organized imports --- .../com/appsmith/server/domains/CommentThread.java | 2 -- .../com/appsmith/server/events/UserChangedEvent.java | 1 - .../server/services/NotificationServiceImpl.java | 10 +--------- .../appsmith/server/solutions/EmailEventHandler.java | 6 +++++- .../server/solutions/EmailEventHandlerTest.java | 6 +++++- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java index 595bd7eea9..26deb328aa 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/CommentThread.java @@ -4,8 +4,6 @@ import com.appsmith.external.models.BaseDomain; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; import org.springframework.data.annotation.Transient; import org.springframework.data.mongodb.core.mapping.Document; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java index b3af1c582c..af2d27d937 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/events/UserChangedEvent.java @@ -2,7 +2,6 @@ package com.appsmith.server.events; import com.appsmith.server.domains.User; import lombok.Data; -import lombok.RequiredArgsConstructor; @Data public class UserChangedEvent { diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java index 2d091a84ce..0b88af94c5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationServiceImpl.java @@ -1,11 +1,7 @@ package com.appsmith.server.services; -import com.appsmith.server.domains.*; -import com.appsmith.server.helpers.CommentUtils; -import com.appsmith.server.notifications.EmailSender; -import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.domains.Notification; import com.appsmith.server.repositories.NotificationRepository; -import com.appsmith.server.repositories.OrganizationRepository; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; @@ -17,10 +13,6 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import javax.validation.Validator; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; @Slf4j @Service diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java index bacb4ea9e4..a3be8b56a8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/EmailEventHandler.java @@ -1,6 +1,10 @@ package com.appsmith.server.solutions; -import com.appsmith.server.domains.*; +import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.Comment; +import com.appsmith.server.domains.CommentThread; +import com.appsmith.server.domains.Organization; +import com.appsmith.server.domains.UserRole; import com.appsmith.server.events.CommentAddedEvent; import com.appsmith.server.events.CommentThreadClosedEvent; import com.appsmith.server.helpers.CommentUtils; diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java index 9d86d70122..bdb0e0eea2 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java @@ -1,6 +1,10 @@ package com.appsmith.server.solutions; -import com.appsmith.server.domains.*; +import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.Comment; +import com.appsmith.server.domains.CommentThread; +import com.appsmith.server.domains.Organization; +import com.appsmith.server.domains.UserRole; import com.appsmith.server.events.CommentAddedEvent; import com.appsmith.server.events.CommentThreadClosedEvent; import com.appsmith.server.notifications.EmailSender; From 87ab6ff92630261066082b46a434412d1fbab1bb Mon Sep 17 00:00:00 2001 From: Nayan Date: Thu, 10 Jun 2021 13:09:36 +0600 Subject: [PATCH 7/7] -removed commented code as per PR review --- .../com/appsmith/server/controllers/CommentController.java | 5 +++-- .../com/appsmith/server/services/NotificationService.java | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java index bd7cd7f896..9c57753dae 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/CommentController.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; @@ -48,9 +49,9 @@ public class CommentController extends BaseController> createThread(@Valid @RequestBody CommentThread resource, - ServerWebExchange exchange) { + @RequestHeader(name = "Origin") String originHeader) { log.debug("Going to create resource {}", resource.getClass().getName()); - return service.createThread(resource, exchange.getRequest().getHeaders().getOrigin()) + return service.createThread(resource, originHeader) .map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null)); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java index 4b9d19ede6..8c4d9cb739 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NotificationService.java @@ -9,6 +9,5 @@ import reactor.core.publisher.Mono; import java.util.List; public interface NotificationService extends CrudService { -// Mono sendEmailForComment(Comment comment, String originHeader, String applicationId); -// Mono sendEmailForComment(String authorUserName, String applicationId, E commentDomain, String originHeader); + }