diff --git a/src/main/java/org/opentripplanner/middleware/i18n/Message.java b/src/main/java/org/opentripplanner/middleware/i18n/Message.java index a590b1990..260c2fb09 100644 --- a/src/main/java/org/opentripplanner/middleware/i18n/Message.java +++ b/src/main/java/org/opentripplanner/middleware/i18n/Message.java @@ -19,10 +19,10 @@ public enum Message { ACCEPT_DEPENDENT_EMAIL_SUBJECT, ACCEPT_DEPENDENT_EMAIL_MANAGE, ACCEPT_DEPENDENT_ERROR, + ARRIVED_AND_MODE_CHANGE_NOTIFICATION, ARRIVED_NOTIFICATION, DEPARTED_NOTIFICATION, LABEL_AND_CONTENT, - MODE_CHANGE_NOTIFICATION, SMS_STOP_NOTIFICATIONS, TRIP_EMAIL_SUBJECT, TRIP_EMAIL_SUBJECT_FOR_USER, @@ -51,7 +51,6 @@ public enum Message { TRIP_INVITE_PRIMARY_TRAVELER, TRIP_INVITE_OBSERVER, TRIP_NAME_UNDEFINED, - TRIP_TRAVELER_GENERIC_NAME, TRIP_NOT_FOUND_NOTIFICATION, TRIP_NO_LONGER_POSSIBLE_NOTIFICATION, TRIP_REMINDER_NOTIFICATION, diff --git a/src/main/java/org/opentripplanner/middleware/models/LegTransitionNotification.java b/src/main/java/org/opentripplanner/middleware/models/LegTransitionNotification.java index d3efa746d..3593fa532 100644 --- a/src/main/java/org/opentripplanner/middleware/models/LegTransitionNotification.java +++ b/src/main/java/org/opentripplanner/middleware/models/LegTransitionNotification.java @@ -1,13 +1,16 @@ package org.opentripplanner.middleware.models; import org.opentripplanner.middleware.i18n.Message; +import org.opentripplanner.middleware.persistence.Persistence; import org.opentripplanner.middleware.tripmonitor.jobs.NotificationType; import org.opentripplanner.middleware.triptracker.TravelerPosition; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; import java.util.Locale; +import java.util.Set; + +import static com.mongodb.client.model.Filters.eq; public class LegTransitionNotification { @@ -34,28 +37,27 @@ public LegTransitionNotification( * Create {@link TripMonitorNotification} for leg transition based on notification type. */ @Nullable - public TripMonitorNotification createTripMonitorNotification(NotificationType notificationType) { + private TripMonitorNotification createTripMonitorNotification(NotificationType notificationType) { String body; switch (notificationType) { - case MODE_CHANGE_NOTIFICATION: + case ARRIVED_AND_MODE_CHANGE_NOTIFICATION: body = String.format( - Message.MODE_CHANGE_NOTIFICATION.get(travelerPosition.locale), - getTravelerName(), - travelerPosition.expectedLeg.mode, - travelerPosition.nextLeg.mode + Message.ARRIVED_AND_MODE_CHANGE_NOTIFICATION.get(observerLocale), + travelerName, + travelerPosition.expectedLeg.to.name ); break; case DEPARTED_NOTIFICATION: body = String.format( Message.DEPARTED_NOTIFICATION.get(observerLocale), - getTravelerName(), + travelerName, travelerPosition.expectedLeg.from.name ); break; case ARRIVED_NOTIFICATION: body = String.format( - Message.ARRIVED_NOTIFICATION.get(travelerPosition.locale), - getTravelerName(), + Message.ARRIVED_NOTIFICATION.get(observerLocale), + travelerName, travelerPosition.expectedLeg.to.name ); break; @@ -66,38 +68,23 @@ public TripMonitorNotification createTripMonitorNotification(NotificationType no } /** - * Get the traveler's name if available, if not provide a generic traveler name. + * Get a list of users that should be notified of a traveler's leg transition. */ - private String getTravelerName() { - if (travelerName != null) { - return travelerName; - } else { - return Message.TRIP_TRAVELER_GENERIC_NAME.get(observerLocale); + public static Set getLegTransitionNotifyUsers(MonitoredTrip trip) { + Set notifyUsers = new HashSet<>(); + + if (trip.ownedByPrimary() && trip.companion != null) { + notifyUsers.add(Persistence.otpUsers.getOneFiltered(eq("email", trip.companion.email))); + } else if (trip.ownedByCompanion() && trip.primary != null) { + notifyUsers.add(Persistence.otpUsers.getById(trip.primary.userId)); } - } - /** - * Create locale specific notifications. - */ - public static TripMonitorNotification[] createLegTransitionNotifications( - List legTransitionTypes, - String travelerName, - TravelerPosition travelerPosition, - Locale observerLocale - ) { - List tripMonitorNotifications = new ArrayList<>(); - // Create locale specific notifications. - for (NotificationType legTransitionType : legTransitionTypes) { - LegTransitionNotification legTransitionNotification = new LegTransitionNotification( - travelerName, - legTransitionType, - travelerPosition, - observerLocale - ); - if (legTransitionNotification.tripMonitorNotification != null) { - tripMonitorNotifications.add(legTransitionNotification.tripMonitorNotification); + trip.observers.forEach(observer -> { + if (observer.isConfirmed()) { + notifyUsers.add(Persistence.otpUsers.getOneFiltered(eq("email", observer.email))); } - } - return tripMonitorNotifications.toArray(new TripMonitorNotification[0]); + }); + + return notifyUsers; } -} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/middleware/models/MonitoredTrip.java b/src/main/java/org/opentripplanner/middleware/models/MonitoredTrip.java index 4a49703a5..4d8ee80fa 100644 --- a/src/main/java/org/opentripplanner/middleware/models/MonitoredTrip.java +++ b/src/main/java/org/opentripplanner/middleware/models/MonitoredTrip.java @@ -32,6 +32,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static com.mongodb.client.model.Filters.eq; + /** * A monitored trip represents a trip a user would like to receive notification on if affected by a delay and/or route * change. @@ -500,4 +502,19 @@ public TripUsers(MobilityProfileLite primary, RelatedUser companion, List findLastTripSurveyNotificationSent() { if (tripSurveyNotifications == null) return Optional.empty(); return tripSurveyNotifications.stream().max(Comparator.comparingLong(n -> n.timeSent.getTime())); } + + /** + * Use name if available, if not fallback on their email (which is a required field). + */ + public String getAddressee() { + return Strings.isBlank(name) ? email : name; + } } diff --git a/src/main/java/org/opentripplanner/middleware/models/TripMonitorNotification.java b/src/main/java/org/opentripplanner/middleware/models/TripMonitorNotification.java index 985970370..cf5fa174b 100644 --- a/src/main/java/org/opentripplanner/middleware/models/TripMonitorNotification.java +++ b/src/main/java/org/opentripplanner/middleware/models/TripMonitorNotification.java @@ -8,6 +8,7 @@ import java.util.Date; import java.util.Locale; +import java.util.Objects; /** * Contains information about the type and details of messages to be sent to users about their {@link MonitoredTrip}s. @@ -110,4 +111,17 @@ public static TripMonitorNotification createInitialReminderNotification( ) ); } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + TripMonitorNotification that = (TripMonitorNotification) o; + return type == that.type && Objects.equals(body, that.body); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), type, body); + } } diff --git a/src/main/java/org/opentripplanner/middleware/tripmonitor/JourneyState.java b/src/main/java/org/opentripplanner/middleware/tripmonitor/JourneyState.java index e1b54b0ce..86ccaea48 100644 --- a/src/main/java/org/opentripplanner/middleware/tripmonitor/JourneyState.java +++ b/src/main/java/org/opentripplanner/middleware/tripmonitor/JourneyState.java @@ -40,7 +40,6 @@ public class JourneyState implements Cloneable { /** * The notifications already sent. - * FIXME this is never set, so it has no effect. */ public Set lastNotifications = new HashSet<>(); diff --git a/src/main/java/org/opentripplanner/middleware/tripmonitor/TrustedCompanion.java b/src/main/java/org/opentripplanner/middleware/tripmonitor/TrustedCompanion.java index cad8bb17b..3b3bea599 100644 --- a/src/main/java/org/opentripplanner/middleware/tripmonitor/TrustedCompanion.java +++ b/src/main/java/org/opentripplanner/middleware/tripmonitor/TrustedCompanion.java @@ -184,7 +184,6 @@ private static boolean sendAcceptDependentEmail(OtpUser dependentUser, OtpUser r String acceptDependentLinkLabel = Message.ACCEPT_DEPENDENT_EMAIL_LINK_TEXT.get(locale); String acceptDependentUrl = getAcceptDependentUrl(acceptKey, locale); - String addressee = (Strings.isBlank(dependentUser.name)) ? dependentUser.email : dependentUser.name; // A HashMap is needed instead of a Map for template data to be serialized to the template renderer. Map templateData = new HashMap<>( @@ -193,7 +192,7 @@ private static boolean sendAcceptDependentEmail(OtpUser dependentUser, OtpUser r "acceptDependentLinkLabelAndUrl", label(acceptDependentLinkLabel, acceptDependentUrl, locale), "acceptDependentUrl", acceptDependentUrl, "emailFooter", Message.ACCEPT_DEPENDENT_EMAIL_FOOTER.get(locale), - "emailGreeting", String.format(Message.ACCEPT_DEPENDENT_EMAIL_GREETING.get(locale), addressee), + "emailGreeting", String.format(Message.ACCEPT_DEPENDENT_EMAIL_GREETING.get(locale), dependentUser.getAddressee()), "manageLinkUrl", String.format("%s%s", OTP_UI_URL, SETTINGS_PATH), "manageLinkText", Message.ACCEPT_DEPENDENT_EMAIL_MANAGE.get(locale) ) diff --git a/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTrip.java b/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTrip.java index 3411602bc..9b93d962b 100644 --- a/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTrip.java +++ b/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTrip.java @@ -36,7 +36,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -44,6 +43,7 @@ import java.util.function.Supplier; import static com.mongodb.client.model.Filters.eq; +import static org.opentripplanner.middleware.models.LegTransitionNotification.getLegTransitionNotifyUsers; /** * This job handles the primary functions for checking a {@link MonitoredTrip}, including: @@ -251,28 +251,24 @@ private boolean isTrackingOngoing() { } /** - * Process leg transition notifications. + * Process leg transition notification. */ - public void processLegTransition(List legTransitionTypes, TravelerPosition travelerPosition) { - OtpUser otpUser = getOtpUser(); - if (otpUser != null) { - otpUser.relatedUsers.forEach(relatedUser -> { - if (relatedUser.isConfirmed()) { - OtpUser observer = Persistence.otpUsers.getOneFiltered(eq("email", relatedUser.email)); - if (observer != null) { - enqueueNotification( - LegTransitionNotification.createLegTransitionNotifications( - legTransitionTypes, - otpUser.name, - travelerPosition, - I18nUtils.getOtpUserLocale(observer) - ) - ); - sendNotifications(observer); - } - } - }); - } + public void processLegTransition(NotificationType notificationType, TravelerPosition travelerPosition) { + OtpUser tripOwner = getOtpUser(); + Set notifyUsers = getLegTransitionNotifyUsers(trip); + notifyUsers.forEach(observer -> { + if (observer != null) { + enqueueNotification( + new LegTransitionNotification( + tripOwner.getAddressee(), + notificationType, + travelerPosition, + I18nUtils.getOtpUserLocale(observer) + ).tripMonitorNotification + ); + sendNotifications(observer); + } + }); } /** @@ -560,7 +556,7 @@ private void sendNotifications(OtpUser otpUser) { return; } - Locale locale = getOtpUserLocale(); + Locale locale = I18nUtils.getOtpUserLocale(otpUser); String tripNameOrReminder = hasInitialReminder ? initialReminderNotification.body : trip.tripName; if (tripNameOrReminder == null) { tripNameOrReminder = Message.TRIP_NAME_UNDEFINED.get(locale); @@ -585,7 +581,7 @@ private void sendNotifications(OtpUser otpUser) { boolean successSms = false; if (otpUser.notificationChannel.contains(OtpUser.Notification.EMAIL)) { - successEmail = sendEmail(otpUser, templateData); + successEmail = sendEmail(otpUser, templateData, locale); } if (otpUser.notificationChannel.contains(OtpUser.Notification.PUSH)) { successPush = sendPush(otpUser, templateData); @@ -597,6 +593,9 @@ private void sendNotifications(OtpUser otpUser) { // TODO: better handle below when one of the following fails if (successEmail || successPush || successSms) { notificationTimestampMillis = DateTimeUtils.currentTimeMillis(); + // Prevent repeated notifications by saving successfully sent notifications. + trip.journeyState.lastNotifications.addAll(notifications); + Persistence.monitoredTrips.replace(trip.id, trip); } } @@ -617,8 +616,7 @@ private boolean sendPush(OtpUser otpUser, Map data) { /** * Send notification email in MonitoredTrip template. */ - private boolean sendEmail(OtpUser otpUser, Map data) { - Locale locale = getOtpUserLocale(); + private boolean sendEmail(OtpUser otpUser, Map data, Locale locale) { String subject = NotificationUtils.getTripEmailSubject(otpUser, locale, trip); return NotificationUtils.sendEmail( otpUser, diff --git a/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/NotificationType.java b/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/NotificationType.java index 62d0fa422..4a40911cc 100644 --- a/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/NotificationType.java +++ b/src/main/java/org/opentripplanner/middleware/tripmonitor/jobs/NotificationType.java @@ -12,7 +12,7 @@ public enum NotificationType { ALERT_FOUND, ITINERARY_NOT_FOUND, INITIAL_REMINDER, - MODE_CHANGE_NOTIFICATION, + ARRIVED_AND_MODE_CHANGE_NOTIFICATION, DEPARTED_NOTIFICATION, ARRIVED_NOTIFICATION } \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/middleware/triptracker/TravelerLocator.java b/src/main/java/org/opentripplanner/middleware/triptracker/TravelerLocator.java index e478ed6dc..0d2afce90 100644 --- a/src/main/java/org/opentripplanner/middleware/triptracker/TravelerLocator.java +++ b/src/main/java/org/opentripplanner/middleware/triptracker/TravelerLocator.java @@ -36,7 +36,7 @@ import java.util.stream.IntStream; import static org.opentripplanner.middleware.tripmonitor.jobs.NotificationType.ARRIVED_NOTIFICATION; -import static org.opentripplanner.middleware.tripmonitor.jobs.NotificationType.MODE_CHANGE_NOTIFICATION; +import static org.opentripplanner.middleware.tripmonitor.jobs.NotificationType.ARRIVED_AND_MODE_CHANGE_NOTIFICATION; import static org.opentripplanner.middleware.tripmonitor.jobs.NotificationType.DEPARTED_NOTIFICATION; import static org.opentripplanner.middleware.triptracker.instruction.TripInstruction.NO_INSTRUCTION; import static org.opentripplanner.middleware.triptracker.instruction.TripInstruction.TRIP_INSTRUCTION_IMMEDIATE_RADIUS; @@ -101,35 +101,38 @@ public static String getInstruction( return NO_INSTRUCTION; } + /** + * If a traveler is on schedule and on either a walk or transit leg check for possible leg transition notification. + */ public static void checkForLegTransition(TripStatus tripStatus, TravelerPosition travelerPosition, MonitoredTrip trip) { if ( hasRequiredTripStatus(tripStatus) && (hasRequiredWalkLeg(travelerPosition) || hasRequiredTransitLeg(travelerPosition)) ) { - List legTransitionTypes = getLegTransitionTypes(travelerPosition); - if (!legTransitionTypes.isEmpty()) { + NotificationType notificationType = getLegTransitionNotificationType(travelerPosition); + if (notificationType != null) { try { - new CheckMonitoredTrip(trip).processLegTransition(legTransitionTypes, travelerPosition); + new CheckMonitoredTrip(trip).processLegTransition(notificationType, travelerPosition); } catch (CloneNotSupportedException e) { LOG.error("Error encountered while checking leg transition.", e); } } } - } - private static List getLegTransitionTypes(TravelerPosition travelerPosition) { - List notificationTypes = new ArrayList<>(); + /** + * Depending on the traveler's proximity to the start/end of a leg return the appropriate notification type. + */ + private static NotificationType getLegTransitionNotificationType(TravelerPosition travelerPosition) { if (isAtStartOfLeg(travelerPosition)) { - notificationTypes.add(DEPARTED_NOTIFICATION); - } - if (isAtEndOfLeg(travelerPosition)) { - notificationTypes.add(ARRIVED_NOTIFICATION); - } - if (hasModeChanged(travelerPosition)) { - notificationTypes.add(MODE_CHANGE_NOTIFICATION); + return DEPARTED_NOTIFICATION; + } else if (isApproachingEndOfLeg(travelerPosition)) { + if (hasModeChanged(travelerPosition)) { + return ARRIVED_AND_MODE_CHANGE_NOTIFICATION; + } + return ARRIVED_NOTIFICATION; } - return notificationTypes; + return null; } /** @@ -138,7 +141,10 @@ private static List getLegTransitionTypes(TravelerPosition tra private static boolean hasModeChanged(TravelerPosition travelerPosition) { Leg nextLeg = travelerPosition.nextLeg; Leg expectedLeg = travelerPosition.expectedLeg; - return isAtEndOfLeg(travelerPosition) && nextLeg != null && !nextLeg.mode.equalsIgnoreCase(expectedLeg.mode); + return + isApproachingEndOfLeg(travelerPosition) && + nextLeg != null && + !nextLeg.mode.equalsIgnoreCase(expectedLeg.mode); } /** diff --git a/src/main/java/org/opentripplanner/middleware/utils/ItineraryUtils.java b/src/main/java/org/opentripplanner/middleware/utils/ItineraryUtils.java index 646c7ec9f..9011c6ddc 100644 --- a/src/main/java/org/opentripplanner/middleware/utils/ItineraryUtils.java +++ b/src/main/java/org/opentripplanner/middleware/utils/ItineraryUtils.java @@ -371,4 +371,4 @@ public static Leg getFirstLeg(Itinerary itinerary) { .map(legs -> legs.get(0)) .orElse(null); } -} \ No newline at end of file +} diff --git a/src/main/resources/Message.properties b/src/main/resources/Message.properties index 651cbfeb8..df70ee760 100644 --- a/src/main/resources/Message.properties +++ b/src/main/resources/Message.properties @@ -4,9 +4,9 @@ ACCEPT_DEPENDENT_EMAIL_LINK_TEXT = Accept trusted companion ACCEPT_DEPENDENT_EMAIL_SUBJECT = Trusted companion request ACCEPT_DEPENDENT_EMAIL_MANAGE = Manage settings ACCEPT_DEPENDENT_ERROR = Unable to accept trusted companion. +ARRIVED_AND_MODE_CHANGE_NOTIFICATION = %s has arrived at transit stop %s. ARRIVED_NOTIFICATION = %s has arrived at %s. LABEL_AND_CONTENT = %s: %s -MODE_CHANGE_NOTIFICATION = %s has changed transit from %s to %s. DEPARTED_NOTIFICATION = %s has departed %s. SMS_STOP_NOTIFICATIONS = To stop receiving notifications, reply STOP. TRIP_EMAIL_SUBJECT = %s Notification @@ -36,7 +36,6 @@ TRIP_INVITE_COMPANION = %s added you as a companion for their trip. TRIP_INVITE_PRIMARY_TRAVELER = %s made you the primary traveler on this trip. TRIP_INVITE_OBSERVER = %s added you as an observer for their trip. TRIP_NAME_UNDEFINED = Trip name undefined -TRIP_TRAVELER_GENERIC_NAME = Traveler TRIP_NOT_FOUND_NOTIFICATION = Your itinerary was not found in today's trip planner results. Please check real-time conditions and plan a new trip. TRIP_NO_LONGER_POSSIBLE_NOTIFICATION = Your itinerary is no longer possible on any monitored day of the week. Please plan and save a new trip. TRIP_REMINDER_NOTIFICATION = Reminder for %s at %s. diff --git a/src/main/resources/Message_fr.properties b/src/main/resources/Message_fr.properties index 1a0a88c50..ac826b816 100644 --- a/src/main/resources/Message_fr.properties +++ b/src/main/resources/Message_fr.properties @@ -4,9 +4,9 @@ ACCEPT_DEPENDENT_EMAIL_LINK_TEXT = Accepter la demande ACCEPT_DEPENDENT_EMAIL_SUBJECT = Demande d'accompagnateur ACCEPT_DEPENDENT_EMAIL_MANAGE = Gérez vos préférences ACCEPT_DEPENDENT_ERROR = La demande d'accompagnateur n'a pas été reçue. +ARRIVED_AND_MODE_CHANGE_NOTIFICATION = %s TODO %s. ARRIVED_NOTIFICATION = TODO %s. LABEL_AND_CONTENT = %s\u00A0: %s -MODE_CHANGE_NOTIFICATION = TODO %s TODO %s. DEPARTED_NOTIFICATION = TODO %s. SMS_STOP_NOTIFICATIONS = Pour arrêter ces notifications, envoyez STOP. TRIP_EMAIL_SUBJECT = Notification pour %s @@ -36,7 +36,6 @@ TRIP_INVITE_COMPANION = %s vous a ajout TRIP_INVITE_PRIMARY_TRAVELER = %s vous a fait le voyageur principal sur ce trajet. TRIP_INVITE_OBSERVER = %s vous a ajouté comme observateur·trice pour un trajet. TRIP_NAME_UNDEFINED = TODO -TRIP_TRAVELER_GENERIC_NAME = TODO TRIP_NOT_FOUND_NOTIFICATION = Votre trajet est introuvable aujourd'hui. Veuillez vérifier les conditions en temps-réel et recherchez un nouveau trajet. TRIP_NO_LONGER_POSSIBLE_NOTIFICATION = Votre trajet n'est plus possible dans aucun jour de suivi de la semaine. Veuillez rechercher et enregistrer un nouveau trajet. TRIP_REMINDER_NOTIFICATION = Rappel pour %s à %s. diff --git a/src/main/resources/latest-spark-swagger-output.yaml b/src/main/resources/latest-spark-swagger-output.yaml index 3d4e09dc7..28389f6a1 100644 --- a/src/main/resources/latest-spark-swagger-output.yaml +++ b/src/main/resources/latest-spark-swagger-output.yaml @@ -56,10 +56,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/AdminUser" schema: $ref: "#/definitions/AdminUser" + responseSchema: + $ref: "#/definitions/AdminUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -90,10 +90,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/AdminUser" schema: $ref: "#/definitions/AdminUser" + responseSchema: + $ref: "#/definitions/AdminUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -123,10 +123,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/Job" schema: $ref: "#/definitions/Job" + responseSchema: + $ref: "#/definitions/Job" /api/admin/user: get: tags: @@ -155,10 +155,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/ResponseList" schema: $ref: "#/definitions/ResponseList" + responseSchema: + $ref: "#/definitions/ResponseList" post: tags: - "api/admin/user" @@ -178,10 +178,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/AdminUser" schema: $ref: "#/definitions/AdminUser" + responseSchema: + $ref: "#/definitions/AdminUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -220,10 +220,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/AdminUser" schema: $ref: "#/definitions/AdminUser" + responseSchema: + $ref: "#/definitions/AdminUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -269,10 +269,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/AdminUser" schema: $ref: "#/definitions/AdminUser" + responseSchema: + $ref: "#/definitions/AdminUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -309,10 +309,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/AdminUser" schema: $ref: "#/definitions/AdminUser" + responseSchema: + $ref: "#/definitions/AdminUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -356,10 +356,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -402,10 +402,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -447,10 +447,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/TokenHolder" schema: $ref: "#/definitions/TokenHolder" + responseSchema: + $ref: "#/definitions/TokenHolder" /api/secure/application/fromtoken: get: tags: @@ -464,10 +464,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -498,10 +498,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -531,10 +531,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/Job" schema: $ref: "#/definitions/Job" + responseSchema: + $ref: "#/definitions/Job" /api/secure/application: get: tags: @@ -563,10 +563,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/ResponseList" schema: $ref: "#/definitions/ResponseList" + responseSchema: + $ref: "#/definitions/ResponseList" post: tags: - "api/secure/application" @@ -586,10 +586,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -628,10 +628,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -677,10 +677,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -717,10 +717,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ApiUser" schema: $ref: "#/definitions/ApiUser" + responseSchema: + $ref: "#/definitions/ApiUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -754,10 +754,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/CDPUser" schema: $ref: "#/definitions/CDPUser" + responseSchema: + $ref: "#/definitions/CDPUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -788,10 +788,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/CDPUser" schema: $ref: "#/definitions/CDPUser" + responseSchema: + $ref: "#/definitions/CDPUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -821,10 +821,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/Job" schema: $ref: "#/definitions/Job" + responseSchema: + $ref: "#/definitions/Job" /api/secure/cdp: get: tags: @@ -853,10 +853,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/ResponseList" schema: $ref: "#/definitions/ResponseList" + responseSchema: + $ref: "#/definitions/ResponseList" post: tags: - "api/secure/cdp" @@ -876,10 +876,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/CDPUser" schema: $ref: "#/definitions/CDPUser" + responseSchema: + $ref: "#/definitions/CDPUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -918,10 +918,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/CDPUser" schema: $ref: "#/definitions/CDPUser" + responseSchema: + $ref: "#/definitions/CDPUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -967,10 +967,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/CDPUser" schema: $ref: "#/definitions/CDPUser" + responseSchema: + $ref: "#/definitions/CDPUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1007,10 +1007,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/CDPUser" schema: $ref: "#/definitions/CDPUser" + responseSchema: + $ref: "#/definitions/CDPUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1050,10 +1050,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/ItineraryExistence" schema: $ref: "#/definitions/ItineraryExistence" + responseSchema: + $ref: "#/definitions/ItineraryExistence" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1102,10 +1102,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/ResponseList" schema: $ref: "#/definitions/ResponseList" + responseSchema: + $ref: "#/definitions/ResponseList" post: tags: - "api/secure/monitoredtrip" @@ -1125,10 +1125,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredTrip" schema: $ref: "#/definitions/MonitoredTrip" + responseSchema: + $ref: "#/definitions/MonitoredTrip" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1167,10 +1167,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredTrip" schema: $ref: "#/definitions/MonitoredTrip" + responseSchema: + $ref: "#/definitions/MonitoredTrip" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1216,10 +1216,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredTrip" schema: $ref: "#/definitions/MonitoredTrip" + responseSchema: + $ref: "#/definitions/MonitoredTrip" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1257,10 +1257,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredTrip" schema: $ref: "#/definitions/MonitoredTrip" + responseSchema: + $ref: "#/definitions/MonitoredTrip" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1298,10 +1298,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/TrackingResponse" schema: $ref: "#/definitions/TrackingResponse" + responseSchema: + $ref: "#/definitions/TrackingResponse" /api/secure/monitoredtrip/updatetracking: post: tags: @@ -1319,10 +1319,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/TrackingResponse" schema: $ref: "#/definitions/TrackingResponse" + responseSchema: + $ref: "#/definitions/TrackingResponse" /api/secure/monitoredtrip/track: post: tags: @@ -1340,10 +1340,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/TrackingResponse" schema: $ref: "#/definitions/TrackingResponse" + responseSchema: + $ref: "#/definitions/TrackingResponse" /api/secure/monitoredtrip/endtracking: post: tags: @@ -1361,10 +1361,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/EndTrackingResponse" schema: $ref: "#/definitions/EndTrackingResponse" + responseSchema: + $ref: "#/definitions/EndTrackingResponse" /api/secure/monitoredtrip/forciblyendtracking: post: tags: @@ -1382,10 +1382,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/EndTrackingResponse" schema: $ref: "#/definitions/EndTrackingResponse" + responseSchema: + $ref: "#/definitions/EndTrackingResponse" /api/secure/triprequests: get: tags: @@ -1430,10 +1430,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/TripRequest" schema: $ref: "#/definitions/TripRequest" + responseSchema: + $ref: "#/definitions/TripRequest" /api/secure/monitoredcomponent: get: tags: @@ -1462,10 +1462,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/ResponseList" schema: $ref: "#/definitions/ResponseList" + responseSchema: + $ref: "#/definitions/ResponseList" post: tags: - "api/secure/monitoredcomponent" @@ -1485,10 +1485,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredComponent" schema: $ref: "#/definitions/MonitoredComponent" + responseSchema: + $ref: "#/definitions/MonitoredComponent" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1527,10 +1527,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredComponent" schema: $ref: "#/definitions/MonitoredComponent" + responseSchema: + $ref: "#/definitions/MonitoredComponent" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1576,10 +1576,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredComponent" schema: $ref: "#/definitions/MonitoredComponent" + responseSchema: + $ref: "#/definitions/MonitoredComponent" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1617,10 +1617,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MonitoredComponent" schema: $ref: "#/definitions/MonitoredComponent" + responseSchema: + $ref: "#/definitions/MonitoredComponent" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1651,10 +1651,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/OtpUser" schema: $ref: "#/definitions/OtpUser" + responseSchema: + $ref: "#/definitions/OtpUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1686,10 +1686,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/MobilityProfileLite" schema: $ref: "#/definitions/MobilityProfileLite" + responseSchema: + $ref: "#/definitions/MobilityProfileLite" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1729,10 +1729,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/VerificationResult" schema: $ref: "#/definitions/VerificationResult" + responseSchema: + $ref: "#/definitions/VerificationResult" /api/secure/user/{id}/verify_sms/{code}: post: tags: @@ -1752,10 +1752,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/VerificationResult" schema: $ref: "#/definitions/VerificationResult" + responseSchema: + $ref: "#/definitions/VerificationResult" /api/secure/user/fromtoken: get: tags: @@ -1769,10 +1769,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/OtpUser" schema: $ref: "#/definitions/OtpUser" + responseSchema: + $ref: "#/definitions/OtpUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1803,10 +1803,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/OtpUser" schema: $ref: "#/definitions/OtpUser" + responseSchema: + $ref: "#/definitions/OtpUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1836,10 +1836,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/Job" schema: $ref: "#/definitions/Job" + responseSchema: + $ref: "#/definitions/Job" /api/secure/user: get: tags: @@ -1868,10 +1868,10 @@ paths: responses: "200": description: "successful operation" - responseSchema: - $ref: "#/definitions/ResponseList" schema: $ref: "#/definitions/ResponseList" + responseSchema: + $ref: "#/definitions/ResponseList" post: tags: - "api/secure/user" @@ -1891,10 +1891,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/OtpUser" schema: $ref: "#/definitions/OtpUser" + responseSchema: + $ref: "#/definitions/OtpUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1933,10 +1933,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/OtpUser" schema: $ref: "#/definitions/OtpUser" + responseSchema: + $ref: "#/definitions/OtpUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -1982,10 +1982,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/OtpUser" schema: $ref: "#/definitions/OtpUser" + responseSchema: + $ref: "#/definitions/OtpUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -2022,10 +2022,10 @@ paths: "200": description: "Successful operation" examples: {} - responseSchema: - $ref: "#/definitions/OtpUser" schema: $ref: "#/definitions/OtpUser" + responseSchema: + $ref: "#/definitions/OtpUser" "400": description: "The request was not formed properly (e.g., some required parameters\ \ may be missing). See the details of the returned response to determine\ @@ -2079,11 +2079,11 @@ paths: responses: "200": description: "successful operation" - responseSchema: + schema: type: "array" items: $ref: "#/definitions/ApiUsageResult" - schema: + responseSchema: type: "array" items: $ref: "#/definitions/ApiUsageResult" @@ -2110,11 +2110,11 @@ paths: responses: "200": description: "successful operation" - responseSchema: + schema: type: "array" items: $ref: "#/definitions/BugsnagEvent" - schema: + responseSchema: type: "array" items: $ref: "#/definitions/BugsnagEvent" @@ -2141,11 +2141,11 @@ paths: responses: "200": description: "successful operation" - responseSchema: + schema: type: "array" items: $ref: "#/definitions/CDPFile" - schema: + responseSchema: type: "array" items: $ref: "#/definitions/CDPFile" @@ -2166,11 +2166,11 @@ paths: responses: "200": description: "successful operation" - responseSchema: + schema: type: "array" items: $ref: "#/definitions/URL" - schema: + responseSchema: type: "array" items: $ref: "#/definitions/URL" @@ -2376,7 +2376,7 @@ definitions: - "ALERT_FOUND" - "ITINERARY_NOT_FOUND" - "INITIAL_REMINDER" - - "MODE_CHANGE_NOTIFICATION" + - "ARRIVED_AND_MODE_CHANGE_NOTIFICATION" - "DEPARTED_NOTIFICATION" - "ARRIVED_NOTIFICATION" body: diff --git a/src/test/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTripTest.java b/src/test/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTripTest.java index f6140a1e0..28c8e2cc4 100644 --- a/src/test/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTripTest.java +++ b/src/test/java/org/opentripplanner/middleware/tripmonitor/jobs/CheckMonitoredTripTest.java @@ -11,8 +11,11 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.middleware.models.ItineraryExistence; import org.opentripplanner.middleware.models.LegTransitionNotification; +import org.opentripplanner.middleware.models.MobilityProfileLite; +import org.opentripplanner.middleware.models.RelatedUser; import org.opentripplanner.middleware.models.TrackedJourney; import org.opentripplanner.middleware.otp.response.Leg; +import org.opentripplanner.middleware.testutils.ApiTestUtils; import org.opentripplanner.middleware.testutils.OtpMiddlewareTestEnvironment; import org.opentripplanner.middleware.testutils.OtpTestUtils; import org.opentripplanner.middleware.testutils.PersistenceTestUtils; @@ -39,6 +42,7 @@ import java.util.Date; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -64,6 +68,9 @@ public class CheckMonitoredTripTest extends OtpMiddlewareTestEnvironment { private static final Logger LOG = LoggerFactory.getLogger(CheckMonitoredTripTest.class); private static OtpUser user; + private static OtpUser primary; + private static OtpUser companion; + private static OtpUser observer; // this is initialized in the setup method after the OTP_TIMEZONE config value is known. private static final ZonedDateTime noonMonday8June2020 = DateTimeUtils.makeOtpZonedDateTime(new Date()) @@ -76,11 +83,14 @@ public class CheckMonitoredTripTest extends OtpMiddlewareTestEnvironment { @BeforeAll public static void setup() { user = PersistenceTestUtils.createUser("user@example.com"); + primary = PersistenceTestUtils.createUser(ApiTestUtils.generateEmailAddress("test-primary-user")); + companion = PersistenceTestUtils.createUser(ApiTestUtils.generateEmailAddress("test-companion-user")); + observer = PersistenceTestUtils.createUser(ApiTestUtils.generateEmailAddress("test-observer-user")); } @AfterAll public static void tearDown() { - Persistence.otpUsers.removeById(user.id); + PersistenceTestUtils.deleteOtpUser(false, user, primary, companion, observer); for (MonitoredTrip trip : Persistence.monitoredTrips.getFiltered(eq("userId", user.id))) { PersistenceTestUtils.deleteMonitoredTrip(trip); } @@ -223,20 +233,20 @@ private static Stream createDelayNotificationTestCases() { @ParameterizedTest @MethodSource("createLegTransitionNotificationTestCases") void testLegTransitionNotifications( - NotificationType legTransitionType, + NotificationType notificationType, String travelerName, TravelerPosition travelerPosition, Locale locale, String message ) { - TripMonitorNotification[] notification = LegTransitionNotification.createLegTransitionNotifications( - List.of(legTransitionType), + TripMonitorNotification notification = new LegTransitionNotification( travelerName, + notificationType, travelerPosition, locale - ); - assertNotNull(notification[0]); - assertEquals(message, notification[0].body); + ).tripMonitorNotification; + assertNotNull(notification); + assertEquals(message, notification.body); } private static Stream createLegTransitionNotificationTestCases() throws Exception { @@ -249,11 +259,11 @@ private static Stream createLegTransitionNotificationTestCases() thro Coordinates nextLegDepartureCoords = new Coordinates(nextLeg.from); return Stream.of( Arguments.of( - NotificationType.MODE_CHANGE_NOTIFICATION, + NotificationType.ARRIVED_AND_MODE_CHANGE_NOTIFICATION, travelerName, new TravelerPosition(expectedLeg, nextLeg, expectedLegDestinationCoords), locale, - "Obi-Wan has changed transit from TRAM to WALK." + "Obi-Wan has arrived at transit stop Pioneer Square South MAX Station." ), Arguments.of( NotificationType.DEPARTED_NOTIFICATION, @@ -268,17 +278,40 @@ private static Stream createLegTransitionNotificationTestCases() thro new TravelerPosition(expectedLeg, nextLeg, expectedLegDestinationCoords), locale, "Obi-Wan has arrived at Pioneer Square South MAX Station." - ), - Arguments.of( - NotificationType.ARRIVED_NOTIFICATION, - null, - new TravelerPosition(expectedLeg, nextLeg, expectedLegDestinationCoords), - locale, - "Traveler has arrived at Pioneer Square South MAX Station." ) ); } + @ParameterizedTest + @MethodSource("createLegTransitionNotifyUsersTestCases") + void testLegTransitionNotifyUsers( + String tripOwnerUserId, + Set expectedUsers + ) { + + RelatedUser relatedUser = new RelatedUser(); + relatedUser.email = observer.email; + relatedUser.status = RelatedUser.RelatedUserStatus.CONFIRMED; + + MonitoredTrip trip = new MonitoredTrip(); + trip.userId = tripOwnerUserId; + trip.primary = new MobilityProfileLite(primary); + trip.companion = new RelatedUser(companion.email, RelatedUser.RelatedUserStatus.CONFIRMED); + trip.observers.add(relatedUser); + + Set users = LegTransitionNotification.getLegTransitionNotifyUsers(trip); + assertNotNull(users); + assertTrue(users.containsAll(expectedUsers)); + assertEquals(users.size(), expectedUsers.size()); + } + + private static Stream createLegTransitionNotifyUsersTestCases() { + return Stream.of( + Arguments.of(primary.id, Set.of(companion, observer)), + Arguments.of(companion.id, Set.of(primary, observer)) + ); + } + /** * Convenience method for creating a CheckMonitoredTrip instance with the default journey state. */