diff --git a/src/error.ts b/src/error.ts index b3d3be9c9f..c962d09229 100644 --- a/src/error.ts +++ b/src/error.ts @@ -3,11 +3,13 @@ import logger from './logger'; export enum ErrorCode { NOT_FOUND = 'NOT_FOUND', + FIELD_NOT_FOUND = 'FIELD_NOT_FOUND', INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', } enum HTTPStatus { NOT_FOUND = 404, + FIELD_NOT_FOUND = 400, INTERNAL_SERVER_ERROR = 500, } diff --git a/src/logger.ts b/src/logger.ts index 62056ae564..5f862e617a 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,5 +1,5 @@ -import pino from "pino"; +import pino from 'pino'; -const logger = pino(); +const logger = pino({ level: process.env.LOG_LEVEL || 'info' }); export default logger; diff --git a/src/middleware/collection-exists.ts b/src/middleware/collection-exists.ts index e9b14bd613..4d76dbe607 100644 --- a/src/middleware/collection-exists.ts +++ b/src/middleware/collection-exists.ts @@ -8,7 +8,10 @@ const collectionExists: RequestHandler = asyncHandler(async (req, res, next) => const exists = await database.schema.hasTable(req.params.collection); - if (exists) return next(); + if (exists) { + res.locals.collection = req.params.collection; + return next(); + } throw new APIError(ErrorCode.NOT_FOUND, `Collection "${req.params.collection}" doesn't exist.`); }); diff --git a/src/middleware/sanitize-query.ts b/src/middleware/sanitize-query.ts index aea4dc5e76..70fd09e0bd 100644 --- a/src/middleware/sanitize-query.ts +++ b/src/middleware/sanitize-query.ts @@ -5,6 +5,7 @@ import { RequestHandler } from 'express'; import { Query } from '../types/query'; +import logger from '../logger'; const sanitizeQuery: RequestHandler = (req, res, next) => { if (!req.query) return; diff --git a/src/middleware/validate-query.ts b/src/middleware/validate-query.ts new file mode 100644 index 0000000000..a425d6563f --- /dev/null +++ b/src/middleware/validate-query.ts @@ -0,0 +1,47 @@ +/** + * Validates query parameters. + * We'll check if all fields you're trying to access exist + * + * This has to be run after sanitizeQuery + */ + +import { RequestHandler } from 'express'; +import { Query } from '../types/query'; +import { hasField } from '../services/schema'; +import asyncHandler from 'express-async-handler'; +import APIError, { ErrorCode } from '../error'; + +const validateQuery: RequestHandler = asyncHandler(async (req, res, next) => { + if (!res.locals.collection) return next(); + if (!res.locals.query) return next(); + + const query: Query = res.locals.query; + + const fieldsToCheck = new Set(); + + if (query.fields) { + query.fields.forEach((field) => fieldsToCheck.add(field)); + } + + try { + await Promise.all( + Array.from(fieldsToCheck).map( + (field) => + new Promise(async (resolve, reject) => { + const exists = await hasField(res.locals.collection, field); + if (exists) return resolve(); + return reject({ collection: res.locals.collection, field: field }); + }) + ) + ); + } catch ({ collection, field }) { + throw new APIError( + ErrorCode.FIELD_NOT_FOUND, + `Field "${field}" doesn't exist in "${collection}"` + ); + } + + return next(); +}); + +export default validateQuery; diff --git a/src/routes/items.ts b/src/routes/items.ts index 10e245b660..d28bd29c3c 100644 --- a/src/routes/items.ts +++ b/src/routes/items.ts @@ -3,6 +3,7 @@ import asyncHandler from 'express-async-handler'; import { createItem, readItems, readItem, updateItem, deleteItem } from '../services/items'; import sanitizeQuery from '../middleware/sanitize-query'; import collectionExists from '../middleware/collection-exists'; +import validateQuery from '../middleware/validate-query'; const router = express.Router(); @@ -19,6 +20,7 @@ router.get( '/:collection', collectionExists, sanitizeQuery, + validateQuery, asyncHandler(async (req, res) => { const records = await readItems(req.params.collection, res.locals.query); diff --git a/src/services/items.ts b/src/services/items.ts index 443b198387..a7458402a3 100644 --- a/src/services/items.ts +++ b/src/services/items.ts @@ -1,6 +1,5 @@ import database from '../database'; import { Query } from '../types/query'; -import logger from '../logger'; export const createItem = async ( collection: string, diff --git a/src/services/schema.ts b/src/services/schema.ts index 715c33ba86..bca5714880 100644 --- a/src/services/schema.ts +++ b/src/services/schema.ts @@ -3,3 +3,7 @@ import database from '../database'; export const hasCollection = async (collection: string) => { return await database.schema.hasTable(collection); }; + +export const hasField = async (collection: string, field: string) => { + return await database.schema.hasColumn(collection, field); +};