From 45ba29cc54a6b2990f6d67877eff8bcd3dbe3773 Mon Sep 17 00:00:00 2001 From: Diana Shkolnikov Date: Fri, 24 Apr 2015 13:27:58 -0400 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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`