mirror of https://github.com/pelias/api.git
Peter Johnson
10 years ago
36 changed files with 1074 additions and 115 deletions
@ -0,0 +1,55 @@
|
||||
# API |
||||
|
||||
Pelias RESTful API |
||||
|
||||
## Documentation |
||||
|
||||
[API Documentation](https://github.com/pelias/api/tree/master/docs) |
||||
|
||||
## Install Dependencies |
||||
|
||||
```bash |
||||
$ npm install |
||||
``` |
||||
|
||||
## Contributing |
||||
|
||||
Please fork and pull request against upstream master on a feature branch. |
||||
|
||||
Pretty please; provide unit tests and script fixtures in the `test` directory. |
||||
|
||||
### Start Server |
||||
|
||||
```bash |
||||
$ npm start |
||||
``` |
||||
|
||||
### Running Unit Tests |
||||
|
||||
```bash |
||||
$ npm run unit |
||||
``` |
||||
|
||||
### Running Functional Tests |
||||
|
||||
```bash |
||||
$ npm run ciao |
||||
``` |
||||
|
||||
### Running All Tests |
||||
|
||||
```bash |
||||
$ npm test |
||||
``` |
||||
|
||||
### Generate API Documentation |
||||
|
||||
```bash |
||||
$ npm run docs |
||||
``` |
||||
|
||||
### Continuous Integration |
||||
|
||||
Travis tests every release against node version `0.10` |
||||
|
||||
[![Build Status](https://travis-ci.org/pelias/api.png?branch=master)](https://travis-ci.org/pelias/api) |
@ -0,0 +1,35 @@
|
||||
|
||||
var app = require('express')(); |
||||
|
||||
/** ----------------------- middleware ----------------------- **/ |
||||
|
||||
app.use( require('./middleware/toobusy') ); // should be first
|
||||
app.use( require('./middleware/headers') ); |
||||
app.use( require('./middleware/cors') ); |
||||
app.use( require('./middleware/jsonp') ); |
||||
|
||||
/** ----------------------- sanitisers ----------------------- **/ |
||||
|
||||
var sanitisers = {}; |
||||
sanitisers.suggest = require('./sanitiser/suggest'); |
||||
|
||||
/** ----------------------- controllers ----------------------- **/ |
||||
|
||||
var controllers = {}; |
||||
controllers.index = require('./controller/index'); |
||||
controllers.suggest = require('./controller/suggest'); |
||||
|
||||
/** ----------------------- routes ----------------------- **/ |
||||
|
||||
// api root
|
||||
app.get( '/', controllers.index() ); |
||||
|
||||
// suggest API
|
||||
app.get( '/suggest', sanitisers.suggest.middleware, controllers.suggest() ); |
||||
|
||||
/** ----------------------- error middleware ----------------------- **/ |
||||
|
||||
app.use( require('./middleware/404') ); |
||||
app.use( require('./middleware/500') ); |
||||
|
||||
module.exports = app; |
@ -1,13 +1,22 @@
|
||||
|
||||
var pkg = require('../package'); |
||||
|
||||
function controller( req, res ){ |
||||
res.json({ |
||||
name: pkg.name, |
||||
version: { |
||||
number: pkg.version |
||||
} |
||||
}); |
||||
function setup(){ |
||||
|
||||
function controller( req, res, next ){ |
||||
|
||||
// stats
|
||||
res.json({ |
||||
name: pkg.name, |
||||
version: { |
||||
number: pkg.version |
||||
} |
||||
}); |
||||
|
||||
} |
||||
|
||||
return controller; |
||||
|
||||
} |
||||
|
||||
module.exports = controller; |
||||
module.exports = setup; |
@ -1,32 +1,41 @@
|
||||
|
||||
var logger = require('../src/logger'), |
||||
responder = require('../src/responder'), |
||||
query = require('../query/suggest'), |
||||
backend = require('../src/backend'); |
||||
function setup( backend, query ){ |
||||
|
||||
module.exports = function( req, res, next ){ |
||||
// allow overriding of dependencies
|
||||
backend = backend || require('../src/backend'); |
||||
query = query || require('../query/suggest'); |
||||
|
||||
var reply = { |
||||
date: new Date().getTime(), |
||||
body: [] |
||||
}; |
||||
function controller( req, res, next ){ |
||||
|
||||
var cmd = { |
||||
index: 'pelias', |
||||
body: query( req.clean ) // generate query from clean params
|
||||
}; |
||||
// backend command
|
||||
var cmd = { |
||||
index: 'pelias', |
||||
body: query( req.clean ) |
||||
}; |
||||
|
||||
// Proxy request to ES backend & map response to a valid FeatureCollection
|
||||
backend().client.suggest( cmd, function( err, data ){ |
||||
// query backend
|
||||
backend().client.suggest( cmd, function( err, data ){ |
||||
|
||||
if( err ){ return responder.error( req, res, next, err ); } |
||||
if( data && data.pelias && data.pelias.length ){ |
||||
var docs = []; |
||||
|
||||
// map options to reply body
|
||||
reply.body = data['pelias'][0].options; |
||||
} |
||||
// handle backend errors
|
||||
if( err ){ return next( err ); } |
||||
|
||||
return responder.cors( req, res, reply ); |
||||
}); |
||||
// map response to a valid FeatureCollection
|
||||
if( data && Array.isArray( data.pelias ) && data.pelias.length ){ |
||||
docs = data['pelias'][0].options || []; |
||||
} |
||||
|
||||
}; |
||||
// respond
|
||||
return res.status(200).json({ |
||||
date: new Date().getTime(), |
||||
body: docs |
||||
}); |
||||
}); |
||||
|
||||
} |
||||
|
||||
return controller; |
||||
} |
||||
|
||||
module.exports = setup; |
@ -0,0 +1,63 @@
|
||||
# invalid path |
||||
|
||||
*Generated: Fri Sep 12 2014 20:51:44 GMT+0100 (BST)* |
||||
## Request |
||||
```javascript |
||||
{ |
||||
"protocol": "http:", |
||||
"host": "localhost", |
||||
"method": "GET", |
||||
"port": 3100, |
||||
"path": "/notexist" |
||||
} |
||||
``` |
||||
|
||||
## Response |
||||
```javascript |
||||
Status: 404 |
||||
{ |
||||
"x-powered-by": "mapzen", |
||||
"charset": "utf8", |
||||
"access-control-allow-origin": "*", |
||||
"access-control-allow-methods": "GET", |
||||
"access-control-allow-headers": "X-Requested-With,content-type", |
||||
"access-control-allow-credentials": "true", |
||||
"server": "Pelias/0.0.0", |
||||
"cache-control": "public,max-age=300", |
||||
"content-type": "application/json; charset=utf-8", |
||||
"content-length": "35", |
||||
"etag": "W/\"23-dfdfa185\"", |
||||
"date": "Fri, 12 Sep 2014 19:51:44 GMT", |
||||
"connection": "close" |
||||
} |
||||
``` |
||||
```javascript |
||||
{ |
||||
"error": "not found: invalid path" |
||||
} |
||||
``` |
||||
|
||||
## Tests |
||||
|
||||
### ✓ cache-control header correctly set |
||||
```javascript |
||||
response.should.have.header 'Cache-Control','public,max-age=300' |
||||
``` |
||||
|
||||
### ✓ content-type header correctly set |
||||
```javascript |
||||
response.should.have.header 'Content-Type','application/json; charset=utf-8' |
||||
``` |
||||
|
||||
### ✓ not found |
||||
```javascript |
||||
response.statusCode.should.equal 404 |
||||
``` |
||||
|
||||
### ✓ should respond in json with server info |
||||
```javascript |
||||
should.exist json |
||||
should.exist json.error |
||||
json.error.should.equal 'not found: invalid path' |
||||
``` |
||||
|
@ -0,0 +1,52 @@
|
||||
# cross-origin resource sharing |
||||
|
||||
*Generated: Fri Sep 12 2014 20:51:44 GMT+0100 (BST)* |
||||
## Request |
||||
```javascript |
||||
{ |
||||
"protocol": "http:", |
||||
"host": "localhost", |
||||
"method": "GET", |
||||
"port": 3100, |
||||
"path": "/" |
||||
} |
||||
``` |
||||
|
||||
## Response |
||||
```javascript |
||||
Status: 200 |
||||
{ |
||||
"x-powered-by": "mapzen", |
||||
"charset": "utf8", |
||||
"access-control-allow-origin": "*", |
||||
"access-control-allow-methods": "GET", |
||||
"access-control-allow-headers": "X-Requested-With,content-type", |
||||
"access-control-allow-credentials": "true", |
||||
"server": "Pelias/0.0.0", |
||||
"cache-control": "public,max-age=60", |
||||
"content-type": "application/json; charset=utf-8", |
||||
"content-length": "50", |
||||
"etag": "W/\"32-85536434\"", |
||||
"date": "Fri, 12 Sep 2014 19:51:44 GMT", |
||||
"connection": "close" |
||||
} |
||||
``` |
||||
```javascript |
||||
{ |
||||
"name": "pelias-api", |
||||
"version": { |
||||
"number": "0.0.0" |
||||
} |
||||
} |
||||
``` |
||||
|
||||
## Tests |
||||
|
||||
### ✓ access control headers correctly set |
||||
```javascript |
||||
response.should.have.header 'Access-Control-Allow-Origin','*' |
||||
response.should.have.header 'Access-Control-Allow-Methods','GET' |
||||
response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' |
||||
response.should.have.header 'Access-Control-Allow-Credentials','true' |
||||
``` |
||||
|
@ -0,0 +1,82 @@
|
||||
# api root |
||||
|
||||
*Generated: Fri Sep 12 2014 20:51:45 GMT+0100 (BST)* |
||||
## Request |
||||
```javascript |
||||
{ |
||||
"protocol": "http:", |
||||
"host": "localhost", |
||||
"method": "GET", |
||||
"port": 3100, |
||||
"path": "/" |
||||
} |
||||
``` |
||||
|
||||
## Response |
||||
```javascript |
||||
Status: 200 |
||||
{ |
||||
"x-powered-by": "mapzen", |
||||
"charset": "utf8", |
||||
"access-control-allow-origin": "*", |
||||
"access-control-allow-methods": "GET", |
||||
"access-control-allow-headers": "X-Requested-With,content-type", |
||||
"access-control-allow-credentials": "true", |
||||
"server": "Pelias/0.0.0", |
||||
"cache-control": "public,max-age=60", |
||||
"content-type": "application/json; charset=utf-8", |
||||
"content-length": "50", |
||||
"etag": "W/\"32-85536434\"", |
||||
"date": "Fri, 12 Sep 2014 19:51:44 GMT", |
||||
"connection": "close" |
||||
} |
||||
``` |
||||
```javascript |
||||
{ |
||||
"name": "pelias-api", |
||||
"version": { |
||||
"number": "0.0.0" |
||||
} |
||||
} |
||||
``` |
||||
|
||||
## Tests |
||||
|
||||
### ✓ content-type header correctly set |
||||
```javascript |
||||
response.should.have.header 'Content-Type','application/json; charset=utf-8' |
||||
``` |
||||
|
||||
### ✓ endpoint available |
||||
```javascript |
||||
response.statusCode.should.equal 200 |
||||
``` |
||||
|
||||
### ✓ cache-control header correctly set |
||||
```javascript |
||||
response.should.have.header 'Cache-Control','public,max-age=60' |
||||
``` |
||||
|
||||
### ✓ charset header correctly set |
||||
```javascript |
||||
response.should.have.header 'Charset','utf8' |
||||
``` |
||||
|
||||
### ✓ server header correctly set |
||||
```javascript |
||||
response.should.have.header 'Server' |
||||
response.headers.server.should.match /Pelias\/\d{1,2}\.\d{1,2}\.\d{1,2}/ |
||||
``` |
||||
|
||||
### ✓ vanity header correctly set |
||||
```javascript |
||||
response.should.have.header 'X-Powered-By','mapzen' |
||||
``` |
||||
|
||||
### ✓ should respond in json with server info |
||||
```javascript |
||||
should.exist json |
||||
should.exist json.name |
||||
should.exist json.version |
||||
``` |
||||
|
@ -0,0 +1,50 @@
|
||||
# jsonp |
||||
|
||||
*Generated: Fri Sep 12 2014 20:51:45 GMT+0100 (BST)* |
||||
## Request |
||||
```javascript |
||||
{ |
||||
"protocol": "http:", |
||||
"host": "localhost", |
||||
"method": "GET", |
||||
"port": 3100, |
||||
"path": "/?callback=test" |
||||
} |
||||
``` |
||||
|
||||
## Response |
||||
```javascript |
||||
Status: 200 |
||||
{ |
||||
"x-powered-by": "mapzen", |
||||
"charset": "utf8", |
||||
"access-control-allow-origin": "*", |
||||
"access-control-allow-methods": "GET", |
||||
"access-control-allow-headers": "X-Requested-With,content-type", |
||||
"access-control-allow-credentials": "true", |
||||
"server": "Pelias/0.0.0", |
||||
"cache-control": "public,max-age=60", |
||||
"content-type": "application/javascript; charset=utf-8", |
||||
"content-length": "57", |
||||
"etag": "W/\"39-b8a2aba1\"", |
||||
"date": "Fri, 12 Sep 2014 19:51:44 GMT", |
||||
"connection": "close" |
||||
} |
||||
``` |
||||
```html |
||||
test({"name":"pelias-api","version":{"number":"0.0.0"}}); |
||||
``` |
||||
|
||||
## Tests |
||||
|
||||
### ✓ should respond with jsonp |
||||
```javascript |
||||
should.exist response.body |
||||
response.body.substr(0,5).should.equal 'test('; |
||||
``` |
||||
|
||||
### ✓ content-type header correctly set |
||||
```javascript |
||||
response.should.have.header 'Content-Type','application/javascript; charset=utf-8' |
||||
``` |
||||
|
@ -1,14 +0,0 @@
|
||||
|
||||
var express = require('express'), |
||||
app = express(); |
||||
|
||||
// middleware modules
|
||||
// app.use( require('cookie-parser')() );
|
||||
|
||||
// enable client-side caching of 60s by default
|
||||
app.use(function(req, res, next){ |
||||
res.header('Cache-Control','public,max-age=60'); |
||||
next(); |
||||
}); |
||||
|
||||
module.exports = app; |
@ -1,10 +1,17 @@
|
||||
|
||||
var app = require('./express'); |
||||
var cluster = require('cluster'), |
||||
app = require('./app'), |
||||
multicore = false, |
||||
port = ( process.env.PORT || 3100 ); |
||||
|
||||
// api root
|
||||
app.get( '/', require('./controller/index') ); |
||||
|
||||
// suggest API
|
||||
app.get( '/suggest', require('./sanitiser/suggest'), require('./controller/suggest') ); |
||||
|
||||
app.listen( process.env.PORT || 3100 ); |
||||
/** cluster webserver across all cores **/ |
||||
if( multicore ){ |
||||
// @todo: not finished yet
|
||||
// cluster(app)
|
||||
// .use(cluster.stats())
|
||||
// .listen( process.env.PORT || 3100 );
|
||||
} |
||||
else { |
||||
console.log( 'listening on ' + port ); |
||||
app.listen( process.env.PORT || 3100 ); |
||||
} |
@ -0,0 +1,8 @@
|
||||
|
||||
// handle not found errors
|
||||
function middleware(req, res) { |
||||
res.header('Cache-Control','public,max-age=300'); // 5 minute cache
|
||||
res.status(404).json({ error: 'not found: invalid path' }); |
||||
} |
||||
|
||||
module.exports = middleware; |
@ -0,0 +1,9 @@
|
||||
|
||||
// handle application errors
|
||||
function middleware(err, req, res, next) { |
||||
res.header('Cache-Control','no-cache'); |
||||
if( res.statusCode < 400 ){ res.status(500); } |
||||
res.json({ error: err }); |
||||
} |
||||
|
||||
module.exports = middleware; |
@ -0,0 +1,10 @@
|
||||
|
||||
function middleware(req, res, next){ |
||||
res.header('Access-Control-Allow-Origin', '*'); |
||||
res.header('Access-Control-Allow-Methods', 'GET'); |
||||
res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type'); |
||||
res.header('Access-Control-Allow-Credentials', true); |
||||
next(); |
||||
} |
||||
|
||||
module.exports = middleware; |
@ -0,0 +1,12 @@
|
||||
|
||||
var pkg = require('../package'); |
||||
|
||||
function middleware(req, res, next){ |
||||
res.header('Charset','utf8'); |
||||
res.header('Cache-Control','public,max-age=60'); |
||||
res.header('Server', 'Pelias/'+pkg.version); |
||||
res.header('X-Powered-By', 'mapzen'); |
||||
next(); |
||||
} |
||||
|
||||
module.exports = middleware; |
@ -0,0 +1,24 @@
|
||||
|
||||
function middleware(req, res, next){ |
||||
|
||||
// store old json function
|
||||
var json = res.json.bind(res); |
||||
|
||||
// replace with jsonp aware function
|
||||
res.json = function( data ){ |
||||
|
||||
// jsonp
|
||||
if( req.query && req.query.callback ){ |
||||
res.header('Content-type','application/javascript'); |
||||
return res.send( req.query.callback + '('+ JSON.stringify( data ) + ');' ); |
||||
} |
||||
|
||||
// regular json
|
||||
res.header('Content-type','application/json'); |
||||
return json( data ); |
||||
}; |
||||
|
||||
next(); |
||||
} |
||||
|
||||
module.exports = middleware; |
@ -0,0 +1,19 @@
|
||||
|
||||
// middleware which blocks requests when the eventloop is too busy
|
||||
var toobusy = require('toobusy'); |
||||
|
||||
function middleware(req, res, next){ |
||||
if( toobusy() ){ |
||||
res.status(503); // Service Unavailable
|
||||
return next('Server Overwhelmed'); |
||||
} |
||||
return next(); |
||||
} |
||||
|
||||
// calling .shutdown allows your process to exit normally
|
||||
process.on('SIGINT', function() { |
||||
toobusy.shutdown(); |
||||
process.exit(); |
||||
}); |
||||
|
||||
module.exports = middleware; |
@ -1,33 +0,0 @@
|
||||
|
||||
// send a reply that is capable of JSON, CORS and JSONP
|
||||
function cors( req, res, obj ){ |
||||
res.header('Charset','utf8'); |
||||
res.header('Cache-Control','public,max-age=60'); |
||||
res.header('Access-Control-Allow-Origin', '*'); |
||||
res.header('Access-Control-Allow-Methods', 'GET'); |
||||
res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type'); |
||||
res.header('Access-Control-Allow-Credentials', true); |
||||
res.header('X-Powered-By', 'pelias'); |
||||
|
||||
// jsonp
|
||||
if( req.query && req.query.callback ){ |
||||
res.header('Content-type','application/javascript'); |
||||
return res.send( req.query.callback + '('+ JSON.stringify( obj ) + ');' ); |
||||
} |
||||
|
||||
// regular json
|
||||
res.header('Content-type','application/json'); |
||||
return res.json( obj ); |
||||
} |
||||
|
||||
// send an error
|
||||
function error( req, res, next, err ){ |
||||
console.error( 'application error:', err ); |
||||
// mask error from user (contains paths)
|
||||
return cors( req, res, { error: 'application error' } ); |
||||
} |
||||
|
||||
module.exports = { |
||||
cors: cors, |
||||
error: error |
||||
}; |
@ -0,0 +1,17 @@
|
||||
|
||||
#> invalid path |
||||
path: '/notexist' |
||||
|
||||
#? not found |
||||
response.statusCode.should.equal 404 |
||||
|
||||
#? content-type header correctly set |
||||
response.should.have.header 'Content-Type','application/json; charset=utf-8' |
||||
|
||||
#? cache-control header correctly set |
||||
response.should.have.header 'Cache-Control','public,max-age=300' |
||||
|
||||
#? should respond in json with server info |
||||
should.exist json |
||||
should.exist json.error |
||||
json.error.should.equal 'not found: invalid path' |
@ -0,0 +1,9 @@
|
||||
|
||||
#> cross-origin resource sharing |
||||
path: '/' |
||||
|
||||
#? access control headers correctly set |
||||
response.should.have.header 'Access-Control-Allow-Origin','*' |
||||
response.should.have.header 'Access-Control-Allow-Methods','GET' |
||||
response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' |
||||
response.should.have.header 'Access-Control-Allow-Credentials','true' |
@ -0,0 +1,10 @@
|
||||
|
||||
#> jsonp |
||||
path: '/?callback=test' |
||||
|
||||
#? content-type header correctly set |
||||
response.should.have.header 'Content-Type','application/javascript; charset=utf-8' |
||||
|
||||
#? should respond with jsonp |
||||
should.exist response.body |
||||
response.body.substr(0,5).should.equal 'test('; |
@ -0,0 +1,15 @@
|
||||
|
||||
#> valid suggest query |
||||
path: '/suggest?input=a&lat=0&lon=0' |
||||
|
||||
#? 200 ok |
||||
response.statusCode.should.equal 200 |
||||
|
||||
#? valid response |
||||
now = new Date().getTime() |
||||
should.exist json |
||||
should.not.exist json.error |
||||
should.exist json.date |
||||
json.date.should.be.within now-1000, now+1000 |
||||
should.exist json.body |
||||
json.body.should.be.instanceof Array |
@ -0,0 +1,64 @@
|
||||
|
||||
var setup = require('../../../controller/suggest'), |
||||
mockBackend = require('../mock/backend'), |
||||
mockQuery = require('../mock/query'); |
||||
|
||||
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.equal(typeof setup(), 'function', 'setup returns a controller'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
// functionally test controller (backend success)
|
||||
module.exports.tests.functional_success = function(test, common) { |
||||
test('functional test', function(t) { |
||||
var backend = mockBackend( 'client/suggest/ok/1', function( cmd ){ |
||||
t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); |
||||
}); |
||||
var controller = setup( backend, mockQuery() ); |
||||
var res = { |
||||
status: function( code ){ |
||||
t.equal(code, 200, 'status set'); |
||||
return res; |
||||
}, |
||||
json: function( json ){ |
||||
t.equal(typeof json, 'object', 'returns json'); |
||||
t.equal(typeof json.date, 'number', 'date set'); |
||||
t.true(Array.isArray(json.body), 'body is array'); |
||||
t.deepEqual(json.body, [ { value: 1 }, { value: 2 } ], 'values correctly mapped'); |
||||
t.end(); |
||||
} |
||||
}; |
||||
controller( { clean: { a: 'b' } }, res ); |
||||
}); |
||||
}; |
||||
|
||||
// functionally test controller (backend failure)
|
||||
module.exports.tests.functional_failure = function(test, common) { |
||||
test('functional test', function(t) { |
||||
var backend = mockBackend( 'client/suggest/fail/1', function( cmd ){ |
||||
t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); |
||||
}); |
||||
var controller = setup( backend, mockQuery() ); |
||||
var next = function( message ){ |
||||
t.equal(message,'a backend error occurred','error passed to errorHandler'); |
||||
t.end(); |
||||
}; |
||||
controller( { clean: { a: 'b' } }, undefined, next ); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.all = function (tape, common) { |
||||
|
||||
function test(name, testFunction) { |
||||
return tape('GET /suggest ' + name, testFunction); |
||||
} |
||||
|
||||
for( var testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,28 @@
|
||||
|
||||
var responses = {}; |
||||
responses['client/suggest/ok/1'] = function( cmd, cb ){ |
||||
return cb( undefined, suggestEnvelope([ { value: 1 }, { value: 2 } ]) ); |
||||
}; |
||||
responses['client/suggest/fail/1'] = function( cmd, cb ){ |
||||
return cb( 'a backend error occurred' ); |
||||
}; |
||||
|
||||
function setup( key, cmdCb ){ |
||||
function backend( a, b ){ |
||||
return { |
||||
client: { |
||||
suggest: function( cmd, cb ){ |
||||
if( 'function' === typeof cmdCb ){ cmdCb( cmd ); } |
||||
return responses[key].apply( this, arguments ); |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
return backend; |
||||
} |
||||
|
||||
function suggestEnvelope( options ){ |
||||
return { pelias: [{ options: options }]}; |
||||
} |
||||
|
||||
module.exports = setup; |
@ -0,0 +1,10 @@
|
||||
|
||||
function setup(){ |
||||
return query; |
||||
} |
||||
|
||||
function query( clean ){ |
||||
return clean; |
||||
} |
||||
|
||||
module.exports = setup; |
@ -0,0 +1,23 @@
|
||||
|
||||
var indeces = require('../../../query/indeces'); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = function(test, common) { |
||||
test('valid interface', function(t) { |
||||
t.true(Array.isArray(indeces), 'valid array'); |
||||
t.equal(indeces.length, 7, 'valid array'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.all = function (tape, common) { |
||||
|
||||
function test(name, testFunction) { |
||||
return tape('indeces ' + name, testFunction); |
||||
} |
||||
|
||||
for( var testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,29 @@
|
||||
|
||||
var query = require('../../../query/suggest'); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = function(test, common) { |
||||
test('valid interface', function(t) { |
||||
t.equal(typeof query, 'function', 'valid function'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.interface = function(test, common) { |
||||
test('valid interface', function(t) { |
||||
t.equal(typeof query, 'function', 'valid function'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.all = function (tape, common) { |
||||
|
||||
function test(name, testFunction) { |
||||
return tape('suggest query ' + name, testFunction); |
||||
} |
||||
|
||||
for( var testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,203 @@
|
||||
|
||||
var sanitize = require('../../../sanitiser/suggest'), |
||||
defaultError = 'invalid param \'input\': text length, must be >0', |
||||
defaultClean = { input: 'test', lat: 0, layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood' ], lon: 0, size: 10, zoom: 10 }; |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = function(test, common) { |
||||
test('sanitize interface', function(t) { |
||||
t.equal(typeof sanitize, 'function', 'sanitize is a function'); |
||||
t.equal(sanitize.length, 2, 'sanitize interface'); |
||||
t.end(); |
||||
}); |
||||
test('middleware interface', function(t) { |
||||
t.equal(typeof sanitize.middleware, 'function', 'middleware is a function'); |
||||
t.equal(sanitize.middleware.length, 3, 'sanitize is valid middleware'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.sanitize_input = function(test, common) { |
||||
var inputs = { |
||||
invalid: [ '', 100, null, undefined, new Date() ], |
||||
valid: [ 'a', 'aa', 'aaaaaaaa' ] |
||||
}; |
||||
inputs.invalid.forEach( function( input ){ |
||||
test('invalid input', function(t) { |
||||
sanitize({ input: input, lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.equal(err, 'invalid param \'input\': text length, must be >0', 'invalid input'); |
||||
t.equal(clean, undefined, 'clean not set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}); |
||||
inputs.valid.forEach( function( input ){ |
||||
test('valid input', function(t) { |
||||
sanitize({ input: input, lat: 0, lon: 0 }, function( err, clean ){ |
||||
var expected = JSON.parse(JSON.stringify( defaultClean )); |
||||
expected.input = input; |
||||
t.equal(err, undefined, 'no error'); |
||||
t.deepEqual(clean, expected, 'clean set correctly'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.sanitize_lat = function(test, common) { |
||||
var lats = { |
||||
invalid: [ -1, -45, -90, 91, 120, 181 ], |
||||
valid: [ 0, 45, 90, -0, '0', '45', '90' ] |
||||
}; |
||||
lats.invalid.forEach( function( lat ){ |
||||
test('invalid lat', function(t) { |
||||
sanitize({ input: 'test', lat: lat, lon: 0 }, function( err, clean ){ |
||||
t.equal(err, 'invalid param \'lat\': must be >0 and <90', 'invalid latitude'); |
||||
t.equal(clean, undefined, 'clean not set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}); |
||||
lats.valid.forEach( function( lat ){ |
||||
test('valid lat', function(t) { |
||||
sanitize({ input: 'test', lat: lat, lon: 0 }, function( err, clean ){ |
||||
var expected = JSON.parse(JSON.stringify( defaultClean )); |
||||
expected.lat = parseFloat( lat ); |
||||
t.equal(err, undefined, 'no error'); |
||||
t.deepEqual(clean, expected, 'clean set correctly'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.sanitize_lon = function(test, common) { |
||||
var lons = { |
||||
invalid: [ -360, -181, 181, 360 ], |
||||
valid: [ -180, -1, -0, 0, 45, 90, '-180', '0', '180' ] |
||||
}; |
||||
lons.invalid.forEach( function( lon ){ |
||||
test('invalid lon', function(t) { |
||||
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){ |
||||
t.equal(err, 'invalid param \'lon\': must be >-180 and <180', 'invalid longitude'); |
||||
t.equal(clean, undefined, 'clean not set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}); |
||||
lons.valid.forEach( function( lon ){ |
||||
test('valid lon', function(t) { |
||||
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){ |
||||
var expected = JSON.parse(JSON.stringify( defaultClean )); |
||||
expected.lon = parseFloat( lon ); |
||||
t.equal(err, undefined, 'no error'); |
||||
t.deepEqual(clean, expected, 'clean set correctly'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.sanitize_zoom = function(test, common) { |
||||
test('invalid zoom value', function(t) { |
||||
sanitize({ zoom: 'a', input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.equal(clean.zoom, 10, 'default zoom set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
test('below min zoom value', function(t) { |
||||
sanitize({ zoom: -100, input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.equal(clean.zoom, 1, 'min zoom set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
test('above max zoom value', function(t) { |
||||
sanitize({ zoom: 9999, input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.equal(clean.zoom, 18, 'max zoom set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.sanitize_size = function(test, common) { |
||||
test('invalid size value', function(t) { |
||||
sanitize({ size: 'a', input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.equal(clean.size, 10, 'default size set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
test('below min size value', function(t) { |
||||
sanitize({ size: -100, input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.equal(clean.size, 1, 'min size set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
test('above max size value', function(t) { |
||||
sanitize({ size: 9999, input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.equal(clean.size, 40, 'max size set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.sanitize_layers = function(test, common) { |
||||
test('unspecified', function(t) { |
||||
sanitize({ layers: undefined, input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
t.deepEqual(clean.layers, defaultClean.layers, 'default layers set'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
test('invalid layer', function(t) { |
||||
sanitize({ layers: 'test_layer', input: 'test', lat: 0, lon: 0 }, function( err, clean ){ |
||||
var msg = 'invalid param \'layer\': must be one or more of geoname,osmnode,osmway,admin0,admin1,admin2,neighborhood'; |
||||
t.equal(err, msg, 'invalid layer requested'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.invalid_params = function(test, common) { |
||||
test('invalid input params', function(t) { |
||||
sanitize( undefined, function( err, clean ){ |
||||
t.equal(err, defaultError, 'handle invalid params gracefully'); |
||||
t.end(); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.middleware_failure = function(test, common) { |
||||
test('middleware failure', function(t) { |
||||
var res = { status: function( code ){ |
||||
t.equal(code, 400, 'status set'); |
||||
}}; |
||||
var next = function( message ){ |
||||
t.equal(message, defaultError); |
||||
t.end(); |
||||
}; |
||||
sanitize.middleware( {}, res, next ); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.middleware_success = function(test, common) { |
||||
test('middleware success', function(t) { |
||||
var req = { query: { input: 'test', lat: 0, lon: 0 }}; |
||||
var next = function( message ){ |
||||
t.equal(message, undefined, 'no error message set'); |
||||
t.deepEqual(req.clean, defaultClean); |
||||
t.end(); |
||||
}; |
||||
sanitize.middleware( req, undefined, next ); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.all = function (tape, common) { |
||||
|
||||
function test(name, testFunction) { |
||||
return tape('SANTIZE /suggest ' + name, testFunction); |
||||
} |
||||
|
||||
for( var testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
Loading…
Reference in new issue