Implement query history

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz 2024-09-04 17:25:34 +02:00
parent f3f324be89
commit a99c01b53f
4 changed files with 56 additions and 10 deletions

View file

@ -68,6 +68,7 @@ import { notifications } from "@mantine/notifications";
import { useSettings } from "../../state/settingsSlice"; import { useSettings } from "../../state/settingsSlice";
import MetricsExplorer from "./MetricsExplorer/MetricsExplorer"; import MetricsExplorer from "./MetricsExplorer/MetricsExplorer";
import ErrorBoundary from "../../components/ErrorBoundary"; import ErrorBoundary from "../../components/ErrorBoundary";
import { useAppSelector } from "../../state/hooks";
const promqlExtension = new PromQLExtension(); const promqlExtension = new PromQLExtension();
@ -127,11 +128,13 @@ const ExpressionInput: FC<ExpressionInputProps> = ({
removePanel, removePanel,
}) => { }) => {
const theme = useComputedColorScheme(); const theme = useComputedColorScheme();
const { queryHistory } = useAppSelector((state) => state.queryPage);
const { const {
pathPrefix, pathPrefix,
enableAutocomplete, enableAutocomplete,
enableSyntaxHighlighting, enableSyntaxHighlighting,
enableLinter, enableLinter,
enableQueryHistory,
} = useSettings(); } = useSettings();
const [expr, setExpr] = useState(initialExpr); const [expr, setExpr] = useState(initialExpr);
useEffect(() => { useEffect(() => {
@ -175,10 +178,6 @@ const ExpressionInput: FC<ExpressionInputProps> = ({
const [showMetricsExplorer, setShowMetricsExplorer] = useState(false); const [showMetricsExplorer, setShowMetricsExplorer] = useState(false);
// TODO: Implement query history.
// This is just a placeholder until query history is implemented, so disable the linter warning.
const queryHistory = useMemo<string[]>(() => [], []);
// (Re)initialize editor based on settings / setting changes. // (Re)initialize editor based on settings / setting changes.
useEffect(() => { useEffect(() => {
// Build the dynamic part of the config. // Build the dynamic part of the config.
@ -193,10 +192,17 @@ const ExpressionInput: FC<ExpressionInputProps> = ({
cache: { initialMetricList: metricNames }, cache: { initialMetricList: metricNames },
}, },
}), }),
queryHistory enableQueryHistory ? queryHistory : []
), ),
}); });
}, [pathPrefix, metricNames, enableAutocomplete, enableLinter, queryHistory]); // TODO: Make this depend on external settings changes, maybe use dynamic config compartment again. }, [
pathPrefix,
metricNames,
enableAutocomplete,
enableLinter,
enableQueryHistory,
queryHistory,
]); // TODO: Make this depend on external settings changes, maybe use dynamic config compartment again.
return ( return (
<Group align="flex-start" wrap="nowrap" gap="xs"> <Group align="flex-start" wrap="nowrap" gap="xs">

View file

@ -17,6 +17,7 @@ import {
import { FC, useCallback, useState } from "react"; import { FC, useCallback, useState } from "react";
import { useAppDispatch, useAppSelector } from "../../state/hooks"; import { useAppDispatch, useAppSelector } from "../../state/hooks";
import { import {
addQueryToHistory,
GraphDisplayMode, GraphDisplayMode,
GraphResolution, GraphResolution,
removePanel, removePanel,
@ -73,6 +74,10 @@ const QueryPanel: FC<PanelProps> = ({ idx, metricNames }) => {
executeQuery={(expr: string) => { executeQuery={(expr: string) => {
setRetriggerIdx((idx) => idx + 1); setRetriggerIdx((idx) => idx + 1);
dispatch(setExpr({ idx, expr })); dispatch(setExpr({ idx, expr }));
if (!metricNames.includes(expr) && expr.trim() !== "") {
dispatch(addQueryToHistory(expr));
}
}} }}
removePanel={() => { removePanel={() => {
dispatch(removePanel(idx)); dispatch(removePanel(idx));

View file

@ -6,9 +6,13 @@ import {
} from "./targetsPageSlice"; } from "./targetsPageSlice";
import { import {
localStorageKeyCollapsedPools as localStorageKeyServiceDiscoveryPageCollapsedPools, localStorageKeyCollapsedPools as localStorageKeyServiceDiscoveryPageCollapsedPools,
setCollapsedPools as ServiceDiscoveryPageSetCollapsedPools, setCollapsedPools as serviceDiscoveryPageSetCollapsedPools,
} from "./serviceDiscoveryPageSlice"; } from "./serviceDiscoveryPageSlice";
import { updateSettings } from "./settingsSlice"; import { updateSettings } from "./settingsSlice";
import {
addQueryToHistory,
localStorageKeyQueryHistory,
} from "./queryPageSlice";
const persistToLocalStorage = <T>(key: string, value: T) => { const persistToLocalStorage = <T>(key: string, value: T) => {
localStorage.setItem(key, JSON.stringify(value)); localStorage.setItem(key, JSON.stringify(value));
@ -29,7 +33,7 @@ startAppListening({
}); });
startAppListening({ startAppListening({
actionCreator: ServiceDiscoveryPageSetCollapsedPools, actionCreator: serviceDiscoveryPageSetCollapsedPools,
effect: ({ payload }) => { effect: ({ payload }) => {
persistToLocalStorage( persistToLocalStorage(
localStorageKeyServiceDiscoveryPageCollapsedPools, localStorageKeyServiceDiscoveryPageCollapsedPools,
@ -38,6 +42,16 @@ startAppListening({
}, },
}); });
startAppListening({
actionCreator: addQueryToHistory,
effect: (_, { getState }) => {
persistToLocalStorage(
localStorageKeyQueryHistory,
getState().queryPage.queryHistory
);
},
});
startAppListening({ startAppListening({
actionCreator: updateSettings, actionCreator: updateSettings,
effect: ({ payload }) => { effect: ({ payload }) => {

View file

@ -1,6 +1,9 @@
import { randomId } from "@mantine/hooks"; import { randomId } from "@mantine/hooks";
import { PayloadAction, createSlice } from "@reduxjs/toolkit"; import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { encodePanelOptionsToURLParams } from "../pages/query/urlStateEncoding"; import { encodePanelOptionsToURLParams } from "../pages/query/urlStateEncoding";
import { initializeFromLocalStorage } from "./initializeFromLocalStorage";
export const localStorageKeyQueryHistory = "queryPage.queryHistory";
export enum GraphDisplayMode { export enum GraphDisplayMode {
Lines = "lines", Lines = "lines",
@ -68,6 +71,7 @@ export type Panel = {
interface QueryPageState { interface QueryPageState {
panels: Panel[]; panels: Panel[];
queryHistory: string[];
} }
export const newDefaultPanel = (): Panel => ({ export const newDefaultPanel = (): Panel => ({
@ -87,6 +91,10 @@ export const newDefaultPanel = (): Panel => ({
const initialState: QueryPageState = { const initialState: QueryPageState = {
panels: [newDefaultPanel()], panels: [newDefaultPanel()],
queryHistory: initializeFromLocalStorage<string[]>(
localStorageKeyQueryHistory,
[]
),
}; };
const updateURL = (panels: Panel[]) => { const updateURL = (panels: Panel[]) => {
@ -116,6 +124,12 @@ export const queryPageSlice = createSlice({
state.panels[payload.idx].expr = payload.expr; state.panels[payload.idx].expr = payload.expr;
updateURL(state.panels); updateURL(state.panels);
}, },
addQueryToHistory: (state, { payload: query }: PayloadAction<string>) => {
state.queryHistory = [
query,
...state.queryHistory.filter((q) => q !== query),
].slice(0, 50);
},
setVisualizer: ( setVisualizer: (
state, state,
{ payload }: PayloadAction<{ idx: number; visualizer: Visualizer }> { payload }: PayloadAction<{ idx: number; visualizer: Visualizer }>
@ -126,7 +140,14 @@ export const queryPageSlice = createSlice({
}, },
}); });
export const { setPanels, addPanel, removePanel, setExpr, setVisualizer } = export const {
queryPageSlice.actions; setPanels,
addPanel,
removePanel,
setExpr,
addQueryToHistory,
setShowTree,
setVisualizer,
} = queryPageSlice.actions;
export default queryPageSlice.reducer; export default queryPageSlice.reducer;