diff --git a/src/middleware/sanitize-query.ts b/src/middleware/sanitize-query.ts index f37142f872..e82cb0c119 100644 --- a/src/middleware/sanitize-query.ts +++ b/src/middleware/sanitize-query.ts @@ -5,6 +5,7 @@ import { RequestHandler } from 'express'; import { Query, Sort, Filter, FilterOperator } from '../types/query'; +import { Meta } from '../types/meta'; const sanitizeQuery: RequestHandler = (req, res, next) => { if (!req.query) return; @@ -39,6 +40,10 @@ const sanitizeQuery: RequestHandler = (req, res, next) => { query.single = sanitizeSingle(req.query.single); } + if (req.query.meta) { + query.meta = sanitizeMeta(req.query.meta); + } + res.locals.query = query; return next(); }; @@ -95,3 +100,17 @@ function sanitizePage(rawPage: any) { function sanitizeSingle(rawSingle: any) { return true; } + +function sanitizeMeta(rawMeta: any) { + if (rawMeta === '*') { + return Object.values(Meta); + } + + if (rawMeta.includes(',')) { + return rawMeta.split(','); + } + + if (Array.isArray(rawMeta)) { + return rawMeta; + } +} diff --git a/src/middleware/validate-query.ts b/src/middleware/validate-query.ts index 2fdc863dbb..92b06c1c32 100644 --- a/src/middleware/validate-query.ts +++ b/src/middleware/validate-query.ts @@ -17,8 +17,11 @@ const validateQuery: RequestHandler = asyncHandler(async (req, res, next) => { const query: Query = res.locals.query; - await validateParams(req.params.collection, query); - await validateFields(req.params.collection, query); + await Promise.all([ + validateParams(req.params.collection, query), + validateFields(req.params.collection, query), + validateMeta(query), + ]); return next(); }); @@ -55,4 +58,10 @@ async function validateFields(collection: string, query: Query) { }); } +async function validateMeta(query: Query) { + if (!query.meta) return; + + return query.meta.every((metaField) => []); +} + export default validateQuery; diff --git a/src/routes/items.ts b/src/routes/items.ts index d28bd29c3c..2a4ce6b791 100644 --- a/src/routes/items.ts +++ b/src/routes/items.ts @@ -4,6 +4,7 @@ import { createItem, readItems, readItem, updateItem, deleteItem } from '../serv import sanitizeQuery from '../middleware/sanitize-query'; import collectionExists from '../middleware/collection-exists'; import validateQuery from '../middleware/validate-query'; +import * as MetaService from '../services/meta'; const router = express.Router(); @@ -22,9 +23,13 @@ router.get( sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { - const records = await readItems(req.params.collection, res.locals.query); + const [records, meta] = await Promise.all([ + readItems(req.params.collection, res.locals.query), + MetaService.getMetaForQuery(req.params.collection, res.locals.query), + ]); return res.json({ + meta: meta, data: records, }); }) diff --git a/src/services/meta.ts b/src/services/meta.ts new file mode 100644 index 0000000000..c97588856a --- /dev/null +++ b/src/services/meta.ts @@ -0,0 +1,31 @@ +import { Query } from '../types/query'; +import database from '../database'; + +export const getMetaForQuery = async (collection: string, query: Query) => { + if (!query.meta) return; + + const results = await Promise.all( + query.meta.map((metaVal) => { + if (metaVal === 'total_count') return totalCount(collection); + if (metaVal === 'filter_count') return filterCount(collection, query); + }) + ); + + return results.reduce((metaObject: Record, value, index) => { + return { + ...metaObject, + [query.meta[index]]: value, + }; + }, {}); +}; + +export const totalCount = async (collection: string) => { + const records = await database(collection).count('*'); + return records[0].count; +}; + +export const filterCount = async (collection: string, query: Query) => { + /** @TODO use actual query builder logic from items service to get count */ + const records = await database(collection).count('*'); + return records[0].count; +}; diff --git a/src/types/meta.ts b/src/types/meta.ts new file mode 100644 index 0000000000..19de6a1c91 --- /dev/null +++ b/src/types/meta.ts @@ -0,0 +1,4 @@ +export enum Meta { + TOTAL_COUNT = 'total_count', + FILTER_COUNT = 'filter_count', +} diff --git a/src/types/query.ts b/src/types/query.ts index b4dd1fc904..f7980421d6 100644 --- a/src/types/query.ts +++ b/src/types/query.ts @@ -1,3 +1,5 @@ +import { Meta } from './meta'; + export type Query = { fields?: string[]; sort?: Sort[]; @@ -6,6 +8,7 @@ export type Query = { offset?: number; page?: number; single?: boolean; + meta?: Meta[]; }; export type Sort = {