Add support back in for external source ETA repositories, if we switch back later

This commit is contained in:
2025-11-13 22:28:07 -08:00
parent b74a0d5d64
commit 6278d695fa
3 changed files with 123 additions and 35 deletions

View File

@@ -22,6 +22,7 @@ import { RedisSelfUpdatingETARepository } from "../repositories/shuttle/eta/Redi
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";
export interface InterchangeSystemBuilderArguments {
name: string;
@@ -99,24 +100,33 @@ export class InterchangeSystem {
private static async buildRedisShuttleLoaderAndRepositories(args: InterchangeSystemBuilderArguments) {
const shuttleRepository = new RedisShuttleRepository();
await shuttleRepository.connect();
const shuttleDataLoader = new ApiBasedShuttleRepositoryLoader(
args.passioSystemId,
args.id,
shuttleRepository
);
const timedShuttleDataLoader = new TimedApiBasedRepositoryLoader(
shuttleDataLoader
);
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 };
}
@@ -238,23 +248,33 @@ export class InterchangeSystem {
private static buildInMemoryShuttleLoaderAndRepositories(args: InterchangeSystemBuilderArguments) {
const shuttleRepository = new UnoptimizedInMemoryShuttleRepository();
const shuttleDataLoader = new ApiBasedShuttleRepositoryLoader(
args.passioSystemId,
args.id,
shuttleRepository
);
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
);
let etaRepository: ETAGetterRepository;
if (args.useSelfUpdatingEtas) {
etaRepository = new InMemorySelfUpdatingETARepository(shuttleRepository);
} else {
etaRepository = new InMemoryExternalSourceETARepository();
}
return { shuttleRepository, etaRepository, timedShuttleLoader };
}

View File

@@ -1,9 +1,10 @@
import { ShuttleGetterSetterRepository } from "../../repositories/shuttle/ShuttleGetterSetterRepository";
import { IRoute, IShuttle, IStop } from "../../entities/ShuttleRepositoryEntities";
import { IEta, IRoute, IShuttle, IStop } from "../../entities/ShuttleRepositoryEntities";
import { ShuttleRepositoryLoader } from "./ShuttleRepositoryLoader";
import { ICoordinates, IEntityWithId } from "../../entities/SharedEntities";
import { ApiResponseError } from "../ApiResponseError";
import { SHUTTLE_TO_ROUTE_COORDINATE_MAXIMUM_DISTANCE_MILES } from "../../environment";
import { ExternalSourceETARepository } from "../../repositories/shuttle/eta/ExternalSourceETARepository";
/**
* Class which can load data into a repository from the
@@ -16,7 +17,8 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
constructor(
public passioSystemId: string,
public systemIdForConstructedData: string,
public repository: ShuttleGetterSetterRepository,
public shuttleRepository: ShuttleGetterSetterRepository,
public etaRepository?: ExternalSourceETARepository,
readonly shuttleToRouteCoordinateMaximumDistanceMiles = SHUTTLE_TO_ROUTE_COORDINATE_MAXIMUM_DISTANCE_MILES,
) {
}
@@ -34,6 +36,10 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
await this.updateRouteDataForSystem();
await this.updateStopAndPolylineDataForRoutesInSystem();
await this.updateShuttleDataForSystemBasedOnProximityToRoutes();
// Because ETA method doesn't support pruning yet,
// add a call to the clear method here
await this.updateEtaDataForExistingStopsForSystem();
}
public async updateRouteDataForSystem() {
@@ -52,16 +58,16 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
private async updateRouteDataInRepository(routes: IRoute[]) {
const routeIdsToPrune = await this.constructExistingEntityIdSet(async () => {
return await this.repository.getRoutes();
return await this.shuttleRepository.getRoutes();
});
await Promise.all(routes.map(async (route) => {
await this.repository.addOrUpdateRoute(route);
await this.shuttleRepository.addOrUpdateRoute(route);
routeIdsToPrune.delete(route.id);
}));
await Promise.all(Array.from(routeIdsToPrune).map(async (routeId) => {
await this.repository.removeRouteIfExists(routeId);
await this.shuttleRepository.removeRouteIfExists(routeId);
}));
}
@@ -117,7 +123,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
private async updateStopAndPolylineDataInRepository(json: any) {
const stopIdsToPrune = await this.constructExistingEntityIdSet(async () => {
return await this.repository.getStops();
return await this.shuttleRepository.getStops();
});
await this.updateStopDataForSystemAndApiResponse(json, stopIdsToPrune);
@@ -125,7 +131,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
await this.updatePolylineDataForExistingRoutesAndApiResponse(json);
await Promise.all(Array.from(stopIdsToPrune).map(async (stopId) => {
await this.repository.removeStopIfExists(stopId);
await this.shuttleRepository.removeStopIfExists(stopId);
}));
}
@@ -169,16 +175,16 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
private async updateShuttleDataInRepository(shuttles: IShuttle[]) {
const shuttleIdsToPrune = await this.constructExistingEntityIdSet(async () => {
return await this.repository.getShuttles();
return await this.shuttleRepository.getShuttles();
});
await Promise.all(shuttles.map(async (shuttle) => {
await this.repository.addOrUpdateShuttle(shuttle);
await this.shuttleRepository.addOrUpdateShuttle(shuttle);
shuttleIdsToPrune.delete(shuttle.id);
}));
await Promise.all(Array.from(shuttleIdsToPrune).map(async (shuttleId) => {
await this.repository.removeShuttleIfExists(shuttleId);
await this.shuttleRepository.removeShuttleIfExists(shuttleId);
}));
}
@@ -233,6 +239,66 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
return null;
}
public async updateEtaDataForExistingStopsForSystem() {
const stops = await this.shuttleRepository.getStops();
await Promise.all(stops.map(async (stop) => {
let stopId = stop.id;
await this.updateEtaDataForStopId(stopId);
}));
}
public async updateEtaDataForStopId(stopId: string) {
try {
const json = await this.fetchEtaDataJson(stopId);
const etas = this.constructEtasFromJson(json, stopId);
if (etas !== null) {
await this.updateEtaDataInRepository(etas);
} else {
console.warn(`ETA update failed for stop ${stopId} with the following JSON: ${JSON.stringify(json)}`);
}
} catch(e: any) {
throw new ApiResponseError(e.message);
}
}
private async updateEtaDataInRepository(etas: IEta[]) {
await Promise.all(etas.map(async (eta) => {
await this.etaRepository?.addOrUpdateEtaFromExternalSource(eta);
}));
}
private async fetchEtaDataJson(stopId: string) {
const params = {
eta: "3",
stopIds: stopId,
};
const query = new URLSearchParams(params).toString();
const response = await fetch(`${this.baseUrl}?${query}`, {
method: "GET",
});
return await response.json();
}
private constructEtasFromJson(json: any, stopId: string): IEta[] | null {
if (json.ETAs && json.ETAs[stopId]) {
return json.ETAs[stopId].map((jsonEta: any) => {
const shuttleId: string = jsonEta.busId;
const eta: IEta = {
secondsRemaining: jsonEta.secondsSpent,
shuttleId: `${shuttleId}`,
stopId: stopId,
updatedTime: new Date(),
systemId: this.systemIdForConstructedData,
};
return eta;
});
}
return null;
}
protected async updateStopDataForSystemAndApiResponse(
json: any,
setOfIdsToPrune: Set<string> = new Set(),
@@ -252,7 +318,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
updatedTime: new Date(),
};
await this.repository.addOrUpdateStop(constructedStop);
await this.shuttleRepository.addOrUpdateStop(constructedStop);
setOfIdsToPrune.delete(constructedStop.id);
}));
@@ -274,7 +340,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
const orderedStopDataArray = jsonOrderedStopData[index];
const stopId = orderedStopDataArray[1];
let constructedOrderedStop = await this.repository.getOrderedStopByRouteAndStopId(routeId, stopId)
let constructedOrderedStop = await this.shuttleRepository.getOrderedStopByRouteAndStopId(routeId, stopId)
if (constructedOrderedStop === null) {
constructedOrderedStop = {
routeId,
@@ -304,7 +370,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
};
}
await this.repository.addOrUpdateOrderedStop(constructedOrderedStop);
await this.shuttleRepository.addOrUpdateOrderedStop(constructedOrderedStop);
}
}));
}
@@ -315,7 +381,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
await Promise.all(Object.keys(json.routePoints).map(async (routeId) => {
const routePoints = json.routePoints[routeId][0];
const existingRoute = await this.repository.getRouteById(routeId);
const existingRoute = await this.shuttleRepository.getRouteById(routeId);
if (!existingRoute) return;
existingRoute.polylineCoordinates = routePoints.map((point: any) => {
@@ -325,7 +391,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
};
});
await this.repository.addOrUpdateRoute(existingRoute);
await this.shuttleRepository.addOrUpdateRoute(existingRoute);
}))
}
}
@@ -334,7 +400,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
let filteredShuttles: IShuttle[] = [];
await Promise.all(shuttles.map(async (shuttle) => {
const route = await this.repository.getRouteById(shuttle.routeId);
const route = await this.shuttleRepository.getRouteById(shuttle.routeId);
if (route != null) {
let closestDistanceMiles = Number.MAX_VALUE;

View File

@@ -4,4 +4,6 @@ export interface ShuttleRepositoryLoader extends RepositoryLoader {
updateRouteDataForSystem(): Promise<void>;
updateStopAndPolylineDataForRoutesInSystem(): Promise<void>;
updateShuttleDataForSystemBasedOnProximityToRoutes(): Promise<void>;
updateEtaDataForExistingStopsForSystem(): Promise<void>;
updateEtaDataForStopId(stopId: string): Promise<void>;
}