atriusmaps-node-sdk
Version:
This project provides an API to Atrius Personal Wayfinder maps within a Node environment. See the README.md for more information
141 lines (113 loc) • 5.27 kB
JavaScript
;
var Zousan = require('zousan');
var processors = require('./processors.js');
/*
This service obtains dynamic POI data from our own backend REST API service.
Currently it drives dynamic data for Security Wait Times and Parking Status.
Configuration Properties Recognized:
urlBase : to override the REST url base. i.e. for alpha use 'https://rest-alpha.locuslabs.com/v1'. You can also reference
a local file, such as './testDynamicPois.json'
Values defined by 'parking':
lotName: Name of the lot for this POI, such as 'East'
lotStatus: returns either 'Closed' or 'Open'
timeIsReal: If true, the time here is live and valid, else its a default fallback
rateDay: Daily Rate expressed in English: '$17 per day'
rateHour: Hourly Rate expressed in English: '$5 per hour'
timeToTerminal1: Time in walking or by shuttle to Terminal 1 : 'Shuttle 5-7'
timeToTerminal2: Time in nwalking or by shuttle to Terminal 2 : 'Walking 5-10'
lastUpdated: timestamp of last update (as sent by server)
Values defined by 'security'
queueTime: Estimated Time in queue (in minutes) : i.e. 45
isTemporarilyClosed: If true, this line is closed - else it is open
timeIsReal: If true, the time here is live and valid, else its a default fallback
lastUpdated: timestamp of last update (as sent by server)
*/
let REFRESH_FREQUENCY = 1000 * 30; // every 30 seconds
const MARKETPLACE_URL_BASE = 'https://marketplace.locuslabs.com';
const MAX_FETCH_FAILS = 3; // 3 strikes and you're out!
const ACCOUNT_ID_HEADER = 'x-account-id';
function create (app, config = {}) {
const state = {
dynamicDataNotPending: new Zousan()
};
const T = app.gt();
// used in testing
if (config._overrideRefreshFrequency)
REFRESH_FREQUENCY = config._overrideRefreshFrequency;
// by returning this dynamicDataLoaded promise, we hold sdkReady event until this is resolved
app.bus.on('system/readywhenyouare', () => state.dynamicDataNotPending);
const init = async () => {
const [accountId, venueId] = await Promise.all([
app.bus.get('venueData/getAccountId'),
app.bus.get('venueData/getVenueId')
]);
const dynamicPoisUrl = `${MARKETPLACE_URL_BASE}/venueId/${venueId}/dynamic-poi`;
let fetchFailuresCount = 0;
let fetchSuccessCount = 0;
let fetchIntervalHandle;
const updateFromAPI = async () => {
const response = await fetch(dynamicPoisUrl, { headers: { [ACCOUNT_ID_HEADER]: accountId } });
if (response.ok) // 404 if customer does not have dynamic POIs
return response.json()
.then(({ data }) => processDynamicPois(data))
.then(() => { fetchSuccessCount++; })
.catch(console.error)
else {
console.warn('dynamicPois: fetch response status not ok', response);
fetchFailuresCount++;
// Don't keep trying after multiple fails unless past success is at least 50%
if (fetchFailuresCount >= MAX_FETCH_FAILS && fetchFailuresCount > fetchSuccessCount)
clearInterval(fetchIntervalHandle);
}
};
return updateFromAPI()
.then(() => {
fetchIntervalHandle = setInterval(updateFromAPI, REFRESH_FREQUENCY);
if (typeof fetchIntervalHandle.unref === 'function')
fetchIntervalHandle.unref(); // Node only - do not keep-alive Lambda or other short-term environments
})
.finally(() => state.dynamicDataNotPending.resolve(true)) // even in case of failure, we don't want to hold up the UI
};
async function processDynamicPois (pois) {
processParkingPOIS(pois);
processOpenClosedPois(pois);
processSecurityWaitTimes(pois);
processDynamicRouting(pois);
}
function processParkingPOIS (poiMap) {
const idValuesMap = processors.processParkingPOIS(poiMap);
app.bus.send('poi/setDynamicData', { plugin: 'parking', idValuesMap });
}
const getPOILabels = async idArray => {
const nameMap = { };
for (const poiId of idArray) {
const poi = await app.bus.get('poi/getById', { id: poiId });
if (poi)
nameMap[poiId] = poi.name;
}
return nameMap
};
/*
API response: https://gitlab.com/locuslabs/core-data-team/json-schemas/-/blob/develop/src/api-marketplace/dynamic-queue-data.json
*/
async function processSecurityWaitTimes (poiMap) {
const idValuesMap = processors.processSecurityWaitTimes(poiMap);
app.bus.send('poi/setDynamicData', { plugin: 'security', idValuesMap });
const labels = await getPOILabels(Object.keys(idValuesMap)); // get a map of labels for the POIs - used in wait time labeling
app.bus.send('map/mutateFeature', {
functor: processors.mutateSecurityCheckpointLabel(T, idValuesMap, labels)
});
}
function processOpenClosedPois (poiMap) {
const idValuesMap = processors.processOpenClosedPois(poiMap);
app.bus.send('poi/setDynamicData', { plugin: 'open-closed-status', idValuesMap });
}
async function processDynamicRouting (poiMap) {
const idValuesMap = await processors.processRoutingPois(poiMap);
app.bus.send('poi/setDynamicRouting', { plugin: 'routing', idValuesMap });
}
return {
init
}
}
exports.create = create;