From b15d9e082f5b0dc45c97e42293f0b1b50fa619ba Mon Sep 17 00:00:00 2001 From: Henry8192 <50559854+Henry8192@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:04:33 -0500 Subject: [PATCH] fix(query): Restart log queries when case-sensitivity or regex button is toggled. (#130) --- .../SearchTabPanel/ResultsGroup.tsx | 2 +- .../SidebarTabs/SearchTabPanel/index.tsx | 45 ++++++++++++++++--- src/contexts/StateContextProvider.tsx | 25 +++++------ src/services/LogFileManager/index.ts | 26 ++++++++--- src/services/MainWorker.ts | 15 +------ src/typings/query.ts | 29 ++++++++++++ src/typings/worker.ts | 18 +------- 7 files changed, 102 insertions(+), 58 deletions(-) create mode 100644 src/typings/query.ts diff --git a/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/ResultsGroup.tsx b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/ResultsGroup.tsx index 28e1ce4a..b1751f4c 100644 --- a/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/ResultsGroup.tsx +++ b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/ResultsGroup.tsx @@ -17,7 +17,7 @@ import { import DescriptionOutlinedIcon from "@mui/icons-material/DescriptionOutlined"; -import {QueryResultsType} from "../../../../../typings/worker"; +import {QueryResultsType} from "../../../../../typings/query"; import Result from "./Result"; import "./ResultsGroup.css"; diff --git a/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx index 631878f1..c32b5b2b 100644 --- a/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx +++ b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx @@ -16,12 +16,15 @@ import UnfoldLessIcon from "@mui/icons-material/UnfoldLess"; import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore"; import {StateContext} from "../../../../../contexts/StateContextProvider"; +import { + QUERY_PROGRESS_VALUE_MAX, + QueryArgs, +} from "../../../../../typings/query"; import {UI_ELEMENT} from "../../../../../typings/states"; import { TAB_DISPLAY_NAMES, TAB_NAME, } from "../../../../../typings/tab"; -import {QUERY_PROGRESS_DONE} from "../../../../../typings/worker"; import {isDisabled} from "../../../../../utils/states"; import CustomTabPanel from "../CustomTabPanel"; import PanelTitleButton from "../PanelTitleButton"; @@ -35,6 +38,24 @@ enum QUERY_OPTION { IS_REGEX = "isRegex" } +/** + * Determines if the query is case-sensitive based on the provided query options. + * + * @param queryOptions + * @return True if the query is case-sensitive. + */ +const getIsCaseSensitive = + (queryOptions: QUERY_OPTION[]) => queryOptions.includes(QUERY_OPTION.IS_CASE_SENSITIVE); + +/** + * Determines if the query is a regular expression based on the provided query options. + * + * @param queryOptions + * @return True if the query is a regular expression. + */ +const getIsRegex = + (queryOptions: QUERY_OPTION[]) => queryOptions.includes(QUERY_OPTION.IS_REGEX); + /** * Displays a panel for submitting queries and viewing query results. * @@ -44,17 +65,31 @@ const SearchTabPanel = () => { const {queryProgress, queryResults, startQuery, uiState} = useContext(StateContext); const [isAllExpanded, setIsAllExpanded] = useState(true); const [queryOptions, setQueryOptions] = useState([]); + const [queryString, setQueryString] = useState(""); + + const handleQuerySubmit = (newArgs: Partial) => { + startQuery({ + isCaseSensitive: getIsCaseSensitive(queryOptions), + isRegex: getIsRegex(queryOptions), + queryString: queryString, + ...newArgs, + }); + }; const handleQueryInputChange = (ev: React.ChangeEvent) => { - const isCaseSensitive = queryOptions.includes(QUERY_OPTION.IS_CASE_SENSITIVE); - const isRegex = queryOptions.includes(QUERY_OPTION.IS_REGEX); - startQuery(ev.target.value, isRegex, isCaseSensitive); + setQueryString(ev.target.value); + handleQuerySubmit({queryString: ev.target.value}); }; + const handleQueryOptionsChange = ( _: React.MouseEvent, newOptions: QUERY_OPTION[] ) => { setQueryOptions(newOptions); + handleQuerySubmit({ + isCaseSensitive: getIsCaseSensitive(newOptions), + isRegex: getIsRegex(newOptions), + }); }; return ( @@ -109,7 +144,7 @@ const SearchTabPanel = () => { determinate={true} thickness={4} value={queryProgress * 100} - color={QUERY_PROGRESS_DONE === queryProgress ? + color={QUERY_PROGRESS_VALUE_MAX === queryProgress ? "success" : "primary"}/> diff --git a/src/contexts/StateContextProvider.tsx b/src/contexts/StateContextProvider.tsx index 996cb226..659f243b 100644 --- a/src/contexts/StateContextProvider.tsx +++ b/src/contexts/StateContextProvider.tsx @@ -24,6 +24,11 @@ import { DEFAULT_AUTO_DISMISS_TIMEOUT_MILLIS, LONG_AUTO_DISMISS_TIMEOUT_MILLIS, } from "../typings/notifications"; +import { + QUERY_PROGRESS_VALUE_MIN, + QueryArgs, + QueryResults, +} from "../typings/query"; import {UI_STATE} from "../typings/states"; import {SEARCH_PARAM_NAMES} from "../typings/url"; import { @@ -33,8 +38,6 @@ import { EVENT_POSITION_ON_PAGE, FileSrcType, MainWorkerRespMessage, - QUERY_PROGRESS_INIT, - QueryResults, WORKER_REQ_CODE, WORKER_RESP_CODE, WorkerReq, @@ -81,7 +84,7 @@ interface StateContextType { loadFile: (fileSrc: FileSrcType, cursor: CursorType) => void, loadPageByAction: (navAction: NavigationAction) => void, setIsSettingsModalOpen: (isOpen: boolean) => void, - startQuery: (queryString: string, isRegex: boolean, isCaseSensitive: boolean) => void, + startQuery: (queryArgs: QueryArgs) => void, } const StateContext = createContext({} as StateContextType); @@ -98,7 +101,7 @@ const STATE_DEFAULT: Readonly = Object.freeze({ numPages: 0, onDiskFileSizeInBytes: 0, pageNum: 0, - queryProgress: QUERY_PROGRESS_INIT, + queryProgress: QUERY_PROGRESS_VALUE_MIN, queryResults: new Map(), uiState: UI_STATE.UNOPENED, @@ -339,7 +342,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { } case WORKER_RESP_CODE.QUERY_RESULT: setQueryProgress(args.progress); - if (QUERY_PROGRESS_INIT === args.progress) { + if (QUERY_PROGRESS_VALUE_MIN === args.progress) { setQueryResults(STATE_DEFAULT.queryResults); } else { setQueryResults((v) => { @@ -361,22 +364,14 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { } }, [postPopUp]); - const startQuery = useCallback(( - queryString: string, - isRegex: boolean, - isCaseSensitive: boolean - ) => { + const startQuery = useCallback((queryArgs: QueryArgs) => { setQueryResults(STATE_DEFAULT.queryResults); if (null === mainWorkerRef.current) { console.error("Unexpected null mainWorkerRef.current"); return; } - workerPostReq(mainWorkerRef.current, WORKER_REQ_CODE.START_QUERY, { - queryString: queryString, - isRegex: isRegex, - isCaseSensitive: isCaseSensitive, - }); + workerPostReq(mainWorkerRef.current, WORKER_REQ_CODE.START_QUERY, queryArgs); }, []); const exportLogs = useCallback(() => { diff --git a/src/services/LogFileManager/index.ts b/src/services/LogFileManager/index.ts index 10566999..328d8c90 100644 --- a/src/services/LogFileManager/index.ts +++ b/src/services/LogFileManager/index.ts @@ -6,6 +6,10 @@ import { } from "../../typings/decoders"; import {MAX_V8_STRING_LENGTH} from "../../typings/js"; import {LogLevelFilter} from "../../typings/logs"; +import { + QueryArgs, + QueryResults, +} from "../../typings/query"; import { BeginLineNumToLogEventNumMap, CURSOR_CODE, @@ -13,7 +17,6 @@ import { CursorType, EMPTY_PAGE_RESP, FileSrcType, - QueryResults, WORKER_RESP_CODE, WorkerResp, } from "../../typings/worker"; @@ -286,11 +289,13 @@ class LogFileManager { * Creates a RegExp object based on the given query string and options, and starts querying the * first log chunk. * - * @param queryString - * @param isRegex - * @param isCaseSensitive + * @param queryArgs + * @param queryArgs.queryString + * @param queryArgs.isRegex + * @param queryArgs.isCaseSensitive + * @throws {SyntaxError} if the query regex string is invalid. */ - startQuery (queryString: string, isRegex: boolean, isCaseSensitive: boolean): void { + startQuery ({queryString, isRegex, isCaseSensitive}: QueryArgs): void { this.#queryId++; this.#queryCount = 0; @@ -310,9 +315,16 @@ class LogFileManager { const regexFlags = isCaseSensitive ? "" : "i"; - const queryRegex = new RegExp(regexPattern, regexFlags); - this.#queryChunkAndScheduleNext(this.#queryId, 0, queryRegex); + try { + const queryRegex = new RegExp(regexPattern, regexFlags); + this.#queryChunkAndScheduleNext(this.#queryId, 0, queryRegex); + } catch (e) { + if (e instanceof SyntaxError) { + console.error("Invalid regular expression:", e); + } + throw e; + } } /** diff --git a/src/services/MainWorker.ts b/src/services/MainWorker.ts index d7ce638d..cd37753b 100644 --- a/src/services/MainWorker.ts +++ b/src/services/MainWorker.ts @@ -4,9 +4,9 @@ import dayjsTimezone from "dayjs/plugin/timezone"; import dayjsUtc from "dayjs/plugin/utc"; import {LOG_LEVEL} from "../typings/logs"; +import {QueryResults} from "../typings/query"; import { MainWorkerReqMessage, - QueryResults, WORKER_REQ_CODE, WORKER_RESP_CODE, WorkerResp, @@ -124,18 +124,7 @@ onmessage = async (ev: MessageEvent) => { if (null === LOG_FILE_MANAGER) { throw new Error("Log file manager hasn't been initialized"); } - if ( - "string" !== typeof args.queryString || - "boolean" !== typeof args.isRegex || - "boolean" !== typeof args.isCaseSensitive - ) { - throw new Error("Invalid arguments for QUERY_LOG"); - } - LOG_FILE_MANAGER.startQuery( - args.queryString, - args.isRegex, - args.isCaseSensitive - ); + LOG_FILE_MANAGER.startQuery(args); break; default: console.error(`Unexpected ev.data: ${JSON.stringify(ev.data)}`); diff --git a/src/typings/query.ts b/src/typings/query.ts new file mode 100644 index 00000000..f252be43 --- /dev/null +++ b/src/typings/query.ts @@ -0,0 +1,29 @@ +interface QueryArgs { + queryString: string; + isCaseSensitive: boolean; + isRegex: boolean; +} + +type TextRange = [number, number]; + +interface QueryResultsType { + logEventNum: number; + message: string; + matchRange: TextRange; +} + +type QueryResults = Map; + +const QUERY_PROGRESS_VALUE_MIN = 0; +const QUERY_PROGRESS_VALUE_MAX = 1; + + +export type { + QueryArgs, + QueryResults, + QueryResultsType, +}; +export { + QUERY_PROGRESS_VALUE_MAX, + QUERY_PROGRESS_VALUE_MIN, +}; diff --git a/src/typings/worker.ts b/src/typings/worker.ts index 2ccbe1a5..1333c19d 100644 --- a/src/typings/worker.ts +++ b/src/typings/worker.ts @@ -7,6 +7,7 @@ import { LOG_LEVEL, LogLevelFilter, } from "./logs"; +import {QueryResults} from "./query"; /** @@ -106,19 +107,6 @@ type WorkerReqMap = { }, }; -type TextRange = [number, number]; - -interface QueryResultsType { - logEventNum: number; - message: string; - matchRange: TextRange; -} - -type QueryResults = Map; - -const QUERY_PROGRESS_INIT = 0; -const QUERY_PROGRESS_DONE = 1; - type WorkerRespMap = { [WORKER_RESP_CODE.CHUNK_DATA]: { logs: string @@ -180,8 +168,6 @@ export { CURSOR_CODE, EMPTY_PAGE_RESP, EVENT_POSITION_ON_PAGE, - QUERY_PROGRESS_DONE, - QUERY_PROGRESS_INIT, WORKER_REQ_CODE, WORKER_RESP_CODE, }; @@ -192,8 +178,6 @@ export type { FileSrcType, MainWorkerReqMessage, MainWorkerRespMessage, - QueryResults, - QueryResultsType, WorkerReq, WorkerResp, };