diff --git a/api/src/models/utils/filters/parser.ts b/api/src/models/utils/filters/parser.ts index 8206122a..26db94c8 100644 --- a/api/src/models/utils/filters/parser.ts +++ b/api/src/models/utils/filters/parser.ts @@ -42,10 +42,9 @@ function t(parser: Parjser): Parjser { return parser.pipe(thenq(string(" ").pipe(many()))); } -const str = t(noCharOf(" ").pipe(many1(), stringify()).expects("a string")); const enumP = t(letter().pipe(many1(), stringify()).expects("an enum value")); -const property = str.expects("a property"); +const property = t(letter().pipe(many1(), stringify())).expects("a property"); const intVal = t(int().pipe(map((i) => ({ type: "int" as const, value: i })))); const floatVal = t( @@ -66,9 +65,11 @@ const dateVal = t( })), ), ).expects("a date"); -const strVal = str.pipe( - between('"'), - or(str.pipe(between("'"))), +const strVal = t(noCharOf('"').pipe(many1(), stringify(), between('"'))).pipe( + or( + noCharOf("'").pipe(many1(), stringify(), between("'")), + noCharOf(" ").pipe(many1(), stringify()), + ), map((s) => ({ type: "string" as const, value: s })), ); const enumVal = enumP.pipe(map((e) => ({ type: "enum" as const, value: e }))); @@ -76,7 +77,7 @@ const value = dateVal .pipe( // until we get the `-` character, this could be an int or a float. recover(() => ({ kind: "Soft" })), - or(intVal, floatVal, strVal, enumVal), + or(intVal, floatVal, enumVal, strVal), ) .expects("a valid value"); diff --git a/api/src/models/utils/filters/to-sql.ts b/api/src/models/utils/filters/to-sql.ts index 49d57c3b..b8e57022 100644 --- a/api/src/models/utils/filters/to-sql.ts +++ b/api/src/models/utils/filters/to-sql.ts @@ -42,6 +42,11 @@ export const toDrizzle = (expr: Expression, config: FilterDef): SQL => { ); } + if (expr.value.type === "enum" && prop.type === "string") { + // promote enum to string since this is legal + // but parser doesn't know if an enum should be a string + expr.value = { type: "string", value: expr.value.value }; + } if (prop.type !== expr.value.type) { throw new KErrorT( comment` diff --git a/api/tests/misc/filter.test.ts b/api/tests/misc/filter.test.ts index d1630e88..07a263d5 100644 --- a/api/tests/misc/filter.test.ts +++ b/api/tests/misc/filter.test.ts @@ -170,4 +170,42 @@ describe("Parse filter", () => { }, }); }); + it('Handle eq with " string', () => { + const ret = parse('tags eq "magic armor"'); + expect(ret).toMatchObject({ + ok: true, + value: { + type: "op", + operator: "eq", + property: "tags", + value: { type: "string", value: "magic armor" }, + }, + }); + }); + it("Handle eq with ' string", () => { + const ret = parse("tags eq 'magic armor'"); + expect(ret).toMatchObject({ + ok: true, + value: { + type: "op", + operator: "eq", + property: "tags", + value: { type: "string", value: "magic armor" }, + }, + }); + }); + + it("Handle eq with string, no quote", () => { + const ret = parse("tags eq magic"); + expect(ret).toMatchObject({ + ok: true, + value: { + type: "op", + operator: "eq", + property: "tags", + // this is parsed as enum but is handled afterwards + value: { type: "enum", value: "magic" }, + }, + }); + }); });