-
Notifications
You must be signed in to change notification settings - Fork 8
/
V2FilterProcessor.ts
172 lines (152 loc) · 5.55 KB
/
V2FilterProcessor.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import {IHash} from '../internal/IHash';
import {Util} from '../internal/Util';
import {addParameter, IFilterProcessor} from '../api/IFilterProcessor';
import {Filter} from '../api/Filter';
import {Comparator, Comparators} from '../api/Comparator';
import {Restriction} from '../api/Restriction';
import {NestedRestriction} from '../api/NestedRestriction';
import {OnmsError} from '../api/OnmsError';
import {Operator, Operators} from '../api/Operator';
import {Clause} from '../api/Clause';
import {SearchProperty} from '../api/SearchProperty';
import {SearchPropertyTypes} from '../api/SearchPropertyType';
/**
* Converts a [[Filter]] into ReSTv2 FIQL parameters.
* @category Filtering
*/
export class V2FilterProcessor implements IFilterProcessor {
/** Constant used to represent null values in the V2 API. */
public static NULL_VALUE = '\u0000';
/** Constant used to represent null dates in the V2 API.
* This must be explicitly set as the restriction value when using
* either the NULL or NOTNULL comparators on date fields.
*/
public static NULL_DATE = '1970-01-01T00:00:00.000+0000';
/**
* pre-encoded to avoid running `encodeURIComponent` every time we deal with a null date
* @hidden
*/
private static NULL_DATE_ENCODED = encodeURIComponent(V2FilterProcessor.NULL_DATE);
/** The accessor for Properties */
private searchProperties?: SearchProperty[];
constructor(searchProperties?: SearchProperty[]) {
this.searchProperties = searchProperties;
}
/** Given a filter, return a hash of URL parameters. */
public getParameters(filter: Filter): IHash<string|string[]> {
const ret = {} as IHash<string|string[]>;
if (filter.limit !== undefined) {
addParameter(ret, 'limit', filter.limit);
}
const search = this.toFIQL(filter.clauses);
if (search.length > 0) {
addParameter(ret, '_s', search);
}
if (filter.orderBy && filter.orderBy.length > 0) {
const orders = filter.orderBy.map((o) => o.order.label).filter((val, index, self) => self.indexOf(val) === index);
if (orders.length > 1) {
throw new OnmsError('The V2 ReST API only supports one order (ASC or DESC), they cannot be mixed.');
}
addParameter(ret, 'order', orders[0] || 'DESC');
for (const orderBy of filter.orderBy) {
addParameter(ret, 'orderBy', orderBy.attribute);
}
}
return ret;
}
/**
* Given a comparator, convert it to a correspond comparator
* that can be used in the FIQL expression.
*/
private toFIQLComparator(comparator: Comparator) {
switch (comparator) {
case Comparators.EQ:
case Comparators.NULL:
return '==';
case Comparators.NE:
case Comparators.NOTNULL:
return '!=';
case Comparators.GT:
return '=gt=';
case Comparators.LT:
return '=lt=';
case Comparators.GE:
return '=ge=';
case Comparators.LE:
return '=le=';
case Comparators.LIKE:
return '==';
case Comparators.ILIKE:
default:
throw new OnmsError('Unsupported comparator type: ' + comparator);
}
}
/** Return a search property by ID */
private getProperty(id: string) {
if (this.searchProperties) {
return this.searchProperties.find((prop: any) => prop.id === id);
}
return undefined;
}
/** Given a restriction, compute the value to use in the FIQL expression. */
private toFIQLValue(restriction: Restriction) {
switch (restriction.comparator) {
case Comparators.NULL:
case Comparators.NOTNULL:
return restriction.value === undefined ? V2FilterProcessor.NULL_VALUE : encodeURIComponent(restriction.value);
default:
if (restriction.value === 'null' || restriction.value === void 0) {
const property = this.getProperty(restriction.attribute);
if (property && property.type === SearchPropertyTypes.TIMESTAMP) {
return V2FilterProcessor.NULL_DATE_ENCODED;
}
return V2FilterProcessor.NULL_VALUE;
}
return encodeURIComponent(this.applyDateConversion(restriction.value));
}
}
/** Given an operator, convert it to the corresponding FIQL operator. */
private toFIQLOperator(operator: Operator) {
switch (operator) {
case Operators.AND:
return ';';
case Operators.OR:
return ',';
default:
throw new OnmsError('Unsupported operator type: ' + operator);
}
}
/** Given a list of clauses, recursively generate the FIQL query string. */
private toFIQL(clauses: Clause[] | undefined) {
let search = '';
if (!clauses || clauses.length === 0) {
return search;
}
for (const clause of clauses) {
if (search.length > 0) {
search += this.toFIQLOperator(clause.operator);
}
if (clause.restriction instanceof NestedRestriction) {
search += '(' + this.toFIQL(clause.restriction.clauses) + ')';
} else {
const restriction = clause.restriction as Restriction;
const comp = this.toFIQLComparator(restriction.comparator);
const value = this.toFIQLValue(restriction);
search += [restriction.attribute, comp, value].join('');
}
}
return search;
}
/**
* If the given value is a date value, it is converted to be properly parsed by the OpenNMS ReST API,
* otherwise it is not modified.
*
* @param value Any value which may need conversion.
*/
private applyDateConversion(value: any): any {
if (Util.isDateObject(value)) {
return Util.toDateString(value);
}
return value;
}
}