From 7667a898ddd52e132bc66f7a45a01970a2c1853b Mon Sep 17 00:00:00 2001 From: Harish Krishna Date: Fri, 17 Apr 2015 11:16:01 -0400 Subject: [PATCH 01/22] param layers should be called layers consistently across the API --- sanitiser/_layers.js | 2 +- test/unit/sanitiser/reverse.js | 2 +- test/unit/sanitiser/search.js | 2 +- test/unit/sanitiser/suggest.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sanitiser/_layers.js b/sanitiser/_layers.js index 24c4b478..f61bb9a3 100644 --- a/sanitiser/_layers.js +++ b/sanitiser/_layers.js @@ -33,7 +33,7 @@ function sanitize( req ){ if( -1 === alias_indeces.indexOf( layers[x] ) ){ return { 'error': true, - 'message': 'invalid param \'layer\': must be one or more of ' + alias_indeces.join(',') + 'message': 'invalid param \'layers\': must be one or more of ' + alias_indeces.join(',') }; } } diff --git a/test/unit/sanitiser/reverse.js b/test/unit/sanitiser/reverse.js index 3a06eaef..20ecbe0c 100644 --- a/test/unit/sanitiser/reverse.js +++ b/test/unit/sanitiser/reverse.js @@ -133,7 +133,7 @@ module.exports.tests.sanitize_layers = function(test, common) { }); 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 '; + var msg = 'invalid param \'layers\': must be one or more of '; t.true(err.match(msg), 'invalid layer requested'); t.true(err.length > msg.length, 'invalid error message'); t.end(); diff --git a/test/unit/sanitiser/search.js b/test/unit/sanitiser/search.js index edb8e7c8..80cb8d0d 100644 --- a/test/unit/sanitiser/search.js +++ b/test/unit/sanitiser/search.js @@ -275,7 +275,7 @@ module.exports.tests.sanitize_layers = function(test, common) { }); test('invalid layer', function(t) { sanitize({ layers: 'test_layer', input: 'test' }, function( err, clean ){ - var msg = 'invalid param \'layer\': must be one or more of '; + var msg = 'invalid param \'layers\': must be one or more of '; t.true(err.match(msg), 'invalid layer requested'); t.true(err.length > msg.length, 'invalid error message'); t.end(); diff --git a/test/unit/sanitiser/suggest.js b/test/unit/sanitiser/suggest.js index 88e75d56..a5824b6b 100644 --- a/test/unit/sanitiser/suggest.js +++ b/test/unit/sanitiser/suggest.js @@ -244,7 +244,7 @@ module.exports.tests.sanitize_layers = function(test, common) { }); 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 '; + var msg = 'invalid param \'layers\': must be one or more of '; t.true(err.match(msg), 'invalid layer requested'); t.true(err.length > msg.length, 'invalid error message'); t.end(); From a91cb23ea46b83d39901705cdefbd2c97749c920 Mon Sep 17 00:00:00 2001 From: Harish Krishna Date: Fri, 17 Apr 2015 14:01:25 -0400 Subject: [PATCH 02/22] adding a new file DOCS.md (same as https://github.com/pelias/api/wiki/API-Endpoints), modifying controller/index.js to return text/html instead of application/json by default. Using markdown module to convert MD to HTML. Fixing tests (removed test/jsonp.coffee) --- DOCS.md | 53 +++++++++++++++++++++++++++++++++++ controller/index.js | 24 ++++++++++++---- package.json | 5 ++-- test/ciao/index.coffee | 7 +---- test/ciao/jsonp.coffee | 10 ------- test/unit/controller/index.js | 4 +++ 6 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 DOCS.md delete mode 100644 test/ciao/jsonp.coffee diff --git a/DOCS.md b/DOCS.md new file mode 100644 index 00000000..5323a1ac --- /dev/null +++ b/DOCS.md @@ -0,0 +1,53 @@ +## /suggest +* this is the autocomplete endpoint (fast response time/ straight from the memory) +* takes the following params + * **input**: query string (required) + * **lat**: latitude from where you are searching (required) + * **lon**: longitude (required) + * **zoom**: zoom level at which you are viewing the world (optional) + * **size**: number of results you need (optional, defaults to 10) + * **layers**: datasets you want to query upon (optional, defaults to ```poi,admin,address```). It can be ```poi```, ```admin``` or ```address``` + * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` + * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` + * ```address``` expands to ```osmaddress```, ```openaddresses``` + * or it can also be specific to one particular dataset for example: ```geoname``` +* Lat/Lon is **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) + +## /suggest/coarse +* Only queries the admin layers +* Its the same as ```/suggest``` with layers param set to ```admin``` +* ```/suggest/coarse``` takes all other params that ```/suggest``` takes + +## /search +* this is the full text search endpoint (looks up elasticsearch doc store, slightly slower than suggest) +* takes the following params (same as suggest) + * **input** (required) + * **lat** (optional) + * **lon** (optional) + * **zoom** (optional) + * **bbox**: (optional) the bounding box where you want all your results to appear and be contained within that bbox. it can be one of the following comma separated string (they are all different ways of saying the same thing) + * bottom_left lat, bottom_left lon, top_right lat, top_right lon + * left,bottom,right,top + * min Longitude , min Latitude , max Longitude , max Latitude + * **size** (optional, defaults to 10) + * **layers** (optional, defaults to ```poi,admin,address```) + +## /search/coarse +* Similar to its suggest counterpart +* Its /search with layers param set to ```admin``` +* ```/search/coarse``` takes all other params that /search takes + +## /reverse +* Does reverse geocoding +* It takes the following params + * **lat** (required) + * **lon** (required) + * **zoom** (optional) + * **bbox** (optional) + * **layers** (optional, defaults to ```poi,admin,address```) + +## /doc +* retrieve a document or multiple documents at once +* it takes just one param + * **id**: (required) unique id of the document that need to be retrieved (should be in the form of type:id for example: ```geoname:4163334``` + * **ids**: (if multiple docs are to be fetched in bulk) an array of ids \ No newline at end of file diff --git a/controller/index.js b/controller/index.js index 10f2d9a4..eb37f86e 100644 --- a/controller/index.js +++ b/controller/index.js @@ -1,16 +1,30 @@ var pkg = require('../package'); +var markdown = require('markdown').markdown; +var fs = require('fs'); function setup(){ function controller( req, res, next ){ - // stats - res.json({ - name: pkg.name, - version: { - number: pkg.version + fs.readFile('./DOCS.md', 'utf8', function (err, content) { + if (!err) { + var header = '# Pelias API\n'; + var version = '### Version: ['+ pkg.version+ '](https://github.com/pelias/api/releases)\n'; + var style = ''; + + res.send(style + markdown.toHTML(header + version + content)); + + } else { + // stats + res.json({ + name: pkg.name, + version: { + number: pkg.version + } + }); } + }); } diff --git a/package.json b/package.json index 57ddf4cb..ac7d9ec0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pelias-api", "author": "mapzen", - "version": "0.0.0", + "version": "1.1.7", "description": "Pelias API", "homepage": "https://github.com/pelias/api", "license": "MIT", @@ -39,7 +39,8 @@ "geopipes-elasticsearch-backend": "0.0.12", "pelias-suggester-pipeline": "2.0.2", "is-object": "^1.0.1", - "pelias-esclient": "0.0.25" + "pelias-esclient": "0.0.25", + "markdown": "0.5.0" }, "devDependencies": { "ciao": "^0.3.4", diff --git a/test/ciao/index.coffee b/test/ciao/index.coffee index 76797848..0a460aa6 100644 --- a/test/ciao/index.coffee +++ b/test/ciao/index.coffee @@ -6,7 +6,7 @@ path: '/' response.statusCode.should.equal 200 #? content-type header correctly set -response.should.have.header 'Content-Type','application/json; charset=utf-8' +response.should.have.header 'Content-Type','text/html; charset=utf-8' #? charset header correctly set response.should.have.header 'Charset','utf8' @@ -20,8 +20,3 @@ response.headers.server.should.match /Pelias\/\d{1,2}\.\d{1,2}\.\d{1,2}/ #? vanity header correctly set response.should.have.header 'X-Powered-By','mapzen' - -#? should respond in json with server info -should.exist json -should.exist json.name -should.exist json.version \ No newline at end of file diff --git a/test/ciao/jsonp.coffee b/test/ciao/jsonp.coffee deleted file mode 100644 index d83f0eaa..00000000 --- a/test/ciao/jsonp.coffee +++ /dev/null @@ -1,10 +0,0 @@ - -#> 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('; \ No newline at end of file diff --git a/test/unit/controller/index.js b/test/unit/controller/index.js index e9752511..4068cd5e 100644 --- a/test/unit/controller/index.js +++ b/test/unit/controller/index.js @@ -20,6 +20,10 @@ module.exports.tests.info = function(test, common) { t.equal(typeof json.version, 'object', 'version'); t.equal(typeof json.version.number, 'string', 'version number'); t.end(); + }, + send: function( html ){ + t.equal(typeof html, 'string', 'returns string'); + t.end(); }}; controller( null, res ); }); From 904cb696a00996c3960c581119d6704dacf711e3 Mon Sep 17 00:00:00 2001 From: Harish Krishna Date: Fri, 17 Apr 2015 14:33:36 -0400 Subject: [PATCH 03/22] using coarse sanitizer for the coarse endpoint --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 49d07b94..b0f65801 100644 --- a/app.js +++ b/app.js @@ -38,7 +38,7 @@ app.get( '/suggest/nearby', sanitisers.suggest.middleware, controllers.suggest(undefined, undefined, require('./helper/queryMixer').suggest_nearby) ); app.get( '/suggest/coarse', - sanitisers.suggest.middleware, + sanitisers.coarse.middleware, controllers.suggest(undefined, undefined, require('./helper/queryMixer').coarse) ); // search API From 6506c84434504f5bd480d202e9d643001c949dec Mon Sep 17 00:00:00 2001 From: Grant Heffernan Date: Fri, 17 Apr 2015 15:00:00 -0400 Subject: [PATCH 04/22] update DOCS.md for readability, etc --- DOCS.md | 102 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/DOCS.md b/DOCS.md index 5323a1ac..8b366232 100644 --- a/DOCS.md +++ b/DOCS.md @@ -1,53 +1,79 @@ +## /search + +Full text search endpoint (queries the elasticsearch doc store, slightly slower than suggest) + +#### Parameters +* required: + * **input** +* optional: + * **lat** + * **lon** + * **zoom** + * **bbox** + * the bounding box where you want all your results to appear and be contained within that bbox, it can be one of the following comma separated strings, all of which are different ways of saying the same thing: + * bottom_left lat, bottom_left lon, top_right lat, top_right lon + * left, bottom, right, top + * min Longitude, min Latitude, max Longitude, max Latitude + * **size** (defaults to 10) + * **layers** (defaults to ```poi,admin,address```) + ## /suggest -* this is the autocomplete endpoint (fast response time/ straight from the memory) -* takes the following params - * **input**: query string (required) - * **lat**: latitude from where you are searching (required) - * **lon**: longitude (required) - * **zoom**: zoom level at which you are viewing the world (optional) - * **size**: number of results you need (optional, defaults to 10) - * **layers**: datasets you want to query upon (optional, defaults to ```poi,admin,address```). It can be ```poi```, ```admin``` or ```address``` + +The autocomplete endpoint: fast response time, served from memory + +#### Parameters +* required: + * **input**: query string + * **lat**: latitude from where you are searching + * **lon**: longitude + * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) +* optional: + * **zoom**: zoom level at which you are viewing the world + * **size**: number of results you need (defaults to 10) + * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```). It can be ```poi```, ```admin``` or ```address``` * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` * ```address``` expands to ```osmaddress```, ```openaddresses``` * or it can also be specific to one particular dataset for example: ```geoname``` -* Lat/Lon is **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) ## /suggest/coarse -* Only queries the admin layers -* Its the same as ```/suggest``` with layers param set to ```admin``` -* ```/suggest/coarse``` takes all other params that ```/suggest``` takes -## /search -* this is the full text search endpoint (looks up elasticsearch doc store, slightly slower than suggest) -* takes the following params (same as suggest) - * **input** (required) - * **lat** (optional) - * **lon** (optional) - * **zoom** (optional) - * **bbox**: (optional) the bounding box where you want all your results to appear and be contained within that bbox. it can be one of the following comma separated string (they are all different ways of saying the same thing) - * bottom_left lat, bottom_left lon, top_right lat, top_right lon - * left,bottom,right,top - * min Longitude , min Latitude , max Longitude , max Latitude - * **size** (optional, defaults to 10) - * **layers** (optional, defaults to ```poi,admin,address```) +Only queries the admin layers + +#### Parameters +* this endpoint is the equivalent of ```/suggest``` with the layers param set to ```admin``` +* takes all other params that ```/suggest``` takes ## /search/coarse -* Similar to its suggest counterpart -* Its /search with layers param set to ```admin``` + +Similar to the suggest endpoint + +#### Parameters +* the equivalent of /search with the layers param set to ```admin``` * ```/search/coarse``` takes all other params that /search takes ## /reverse -* Does reverse geocoding -* It takes the following params - * **lat** (required) - * **lon** (required) - * **zoom** (optional) - * **bbox** (optional) - * **layers** (optional, defaults to ```poi,admin,address```) + +Reverse geocoding endpoint + +#### Parameters +* required: + * **lat** + * **lon** +* optional: + * **zoom** + * **bbox** + * **layers** (defaults to ```poi,admin,address```) ## /doc -* retrieve a document or multiple documents at once -* it takes just one param - * **id**: (required) unique id of the document that need to be retrieved (should be in the form of type:id for example: ```geoname:4163334``` - * **ids**: (if multiple docs are to be fetched in bulk) an array of ids \ No newline at end of file + +Retrieves a document or multiple documents at once + +#### Parameters +* required: + * one of **id** or **ids** + * **id**: + * unique id of the document that to be retrieved + * should be in the form of type:id, for example: ```geoname:4163334``` + * **ids** + * if multiple docs are to be fetched in bulk, an array of ids From b07d693d04e44ea0207e4ec70c752746c073a597 Mon Sep 17 00:00:00 2001 From: Harish Krishna Date: Fri, 17 Apr 2015 15:17:55 -0400 Subject: [PATCH 05/22] updates (added suggest/nearby) --- DOCS.md | 81 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/DOCS.md b/DOCS.md index 8b366232..09108700 100644 --- a/DOCS.md +++ b/DOCS.md @@ -4,22 +4,45 @@ Full text search endpoint (queries the elasticsearch doc store, slightly slower #### Parameters * required: - * **input** + * **input**: query string +* optional: + * **lat**: latitude from where you are searching + * **lon**: longitude + * **zoom**: zoom level at which you are viewing the world + * **size**: number of results you need (defaults to 10) + * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```). + * It can be ```poi```, ```admin``` or ```address``` + * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` + * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` + * ```address``` expands to ```osmaddress```, ```openaddresses``` + * or it can also be specific to one particular dataset for example: ```geoname``` + * **bbox**: the bounding box where you want all your results to appear in. + * It can be one of the following comma separated string value + * bottom_left lat, bottom_left lon, top_right lat, top_right lon + * left,bottom,right,top + * min Longitude , min Latitude , max Longitude , max Latitude + +## /search/coarse +This is a coarse forward geocoder endpoint: only searches admin dataset layers + +#### Parameters +* required: + * **input** * optional: * **lat** * **lon** * **zoom** - * **bbox** - * the bounding box where you want all your results to appear and be contained within that bbox, it can be one of the following comma separated strings, all of which are different ways of saying the same thing: - * bottom_left lat, bottom_left lon, top_right lat, top_right lon - * left, bottom, right, top - * min Longitude, min Latitude, max Longitude, max Latitude + * **bbox**: the bounding box where you want all your results to appear in. + * It can be one of the following comma separated string value + * bottom_left lat, bottom_left lon, top_right lat, top_right lon + * left,bottom,right,top + * min Longitude , min Latitude , max Longitude , max Latitude * **size** (defaults to 10) - * **layers** (defaults to ```poi,admin,address```) + * **layers** (set to ```admin``` by default) ## /suggest -The autocomplete endpoint: fast response time, served from memory +The autocomplete endpoint: fast response time. Mixes results from around the provided lat/lon and also from precision level 1 and 3 #### Parameters * required: @@ -30,27 +53,39 @@ The autocomplete endpoint: fast response time, served from memory * optional: * **zoom**: zoom level at which you are viewing the world * **size**: number of results you need (defaults to 10) - * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```). It can be ```poi```, ```admin``` or ```address``` - * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` - * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` - * ```address``` expands to ```osmaddress```, ```openaddresses``` - * or it can also be specific to one particular dataset for example: ```geoname``` + * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```) ## /suggest/coarse Only queries the admin layers #### Parameters -* this endpoint is the equivalent of ```/suggest``` with the layers param set to ```admin``` -* takes all other params that ```/suggest``` takes +* required: + * **input**: query string + * **lat**: latitude from where you are searching + * **lon**: longitude + * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) +* optional: + * **zoom**: zoom level at which you are viewing the world + * **size**: number of results you need (defaults to 10) + * **layers**: datasets you want to query upon (defaults to ```admin```) -## /search/coarse +## /suggest/nearby -Similar to the suggest endpoint +* Works as autocomplete for places located nearby the lat/lon +* Its the same as ```/suggest``` but the results are all within 50kms of lat/lon thats passed +* Unlike ```/suggest```, ```/suggest/nearby``` does not mix results from different precision levels (500km, 1000km etc from lat/lon) #### Parameters -* the equivalent of /search with the layers param set to ```admin``` -* ```/search/coarse``` takes all other params that /search takes +* required: + * **input**: query string + * **lat**: latitude from where you are searching + * **lon**: longitude + * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) +* optional: + * **zoom**: zoom level at which you are viewing the world + * **size**: number of results you need (defaults to 10) + * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```) ## /reverse @@ -58,11 +93,11 @@ Reverse geocoding endpoint #### Parameters * required: - * **lat** - * **lon** + * **lat**: latitude + * **lon**: longitude * optional: - * **zoom** - * **bbox** + * **zoom**: zoom level + * **bbox**: bounding box * **layers** (defaults to ```poi,admin,address```) ## /doc From 189a768d05202bb941b0011074a637b5530a4e1e Mon Sep 17 00:00:00 2001 From: Grant Heffernan Date: Fri, 17 Apr 2015 15:31:56 -0400 Subject: [PATCH 06/22] more DOCS updates --- DOCS.md | 176 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/DOCS.md b/DOCS.md index 09108700..5e07a009 100644 --- a/DOCS.md +++ b/DOCS.md @@ -1,114 +1,116 @@ ## /search -Full text search endpoint (queries the elasticsearch doc store, slightly slower than suggest) +Full text search endpoint (queries the elasticsearch doc store, slightly slower than suggest). -#### Parameters -* required: - * **input**: query string -* optional: - * **lat**: latitude from where you are searching +#### Required Parameters +* **input**: query string + +#### Optional Parameters + * **lat**: latitude * **lon**: longitude - * **zoom**: zoom level at which you are viewing the world - * **size**: number of results you need (defaults to 10) - * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```). - * It can be ```poi```, ```admin``` or ```address``` - * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` - * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` - * ```address``` expands to ```osmaddress```, ```openaddresses``` - * or it can also be specific to one particular dataset for example: ```geoname``` - * **bbox**: the bounding box where you want all your results to appear in. - * It can be one of the following comma separated string value - * bottom_left lat, bottom_left lon, top_right lat, top_right lon - * left,bottom,right,top - * min Longitude , min Latitude , max Longitude , max Latitude + * **zoom**: zoom level from which you wish to view the world + * **size**: number of results requested (defaults to 10) + * **layers**: datasets you wish to query (defaults to ```poi,admin,address```). + * valid values are ```poi```, ```admin``` or ```address``` + * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` + * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` + * ```address``` expands to ```osmaddress```, ```openaddresses``` + * can also be specific to one particular dataset, for example ```geoname``` + * **bbox**: the bounding box from which you want all your results to come + * can be one of the following comma separated string values + * bottom_left lat, bottom_left lon, top_right lat, top_right lon + * left, bottom, right, top + * min longitude, min latitude, max longitude, max latitude + ## /search/coarse -This is a coarse forward geocoder endpoint: only searches admin dataset layers - -#### Parameters -* required: - * **input** -* optional: - * **lat** - * **lon** - * **zoom** - * **bbox**: the bounding box where you want all your results to appear in. - * It can be one of the following comma separated string value +This is a coarse forward geocoder endpoint: only searches admin dataset layers. + +#### Required Parameters +* **input**: query string + +#### Optional Parameters +* **lat**: latitude +* **lon**: longitude +* **zoom**: zoom level from which you wish to view the world +* **bbox**: the bounding box frome which you want all your results to come + * can be one of the following comma separated string values * bottom_left lat, bottom_left lon, top_right lat, top_right lon - * left,bottom,right,top - * min Longitude , min Latitude , max Longitude , max Latitude - * **size** (defaults to 10) - * **layers** (set to ```admin``` by default) + * left, bottom, right, top + * min longitude, min latitude, max longitude, max latitude +* **size** (defaults to 10) +* **layers** (defaults to ```admin```) + ## /suggest -The autocomplete endpoint: fast response time. Mixes results from around the provided lat/lon and also from precision level 1 and 3 +The autocomplete endpoint, it offers fast response time. Mixes results from around the provided lat/lon and also from precision level 1 and 3. + +#### Required Parameters +* **input**: query string +* **lat**: latitude +* **lon**: longitude + * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) + +#### Optional Parameters +* **zoom**: zoom level at which you are viewing the world +* **size**: number of results requested (defaults to 10) +* **layers**: datasets you wish to query (defaults to ```poi,admin,address```) -#### Parameters -* required: - * **input**: query string - * **lat**: latitude from where you are searching - * **lon**: longitude - * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) -* optional: - * **zoom**: zoom level at which you are viewing the world - * **size**: number of results you need (defaults to 10) - * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```) ## /suggest/coarse -Only queries the admin layers +Only queries the admin layers. + +#### Required Parameters +* **input**: query string +* **lat**: latitude from where you are searching +* **lon**: longitude + * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) + +#### Optional Parameters +* **zoom**: zoom level from which you wish to view the world +* **size**: number of results requested (defaults to 10) +* **layers**: datasets you wish to query (defaults to ```admin```) -#### Parameters -* required: - * **input**: query string - * **lat**: latitude from where you are searching - * **lon**: longitude - * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) -* optional: - * **zoom**: zoom level at which you are viewing the world - * **size**: number of results you need (defaults to 10) - * **layers**: datasets you want to query upon (defaults to ```admin```) ## /suggest/nearby -* Works as autocomplete for places located nearby the lat/lon -* Its the same as ```/suggest``` but the results are all within 50kms of lat/lon thats passed -* Unlike ```/suggest```, ```/suggest/nearby``` does not mix results from different precision levels (500km, 1000km etc from lat/lon) +Works as autocomplete for places located near a latitude/longitude, this endpoint is the same as ```/suggest``` but the results are all from within 50 kilometers of the specified point. Unlike ```/suggest```, ```/suggest/nearby``` does not mix results from different precision levels (500km, 1000km etc from lat/lon). -#### Parameters -* required: - * **input**: query string - * **lat**: latitude from where you are searching - * **lon**: longitude - * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) -* optional: - * **zoom**: zoom level at which you are viewing the world - * **size**: number of results you need (defaults to 10) - * **layers**: datasets you want to query upon (defaults to ```poi,admin,address```) +#### Required Parameters +* **input**: query string +* **lat**: latitude +* **lon**: longitude + * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) + +#### Optional Parameters +* **zoom**: zoom level at which you are viewing the world +* **size**: number of results you need (defaults to 10) +* **layers**: datasets you wish to query (defaults to ```poi,admin,address```) ## /reverse -Reverse geocoding endpoint +Reverse geocoding endpoint. + +#### Required Parameters +* **lat**: latitude +* **lon**: longitude + +#### Optional Parameters +* **zoom**: zoom level +* **bbox**: bounding box +* **layers** (defaults to ```poi,admin,address```) -#### Parameters -* required: - * **lat**: latitude - * **lon**: longitude -* optional: - * **zoom**: zoom level - * **bbox**: bounding box - * **layers** (defaults to ```poi,admin,address```) ## /doc -Retrieves a document or multiple documents at once +Retrieves a document or multiple documents at once. -#### Parameters -* required: - * one of **id** or **ids** - * **id**: - * unique id of the document that to be retrieved - * should be in the form of type:id, for example: ```geoname:4163334``` - * **ids** - * if multiple docs are to be fetched in bulk, an array of ids +#### Required Parameters +* one of **id** or **ids** + * **id**: + * unique id of the document that to be retrieved + * should be in the form of type:id, for example: ```geoname:4163334``` + * **ids** + * if multiple docs are to be fetched in bulk, an array of ids From 6bff821c53409b639311e120c4fb5fd1ddf6756e Mon Sep 17 00:00:00 2001 From: Grant Heffernan Date: Fri, 17 Apr 2015 15:34:48 -0400 Subject: [PATCH 07/22] standardize some DOCS language, fix indentation --- DOCS.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/DOCS.md b/DOCS.md index 5e07a009..b45ffaec 100644 --- a/DOCS.md +++ b/DOCS.md @@ -6,21 +6,21 @@ Full text search endpoint (queries the elasticsearch doc store, slightly slower * **input**: query string #### Optional Parameters - * **lat**: latitude - * **lon**: longitude - * **zoom**: zoom level from which you wish to view the world - * **size**: number of results requested (defaults to 10) - * **layers**: datasets you wish to query (defaults to ```poi,admin,address```). - * valid values are ```poi```, ```admin``` or ```address``` - * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` - * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` - * ```address``` expands to ```osmaddress```, ```openaddresses``` - * can also be specific to one particular dataset, for example ```geoname``` - * **bbox**: the bounding box from which you want all your results to come - * can be one of the following comma separated string values - * bottom_left lat, bottom_left lon, top_right lat, top_right lon - * left, bottom, right, top - * min longitude, min latitude, max longitude, max latitude +* **lat**: latitude +* **lon**: longitude +* **zoom**: zoom level from which you wish to view the world +* **size**: number of results requested (defaults to 10) +* **layers**: datasets you wish to query (defaults to ```poi,admin,address```). + * valid values are ```poi```, ```admin``` or ```address``` + * ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` + * ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` + * ```address``` expands to ```osmaddress```, ```openaddresses``` + * can also be specific to one particular dataset, for example ```geoname``` +* **bbox**: the bounding box from which you want all your results to come + * can be one of the following comma separated string values + * bottom_left lat, bottom_left lon, top_right lat, top_right lon + * left, bottom, right, top + * min longitude, min latitude, max longitude, max latitude ## /search/coarse @@ -53,7 +53,7 @@ The autocomplete endpoint, it offers fast response time. Mixes results from arou * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) #### Optional Parameters -* **zoom**: zoom level at which you are viewing the world +* **zoom**: zoom level from which you wish to view the world * **size**: number of results requested (defaults to 10) * **layers**: datasets you wish to query (defaults to ```poi,admin,address```) @@ -85,7 +85,7 @@ Works as autocomplete for places located near a latitude/longitude, this endpoin * lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) #### Optional Parameters -* **zoom**: zoom level at which you are viewing the world +* **zoom**: zoom level from which you wish to view the world * **size**: number of results you need (defaults to 10) * **layers**: datasets you wish to query (defaults to ```poi,admin,address```) @@ -98,7 +98,7 @@ Reverse geocoding endpoint. * **lon**: longitude #### Optional Parameters -* **zoom**: zoom level +* **zoom**: zoom level from which you wish to view the world * **bbox**: bounding box * **layers** (defaults to ```poi,admin,address```) From 9e7e1455b535884907d7aedf5820f6b8e5caf982 Mon Sep 17 00:00:00 2001 From: Grant Heffernan Date: Fri, 17 Apr 2015 15:36:01 -0400 Subject: [PATCH 08/22] a bit more DOCS cleanup --- DOCS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DOCS.md b/DOCS.md index b45ffaec..3a1db495 100644 --- a/DOCS.md +++ b/DOCS.md @@ -24,7 +24,8 @@ Full text search endpoint (queries the elasticsearch doc store, slightly slower ## /search/coarse -This is a coarse forward geocoder endpoint: only searches admin dataset layers. + +This is a coarse forward geocoder endpoint which only searches admin dataset layers. #### Required Parameters * **input**: query string @@ -89,6 +90,7 @@ Works as autocomplete for places located near a latitude/longitude, this endpoin * **size**: number of results you need (defaults to 10) * **layers**: datasets you wish to query (defaults to ```poi,admin,address```) + ## /reverse Reverse geocoding endpoint. From 83dbdc7d4192a36e25eb548167919ef3c09cf851 Mon Sep 17 00:00:00 2001 From: Grant Heffernan Date: Fri, 17 Apr 2015 15:38:48 -0400 Subject: [PATCH 09/22] DOCS formatting --- DOCS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DOCS.md b/DOCS.md index 3a1db495..d640cf8a 100644 --- a/DOCS.md +++ b/DOCS.md @@ -39,8 +39,8 @@ This is a coarse forward geocoder endpoint which only searches admin dataset lay * bottom_left lat, bottom_left lon, top_right lat, top_right lon * left, bottom, right, top * min longitude, min latitude, max longitude, max latitude -* **size** (defaults to 10) -* **layers** (defaults to ```admin```) +* **size**: (defaults to 10) +* **layers**: (defaults to ```admin```) ## /suggest @@ -102,7 +102,7 @@ Reverse geocoding endpoint. #### Optional Parameters * **zoom**: zoom level from which you wish to view the world * **bbox**: bounding box -* **layers** (defaults to ```poi,admin,address```) +* **layers**: (defaults to ```poi,admin,address```) ## /doc @@ -112,7 +112,7 @@ Retrieves a document or multiple documents at once. #### Required Parameters * one of **id** or **ids** * **id**: - * unique id of the document that to be retrieved + * unique id of the document to be retrieved * should be in the form of type:id, for example: ```geoname:4163334``` - * **ids** + * **ids**: * if multiple docs are to be fetched in bulk, an array of ids From 22d235907db9074799a83fd0df9e6768a767c5f9 Mon Sep 17 00:00:00 2001 From: Grant Heffernan Date: Fri, 17 Apr 2015 15:56:36 -0400 Subject: [PATCH 10/22] fix bbox italics in DOCS --- DOCS.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/DOCS.md b/DOCS.md index d640cf8a..c63d4ee2 100644 --- a/DOCS.md +++ b/DOCS.md @@ -18,7 +18,7 @@ Full text search endpoint (queries the elasticsearch doc store, slightly slower * can also be specific to one particular dataset, for example ```geoname``` * **bbox**: the bounding box from which you want all your results to come * can be one of the following comma separated string values - * bottom_left lat, bottom_left lon, top_right lat, top_right lon + * bottom left lat, bottom left lon, top right lat, top right lon * left, bottom, right, top * min longitude, min latitude, max longitude, max latitude @@ -35,10 +35,6 @@ This is a coarse forward geocoder endpoint which only searches admin dataset lay * **lon**: longitude * **zoom**: zoom level from which you wish to view the world * **bbox**: the bounding box frome which you want all your results to come - * can be one of the following comma separated string values - * bottom_left lat, bottom_left lon, top_right lat, top_right lon - * left, bottom, right, top - * min longitude, min latitude, max longitude, max latitude * **size**: (defaults to 10) * **layers**: (defaults to ```admin```) From 1fc624c95739092899f708e531d50b1bb48d9a96 Mon Sep 17 00:00:00 2001 From: Grant Heffernan Date: Fri, 17 Apr 2015 16:03:24 -0400 Subject: [PATCH 11/22] DOCS update --- DOCS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DOCS.md b/DOCS.md index c63d4ee2..478aeb90 100644 --- a/DOCS.md +++ b/DOCS.md @@ -1,6 +1,6 @@ ## /search -Full text search endpoint (queries the elasticsearch doc store, slightly slower than suggest). +Full text search endpoint which queries the elasticsearch doc store, slightly slower than suggest. #### Required Parameters * **input**: query string From 1d45b2026866c8544135cc535264da91e09549cb Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Thu, 23 Apr 2015 17:21:58 -0400 Subject: [PATCH 12/22] Load markdown file into memory and check if client requested html --- controller/index.js | 55 ++++++++++++++++++++++------------- test/unit/controller/index.js | 31 ++++++++++++++++---- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/controller/index.js b/controller/index.js index eb37f86e..0c2f5ab0 100644 --- a/controller/index.js +++ b/controller/index.js @@ -5,32 +5,47 @@ var fs = require('fs'); function setup(){ - function controller( req, res, next ){ - - fs.readFile('./DOCS.md', 'utf8', function (err, content) { - if (!err) { - var header = '# Pelias API\n'; - var version = '### Version: ['+ pkg.version+ '](https://github.com/pelias/api/releases)\n'; - var style = ''; - - res.send(style + markdown.toHTML(header + version + content)); - - } else { - // stats - res.json({ - name: pkg.name, - version: { - number: pkg.version - } - }); + var text = null; + function getText( callback ) { + if( text ) { + process.nextTick( callback.bind( null, null, text ) ); + return; + } + + fs.readFile( './DOCS.md', 'utf8', function ( err, content ) { + if( err ) { + callback( err ); + return; } - + text = '# Pelias API\n'; + text += '### Version: ['+ pkg.version+ '](https://github.com/pelias/api/releases)\n'; + text += content; + + callback( null, text ); }); + } + function controller( req, res, next ) { + getText( function ( err, content ) { + if( !err ) { + if( req.accepts( 'html' ) ) { + var style = ''; + res.send( style + markdown.toHTML( content ) ); + } + else { + // stats + res.json({ + name: pkg.name, + version: { + number: pkg.version + } + }); + } + } + }); } return controller; - } module.exports = setup; \ No newline at end of file diff --git a/test/unit/controller/index.js b/test/unit/controller/index.js index 4068cd5e..ea4f6991 100644 --- a/test/unit/controller/index.js +++ b/test/unit/controller/index.js @@ -11,21 +11,40 @@ module.exports.tests.interface = function(test, common) { }); }; -module.exports.tests.info = function(test, common) { - test('returns server info', function(t) { +module.exports.tests.info_json = function(test, common) { + test('returns server info in json', function(t) { var controller = setup(); + var req = { + accepts: function (format) { + t.equal(format, 'html', 'check for Accepts:html'); + return false; + } + }; var res = { json: function( json ){ t.equal(typeof json, 'object', 'returns json'); t.equal(typeof json.name, 'string', 'name'); t.equal(typeof json.version, 'object', 'version'); t.equal(typeof json.version.number, 'string', 'version number'); t.end(); - }, - send: function( html ){ - t.equal(typeof html, 'string', 'returns string'); + }}; + controller( req, res ); + }); +}; + +module.exports.tests.info_html = function(test, common) { + test('returns server info in json', function(t) { + var controller = setup(); + var req = { + accepts: function () { + return true; + } + }; + var res = { send: function( content ){ + t.equal(typeof content, 'string', 'returns string'); + t.assert(content.indexOf('') === 0, 'style set to monospace'); t.end(); }}; - controller( null, res ); + controller( req, res ); }); }; From 4f1b75ce945bd528ecf69ac975445bfe8197307e Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Thu, 23 Apr 2015 17:25:21 -0400 Subject: [PATCH 13/22] Fix test name --- test/unit/controller/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/controller/index.js b/test/unit/controller/index.js index ea4f6991..aa08d0d7 100644 --- a/test/unit/controller/index.js +++ b/test/unit/controller/index.js @@ -32,7 +32,7 @@ module.exports.tests.info_json = function(test, common) { }; module.exports.tests.info_html = function(test, common) { - test('returns server info in json', function(t) { + test('returns server info in html', function(t) { var controller = setup(); var req = { accepts: function () { From 99ee2d95a32a56d2e2cbc10b2f3905569c1b287e Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Fri, 24 Apr 2015 11:52:37 -0400 Subject: [PATCH 14/22] Change to readFileSync and update test to use proxyquire --- controller/index.js | 49 +++++++++++------------------------ package.json | 11 ++++---- test/unit/controller/index.js | 17 +++++++++++- 3 files changed, 37 insertions(+), 40 deletions(-) diff --git a/controller/index.js b/controller/index.js index 0c2f5ab0..43acf064 100644 --- a/controller/index.js +++ b/controller/index.js @@ -5,42 +5,23 @@ var fs = require('fs'); function setup(){ - var text = null; - function getText( callback ) { - if( text ) { - process.nextTick( callback.bind( null, null, text ) ); - return; - } - - fs.readFile( './DOCS.md', 'utf8', function ( err, content ) { - if( err ) { - callback( err ); - return; - } - text = '# Pelias API\n'; - text += '### Version: ['+ pkg.version+ '](https://github.com/pelias/api/releases)\n'; - text += content; - - callback( null, text ); - }); - } + var text = '# Pelias API\n'; + text += '### Version: ['+ pkg.version+ '](https://github.com/pelias/api/releases)\n'; + text += fs.readFileSync( './DOCS.md', 'utf8'); function controller( req, res, next ) { - getText( function ( err, content ) { - if( !err ) { - if( req.accepts( 'html' ) ) { - var style = ''; - res.send( style + markdown.toHTML( content ) ); - } - else { - // stats - res.json({ - name: pkg.name, - version: { - number: pkg.version - } - }); - } + if (req.accepts('html')) { + if( text ) { + var style = ''; + res.send(style + markdown.toHTML(text)); + } + return; + } + // default behaviour + res.json({ + name: pkg.name, + version: { + number: pkg.version } }); } diff --git a/package.json b/package.json index ac7d9ec0..0276211e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "start": "node index.js", - "test": "npm run unit && npm run ciao", + "test": "npm run unit", "unit": "node test/unit/run.js | tap-spec", "ciao": "node node_modules/ciao/bin/ciao -c test/ciao.json test/ciao", "audit": "npm shrinkwrap; node node_modules/nsp/bin/nspCLI.js audit-shrinkwrap; rm npm-shrinkwrap.json;", @@ -37,17 +37,18 @@ "geojson": "^0.2.0", "geojson-extent": "^0.3.1", "geopipes-elasticsearch-backend": "0.0.12", - "pelias-suggester-pipeline": "2.0.2", "is-object": "^1.0.1", + "markdown": "0.5.0", "pelias-esclient": "0.0.25", - "markdown": "0.5.0" + "pelias-suggester-pipeline": "2.0.2" }, "devDependencies": { "ciao": "^0.3.4", "jshint": "^2.5.6", + "nsp": "^0.3.0", "precommit-hook": "^1.0.7", - "tape": "^2.13.4", + "proxyquire": "^1.4.0", "tap-spec": "^0.2.0", - "nsp": "^0.3.0" + "tape": "^2.13.4" } } diff --git a/test/unit/controller/index.js b/test/unit/controller/index.js index aa08d0d7..f59dacd4 100644 --- a/test/unit/controller/index.js +++ b/test/unit/controller/index.js @@ -33,6 +33,20 @@ module.exports.tests.info_json = function(test, common) { module.exports.tests.info_html = function(test, common) { test('returns server info in html', function(t) { + + var style = ''; + var mockText = 'this text should show up in the html content'; + var fsMock = { + readFileSync: function (path, format) { + t.equal(path, './DOCS.md', 'open DOCS.md file'); + t.equal(format, 'utf8', 'file format'); + return mockText; + } + }; + + var proxyquire = require('proxyquire'); + var setup = proxyquire('../../../controller/index', { 'fs': fsMock }); + var controller = setup(); var req = { accepts: function () { @@ -41,7 +55,8 @@ module.exports.tests.info_html = function(test, common) { }; var res = { send: function( content ){ t.equal(typeof content, 'string', 'returns string'); - t.assert(content.indexOf('') === 0, 'style set to monospace'); + t.assert(content.indexOf(style) === 0, 'style set'); + t.assert(content.indexOf(mockText) !== -1, 'file content added'); t.end(); }}; controller( req, res ); From 29838e5b8c7c7ed7018878bcaaa535730d8dabe5 Mon Sep 17 00:00:00 2001 From: Severyn Kozak Date: Fri, 24 Apr 2015 12:22:51 -0400 Subject: [PATCH 15/22] Move code that needs to run only once. controller/index.js -Move all of the index page HTML "initialization" code that only needs to run once, like markdown compilation and `style` prepending, out of the request handler. --- controller/index.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/controller/index.js b/controller/index.js index 43acf064..d900793f 100644 --- a/controller/index.js +++ b/controller/index.js @@ -5,16 +5,15 @@ var fs = require('fs'); function setup(){ + var styleString = ''; var text = '# Pelias API\n'; text += '### Version: ['+ pkg.version+ '](https://github.com/pelias/api/releases)\n'; text += fs.readFileSync( './DOCS.md', 'utf8'); + var indexHtml = styleString + markdown.toHTML(text); function controller( req, res, next ) { if (req.accepts('html')) { - if( text ) { - var style = ''; - res.send(style + markdown.toHTML(text)); - } + res.send(indexHtml); return; } // default behaviour @@ -29,4 +28,4 @@ function setup(){ return controller; } -module.exports = setup; \ No newline at end of file +module.exports = setup; From 45ba29cc54a6b2990f6d67877eff8bcd3dbe3773 Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Fri, 24 Apr 2015 13:27:58 -0400 Subject: [PATCH 16/22] Add categories param to /reverse --- query/reverse.js | 21 +++++++++-- sanitiser/_categories.js | 38 ++++++++++++++++++++ sanitiser/reverse.js | 7 ++-- test/unit/query/reverse.js | 66 ++++++++++++++++++++++++++++++++++ test/unit/sanitiser/reverse.js | 42 +++++++++++++++++++++- 5 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 sanitiser/_categories.js diff --git a/query/reverse.js b/query/reverse.js index 35b86ade..185d312d 100644 --- a/query/reverse.js +++ b/query/reverse.js @@ -1,7 +1,6 @@ -var logger = require('../src/logger'), - queries = require('geopipes-elasticsearch-backend').queries, - sort = require('../query/sort'); +var queries = require('geopipes-elasticsearch-backend').queries, + sort = require('./sort'); function generate( params ){ @@ -13,7 +12,23 @@ function generate( params ){ var query = queries.distance( centroid, { size: params.size || 1 } ); query.sort = query.sort.concat(sort); + if ( params.categories && params.categories.length > 0 ) { + addCategoriesFilter( query, params.categories ); + } + return query; } +function addCategoriesFilter( query, categories ) { + query.query.filtered.query = { + match: { + category: { + query: categories.join(' '), + analyzer: 'standard', + operator: 'or' + } + } + }; +} + module.exports = generate; \ No newline at end of file diff --git a/sanitiser/_categories.js b/sanitiser/_categories.js new file mode 100644 index 00000000..f29c9fd0 --- /dev/null +++ b/sanitiser/_categories.js @@ -0,0 +1,38 @@ + +var isObject = require('is-object'); + +// validate inputs, convert types and apply defaults +function sanitize( req ){ + + var clean = req.clean || {}; + var params= req.query; + + // ensure the input params are a valid object + if( !isObject( params ) ){ + params = {}; + } + + // default case (no categories specified in GET params) + if('string' !== typeof params.categories || !params.categories.length){ + clean.categories = []; + } + else { + // parse GET params + clean.categories = params.categories.split(',') + .map(function (cat) { + return cat.toLowerCase().trim(); // lowercase inputs + }) + .filter( function( cat ) { + return ( cat.length > 0 ); + }); + } + + // pass validated params to next middleware + req.clean = clean; + + return { 'error': false }; + +} + +// export function +module.exports = sanitize; diff --git a/sanitiser/reverse.js b/sanitiser/reverse.js index 117b67d2..9804b4e9 100644 --- a/sanitiser/reverse.js +++ b/sanitiser/reverse.js @@ -1,6 +1,5 @@ -var logger = require('../src/logger'), - _sanitize = require('../sanitiser/_sanitize'), +var _sanitize = require('../sanitiser/_sanitize'), sanitiser = { latlonzoom: function( req ) { var geo = require('../sanitiser/_geo'); @@ -10,6 +9,10 @@ var logger = require('../src/logger'), size: function( req ) { var size = require('../sanitiser/_size'); return size(req, 1); + }, + categories: function ( req ) { + var categories = require('../sanitiser/_categories'); + return categories(req); } }; diff --git a/test/unit/query/reverse.js b/test/unit/query/reverse.js index 0380fb85..a2878e94 100644 --- a/test/unit/query/reverse.js +++ b/test/unit/query/reverse.js @@ -111,6 +111,72 @@ module.exports.tests.query = function(test, common) { }); t.end(); }); + + test('valid query with categories', function(t) { + var params = { lat: 29.49136, lon: -82.50622, categories: ['food', 'education', 'entertainment'] }; + var query = generate(params); + + var expected = { + 'query': { + 'filtered': { + 'query': { + 'match': { + 'category': { + 'query': 'food education entertainment', + 'analyzer': 'standard', + 'operator': 'or' + } + } + }, + 'filter': { + 'bool': { + 'must': [ + { + 'geo_distance': { + 'distance': '50km', + 'distance_type': 'plane', + 'optimize_bbox': 'indexed', + '_cache': true, + 'center_point': { + 'lat': '29.49', + 'lon': '-82.51' + } + } + } + ] + } + } + } + }, + 'sort': [ + '_score', + { + '_geo_distance': { + 'center_point': { + 'lat': 29.49136, + 'lon': -82.50622 + }, + 'order': 'asc', + 'unit': 'km' + } + } + ].concat(sort.slice(1)), + 'size': 1, + 'track_scores': true + }; + + t.deepEqual(query, expected, 'valid reverse query with categories'); + + // test different sizes + var sizes = [1,2,10,undefined,null]; + sizes.forEach( function(size) { + params.size = size; + query = generate(params); + expected.size = size ? size : 1; + t.deepEqual(query, expected, 'valid reverse query for size: '+ size); + }); + t.end(); + }); }; module.exports.all = function (tape, common) { diff --git a/test/unit/sanitiser/reverse.js b/test/unit/sanitiser/reverse.js index 3a06eaef..ab47b334 100644 --- a/test/unit/sanitiser/reverse.js +++ b/test/unit/sanitiser/reverse.js @@ -8,7 +8,8 @@ var suggest = require('../../../sanitiser/reverse'), layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood', 'locality', 'local_admin', 'osmaddress', 'openaddresses' ], lon: 0, - size: 1 + size: 1, + categories: [] }, sanitize = function(query, cb) { _sanitize({'query':query}, cb); }; @@ -200,6 +201,45 @@ module.exports.tests.sanitize_layers = function(test, common) { }); }; +module.exports.tests.sanitize_categories = function(test, common) { + var queryParams = { input: 'test', lat: 0, lon: 0 }; + test('unspecified', function(t) { + queryParams.categories = undefined; + sanitize(queryParams, function( err, clean ){ + t.deepEqual(clean.categories, defaultClean.categories, 'default to empty categories array'); + t.end(); + }); + }); + test('single category', function(t) { + queryParams.categories = 'food'; + sanitize(queryParams, function( err, clean ){ + t.deepEqual(clean.categories, ['food'], 'category set'); + t.end(); + }); + }); + test('multiple categories', function(t) { + queryParams.categories = 'food,education,nightlife'; + sanitize(queryParams, function( err, clean ){ + t.deepEqual(clean.categories, ['food', 'education', 'nightlife'], 'categories set'); + t.end(); + }); + }); + test('whitespace and empty strings', function(t) { + queryParams.categories = 'food, , nightlife ,'; + sanitize(queryParams, function( err, clean ){ + t.deepEqual(clean.categories, ['food', 'nightlife'], 'categories set'); + t.end(); + }); + }); + test('all empty strings', function(t) { + queryParams.categories = ', , ,'; + sanitize(queryParams, function( err, clean ){ + t.deepEqual(clean.categories, defaultClean.categories, 'empty strings filtered out'); + t.end(); + }); + }); +}; + module.exports.tests.middleware_failure = function(test, common) { test('middleware failure', function(t) { var res = { status: function( code ){ From 7b5a2fafb9b5cef5b065230b12a864b613037da2 Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Fri, 24 Apr 2015 23:19:43 -0400 Subject: [PATCH 17/22] Change query to use `terms` and add code coverage stats --- .gitignore | 2 ++ .jshintignore | 1 + .travis.yml | 7 ++++++- README.md | 4 ++++ package.json | 4 +++- query/reverse.js | 12 +++--------- test/unit/query/reverse.js | 13 ++++++------- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index ab05030f..e84bbfbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules +coverage +.idea *.log \ No newline at end of file diff --git a/.jshintignore b/.jshintignore index 3c3629e6..ba2a97b5 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1 +1,2 @@ node_modules +coverage diff --git a/.travis.yml b/.travis.yml index 82ec939c..0d65b3bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,9 @@ language: node_js script: "npm run unit" node_js: - - "0.10" \ No newline at end of file + - "0.10" +addons: + code_climate: + repo_token: a98bd9004512c8caf44e5cb2b3586d01baf190ee069ec0b731b451fc9966e639 +after_script: + - codeclimate < coverage/lcov.info \ No newline at end of file diff --git a/README.md b/README.md index 01da637a..71cd6c97 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # API [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/pelias/api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Code Climate](https://codeclimate.com/github/pelias/api/badges/gpa.svg)](https://codeclimate.com/github/pelias/api) + +[![Test Coverage](https://codeclimate.com/github/pelias/api/badges/coverage.svg)](https://codeclimate.com/github/pelias/api) + Pelias RESTful API ## Documentation diff --git a/package.json b/package.json index 0276211e..4f35bbe2 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,10 @@ "main": "index.js", "scripts": { "start": "node index.js", - "test": "npm run unit", + "test": "npm run unit && npm run coverage", "unit": "node test/unit/run.js | tap-spec", "ciao": "node node_modules/ciao/bin/ciao -c test/ciao.json test/ciao", + "coverage": "node_modules/.bin/istanbul cover test/unit/run.js", "audit": "npm shrinkwrap; node node_modules/nsp/bin/nspCLI.js audit-shrinkwrap; rm npm-shrinkwrap.json;", "docs": "rm -r docs; cd test/ciao; node ../../node_modules/ciao/bin/ciao -c ../ciao.json . -d ../../docs" }, @@ -44,6 +45,7 @@ }, "devDependencies": { "ciao": "^0.3.4", + "istanbul": "^0.3.13", "jshint": "^2.5.6", "nsp": "^0.3.0", "precommit-hook": "^1.0.7", diff --git a/query/reverse.js b/query/reverse.js index 185d312d..499ddddc 100644 --- a/query/reverse.js +++ b/query/reverse.js @@ -20,15 +20,9 @@ function generate( params ){ } function addCategoriesFilter( query, categories ) { - query.query.filtered.query = { - match: { - category: { - query: categories.join(' '), - analyzer: 'standard', - operator: 'or' - } - } - }; + query.query.filtered.filter.bool.must.push({ + terms: { category: categories } + }); } module.exports = generate; \ No newline at end of file diff --git a/test/unit/query/reverse.js b/test/unit/query/reverse.js index a2878e94..00a3d15e 100644 --- a/test/unit/query/reverse.js +++ b/test/unit/query/reverse.js @@ -120,13 +120,7 @@ module.exports.tests.query = function(test, common) { 'query': { 'filtered': { 'query': { - 'match': { - 'category': { - 'query': 'food education entertainment', - 'analyzer': 'standard', - 'operator': 'or' - } - } + 'match_all': {} }, 'filter': { 'bool': { @@ -142,6 +136,11 @@ module.exports.tests.query = function(test, common) { 'lon': '-82.51' } } + }, + { + 'terms': { + 'category': params.categories + } } ] } From 5101d802ab8db3273a2379156a8dba764c6947ca Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Mon, 27 Apr 2015 10:27:24 -0400 Subject: [PATCH 18/22] remove code-climate hooks --- .travis.yml | 7 +------ README.md | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0d65b3bc..82ec939c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,4 @@ language: node_js script: "npm run unit" node_js: - - "0.10" -addons: - code_climate: - repo_token: a98bd9004512c8caf44e5cb2b3586d01baf190ee069ec0b731b451fc9966e639 -after_script: - - codeclimate < coverage/lcov.info \ No newline at end of file + - "0.10" \ No newline at end of file diff --git a/README.md b/README.md index 71cd6c97..01da637a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # API [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/pelias/api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Code Climate](https://codeclimate.com/github/pelias/api/badges/gpa.svg)](https://codeclimate.com/github/pelias/api) - -[![Test Coverage](https://codeclimate.com/github/pelias/api/badges/coverage.svg)](https://codeclimate.com/github/pelias/api) - Pelias RESTful API ## Documentation From 4d0c23e74275eeaa1f49b3a1f53cf68d38e385f5 Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Mon, 27 Apr 2015 10:28:22 -0400 Subject: [PATCH 19/22] remove code-climate hooks --- .gitignore | 3 ++- .jshintignore | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e84bbfbc..419c501c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules coverage .idea -*.log \ No newline at end of file +*.log +reports \ No newline at end of file diff --git a/.jshintignore b/.jshintignore index ba2a97b5..ab14aab9 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,2 +1,3 @@ node_modules coverage +reports From 0d07c38b0696a1bc9c8458c7cf3646d5a3592322 Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Mon, 27 Apr 2015 10:33:01 -0400 Subject: [PATCH 20/22] add to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 01da637a..9c5c1121 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,12 @@ $ npm test $ npm run docs ``` +### Code Coverage + +```bash +$ npm run coverage +``` + ### Continuous Integration Travis tests every release against node version `0.10` From 17bb6db6e56e5c6b96ab1d9b06d57889d1b651c9 Mon Sep 17 00:00:00 2001 From: Severyn Kozak Date: Mon, 27 Apr 2015 11:53:29 -0400 Subject: [PATCH 21/22] Resolve #114: report runtime errors. middleware/500.js -Add code to the 500 catchall middleware to report errors to stdout/stderr. -Also, stringify the error via `.toString()` before passing it to `res.json()`, since otherwise an empty object appears to be returned. --- middleware/500.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/middleware/500.js b/middleware/500.js index d73c5684..15846ce0 100644 --- a/middleware/500.js +++ b/middleware/500.js @@ -1,9 +1,12 @@ +var logger = require( '../src/logger' ); // handle application errors function middleware(err, req, res, next) { + logger.error( 'Error:', err ); + logger.error( 'Stack trace:', err.trace ); res.header('Cache-Control','no-cache'); if( res.statusCode < 400 ){ res.status(500); } - res.json({ error: err }); + res.json({ error: err.toString() }); } -module.exports = middleware; \ No newline at end of file +module.exports = middleware; From cf1a483edecf42ab4f2acaf8adf42996c7078e31 Mon Sep 17 00:00:00 2001 From: Severyn Kozak Date: Mon, 27 Apr 2015 12:05:48 -0400 Subject: [PATCH 22/22] Don't report full errors in API results. middleware/500.js -Return a generic error message rather than stringified error object in API results. --- middleware/500.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/500.js b/middleware/500.js index 15846ce0..02e8a3f9 100644 --- a/middleware/500.js +++ b/middleware/500.js @@ -6,7 +6,7 @@ function middleware(err, req, res, next) { logger.error( 'Stack trace:', err.trace ); res.header('Cache-Control','no-cache'); if( res.statusCode < 400 ){ res.status(500); } - res.json({ error: err.toString() }); + res.json({ error: 'internal server error' }); } module.exports = middleware;