tg-knex-query-resolver
Version:
TeselaGen's Knex based query resolver
101 lines (88 loc) • 3.58 kB
JavaScript
const parseWhereFilter = require("./parse-where");
const parseGroupFilter = require("./parse-group");
const parseSubqueryFilter = require("./parse-subquery");
const parseExpressionFilter = require("./parse-expression");
// parseFilterQuery parses the client side query JSON
// and converts it to knex JSON that can be resolved
// by the knex query resolver.
// We don't do this in one function because the query JSON
// does not contain the projection details (columns being selected).
// That information will come from the graphQL query and it's
// assumed that the graqhQL resolver will set the project property
// on the returned parsedQuery to the appropriate array of columns
// prior to calling the resolve-query function. For testing purposes
// the projection is defaulted to '*' (select all).
module.exports = function parseFilterQuery(filterQuery, opts) {
var parsedQuery = {
projection: "*",
entity: filterQuery.entity,
filter: [],
};
if (filterQuery.__objectType === "query") {
filterQuery.filters.forEach((filter) => {
var parsedFilter = parseFilter(filter, undefined, filterQuery.entity);
parsedQuery.filter.push(parsedFilter);
});
} else {
throw new Error("Invalid query type: " + filterQuery.__objectType);
}
return parsedQuery;
};
// parseFilter recursively parses the client query
// and converts the client query filters into knex
// resolver JSON ( types to ops ) and maps expressions
// to their appropriate knex functions and arguments.
//
// The client query can chain where filters together
// using either 'and' or 'or'. Additionally the client
// query can group where filters which are combined
// using a joinOperator of 'and' or 'or'. Chain operators
// take precedence over join operators.
// Example:
// where ( id = 54 or name like '%adam%' ) and status = 'active'
// | |
// group with two where filters additional where
// joined with 'or' chained with 'and'
//
// The client query filters can also have a modifier field
// which specifies the 'not' modifier. The modifier should
// never be concatenated into the op name for security reasons.
// Do this:
// if(modifier === 'not') op = "whereNot";
// Not this:
// modifier = modifier || ""
// op = "where" + modifier
//
// For the same reason expressions always need to be mapped as well.
// Do this:
// if(expression === 'lessThan') expressionArgs.push('<');
// Not this (assuming expression has the value '<'):
// expressionArgs.push(expression);
function parseFilter(filter, joinOperator, entity) {
var op = "";
//check for injection or other wrong input
if (joinOperator) {
if (!(joinOperator === "and" || joinOperator === "or")) {
throw Error("Invalide join operator " + joinOperator);
}
}
if (filter.chainedWith) {
if (!(filter.chainedWith === "and" || filter.chainedWith === "or")) {
throw Error("Invalide chain operator " + filter.chainedWith);
}
}
//parse filters based on type
if (filter.type === "where") {
return parseWhereFilter(filter, joinOperator);
}
if (filter.type === "expression") {
return parseExpressionFilter(filter, joinOperator);
}
if (filter.type === "group") {
return parseGroupFilter(filter, joinOperator, parseFilter, entity);
}
if (filter.type === "subquery") {
return parseSubqueryFilter(filter, joinOperator, parseFilter, entity);
}
throw new Error("Unrecognized filter type: " + filter.type);
}