mirror of https://github.com/pelias/api.git
Peter Johnson
10 years ago
11 changed files with 225 additions and 5 deletions
@ -0,0 +1,32 @@ |
|||||||
|
|
||||||
|
var logger = require('../src/logger'), |
||||||
|
responder = require('../src/responder'), |
||||||
|
query = require('../query/suggest'), |
||||||
|
backend = require('../src/backend'); |
||||||
|
|
||||||
|
module.exports = function( req, res, next ){ |
||||||
|
|
||||||
|
var reply = { |
||||||
|
date: new Date().getTime(), |
||||||
|
body: [] |
||||||
|
}; |
||||||
|
|
||||||
|
var cmd = { |
||||||
|
index: 'pelias', |
||||||
|
body: query( req.clean ) // generate query from clean params
|
||||||
|
}; |
||||||
|
|
||||||
|
// Proxy request to ES backend & map response to a valid FeatureCollection
|
||||||
|
backend().client.suggest( cmd, function( err, data ){ |
||||||
|
|
||||||
|
if( err ){ return responder.error( req, res, next, err ); } |
||||||
|
if( data && data.pelias && data.pelias.length ){ |
||||||
|
|
||||||
|
// map options to reply body
|
||||||
|
reply.body = data['pelias'][0].options; |
||||||
|
} |
||||||
|
|
||||||
|
return responder.cors( req, res, reply ); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,12 @@ |
|||||||
|
|
||||||
|
// querable indeces
|
||||||
|
|
||||||
|
module.exports = [ |
||||||
|
'geoname', |
||||||
|
'osmnode', |
||||||
|
'osmway', |
||||||
|
'admin0', |
||||||
|
'admin1', |
||||||
|
'admin2', |
||||||
|
'neighborhood' |
||||||
|
]; |
@ -0,0 +1,31 @@ |
|||||||
|
|
||||||
|
var logger = require('../src/logger'); |
||||||
|
|
||||||
|
// Build pelias suggest query
|
||||||
|
function generate( params ){ |
||||||
|
|
||||||
|
var cmd = { |
||||||
|
'pelias' : { |
||||||
|
'text' : params.input, |
||||||
|
'completion' : { |
||||||
|
'size' : params.size, |
||||||
|
'field' : 'suggest', |
||||||
|
'context': { |
||||||
|
'dataset': params.layers, |
||||||
|
'location': { |
||||||
|
'value': [ params.lon, params.lat ], |
||||||
|
|
||||||
|
// // commented out until they fix the precision bug in ES 1.3.3
|
||||||
|
'precision': 2 // params.zoom > 9 ? 3 : (params.zoom > 7 ? 2 : 1)
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
logger.log( 'cmd', JSON.stringify( cmd, null, 2 ) ); |
||||||
|
return cmd; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
module.exports = generate; |
@ -0,0 +1,77 @@ |
|||||||
|
|
||||||
|
var logger = require('../src/logger'), |
||||||
|
indeces = require('../query/indeces'); |
||||||
|
|
||||||
|
// validate inputs, convert types and apply defaults
|
||||||
|
function sanitize( params, cb ){ |
||||||
|
|
||||||
|
var clean = {}; |
||||||
|
|
||||||
|
// ensure the input params are a valid object
|
||||||
|
if( Object.prototype.toString.call( params ) !== '[object Object]' ){ |
||||||
|
params = {}; |
||||||
|
} |
||||||
|
|
||||||
|
// input text
|
||||||
|
if('string' !== typeof params.input || !params.input.length){ |
||||||
|
return cb( 'invalid input text length, must be >0' ); |
||||||
|
} |
||||||
|
clean.input = params.input; |
||||||
|
|
||||||
|
// total results
|
||||||
|
var size = parseInt( params.size, 10 ); |
||||||
|
if( !isNaN( size ) ){ |
||||||
|
clean.size = Math.min( size, 40 ); // max
|
||||||
|
} else { |
||||||
|
clean.size = 10; // default
|
||||||
|
} |
||||||
|
|
||||||
|
// which layers to query
|
||||||
|
if('string' === typeof params.layers && params.layers.length){ |
||||||
|
var layers = params.layers.split(',').map( function( layer ){ |
||||||
|
return layer.toLowerCase(); // lowercase inputs
|
||||||
|
}); |
||||||
|
for( var x=0; x<layers.length; x++ ){ |
||||||
|
if( -1 === indeces.indexOf( layers[x] ) ){ |
||||||
|
return cb( 'invalid layer, must be one or more of ' + layers.join(',') ); |
||||||
|
} |
||||||
|
} |
||||||
|
clean.layers = layers; |
||||||
|
} |
||||||
|
else { |
||||||
|
clean.layers = indeces; // default (all layers)
|
||||||
|
} |
||||||
|
|
||||||
|
// lat
|
||||||
|
var lat = parseFloat( params.lat, 10 ); |
||||||
|
if( isNaN( lat ) || lat < 0 || lat > 90 ){ |
||||||
|
return cb( 'invalid lat, must be >0 and <90' ); |
||||||
|
} |
||||||
|
clean.lat = lat; |
||||||
|
|
||||||
|
// lon
|
||||||
|
var lon = parseFloat( params.lon, 10 ); |
||||||
|
if( isNaN( lon ) || lon < -180 || lon > 180 ){ |
||||||
|
return cb( 'invalid lon, must be >-180 and <180' ); |
||||||
|
} |
||||||
|
clean.lon = lon; |
||||||
|
|
||||||
|
// zoom level
|
||||||
|
var zoom = parseInt( params.zoom, 10 ); |
||||||
|
if( !isNaN( zoom ) ){ |
||||||
|
clean.zoom = Math.min( zoom, 18 ); // max
|
||||||
|
} else { |
||||||
|
clean.zoom = 10; // default
|
||||||
|
} |
||||||
|
|
||||||
|
return cb( undefined, clean ); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
module.exports = function( req, res, next ){ |
||||||
|
sanitize( req.query, function( err, clean ){ |
||||||
|
if( err ){ next( err ); } |
||||||
|
req.clean = clean; |
||||||
|
next(); |
||||||
|
}); |
||||||
|
}; |
@ -0,0 +1,21 @@ |
|||||||
|
|
||||||
|
var Backend = require('geopipes-elasticsearch-backend'), |
||||||
|
backends = {}, |
||||||
|
client; |
||||||
|
|
||||||
|
// set env specific client
|
||||||
|
if( process.env.NODE_ENV === 'test' ){ |
||||||
|
client = require('./pelias-mockclient'); |
||||||
|
} else { |
||||||
|
client = require('pelias-esclient')(); |
||||||
|
} |
||||||
|
|
||||||
|
function getBackend( index, type ){ |
||||||
|
var key = ( index + ':' + type ); |
||||||
|
if( !backends[key] ){ |
||||||
|
backends[key] = new Backend( client, index, type ); |
||||||
|
} |
||||||
|
return backends[key]; |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = getBackend; |
@ -0,0 +1,5 @@ |
|||||||
|
module.exports = { |
||||||
|
log: console.log.bind( console ), |
||||||
|
warn: console.warn.bind( console ), |
||||||
|
error: console.error.bind( console ) |
||||||
|
}; |
@ -0,0 +1,33 @@ |
|||||||
|
|
||||||
|
// send a reply that is capable of JSON, CORS and JSONP
|
||||||
|
function cors( req, res, obj ){ |
||||||
|
res.header('Charset','utf8'); |
||||||
|
res.header('Cache-Control','public,max-age=60'); |
||||||
|
res.header('Access-Control-Allow-Origin', '*'); |
||||||
|
res.header('Access-Control-Allow-Methods', 'GET'); |
||||||
|
res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type'); |
||||||
|
res.header('Access-Control-Allow-Credentials', true); |
||||||
|
res.header('X-Powered-By', 'pelias'); |
||||||
|
|
||||||
|
// jsonp
|
||||||
|
if( req.query && req.query.callback ){ |
||||||
|
res.header('Content-type','application/javascript'); |
||||||
|
return res.send( req.query.callback + '('+ JSON.stringify( obj ) + ');' ); |
||||||
|
} |
||||||
|
|
||||||
|
// regular json
|
||||||
|
res.header('Content-type','application/json'); |
||||||
|
return res.json( obj ); |
||||||
|
} |
||||||
|
|
||||||
|
// send an error
|
||||||
|
function error( req, res, next, err ){ |
||||||
|
console.error( 'application error:', err ); |
||||||
|
// mask error from user (contains paths)
|
||||||
|
return cors( req, res, { error: 'application error' } ); |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
cors: cors, |
||||||
|
error: error |
||||||
|
}; |
Loading…
Reference in new issue