diff --git a/src/notifications/schedulers/ETANotificationScheduler.ts b/src/notifications/schedulers/ETANotificationScheduler.ts index 4e0227a..234b310 100644 --- a/src/notifications/schedulers/ETANotificationScheduler.ts +++ b/src/notifications/schedulers/ETANotificationScheduler.ts @@ -3,27 +3,26 @@ import { TupleKey } from "../../types/TupleKey"; import { IEta } from "../../entities/entities"; import { AppleNotificationSender, NotificationAlertArguments } from "../senders/AppleNotificationSender"; -export interface ScheduledNotificationData { +export interface NotificationLookupArguments { deviceId: string; shuttleId: string; stopId: string; +} +export interface NotificationSchedulingArguments extends NotificationLookupArguments { /** * Value which specifies the ETA of the shuttle for when * the notification should fire. * For example, a secondsThreshold of 180 would mean that the notification * fires when the ETA drops below 3 minutes. - * - * Optional to retain backwards compatibility with devices on the - * older API version. */ - secondsThreshold?: number; + secondsThreshold: number; } type DeviceIdSecondsThresholdAssociation = { [key: string]: number }; export class ETANotificationScheduler { - public readonly defaultSecondsThresholdForNotificationToFire = 180; + public static readonly defaultSecondsThresholdForNotificationToFire = 180; constructor(private repository: GetterRepository, private appleNotificationSender = new AppleNotificationSender() @@ -44,7 +43,7 @@ export class ETANotificationScheduler { */ private deviceIdsToDeliverTo: { [key: string]: DeviceIdSecondsThresholdAssociation } = {} - private async sendEtaNotificationImmediately(notificationData: ScheduledNotificationData): Promise { + private async sendEtaNotificationImmediately(notificationData: NotificationSchedulingArguments): Promise { const { deviceId, shuttleId, stopId } = notificationData; const shuttle = await this.repository.getShuttleById(shuttleId); @@ -82,7 +81,14 @@ export class ETANotificationScheduler { const deviceIdsToRemove = new Set(); for (let deviceId of Object.keys(this.deviceIdsToDeliverTo[tupleKey])) { - const deliveredSuccessfully = await this.sendEtaNotificationImmediatelyIfSecondsRemainingBelowThreshold(deviceId, eta); + const scheduledNotificationData: NotificationSchedulingArguments = { + deviceId, + secondsThreshold: this.deviceIdsToDeliverTo[tupleKey][deviceId], + shuttleId: eta.shuttleId, + stopId: eta.stopId, + } + + const deliveredSuccessfully = await this.sendEtaNotificationImmediatelyIfSecondsRemainingBelowThreshold(scheduledNotificationData, eta.secondsRemaining); if (deliveredSuccessfully) { deviceIdsToRemove.add(deviceId); } @@ -93,16 +99,12 @@ export class ETANotificationScheduler { }); } - private async sendEtaNotificationImmediatelyIfSecondsRemainingBelowThreshold(deviceId: string, eta: IEta) { - if (eta.secondsRemaining > this.defaultSecondsThresholdForNotificationToFire) { + private async sendEtaNotificationImmediatelyIfSecondsRemainingBelowThreshold(notificationObject: NotificationSchedulingArguments, etaSecondsRemaining: number) { + if (etaSecondsRemaining > notificationObject.secondsThreshold) { return false; } - return await this.sendEtaNotificationImmediately({ - deviceId, - shuttleId: eta.shuttleId, - stopId: eta.stopId, - }); + return await this.sendEtaNotificationImmediately(notificationObject); } /** @@ -113,17 +115,13 @@ export class ETANotificationScheduler { * @param secondsThreshold Value which specifies the ETA of the shuttle for when * the notification should fire. */ - public async scheduleNotification({ deviceId, shuttleId, stopId, secondsThreshold }: ScheduledNotificationData) { + public async scheduleNotification({ deviceId, shuttleId, stopId, secondsThreshold }: NotificationSchedulingArguments) { const tuple = new TupleKey(shuttleId, stopId); if (this.deviceIdsToDeliverTo[tuple.toString()] === undefined) { this.deviceIdsToDeliverTo[tuple.toString()] = {}; } - if (secondsThreshold !== undefined) { - this.deviceIdsToDeliverTo[tuple.toString()][deviceId] = secondsThreshold; - } else { - this.deviceIdsToDeliverTo[tuple.toString()][deviceId] = this.defaultSecondsThresholdForNotificationToFire; - } + this.deviceIdsToDeliverTo[tuple.toString()][deviceId] = secondsThreshold; this.repository.unsubscribeFromEtaUpdates(this.etaSubscriberCallback); this.repository.subscribeToEtaUpdates(this.etaSubscriberCallback); @@ -135,7 +133,7 @@ export class ETANotificationScheduler { * @param shuttleId Shuttle ID of the ETA object. * @param stopId Stop ID of the ETA object. */ - public async cancelNotificationIfExists({ deviceId, shuttleId, stopId }: ScheduledNotificationData) { + public async cancelNotificationIfExists({ deviceId, shuttleId, stopId }: NotificationLookupArguments) { const tupleKey = new TupleKey(shuttleId, stopId); if ( this.deviceIdsToDeliverTo[tupleKey.toString()] === undefined @@ -153,7 +151,7 @@ export class ETANotificationScheduler { * @param shuttleId * @param stopId */ - public isNotificationScheduled({ deviceId, shuttleId, stopId }: ScheduledNotificationData): boolean { + public isNotificationScheduled({ deviceId, shuttleId, stopId }: NotificationLookupArguments): boolean { const tuple = new TupleKey(shuttleId, stopId); if (this.deviceIdsToDeliverTo[tuple.toString()] === undefined) { return false; @@ -165,19 +163,21 @@ export class ETANotificationScheduler { * Return all scheduled notification for the given device ID. * @param deviceId */ - public async getAllScheduledNotificationsForDevice(deviceId: string): Promise { - const scheduledNotifications: ScheduledNotificationData[] = []; + public async getAllScheduledNotificationsForDevice(deviceId: string): Promise { + const scheduledNotifications: NotificationSchedulingArguments[] = []; for (const key of Object.keys(this.deviceIdsToDeliverTo)) { if (deviceId in this.deviceIdsToDeliverTo[key]) { const tupleKey = TupleKey.fromExistingStringKey(key); const shuttleId = tupleKey.tuple[0] const stopId = tupleKey.tuple[1]; + const secondsThreshold = this.deviceIdsToDeliverTo[key][deviceId]; scheduledNotifications.push({ shuttleId, stopId, deviceId, + secondsThreshold, }); } } diff --git a/src/resolvers/MutationResolvers.ts b/src/resolvers/MutationResolvers.ts index c1a4ce3..93ae763 100644 --- a/src/resolvers/MutationResolvers.ts +++ b/src/resolvers/MutationResolvers.ts @@ -1,5 +1,9 @@ import { NotificationInput, NotificationResponse, Resolvers } from "../generated/graphql"; import { ServerContext } from "../ServerContext"; +import { + ETANotificationScheduler, + NotificationSchedulingArguments +} from "../notifications/schedulers/ETANotificationScheduler"; export const MutationResolvers: Resolvers = { Mutation: { @@ -19,7 +23,12 @@ export const MutationResolvers: Resolvers = { } } - await context.notificationService.scheduleNotification(args.input); + const notificationData: NotificationSchedulingArguments = { + ...args.input, + secondsThreshold: ETANotificationScheduler.defaultSecondsThresholdForNotificationToFire, + } + + await context.notificationService.scheduleNotification(notificationData); const response: NotificationResponse = { message: "Notification scheduled", diff --git a/test/resolvers/QueryResolverTests.test.ts b/test/resolvers/QueryResolverTests.test.ts index 8b0b60e..c35be13 100644 --- a/test/resolvers/QueryResolverTests.test.ts +++ b/test/resolvers/QueryResolverTests.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "@jest/globals"; import { generateMockSystems } from "../testHelpers/mockDataGenerators"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; import assert = require("node:assert"); -import { ScheduledNotificationData } from "../../src/notifications/schedulers/ETANotificationScheduler"; +import { NotificationSchedulingArguments } from "../../src/notifications/schedulers/ETANotificationScheduler"; import { addMockShuttleToRepository, addMockStopToRepository } from "../testHelpers/repositorySetupHelpers"; // See Apollo documentation for integration test guide @@ -108,7 +108,7 @@ describe("QueryResolvers", () => { const shuttle = await addMockShuttleToRepository(context.repository, "1"); const stop = await addMockStopToRepository(context.repository, "1") - const notification: ScheduledNotificationData = { + const notification: NotificationSchedulingArguments = { shuttleId: shuttle.id, stopId: stop.id, deviceId: "1",