mirror of https://github.com/pelias/api.git
Stephen K Hess
8 years ago
committed by
GitHub
64 changed files with 3446 additions and 110 deletions
@ -0,0 +1,129 @@ |
|||||||
|
const logger = require('pelias-logger').get('coarse_reverse'); |
||||||
|
const _ = require('lodash'); |
||||||
|
const Document = require('pelias-model').Document; |
||||||
|
|
||||||
|
const granularities = [ |
||||||
|
'neighbourhood', |
||||||
|
'borough', |
||||||
|
'locality', |
||||||
|
'localadmin', |
||||||
|
'county', |
||||||
|
'macrocounty', |
||||||
|
'region', |
||||||
|
'macroregion', |
||||||
|
'dependency', |
||||||
|
'country' |
||||||
|
]; |
||||||
|
|
||||||
|
function getMostGranularLayer(results) { |
||||||
|
return granularities.find((granularity) => { |
||||||
|
return results.hasOwnProperty(granularity); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function hasResultsAtRequestedLayers(results, layers) { |
||||||
|
return _.intersection(layers, Object.keys(results)).length > 0; |
||||||
|
} |
||||||
|
|
||||||
|
function synthesizeDoc(results) { |
||||||
|
// now create a model.Document from what's level, using the most granular
|
||||||
|
// result available as the starting point
|
||||||
|
// the immediately above cannot be re-used since county may be the most
|
||||||
|
// granular layer requested but the results may start at region (no county found)
|
||||||
|
const most_granular_layer = getMostGranularLayer(results); |
||||||
|
const id = results[most_granular_layer][0].id; |
||||||
|
|
||||||
|
const doc = new Document('whosonfirst', most_granular_layer, id.toString()); |
||||||
|
doc.setName('default', results[most_granular_layer][0].name); |
||||||
|
|
||||||
|
if (results[most_granular_layer][0].hasOwnProperty('centroid')) { |
||||||
|
doc.setCentroid( results[most_granular_layer][0].centroid ); |
||||||
|
} |
||||||
|
|
||||||
|
if (results[most_granular_layer][0].hasOwnProperty('bounding_box')) { |
||||||
|
const parsedBoundingBox = results[most_granular_layer][0].bounding_box.split(',').map(parseFloat); |
||||||
|
doc.setBoundingBox({ |
||||||
|
upperLeft: { |
||||||
|
lat: parsedBoundingBox[3], |
||||||
|
lon: parsedBoundingBox[0] |
||||||
|
}, |
||||||
|
lowerRight: { |
||||||
|
lat: parsedBoundingBox[1], |
||||||
|
lon: parsedBoundingBox[2] |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (_.has(results, 'country[0].abbr')) { |
||||||
|
doc.setAlpha3(results.country[0].abbr); |
||||||
|
} |
||||||
|
|
||||||
|
// assign the administrative hierarchy
|
||||||
|
Object.keys(results).forEach((layer) => { |
||||||
|
if (results[layer][0].hasOwnProperty('abbr')) { |
||||||
|
doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString(), results[layer][0].abbr); |
||||||
|
} else { |
||||||
|
doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
const esDoc = doc.toESDocument(); |
||||||
|
esDoc.data._id = esDoc._id; |
||||||
|
esDoc.data._type = esDoc._type; |
||||||
|
return esDoc.data; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function setup(service, should_execute) { |
||||||
|
function controller(req, res, next) { |
||||||
|
// do not run controller when a request validation error has occurred
|
||||||
|
if (!should_execute(req, res)) { |
||||||
|
return next(); |
||||||
|
} |
||||||
|
|
||||||
|
const centroid = { |
||||||
|
lat: req.clean['point.lat'], |
||||||
|
lon: req.clean['point.lon'] |
||||||
|
}; |
||||||
|
|
||||||
|
service(centroid, (err, results) => { |
||||||
|
// if there's an error, log it and bail
|
||||||
|
if (err) { |
||||||
|
logger.error(err); |
||||||
|
return next(); |
||||||
|
} |
||||||
|
|
||||||
|
// find the finest granularity requested
|
||||||
|
const finest_granularity_requested = granularities.findIndex((granularity) => { |
||||||
|
return req.clean.layers.indexOf(granularity) !== -1; |
||||||
|
}); |
||||||
|
|
||||||
|
// now remove everything from the response that is more granular than the
|
||||||
|
// most granular layer requested. that is, if req.clean.layers=['county'],
|
||||||
|
// remove neighbourhoods, localities, and localadmins
|
||||||
|
Object.keys(results).forEach((layer) => { |
||||||
|
if (granularities.indexOf(layer) < finest_granularity_requested) { |
||||||
|
delete results[layer]; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
res.meta = {}; |
||||||
|
res.data = []; |
||||||
|
// synthesize a doc from results if there's a result at the request layer(s)
|
||||||
|
if (hasResultsAtRequestedLayers(results, req.clean.layers)) { |
||||||
|
res.data.push(synthesizeDoc(results)); |
||||||
|
} |
||||||
|
|
||||||
|
return next(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return controller; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
module.exports = setup; |
@ -0,0 +1,5 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
module.exports = (request, response) => { |
||||||
|
return _.get(request, 'errors', []).length > 0; |
||||||
|
}; |
@ -0,0 +1,5 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
module.exports = (request, response) => { |
||||||
|
return _.get(response, 'data', []).length > 0; |
||||||
|
}; |
@ -0,0 +1,9 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
const non_coarse_layers = ['address', 'street', 'venue']; |
||||||
|
|
||||||
|
module.exports = (req, res) => { |
||||||
|
// returns true if layers is undefined, empty, or contains 'address', 'street', or 'venue'
|
||||||
|
return !_.isEmpty(req.clean.layers) && |
||||||
|
_.intersection(req.clean.layers, non_coarse_layers).length === 0; |
||||||
|
}; |
@ -0,0 +1,7 @@ |
|||||||
|
module.exports = (uri) => { |
||||||
|
// this predicate relies upon the fact that the schema has already validated
|
||||||
|
// that api.pipService is a URI-formatted string
|
||||||
|
return (request, response) => { |
||||||
|
return uri !== undefined; |
||||||
|
}; |
||||||
|
}; |
@ -0,0 +1,172 @@ |
|||||||
|
|
||||||
|
var logger = require( 'pelias-logger' ).get( 'api' ); |
||||||
|
var service = require('../service/language'); |
||||||
|
|
||||||
|
/** |
||||||
|
example response from language web service: |
||||||
|
{ |
||||||
|
"101748479": { |
||||||
|
"wofid": 101748479, |
||||||
|
"placetype": "locality", |
||||||
|
"iso": "DE", |
||||||
|
"area": 0.031614, |
||||||
|
"lineage": { |
||||||
|
"continent_id": 102191581, |
||||||
|
"country_id": 85633111, |
||||||
|
"county_id": 102063261, |
||||||
|
"locality_id": 101748479, |
||||||
|
"macrocounty_id": 404227567, |
||||||
|
"region_id": 85682571 |
||||||
|
}, |
||||||
|
"rowid": 90425, |
||||||
|
"names": { |
||||||
|
"default": "München", |
||||||
|
"eng": "Munich" |
||||||
|
} |
||||||
|
}, |
||||||
|
} |
||||||
|
**/ |
||||||
|
|
||||||
|
function setup() { |
||||||
|
|
||||||
|
var transport = service.findById(); |
||||||
|
var middleware = function(req, res, next) { |
||||||
|
|
||||||
|
// no-op, request did not require a language change
|
||||||
|
if( !isLanguageChangeRequired( req, res ) ){ |
||||||
|
return next(); |
||||||
|
} |
||||||
|
|
||||||
|
// collect a list of parent ids to fetch translations for
|
||||||
|
var ids = extractIds( res ); |
||||||
|
|
||||||
|
// perform language lookup for all relevant ids
|
||||||
|
var timer = (new Date()).getTime(); |
||||||
|
transport.query( ids, function( err, translations ){ |
||||||
|
|
||||||
|
// update documents using a translation map
|
||||||
|
if( err ){ |
||||||
|
logger.error( '[language] [error]', err ); |
||||||
|
} else { |
||||||
|
updateDocs( req, res, translations ); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info( '[language] [took]', (new Date()).getTime() - timer, 'ms' ); |
||||||
|
next(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
middleware.transport = transport; |
||||||
|
return middleware; |
||||||
|
} |
||||||
|
|
||||||
|
// collect a list of parent ids to fetch translations for
|
||||||
|
function extractIds( res ){ |
||||||
|
|
||||||
|
// store ids in an object in order to avoid duplicates
|
||||||
|
var ids = {}; |
||||||
|
|
||||||
|
// convenience function for adding a new id to the object
|
||||||
|
function addId(id) { |
||||||
|
ids[id] = true; |
||||||
|
} |
||||||
|
|
||||||
|
// extract all parent ids from documents
|
||||||
|
res.data.forEach( function( doc ){ |
||||||
|
|
||||||
|
// skip invalid records
|
||||||
|
if( !doc || !doc.parent ){ return; } |
||||||
|
|
||||||
|
// iterate over doc.parent.* attributes
|
||||||
|
for( var attr in doc.parent ){ |
||||||
|
|
||||||
|
// match only attributes ending with '_id'
|
||||||
|
var match = attr.match(/_id$/); |
||||||
|
if( !match ){ continue; } |
||||||
|
|
||||||
|
// skip invalid/empty arrays
|
||||||
|
if( !Array.isArray( doc.parent[attr] ) || !doc.parent[attr].length ){ |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// add each id as a key in the ids object
|
||||||
|
doc.parent[attr].forEach( addId ); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// return a deduplicated array of ids
|
||||||
|
return Object.keys( ids ); |
||||||
|
} |
||||||
|
|
||||||
|
// update documents using a translation map
|
||||||
|
function updateDocs( req, res, translations ){ |
||||||
|
|
||||||
|
// sanity check arguments
|
||||||
|
if( !req || !res || !res.data || !translations ){ return; } |
||||||
|
|
||||||
|
// this is the target language we will be translating to
|
||||||
|
var requestLanguage = req.language.iso6393; |
||||||
|
|
||||||
|
// iterate over response documents
|
||||||
|
res.data.forEach( function( doc, p ){ |
||||||
|
|
||||||
|
// skip invalid records
|
||||||
|
if( !doc || !doc.parent ){ return; } |
||||||
|
|
||||||
|
// iterate over doc.parent.* attributes
|
||||||
|
for( var attr in doc.parent ){ |
||||||
|
|
||||||
|
// match only attributes ending with '_id'
|
||||||
|
var match = attr.match(/^(.*)_id$/); |
||||||
|
if( !match ){ continue; } |
||||||
|
|
||||||
|
// adminKey is the property name without the '_id'
|
||||||
|
// eg. for 'country_id', adminKey would be 'country'.
|
||||||
|
var adminKey = match[1]; |
||||||
|
var adminValues = doc.parent[adminKey]; |
||||||
|
|
||||||
|
// skip invalid/empty arrays
|
||||||
|
if( !Array.isArray( adminValues ) || !adminValues.length ){ continue; } |
||||||
|
|
||||||
|
// iterate over adminValues (it's an array and can have more than one value)
|
||||||
|
for( var i in adminValues ){ |
||||||
|
|
||||||
|
// find the corresponding key from the '_id' Array
|
||||||
|
var id = doc.parent[attr][i]; |
||||||
|
if( !id ){ continue; } |
||||||
|
|
||||||
|
// id not found in translation service response
|
||||||
|
if( !translations.hasOwnProperty( id ) ){ |
||||||
|
logger.error( '[language] [error]', 'failed to find translations for', id ); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// skip invalid records
|
||||||
|
if( !translations[id].hasOwnProperty( 'names' ) ){ continue; } |
||||||
|
|
||||||
|
// requested language is not available
|
||||||
|
if( !translations[id].names.hasOwnProperty( requestLanguage ) ){ |
||||||
|
logger.info( '[language] [info]', 'missing translation', requestLanguage, id ); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// translate 'parent.*' property
|
||||||
|
adminValues[i] = translations[id].names[ requestLanguage ]; |
||||||
|
|
||||||
|
// if the record is an admin record we also translate
|
||||||
|
// the 'name.default' property.
|
||||||
|
if( adminKey === doc.layer ){ |
||||||
|
doc.name.default = translations[id].names[ requestLanguage ]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// boolean function to check if changing the language is required
|
||||||
|
function isLanguageChangeRequired( req, res ){ |
||||||
|
return req && res && res.data && res.data.length && |
||||||
|
req.hasOwnProperty('language'); |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = setup; |
@ -0,0 +1,109 @@ |
|||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
const logger = require( 'pelias-logger' ).get( 'api' ); |
||||||
|
|
||||||
|
/** |
||||||
|
this middleware is responsible for negotiating HTTP locales for incoming |
||||||
|
browser requests by reading the querystring param 'lang' or 'Accept-Language' request headers. |
||||||
|
|
||||||
|
the preferred language will then be available on the $req object: |
||||||
|
eg. for '?lang=fr' or 'Accept-Language: fr': |
||||||
|
``` |
||||||
|
console.log( req.language ); |
||||||
|
|
||||||
|
{ |
||||||
|
name: 'French', |
||||||
|
type: 'living', |
||||||
|
scope: 'individual', |
||||||
|
iso6393: 'fra', |
||||||
|
iso6392B: 'fre', |
||||||
|
iso6392T: 'fra', |
||||||
|
iso6391: 'fr', |
||||||
|
defaulted: false |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
for configuration options see: |
||||||
|
https://github.com/florrain/locale
|
||||||
|
**/ |
||||||
|
|
||||||
|
const locale = require('locale'); |
||||||
|
|
||||||
|
/** |
||||||
|
BCP47 language tags can contain three parts: |
||||||
|
1. A language subtag (en, zh). |
||||||
|
2. A script subtag (Hant, Latn). |
||||||
|
3. A region subtag (US, CN). |
||||||
|
|
||||||
|
at time of writing we will only be concerned with 1. (the language subtag) with |
||||||
|
the intention of being compatible with the language standard of whosonfirst data. |
||||||
|
|
||||||
|
whosonfirst data is in ISO 639-3 format so we will need to configure the library |
||||||
|
to support all ISO 639-1 (2 char) codes and convert them to 639-1 (3-char) codes. |
||||||
|
|
||||||
|
see: https://github.com/whosonfirst/whosonfirst-names
|
||||||
|
**/ |
||||||
|
const iso6393 = require('iso-639-3'); |
||||||
|
|
||||||
|
// create a dictionary which maps the ISO 639-1 language subtags to a map
|
||||||
|
// of it's represenation in several different standards.
|
||||||
|
const language = {}; |
||||||
|
iso6393.filter( i => !!i.iso6391 ).forEach( i => language[ i.iso6391 ] = i ); |
||||||
|
|
||||||
|
// a pre-processed locale list of language subtags we support (all of them).
|
||||||
|
const allLocales = new locale.Locales( Object.keys( language ) ); |
||||||
|
|
||||||
|
// return the middleware
|
||||||
|
module.exports = function middleware( req, res, next ){ |
||||||
|
|
||||||
|
// init an object to store clean (sanitized) input parameters if not initialized
|
||||||
|
req.clean = req.clean || {}; |
||||||
|
|
||||||
|
// init warnings array if not initialized
|
||||||
|
req.warnings = req.warnings || []; |
||||||
|
|
||||||
|
// set defaults
|
||||||
|
var lang = language.en; |
||||||
|
var locales, best, via = 'default'; |
||||||
|
|
||||||
|
// input language via query param
|
||||||
|
if( via === 'default' && req.query && req.query.lang ){ |
||||||
|
locales = new locale.Locales( req.query.lang ); |
||||||
|
best = locales.best( allLocales ); |
||||||
|
if( best.defaulted ){ |
||||||
|
req.warnings.push( 'invalid language provided via querystring' ); |
||||||
|
} else { |
||||||
|
lang = language[ best.language ]; |
||||||
|
via = 'querystring'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// input language via request headers
|
||||||
|
if( via === 'default' && req.headers && req.headers['accept-language'] ){ |
||||||
|
locales = new locale.Locales( req.headers['accept-language'] ); |
||||||
|
best = locales.best( allLocales ); |
||||||
|
if( best.defaulted ){ |
||||||
|
req.warnings.push( 'invalid language provided via header' ); |
||||||
|
} else { |
||||||
|
lang = language[ best.language ]; |
||||||
|
via = 'header'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// set $req.language property
|
||||||
|
req.language = _.clone( lang ); |
||||||
|
req.language.defaulted = ( via === 'default' ); |
||||||
|
|
||||||
|
// set $req.clean property in order to print language info in response header
|
||||||
|
req.clean.lang = { |
||||||
|
name: req.language.name, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
defaulted: req.language.defaulted |
||||||
|
}; |
||||||
|
|
||||||
|
// logging
|
||||||
|
logger.info( '[lang] \'%s\' via \'%s\'', lang.iso6391, via ); |
||||||
|
|
||||||
|
next(); |
||||||
|
}; |
@ -0,0 +1,93 @@ |
|||||||
|
|
||||||
|
var logger = require( 'pelias-logger' ).get( 'api' ), |
||||||
|
request = require( 'superagent' ), |
||||||
|
peliasConfig = require( 'pelias-config' ); |
||||||
|
|
||||||
|
/** |
||||||
|
|
||||||
|
language subsitution service client |
||||||
|
|
||||||
|
this file provides a 'transport' which can be used to access the language |
||||||
|
service via a network connnection. |
||||||
|
|
||||||
|
the exported method for this module checks pelias-config for a configuration block such as: |
||||||
|
|
||||||
|
"language": { |
||||||
|
"client": { |
||||||
|
"adapter": "http", |
||||||
|
"host": "http://localhost:6100" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for more info on running the service see: https://github.com/pelias/placeholder
|
||||||
|
|
||||||
|
**/ |
||||||
|
|
||||||
|
/** |
||||||
|
NullTransport |
||||||
|
|
||||||
|
disables the service completely |
||||||
|
**/ |
||||||
|
function NullTransport(){} |
||||||
|
NullTransport.prototype.query = function( ids, cb ){ |
||||||
|
cb(); // no-op
|
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
HttpTransport |
||||||
|
|
||||||
|
allows the api to be used via a remote web service |
||||||
|
**/ |
||||||
|
function HttpTransport( host, settings ){ |
||||||
|
this.query = function( ids, cb ){ |
||||||
|
request |
||||||
|
.get( host + '/parser/findbyid' ) |
||||||
|
.set( 'Accept', 'application/json' ) |
||||||
|
.query({ ids: Array.isArray( ids ) ? ids.join(',') : '' }) |
||||||
|
.timeout( settings && settings.timeout || 1000 ) |
||||||
|
.end( function( err, res ){ |
||||||
|
if( err || !res ){ return cb( err ); } |
||||||
|
if( 200 !== res.status ){ return cb( 'non 200 status' ); } |
||||||
|
return cb( null, res.body ); |
||||||
|
}); |
||||||
|
}; |
||||||
|
} |
||||||
|
HttpTransport.prototype.query = function( coord, number, street, cb ){ |
||||||
|
throw new Error( 'language: transport not connected' ); |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
Setup |
||||||
|
|
||||||
|
allows instantiation of transport depending on configuration and preference |
||||||
|
**/ |
||||||
|
module.exports.findById = function setup(){ |
||||||
|
|
||||||
|
// user config
|
||||||
|
var config = peliasConfig.generate(); |
||||||
|
|
||||||
|
// ensure config variables set correctly
|
||||||
|
if( !config.hasOwnProperty('language') || !config.language.hasOwnProperty('client') ){ |
||||||
|
logger.warn( 'language: configuration not found' ); |
||||||
|
} |
||||||
|
|
||||||
|
// valid configuration found
|
||||||
|
else { |
||||||
|
|
||||||
|
// get adapter settings from config
|
||||||
|
var settings = config.language.client; |
||||||
|
|
||||||
|
// http adapter
|
||||||
|
if( 'http' === settings.adapter && settings.hasOwnProperty('host') ){ |
||||||
|
logger.info( 'language: using http transport:', settings.host ); |
||||||
|
if( settings.hasOwnProperty('timeout') ){ |
||||||
|
return new HttpTransport( settings.host, { timeout: parseInt( settings.timeout, 10 ) } ); |
||||||
|
} |
||||||
|
return new HttpTransport( settings.host ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// default adapter
|
||||||
|
logger.info( 'language: using null transport' ); |
||||||
|
return new NullTransport(); |
||||||
|
}; |
@ -0,0 +1,44 @@ |
|||||||
|
const logger = require( 'pelias-logger' ).get( 'pointinpolygon' ); |
||||||
|
const request = require('request'); |
||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
module.exports = (url) => { |
||||||
|
if (!_.isEmpty(url)) { |
||||||
|
logger.info(`using point-in-polygon service at ${url}`); |
||||||
|
|
||||||
|
return function pointinpolygon( centroid, callback ) { |
||||||
|
const requestUrl = `${url}/${centroid.lon}/${centroid.lat}`; |
||||||
|
|
||||||
|
request.get(requestUrl, (err, response, body) => { |
||||||
|
if (err) { |
||||||
|
logger.error(JSON.stringify(err)); |
||||||
|
callback(err); |
||||||
|
} |
||||||
|
else if (response.statusCode === 200) { |
||||||
|
try { |
||||||
|
const parsed = JSON.parse(body); |
||||||
|
callback(err, parsed); |
||||||
|
} |
||||||
|
catch (err) { |
||||||
|
logger.error(`${requestUrl}: could not parse response body: ${body}`); |
||||||
|
callback(`${requestUrl} returned status 200 but with non-JSON response: ${body}`); |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
logger.error(`${requestUrl} returned status ${response.statusCode}: ${body}`); |
||||||
|
callback(`${requestUrl} returned status ${response.statusCode}: ${body}`); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
} else { |
||||||
|
logger.warn('point-in-polygon service disabled'); |
||||||
|
|
||||||
|
return (centroid, callback) => { |
||||||
|
callback(`point-in-polygon service disabled, unable to resolve ${JSON.stringify(centroid)}`); |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/autocomplete?text=example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/autocomplete?text=example' |
||||||
|
headers: 'Accept-Language': 'example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/autocomplete?text=example' |
||||||
|
headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/autocomplete?lang=example&text=example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/autocomplete?lang=fr&text=example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/place?ids=geonames:venue:1' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/place?ids=geonames:venue:1' |
||||||
|
headers: 'Accept-Language': 'example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/place?ids=geonames:venue:1' |
||||||
|
headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/place?lang=example&ids=geonames:venue:1' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/place?lang=fr&ids=geonames:venue:1' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/reverse?point.lat=1&point.lon=2' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/reverse?point.lat=1&point.lon=2' |
||||||
|
headers: 'Accept-Language': 'example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/reverse?point.lat=1&point.lon=2' |
||||||
|
headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/reverse?lang=example&point.lat=1&point.lon=2' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/reverse?lang=fr&point.lat=1&point.lon=2' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/search?text=example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/search?text=example' |
||||||
|
headers: 'Accept-Language': 'example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/search?text=example' |
||||||
|
headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/search?lang=example&text=example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
name: 'English', |
||||||
|
iso6391: 'en', |
||||||
|
iso6393: 'eng', |
||||||
|
defaulted: true |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
|
||||||
|
#> language |
||||||
|
path: '/v1/search?lang=fr&text=example' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.not.exist json.geocoding.warnings |
||||||
|
|
||||||
|
#? language |
||||||
|
json.geocoding.query['lang'].should.eql { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French' |
||||||
|
} |
@ -0,0 +1,578 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const setup = require('../../../controller/coarse_reverse'); |
||||||
|
const proxyquire = require('proxyquire').noCallThru(); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
t.equal(typeof setup, 'function', 'setup is a function'); |
||||||
|
t.equal(typeof setup(), 'function', 'setup returns a controller'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.early_exit_conditions = (test, common) => { |
||||||
|
test('should_execute returning false should not call service', (t) => { |
||||||
|
const service = () => { |
||||||
|
throw Error('service should not have been called'); |
||||||
|
}; |
||||||
|
|
||||||
|
const should_execute = () => { return false; }; |
||||||
|
const controller = setup(service, should_execute); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['locality'] |
||||||
|
}, |
||||||
|
errors: ['error'] |
||||||
|
}; |
||||||
|
|
||||||
|
// verify that next was called
|
||||||
|
let next_was_called = false; |
||||||
|
const next = () => { |
||||||
|
next_was_called = true; |
||||||
|
}; |
||||||
|
|
||||||
|
// passing res=undefined verifies that it wasn't interacted with
|
||||||
|
t.doesNotThrow(controller.bind(null, req, undefined, next)); |
||||||
|
t.ok(next_was_called); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.error_conditions = (test, common) => { |
||||||
|
test('service error should log and call next', (t) => { |
||||||
|
const service = (point, callback) => { |
||||||
|
callback('this is an error'); |
||||||
|
}; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const should_execute = () => { return true; }; |
||||||
|
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(service, should_execute); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['locality'], |
||||||
|
point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// verify that next was called
|
||||||
|
let next_was_called = false; |
||||||
|
const next = () => { |
||||||
|
next_was_called = true; |
||||||
|
}; |
||||||
|
|
||||||
|
// passing res=undefined verifies that it wasn't interacted with
|
||||||
|
controller(req, undefined, next); |
||||||
|
|
||||||
|
t.ok(logger.isErrorMessage('this is an error')); |
||||||
|
t.ok(next_was_called); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.success_conditions = (test, common) => { |
||||||
|
test('service returning results should use first entry for each layer', (t) => { |
||||||
|
const service = (point, callback) => { |
||||||
|
const results = { |
||||||
|
neighbourhood: [ |
||||||
|
{ |
||||||
|
id: 10, |
||||||
|
name: 'neighbourhood name', |
||||||
|
abbr: 'neighbourhood abbr', |
||||||
|
centroid: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
}, |
||||||
|
bounding_box: '-76.345902,40.006751,-76.254038,40.072939' |
||||||
|
}, |
||||||
|
{ id: 11, name: 'neighbourhood name 2'} |
||||||
|
], |
||||||
|
borough: [ |
||||||
|
{ id: 20, name: 'borough name', abbr: 'borough abbr'}, |
||||||
|
{ id: 21, name: 'borough name 2'} |
||||||
|
], |
||||||
|
locality: [ |
||||||
|
{ id: 30, name: 'locality name', abbr: 'locality abbr'}, |
||||||
|
{ id: 31, name: 'locality name 2'} |
||||||
|
], |
||||||
|
localadmin: [ |
||||||
|
{ id: 40, name: 'localadmin name', abbr: 'localadmin abbr'}, |
||||||
|
{ id: 41, name: 'localadmin name 2'} |
||||||
|
], |
||||||
|
county: [ |
||||||
|
{ id: 50, name: 'county name', abbr: 'county abbr'}, |
||||||
|
{ id: 51, name: 'county name 2'} |
||||||
|
], |
||||||
|
macrocounty: [ |
||||||
|
{ id: 60, name: 'macrocounty name', abbr: 'macrocounty abbr'}, |
||||||
|
{ id: 61, name: 'macrocounty name 2'} |
||||||
|
], |
||||||
|
region: [ |
||||||
|
{ id: 70, name: 'region name', abbr: 'region abbr'}, |
||||||
|
{ id: 71, name: 'region name 2'} |
||||||
|
], |
||||||
|
macroregion: [ |
||||||
|
{ id: 80, name: 'macroregion name', abbr: 'macroregion abbr'}, |
||||||
|
{ id: 81, name: 'macroregion name 2'} |
||||||
|
], |
||||||
|
dependency: [ |
||||||
|
{ id: 90, name: 'dependency name', abbr: 'dependency abbr'}, |
||||||
|
{ id: 91, name: 'dependency name 2'} |
||||||
|
], |
||||||
|
country: [ |
||||||
|
{ id: 100, name: 'country name', abbr: 'xyz'}, |
||||||
|
{ id: 101, name: 'country name 2'} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
callback(undefined, results); |
||||||
|
}; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const should_execute = () => { return true; }; |
||||||
|
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(service, should_execute); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['neighbourhood'], |
||||||
|
point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const res = { }; |
||||||
|
|
||||||
|
// verify that next was called
|
||||||
|
let next_was_called = false; |
||||||
|
const next = () => { |
||||||
|
next_was_called = true; |
||||||
|
}; |
||||||
|
|
||||||
|
controller(req, res, next); |
||||||
|
|
||||||
|
const expected = { |
||||||
|
meta: {}, |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
_id: '10', |
||||||
|
_type: 'neighbourhood', |
||||||
|
layer: 'neighbourhood', |
||||||
|
source: 'whosonfirst', |
||||||
|
source_id: '10', |
||||||
|
name: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
phrase: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
parent: { |
||||||
|
neighbourhood: ['neighbourhood name'], |
||||||
|
neighbourhood_id: ['10'], |
||||||
|
neighbourhood_a: ['neighbourhood abbr'], |
||||||
|
borough: ['borough name'], |
||||||
|
borough_id: ['20'], |
||||||
|
borough_a: ['borough abbr'], |
||||||
|
locality: ['locality name'], |
||||||
|
locality_id: ['30'], |
||||||
|
locality_a: ['locality abbr'], |
||||||
|
localadmin: ['localadmin name'], |
||||||
|
localadmin_id: ['40'], |
||||||
|
localadmin_a: ['localadmin abbr'], |
||||||
|
county: ['county name'], |
||||||
|
county_id: ['50'], |
||||||
|
county_a: ['county abbr'], |
||||||
|
macrocounty: ['macrocounty name'], |
||||||
|
macrocounty_id: ['60'], |
||||||
|
macrocounty_a: ['macrocounty abbr'], |
||||||
|
region: ['region name'], |
||||||
|
region_id: ['70'], |
||||||
|
region_a: ['region abbr'], |
||||||
|
macroregion: ['macroregion name'], |
||||||
|
macroregion_id: ['80'], |
||||||
|
macroregion_a: ['macroregion abbr'], |
||||||
|
dependency: ['dependency name'], |
||||||
|
dependency_id: ['90'], |
||||||
|
dependency_a: ['dependency abbr'], |
||||||
|
country: ['country name'], |
||||||
|
country_id: ['100'], |
||||||
|
country_a: ['xyz'] |
||||||
|
}, |
||||||
|
alpha3: 'XYZ', |
||||||
|
center_point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
}, |
||||||
|
bounding_box: '{"min_lat":40.006751,"max_lat":40.072939,"min_lon":-76.345902,"max_lon":-76.254038}' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(res, expected); |
||||||
|
|
||||||
|
t.notOk(logger.hasErrorMessages()); |
||||||
|
t.ok(next_was_called); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('layers missing from results should be ignored', (t) => { |
||||||
|
const service = (point, callback) => { |
||||||
|
const results = { |
||||||
|
neighbourhood: [ |
||||||
|
{ |
||||||
|
id: 10, |
||||||
|
name: 'neighbourhood name', |
||||||
|
abbr: 'neighbourhood abbr', |
||||||
|
centroid: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
}, |
||||||
|
bounding_box: '-76.345902,40.006751,-76.254038,40.072939' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
callback(undefined, results); |
||||||
|
}; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const should_execute = () => { return true; }; |
||||||
|
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(service, should_execute); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['neighbourhood'], |
||||||
|
point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const res = { }; |
||||||
|
|
||||||
|
// verify that next was called
|
||||||
|
let next_was_called = false; |
||||||
|
const next = () => { |
||||||
|
next_was_called = true; |
||||||
|
}; |
||||||
|
|
||||||
|
controller(req, res, next); |
||||||
|
|
||||||
|
const expected = { |
||||||
|
meta: {}, |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
_id: '10', |
||||||
|
_type: 'neighbourhood', |
||||||
|
layer: 'neighbourhood', |
||||||
|
source: 'whosonfirst', |
||||||
|
source_id: '10', |
||||||
|
name: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
phrase: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
parent: { |
||||||
|
neighbourhood: ['neighbourhood name'], |
||||||
|
neighbourhood_id: ['10'], |
||||||
|
neighbourhood_a: ['neighbourhood abbr'] |
||||||
|
}, |
||||||
|
center_point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
}, |
||||||
|
bounding_box: '{"min_lat":40.006751,"max_lat":40.072939,"min_lon":-76.345902,"max_lon":-76.254038}' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(res, expected); |
||||||
|
|
||||||
|
t.notOk(logger.hasErrorMessages()); |
||||||
|
t.ok(next_was_called); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('most granular layer missing centroid should not set', (t) => { |
||||||
|
const service = (point, callback) => { |
||||||
|
const results = { |
||||||
|
neighbourhood: [ |
||||||
|
{ |
||||||
|
id: 10, |
||||||
|
name: 'neighbourhood name', |
||||||
|
abbr: 'neighbourhood abbr', |
||||||
|
bounding_box: '-76.345902,40.006751,-76.254038,40.072939' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
callback(undefined, results); |
||||||
|
}; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const should_execute = () => { return true; }; |
||||||
|
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(service, should_execute); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['neighbourhood'], |
||||||
|
point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const res = { }; |
||||||
|
|
||||||
|
// verify that next was called
|
||||||
|
let next_was_called = false; |
||||||
|
const next = () => { |
||||||
|
next_was_called = true; |
||||||
|
}; |
||||||
|
|
||||||
|
controller(req, res, next); |
||||||
|
|
||||||
|
const expected = { |
||||||
|
meta: {}, |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
_id: '10', |
||||||
|
_type: 'neighbourhood', |
||||||
|
layer: 'neighbourhood', |
||||||
|
source: 'whosonfirst', |
||||||
|
source_id: '10', |
||||||
|
name: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
phrase: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
parent: { |
||||||
|
neighbourhood: ['neighbourhood name'], |
||||||
|
neighbourhood_id: ['10'], |
||||||
|
neighbourhood_a: ['neighbourhood abbr'] |
||||||
|
}, |
||||||
|
bounding_box: '{"min_lat":40.006751,"max_lat":40.072939,"min_lon":-76.345902,"max_lon":-76.254038}' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(res, expected); |
||||||
|
|
||||||
|
t.notOk(logger.hasErrorMessages()); |
||||||
|
t.ok(next_was_called); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('most granular layer missing bounding_box should not set', (t) => { |
||||||
|
const service = (point, callback) => { |
||||||
|
const results = { |
||||||
|
neighbourhood: [ |
||||||
|
{ |
||||||
|
id: 10, |
||||||
|
name: 'neighbourhood name', |
||||||
|
abbr: 'neighbourhood abbr', |
||||||
|
centroid: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
callback(undefined, results); |
||||||
|
}; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const should_execute = () => { return true; }; |
||||||
|
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(service, should_execute); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['neighbourhood'], |
||||||
|
point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const res = { }; |
||||||
|
|
||||||
|
// verify that next was called
|
||||||
|
let next_was_called = false; |
||||||
|
const next = () => { |
||||||
|
next_was_called = true; |
||||||
|
}; |
||||||
|
|
||||||
|
controller(req, res, next); |
||||||
|
|
||||||
|
const expected = { |
||||||
|
meta: {}, |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
_id: '10', |
||||||
|
_type: 'neighbourhood', |
||||||
|
layer: 'neighbourhood', |
||||||
|
source: 'whosonfirst', |
||||||
|
source_id: '10', |
||||||
|
name: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
phrase: { |
||||||
|
'default': 'neighbourhood name' |
||||||
|
}, |
||||||
|
parent: { |
||||||
|
neighbourhood: ['neighbourhood name'], |
||||||
|
neighbourhood_id: ['10'], |
||||||
|
neighbourhood_a: ['neighbourhood abbr'] |
||||||
|
}, |
||||||
|
center_point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(res, expected); |
||||||
|
|
||||||
|
t.notOk(logger.hasErrorMessages()); |
||||||
|
t.ok(next_was_called); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.failure_conditions = (test, common) => { |
||||||
|
test('service returning 0 results at the requested layer should return nothing', (t) => { |
||||||
|
const service = (point, callback) => { |
||||||
|
// response without neighbourhood results
|
||||||
|
const results = { |
||||||
|
borough: [ |
||||||
|
{ id: 20, name: 'borough name', abbr: 'borough abbr'}, |
||||||
|
{ id: 21, name: 'borough name 2'} |
||||||
|
], |
||||||
|
locality: [ |
||||||
|
{ id: 30, name: 'locality name', abbr: 'locality abbr'}, |
||||||
|
{ id: 31, name: 'locality name 2'} |
||||||
|
], |
||||||
|
localadmin: [ |
||||||
|
{ id: 40, name: 'localadmin name', abbr: 'localadmin abbr'}, |
||||||
|
{ id: 41, name: 'localadmin name 2'} |
||||||
|
], |
||||||
|
county: [ |
||||||
|
{ id: 50, name: 'county name', abbr: 'county abbr'}, |
||||||
|
{ id: 51, name: 'county name 2'} |
||||||
|
], |
||||||
|
macrocounty: [ |
||||||
|
{ id: 60, name: 'macrocounty name', abbr: 'macrocounty abbr'}, |
||||||
|
{ id: 61, name: 'macrocounty name 2'} |
||||||
|
], |
||||||
|
region: [ |
||||||
|
{ id: 70, name: 'region name', abbr: 'region abbr'}, |
||||||
|
{ id: 71, name: 'region name 2'} |
||||||
|
], |
||||||
|
macroregion: [ |
||||||
|
{ id: 80, name: 'macroregion name', abbr: 'macroregion abbr'}, |
||||||
|
{ id: 81, name: 'macroregion name 2'} |
||||||
|
], |
||||||
|
dependency: [ |
||||||
|
{ id: 90, name: 'dependency name', abbr: 'dependency abbr'}, |
||||||
|
{ id: 91, name: 'dependency name 2'} |
||||||
|
], |
||||||
|
country: [ |
||||||
|
{ id: 100, name: 'country name', abbr: 'xyz'}, |
||||||
|
{ id: 101, name: 'country name 2'} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
callback(undefined, results); |
||||||
|
}; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const should_execute = () => { return true; }; |
||||||
|
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(service, should_execute); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['neighbourhood'], |
||||||
|
point: { |
||||||
|
lat: 12.121212, |
||||||
|
lon: 21.212121 |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const res = { }; |
||||||
|
|
||||||
|
// verify that next was called
|
||||||
|
let next_was_called = false; |
||||||
|
const next = () => { |
||||||
|
next_was_called = true; |
||||||
|
}; |
||||||
|
|
||||||
|
controller(req, res, next); |
||||||
|
|
||||||
|
const expected = { |
||||||
|
meta: {}, |
||||||
|
data: [] |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(res, expected); |
||||||
|
|
||||||
|
t.notOk(logger.hasErrorMessages()); |
||||||
|
t.ok(next_was_called); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
|
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`GET /coarse_reverse ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( const testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,60 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
const has_request_errors = require('../../../../controller/predicates/has_request_errors'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
t.equal(typeof has_request_errors, 'function', 'has_request_errors is a function'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.true_conditions = (test, common) => { |
||||||
|
test('request with non-empty errors should return true', (t) => { |
||||||
|
const req = { |
||||||
|
errors: ['error'] |
||||||
|
}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
t.ok(has_request_errors(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.false_conditions = (test, common) => { |
||||||
|
test('response with undefined errors should return false', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
t.notOk(has_request_errors(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('response with empty errors array should return false', (t) => { |
||||||
|
const req = { |
||||||
|
errors: [] |
||||||
|
}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
t.notOk(has_request_errors(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`GET /has_request_errors ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( const testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,60 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
const has_response_data = require('../../../../controller/predicates/has_response_data'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
t.equal(typeof has_response_data, 'function', 'has_response_data is a function'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.true_conditions = (test, common) => { |
||||||
|
test('response with non-empty data should return true', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [1] |
||||||
|
}; |
||||||
|
|
||||||
|
t.ok(has_response_data(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.false_conditions = (test, common) => { |
||||||
|
test('response with undefined data should return true', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
t.notOk(has_response_data(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('response with empty data array should return true', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [] |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(has_response_data(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`GET /has_response_data ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( const testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,128 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
const is_coarse_reverse = require('../../../../controller/predicates/is_coarse_reverse'); |
||||||
|
|
||||||
|
const coarse_layers = [ |
||||||
|
'continent', |
||||||
|
'country', |
||||||
|
'dependency', |
||||||
|
'macroregion', |
||||||
|
'region', |
||||||
|
'locality', |
||||||
|
'localadmin', |
||||||
|
'macrocounty', |
||||||
|
'county', |
||||||
|
'macrohood', |
||||||
|
'borough', |
||||||
|
'neighbourhood', |
||||||
|
'microhood', |
||||||
|
'disputed' |
||||||
|
]; |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
t.equal(typeof is_coarse_reverse, 'function', 'is_coarse_reverse is a function'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.false_conditions = (test, common) => { |
||||||
|
test('request without layers should return false', (t) => { |
||||||
|
const req = { |
||||||
|
clean: {} |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(is_coarse_reverse(req)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('request with empty layers should return false', (t) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: [] |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(is_coarse_reverse(req)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('request with layers just "address" or "venue" return false', (t) => { |
||||||
|
['address', 'street', 'venue'].forEach((non_coarse_layer) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: [non_coarse_layer] |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(is_coarse_reverse(req)); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('request with layers containing "address" or "venue" and a coarse layer should return false', (t) => { |
||||||
|
['address', 'street', 'venue'].forEach((non_coarse_layer) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: [_.sample(coarse_layers), non_coarse_layer] |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(is_coarse_reverse(req)); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('request with layers containing "address" and "venue" should return false', (t) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: ['address', 'venue'] |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(is_coarse_reverse(req)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.true_conditions = (test, common) => { |
||||||
|
test('request with non-empty layers and not containing "address" or "venue" should return true', (t) => { |
||||||
|
coarse_layers.forEach((coarse_layer) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
layers: [coarse_layer] |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.ok(is_coarse_reverse(req)); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`GET /is_coarse_reverse ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( const testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,42 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
const is_pip_service_enabled = require('../../../../controller/predicates/is_pip_service_enabled'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
t.equal(typeof is_pip_service_enabled, 'function', 'is_pip_service_enabled is a function'); |
||||||
|
t.equal(typeof is_pip_service_enabled(), 'function', 'is_pip_service_enabled() is a function'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.true_conditions = (test, common) => { |
||||||
|
test('string uri should return true', (t) => { |
||||||
|
t.ok(is_pip_service_enabled('pip uri')()); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.false_conditions = (test, common) => { |
||||||
|
test('undefined uri should return false', (t) => { |
||||||
|
t.notOk(is_pip_service_enabled()()); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`GET /is_pip_service_enabled ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( const testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,68 @@ |
|||||||
|
module.exports = { |
||||||
|
'query': { |
||||||
|
'function_score': { |
||||||
|
'query': { |
||||||
|
'filtered': { |
||||||
|
'query': { |
||||||
|
'bool': { |
||||||
|
'should': [ |
||||||
|
{ |
||||||
|
'bool': { |
||||||
|
'_name': 'fallback.postalcode', |
||||||
|
'must': [ |
||||||
|
{ |
||||||
|
'multi_match': { |
||||||
|
'query': 'postalcode value', |
||||||
|
'type': 'phrase', |
||||||
|
'fields': [ |
||||||
|
'parent.postalcode' |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
'filter': { |
||||||
|
'term': { |
||||||
|
'layer': 'postalcode' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
'filter': { |
||||||
|
'bool': { |
||||||
|
'must': [] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
'max_boost': 20, |
||||||
|
'functions': [ |
||||||
|
{ |
||||||
|
'field_value_factor': { |
||||||
|
'modifier': 'log1p', |
||||||
|
'field': 'popularity', |
||||||
|
'missing': 1 |
||||||
|
}, |
||||||
|
'weight': 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
'field_value_factor': { |
||||||
|
'modifier': 'log1p', |
||||||
|
'field': 'population', |
||||||
|
'missing': 1 |
||||||
|
}, |
||||||
|
'weight': 2 |
||||||
|
} |
||||||
|
], |
||||||
|
'score_mode': 'avg', |
||||||
|
'boost_mode': 'multiply' |
||||||
|
} |
||||||
|
}, |
||||||
|
'size': 20, |
||||||
|
'track_scores': true, |
||||||
|
'sort': [ |
||||||
|
'_score' |
||||||
|
] |
||||||
|
}; |
@ -0,0 +1,322 @@ |
|||||||
|
|
||||||
|
var middleware = require('../../../middleware/requestLanguage'); |
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
var DEFAULTS = { |
||||||
|
defaulted: true, |
||||||
|
iso6391: 'en', |
||||||
|
iso6392B: 'eng', |
||||||
|
iso6392T: 'eng', |
||||||
|
iso6393: 'eng', |
||||||
|
name: 'English', |
||||||
|
scope: 'individual', |
||||||
|
type: 'living' |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.defaults = function(test, common) { |
||||||
|
test('default language', function(t) { |
||||||
|
|
||||||
|
var req = {}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, DEFAULTS, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, []); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
test('both querystring & header invalid', function(t) { |
||||||
|
|
||||||
|
var req = { |
||||||
|
headers: { 'accept-language': 'foobar' }, |
||||||
|
query: { 'lang': 'foobar' } |
||||||
|
}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, DEFAULTS, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, [ |
||||||
|
'invalid language provided via querystring', |
||||||
|
'invalid language provided via header' |
||||||
|
]); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.invalid = function(test, common) { |
||||||
|
test('headers: invalid language', function(t) { |
||||||
|
|
||||||
|
var req = { headers: { |
||||||
|
'accept-language': 'invalid language' |
||||||
|
}}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, DEFAULTS, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, [ |
||||||
|
'invalid language provided via header' |
||||||
|
]); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
test('query: invalid language', function(t) { |
||||||
|
|
||||||
|
var req = { query: { |
||||||
|
lang: 'invalid language' |
||||||
|
}}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, DEFAULTS, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, [ |
||||||
|
'invalid language provided via querystring' |
||||||
|
]); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.valid = function(test, common) { |
||||||
|
test('headers: valid language - french', function(t) { |
||||||
|
|
||||||
|
var req = { headers: { |
||||||
|
'accept-language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' |
||||||
|
}}; |
||||||
|
|
||||||
|
var expected = { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6392B: 'fre', |
||||||
|
iso6392T: 'fra', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French', |
||||||
|
scope: 'individual', |
||||||
|
type: 'living' |
||||||
|
}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, expected, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, []); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
test('query: valid language - french', function(t) { |
||||||
|
|
||||||
|
var req = { query: { |
||||||
|
lang: 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' |
||||||
|
}}; |
||||||
|
|
||||||
|
var expected = { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6392B: 'fre', |
||||||
|
iso6392T: 'fra', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French', |
||||||
|
scope: 'individual', |
||||||
|
type: 'living' |
||||||
|
}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, expected, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, []); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
test('headers: valid language - english', function(t) { |
||||||
|
|
||||||
|
var req = { headers: { |
||||||
|
'accept-language': 'en' |
||||||
|
}}; |
||||||
|
|
||||||
|
var expected = { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'en', |
||||||
|
iso6392B: 'eng', |
||||||
|
iso6392T: 'eng', |
||||||
|
iso6393: 'eng', |
||||||
|
name: 'English', |
||||||
|
scope: 'individual', |
||||||
|
type: 'living' |
||||||
|
}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, expected, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, []); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
test('query: valid language - english', function(t) { |
||||||
|
|
||||||
|
var req = { query: { |
||||||
|
lang: 'en' |
||||||
|
}}; |
||||||
|
|
||||||
|
var expected = { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'en', |
||||||
|
iso6392B: 'eng', |
||||||
|
iso6392T: 'eng', |
||||||
|
iso6393: 'eng', |
||||||
|
name: 'English', |
||||||
|
scope: 'individual', |
||||||
|
type: 'living' |
||||||
|
}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, expected, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, []); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.precedence = function(test, common) { |
||||||
|
test('precedence: query has precedence over headers', function(t) { |
||||||
|
|
||||||
|
var req = { |
||||||
|
headers: { 'accept-language': 'fr' }, |
||||||
|
query: { 'lang': 'es' } |
||||||
|
}; |
||||||
|
|
||||||
|
var expected = { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'es', |
||||||
|
iso6392B: 'spa', |
||||||
|
iso6392T: 'spa', |
||||||
|
iso6393: 'spa', |
||||||
|
name: 'Spanish', |
||||||
|
scope: 'individual', |
||||||
|
type: 'living' |
||||||
|
}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, expected, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, []); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
test('precedence: invalid querystring but valid header', function(t) { |
||||||
|
|
||||||
|
var req = { |
||||||
|
headers: { 'accept-language': 'fr' }, |
||||||
|
query: { 'lang': 'foobar' } |
||||||
|
}; |
||||||
|
|
||||||
|
var expected = { |
||||||
|
defaulted: false, |
||||||
|
iso6391: 'fr', |
||||||
|
iso6392B: 'fre', |
||||||
|
iso6392T: 'fra', |
||||||
|
iso6393: 'fra', |
||||||
|
name: 'French', |
||||||
|
scope: 'individual', |
||||||
|
type: 'living' |
||||||
|
}; |
||||||
|
|
||||||
|
middleware(req, {}, function () { |
||||||
|
t.deepEqual( req.language, expected, '$req.language set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.clean.lang, { |
||||||
|
defaulted: req.language.defaulted, |
||||||
|
iso6391: req.language.iso6391, |
||||||
|
iso6393: req.language.iso6393, |
||||||
|
name: req.language.name |
||||||
|
}, '$req.clean.lang set' ); |
||||||
|
|
||||||
|
t.deepEqual( req.warnings, [ |
||||||
|
'invalid language provided via querystring' |
||||||
|
]); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = function (tape, common) { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape('[middleware] requestLanguage: ' + name, testFunction); |
||||||
|
} |
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,107 @@ |
|||||||
|
|
||||||
|
var fs = require('fs'), |
||||||
|
tmp = require('tmp'), |
||||||
|
setup = require('../../../service/language').findById; |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = function(test, common) { |
||||||
|
test('valid interface', function(t) { |
||||||
|
t.equal(typeof setup, 'function', 'setup is a function'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
// adapter factory
|
||||||
|
module.exports.tests.factory = function(test, common) { |
||||||
|
|
||||||
|
test('http adapter', function(t) { |
||||||
|
var config = { language: { client: { |
||||||
|
adapter: 'http', |
||||||
|
host: 'http://example.com' |
||||||
|
}}}; |
||||||
|
|
||||||
|
// adapter is driven by config
|
||||||
|
var tmpfile = tmp.tmpNameSync({ postfix: '.json' }); |
||||||
|
fs.writeFileSync( tmpfile, JSON.stringify( config ), { encoding: 'utf8' } ); |
||||||
|
process.env.PELIAS_CONFIG = tmpfile; |
||||||
|
var adapter = setup(); |
||||||
|
delete process.env.PELIAS_CONFIG; |
||||||
|
|
||||||
|
t.equal(adapter.constructor.name, 'HttpTransport', 'HttpTransport'); |
||||||
|
t.equal(typeof adapter, 'object', 'adapter is an object'); |
||||||
|
t.equal(typeof adapter.query, 'function', 'query is a function'); |
||||||
|
t.equal(adapter.query.length, 2, 'query function signature'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
test('null adapter', function(t) { |
||||||
|
var config = { language: { client: { |
||||||
|
adapter: 'null' |
||||||
|
}}}; |
||||||
|
|
||||||
|
// adapter is driven by config
|
||||||
|
var tmpfile = tmp.tmpNameSync({ postfix: '.json' }); |
||||||
|
fs.writeFileSync( tmpfile, JSON.stringify( config ), { encoding: 'utf8' } ); |
||||||
|
process.env.PELIAS_CONFIG = tmpfile; |
||||||
|
var adapter = setup(); |
||||||
|
delete process.env.PELIAS_CONFIG; |
||||||
|
|
||||||
|
t.equal(adapter.constructor.name, 'NullTransport', 'NullTransport'); |
||||||
|
t.equal(typeof adapter, 'object', 'adapter is an object'); |
||||||
|
t.equal(typeof adapter.query, 'function', 'query is a function'); |
||||||
|
t.equal(adapter.query.length, 2, 'query function signature'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
test('default adapter', function(t) { |
||||||
|
var config = {}; |
||||||
|
|
||||||
|
// adapter is driven by config
|
||||||
|
var tmpfile = tmp.tmpNameSync({ postfix: '.json' }); |
||||||
|
fs.writeFileSync( tmpfile, JSON.stringify( config ), { encoding: 'utf8' } ); |
||||||
|
process.env.PELIAS_CONFIG = tmpfile; |
||||||
|
var adapter = setup(); |
||||||
|
delete process.env.PELIAS_CONFIG; |
||||||
|
|
||||||
|
t.equal(adapter.constructor.name, 'NullTransport', 'NullTransport'); |
||||||
|
t.equal(typeof adapter, 'object', 'adapter is an object'); |
||||||
|
t.equal(typeof adapter.query, 'function', 'query is a function'); |
||||||
|
t.equal(adapter.query.length, 2, 'query function signature'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
// null transport
|
||||||
|
module.exports.tests.NullTransport = function(test, common) { |
||||||
|
|
||||||
|
test('null transport', function(t) { |
||||||
|
|
||||||
|
// adapter is driven by config
|
||||||
|
var tmpfile = tmp.tmpNameSync({ postfix: '.json' }); |
||||||
|
fs.writeFileSync( tmpfile, '{}', { encoding: 'utf8' } ); |
||||||
|
process.env.PELIAS_CONFIG = tmpfile; |
||||||
|
var adapter = setup(); |
||||||
|
delete process.env.PELIAS_CONFIG; |
||||||
|
|
||||||
|
// test null transport performs a no-op
|
||||||
|
adapter.query( null, function( err, res ){ |
||||||
|
t.equal(err, undefined, 'no-op'); |
||||||
|
t.equal(res, undefined, 'no-op'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = function (tape, common) { |
||||||
|
|
||||||
|
function test(name, testFunction) { |
||||||
|
return tape('SERVICE language', testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,169 @@ |
|||||||
|
const proxyquire = require('proxyquire').noCallThru(); |
||||||
|
|
||||||
|
const setup = require('../../../service/pointinpolygon'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
var service = proxyquire('../../../service/pointinpolygon', { |
||||||
|
'pelias-logger': logger |
||||||
|
}); |
||||||
|
|
||||||
|
t.equal(typeof service, 'function', 'service is a function'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.do_nothing_service = (test, common) => { |
||||||
|
test('undefined PiP uri should return service that logs fact that PiP service is not available', (t) => { |
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const service = proxyquire('../../../service/pointinpolygon', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(); |
||||||
|
|
||||||
|
service({ lat: 12.121212, lon: 21.212121 }, (err) => { |
||||||
|
t.deepEquals(logger.getWarnMessages(), [ |
||||||
|
'point-in-polygon service disabled' |
||||||
|
]); |
||||||
|
t.equals(err, 'point-in-polygon service disabled, unable to resolve {"lat":12.121212,"lon":21.212121}'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.success = (test, common) => { |
||||||
|
test('lat and lon should be passed to server', (t) => { |
||||||
|
const pipServer = require('express')(); |
||||||
|
pipServer.get('/:lon/:lat', (req, res, next) => { |
||||||
|
t.equals(req.params.lat, '12.121212'); |
||||||
|
t.equals(req.params.lon, '21.212121'); |
||||||
|
|
||||||
|
res.send('{ "field": "value" }'); |
||||||
|
}); |
||||||
|
|
||||||
|
const server = pipServer.listen(); |
||||||
|
const port = server.address().port; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const service = proxyquire('../../../service/pointinpolygon', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(`http://localhost:${port}`); |
||||||
|
|
||||||
|
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||||
|
t.notOk(err); |
||||||
|
t.deepEquals(results, { field: 'value' }); |
||||||
|
|
||||||
|
t.ok(logger.isInfoMessage(`using point-in-polygon service at http://localhost:${port}`)); |
||||||
|
t.notOk(logger.hasErrorMessages()); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
|
||||||
|
server.close(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.failure = (test, common) => { |
||||||
|
test('server returning success but non-JSON body should log error and return no results', (t) => { |
||||||
|
const pipServer = require('express')(); |
||||||
|
pipServer.get('/:lon/:lat', (req, res, next) => { |
||||||
|
t.equals(req.params.lat, '12.121212'); |
||||||
|
t.equals(req.params.lon, '21.212121'); |
||||||
|
|
||||||
|
res.send('this is not JSON'); |
||||||
|
}); |
||||||
|
|
||||||
|
const server = pipServer.listen(); |
||||||
|
const port = server.address().port; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const service = proxyquire('../../../service/pointinpolygon', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(`http://localhost:${port}`); |
||||||
|
|
||||||
|
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||||
|
t.equals(err, `http://localhost:${port}/21.212121/12.121212 returned status 200 but with non-JSON response: this is not JSON`); |
||||||
|
t.notOk(results); |
||||||
|
t.ok(logger.isErrorMessage(`http://localhost:${port}/21.212121/12.121212: could not parse response body: this is not JSON`)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
server.close(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('server returning error should log it and return no results', (t) => { |
||||||
|
const server = require('express')().listen(); |
||||||
|
const port = server.address().port; |
||||||
|
|
||||||
|
// immediately close the server so to ensure an error response
|
||||||
|
server.close(); |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const service = proxyquire('../../../service/pointinpolygon', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(`http://localhost:${port}`); |
||||||
|
|
||||||
|
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||||
|
t.equals(err.code, 'ECONNREFUSED'); |
||||||
|
t.notOk(results); |
||||||
|
t.ok(logger.isErrorMessage(/ECONNREFUSED/), 'there should be a connection refused error message'); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
server.close(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('non-OK status should log error and return no results', (t) => { |
||||||
|
const pipServer = require('express')(); |
||||||
|
pipServer.get('/:lat/:lon', (req, res, next) => { |
||||||
|
res.status(400).send('a bad request was made'); |
||||||
|
}); |
||||||
|
|
||||||
|
const server = pipServer.listen(); |
||||||
|
const port = server.address().port; |
||||||
|
|
||||||
|
const logger = require('pelias-mock-logger')(); |
||||||
|
|
||||||
|
const service = proxyquire('../../../service/pointinpolygon', { |
||||||
|
'pelias-logger': logger |
||||||
|
})(`http://localhost:${port}`); |
||||||
|
|
||||||
|
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||||
|
t.equals(err, `http://localhost:${port}/21.212121/12.121212 returned status 400: a bad request was made`); |
||||||
|
t.notOk(results); |
||||||
|
t.ok(logger.isErrorMessage(`http://localhost:${port}/21.212121/12.121212 returned status 400: a bad request was made`)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
server.close(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`SERVICE /pointinpolygon ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
Loading…
Reference in new issue