@opra/common
Version:
Opra common package
151 lines (150 loc) • 6.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FilterRules = void 0;
require("../polifils/array-find-last.js");
const objects_1 = require("@jsopen/objects");
const valgen_1 = require("valgen");
const index_js_1 = require("../exception/index.js");
const index_js_2 = require("../helpers/index.js");
const index_js_3 = require("./ast/index.js");
const parse_js_1 = require("./parse.js");
class FilterRules {
constructor(rules, options) {
this._rules = new index_js_2.ResponsiveMap();
this._decoderCache = new WeakMap();
Object.defineProperty(this, '_rules', {
value: new index_js_2.ResponsiveMap(null, { caseSensitive: options?.caseSensitive }),
enumerable: false,
});
if (rules) {
for (const [k, v] of Object.entries(rules)) {
this.set(k, v);
}
}
}
set(fieldName, options) {
const operators = typeof options?.operators === 'string'
? options.operators.split(/\s*[,| ]\s*/)
: options?.operators;
this._rules.set(fieldName, (0, objects_1.omitUndefined)({
...options,
operators,
}));
}
normalizeFilter(filter, currentType, scope) {
const ast = typeof filter === 'string' ? (0, parse_js_1.parse)(filter) : filter;
return this.normalizeFilterAst(ast, [], currentType, scope);
}
normalizeFilterAst(ast, stack, currentType, scope) {
if (ast instanceof index_js_3.ComparisonExpression) {
stack.push(ast);
this.normalizeFilterAst(ast.left, stack, currentType);
if (!(ast.left instanceof index_js_3.QualifiedIdentifier && ast.left.field)) {
throw new TypeError(`Invalid filter query. Left side should be a data field.`);
}
// Check if filtering accepted for given field
const rule = this._rules.get(ast.left.value);
if (!rule) {
throw new index_js_1.OpraException({
message: `Field '${ast.left.value}' is not available for filter operation`,
code: 'UNACCEPTED_FILTER_FIELD',
details: {
field: ast.left.value,
},
});
}
// Check if filtering endpoint accepted for given field
if (rule.operators && !rule.operators.includes(ast.op)) {
throw new index_js_1.OpraException({
message: `'${ast.left.value}' field do not accept '${ast.op}' filter operator`,
code: 'UNACCEPTED_FILTER_OPERATION',
details: {
field: ast.left.value,
operator: ast.op,
},
});
}
if (rule.mappedField)
ast.left.value = rule.mappedField;
if (rule.prepare)
ast.prepare = rule.prepare;
this.normalizeFilterAst(ast.right, stack, currentType);
stack.pop();
return ast;
}
if (ast instanceof index_js_3.LogicalExpression) {
stack.push(ast);
ast.items.forEach(item => this.normalizeFilterAst(item, stack, currentType));
stack.pop();
return ast;
}
if (ast instanceof index_js_3.ArithmeticExpression) {
stack.push(ast);
ast.items.forEach(item => this.normalizeFilterAst(item.expression, stack, currentType));
stack.pop();
return ast;
}
if (ast instanceof index_js_3.ArrayExpression) {
stack.push(ast);
ast.items.forEach(item => this.normalizeFilterAst(item, stack, currentType));
stack.pop();
return ast;
}
if (ast instanceof index_js_3.ParenthesizedExpression) {
stack.push(ast);
this.normalizeFilterAst(ast.expression, stack, currentType);
stack.pop();
return ast;
}
if (ast instanceof index_js_3.QualifiedIdentifier && currentType) {
ast.value = currentType.normalizeFieldPath(ast.value, { scope });
ast.field = currentType.getField(ast.value, scope);
ast.dataType = ast.field.type;
return ast;
}
if (ast instanceof index_js_3.Literal) {
/** Check if comparison expression has in stack */
const compIdx = stack.findLastIndex(x => x instanceof index_js_3.ComparisonExpression);
if (compIdx >= 0) {
const comp = stack[compIdx];
/** If calling for right side of comparison */
if (ast === comp.right || stack[compIdx + 1] === comp.right) {
/** Check if comparison expression left side is a field */
if (comp &&
comp.left instanceof index_js_3.QualifiedIdentifier &&
comp.left.field) {
if (ast.value == null && !comp.left.field.required)
return ast.value;
let decoder;
if (comp.op === 'like' ||
comp.op === '!like' ||
comp.op === 'ilike' ||
comp.op === '!ilike') {
decoder = valgen_1.isString;
}
else
decoder = this._decoderCache.get(comp.left.field);
if (!decoder) {
decoder = comp.left.field.type.generateCodec('decode', {
scope,
projection: '*',
ignoreWriteonlyFields: true,
coerce: true,
});
this._decoderCache.set(comp.left.field, decoder);
}
ast.value = decoder(ast.value);
}
}
}
}
return ast;
}
toJSON() {
return this._rules.toObject();
}
[Symbol.iterator]() {
return this._rules.entries();
}
}
exports.FilterRules = FilterRules;