mirror of https://github.com/pelias/api.git
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.
306 lines
12 KiB
306 lines
12 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'), |
|
search_with_ids: require('../controller/search_with_ids'), |
|
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'), |
|
address_using_ids: require('../query/address_search_using_ids') |
|
}; |
|
|
|
/** ----------------------- 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'), |
|
assignLabels: require('../middleware/assignLabels'), |
|
changeLanguage: require('../middleware/changeLanguage'), |
|
sortResponseData: require('../middleware/sortResponseData') |
|
}; |
|
|
|
// predicates that drive whether controller/search runs |
|
const hasParsedTextProperty = require('../controller/predicates/has_parsed_text_property'); |
|
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()); |
|
|
|
// fallback to coarse reverse when regular reverse didn't return anything |
|
const coarseReverseShouldExecute = all( |
|
isPipServiceEnabled, not(hasRequestErrors), not(hasResponseData) |
|
); |
|
|
|
const placeholderShouldExecute = all( |
|
not(hasResponseDataOrRequestErrors), |
|
isPlaceholderServiceEnabled, |
|
not( |
|
any( |
|
hasParsedTextProperty('venue'), |
|
hasParsedTextProperty('category') |
|
) |
|
) |
|
); |
|
|
|
const searchWithIdsShouldExecute = all( |
|
not(hasRequestErrors), |
|
not(hasParsedTextProperty('venue')), |
|
hasParsedTextProperty('street') |
|
); |
|
|
|
// execute under the following conditions: |
|
// - there are no errors or data |
|
// - request is not coarse OR pip service is disabled |
|
const nonCoarseReverseShouldExecute = 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(peliasConfig.api), |
|
middleware.requestLanguage, |
|
middleware.calcSize(), |
|
controllers.placeholder(placeholderService, placeholderShouldExecute), |
|
controllers.search_with_ids(peliasConfig.api, esclient, queries.address_using_ids, searchWithIdsShouldExecute), |
|
// 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(), |
|
postProc.changeLanguage(), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
structured: createRouter([ |
|
sanitizers.structured_geocoding.middleware(peliasConfig.api), |
|
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(), |
|
postProc.changeLanguage(), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
autocomplete: createRouter([ |
|
sanitizers.autocomplete.middleware(peliasConfig.api), |
|
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(), |
|
postProc.changeLanguage(), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
reverse: createRouter([ |
|
sanitizers.reverse.middleware, |
|
middleware.requestLanguage, |
|
middleware.calcSize(), |
|
controllers.search(peliasConfig.api, esclient, queries.reverse, nonCoarseReverseShouldExecute), |
|
controllers.coarse_reverse(pipService, coarseReverseShouldExecute), |
|
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(), |
|
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(), |
|
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(), |
|
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;
|
|
|