Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions src/lib/utilities/query/filter-workflow-query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,74 @@ describe('toListWorkflowQueryFromFilters', () => {
},
);

it('should convert an Int filter without quoting the value', () => {
const filters = [
{
attribute: 'CustomIntField',
type: 'Int',
conditional: '=',
operator: '',
parenthesis: '',
value: '1',
},
];
const query = toListWorkflowQueryFromFilters(filters);
expect(query).toBe('`CustomIntField`=1');
});

it('should convert a Double filter without quoting the value', () => {
const filters = [
{
attribute: 'CustomDoubleField',
type: 'Double',
conditional: '>',
operator: '',
parenthesis: '',
value: '1.5',
},
];
const query = toListWorkflowQueryFromFilters(filters);
expect(query).toBe('`CustomDoubleField`>1.5');
});

it('should convert numeric filters alongside keyword filters', () => {
const filters = [
{
attribute: 'CustomIntField',
type: 'Int',
conditional: '>=',
operator: '',
parenthesis: '',
value: '10',
},
{
attribute: 'WorkflowId',
type: 'Keyword',
conditional: '=',
operator: '',
parenthesis: '',
value: 'abcd',
},
];
const query = toListWorkflowQueryFromFilters(combineFilters(filters));
expect(query).toBe('`CustomIntField`>=10 AND `WorkflowId`="abcd"');
});

it('should convert an Int filter with is null conditional', () => {
const filters = [
{
attribute: 'CustomIntField',
type: 'Int',
conditional: 'is',
operator: '',
parenthesis: '',
value: null,
},
];
const query = toListWorkflowQueryFromFilters(filters);
expect(query).toBe('`CustomIntField` is null');
});

