@golemio/pid
Version:
Golemio PID Module
244 lines • 11.7 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PIDDepartureBoardsModel = void 0;
const StopEnums_1 = require("../../../helpers/StopEnums");
const data_access_1 = require("../data-access");
const models_1 = require("../../ropid-gtfs/models");
const CoreToken_1 = require("@golemio/core/dist/helpers/ioc/CoreToken");
const trace_provider_1 = require("@golemio/core/dist/monitoring/opentelemetry/trace-provider");
const golemio_errors_1 = require("@golemio/core/dist/shared/golemio-errors");
const moment_timezone_1 = __importDefault(require("@golemio/core/dist/shared/moment-timezone"));
const DepartureSkip_1 = require("../helpers/enums/DepartureSkip");
const Di_1 = require("../ioc/Di");
const OgPidToken_1 = require("../ioc/OgPidToken");
const PIDDepartureModel_1 = __importDefault(require("./helpers/PIDDepartureModel"));
const RunHelper_1 = require("./helpers/RunHelper");
const RopidDeparturesDirectionsModel_1 = require("./RopidDeparturesDirectionsModel");
const STOPS_MAX_COUNT = 100;
// TODO refactor - split into facade and multiple service/strategy classes
// Check V3TransferBoardsController and TransferFacade for inspiration
class PIDDepartureBoardsModel {
constructor() {
this.log = Di_1.OgPidContainer.resolve(CoreToken_1.CoreToken.Logger);
this.ropidDeparturesDirectionsModel = new RopidDeparturesDirectionsModel_1.RopidDeparturesDirectionsModel();
this.departuresRepository = new data_access_1.DeparturesRepository(Di_1.OgPidContainer.resolve(CoreToken_1.CoreToken.PostgresConnector), Di_1.OgPidContainer.resolve(CoreToken_1.CoreToken.SimpleConfig));
this.runTripsRedisRepository = Di_1.OgPidContainer.resolve(OgPidToken_1.OgPidToken.RunTripsRedisRepository);
this.positionsRedisRepository = Di_1.OgPidContainer.resolve(OgPidToken_1.OgPidToken.PublicVehiclePositionsRepository);
this.infotextFacade = Di_1.OgPidContainer.resolve(OgPidToken_1.OgPidToken.InfotextFacade);
}
async getAll(options) {
const currentMoment = (0, moment_timezone_1.default)();
//#region Data retrieval from DB (GTFS stops, infotexts, directions, departures)
const { stopsToInclude, stopIds } = await this.retrieveAndReturnStops(options);
const [infotextsToInclude, directions, departures] = await Promise.all([
this.retrieveAndReturnInfotexts(stopIds, currentMoment, options),
this.retrieveAndReturnDirections(stopIds),
this.retrieveAndReturnDepartures(stopIds, currentMoment, options),
]);
//#endregion
//#region Data retrieval from Redis (run schedule, untracked trips)
let runScheduleMap = new Map();
let untrackedTrips = new Set();
const isMissingRequested = options.skip.includes(DepartureSkip_1.DepartureSkip.MISSING) && !options.skip.includes(DepartureSkip_1.DepartureSkip.UNTRACKED);
// Note: The run schedule and untracked trips are populated only if the skip[]=missing (without untracked) is present
// Check the PIDDeparturesModel.skip function. In other cases they are not needed
if (isMissingRequested) {
const runTuples = new Set();
for (const departure of departures) {
if (!departure.route_id || !departure.run_number) {
continue;
}
runTuples.add(RunHelper_1.RunHelper.composeRunId(departure.route_id, departure.run_number));
}
const { runSchedule, runTripIds } = await this.retrieveAndReturnRunTrips(runTuples);
runScheduleMap = runSchedule;
untrackedTrips = await this.retrieveAndReturnUntrackedTrips(runTripIds);
}
//#endregion
//#region Processing and returning departures
const spanProcessing = (0, trace_provider_1.createChildSpan)("V2PIDRouter.Processing");
try {
const departuresToInclude = new PIDDepartureModel_1.default(departures, {
limit: options.limit,
offset: options.offset,
total: options.total,
mode: options.mode,
order: options.order,
filter: options.filter,
skip: options.skip,
departuresDirections: directions,
timezone: options.timezone,
tripNumber: null, // not used in this context
runScheduleMap,
untrackedTrips,
appendHeadsignsLimit: options.appendHeadsignsLimit,
}).toArray();
return {
departures: departuresToInclude,
infotexts: infotextsToInclude,
stops: stopsToInclude,
};
}
catch (error) {
spanProcessing?.recordException(error);
if (error instanceof golemio_errors_1.AbstractGolemioError) {
throw error;
}
throw new golemio_errors_1.GeneralError("Error while processing departures", this.constructor.name, error, 500);
}
finally {
spanProcessing?.end();
}
//#endregion
}
async retrieveAndReturnStops(options) {
const spanStops = (0, trace_provider_1.createChildSpan)("V2PIDRouter.DB.stops");
spanStops?.setAttributes({ ...options, timeFrom: options.timeFrom?.toString() });
try {
const stopsToInclude = await models_1.models.GTFSStopModel.GetAll({
includeMetroTrains: options.includeMetroTrains,
appendAswId: true,
aswIds: options.aswIds,
cisIds: options.cisIds,
gtfsIds: options.gtfsIds,
limit: STOPS_MAX_COUNT + 1,
locationType: StopEnums_1.GtfsStopLocationType.StopOrPlatform,
names: options.names,
returnRaw: true,
});
if (stopsToInclude.length === 0) {
throw new golemio_errors_1.GeneralError("No stops found.", this.constructor.name, undefined, 404);
}
else if (stopsToInclude.length > STOPS_MAX_COUNT) {
throw new golemio_errors_1.GeneralError(`Too many stops, try lower number or split requests. The maximum is ${STOPS_MAX_COUNT} stops.`, this.constructor.name, undefined, 413);
}
const stopIds = stopsToInclude.map(({ stop_id }) => stop_id);
return { stopsToInclude, stopIds };
}
catch (error) {
spanStops?.recordException(error);
if (error instanceof golemio_errors_1.AbstractGolemioError) {
throw error;
}
throw new golemio_errors_1.GeneralError("Error while getting stops", this.constructor.name, error, 500);
}
finally {
spanStops?.end();
}
}
async retrieveAndReturnInfotexts(stopIds, currentMoment, options) {
const spanInfotexts = (0, trace_provider_1.createChildSpan)("V2PIDRouter.DB.infotexts");
try {
return await this.infotextFacade.getInfotextsForDepartureBoards(stopIds, currentMoment, options);
}
catch (error) {
spanInfotexts?.recordException(error);
throw error;
}
finally {
spanInfotexts?.end();
}
}
async retrieveAndReturnDirections(stopIds) {
const spanDirections = (0, trace_provider_1.createChildSpan)("V2PIDRouter.DB.directions");
try {
return await this.ropidDeparturesDirectionsModel.GetAll(stopIds);
}
catch (error) {
spanDirections?.recordException(error);
if (error instanceof golemio_errors_1.AbstractGolemioError) {
throw error;
}
throw new golemio_errors_1.GeneralError("Error while getting directions", this.constructor.name, error, 500);
}
finally {
spanDirections?.end();
}
}
async retrieveAndReturnDepartures(stopIds, currentMoment, options) {
const spanDepartures = (0, trace_provider_1.createChildSpan)("V2PIDRouter.DB.departures");
spanDepartures?.setAttributes({ stopIds });
try {
const minutesOffset = options.timeFrom ? options.timeFrom.diff(currentMoment, "minutes") : 0;
return await this.departuresRepository.GetAll({
currentMoment,
minutesAfter: options.minutesAfter,
minutesBefore: options.minutesBefore,
minutesOffset,
stopsIds: stopIds,
mode: options.mode,
isAirCondition: !!options.airCondition,
});
}
catch (error) {
spanDepartures?.recordException(error);
if (error instanceof golemio_errors_1.AbstractGolemioError) {
throw error;
}
throw new golemio_errors_1.GeneralError("Error while getting departures", this.constructor.name, error, 500);
}
finally {
spanDepartures?.end();
}
}
async retrieveAndReturnRunTrips(runTuples) {
const runScheduleMap = new Map();
const tripIds = new Set();
const spanRunTrips = (0, trace_provider_1.createChildSpan)("V2PIDRouter.Redis.runTrips");
try {
const runTuplesArray = Array.from(runTuples);
const runSchedule = await this.runTripsRedisRepository.getMultipleSchedule(runTuplesArray);
for (let i = 0; i < runSchedule.length; i++) {
const runTrip = runSchedule[i];
if (runTrip) {
const tuple = runTuplesArray[i];
runScheduleMap.set(tuple, runTrip.schedule);
for (const trip of runTrip.schedule) {
tripIds.add(trip.trip_id);
}
}
}
return {
runSchedule: runScheduleMap,
runTripIds: tripIds,
};
}
catch (error) {
spanRunTrips?.recordException(error);
this.log.error(error,
// eslint-disable-next-line max-len
"Cannot retrieve run schedule. The API consumer will receive departures with missing vehicles even if they requested to skip them.");
return {
runSchedule: runScheduleMap,
runTripIds: tripIds,
};
}
finally {
spanRunTrips?.end();
}
}
async retrieveAndReturnUntrackedTrips(tripIds) {
let untrackedTrips = new Set();
const spanUntrackedTrips = (0, trace_provider_1.createChildSpan)("V2PIDRouter.Redis.untrackedTrips");
try {
const tripIdsArray = Array.from(tripIds);
untrackedTrips = await this.positionsRedisRepository.getTripsWithUntrackedVehicles(tripIdsArray);
return untrackedTrips;
}
catch (error) {
spanUntrackedTrips?.recordException(error);
this.log.error(error,
// eslint-disable-next-line max-len
"Cannot retrieve untracked trips. The API consumer will receive departures with missing vehicles even if they requested to skip them.");
return untrackedTrips;
}
finally {
spanUntrackedTrips?.end();
}
}
}
exports.PIDDepartureBoardsModel = PIDDepartureBoardsModel;
//# sourceMappingURL=PIDDepartureBoardsModel.js.map