@json-api/query-parser
Advanced tools
Comparing version
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const R = require("ramda"); | ||
const parser = require("./parser"); | ||
@@ -8,12 +7,14 @@ const helpers_1 = require("./helpers"); | ||
const constraintLists = parser.parse(filterVal, { startRule: "Filter" }); | ||
const toFieldExpression = helpers_1.listToFieldExpression(filterOperators); | ||
const finalizeExp = (exp) => { | ||
const operatorConfig = filterOperators[exp.operator]; | ||
const finalizeArgsFn = (operatorConfig && operatorConfig.finalizeArgs) | ||
? operatorConfig.finalizeArgs | ||
: helpers_1.finalizeArgs; | ||
return Object.assign({}, exp, { args: finalizeArgsFn(filterOperators, toFieldExpression, exp.operator, exp.args) }); | ||
}; | ||
return constraintLists.map(R.pipe(toFieldExpression, finalizeExp)); | ||
const toFieldExpression = helpers_1.finalizeFieldExpression(filterOperators); | ||
return constraintLists.map(function toFinalExp(rawExp) { | ||
const exp = toFieldExpression(rawExp); | ||
const finalArgs = filterOperators[exp.operator].finalizeArgs(filterOperators, exp.operator, exp.args.map((arg) => { | ||
if (arg && arg.type === 'RawFieldExpression') { | ||
return toFinalExp(arg); | ||
} | ||
return arg; | ||
})); | ||
return Object.assign({}, exp, { args: finalArgs }); | ||
}); | ||
} | ||
exports.default = parse; |
/// <reference types="ramda" /> | ||
import R = require("ramda"); | ||
export declare type Identifier = { | ||
type: "identifier"; | ||
type: "Identifier"; | ||
value: string; | ||
}; | ||
export declare const isId: (it: any) => it is Identifier; | ||
export declare type FinalizeArgs = (operators: OperatorsConfig, listToFieldExp: (parseResult: any) => FieldExpression, operator: string, args: any[]) => any; | ||
export declare type OperatorsConfig = { | ||
[operatorName: string]: { | ||
isBinary: boolean; | ||
finalizeArgs?: FinalizeArgs; | ||
finalizeArgs: (operators: OperatorsConfig, operator: string, args: any[]) => any[]; | ||
} | undefined; | ||
}; | ||
export declare type FieldExpression = ({ | ||
operator: "or" | "and"; | ||
args: FieldExpression[]; | ||
} | { | ||
operator: "eq" | 'neq' | 'ne'; | ||
args: [Identifier, any]; | ||
} | { | ||
operator: "in" | "nin"; | ||
args: [Identifier, string[] | number[]]; | ||
} | { | ||
operator: 'lt' | 'gt' | 'lte' | 'gte'; | ||
args: [Identifier, string | number]; | ||
} | { | ||
export declare type FieldExpression = { | ||
type: "FieldExpression"; | ||
operator: string; | ||
args: any[]; | ||
}); | ||
}; | ||
export declare const isKnownOperator: R.CurriedTypeGuard2<OperatorsConfig, any, Identifier>; | ||
export declare const isBinaryOperator: R.CurriedFunction2<OperatorsConfig, any, boolean>; | ||
export declare const isNaryOperator: R.CurriedFunction2<OperatorsConfig, any, boolean>; | ||
export declare const listToFieldExpression: R.CurriedFunction2<OperatorsConfig, any, { | ||
operator: "or" | "and"; | ||
args: (any | { | ||
operator: "eq" | "neq" | "ne"; | ||
args: [Identifier, any]; | ||
} | { | ||
operator: "in" | "nin"; | ||
args: [Identifier, string[] | number[]]; | ||
} | { | ||
operator: "lt" | "gt" | "lte" | "gte"; | ||
args: [Identifier, string | number]; | ||
} | { | ||
operator: string; | ||
args: any[]; | ||
})[]; | ||
} | { | ||
operator: "eq" | "neq" | "ne"; | ||
args: [Identifier, any]; | ||
} | { | ||
operator: "in" | "nin"; | ||
args: [Identifier, string[] | number[]]; | ||
} | { | ||
operator: "lt" | "gt" | "lte" | "gte"; | ||
args: [Identifier, string | number]; | ||
} | { | ||
operator: string; | ||
args: any[]; | ||
}>; | ||
export declare function finalizeArgs(operators: OperatorsConfig, listToFieldExp: (parseResult: any) => FieldExpression, operator: string, args: any[]): any[]; | ||
export declare const finalizeFieldExpression: R.CurriedFunction2<OperatorsConfig, any, FieldExpression>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const R = require("ramda"); | ||
exports.isId = (it) => it && it.type === "identifier"; | ||
exports.isId = (it) => it && it.type === "Identifier"; | ||
exports.isKnownOperator = R.curry((operators, node) => { | ||
@@ -14,8 +14,10 @@ return exports.isId(node) && Boolean(operators[node.value]); | ||
}); | ||
exports.listToFieldExpression = R.curry((operators, list) => { | ||
if (!Array.isArray(list)) { | ||
throw new SyntaxError("Expression must be a list."); | ||
exports.finalizeFieldExpression = R.curry((operators, it) => { | ||
if (!(it && it.type === "RawFieldExpression")) { | ||
throw new SyntaxError("Expected a parenthesized list."); | ||
} | ||
const list = it.items; | ||
if (list.length === 3 && exports.isBinaryOperator(operators, list[1])) { | ||
return { | ||
type: "FieldExpression", | ||
operator: list[1].value, | ||
@@ -27,2 +29,3 @@ args: [list[0], list[2]] | ||
return { | ||
type: "FieldExpression", | ||
operator: list[0].value, | ||
@@ -34,2 +37,3 @@ args: list.slice(1) | ||
return { | ||
type: "FieldExpression", | ||
operator: "eq", | ||
@@ -44,18 +48,1 @@ args: list | ||
}); | ||
function finalizeArgs(operators, listToFieldExp, operator, args) { | ||
if (operator === "and" || operator === "or") { | ||
if (args.length === 0) { | ||
throw new Error(`The "${operator}" operator requires at least one argument.`); | ||
} | ||
return args.map(it => { | ||
const exp = listToFieldExp(it); | ||
return Object.assign({}, exp, { args: finalizeArgs(operators, listToFieldExp, exp.operator, exp.args) }); | ||
}); | ||
} | ||
else if (operators[operator].isBinary && !exports.isId(args[0])) { | ||
throw new SyntaxError(`"${operator}" operator expects field reference as first argument.`); | ||
} | ||
return args; | ||
} | ||
exports.finalizeArgs = finalizeArgs; | ||
; |
import parseFilter from './filter-param-parser'; | ||
import { finalizeArgs } from './helpers'; | ||
export { parseFilter, finalizeArgs }; | ||
export { parseFilter }; |
@@ -5,3 +5,1 @@ "use strict"; | ||
exports.parseFilter = filter_param_parser_1.default; | ||
const helpers_1 = require("./helpers"); | ||
exports.finalizeArgs = helpers_1.finalizeArgs; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const { expect } = require("chai"); | ||
const helpers_1 = require("../src/helpers"); | ||
const helpers_2 = require("./helpers"); | ||
const filter_param_parser_1 = require("../src/filter-param-parser"); | ||
const noValidationFinalizeArgs = function (a, b, args) { | ||
return args; | ||
}; | ||
const eqOperator = { | ||
"eq": { isBinary: true } | ||
"eq": { isBinary: true, finalizeArgs: noValidationFinalizeArgs } | ||
}; | ||
const andOrOperators = { | ||
"and": { isBinary: false }, | ||
"or": { isBinary: false } | ||
"and": { isBinary: false, finalizeArgs: noValidationFinalizeArgs }, | ||
"or": { isBinary: false, finalizeArgs: noValidationFinalizeArgs } | ||
}; | ||
const gteOperator = { | ||
"gte": { isBinary: true } | ||
"gte": { isBinary: true, finalizeArgs: noValidationFinalizeArgs } | ||
}; | ||
const nowOperator = { | ||
"now": { isBinary: false } | ||
"now": { isBinary: false, finalizeArgs: noValidationFinalizeArgs } | ||
}; | ||
const andOrProperOperators = { | ||
"and-list": { | ||
isBinary: false, | ||
finalizeArgs(a, b, c) { | ||
if (!c.every(it => it && it.type === "FieldExpression")) { | ||
throw new Error("Arguments must be field expressions."); | ||
} | ||
return c; | ||
} | ||
}, | ||
"or-list": { | ||
isBinary: false, | ||
finalizeArgs(a, b, c) { | ||
if (!c.every(it => it && it.type === "FieldExpression")) { | ||
throw new Error("Arguments must be field expressions."); | ||
} | ||
return c; | ||
} | ||
} | ||
}; | ||
const nowProperOperator = { | ||
"now": { | ||
isBinary: false, | ||
finalizeArgs(a, b, c, args) { | ||
finalizeArgs(a, b, args) { | ||
if (args.length) { | ||
@@ -29,6 +54,26 @@ throw new Error("`now` operator cannot take any arguments."); | ||
}; | ||
const withFieldOperators = { | ||
"eq": { | ||
isBinary: true, | ||
finalizeArgs(a, b, args) { | ||
if (!helpers_1.isId(args[0])) { | ||
throw new Error("field reference required as first argument."); | ||
} | ||
return args; | ||
} | ||
}, | ||
"lte": { | ||
isBinary: true, | ||
finalizeArgs(a, b, args) { | ||
if (!helpers_1.isId(args[0])) { | ||
throw new Error("field reference required as first argument."); | ||
} | ||
return args; | ||
} | ||
} | ||
}; | ||
const gteExtendedOperator = { | ||
"gte": { | ||
isBinary: false, | ||
finalizeArgs(a, b, c, args) { | ||
finalizeArgs(a, b, args) { | ||
return ["custom args"]; | ||
@@ -41,3 +86,3 @@ } | ||
it("Should reject them as invalid filter constraints", () => { | ||
expect(() => filter_param_parser_1.default(eqOperator, "()")).to.throw(/Expected comma-separated list/); | ||
expect(() => filter_param_parser_1.default(eqOperator, "()")).to.throw(/Expected field expression/); | ||
}); | ||
@@ -54,3 +99,3 @@ }); | ||
expect(filter_param_parser_1.default(nowOperator, "(now)")).to.deep.equal([ | ||
{ operator: "now", args: [] } | ||
helpers_2.FieldExpression("now", []) | ||
]); | ||
@@ -62,3 +107,3 @@ }); | ||
expect(filter_param_parser_1.default(nowOperator, "(now,1)")).to.deep.equal([ | ||
{ operator: "now", args: [1] } | ||
helpers_2.FieldExpression("now", [1]) | ||
]); | ||
@@ -68,3 +113,3 @@ }); | ||
expect(filter_param_parser_1.default(eqOperator, "(fieldName,1)")).to.deep.equal([ | ||
{ operator: "eq", args: [{ type: "identifier", value: "fieldName" }, 1] } | ||
helpers_2.FieldExpression("eq", [helpers_2.Identifier("fieldName"), 1]) | ||
]); | ||
@@ -85,6 +130,6 @@ }); | ||
expect(filter_param_parser_1.default(eqOperator, "(fieldName,eq,1)")).to.deep.equal([ | ||
{ operator: "eq", args: [{ type: "identifier", value: "fieldName" }, 1] } | ||
helpers_2.FieldExpression("eq", [{ type: "Identifier", value: "fieldName" }, 1]) | ||
]); | ||
expect(filter_param_parser_1.default(gteOperator, "(fieldName,gte,1)")).to.deep.equal([ | ||
{ operator: "gte", args: [{ type: "identifier", value: "fieldName" }, 1] } | ||
helpers_2.FieldExpression("gte", [{ type: "Identifier", value: "fieldName" }, 1]) | ||
]); | ||
@@ -95,26 +140,18 @@ }); | ||
expect(filter_param_parser_1.default(gteOperator, "(gte,gte,1)")).to.deep.equal([ | ||
{ operator: "gte", args: [{ type: "identifier", value: "gte" }, 1] } | ||
helpers_2.FieldExpression("gte", [{ type: "Identifier", value: "gte" }, 1]) | ||
]); | ||
}); | ||
it("should recognize leading n-ary operators", () => { | ||
expect(filter_param_parser_1.default(nowOperator, "(now,fieldName,())")).to.deep.equal([{ | ||
operator: "now", | ||
args: [ | ||
{ type: "identifier", value: "fieldName" }, | ||
[] | ||
] | ||
}]); | ||
expect(filter_param_parser_1.default(nowOperator, "(now,fieldName,[])")).to.deep.equal([ | ||
helpers_2.FieldExpression("now", [helpers_2.Identifier("fieldName"), []]) | ||
]); | ||
}); | ||
it("should prefer a known infixed binary op over a known leadingnary op", () => { | ||
expect(filter_param_parser_1.default(Object.assign({}, nowOperator, gteOperator), "(now,gte,())")) | ||
.to.deep.equal([{ | ||
operator: "gte", | ||
args: [ | ||
{ type: "identifier", value: "now" }, | ||
[] | ||
] | ||
}]); | ||
expect(filter_param_parser_1.default(Object.assign({}, nowOperator, gteOperator), "(now,gte,[2])")) | ||
.to.deep.equal([ | ||
helpers_2.FieldExpression("gte", [helpers_2.Identifier("now"), [2]]) | ||
]); | ||
}); | ||
it("should error if first arg is not a known operator", () => { | ||
expect(() => filter_param_parser_1.default(eqOperator, "(now,fieldName,())")).to.throw(); | ||
expect(() => filter_param_parser_1.default(eqOperator, "(now,fieldName,[])")).to.throw(); | ||
}); | ||
@@ -130,41 +167,40 @@ }); | ||
it("should wrap up all args into an array", () => { | ||
expect(filter_param_parser_1.default(nowOperator, "(now,233,fieldName,(true))")).to.deep.equal([{ | ||
operator: "now", | ||
args: [233, { type: "identifier", value: "fieldName" }, [true]] | ||
}]); | ||
expect(filter_param_parser_1.default(nowOperator, "(now,233,fieldName,(now),[true])")).to.deep.equal([ | ||
helpers_2.FieldExpression("now", [233, helpers_2.Identifier("fieldName"), helpers_2.FieldExpression("now", []), [true]]) | ||
]); | ||
}); | ||
}); | ||
describe("custom finalizeArgs", () => { | ||
it("should use the user's finalizeArgs instead of the built-in one", () => { | ||
describe("finalizeArgs", () => { | ||
it("should call it recursively", () => { | ||
expect(filter_param_parser_1.default(gteExtendedOperator, "(gte,1000,fieldName,230)")).to.deep.equal([ | ||
{ operator: "gte", args: ["custom args"] } | ||
helpers_2.FieldExpression("gte", ["custom args"]) | ||
]); | ||
expect(filter_param_parser_1.default(Object.assign({}, andOrOperators, gteExtendedOperator), "(and,(gte,1000,fieldName,230))")).to.deep.equal([ | ||
helpers_2.FieldExpression("and", [helpers_2.FieldExpression("gte", ["custom args"])]) | ||
]); | ||
expect(() => filter_param_parser_1.default(nowProperOperator, "(now,1)")) | ||
.to.throw(/`now` operator cannot take any arguments/); | ||
expect(filter_param_parser_1.default(nowProperOperator, "(now)")).to.deep.equal([ | ||
{ operator: "now", args: [] } | ||
helpers_2.FieldExpression("now", []) | ||
]); | ||
const missingFieldError = /field reference required/; | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "(2,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "(2,eq,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "(2,lte,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "((a,eq,c),lte,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "(null,lte,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "([a,b],lte,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "(test,1)")).to.not.throw(); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "(test,eq,1)")).to.not.throw(); | ||
expect(() => filter_param_parser_1.default(withFieldOperators, "(test,lte,1)")).to.not.throw(); | ||
}); | ||
}); | ||
describe("binary operators", () => { | ||
it("should require the first item be a field reference", () => { | ||
const missingFieldError = /expects field reference/; | ||
expect(() => filter_param_parser_1.default(eqOperator, "(2,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(eqOperator, "(2,eq,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(gteOperator, "(2,gte,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(gteOperator, "((a,b,c),gte,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(gteOperator, "(null,gte,1)")).to.throw(missingFieldError); | ||
expect(() => filter_param_parser_1.default(gteOperator, "((),gte,1)")).to.throw(missingFieldError); | ||
}); | ||
}); | ||
describe("and/or operators", () => { | ||
it("should verify (recursively) that args are valid field expressions themselves", () => { | ||
const sutWithOps = filter_param_parser_1.default.bind(null, Object.assign({}, eqOperator, nowProperOperator, gteOperator, andOrOperators)); | ||
describe("operators with raw field expressions as args", () => { | ||
it("should (recursively) process the field expressions, calling finalizeArgs", () => { | ||
const sutWithOps = filter_param_parser_1.default.bind(null, Object.assign({}, eqOperator, nowProperOperator, gteOperator, andOrOperators, andOrProperOperators)); | ||
const invalidsToErrors = { | ||
"(and,())": /must have a valid operator symbol/, | ||
"(or,(field,gte,2),(test,fieldName,3))": /must have a valid operator symbol/, | ||
"(and,(or,()))": /must have a valid operator symbol/, | ||
"(and,true)": /expression must be a list/i, | ||
"(or,(field,eq,2),(date,gte,(now)),(and,test))": /expression must be a list/i, | ||
"(and,((field,eq,2),(date,gte,(now))))": /"eq" operator expects field reference/ | ||
"(and,(or,(test,x,1)))": /must have a valid operator symbol/, | ||
"(and-list,true)": /arguments must be field expressions/i, | ||
"(or-list,(field,eq,2),(date,gte,(now)),(and-list,test))": /arguments must be field expressions/i, | ||
}; | ||
@@ -175,34 +211,31 @@ Object.keys(invalidsToErrors).forEach(k => { | ||
expect(sutWithOps("(and,(field,eq,2),(date,gte,(now)),(test,4))")) | ||
.to.deep.equal([{ | ||
operator: "and", | ||
args: [{ | ||
operator: "eq", | ||
args: [{ type: "identifier", value: "field" }, 2] | ||
}, { | ||
operator: "gte", | ||
args: [ | ||
{ type: "identifier", value: "date" }, | ||
[{ type: "identifier", value: "now" }] | ||
] | ||
}, { | ||
operator: "eq", | ||
args: [{ type: "identifier", value: "test" }, 4] | ||
}] | ||
}]); | ||
expect(sutWithOps("(and,(or,(and,(it,3))))")) | ||
.to.deep.equal([{ | ||
operator: "and", | ||
args: [{ | ||
operator: "or", | ||
args: [{ | ||
operator: "and", | ||
args: [{ | ||
operator: "eq", | ||
args: [{ type: "identifier", value: "it" }, 3] | ||
}] | ||
}] | ||
}] | ||
}]); | ||
.to.deep.equal([ | ||
helpers_2.FieldExpression("and", [ | ||
helpers_2.FieldExpression("eq", [{ type: "Identifier", value: "field" }, 2]), | ||
helpers_2.FieldExpression("gte", [helpers_2.Identifier("date"), helpers_2.FieldExpression("now", [])]), | ||
helpers_2.FieldExpression("eq", [{ type: "Identifier", value: "test" }, 4]) | ||
]) | ||
]); | ||
expect(sutWithOps("(and,true)")).to.deep.equal([ | ||
helpers_2.FieldExpression("and", [true]) | ||
]); | ||
expect(filter_param_parser_1.default(withFieldOperators, "(test,lte,(a,eq,c))")).to.deep.equal([ | ||
helpers_2.FieldExpression("lte", [ | ||
helpers_2.Identifier("test"), | ||
helpers_2.FieldExpression("eq", [helpers_2.Identifier("a"), helpers_2.Identifier("c")]) | ||
]) | ||
]); | ||
expect(sutWithOps("(and,(or,(and,(it,3)),(test,gte,null)))")) | ||
.to.deep.equal([ | ||
helpers_2.FieldExpression("and", [ | ||
helpers_2.FieldExpression("or", [ | ||
helpers_2.FieldExpression("and", [ | ||
helpers_2.FieldExpression("eq", [helpers_2.Identifier("it"), 3]) | ||
]), | ||
helpers_2.FieldExpression("gte", [helpers_2.Identifier("test"), null]) | ||
]) | ||
]) | ||
]); | ||
}); | ||
}); | ||
}); |
@@ -1,8 +0,1 @@ | ||
declare const expect: any; | ||
declare const sut: any; | ||
declare const parseFilter: (str: string) => any; | ||
declare const parseSort: (str: string) => any; | ||
declare const Identifier: (value: string) => { | ||
type: string; | ||
value: string; | ||
}; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const { expect } = require("chai"); | ||
const sut = require("../src/parser"); | ||
const helpers_1 = require("./helpers"); | ||
const parseFilter = (str) => sut.parse(str, { startRule: "Filter" }); | ||
const parseSort = (str) => sut.parse(str, { startRule: "Sort" }); | ||
const Identifier = (value) => ({ type: "identifier", value }); | ||
describe('Parser from underlying grammar', () => { | ||
describe("Filter", () => { | ||
it("should reject empty lists at the top level", () => { | ||
expect(() => parseFilter("()")).to.throw(/Expected comma-separated list/); | ||
expect(() => parseFilter("(a,b,c)()")).to.throw(/Expected comma-separated list/); | ||
it("should reject empty field expressions", () => { | ||
expect(() => parseFilter("()")).to.throw(/Expected.+field expression/); | ||
expect(() => parseFilter("(a,b,c)()")).to.throw(/Expected.+field expression/); | ||
expect(() => parseFilter("(a,b,())")).to.throw(/Expected.+field expression/); | ||
}); | ||
it("should support only lists without any separators at top-level", () => { | ||
it("should support only field expressions without any separators at top-level", () => { | ||
expect(parseFilter("(ab,c)(3,e)")).to.deep.equal([ | ||
[Identifier("ab"), Identifier("c")], | ||
[3, Identifier("e")] | ||
helpers_1.RawFieldExpression([helpers_1.Identifier("ab"), helpers_1.Identifier("c")]), | ||
helpers_1.RawFieldExpression([3, helpers_1.Identifier("e")]) | ||
]); | ||
@@ -21,14 +23,18 @@ expect(() => parseFilter("(ab,c),(3,e)")).to.throw(/but "," found/); | ||
}); | ||
it("should not allow non-list items at top-level", () => { | ||
expect(() => parseFilter("44")).to.throw(/comma-separated list but "4"/); | ||
expect(() => parseFilter("-4")).to.throw(/comma-separated list but "-"/); | ||
expect(() => parseFilter("ab")).to.throw(/comma-separated list but "a"/); | ||
expect(() => parseFilter("true")).to.throw(/comma-separated list but "t"/); | ||
expect(() => parseFilter("null")).to.throw(/comma-separated list but "n"/); | ||
it("should not allow non-field-expression items at top-level", () => { | ||
expect(() => parseFilter("44")).to.throw(/field expression but "4"/); | ||
expect(() => parseFilter("-4")).to.throw(/field expression but "-"/); | ||
expect(() => parseFilter("ab")).to.throw(/field expression but "a"/); | ||
expect(() => parseFilter("true")).to.throw(/field expression but "t"/); | ||
expect(() => parseFilter("null")).to.throw(/field expression but "n"/); | ||
expect(() => parseFilter("null")).to.throw(/field expression but "n"/); | ||
expect(() => parseFilter("[test]")).to.throw(/field expression but \"\[\"/); | ||
}); | ||
}); | ||
describe("Sort", () => { | ||
it("should reject empty lists at the top level", () => { | ||
it("should reject empty field expressions", () => { | ||
expect(() => parseSort("fieldA,()")).to.throw(); | ||
expect(() => parseSort("fieldA,(a,())")).to.throw(); | ||
expect(() => parseSort("fieldA,(ax,x)")).to.not.throw(); | ||
expect(() => parseSort("fieldA,(ax,[])")).to.not.throw(); | ||
}); | ||
@@ -51,8 +57,15 @@ it("should reject number literals as sort fields", () => { | ||
}); | ||
it('should support list expressions, with directions', () => { | ||
const expression = [ | ||
Identifier("x"), | ||
Identifier("y"), | ||
Identifier("z") | ||
]; | ||
it("should reject list literals as sort fields", () => { | ||
expect(() => parseSort("john,[test]")).to.throw(); | ||
expect(() => parseSort("[test],john")).to.throw(/sort fields list but \"\[\" found/); | ||
}); | ||
it('should support field expressions, with directions', () => { | ||
const expression = { | ||
type: "RawFieldExpression", | ||
items: [ | ||
helpers_1.Identifier("x"), | ||
helpers_1.Identifier("y"), | ||
helpers_1.Identifier("z") | ||
] | ||
}; | ||
const ascResult = { direction: "ASC", expression }; | ||
@@ -66,3 +79,3 @@ const descResult = { direction: "DESC", expression }; | ||
}); | ||
describe("Comma-separated lists", () => { | ||
describe("Field expression lists", () => { | ||
it("should reject trailing commas", () => { | ||
@@ -79,5 +92,3 @@ expect(() => parseSort("(a,)")).to.throw(); | ||
expect(sut.parse("(true,truedat)", { startRule: "Filter" })) | ||
.to.deep.equal([ | ||
[true, Identifier("truedat")] | ||
]); | ||
.to.deep.equal([helpers_1.RawFieldExpression([true, helpers_1.Identifier("truedat")])]); | ||
}); | ||
@@ -92,19 +103,46 @@ it("should reject leading period, minus, and number in symbol names", () => { | ||
it("should allow periods, minus signs, and numbers in symbol names", () => { | ||
expect(parseFilter("(a-test)")).to.deep.equal([[Identifier("a-test")]]); | ||
expect(parseFilter("(a-22d)")).to.deep.equal([[Identifier("a-22d")]]); | ||
expect(parseFilter("(a1d)")).to.deep.equal([[Identifier("a1d")]]); | ||
expect(parseFilter("(a.test)")).to.deep.equal([[Identifier("a.test")]]); | ||
expect(parseFilter("(a.22d)")).to.deep.equal([[Identifier("a.22d")]]); | ||
expect(parseFilter("(a-test)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([helpers_1.Identifier("a-test")]) | ||
]); | ||
expect(parseFilter("(a-22d)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([helpers_1.Identifier("a-22d")]) | ||
]); | ||
expect(parseFilter("(a1d)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([helpers_1.Identifier("a1d")]) | ||
]); | ||
expect(parseFilter("(a.test)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([helpers_1.Identifier("a.test")]) | ||
]); | ||
expect(parseFilter("(a.22d)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([helpers_1.Identifier("a.22d")]) | ||
]); | ||
}); | ||
it("should not allow [] in symbol names", () => { | ||
expect(() => parseFilter("(ast[rst)")).to.throw(/expected field expression/i); | ||
expect(() => parseFilter("(astrs]t)")).to.throw(/expected field expression/i); | ||
expect(() => parseFilter("(ast[rst])")).to.throw(/expected field expression/i); | ||
}); | ||
}); | ||
describe("Number", () => { | ||
it("should support integers and integer-prefixed decimals", () => { | ||
expect(parseFilter("(2)")).to.deep.equal([[2]]); | ||
expect(parseFilter("(-2)")).to.deep.equal([[-2]]); | ||
expect(parseFilter("(2.1)")).to.deep.equal([[2.1]]); | ||
expect(parseFilter("(-2.1)")).to.deep.equal([[-2.1]]); | ||
expect(parseFilter("(2)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([2]) | ||
]); | ||
expect(parseFilter("(-2)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([-2]) | ||
]); | ||
expect(parseFilter("(2.1)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([2.1]) | ||
]); | ||
expect(parseFilter("(-2.1)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([-2.1]) | ||
]); | ||
}); | ||
it("should allow literals with no integer part", () => { | ||
expect(parseFilter("(.99)")).to.deep.equal([[.99]]); | ||
expect(parseFilter("(-.99)")).to.deep.equal([[-0.99]]); | ||
expect(parseFilter("(.99)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([.99]) | ||
]); | ||
expect(parseFilter("(-.99)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([-0.99]) | ||
]); | ||
}); | ||
@@ -120,5 +158,11 @@ it("should reject trailing decimal point", () => { | ||
it("should allow 0-prefixed integer parts", () => { | ||
expect(parseFilter("(0.99)")).to.deep.equal([[.99]]); | ||
expect(parseFilter("(011.99)")).to.deep.equal([[11.99]]); | ||
expect(parseFilter("(001.99)")).to.deep.equal([[1.99]]); | ||
expect(parseFilter("(0.99)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([.99]) | ||
]); | ||
expect(parseFilter("(011.99)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([11.99]) | ||
]); | ||
expect(parseFilter("(001.99)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([1.99]) | ||
]); | ||
}); | ||
@@ -128,5 +172,7 @@ }); | ||
it("should parse them into their js equivalents", () => { | ||
expect(parseFilter("(true,false,null)")).to.deep.equal([[true, false, null]]); | ||
expect(parseFilter("(true,false,null)")).to.deep.equal([ | ||
helpers_1.RawFieldExpression([true, false, null]) | ||
]); | ||
}); | ||
}); | ||
}); |
{ | ||
"name": "@json-api/query-parser", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "A query parser for the ?filter and ?sort param syntax used by @json-api/server", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
26174
11.08%13
18.18%525
9.15%0
-100%