diff --git a/packages/core/src/backend/data-source/queries.ts b/packages/core/src/backend/data-source/queries.ts index 2ae49ca..df83512 100644 --- a/packages/core/src/backend/data-source/queries.ts +++ b/packages/core/src/backend/data-source/queries.ts @@ -1,92 +1,98 @@ export const DIRECTIVE_MAP = { '=': 'equals', '!=': 'not-equals', - '<': 'greater-than', - '>': 'less-than', - '<=': 'greater-than-equal', - '>=': 'less-than-equal', + '>=': 'greater-than-equal', + '<=': 'less-than-equal', + '>': 'greater-than', + '<': 'less-than', }; export const DATA_SOURCE_QUERY_OPERATORS = Object.keys(DIRECTIVE_MAP) as (keyof typeof DIRECTIVE_MAP)[]; export type DataSourceQueryOperator = typeof DATA_SOURCE_QUERY_OPERATORS[number]; -export interface DataSourceQuery { +export interface DataSourceOperatorQuery { lhs: string; operator: DataSourceQueryOperator; rhs: string; } +export interface DataSourceFunctionQuery { + name: string; + args: string[]; +} + +export type DataSourceQuery = DataSourceOperatorQuery | DataSourceFunctionQuery; + interface ConvertToDataSourceQueryCollectionOptions { } const parseDirectives = (valueRaw: string) => { - let operator = '='; - - let hasDirective: boolean; - let valueTest = valueRaw.toLowerCase(); - let valuePredicate = valueRaw; - const fragments = valueTest.split('.'); - const directiveShorthands = Object.values(DIRECTIVE_MAP).map((s) => { - // TODO how to parse? - }) - do { - hasDirective = false; - - if (valueTest.startsWith('.neq.')) { - operator = '!='; - - valueTest = valueTest.slice('.neq.'.length); - valuePredicate = valuePredicate.slice('.neq.'.length); - hasDirective = true; - } else if (valueTest.startsWith('.gt.eq.')) { - operator = '>='; - - valueTest = valueTest.slice('.gt.eq.'.length); - valuePredicate = valuePredicate.slice('.gt.eq.'.length); - hasDirective = true; - } else if (valueTest.startsWith('.gte.')) { - operator = '>='; - - valueTest = valueTest.slice('.gte.'.length); - valuePredicate = valuePredicate.slice('.gte.'.length); - hasDirective = true; - } else if (valueTest.startsWith('.lt.eq.')) { - operator = '<='; - - valueTest = valueTest.slice('.lt.eq.'.length); - valuePredicate = valuePredicate.slice('.lt.eq.'.length); - hasDirective = true; - } else if (valueTest.startsWith('.lte.')) { - operator = '<='; - - valueTest = valueTest.slice('.lte.'.length); - valuePredicate = valuePredicate.slice('.lte.'.length); - hasDirective = true; - } else if (valueTest.startsWith('.gt.')) { - operator = '>'; - - valueTest = valueTest.slice('.gt.'.length); - valuePredicate = valuePredicate.slice('.gt.'.length); - hasDirective = true; - } else if (valueTest.startsWith('.lt.')) { - operator = '<'; - - valueTest = valueTest.slice('.lt.'.length); - valuePredicate = valuePredicate.slice('.lt.'.length); - hasDirective = true; - } else if (valueTest.startsWith('.eq.')) { - operator = '='; - - valueTest = valueTest.slice('.eq.'.length); - valuePredicate = valuePredicate.slice('.eq.'.length); - hasDirective = true; - } - } while (hasDirective); + const fragments = valueRaw.split('.'); + + if (!( + fragments.length > 1 && fragments[0].length === 0 + )) { + return { + operator: '=', + rhs: valueRaw, + }; + } + + const theOperator = fragments.slice(1, -1).reduce( + (theState, f) => { + const whichDirective = Object.entries(DIRECTIVE_MAP).find( + ([, match]) => { + const remainingChars = match.split('').reduce( + (rem, matchChar) => { + if (matchChar === rem.charAt(0)) { + return rem.slice(1); + } + + return rem; + }, + f.toLowerCase() + ); + + return remainingChars.length <= 0; + }, + ); + + const selectedOperator = whichDirective?.[0] ?? theState.operator; + + if (selectedOperator === '=') { + if (theState.operator === '>') { + return { + ...theState, + operator: '>=', + currentToken: `${theState.currentToken}${f}`, + }; + } + + if (theState.operator === '<') { + return { + ...theState, + operator: '<=', + currentToken: `${theState.currentToken}${f}`, + }; + } + } + + return { + ...theState, + operator: selectedOperator, + currentToken: `${theState.currentToken}${f}`, + }; + }, + { + operator: undefined as (string | undefined), + currentToken: '', + }, + ); return { - operator, - rhs: valuePredicate, + operator: theOperator.operator ?? '=', + rhs: fragments.at(-1), }; }; diff --git a/packages/core/test/features/query.test.ts b/packages/core/test/features/query.test.ts index 7af9586..5c48e63 100644 --- a/packages/core/test/features/query.test.ts +++ b/packages/core/test/features/query.test.ts @@ -78,11 +78,24 @@ describe('query', () => { ]); }); - it('returns a greater than or equal expression query', () => { + it.only('returns a greater than or equal expression query', () => { + // expect( + // convertToDataSourceQueryCollection( + // new URLSearchParams({ + // attr: '.gt.eq.foo' + // }) + // ) + // ).toEqual([ + // { + // lhs: 'attr', + // operator: '>=', + // rhs: '"foo"', + // }, + // ]); expect( convertToDataSourceQueryCollection( new URLSearchParams({ - attr: '.gt.eq.foo' + attr: '.gte.foo' }) ) ).toEqual([ @@ -92,19 +105,34 @@ describe('query', () => { rhs: '"foo"', }, ]); + expect( convertToDataSourceQueryCollection( new URLSearchParams({ - attr: '.gte.foo' + attr: '.neq.test' }) ) ).toEqual([ { lhs: 'attr', - operator: '>=', - rhs: '"foo"', + operator: '!=', + rhs: '"test"', }, ]); + + // expect( + // convertToDataSourceQueryCollection( + // new URLSearchParams({ + // attr: '.gte.foo..test' + // }) + // ) + // ).toEqual([ + // { + // lhs: 'attr', + // operator: '>=', + // rhs: '"foo.test"', + // }, + // ]); }); it('returns a less than or equal expression query', () => {