UNPKG

tg-knex-query-resolver

Version:

TeselaGen's Knex based query resolver

101 lines (88 loc) 3.58 kB
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); }