import { TupleKey } from '../../types/TupleKey'; import { Listener, NotificationEvent, NotificationLookupArguments, NotificationRepository, ScheduledNotification } from "./NotificationRepository"; import { BaseRedisRepository } from "../BaseRedisRepository"; export class RedisNotificationRepository extends BaseRedisRepository implements NotificationRepository { private notificationListeners: Listener[] = []; private readonly NOTIFICATION_KEY_PREFIX = 'notification:'; private getNotificationKey = (shuttleId: string, stopId: string): string => { const tuple = new TupleKey(shuttleId, stopId); return `${this.NOTIFICATION_KEY_PREFIX}${tuple.toString()}`; }; public addOrUpdateNotification = async (notification: ScheduledNotification): Promise => { const { shuttleId, stopId, deviceId, secondsThreshold } = notification; const key = this.getNotificationKey(shuttleId, stopId); await this.redisClient.hSet(key, deviceId, secondsThreshold.toString()); this.notificationListeners.forEach((listener: Listener) => { const event: NotificationEvent = { event: 'addOrUpdate', notification }; listener(event); }); }; public deleteNotificationIfExists = async (lookupArguments: NotificationLookupArguments): Promise => { const { shuttleId, stopId, deviceId } = lookupArguments; const key = this.getNotificationKey(shuttleId, stopId); const secondsThreshold = await this.redisClient.hGet(key, deviceId); if (secondsThreshold) { await this.redisClient.hDel(key, deviceId); // Check if hash is empty and delete it if so const remainingFields = await this.redisClient.hLen(key); if (remainingFields === 0) { await this.redisClient.del(key); } this.notificationListeners.forEach((listener) => { const event: NotificationEvent = { event: 'delete', notification: { deviceId, shuttleId, stopId, secondsThreshold: parseInt(secondsThreshold) } }; listener(event); }); } }; public getAllNotificationsForShuttleAndStopId = async ( shuttleId: string, stopId: string ): Promise => { const key = this.getNotificationKey(shuttleId, stopId); const allNotifications = await this.redisClient.hGetAll(key); return Object.entries(allNotifications).map(([deviceId, secondsThreshold]) => ({ shuttleId, stopId, deviceId, secondsThreshold: parseInt(secondsThreshold) })); }; public getSecondsThresholdForNotificationIfExists = async ( lookupArguments: NotificationLookupArguments ): Promise => { const { shuttleId, stopId, deviceId } = lookupArguments; const key = this.getNotificationKey(shuttleId, stopId); const threshold = await this.redisClient.hGet(key, deviceId); return threshold ? parseInt(threshold) : null; }; public isNotificationScheduled = async ( lookupArguments: NotificationLookupArguments ): Promise => { const threshold = await this.getSecondsThresholdForNotificationIfExists(lookupArguments); return threshold !== null; }; public subscribeToNotificationChanges = (listener: Listener): void => { const index = this.notificationListeners.findIndex( (existingListener) => existingListener === listener ); if (index < 0) { this.notificationListeners.push(listener); } }; public unsubscribeFromNotificationChanges = (listener: Listener): void => { const index = this.notificationListeners.findIndex( (existingListener) => existingListener === listener ); if (index >= 0) { this.notificationListeners.splice(index, 1); } }; }