/
filter-query.ts
119 lines (94 loc) · 3.27 KB
/
filter-query.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { _ } from '@feathersjs/commons';
import { BadRequest } from '@feathersjs/errors';
function parse (number: any) {
if (typeof number !== 'undefined') {
return Math.abs(parseInt(number, 10));
}
return undefined;
}
// Returns the pagination limit and will take into account the
// default and max pagination settings
function getLimit (limit: any, paginate: any) {
if (paginate && paginate.default) {
const lower = typeof limit === 'number' && !isNaN(limit) ? limit : paginate.default;
const upper = typeof paginate.max === 'number' ? paginate.max : Number.MAX_VALUE;
return Math.min(lower, upper);
}
return limit;
}
// Makes sure that $sort order is always converted to an actual number
function convertSort (sort: any) {
if (typeof sort !== 'object' || Array.isArray(sort)) {
return sort;
}
return Object.keys(sort).reduce((result, key) => {
result[key] = typeof sort[key] === 'object'
? sort[key] : parseInt(sort[key], 10);
return result;
}, {} as { [key: string]: number });
}
function cleanQuery (query: any, operators: any, filters: any): any {
if (Array.isArray(query)) {
return query.map(value => cleanQuery(value, operators, filters));
} else if (_.isObject(query) && query.constructor === {}.constructor) {
const result: { [key: string]: any } = {};
_.each(query, (value, key) => {
if (key[0] === '$') {
if (filters[key] !== undefined) {
return;
}
if (!operators.includes(key)) {
throw new BadRequest(`Invalid query parameter ${key}`, query);
}
}
result[key] = cleanQuery(value, operators, filters);
});
Object.getOwnPropertySymbols(query).forEach(symbol => {
// @ts-ignore
result[symbol] = query[symbol];
});
return result;
}
return query;
}
function assignFilters (object: any, query: any, filters: any, options: any) {
if (Array.isArray(filters)) {
_.each(filters, (key) => {
if (query[key] !== undefined) {
object[key] = query[key];
}
});
} else {
_.each(filters, (converter, key) => {
const converted = converter(query[key], options);
if (converted !== undefined) {
object[key] = converted;
}
});
}
return object;
}
export const FILTERS = {
$sort: (value: any) => convertSort(value),
$limit: (value: any, options: any) => getLimit(parse(value), options.paginate),
$skip: (value: any) => parse(value),
$select: (value: any) => value
};
export const OPERATORS = ['$in', '$nin', '$lt', '$lte', '$gt', '$gte', '$ne', '$or'];
// Converts Feathers special query parameters and pagination settings
// and returns them separately a `filters` and the rest of the query
// as `query`
export default function filterQuery (query: any, options: any = {}) {
const {
filters: additionalFilters = {},
operators: additionalOperators = []
} = options;
const result: { [key: string]: any } = {};
result.filters = assignFilters({}, query, FILTERS, options);
result.filters = assignFilters(result.filters, query, additionalFilters, options);
result.query = cleanQuery(query, OPERATORS.concat(additionalOperators), result.filters);
return result;
}
if (typeof module !== 'undefined') {
module.exports = Object.assign(filterQuery, module.exports);
}