// Parse and return an LDB Request const ldb = require("ldbs-json"); const util = require("../utils/ldb.utils"); const san = require("../utils/sanitizer.utils"); const db = require("../services/dbAccess.services"); import { findStationsByDistancePipeline } from "../utils/ldbPipeline.utils"; import { logger } from "../utils/logger.utils"; import { transform as staffStationTransform } from "../utils/processors/ldb/staffStation"; const ldbKey = process.env.OWL_LDB_KEY; const ldbsvKey = process.env.OWL_LDB_SVKEY; async function get(id, staff = false) { const cleanId = san.cleanApiEndpointTxt(id); const obj = await util.checkCrs(cleanId); try { const crs = obj[0]["3ALPHA"]; logger.debug(`ldbService.get: Determined CRS for lookup to be: ${crs}`); if (staff) { const data = arrDepBoardStaff(crs); db.increment("ldbsvws"); return await data; } else { const data = arrDepBoard(crs); db.increment("ldbws"); return await data; } } catch (err) { logger.error(err, "ldbService.get: Error, Unable to find CRS"); return { obStatus: "LOC_NOT_FOUND", obMsg: "Location is not available", }; } } async function arrDepBoard(CRS) { logger.trace(`ldbService.arrDepBoard: Trying to fetch board for ${CRS}`); try { const options = { numRows: 10, crs: CRS.toUpperCase(), }; const api = new ldb(ldbKey, false); let d = await api.call("GetArrDepBoardWithDetails", options, false, false); return await util.cleanData(d); } catch (err) { logger.error(err, "ldbService.arrDepBoard: Lookup Failed"); return { GetStationBoardResult: "not available", Reason: `The CRS code ${CRS} is not valid`, }; } } async function arrDepBoardStaff(CRS) { logger.debug(`ldbService.arrDepBoardStaff: Try to fetch board for ${CRS}`); try { const options = { numRows: 40, crs: CRS.toUpperCase(), getNonPassengerServices: true, time: await getDateTimeString(new Date()), timeWindow: 120, services: "PBS", }; const api = new ldb(ldbsvKey, true); console.time(`Fetch Staff LDB for ${CRS.toUpperCase()}`); let result try { result = await staffApiCallRetry( api, "GetArrivalDepartureBoardByCRS", options, 5, ); } catch (err) { logger.error(err, "Error fetching board data"); return {obStatus: "Error", obMsg: "Error fetching data from National Rail", data: null} } console.timeEnd(`Fetch Staff LDB for ${CRS.toUpperCase()}`); try { const _staffLdb = staffStationTransform(result); logger.debug("StaffLDB Transformed"); logger.trace(_staffLdb, "StaffLDB Transformed"); return { obStatus: "OK", obMsg: "OK", data: _staffLdb, }; } catch (err) { logger.error(err, "Transformation Error"); } return result; } catch (err) { logger.error(err, "ldbService.arrDepBoardStaff error"); return { GetStationBoardResult: "not available", Reason: `The CRS code ${CRS} is not valid`, }; } } async function getServiceByRID(rid) { logger.debug(`ldbService.getServiceByRID: Finding RID: ${rid}`); try { const options = { rid: String(rid), }; const api = new ldb(ldbsvKey, true); return await api.call("GetServiceDetailsByRID", options, false, false); } catch (err) { logger.error(err, `ldbService.queryService`); } } async function getServicesByOther(id) { logger.debug(`ldbService.getServiceByOther: Finding services: ${id}`); try { const options = { serviceID: id, sdd: getDateString(new Date()), }; const api = new ldb(ldbsvKey, true); return await api.call("QueryServices", options, false, false); } catch (err) { logger.error(err, "ldbService.getServiceByOther"); return false; } } async function staffApiCallRetry(api, method, options, retries) { for (let i=0; i < retries; i++) { try { return await api.call(method, options, false, false); } catch (err) { if (err.code === 'ENOTFOUND') { logger.warn(err, "DNS ERR") if (i < retries - 1) { logger.debug('Retrying API Call') await delay(500) continue; } } throw err; } } throw new Error("Max retries exceeded"); } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function getReasonCodeList() { logger.debug("ldbService.getReasonCodeList: Fetching reason code list"); try { const dbFilter = {}; return await db.query("reasonCodes", dbFilter, false); } catch (err) { logger.error(err, "ldbService.getReasonCodeList"); } } async function getReasonCode(code) { logger.debug(`ldbService.getReasonCode: Fetching reason code ${code}`); try { const dbFilter = { code: code, }; return await db.query("reasonCodes", dbFilter, false); } catch (err) { logger.error(err, "ldbService.getReasonCode"); } } async function getNearestStations(lat, long) { logger.debug(`ldbService.getNearestStations: Fetching nearest stations`) let pipeline = findStationsByDistancePipeline(4, lat, long) try { return await db.queryAggregate("stations", pipeline) } catch (err) { logger.error(err, `ldbService.getNearestStations`) } } async function getDateTimeString(date) { const year = date.getFullYear(), month = String(date.getMonth() + 1).padStart(2, "0"), day = String(date.getDate()).padStart(2, "0"), hour = String(date.getHours()).padStart(2, "0"), minute = String(date.getMinutes()).padStart(2, "0"), second = String(date.getSeconds()).padStart(2, "0"); const format = `${year}-${month}-${day}T${hour}:${minute}:${second}`; return format; } async function getDateString(date) { const year = date.getFullYear(), month = String(date.getMonth() + 1).padStart(2, "0"), day = String(date.getDate()).padStart(2, "0"); const format = `${year}-${month}-${day}`; return format; } module.exports = { get, getServiceByRID, getServicesByOther, getReasonCodeList, getReasonCode, getNearestStations, };