Skip to content

Commit

Permalink
Merge pull request #474 from RADAR-base/notifications-via-email
Browse files Browse the repository at this point in the history
Send optional notifications via email
  • Loading branch information
yatharthranjan committed Jul 9, 2024
2 parents 00ad614 + 2343690 commit 04e7edd
Show file tree
Hide file tree
Showing 36 changed files with 777 additions and 359 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ dependencies {
implementation('org.springframework.boot:spring-boot-starter-quartz')
implementation('org.springframework.boot:spring-boot-starter-security')
implementation('org.springframework.boot:spring-boot-starter-actuator')
implementation('org.springframework.boot:spring-boot-starter-mail')
implementation group: "org.springframework.security", name: "spring-security-config", version: springSecurityVersion
implementation('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:' + springBootVersion)
implementation('org.springframework.security.oauth:spring-security-oauth2:' + springOauth2Version)
Expand Down Expand Up @@ -206,3 +207,7 @@ tasks.named("dependencyUpdates").configure {
isNonStable(it.candidate.version)
}
}

pmd {
sourceSets = [sourceSets.main]
}
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
rootProject.name = 'appserver'
rootProject.name = 'appserver'
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public Notification dtoToEntity(FcmNotificationDto notificationDto) {
.sound(notificationDto.getSound())
.subtitle(notificationDto.getSubtitle())
.tag(notificationDto.getTag())
.emailEnabled(notificationDto.isEmailEnabled())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public User dtoToEntity(FcmUserDto fcmUserDto) {
return new User()
.setFcmToken(fcmUserDto.getFcmToken())
.setSubjectId(fcmUserDto.getSubjectId())
.setEmailAddress(fcmUserDto.getEmailAddress())
.setUserMetrics(getValidUserMetrics(fcmUserDto))
.setEnrolmentDate(fcmUserDto.getEnrolmentDate())
.setTimezone(fcmUserDto.getTimezone())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public class FcmNotificationDto implements Serializable {

private String clickAction;

private boolean emailEnabled;

private boolean mutableContent;

@DateTimeFormat(iso = ISO.DATE_TIME)
Expand Down Expand Up @@ -152,6 +154,7 @@ public FcmNotificationDto(Notification notificationEntity) {
this.androidChannelId = notificationEntity.getAndroidChannelId();
this.tag = notificationEntity.getTag();
this.clickAction = notificationEntity.getClickAction();
this.emailEnabled = notificationEntity.isEmailEnabled();
this.mutableContent = notificationEntity.isMutableContent();
}

Expand Down Expand Up @@ -300,6 +303,11 @@ public FcmNotificationDto setClickAction(String clickAction) {
return this;
}

public FcmNotificationDto setEmailEnabled(boolean emailEnabled) {
this.emailEnabled = emailEnabled;
return this;
}

public FcmNotificationDto setMutableContent(boolean mutableContent) {
this.mutableContent = mutableContent;
return this;
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/radarbase/appserver/dto/fcm/FcmUserDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import java.io.Serializable;
import java.time.Instant;
import java.util.Map;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
Expand Down Expand Up @@ -56,6 +58,10 @@ public class FcmUserDto implements Serializable {
// User ID to be used in org.radarcns.kafka.ObservationKey record keys
private String subjectId;

@Email
// Email address of the user (optional, needed when sending notifications via email)
private String emailAddress;

// The most recent time when the app was opened
private Instant lastOpened;

Expand Down Expand Up @@ -129,6 +135,11 @@ public FcmUserDto setSubjectId(String subjectId) {
return this;
}

public FcmUserDto setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
return this;
}

public FcmUserDto setLastOpened(Instant lastOpened) {
this.lastOpened = lastOpened;
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.radarbase.appserver.dto.protocol;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* @author yatharthranjan
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class EmailNotificationProtocol {

@JsonProperty("enabled")
private boolean enabled = false;

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ public class NotificationProtocol {

@JsonProperty("text")
private LanguageText body;

@JsonProperty("email")
private EmailNotificationProtocol email;
}

13 changes: 12 additions & 1 deletion src/main/java/org/radarbase/appserver/entity/Notification.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
*
* @author yatharthranjan
* @see Scheduled
* @see org.radarbase.appserver.service.scheduler.NotificationSchedulerService
* @see org.radarbase.appserver.service.scheduler.MessageSchedulerService
*/
@Table(
name = "notifications",
Expand Down Expand Up @@ -120,6 +120,9 @@ public class Notification extends Message {
@Column(name = "click_action")
private String clickAction;

@Column(name = "email_enabled")
private boolean emailEnabled = false;

@Nullable
@ElementCollection(fetch = FetchType.EAGER)
@MapKeyColumn(name = "additional_key", nullable = true)
Expand Down Expand Up @@ -158,6 +161,7 @@ public static class NotificationBuilder {
transient String androidChannelId;
transient String tag;
transient String clickAction;
transient boolean emailEnabled;
transient Map<String, String> additionalData;
transient Task task;

Expand Down Expand Up @@ -193,6 +197,7 @@ public NotificationBuilder(Notification notification) {
this.androidChannelId = notification.getAndroidChannelId();
this.tag = notification.getTag();
this.clickAction = notification.getClickAction();
this.emailEnabled = notification.isEmailEnabled();
this.additionalData = notification.getAdditionalData();
this.task = notification.getTask();
}
Expand Down Expand Up @@ -343,6 +348,11 @@ public NotificationBuilder clickAction(String clickAction) {
return this;
}

public NotificationBuilder emailEnabled(boolean emailEnabled) {
this.emailEnabled = emailEnabled;
return this;
}

public NotificationBuilder additionalData(Map<String, String> additionalData) {
this.additionalData = additionalData;
return this;
Expand Down Expand Up @@ -386,6 +396,7 @@ public Notification build() {
notification.setAndroidChannelId(this.androidChannelId);
notification.setTag(this.tag);
notification.setClickAction(this.clickAction);
notification.setEmailEnabled(this.emailEnabled);
notification.setAdditionalData(this.additionalData);
notification.setTask(this.task);

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/radarbase/appserver/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public class User extends AuditModel implements Serializable {
@Column(name = "subject_id", nullable = false)
private String subjectId;

@Column(name = "email")
private String emailAddress;

@NotNull
@Column(name = "fcm_token", nullable = false, unique = true)
private String fcmToken;
Expand Down Expand Up @@ -118,6 +121,11 @@ public User setSubjectId(String subjectId) {
return this;
}

public User setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
return this;
}

public User setFcmToken(String fcmToken) {
this.fcmToken = fcmToken;
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.radarbase.appserver.exception;

public class EmailMessageTransmitException extends MessageTransmitException {

private static final long serialVersionUID = -1927189245766939L;

public EmailMessageTransmitException(String message) {
super(message);
}
public EmailMessageTransmitException(String message, Throwable e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.radarbase.appserver.exception;

public class FcmMessageTransmitException extends MessageTransmitException {

private static final long serialVersionUID = -923871442166939L;

public FcmMessageTransmitException(String message) {
super(message);
}
public FcmMessageTransmitException(String message, Throwable e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.radarbase.appserver.exception;

public class MessageTransmitException extends Exception {
private static final long serialVersionUID = -281834508766939L;

public MessageTransmitException(String message) {
super(message);
}
public MessageTransmitException(String message, Throwable e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@

package org.radarbase.appserver.service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.radarbase.appserver.converter.DataMessageConverter;
import org.radarbase.appserver.dto.fcm.FcmDataMessageDto;
import org.radarbase.appserver.dto.fcm.FcmDataMessages;
Expand All @@ -43,12 +36,20 @@
import org.radarbase.appserver.repository.DataMessageRepository;
import org.radarbase.appserver.repository.ProjectRepository;
import org.radarbase.appserver.repository.UserRepository;
import org.radarbase.appserver.service.scheduler.DataMessageSchedulerService;
import org.radarbase.appserver.service.scheduler.MessageSchedulerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
* {@link Service} for interacting with the {@link DataMessage} {@link jakarta.persistence.Entity}
* using the {@link DataMessageRepository}.
Expand All @@ -67,7 +68,7 @@ public class FcmDataMessageService implements DataMessageService {
private final transient DataMessageRepository dataMessageRepository;
private final transient UserRepository userRepository;
private final transient ProjectRepository projectRepository;
private final transient DataMessageSchedulerService schedulerService;
private final transient MessageSchedulerService schedulerService;
private final transient DataMessageConverter dataMessageConverter;
private final transient ApplicationEventPublisher dataMessageStateEventPublisher;

Expand All @@ -76,7 +77,7 @@ public FcmDataMessageService(
DataMessageRepository dataMessageRepository,
UserRepository userRepository,
ProjectRepository projectRepository,
DataMessageSchedulerService schedulerService,
MessageSchedulerService schedulerService,
DataMessageConverter dataMessageConverter,
ApplicationEventPublisher eventPublisher) {
this.dataMessageRepository = dataMessageRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import org.radarbase.appserver.repository.NotificationRepository;
import org.radarbase.appserver.repository.ProjectRepository;
import org.radarbase.appserver.repository.UserRepository;
import org.radarbase.appserver.service.scheduler.NotificationSchedulerService;
import org.radarbase.appserver.service.scheduler.MessageSchedulerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
Expand All @@ -66,7 +66,7 @@ public class FcmNotificationService implements NotificationService {
private final transient NotificationRepository notificationRepository;
private final transient UserRepository userRepository;
private final transient ProjectRepository projectRepository;
private final transient NotificationSchedulerService schedulerService;
private final transient MessageSchedulerService schedulerService;
private final transient NotificationConverter notificationConverter;
private final transient ApplicationEventPublisher notificationStateEventPublisher;

Expand All @@ -75,7 +75,7 @@ public FcmNotificationService(
NotificationRepository notificationRepository,
UserRepository userRepository,
ProjectRepository projectRepository,
NotificationSchedulerService schedulerService,
MessageSchedulerService schedulerService,
NotificationConverter notificationConverter,
ApplicationEventPublisher eventPublisher) {
this.notificationRepository = notificationRepository;
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/radarbase/appserver/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.radarbase.appserver.repository.ProjectRepository;
import org.radarbase.appserver.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -50,6 +51,9 @@
@Slf4j
public class UserService {

@Value("${radar.notification.email.enabled:false}")
private transient boolean sendEmailNotifications;

private final transient UserConverter userConverter;
private final transient UserRepository userRepository;
private final transient ProjectRepository projectRepository;
Expand Down Expand Up @@ -152,6 +156,12 @@ public FcmUserDto saveUserInProject(FcmUserDto userDto) {
+ ". Please use Update endpoint if need to update the user");
}

if (sendEmailNotifications && (userDto.getEmailAddress() == null || userDto.getEmailAddress().isEmpty())) {
log.warn("No email address was provided for new subject '{}'. The option to send notifications via email " +
"('radar.notification.email.enabled') will not work for this subject. Consider to provide a valid email " +
"address for subject '{}'.", userDto.getSubjectId());
}

User newUser = userConverter.dtoToEntity(userDto).setProject(project);
// maintain a bi-directional relationship
newUser.getUsermetrics().setUser(newUser);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
public class TaskNotificationGeneratorService {
private transient int SECONDS_TO_MILLIS = 1000;

public Notification createNotification(Task task, Instant notificationTimestamp, String title, String body) {
public Notification createNotification(Task task, Instant notificationTimestamp,
String title, String body, boolean emailEnabled) {

Notification.NotificationBuilder current = new Notification.NotificationBuilder();
current.scheduledTime(notificationTimestamp);
Expand All @@ -22,6 +23,7 @@ public Notification createNotification(Task task, Instant notificationTimestamp,
current.task(task);
current.title(title);
current.body(body);
current.emailEnabled(emailEnabled);

return current.build();
}
Expand Down
Loading

0 comments on commit 04e7edd

Please sign in to comment.