Skip to content

Commit

Permalink
back: Add validation for numerical inputs in API.
Browse files Browse the repository at this point in the history
This commit includes the addition of a utility function that checks the
range of the data types and validates them. This is particularly useful
for numerical inputs in the API that need to be within a specific range.
The commit also updates the backend routes to use these validations.

Issue: #202

Signed-off-by: Nikolay Martyanov <[email protected]>
  • Loading branch information
OhmSpectator committed Jan 13, 2024
1 parent 527ad8e commit 39e8890
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 11 deletions.
5 changes: 4 additions & 1 deletion backend/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
"files": ["*.md"],
"parser": "eslint-plugin-markdownlint/parser",
"extends": ["plugin:markdownlint/recommended"]
}]
}],
"parserOptions": {
"ecmaVersion": 2020 // Need to use BigInt
}
}
42 changes: 32 additions & 10 deletions backend/src/routes/regionRoutes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const express = require('express');
const { check, query, validationResult } = require('express-validator');
const regionController = require('../controllers/regionController');
const { getDataTypeRange } = require('../utils/dataTypes');
const { Hierarchy, Region } = require('../models');

const router = express.Router();

Expand All @@ -13,7 +15,9 @@ router.get(
router.get(
'/root',
...[
check('hierarchyId').optional().isInt().withMessage('Hierarchy ID must be an integer'),
check('hierarchyId').optional().isInt(
{ min: 0, max: getDataTypeRange(Hierarchy, 'hierarchyId').max },
).withMessage('Hierarchy ID must be valid non-negative integer'),
],
async (req, res) => {
const errors = validationResult(req);
Expand All @@ -29,7 +33,9 @@ router.get(
'/search',
[
query('query').isString().withMessage('Query must be a string'),
query('hierarchyId').optional().isInt().withMessage('Hierarchy ID must be an integer'),
query('hierarchyId').optional().isInt(
{ min: 0, max: getDataTypeRange(Hierarchy, 'hierarchyId').max },
).withMessage('Hierarchy ID must be a valid non-negative integer'),
],
async (req, res) => {
const errors = validationResult(req);
Expand All @@ -44,8 +50,12 @@ router.get(
router.get(
'/:regionId',
...[
check('regionId').isInt().withMessage('Region ID must be an integer'),
check('hierarchyId').optional().isInt().withMessage('Hierarchy ID must be an integer'),
check('regionId').isInt(
{ min: 0, max: getDataTypeRange(Region, 'id').max },
).withMessage('Region ID must be a valid non-negative integer'),
check('hierarchyId').optional().isInt(
{ min: 0, max: getDataTypeRange(Hierarchy, 'hierarchyId').max },
).withMessage('Hierarchy ID must be a valid non-negative integer'),
],
async (req, res) => {
const errors = validationResult(req);
Expand All @@ -59,9 +69,13 @@ router.get(
router.get(
'/:regionId/subregions',
...[
check('regionId').isInt().withMessage('Region ID must be an integer'),
check('regionId').isInt(
{ min: 0, max: getDataTypeRange(Region, 'id').max },
).withMessage('Region ID must be a valid non-negative integer'),
query('getAll').optional().isBoolean().withMessage('getAll must be a boolean'),
query('hierarchyId').optional().isInt().withMessage('Hierarchy ID must be an integer'),
query('hierarchyId').optional().isInt(
{ min: 0, max: getDataTypeRange(Hierarchy, 'hierarchyId').max },
).withMessage('Hierarchy ID must be a valid non-negative integer'),
],
async (req, res) => {
const errors = validationResult(req);
Expand All @@ -76,7 +90,9 @@ router.get(
router.get(
'/:regionId/ancestors',
...[
check('regionId').isInt().withMessage('Region ID must be an integer'),
check('regionId').isInt(
{ min: 0, max: getDataTypeRange(Region, 'id').max },
).withMessage('Region ID must be a valid non-negative integer'),
],
async (req, res) => {
const errors = validationResult(req);
Expand All @@ -91,7 +107,9 @@ router.get(
router.get(
'/:regionId/geometry',
...[
check('regionId').isInt().withMessage('Region ID must be an integer'),
check('regionId').isInt(
{ min: 0, max: getDataTypeRange(Region, 'id').max },
).withMessage('Region ID must be a valid non-negative integer'),
check('resolveEmpty').optional().isBoolean().withMessage('resolveEmpty must be a boolean'),
],
async (req, res) => {
Expand All @@ -107,8 +125,12 @@ router.get(
router.get(
'/:regionId/siblings',
...[
check('regionId').isInt().withMessage('Region ID must be an integer'),
check('hierarchyId').optional().isInt().withMessage('Hierarchy ID must be an integer'),
check('regionId').isInt(
{ min: 0, max: getDataTypeRange(Region, 'id').max },
).withMessage('Region ID must be a valid non-negative integer'),
check('hierarchyId').optional().isInt(
{ min: 0, max: getDataTypeRange(Hierarchy, 'hierarchyId').max },
).withMessage('Hierarchy ID must be a valid non-negative integer'),
],
async (req, res) => {
const errors = validationResult(req);
Expand Down
23 changes: 23 additions & 0 deletions backend/src/utils/dataTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function getDataTypeRange(model, fieldName) {
const field = model.rawAttributes[fieldName];
if (!field) {
throw new Error(`Field ${fieldName} does not exist in the model`);
}

const dataType = field.type.key;

switch (dataType) {
case 'INTEGER': // Standard 4-byte integer
return { min: -2147483648, max: 2147483647 };
case 'BIGINT': // 8-byte integer
return { min: -9223372036854775808, max: 9223372036854775807n };
case 'SMALLINT': // 2-byte integer
return { min: -32768, max: 32767 };
default:
throw new Error(`Range for data type ${dataType} is not defined`);
}
}

module.exports = {
getDataTypeRange,
};

0 comments on commit 39e8890

Please sign in to comment.