Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple notification channels in middleware #185

Merged
merged 20 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4321270
Support multiple notification channels.
May 17, 2023
0644054
Handle "none" value in notification channel.
JymDyerIBI May 17, 2023
2715c00
Push notifications.
JymDyerIBI Jun 8, 2023
c3a96c7
Add pushDevices (number of push devices last we looked)
JymDyerIBI Aug 10, 2023
5f28501
Add pushDevices (number of push devices) to test
JymDyerIBI Aug 10, 2023
eb2d207
Add sendPush() method for push notifications.
JymDyerIBI Aug 10, 2023
722143a
Rely on PUSH_API_URL as config property for entire push API URL.
JymDyerIBI Aug 10, 2023
a109dc6
Add canSendPushNotification() test.
JymDyerIBI Aug 10, 2023
24e825e
Generated with VersionControlledSwaggerUpdater
JymDyerIBI Aug 11, 2023
1fa77f3
Update with PUSH_API_* properties.
JymDyerIBI Aug 11, 2023
13893dd
Generated with ReadMeEnvSchemaValuesUpdater
JymDyerIBI Aug 17, 2023
3503ee2
Space formatting.
JymDyerIBI Aug 17, 2023
d6c484f
Add in `get` endpoint to maintain number of push notification devices.
JymDyerIBI Aug 21, 2023
759d21a
Persistence of pushDevices
JymDyerIBI Aug 23, 2023
ce21e89
Persistence of pushDevices
JymDyerIBI Aug 23, 2023
a3e08ab
Move NotificationUtils#getPushInfo() call into CheckMonitoredTrip class.
JymDyerIBI Aug 24, 2023
fb0df00
Update number of push devices sooner (per binh's PR comment).
JymDyerIBI Aug 28, 2023
be241ed
Actually, put it very near the very top, after getting `otpUser`.
JymDyerIBI Aug 28, 2023
38660e6
Make public static methods contingent on setting PUSH_API_KEY/PUSH_AP…
JymDyerIBI Aug 31, 2023
17ba7fb
Make public static methods contingent on setting PUSH_API_KEY/PUSH_AP…
JymDyerIBI Aug 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public VerificationResult sendVerificationText(Request req, Response res) {
if (verification.getStatus().equals("pending")) {
otpUser.phoneNumber = phoneNumber;
otpUser.isPhoneNumberVerified = false;
otpUser.notificationChannel = "sms";
otpUser.notificationChannel.add(OtpUser.Notification.SMS);
Persistence.otpUsers.replace(otpUser.id, otpUser);
}

Expand Down
49 changes: 43 additions & 6 deletions src/main/java/org/opentripplanner/middleware/models/OtpUser.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package org.opentripplanner.middleware.models;

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSetter;
import org.opentripplanner.middleware.auth.Auth0Users;
import org.opentripplanner.middleware.auth.RequestingUser;
import org.opentripplanner.middleware.persistence.Persistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* This represents a user of an OpenTripPlanner instance (typically of the standard OTP UI/otp-react-redux).
* otp-middleware stores these users and associated information (e.g., home/work locations and other favorites). Users
* can also opt-in to storing their trip planning requests/responses.
*/
public class OtpUser extends AbstractUser {
public enum Notification {
EMAIL, PUSH, SMS
}

public static final String AUTH0_SCOPE = "otp-user";
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(OtpUser.class);
Expand All @@ -30,11 +37,10 @@ public class OtpUser extends AbstractUser {
public boolean isPhoneNumberVerified;
JymDyerIBI marked this conversation as resolved.
Show resolved Hide resolved

/**
* Notification preference for this user
* ("email", "sms", or "none").
* TODO: Convert to enum. See http://mongodb.github.io/mongo-java-driver/3.7/bson/pojos/ for guidance.
* Notification preferences for this user
* (EMAIL and/or SMS and/or PUSH).
*/
public String notificationChannel;
public EnumSet<OtpUser.Notification> notificationChannel = EnumSet.noneOf(OtpUser.Notification.class);

/**
* Verified phone number for SMS notifications, in +15551234 format (E.164 format, includes country code, no spaces).
Expand Down Expand Up @@ -105,4 +111,35 @@ public boolean canBeManagedBy(RequestingUser requestingUser) {
return super.canBeManagedBy(requestingUser);
}

/**
* Get notification channels as comma-separated list in one string
*/
@JsonGetter(value = "notificationChannel")
public String getNotificationChannel() {
return notificationChannel.stream()
.map(channel -> channel.name().toLowerCase())
.collect(Collectors.joining(","));
}

/**
* Set notification channels based on comma-separated list in one string
*/
@JsonSetter(value = "notificationChannel")
public void setNotificationChannel(String channels) {
if (channels.isEmpty() || "none".equals(channels)) {
notificationChannel.clear();
} else {
Stream.of(channels.split(","))
.filter(Objects::nonNull)
.map(str -> str.trim().toUpperCase())
.filter(str -> !str.isEmpty())
.forEach(channel -> {
try {
notificationChannel.add(Enum.valueOf(OtpUser.Notification.class, channel));
} catch (Exception e) {
LOG.error("Notification channel \"{}\" is not valid", channel);
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -403,23 +403,18 @@ private void sendNotifications() {
);
// FIXME: Change log level
LOG.info("Sending notification to user {}", trip.userId);
boolean success = false;
// FIXME: This needs to be an enum.
switch (otpUser.notificationChannel.toLowerCase()) {
case "sms":
success = sendSMS(otpUser, templateData);
break;
case "email":
success = sendEmail(otpUser, templateData);
break;
case "all":
// TODO better handle below when one of the following fails
success = sendSMS(otpUser, templateData) && sendEmail(otpUser, templateData);
break;
default:
break;
boolean successEmail = false;
boolean successSms = false;

JymDyerIBI marked this conversation as resolved.
Show resolved Hide resolved
if (otpUser.notificationChannel.contains(OtpUser.Notification.EMAIL)) {
successEmail = sendEmail(otpUser, templateData);
}
if (otpUser.notificationChannel.contains(OtpUser.Notification.SMS)) {
successSms = sendSMS(otpUser, templateData);
}
if (success) {

// TODO: better handle below when one of the following fails
if (successEmail || successSms) {
notificationTimestampMillis = DateTimeUtils.currentTimeMillis();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static OtpUser createUser(String email, String phoneNumber) {
OtpUser user = new OtpUser();
user.email = email;
user.phoneNumber = phoneNumber;
user.notificationChannel = "email";
user.notificationChannel.add(OtpUser.Notification.EMAIL);
user.hasConsentedToTerms = true;
user.storeTripHistory = true;
Persistence.otpUsers.create(user);
Expand Down