diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19063e44..65843d42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ Make sure you have the following tools installed: - Docker - Docker Compose -- Node.Js v14 +- Node.Js v16 - Yarn package manager Fork, then clone the repo: diff --git a/packages/api/lib/controllers/boxesController.js b/packages/api/lib/controllers/boxesController.js index 03170c17..4d1e9516 100644 --- a/packages/api/lib/controllers/boxesController.js +++ b/packages/api/lib/controllers/boxesController.js @@ -203,7 +203,7 @@ const geoJsonStringifyReplacer = function geoJsonStringifyReplacer (key, box) { * @apiParam {Boolean="true","false"} [classify=false] if specified, the api will classify the boxes accordingly to their last measurements. * @apiParam {Boolean="true","false"} [minimal=false] if specified, the api will only return a minimal set of box metadata consisting of [_id, updatedAt, currentLocation, exposure, name] for a fast response. * @apiParam {Boolean="true","false"} [full=false] if true the API will return populated lastMeasurements (use this with caution for now, expensive on the database) - * @apiParam {String} [near] A comma separated coordinate, if specified, the api will only return senseBoxes within maxDistance (in m) of this location + * @apiParam {Number} [near] A comma separated coordinate, if specified, the api will only return senseBoxes within maxDistance (in m) of this location * @apiParam {Number} [maxDistance=1000] the amount of meters around the near Parameter that the api will search for senseBoxes * @apiUse ExposureFilterParam * @apiUse BBoxParam @@ -819,7 +819,7 @@ module.exports = { allowedValues: ['true', 'false'], }, { name: 'full', defaultValue: 'false', allowedValues: ['true', 'false'] }, - { name: 'near' }, + { predef: 'near' }, { name: 'maxDistance' }, { predef: 'bbox' }, ]), diff --git a/packages/api/lib/helpers/userParamHelpers.js b/packages/api/lib/helpers/userParamHelpers.js index d5e85755..4eddac6b 100644 --- a/packages/api/lib/helpers/userParamHelpers.js +++ b/packages/api/lib/helpers/userParamHelpers.js @@ -348,6 +348,27 @@ const retrieveLocationParameter = function ({ value }) { } }; +const retrieveNearParameter = function ({ value, dataType, dataTypeIsArray }) { + try { + // wrap dataType in array for calling of casting function + dataType = dataTypeIsArray ? dataType[0] : dataType; + if (!dataTypeIsArray && Array.isArray(value)) { + return { error: ARRAY_NOT_ALLOWED }; + } + + // test and cast value against dataType + value = castParam(value, dataType, dataTypeIsArray); + + if (typeof value === 'undefined') { + return { error: CAST_FAILED }; + } + + return { castedValue: transformAndValidateCoords(value) }; + } catch (err) { + return { error: ERROR_CUSTOM_MESSAGE, message: err.message }; + } +}; + const GET_DATA_MULTI_DEFAULT_COLUMNS = ['sensorId', 'createdAt', 'value', 'lat', 'lon'], GET_DATA_MULTI_ALLOWED_COLUMNS = ['createdAt', 'value', 'lat', 'lon', 'height', 'unit', 'boxId', 'sensorId', 'phenomenon', 'sensorType', 'boxName', 'exposure']; @@ -389,7 +410,7 @@ const retrieveParametersPredefs = { return { name: 'bbox', dataType: 'bbox' }; }, 'near' () { - return { name: 'near', dataType: 'as-is' }; + return { name: 'near', dataType: ['Number'], paramValidatorAndParser: retrieveNearParameter }; }, 'maxDistance' () { return { name: 'maxDistance', dataType: 'as-is' }; diff --git a/packages/models/src/box/box.js b/packages/models/src/box/box.js index 32200502..279799ae 100644 --- a/packages/models/src/box/box.js +++ b/packages/models/src/box/box.js @@ -1005,7 +1005,7 @@ const buildFindBoxesQuery = function buildFindBoxesQuery (opts = {}) { '$near': { '$geometry': { type: 'Point', - coordinates: [near.split(',')[0], near.split(',')[1]] + coordinates: [near[0], near[1]] }, '$maxDistance': maxDistance ? maxDistance : 1000, } diff --git a/tests/tests/002-location_tests.js b/tests/tests/002-location_tests.js index ec0bbb49..63c4ae03 100644 --- a/tests/tests/002-location_tests.js +++ b/tests/tests/002-location_tests.js @@ -284,6 +284,14 @@ describe('openSenseMap API locations tests', function () { }); }); + it('should reject filtering boxes near a location with wrong parameter values', function () { + return chakram.get(`${BASE_URL}?near=test,60`).then(function (response) { + expect(response).to.have.status(422); + + return chakram.wait(); + }); + }); + }); describe('POST /boxes/:boxID/:sensorID', function () {