UNPKG

@truepic/queryql

Version:

Easily add filtering, sorting, and pagination to your REST API through your old friend: the query string!

110 lines (91 loc) 2.34 kB
const is = require('is') const BaseParser = require('./base') const flattenMap = require('../services/flatten_map') class FilterParser extends BaseParser { static get DEFAULTS() { return { name: null, field: null, operator: null, value: null, } } buildKey({ name, operator }) { return `${this.queryKey}:${name}[${operator}]` } defineValidation(schema) { const defaultOperator = this.defaults.operator const mapNamesToOperators = Object.entries( this.schema.mapFilterNamesToOperators(), ) const values = [ schema.array(), schema.string(), schema.number(), schema.boolean(), schema.valid(null), ] return schema.object().keys( mapNamesToOperators.reduce((accumulator, [field, operators]) => { const operatorObject = schema .object() .pattern(schema.string().valid(...operators), [ schema.object(), ...values, ]) return { ...accumulator, [field]: operators.includes(defaultOperator) ? [...values, operatorObject] : operatorObject, } }, {}), ) } flatten(map) { return flattenMap({ map, value: (value) => value.value, }) } parseObject(name, value) { return Object.keys(value).map((operator) => { const { options } = this.schema.filters.get(`${name}[${operator}]`) return { ...this.defaults, name, field: options.field || name, operator, value: value[operator], } }) } parseNonObject(name, value) { const { options } = this.schema.filters.get( `${name}[${this.defaults.operator}]`, ) return { ...this.defaults, name, field: options.field || name, value, } } parse() { if (!this.query) { return new Map() } this.validate() const entries = Object.entries(this.query) const filters = [] for (const [name, value] of entries) { if (is.object(value)) { filters.push(...this.parseObject(name, value)) } else { filters.push(this.parseNonObject(name, value)) } } return new Map(filters.map((filter) => [this.buildKey(filter), filter])) } } module.exports = FilterParser