Skip to content

Commit 58f3787

Browse files
add min max parameter validation for Number and Integer datatypes
1 parent b590a21 commit 58f3787

File tree

3 files changed

+47
-32
lines changed

3 files changed

+47
-32
lines changed

lib/controllers/measurementsController.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ module.exports = {
340340
{ name: 'download', defaultValue: 'false', allowedValues: ['true', 'false'] },
341341
{ predef: 'delimiter' },
342342
{ name: 'outliers', allowedValues: ['mark', 'replace'] },
343-
{ name: 'outlierWindow', dataType: 'Number', aliases: ['outlier-window'], defaultValue: 15 },
343+
{ name: 'outlierWindow', dataType: 'Integer', aliases: ['outlier-window'], defaultValue: 15, min: 1, max: 50 },
344344
{ predef: 'toDate' },
345345
{ predef: 'fromDate' }
346346
]),

lib/controllers/statisticsController.js

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
const models = require('../models'),
4-
{ BadRequestError, UnprocessableEntityError } = require('restify-errors'),
4+
{ UnprocessableEntityError } = require('restify-errors'),
55
statistics = require('../statistics'),
66
{ addCache } = require('../helpers/apiUtils'),
77
{ retrieveParameters, validateFromToTimeParams } = require('../helpers/userParamHelpers'),
@@ -72,26 +72,6 @@ const idwColumns = ['sensorId', 'value', 'lat', 'lon'];
7272
const idwHandler = function (req, res, next) {
7373
const { phenomenon, bbox, exposure, cellWidth, gridType, power, numTimeSteps, numClasses, fromDate, toDate } = req._userParams;
7474

75-
// validate numTimeSteps
76-
if (numTimeSteps < 1 || numTimeSteps >= 10) {
77-
return next(new BadRequestError('parameter numTimeSteps must be between 1 and 10'));
78-
}
79-
80-
// validate power
81-
if (power < 1 || power > 9) {
82-
return next(new BadRequestError('parameter power must be between 1 and 9'));
83-
}
84-
85-
// validate cellWidth
86-
if (cellWidth < 0) {
87-
return next(new BadRequestError('parameter cellWidth must be positive'));
88-
}
89-
90-
// validate numClasses
91-
if (numClasses < 0) {
92-
return next(new BadRequestError('parameter numClasses must be positive'));
93-
}
94-
9575
// validate bbox param, we don't want too much load on our server!
9676
const areaSqKm = area(bbox) / 10e6;
9777

@@ -152,14 +132,14 @@ module.exports = {
152132
],
153133
getIdw: [
154134
retrieveParameters([
155-
{ predef: 'bbox' },
135+
{ predef: 'bbox', required: true },
156136
{ name: 'exposure', allowedValues: models.Box.BOX_VALID_EXPOSURES },
157137
{ name: 'phenomenon', required: true },
158138
{ name: 'gridType', defaultValue: 'hex', allowedValues: ['hex', 'square', 'triangle'] },
159-
{ name: 'cellWidth', dataType: 'Number', defaultValue: 50 },
160-
{ name: 'power', dataType: 'Number', defaultValue: 1 },
161-
{ name: 'numTimeSteps', dataType: 'Number', defaultValue: 6 },
162-
{ name: 'numClasses', dataType: 'Number', defaultValue: 6 },
139+
{ name: 'cellWidth', dataType: 'Number', defaultValue: 50, min: 0.001 },
140+
{ name: 'power', dataType: 'Number', defaultValue: 1, min: 1, max: 9 },
141+
{ name: 'numTimeSteps', dataType: 'Integer', defaultValue: 6, min: 1, max: 10 },
142+
{ name: 'numClasses', dataType: 'Integer', defaultValue: 6, min: 1 },
163143
{ predef: 'toDate' },
164144
{ predef: 'fromDate' }
165145
]),

lib/helpers/userParamHelpers.js

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ const paramParsersAndChecks = {
142142
'bbox': {
143143
parser: bboxParser,
144144
check: poly => poly
145+
},
146+
'Integer': {
147+
parser: Number.parseFloat,
148+
check: Number.isSafeInteger,
149+
failOnCheckResultEquals: false
145150
}
146151
};
147152

@@ -208,25 +213,53 @@ const extractParam = function extractParam ({ req: { params, rawBody, body, _bod
208213
return { value, nameUsed };
209214
};
210215

216+
const validateMinMaxParam = function validateMinMaxParam (valueArr, min, max) {
217+
if (!Array.isArray(valueArr)) {
218+
return valueArr;
219+
}
220+
221+
for (const value of valueArr) {
222+
const tooLow = (min && value < min),
223+
tooHigh = (max && value > max);
224+
225+
const errStr = `Supplied value ${value} is outside of allowed range`;
226+
if (tooLow === true && tooHigh === true) {
227+
return new Error(`${errStr} (${min}, ${max})`);
228+
}
229+
230+
if (tooLow === true) {
231+
return new Error(`${errStr} (< ${min})`);
232+
}
233+
234+
if (tooHigh === true) {
235+
return new Error(`${errStr} (> ${max})`);
236+
}
237+
}
238+
239+
return valueArr;
240+
};
241+
211242
const ARRAY_NOT_ALLOWED = 1,
212243
CAST_FAILED = 2,
213244
ILLEGAL_VALUE = 3,
214245
ERROR_CUSTOM_MESSAGE = 4;
215246

216-
const validateAndCastParam = function validateAndCastParam ({ value, dataType, allowedValues, mapping, dataTypeIsArray }) {
247+
const validateAndCastParam = function validateAndCastParam ({ value, dataType, allowedValues, mapping, dataTypeIsArray, min, max }) {
217248
// wrap dataType in array for calling of casting function
218249
dataType = (dataTypeIsArray ? dataType[0] : dataType);
219250
if (!dataTypeIsArray && Array.isArray(value)) {
220251
return { error: ARRAY_NOT_ALLOWED };
221252
}
222253

223254
// test and cast value against dataType
224-
const castedValue = castParam(value, dataType, dataTypeIsArray);
255+
let castedValue = castParam(value, dataType, dataTypeIsArray);
225256

226257
if (typeof castedValue === 'undefined') {
227258
return { error: CAST_FAILED };
228259
}
229260

261+
castedValue = validateMinMaxParam(castedValue, min, max);
262+
230263
if (castedValue instanceof Error) {
231264
return { error: ERROR_CUSTOM_MESSAGE, message: castedValue.message };
232265
}
@@ -325,7 +358,7 @@ const retrieveParametersPredefs = {
325358
}
326359
};
327360

328-
const handleAndSetParameterRequest = function handleAndSetParameterRequest (req, next, { name, aliases, dataType = 'String', allowedValues, mapping, required = false, defaultValue, paramValidatorAndParser = validateAndCastParam }) {
361+
const handleAndSetParameterRequest = function handleAndSetParameterRequest (req, next, { name, aliases, dataType = 'String', allowedValues, mapping, required = false, defaultValue, min, max, paramValidatorAndParser = validateAndCastParam }) {
329362
// extract param from request
330363
const { value, nameUsed } = extractParam({ req, name, aliases });
331364
// there was no user supplied value but a default value
@@ -344,7 +377,7 @@ const handleAndSetParameterRequest = function handleAndSetParameterRequest (req,
344377
const dataTypeIsArray = Array.isArray(dataType);
345378
// at this point we can assume the parameter was set
346379
// validate
347-
const { castedValue, error, message } = paramValidatorAndParser({ value, dataType, allowedValues, mapping, dataTypeIsArray });
380+
const { castedValue, error, message } = paramValidatorAndParser({ value, dataType, allowedValues, mapping, dataTypeIsArray, min, max });
348381
switch (error) {
349382
case ARRAY_NOT_ALLOWED:
350383
return next(new BadRequestError(`Parameter ${nameUsed} must only be specified once`));
@@ -365,12 +398,14 @@ const handleAndSetParameterRequest = function handleAndSetParameterRequest (req,
365398
// A single parameter has the following properties:
366399
// name: String
367400
// aliases: String[]
368-
// dataType: ['String', ['String'], 'StringWithEmpty', ['StringWithEmpty'], 'Number', ['Number'] 'object', ['object'], 'ISO8601', ['ISO8601'], 'as-is'] // default: String 'as-is' disables casting and lets restify cast the value
401+
// dataType: ['String', ['String'], 'StringWithEmpty', ['StringWithEmpty'], 'Number', ['Number'] 'object', ['object'], 'ISO8601', ['ISO8601'], 'as-is', 'Integer', ['Integer']] // default: String 'as-is' disables casting and lets restify cast the value
369402
// allowedValues: String[]
370403
// mapping: Object
371404
// required: Boolean // default: false
372405
// defaultValue: Anything
373406
// predef: load definition from elsewhere
407+
// min: minimal value for Numbers and Integers. Compared with >=
408+
// max: maximal value for Numbers and Integers. Compared with <
374409
const retrieveParameters = function retrieveParameters (parameters = []) {
375410
return function (req, res, next) {
376411
//for (let { name, aliases, dataType, allowedValues, mapping, required, defaultValue, predef } of parameters) {

0 commit comments

Comments
 (0)