mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-16 23:40:32 +00:00
295 lines
11 KiB
TypeScript
295 lines
11 KiB
TypeScript
import { ETANotificationScheduler } from "../notifications/schedulers/ETANotificationScheduler";
|
|
import { TimedApiBasedRepositoryLoader } from "../loaders/TimedApiBasedRepositoryLoader";
|
|
import { UnoptimizedInMemoryShuttleRepository } from "../repositories/shuttle/UnoptimizedInMemoryShuttleRepository";
|
|
import { RedisNotificationRepository } from "../repositories/notifications/RedisNotificationRepository";
|
|
import { NotificationRepository } from "../repositories/notifications/NotificationRepository";
|
|
import { ShuttleGetterSetterRepository } from "../repositories/shuttle/ShuttleGetterSetterRepository";
|
|
import { InMemoryNotificationRepository } from "../repositories/notifications/InMemoryNotificationRepository";
|
|
import { AppleNotificationSender } from "../notifications/senders/AppleNotificationSender";
|
|
import { ApiBasedShuttleRepositoryLoader } from "../loaders/shuttle/ApiBasedShuttleRepositoryLoader";
|
|
import { ParkingGetterSetterRepository } from "../repositories/parking/ParkingGetterSetterRepository";
|
|
import { InMemoryParkingRepository } from "../repositories/parking/InMemoryParkingRepository";
|
|
import {
|
|
buildParkingRepositoryLoaderIfExists,
|
|
ParkingRepositoryLoaderBuilderArguments
|
|
} from "../loaders/parking/buildParkingRepositoryLoaderIfExists";
|
|
import { RedisParkingRepository } from "../repositories/parking/RedisParkingRepository";
|
|
import { RedisShuttleRepository } from "../repositories/shuttle/RedisShuttleRepository";
|
|
import { ShuttleGetterRepository } from "../repositories/shuttle/ShuttleGetterRepository";
|
|
import { InMemoryExternalSourceETARepository } from "../repositories/shuttle/eta/InMemoryExternalSourceETARepository";
|
|
import { ETAGetterRepository } from "../repositories/shuttle/eta/ETAGetterRepository";
|
|
import { RedisSelfUpdatingETARepository } from "../repositories/shuttle/eta/RedisSelfUpdatingETARepository";
|
|
import { RedisExternalSourceETARepository } from "../repositories/shuttle/eta/RedisExternalSourceETARepository";
|
|
import { InMemorySelfUpdatingETARepository } from "../repositories/shuttle/eta/InMemorySelfUpdatingETARepository";
|
|
import { BaseRedisETARepository } from "../repositories/shuttle/eta/BaseRedisETARepository";
|
|
import { BaseInMemoryETARepository } from "../repositories/shuttle/eta/BaseInMemoryETARepository";
|
|
import createRedisClientForRepository from "../helpers/createRedisClientForRepository";
|
|
|
|
export interface InterchangeSystemBuilderArguments {
|
|
name: string;
|
|
|
|
/**
|
|
* ID to identify the system internally and in the API.
|
|
*/
|
|
id: string;
|
|
|
|
/**
|
|
* ID for fetching shuttle data from the Passio GO! system.
|
|
*/
|
|
passioSystemId: string;
|
|
|
|
/**
|
|
* ID for the parking repository ID in the codebase.
|
|
*/
|
|
parkingSystemId?: string;
|
|
|
|
/**
|
|
* Controls whether to self-calculate ETAs or use the external
|
|
* shuttle provider for them.
|
|
*/
|
|
useSelfUpdatingEtas: boolean;
|
|
|
|
/**
|
|
* The size of the threshold to detect when a shuttle has arrived
|
|
* at a stop, in latitude/longitude degrees.
|
|
*/
|
|
shuttleStopArrivalDegreeDelta: number;
|
|
}
|
|
|
|
export class InterchangeSystem {
|
|
private constructor(
|
|
public name: string,
|
|
public id: string,
|
|
public shuttleTimedDataLoader: TimedApiBasedRepositoryLoader,
|
|
public shuttleRepository: ShuttleGetterSetterRepository,
|
|
public etaRepository: ETAGetterRepository,
|
|
public notificationScheduler: ETANotificationScheduler,
|
|
public notificationRepository: NotificationRepository,
|
|
public parkingTimedDataLoader: TimedApiBasedRepositoryLoader | null,
|
|
public parkingRepository: ParkingGetterSetterRepository | null,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Construct an instance of the class where all composited
|
|
* classes are correctly linked, meant for use in development and production.
|
|
* @param args
|
|
*/
|
|
static async build(
|
|
args: InterchangeSystemBuilderArguments,
|
|
) {
|
|
const { shuttleRepository, timedShuttleDataLoader, etaRepository } = await InterchangeSystem.buildRedisShuttleLoaderAndRepositories(args);
|
|
timedShuttleDataLoader.start();
|
|
|
|
const { notificationScheduler, notificationRepository } = await InterchangeSystem.buildNotificationSchedulerAndRepository(
|
|
etaRepository,
|
|
shuttleRepository,
|
|
args
|
|
);
|
|
notificationScheduler.startListeningForUpdates();
|
|
|
|
let { parkingRepository, timedParkingLoader } = await InterchangeSystem.buildRedisParkingLoaderAndRepository(args.parkingSystemId);
|
|
timedParkingLoader?.start();
|
|
|
|
return new InterchangeSystem(
|
|
args.name,
|
|
args.id,
|
|
timedShuttleDataLoader,
|
|
shuttleRepository,
|
|
etaRepository,
|
|
notificationScheduler,
|
|
notificationRepository,
|
|
timedParkingLoader,
|
|
parkingRepository,
|
|
);
|
|
}
|
|
|
|
private static async buildRedisShuttleLoaderAndRepositories(args: InterchangeSystemBuilderArguments) {
|
|
const shuttleRepository = new RedisShuttleRepository(
|
|
createRedisClientForRepository(),
|
|
args.shuttleStopArrivalDegreeDelta,
|
|
);
|
|
await shuttleRepository.connect();
|
|
|
|
let etaRepository: BaseRedisETARepository;
|
|
let shuttleDataLoader: ApiBasedShuttleRepositoryLoader;
|
|
if (args.useSelfUpdatingEtas) {
|
|
etaRepository = new RedisSelfUpdatingETARepository(shuttleRepository);
|
|
(etaRepository as RedisSelfUpdatingETARepository).startListeningForUpdates();
|
|
shuttleDataLoader = new ApiBasedShuttleRepositoryLoader(
|
|
args.passioSystemId,
|
|
args.id,
|
|
shuttleRepository,
|
|
);
|
|
} else {
|
|
etaRepository = new RedisExternalSourceETARepository();
|
|
shuttleDataLoader = new ApiBasedShuttleRepositoryLoader(
|
|
args.passioSystemId,
|
|
args.id,
|
|
shuttleRepository,
|
|
etaRepository as RedisExternalSourceETARepository,
|
|
);
|
|
}
|
|
await etaRepository.connect();
|
|
|
|
const timedShuttleDataLoader = new TimedApiBasedRepositoryLoader(
|
|
shuttleDataLoader,
|
|
);
|
|
|
|
|
|
return { shuttleRepository, etaRepository, timedShuttleDataLoader };
|
|
}
|
|
|
|
private static async buildNotificationSchedulerAndRepository(
|
|
etaRepository: ETAGetterRepository,
|
|
shuttleRepository: ShuttleGetterRepository,
|
|
args: InterchangeSystemBuilderArguments
|
|
) {
|
|
const notificationRepository = new RedisNotificationRepository();
|
|
await notificationRepository.connect();
|
|
const notificationScheduler = new ETANotificationScheduler(
|
|
etaRepository,
|
|
shuttleRepository,
|
|
notificationRepository,
|
|
new AppleNotificationSender(),
|
|
args.id
|
|
);
|
|
return { notificationScheduler, notificationRepository };
|
|
}
|
|
|
|
private static async buildRedisParkingLoaderAndRepository(id?: string) {
|
|
if (id === undefined) {
|
|
return { parkingRepository: null, timedParkingLoader: null };
|
|
}
|
|
|
|
let parkingRepository: RedisParkingRepository | null = new RedisParkingRepository();
|
|
await parkingRepository.connect();
|
|
|
|
const loaderBuilderArguments: ParkingRepositoryLoaderBuilderArguments = {
|
|
id,
|
|
repository: parkingRepository,
|
|
};
|
|
let parkingLoader = buildParkingRepositoryLoaderIfExists(
|
|
loaderBuilderArguments,
|
|
);
|
|
|
|
let timedParkingLoader = null;
|
|
if (parkingLoader == null) {
|
|
parkingRepository = null;
|
|
} else {
|
|
timedParkingLoader = new TimedApiBasedRepositoryLoader(parkingLoader);
|
|
}
|
|
return { parkingRepository, timedParkingLoader };
|
|
}
|
|
|
|
/**
|
|
* Construct an instance of the class where all composited
|
|
* classes are correctly linked, meant for unit tests, and server/app
|
|
* integration tests.
|
|
* @param args
|
|
*/
|
|
static buildForTesting(
|
|
args: InterchangeSystemBuilderArguments,
|
|
) {
|
|
const { shuttleRepository, timedShuttleLoader, etaRepository } = InterchangeSystem.buildInMemoryShuttleLoaderAndRepositories(args);
|
|
// Timed shuttle loader is not started here
|
|
|
|
const { notificationScheduler, notificationRepository } = InterchangeSystem.buildInMemoryNotificationSchedulerAndRepository(
|
|
etaRepository,
|
|
shuttleRepository,
|
|
args
|
|
);
|
|
notificationScheduler.startListeningForUpdates();
|
|
|
|
let { parkingRepository, timedParkingLoader } = this.buildInMemoryParkingLoaderAndRepository(args.parkingSystemId);
|
|
// Timed parking loader is not started here
|
|
|
|
return new InterchangeSystem(
|
|
args.name,
|
|
args.id,
|
|
timedShuttleLoader,
|
|
shuttleRepository,
|
|
etaRepository,
|
|
notificationScheduler,
|
|
notificationRepository,
|
|
timedParkingLoader,
|
|
parkingRepository,
|
|
);
|
|
}
|
|
|
|
private static buildInMemoryNotificationSchedulerAndRepository(
|
|
etaRepository: ETAGetterRepository,
|
|
shuttleRepository: UnoptimizedInMemoryShuttleRepository,
|
|
args: InterchangeSystemBuilderArguments
|
|
) {
|
|
const notificationRepository = new InMemoryNotificationRepository();
|
|
const notificationScheduler = new ETANotificationScheduler(
|
|
etaRepository,
|
|
shuttleRepository,
|
|
notificationRepository,
|
|
new AppleNotificationSender(false),
|
|
args.id
|
|
);
|
|
return { notificationScheduler, notificationRepository };
|
|
}
|
|
|
|
private static buildInMemoryParkingLoaderAndRepository(id?: string) {
|
|
if (id === undefined) {
|
|
return { parkingRepository: null, timedParkingLoader: null };
|
|
}
|
|
|
|
let parkingRepository: ParkingGetterSetterRepository | null = new InMemoryParkingRepository();
|
|
const loaderBuilderArguments: ParkingRepositoryLoaderBuilderArguments = {
|
|
id,
|
|
repository: parkingRepository,
|
|
};
|
|
let parkingLoader = buildParkingRepositoryLoaderIfExists(
|
|
loaderBuilderArguments,
|
|
);
|
|
|
|
let timedParkingLoader = null;
|
|
if (parkingLoader == null) {
|
|
parkingRepository = null;
|
|
} else {
|
|
timedParkingLoader = new TimedApiBasedRepositoryLoader(parkingLoader);
|
|
}
|
|
return { parkingRepository, timedParkingLoader };
|
|
}
|
|
|
|
private static buildInMemoryShuttleLoaderAndRepositories(args: InterchangeSystemBuilderArguments) {
|
|
const shuttleRepository = new UnoptimizedInMemoryShuttleRepository(
|
|
args.shuttleStopArrivalDegreeDelta,
|
|
);
|
|
|
|
let etaRepository: BaseInMemoryETARepository;
|
|
let shuttleDataLoader: ApiBasedShuttleRepositoryLoader;
|
|
if (args.useSelfUpdatingEtas) {
|
|
etaRepository = new InMemorySelfUpdatingETARepository(shuttleRepository);
|
|
(etaRepository as InMemorySelfUpdatingETARepository).startListeningForUpdates();
|
|
shuttleDataLoader = new ApiBasedShuttleRepositoryLoader(
|
|
args.passioSystemId,
|
|
args.id,
|
|
shuttleRepository,
|
|
);
|
|
} else {
|
|
etaRepository = new InMemoryExternalSourceETARepository();
|
|
shuttleDataLoader = new ApiBasedShuttleRepositoryLoader(
|
|
args.passioSystemId,
|
|
args.id,
|
|
shuttleRepository,
|
|
etaRepository as InMemoryExternalSourceETARepository,
|
|
);
|
|
}
|
|
|
|
// Note that this loader should not be started,
|
|
// so the test data doesn't get overwritten
|
|
const timedShuttleLoader = new TimedApiBasedRepositoryLoader(
|
|
shuttleDataLoader
|
|
);
|
|
|
|
|
|
return { shuttleRepository, etaRepository, timedShuttleLoader };
|
|
}
|
|
|
|
}
|