From 0f23f141655825d0412b3cdf2b36fcc63110593e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 16:30:46 -0600 Subject: [PATCH 1/8] create getFilters --- src/CONST.ts | 19 ++++++++++ src/components/Search/types.ts | 17 ++++++++- src/libs/SearchUtils.ts | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index f50914aab5fc..2b10150cd745 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5206,6 +5206,25 @@ const CONST = { APPROVE: 'approve', PAY: 'pay', }, + SYNTAX_OPERATORS: { + AND: 'and', + OR: 'or', + EQUAL_TO: 'eq', + NOT_EQUAL_TO: 'neq', + GREATER_THAN: 'gt', + GREATER_THAN_OR_EQUAL_TO: 'gte', + LOWER_THAN: 'lt', + LOWER_THAN_OR_EQUAL_TO: 'lte', + }, + SYNTAX_FIELD_KEYS: { + TYPE: 'type', + STATUS: 'status', + SORT_BY: 'sortBy', + SORT_ORDER: 'sortOrder', + OFFSET: 'offset', + DATE: 'date', + AMOUNT: 'amount', + }, }, REFERRER: { diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index cff74fe08a0a..20e04e6c2bd6 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -26,4 +26,19 @@ type SearchContext = { setSelectedTransactionIds: (selectedTransactionIds: string[]) => void; }; -export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext}; +type ASTNode = { + operator: ValueOf; + left: string | ASTNode; + right: string | ASTNode; +} + +type QueryFilter = { + operator: ValueOf; + value: string; +} + +type QueryFilters = { + [K in keyof typeof CONST.SEARCH.SYNTAX_FIELD_KEYS]: QueryFilter | QueryFilter[]; +} + +export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilters}; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 6ddcac9263ce..055f22f0f7e9 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,4 +1,5 @@ -import type {SearchColumnType, SortOrder} from '@components/Search/types'; +import type {SearchColumnType, SortOrder, ASTNode, QueryFilters} from '@components/Search/types'; +import type {ValueOf} from 'type-fest'; import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; @@ -312,6 +313,7 @@ function getQueryHashFromString(query: string): number { type JSONQuery = { input: string; hash: number; + filters: ASTNode; }; function buildJSONQuery(query: string) { @@ -327,6 +329,64 @@ function buildJSONQuery(query: string) { } } +function getFilters(query: string, fields: string[]) { + let jsonQuery; + try { + jsonQuery = searchParser.parse(query) as JSONQuery; + } catch (e) { + console.error(e); + return; + } + + const filters = {} as QueryFilters; + + // Include root properties if they are specified fields + fields.forEach(field => { + if (jsonQuery[field] === undefined) { + return; + } + + filters[field] = { + operator: 'eq', + value: jsonQuery[field] + }; + }); + + function traverse(node: ASTNode) { + if (!node.operator) { + return; + } + + if (node.left && typeof node.left === 'object') { + traverse(node.left); + } + + if (node.right && typeof node.right === 'object') { + traverse(node.right); + } + + const nodeKey = node.left as ValueOf; + if (!fields.includes(nodeKey)) { + return; + } + + if (!filters[nodeKey]) { + filters[nodeKey] = []; + } + + filters[nodeKey].push({ + operator: node.operator, + value: node.right + }); + } + + if (jsonQuery.filters) { + traverse(jsonQuery.filters); + } + + return filters; +} + export { buildJSONQuery, getListItem, @@ -340,4 +400,5 @@ export { isReportListItemType, isTransactionListItemType, isSearchResultsEmpty, + getFilters, }; From e98f1f811fd5e80bd25a451c4bea105759e69307 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 16:31:48 -0600 Subject: [PATCH 2/8] fix lint --- src/components/Search/types.ts | 10 +++++----- src/libs/SearchUtils.ts | 36 +++++++++++++++++----------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 20e04e6c2bd6..e56a6affb728 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -30,15 +30,15 @@ type ASTNode = { operator: ValueOf; left: string | ASTNode; right: string | ASTNode; -} +}; type QueryFilter = { - operator: ValueOf; - value: string; -} + operator: ValueOf; + value: string; +}; type QueryFilters = { [K in keyof typeof CONST.SEARCH.SYNTAX_FIELD_KEYS]: QueryFilter | QueryFilter[]; -} +}; export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilters}; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 055f22f0f7e9..fdea177fd4d5 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,5 +1,5 @@ -import type {SearchColumnType, SortOrder, ASTNode, QueryFilters} from '@components/Search/types'; import type {ValueOf} from 'type-fest'; +import type {ASTNode, QueryFilters, SearchColumnType, SortOrder} from '@components/Search/types'; import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; @@ -332,37 +332,37 @@ function buildJSONQuery(query: string) { function getFilters(query: string, fields: string[]) { let jsonQuery; try { - jsonQuery = searchParser.parse(query) as JSONQuery; + jsonQuery = searchParser.parse(query) as JSONQuery; } catch (e) { - console.error(e); - return; + console.error(e); + return; } - + const filters = {} as QueryFilters; - + // Include root properties if they are specified fields - fields.forEach(field => { + fields.forEach((field) => { if (jsonQuery[field] === undefined) { return; } filters[field] = { - operator: 'eq', - value: jsonQuery[field] + operator: 'eq', + value: jsonQuery[field], }; }); - + function traverse(node: ASTNode) { if (!node.operator) { return; } if (node.left && typeof node.left === 'object') { - traverse(node.left); + traverse(node.left); } if (node.right && typeof node.right === 'object') { - traverse(node.right); + traverse(node.right); } const nodeKey = node.left as ValueOf; @@ -375,17 +375,17 @@ function getFilters(query: string, fields: string[]) { } filters[nodeKey].push({ - operator: node.operator, - value: node.right + operator: node.operator, + value: node.right, }); } - + if (jsonQuery.filters) { - traverse(jsonQuery.filters); + traverse(jsonQuery.filters); } - + return filters; -} +} export { buildJSONQuery, From 5d21b2758d6523a28a7b223d38bf7ebcd17602de Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 17:04:06 -0600 Subject: [PATCH 3/8] fix some types --- src/CONST.ts | 4 +++- src/components/Search/types.ts | 10 ++++++---- src/libs/SearchUtils.ts | 18 ++++++++++++------ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 2b10150cd745..f03d8ed9d0fc 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5216,12 +5216,14 @@ const CONST = { LOWER_THAN: 'lt', LOWER_THAN_OR_EQUAL_TO: 'lte', }, - SYNTAX_FIELD_KEYS: { + SYNTAX_ROOT_KEYS: { TYPE: 'type', STATUS: 'status', SORT_BY: 'sortBy', SORT_ORDER: 'sortOrder', OFFSET: 'offset', + }, + SYNTAX_FILTER_KEYS: { DATE: 'date', AMOUNT: 'amount', }, diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index e56a6affb728..44183a4efeb2 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -28,17 +28,19 @@ type SearchContext = { type ASTNode = { operator: ValueOf; - left: string | ASTNode; + left: ValueOf | ASTNode; right: string | ASTNode; }; type QueryFilter = { operator: ValueOf; - value: string; + value: string | number; }; +type AllFieldKeys = keyof typeof CONST.SEARCH.SYNTAX_FILTER_KEYS | keyof typeof CONST.SEARCH.SYNTAX_ROOT_KEYS; + type QueryFilters = { - [K in keyof typeof CONST.SEARCH.SYNTAX_FIELD_KEYS]: QueryFilter | QueryFilter[]; + [K in AllFieldKeys]: QueryFilter | QueryFilter[]; }; -export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilters}; +export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilters, AllFieldKeys}; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index fdea177fd4d5..2b8d0699313e 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,5 +1,5 @@ import type {ValueOf} from 'type-fest'; -import type {ASTNode, QueryFilters, SearchColumnType, SortOrder} from '@components/Search/types'; +import type {ASTNode, QueryFilters, SearchColumnType, SortOrder, AllFieldKeys} from '@components/Search/types'; import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; @@ -313,6 +313,11 @@ function getQueryHashFromString(query: string): number { type JSONQuery = { input: string; hash: number; + type: string; + status: string; + sortBy: string; + sortOrder: string; + offset: number; filters: ASTNode; }; @@ -329,7 +334,7 @@ function buildJSONQuery(query: string) { } } -function getFilters(query: string, fields: string[]) { +function getFilters(query: string, fields: Array>) { let jsonQuery; try { jsonQuery = searchParser.parse(query) as JSONQuery; @@ -341,14 +346,15 @@ function getFilters(query: string, fields: string[]) { const filters = {} as QueryFilters; // Include root properties if they are specified fields - fields.forEach((field) => { - if (jsonQuery[field] === undefined) { + fields.forEach(field => { + const rootFieldKey = field as ValueOf; + if (jsonQuery[rootFieldKey] === undefined) { return; } filters[field] = { operator: 'eq', - value: jsonQuery[field], + value: jsonQuery[rootFieldKey], }; }); @@ -365,7 +371,7 @@ function getFilters(query: string, fields: string[]) { traverse(node.right); } - const nodeKey = node.left as ValueOf; + const nodeKey = node.left as ValueOf; if (!fields.includes(nodeKey)) { return; } From 52012c7add80a0c695b66c22d1a67012b09600bb Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 18:47:45 -0600 Subject: [PATCH 4/8] fix types --- src/components/Search/types.ts | 4 ++-- src/libs/SearchUtils.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 44183a4efeb2..2238b0f49855 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -37,10 +37,10 @@ type QueryFilter = { value: string | number; }; -type AllFieldKeys = keyof typeof CONST.SEARCH.SYNTAX_FILTER_KEYS | keyof typeof CONST.SEARCH.SYNTAX_ROOT_KEYS; +type AllFieldKeys = ValueOf | ValueOf; type QueryFilters = { [K in AllFieldKeys]: QueryFilter | QueryFilter[]; }; -export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilters, AllFieldKeys}; +export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilter, QueryFilters, AllFieldKeys}; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 2b8d0699313e..d9c1b38bf1ca 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,5 +1,5 @@ import type {ValueOf} from 'type-fest'; -import type {ASTNode, QueryFilters, SearchColumnType, SortOrder, AllFieldKeys} from '@components/Search/types'; +import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SortOrder, AllFieldKeys} from '@components/Search/types'; import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; @@ -345,7 +345,6 @@ function getFilters(query: string, fields: Array>) { const filters = {} as QueryFilters; - // Include root properties if they are specified fields fields.forEach(field => { const rootFieldKey = field as ValueOf; if (jsonQuery[rootFieldKey] === undefined) { @@ -363,11 +362,11 @@ function getFilters(query: string, fields: Array>) { return; } - if (node.left && typeof node.left === 'object') { + if (typeof node?.left === 'object') { traverse(node.left); } - if (node.right && typeof node.right === 'object') { + if (typeof node?.right === 'object') { traverse(node.right); } @@ -380,9 +379,10 @@ function getFilters(query: string, fields: Array>) { filters[nodeKey] = []; } - filters[nodeKey].push({ + const filterArray = filters[nodeKey] as QueryFilter[]; + filterArray.push({ operator: node.operator, - value: node.right, + value: node.right as string | number, }); } From a231e6cd2fa4318907df0c5abfc1981e51984df9 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 18:59:58 -0600 Subject: [PATCH 5/8] fix lint --- src/libs/SearchUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index d9c1b38bf1ca..a91d3a8f0df5 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,5 +1,5 @@ import type {ValueOf} from 'type-fest'; -import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SortOrder, AllFieldKeys} from '@components/Search/types'; +import type {AllFieldKeys, ASTNode, QueryFilter, QueryFilters, SearchColumnType, SortOrder} from '@components/Search/types'; import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; @@ -345,7 +345,7 @@ function getFilters(query: string, fields: Array>) { const filters = {} as QueryFilters; - fields.forEach(field => { + fields.forEach((field) => { const rootFieldKey = field as ValueOf; if (jsonQuery[rootFieldKey] === undefined) { return; From 1594711e0abb88ac75f785b67b2b1e20f4e41874 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 19:00:35 -0600 Subject: [PATCH 6/8] ignore parser js file --- .github/workflows/typecheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 476b01f87b07..4186de314c96 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -34,7 +34,7 @@ jobs: # - git diff is used to see the files that were added on this branch # - gh pr view is used to list files touched by this PR. Git diff may give false positives if the branch isn't up-to-date with main # - wc counts the words in the result of the intersection - count_new_js=$(comm -1 -2 <(git diff --name-only --diff-filter=A origin/main HEAD -- 'src/*.js' '__mocks__/*.js' '.storybook/*.js' 'assets/*.js' 'config/*.js' 'desktop/*.js' 'jest/*.js' 'scripts/*.js' 'tests/*.js' 'workflow_tests/*.js' '.github/libs/*.js' '.github/scripts/*.js') <(gh pr view ${{ github.event.pull_request.number }} --json files | jq -r '.files | map(.path) | .[]') | wc -l) + count_new_js=$(comm -1 -2 <(git diff --name-only --diff-filter=A origin/main HEAD -- 'src/*.js' '__mocks__/*.js' '.storybook/*.js' 'assets/*.js' 'config/*.js' 'desktop/*.js' 'jest/*.js' 'scripts/*.js' 'tests/*.js' 'workflow_tests/*.js' '.github/libs/*.js' '.github/scripts/*.js' | grep -v 'src/libs/Search/searchParser.js') <(gh pr view ${{ github.event.pull_request.number }} --json files | jq -r '.files | map(.path) | .[]') | wc -l) if [ "$count_new_js" -gt "0" ]; then echo "ERROR: Found new JavaScript files in the project; use TypeScript instead." exit 1 From 2e7996c2f72535590f266ab29000afd316d63f9e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 19:11:16 -0600 Subject: [PATCH 7/8] add more filter keys --- src/CONST.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index f03d8ed9d0fc..4cb6b8471cdf 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5226,6 +5226,18 @@ const CONST = { SYNTAX_FILTER_KEYS: { DATE: 'date', AMOUNT: 'amount', + EXPENSE_TYPE: 'expenseType', + CURRENCY: 'currency', + MERCHANT: 'merchant', + DESCRIPTION: 'description', + FROM: 'from', + TO: 'to', + CATEGORY: 'category', + TAG: 'tag', + TAX_RATE: 'taxRate', + CARD_ID: 'cardID', + REPORT_ID: 'reportID', + KEYWORD: 'keyword', }, }, From a722a820ea9b0fb4ed8a7010b85f04a80f6d995e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 10 Jul 2024 19:33:21 -0600 Subject: [PATCH 8/8] fix lint --- src/CONST.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 4cb6b8471cdf..0f53fdcc09ca 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5237,7 +5237,7 @@ const CONST = { TAX_RATE: 'taxRate', CARD_ID: 'cardID', REPORT_ID: 'reportID', - KEYWORD: 'keyword', + KEYWORD: 'keyword', }, },