You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
11 KiB

var Router = require('express').Router;
var elasticsearch = require('elasticsearch');
const all = require('predicates').all;
const any = require('predicates').any;
const not = require('predicates').not;
const _ = require('lodash');
/** ----------------------- sanitizers ----------------------- **/
var sanitizers = {
autocomplete: require('../sanitizer/autocomplete'),
place: require('../sanitizer/place'),
search: require('../sanitizer/search'),
search_fallback: require('../sanitizer/search_fallback'),
structured_geocoding: require('../sanitizer/structured_geocoding'),
reverse: require('../sanitizer/reverse'),
nearby: require('../sanitizer/nearby')
};
/** ----------------------- middleware ------------------------ **/
var middleware = {
calcSize: require('../middleware/sizeCalculator'),
requestLanguage: require('../middleware/requestLanguage')
};
/** ----------------------- controllers ----------------------- **/
var controllers = {
coarse_reverse: require('../controller/coarse_reverse'),
mdToHTML: require('../controller/markdownToHtml'),
place: require('../controller/place'),
placeholder: require('../controller/placeholder'),
search: require('../controller/search'),
status: require('../controller/status')
};
var queries = {
libpostal: require('../query/search'),
fallback_to_old_prod: require('../query/search_original'),
structured_geocoding: require('../query/structured_geocoding'),
reverse: require('../query/reverse'),
autocomplete: require('../query/autocomplete')
};
/** ----------------------- controllers ----------------------- **/
var postProc = {
trimByGranularity: require('../middleware/trimByGranularity'),
trimByGranularityStructured: require('../middleware/trimByGranularityStructured'),
distances: require('../middleware/distance'),
confidenceScores: require('../middleware/confidenceScore'),
confidenceScoresFallback: require('../middleware/confidenceScoreFallback'),
confidenceScoresReverse: require('../middleware/confidenceScoreReverse'),
accuracy: require('../middleware/accuracy'),
dedupe: require('../middleware/dedupe'),
interpolate: require('../middleware/interpolate'),
localNamingConventions: require('../middleware/localNamingConventions'),
renamePlacenames: require('../middleware/renamePlacenames'),
geocodeJSON: require('../middleware/geocodeJSON'),
sendJSON: require('../middleware/sendJSON'),
parseBoundingBox: require('../middleware/parseBBox'),
normalizeParentIds: require('../middleware/normalizeParentIds'),
8 years ago
assignLabels: require('../middleware/assignLabels'),
changeLanguage: require('../middleware/changeLanguage'),
sortResponseData: require('../middleware/sortResponseData')
};
// predicates that drive whether controller/search runs
const hasResponseData = require('../controller/predicates/has_response_data');
const hasRequestErrors = require('../controller/predicates/has_request_errors');
const isCoarseReverse = require('../controller/predicates/is_coarse_reverse');
const isAdminOnlyAnalysis = require('../controller/predicates/is_admin_only_analysis');
const hasResultsAtLayers = require('../controller/predicates/has_results_at_layers');
// shorthand for standard early-exit conditions
const hasResponseDataOrRequestErrors = any(hasResponseData, hasRequestErrors);
const hasAdminOnlyResults = not(hasResultsAtLayers(['venue', 'address', 'street']));
const serviceWrapper = require('pelias-microservice-wrapper').service;
const PlaceHolder = require('../service/configurations/PlaceHolder');
const PointInPolygon = require('../service/configurations/PointInPolygon');
/**
* Append routes to app
*
* @param {object} app
* @param {object} peliasConfig
*/
function addRoutes(app, peliasConfig) {
const esclient = elasticsearch.Client(peliasConfig.esclient);
const pipConfiguration = new PointInPolygon(_.defaultTo(peliasConfig.api.services.pip, {}));
const pipService = serviceWrapper(pipConfiguration);
const isPipServiceEnabled = _.constant(pipConfiguration.isEnabled());
const placeholderConfiguration = new PlaceHolder(_.defaultTo(peliasConfig.api.services.placeholder, {}));
const placeholderService = serviceWrapper(placeholderConfiguration);
const isPlaceholderServiceEnabled = _.constant(placeholderConfiguration.isEnabled());
const coarse_reverse_should_execute = all(
not(hasRequestErrors), isPipServiceEnabled, isCoarseReverse
);
const coarse_reverse_fallback_should_execute = all(
isPipServiceEnabled, not(hasRequestErrors), not(hasResponseData), not(isCoarseReverse)
);
const placeholderShouldExecute = all(
not(hasResponseDataOrRequestErrors), isPlaceholderServiceEnabled, isAdminOnlyAnalysis
);
// execute under the following conditions:
// - there are no errors or data
// - request is not coarse OR pip service is disabled
const original_reverse_should_execute = all(
not(hasResponseDataOrRequestErrors),
any(
not(isCoarseReverse),
not(isPipServiceEnabled)
)
);
var base = '/v1/';
/** ------------------------- routers ------------------------- **/
var routers = {
index: createRouter([
controllers.mdToHTML(peliasConfig.api, './public/apiDoc.md')
]),
attribution: createRouter([
controllers.mdToHTML(peliasConfig.api, './public/attribution.md')
]),
search: createRouter([
sanitizers.search.middleware,
middleware.requestLanguage,
middleware.calcSize(),
controllers.placeholder(placeholderService, placeholderShouldExecute),
// 3rd parameter is which query module to use, use fallback/geodisambiguation
// first, then use original search strategy if first query didn't return anything
controllers.search(peliasConfig.api, esclient, queries.libpostal, not(hasResponseDataOrRequestErrors)),
sanitizers.search_fallback.middleware,
controllers.search(peliasConfig.api, esclient, queries.fallback_to_old_prod, not(hasResponseDataOrRequestErrors)),
postProc.trimByGranularity(),
postProc.distances('focus.point.'),
postProc.confidenceScores(peliasConfig.api),
postProc.confidenceScoresFallback(),
postProc.interpolate(),
postProc.sortResponseData(require('pelias-sorting'), hasAdminOnlyResults),
postProc.dedupe(),
postProc.accuracy(),
postProc.localNamingConventions(),
postProc.renamePlacenames(),
postProc.parseBoundingBox(),
postProc.normalizeParentIds(),
8 years ago
postProc.changeLanguage(),
postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON
]),
structured: createRouter([
sanitizers.structured_geocoding.middleware,
middleware.requestLanguage,
middleware.calcSize(),
controllers.search(peliasConfig.api, esclient, queries.structured_geocoding, not(hasResponseDataOrRequestErrors)),
postProc.trimByGranularityStructured(),
postProc.distances('focus.point.'),
postProc.confidenceScores(peliasConfig.api),
postProc.confidenceScoresFallback(),
postProc.interpolate(),
postProc.dedupe(),
postProc.accuracy(),
postProc.localNamingConventions(),
postProc.renamePlacenames(),
postProc.parseBoundingBox(),
postProc.normalizeParentIds(),
8 years ago
postProc.changeLanguage(),
postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON
]),
autocomplete: createRouter([
sanitizers.autocomplete.middleware,
middleware.requestLanguage,
controllers.search(peliasConfig.api, esclient, queries.autocomplete, not(hasResponseDataOrRequestErrors)),
postProc.distances('focus.point.'),
postProc.confidenceScores(peliasConfig.api),
postProc.dedupe(),
postProc.accuracy(),
postProc.localNamingConventions(),
postProc.renamePlacenames(),
postProc.parseBoundingBox(),
postProc.normalizeParentIds(),
8 years ago
postProc.changeLanguage(),
postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON
]),
reverse: createRouter([
sanitizers.reverse.middleware,
middleware.requestLanguage,
middleware.calcSize(),
controllers.coarse_reverse(pipService, coarse_reverse_should_execute),
controllers.search(peliasConfig.api, esclient, queries.reverse, original_reverse_should_execute),
controllers.coarse_reverse(pipService, coarse_reverse_fallback_should_execute),
postProc.distances('point.'),
// reverse confidence scoring depends on distance from origin
// so it must be calculated first
postProc.confidenceScoresReverse(),
postProc.dedupe(),
postProc.accuracy(),
postProc.localNamingConventions(),
postProc.renamePlacenames(),
postProc.parseBoundingBox(),
postProc.normalizeParentIds(),
8 years ago
postProc.changeLanguage(),
postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON
]),
nearby: createRouter([
sanitizers.nearby.middleware,
middleware.requestLanguage,
middleware.calcSize(),
controllers.search(peliasConfig.api, esclient, queries.reverse, not(hasResponseDataOrRequestErrors)),
postProc.distances('point.'),
// reverse confidence scoring depends on distance from origin
// so it must be calculated first
postProc.confidenceScoresReverse(),
postProc.dedupe(),
postProc.accuracy(),
postProc.localNamingConventions(),
postProc.renamePlacenames(),
postProc.parseBoundingBox(),
postProc.normalizeParentIds(),
8 years ago
postProc.changeLanguage(),
postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON
]),
place: createRouter([
sanitizers.place.middleware,
middleware.requestLanguage,
controllers.place(peliasConfig.api, esclient),
postProc.accuracy(),
postProc.localNamingConventions(),
postProc.renamePlacenames(),
postProc.parseBoundingBox(),
postProc.normalizeParentIds(),
8 years ago
postProc.changeLanguage(),
postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON
]),
status: createRouter([
controllers.status
])
};
// static data endpoints
app.get ( base, routers.index );
app.get ( base + 'attribution', routers.attribution );
app.get ( '/attribution', routers.attribution );
app.get ( '/status', routers.status );
// backend dependent endpoints
app.get ( base + 'place', routers.place );
app.get ( base + 'autocomplete', routers.autocomplete );
app.get ( base + 'search', routers.search );
app.post( base + 'search', routers.search );
app.get ( base + 'search/structured', routers.structured );
app.get ( base + 'reverse', routers.reverse );
app.get ( base + 'nearby', routers.nearby );
}
/**
* Helper function for creating routers
*
* @param {[{function}]} functions
* @returns {express.Router}
*/
function createRouter(functions) {
var router = Router(); // jshint ignore:line
functions.forEach(function (f) {
router.use(f);
});
return router;
}
module.exports.addRoutes = addRoutes;