From 42ac5f05eb547af2277ff1767e9dafff9d89861a Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Wed, 24 Sep 2014 13:13:29 +0100 Subject: [PATCH] geojsonify suggest API --- controller/suggest.js | 55 +++++++++++++++++++++++++++++--- package.json | 1 + test/ciao/suggest/success.coffee | 9 +++--- test/unit/controller/suggest.js | 36 ++++++++++++++++++--- test/unit/mock/backend.js | 7 +++- 5 files changed, 95 insertions(+), 13 deletions(-) diff --git a/controller/suggest.js b/controller/suggest.js index b62b2fe6..7907f462 100644 --- a/controller/suggest.js +++ b/controller/suggest.js @@ -1,4 +1,6 @@ +var GeoJSON = require('geojson'); + function setup( backend, query ){ // allow overriding of dependencies @@ -26,10 +28,15 @@ function setup( backend, query ){ docs = data['pelias'][0].options || []; } - // respond - return res.status(200).json({ - date: new Date().getTime(), - body: docs + // convert docs to geojson + geoJsonifyDocs( docs, function( geojson ){ + + // response envelope + geojson.date = new Date().getTime(); + + // respond + return res.status(200).json( geojson ); + }); }); @@ -38,4 +45,44 @@ function setup( backend, query ){ return controller; } +function geoJsonifyDocs( docs, cb ){ + + // emit a warning if the doc format is invalid + // @note: if you see this error, fix it ASAP! + function warning(){ + console.error( 'error: invalid doc', __filename ); + return false; // remove offending doc from results + } + + // flatten & expand data for geojson conversion + var geodata = docs.map( function( doc ){ + + // something went very wrong + if( !doc || !doc.payload ) return warning(); + + // split payload id string in to geojson properties + if( 'string' !== typeof doc.payload.id ) return warning(); + var idParts = doc.payload.id.split('/'); + doc.type = idParts[0]; + doc.id = idParts[1]; + + // split payload geo string in to geojson properties + if( 'string' !== typeof doc.payload.geo ) return warning(); + var geoParts = doc.payload.geo.split(','); + doc.lat = parseFloat( geoParts[0] ); + doc.lng = parseFloat( geoParts[1] ); + + // remove payload from doc + delete doc.payload; + return doc; + + // filter-out invalid entries + }).filter( function( doc ){ + return doc; + }); + + // convert to geojson + GeoJSON.parse( geodata, { Point: ['lat', 'lng'] }, cb ); +} + module.exports = setup; \ No newline at end of file diff --git a/package.json b/package.json index 76962055..d5459a14 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "express": "^4.8.8", + "geojson": "https://github.com/missinglink/GeoJSON.js/tarball/master", "geopipes-elasticsearch-backend": "0.0.7", "pelias-esclient": "0.0.25", "toobusy": "^0.2.4" diff --git a/test/ciao/suggest/success.coffee b/test/ciao/suggest/success.coffee index 33f4f221..e54e6424 100644 --- a/test/ciao/suggest/success.coffee +++ b/test/ciao/suggest/success.coffee @@ -9,7 +9,8 @@ response.statusCode.should.equal 200 now = new Date().getTime() should.exist json should.not.exist json.error -should.exist json.date -json.date.should.be.within now-2000, now+2000 -should.exist json.body -json.body.should.be.instanceof Array \ No newline at end of file +json.date.should.be.within now-5000, now+5000 + +#? valid geojson +json.type.should.equal 'FeatureCollection' +json.features.should.be.instanceof Array \ No newline at end of file diff --git a/test/unit/controller/suggest.js b/test/unit/controller/suggest.js index 02c40b43..cd9c8c00 100644 --- a/test/unit/controller/suggest.js +++ b/test/unit/controller/suggest.js @@ -15,7 +15,33 @@ module.exports.tests.interface = function(test, common) { // functionally test controller (backend success) module.exports.tests.functional_success = function(test, common) { - test('functional test', function(t) { + + // expected geojson features for 'client/suggest/ok/1' fixture + var expected = [{ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [ -10.1, 101 ] + }, + properties: { + id: 'mockid', + type: 'mocktype', + value: 1 + } + }, { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [ -10.1, 101 ] + }, + properties: { + id: 'mockid', + type: 'mocktype', + value: 2 + } + }]; + + test('functional success', function(t) { var backend = mockBackend( 'client/suggest/ok/1', function( cmd ){ t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); }); @@ -26,10 +52,12 @@ module.exports.tests.functional_success = function(test, common) { return res; }, json: function( json ){ + console.log(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.equal(json.type, 'FeatureCollection', 'valid geojson'); + t.true(Array.isArray(json.features), 'features is array'); + t.deepEqual(json.features, expected, 'values correctly mapped'); t.end(); } }; @@ -39,7 +67,7 @@ module.exports.tests.functional_success = function(test, common) { // functionally test controller (backend failure) module.exports.tests.functional_failure = function(test, common) { - test('functional test', function(t) { + test('functional failure', function(t) { var backend = mockBackend( 'client/suggest/fail/1', function( cmd ){ t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); }); diff --git a/test/unit/mock/backend.js b/test/unit/mock/backend.js index acecc6fc..32a69228 100644 --- a/test/unit/mock/backend.js +++ b/test/unit/mock/backend.js @@ -1,7 +1,12 @@ +var mockPayload = { + id: 'mocktype/mockid', + geo: '101,-10.1' +}; + var responses = {}; responses['client/suggest/ok/1'] = function( cmd, cb ){ - return cb( undefined, suggestEnvelope([ { value: 1 }, { value: 2 } ]) ); + return cb( undefined, suggestEnvelope([ { value: 1, payload: mockPayload }, { value: 2, payload: mockPayload } ]) ); }; responses['client/suggest/fail/1'] = function( cmd, cb ){ return cb( 'a backend error occurred' );