Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
2 changes: 1 addition & 1 deletion components/log-viewer-webui/client/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"MongoDbSearchResultsMetadataCollectionName": "results-metadata",
"ClpStorageEngine": "clp"
"ClpStorageEngine": "clp-s"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify storage engine configuration update.
Changing "ClpStorageEngine" from "clp" to "clp-s" must be supported by the backend and all references in code and documentation updated accordingly.

Run this script to ensure no stale references remain:


🏁 Script executed:

#!/bin/bash
# Search for hardcoded 'ClpStorageEngine' values
rg -n '"ClpStorageEngine"'

Length of output: 315


Fix default ClpStorageEngine in webui settings and verify backend support
The search still shows a hardcoded "ClpStorageEngine": "clp" in your WebUI defaults. Please update that value and confirm the backend handles the new engine ID everywhere.

• components/webui/settings.json (line 14): change
"ClpStorageEngine": "clp" → "ClpStorageEngine": "clp-s"
• components/log-viewer-webui/client/settings.json is already set to "clp-s"
• components/clp-package-utils/clp_package_utils/scripts/start_clp.py (line 878): verify storage_engine can be "clp-s"
• Review any documentation or README files for lingering “clp” references and update accordingly

🤖 Prompt for AI Agents
In components/log-viewer-webui/client/settings.json at line 3, the
"ClpStorageEngine" is updated to "clp-s". Ensure consistency by also updating
components/webui/settings.json at line 14 to set "ClpStorageEngine" to "clp-s".
Verify that components/clp-package-utils/clp_package_utils/scripts/start_clp.py
at line 878 supports "clp-s" as a valid storage_engine value. Additionally,
review and update all documentation and README files to replace any remaining
"clp" references with "clp-s" to maintain alignment across the project.

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import SubmitButton from "./SubmitButton";
* @return
*/
const SearchButton = () => {
const store = useSearchStore();
const searchUiState = useSearchStore((state) => state.searchUiState);

return (
(store.searchUiState === SEARCH_UI_STATE.QUERYING) ?
(searchUiState === SEARCH_UI_STATE.QUERYING) ?
<CancelButton/> :
<SubmitButton/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
QueryJobSchema,
submitQuery,
} from "../../../api/search";
import useSearchStore from "../SearchState/index";
import useSearchStore, {SEARCH_STATE_DEFAULT} from "../SearchState";
import {SEARCH_UI_STATE} from "../SearchState/typings";


Expand Down Expand Up @@ -47,7 +47,7 @@ const handleClearResults = () => {
const handleQuerySubmit = (payload: QueryJobCreationSchema) => {
const store = useSearchStore.getState();

// Buttons to submit a query should be disabled while an existing query is in progress.
// User should NOT be able to submit a new query while an existing query is in progress.
if (
store.searchUiState !== SEARCH_UI_STATE.DEFAULT &&
store.searchUiState !== SEARCH_UI_STATE.DONE
Expand All @@ -59,6 +59,8 @@ const handleQuerySubmit = (payload: QueryJobCreationSchema) => {

handleClearResults();

store.updateNumSearchResultsTable(SEARCH_STATE_DEFAULT.numSearchResultsTable);
store.updateNumSearchResultsTimeline(SEARCH_STATE_DEFAULT.numSearchResultsTimeline);
store.updateSearchUiState(SEARCH_UI_STATE.QUERY_ID_PENDING);

submitQuery(payload)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {useMemo} from "react";

import {
GetProps,
Typography,
} from "antd";

import useSearchStore from "../SearchState/index";
import {SEARCH_UI_STATE} from "../SearchState/typings";


const {Text} = Typography;
type TextTypes = GetProps<typeof Text>["type"];


/**
* Displays the number of search results.
*
* @return
*/
const Results = () => {
const numSearchResultsTimeline = useSearchStore((state) => state.numSearchResultsTimeline);
const numSearchResultsTable = useSearchStore((state) => state.numSearchResultsTable);
const searchUiState = useSearchStore((state) => state.searchUiState);

// Number of results is the maximum of the number of results in the timeline and table. The
// timeline may have more results since the table results are capped. Having two sources may
// provide more timely updates to the user.
const numResults = useMemo(
() => Math.max(numSearchResultsTimeline, numSearchResultsTable),
[
numSearchResultsTimeline,
numSearchResultsTable,
]
);

let textType: TextTypes;
switch (searchUiState) {
case SEARCH_UI_STATE.QUERYING:
textType = "warning";
break;
case SEARCH_UI_STATE.DEFAULT:
case SEARCH_UI_STATE.QUERY_ID_PENDING:
textType = "secondary";
break;
case SEARCH_UI_STATE.DONE:
textType = "success";
break;
default:
textType = "secondary";
}

return (
<Text
strong={true}
type={textType}
>
{numResults}
</Text>
);
};

export default Results;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.status {
margin-left: 2px;
margin-top: 4px;
}

.badge {
padding-bottom: 4px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {Typography} from "antd";

import useSearchStore from "../SearchState/index";
import {SEARCH_UI_STATE} from "../SearchState/typings";
import styles from "./index.module.css";
import Results from "./Results";


const {Text} = Typography;

/**
* Displays the search job ID and the number of results found.
*
* @return
*/
const SearchQueryStatus = () => {
const {
searchJobId,
searchUiState,
} = useSearchStore();

return (
<div className={styles["status"]}>
{(searchUiState === SEARCH_UI_STATE.QUERYING ||
searchUiState === SEARCH_UI_STATE.DONE) && (
<Text type={"secondary"}>
Search job #
{searchJobId}
{" "}
found
{" "}
</Text>
)}
<Results/>
<Text type={"secondary"}> results</Text>
</div>
);
};


export default SearchQueryStatus;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {

import {Table} from "antd";

import useSearchStore from "../../SearchState/index";
import {
SearchResult,
searchResultsTableColumns,
Expand All @@ -20,10 +21,22 @@ import {useSearchResults} from "./useSearchResults";
* @return
*/
const SearchResultsTable = () => {
const {updateNumSearchResultsTable} = useSearchStore();
const searchResults = useSearchResults();
const [tableHeight, setTableHeight] = useState<number>(0);
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const num = searchResults ?
searchResults.length :
0;

updateNumSearchResultsTable(num);
}, [
searchResults,
updateNumSearchResultsTable,
]);

// Antd table requires a fixed height for virtual scrolling. The effect sets a fixed height
// based on the window height, container top, and fixed padding.
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {useEffect} from "react";

import {Card} from "antd";
import {Dayjs} from "dayjs";
import {TimelineConfig} from "src/components/ResultsTimeline/typings";
Expand All @@ -24,10 +26,22 @@ const SearchResultsTimeline = () => {
timelineConfig,
searchUiState,
updateTimelineConfig,
updateNumSearchResultsTimeline,
} = useSearchStore();

const aggregationResults = useAggregationResults();

useEffect(() => {
const numSearchResultsTimeline = aggregationResults?.reduce(
(acc, curr) => acc + curr.count,
0
) ?? 0;

updateNumSearchResultsTimeline(numSearchResultsTimeline);
}, [
aggregationResults,
updateNumSearchResultsTimeline,
]);

const handleTimelineZoom = (newTimeRange: [Dayjs, Dayjs]) => {
const newTimelineConfig: TimelineConfig = computeTimelineConfig(newTimeRange);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {SEARCH_UI_STATE} from "./typings";
*/
const SEARCH_STATE_DEFAULT = Object.freeze({
aggregationJobId: null,
numSearchResultsTable: 0,
numSearchResultsTimeline: 0,
queryIsCaseSensitive: false,
queryString: "",
searchJobId: null,
Expand All @@ -32,6 +34,16 @@ interface SearchState {
*/
aggregationJobId: string | null;

/**
* The number of search table results.
*/
numSearchResultsTable: number;

/**
* The number of timeline results.
*/
numSearchResultsTimeline: number;

/**
* Whether the query is case sensitive.
*/
Expand Down Expand Up @@ -70,6 +82,8 @@ interface SearchState {
timelineConfig: TimelineConfig;

updateAggregationJobId: (id: string | null) => void;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

following our latest (undocumented yet - sorry!) Zustand practices, Zustand actions that do not perform more actions than state settings should be prefixed with set instead of update. This is fine to leave in the current PR, but we should address such in a future Zustand refactoring PR.

updateNumSearchResultsTable: (num: number) => void;
updateNumSearchResultsTimeline: (num: number) => void;
updateQueryIsCaseSensitive: (newValue: boolean) => void;
updateQueryString: (query: string) => void;
updateSearchJobId: (id: string | null) => void;
Expand All @@ -84,6 +98,12 @@ const useSearchStore = create<SearchState>((set) => ({
updateAggregationJobId: (id) => {
set({aggregationJobId: id});
},
updateNumSearchResultsTable: (num) => {
set({numSearchResultsTable: num});
},
updateNumSearchResultsTimeline: (num) => {
set({numSearchResultsTimeline: num});
},
updateQueryIsCaseSensitive: (newValue: boolean) => {
set({queryIsCaseSensitive: newValue});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ const useResultsMetadata = () => {
return resultsMetadata;
};


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove unnecessary blank line before export

The blank line added at line 51 serves no functional purpose and may introduce trailing whitespace. Please remove it to keep formatting consistent.

🤖 Prompt for AI Agents
In
components/log-viewer-webui/client/src/pages/SearchPage/SearchState/useResultsMetadata.ts
at line 51, remove the unnecessary blank line before the export statement to
avoid trailing whitespace and maintain consistent formatting.

export {useResultsMetadata};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styles from "./index.module.css";
import SearchControls from "./SearchControls";
import SearchQueryStatus from "./SearchQueryStatus";
import SearchResultsTable from "./SearchResults/SearchResultsTable";
import SearchResultsTimeline from "./SearchResults/SearchResultsTimeline";
import {useUiUpdateOnDoneSignal} from "./SearchState/useUpdateStateWithMetadata";
Expand All @@ -15,7 +16,10 @@ const SearchPage = () => {

return (
<div className={styles["searchPageContainer"]}>
<SearchControls/>
<div>
<SearchControls/>
<SearchQueryStatus/>
</div>
<SearchResultsTimeline/>
<SearchResultsTable/>
</div>
Expand Down
6 changes: 3 additions & 3 deletions components/log-viewer-webui/client/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ export default defineConfig({
"/query/": {
// Below target should match the server's configuration in
// `components/log-viewer-webui/server/.env` (or `.env.local` if overridden)
target: "http://localhost:3000/",
target: "http://localhost:3001/",
changeOrigin: true,
},
"/api/": {
target: "http://localhost:3000/",
target: "http://localhost:3001/",
changeOrigin: true,
},
"/socket.io/": {
target: "ws://localhost:3000/",
target: "ws://localhost:3001/",
changeOrigin: true,
ws: true,
},
Expand Down
4 changes: 2 additions & 2 deletions components/log-viewer-webui/server/.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
NODE_ENV=production

HOST=localhost
PORT=3000
PORT=3001
CLP_DB_USER=clp-user
CLP_DB_PASS=
CLP_DB_PASS=qO55i66GEUs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid committing sensitive credentials.
Storing CLP_DB_PASS directly in a tracked .env poses a security risk. Consider moving this secret to a secure vault or adding .env to .gitignore and providing a template (.env.example).

🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 9-9: [UnorderedKey] The CLP_DB_PASS key should go before the CLP_DB_USER key

🤖 Prompt for AI Agents
In components/log-viewer-webui/server/.env at line 9, the database password is
stored directly in the tracked .env file, which is a security risk. Remove the
CLP_DB_PASS entry from this file, add .env to .gitignore to prevent committing
sensitive credentials, and create a .env.example file with placeholder values to
serve as a template for environment variables.