it('should convert a KeywordList filter', () => {
const filters = [
{
Expand Down
6 changes: 6 additions & 0 deletions src/lib/utilities/query/filter-workflow-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ const formatValue = ({
) {
return value;
}
if (
type === SEARCH_ATTRIBUTE_TYPE.INT ||
type === SEARCH_ATTRIBUTE_TYPE.DOUBLE
) {
return value;
}
return `"${value}"`;
};

Expand Down
35 changes: 35 additions & 0 deletions src/lib/utilities/query/to-list-workflow-filters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const attributes = {
'Custom Keyword Field': 'Keyword',
'Custom Bool Field': 'Bool',
CustomKeywordListField: 'KeywordList',
CustomIntField: 'Int',
CustomDoubleField: 'Double',
};

describe('toListWorkflowFilters', () => {
Expand Down Expand Up @@ -270,6 +272,39 @@ describe('toListWorkflowFilters', () => {
expect(result).toMatchObject(expectedFilters);
});

it('should parse a query with an unquoted Int value', () => {
const result = toListWorkflowFilters('`CustomIntField`=1', attributes);
const expectedFilters = [
{
attribute: 'CustomIntField',
type: 'Int',
conditional: '=',
operator: '',
parenthesis: '',
value: '1',
},
];
expect(result).toMatchObject(expectedFilters);
});

it('should parse a query with an unquoted Double value and a comparison operator', () => {
const result = toListWorkflowFilters(
'`CustomDoubleField`>=1.5',
attributes,
);
const expectedFilters = [
{
attribute: 'CustomDoubleField',
type: 'Double',
conditional: '>=',
operator: '',
parenthesis: '',
value: '1.5',
},
];
expect(result).toMatchObject(expectedFilters);
});

it('should parse a query with a KeywordList type', () => {
const result = toListWorkflowFilters(keywordListQuery, attributes);
const expectedFilters = [
Expand Down
2 changes: 1 addition & 1 deletion src/lib/utilities/query/to-list-workflow-filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export const toListWorkflowFilters = (
console.error('Error parsing Datetime field from query');
}
} else if (isBoolStatement(filter.type)) {
filter.value = nextToken.replace('=', '');
filter.value = tokenTwoAhead;
filter.conditional = '=';
} else {
filter.value = tokenTwoAhead;
Expand Down
3 changes: 2 additions & 1 deletion src/lib/utilities/query/tokenize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ describe('tokenize', () => {
'some workflow',
'AND',
'Custom Boolean',
'=true',
'=',
'true',
]);
});

Expand Down
11 changes: 9 additions & 2 deletions src/lib/utilities/query/tokenize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import {

type Tokens = string[];

const OPERATOR_CONDITIONALS = new Set(['>=', '<=', '!=', '==']);
const isOperatorConditional = (value: string): boolean =>
OPERATOR_CONDITIONALS.has(value);

export const tokenize = (string: string): Tokens => {
const tokens: Tokens = [];
const addBufferToTokens = (): void => {
Expand Down Expand Up @@ -91,16 +95,19 @@ export const tokenize = (string: string): Tokens => {
isConditional(minConditional) &&
(isSpace(string[cursor + 2]) ||
isQuote(string[cursor + 2]) ||
isParenthesis(string[cursor + 2]))
isParenthesis(string[cursor + 2]) ||
isOperatorConditional(minConditional))
) {
// To prevent false positives like "inspect" being a "in" conditional, check for space, quote, or parenthesis after the midConditional
// To prevent false positives like "inspect" being a "in" conditional, check for space, quote, or parenthesis after the midConditional.
// Operator-style conditionals like >=, <=, != never collide with identifiers, so accept them even without a trailing space.
buffer += minConditional;
addBufferToTokens();
cursor += 2;
continue;
} else if (isConditional(character)) {
addBufferToTokens();
buffer += character;
addBufferToTokens();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just double checking, this was meant to be added twice?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, this is intentional. For unquoted values, the old code glued the conditional to the value, and later we'd strip it with filter.value = nextToken.replace('=', ''); . That got complicated when int/double came into play, so instead we separate the conditional token from the value token up front.

cursor++;
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,7 @@ test('it should filter by HistoryLength (number)', async ({ page }) => {
.fill('10');
await page.getByTestId('apply-filter-button').click();

await expect
.poll(() => getQueryParam(page.url()))
.toBe('`HistoryLength`="10"');
await expect.poll(() => getQueryParam(page.url())).toBe('`HistoryLength`=10');
});

test('it should combine filters', async ({ page }) => {
Expand All @@ -188,7 +186,7 @@ test('it should combine filters', async ({ page }) => {

await expect
.poll(() => getQueryParam(page.url()))
.toBe('`ExecutionStatus`="Completed" AND `HistoryLength`="10"');
.toBe('`ExecutionStatus`="Completed" AND `HistoryLength`=10');

await page.getByTestId('add-filter-button').click();
await page.getByRole('menuitem', { name: 'WorkflowType Keyword' }).click();
Expand All @@ -202,7 +200,7 @@ test('it should combine filters', async ({ page }) => {
await expect
.poll(() => getQueryParam(page.url()))
.toBe(
'`ExecutionStatus`="Completed" AND `HistoryLength`="10" AND `WorkflowType`="ExampleWorkflow"',
'`ExecutionStatus`="Completed" AND `HistoryLength`=10 AND `WorkflowType`="ExampleWorkflow"',
);
});

Expand All @@ -226,7 +224,7 @@ test('it should combine filters and then clear them all', async ({ page }) => {

await expect
.poll(() => getQueryParam(page.url()))
.toBe('`ExecutionStatus`="Completed" AND `HistoryLength`="10"');
.toBe('`ExecutionStatus`="Completed" AND `HistoryLength`=10');

await page.getByTestId('add-filter-button').click();
await page.getByRole('menuitem', { name: 'WorkflowType Keyword' }).click();
Expand All @@ -240,7 +238,7 @@ test('it should combine filters and then clear them all', async ({ page }) => {
await expect
.poll(() => getQueryParam(page.url()))
.toBe(
'`ExecutionStatus`="Completed" AND `HistoryLength`="10" AND `WorkflowType`="ExampleWorkflow"',
'`ExecutionStatus`="Completed" AND `HistoryLength`=10 AND `WorkflowType`="ExampleWorkflow"',
);

await page.getByTestId('clear-all-filters-button').click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,7 @@ test('it should filter by HistoryLength (number)', async ({ page }) => {
.fill('10');
await page.getByTestId('apply-filter-button').click();

await expect
.poll(() => getQueryParam(page.url()))
.toBe('`HistoryLength`="10"');
await expect.poll(() => getQueryParam(page.url())).toBe('`HistoryLength`=10');
});

test('it should combine filters and then clear them all', async ({ page }) => {
Expand Down
Loading