UNPKG

@golemio/pid

Version:
238 lines • 11.3 kB
"use strict"; 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 _1 = require("."); const __1 = require(".."); 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 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 _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(__1.DepartureSkip.MISSING) && !options.skip.includes(__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, runScheduleMap, untrackedTrips, }).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); } } } } 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."); } finally { spanRunTrips?.end(); return { runSchedule: runScheduleMap, runTripIds: tripIds, }; } } 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); } 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."); } finally { spanUntrackedTrips?.end(); return untrackedTrips; } } } exports.PIDDepartureBoardsModel = PIDDepartureBoardsModel; //# sourceMappingURL=PIDDepartureBoardsModel.js.map