mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Fix URL sync with async queries import (#79584)
* cancel pending queries * Update public/app/features/explore/hooks/useStateSync/index.ts Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com> * Update public/app/features/explore/hooks/useStateSync/index.ts * Fix test --------- Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
This commit is contained in:
parent
5ae3249c36
commit
2d18961768
@ -57,7 +57,9 @@ export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, Expl
|
|||||||
) {
|
) {
|
||||||
const { exploreId, changeDatasourceUid } = correlationDetails?.postConfirmAction;
|
const { exploreId, changeDatasourceUid } = correlationDetails?.postConfirmAction;
|
||||||
if (exploreId && changeDatasourceUid) {
|
if (exploreId && changeDatasourceUid) {
|
||||||
dispatch(changeDatasource(exploreId, changeDatasourceUid, { importQueries: true }));
|
dispatch(
|
||||||
|
changeDatasource({ exploreId, datasource: changeDatasourceUid, options: { importQueries: true } })
|
||||||
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
changeCorrelationEditorDetails({
|
changeCorrelationEditorDetails({
|
||||||
isExiting: false,
|
isExiting: false,
|
||||||
@ -143,7 +145,7 @@ export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, Expl
|
|||||||
|
|
||||||
const changeDatasourcePostAction = (exploreId: string, datasourceUid: string) => {
|
const changeDatasourcePostAction = (exploreId: string, datasourceUid: string) => {
|
||||||
setSaveMessage(undefined);
|
setSaveMessage(undefined);
|
||||||
dispatch(changeDatasource(exploreId, datasourceUid, { importQueries: true }));
|
dispatch(changeDatasource({ exploreId, datasource: datasourceUid, options: { importQueries: true } }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveCorrelationPostAction = (skipPostConfirmAction: boolean) => {
|
const saveCorrelationPostAction = (skipPostConfirmAction: boolean) => {
|
||||||
@ -163,7 +165,7 @@ export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, Expl
|
|||||||
action === CORRELATION_EDITOR_POST_CONFIRM_ACTION.CHANGE_DATASOURCE &&
|
action === CORRELATION_EDITOR_POST_CONFIRM_ACTION.CHANGE_DATASOURCE &&
|
||||||
changeDatasourceUid !== undefined
|
changeDatasourceUid !== undefined
|
||||||
) {
|
) {
|
||||||
changeDatasource(exploreId, changeDatasourceUid);
|
changeDatasource({ exploreId, datasource: changeDatasourceUid });
|
||||||
resetEditor();
|
resetEditor();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,7 +100,7 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
|||||||
|
|
||||||
const onChangeDatasource = async (dsSettings: DataSourceInstanceSettings) => {
|
const onChangeDatasource = async (dsSettings: DataSourceInstanceSettings) => {
|
||||||
if (!isCorrelationsEditorMode) {
|
if (!isCorrelationsEditorMode) {
|
||||||
dispatch(changeDatasource(exploreId, dsSettings.uid, { importQueries: true }));
|
dispatch(changeDatasource({ exploreId, datasource: dsSettings.uid, options: { importQueries: true } }));
|
||||||
} else {
|
} else {
|
||||||
if (correlationDetails?.correlationDirty || correlationDetails?.queryEditorDirty) {
|
if (correlationDetails?.correlationDirty || correlationDetails?.queryEditorDirty) {
|
||||||
// prompt will handle datasource change if needed
|
// prompt will handle datasource change if needed
|
||||||
@ -128,7 +128,7 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(changeDatasource(exploreId, dsSettings.uid, { importQueries: true }));
|
dispatch(changeDatasource({ exploreId, datasource: dsSettings.uid, options: { importQueries: true } }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -374,7 +374,7 @@ describe('RichHistoryCard', () => {
|
|||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(setQueries).toHaveBeenCalledWith(expect.any(String), queries);
|
expect(setQueries).toHaveBeenCalledWith(expect.any(String), queries);
|
||||||
expect(changeDatasource).toHaveBeenCalledWith(expect.any(String), 'mixed');
|
expect(changeDatasource).toHaveBeenCalledWith({ datasource: 'mixed', exploreId: 'left' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -188,7 +188,7 @@ export function RichHistoryCard(props: Props) {
|
|||||||
const queriesToRun = queryHistoryItem.queries;
|
const queriesToRun = queryHistoryItem.queries;
|
||||||
const differentDataSource = queryHistoryItem.datasourceUid !== datasourceInstance?.uid;
|
const differentDataSource = queryHistoryItem.datasourceUid !== datasourceInstance?.uid;
|
||||||
if (differentDataSource) {
|
if (differentDataSource) {
|
||||||
await changeDatasource(exploreId, queryHistoryItem.datasourceUid);
|
await changeDatasource({ exploreId, datasource: queryHistoryItem.datasourceUid });
|
||||||
}
|
}
|
||||||
setQueries(exploreId, queriesToRun);
|
setQueries(exploreId, queriesToRun);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import { addListener, ExploreItemState, ExploreQueryParams, useDispatch, useSele
|
|||||||
import { changeDatasource } from '../../state/datasource';
|
import { changeDatasource } from '../../state/datasource';
|
||||||
import { changePanelsStateAction, initializeExplore } from '../../state/explorePane';
|
import { changePanelsStateAction, initializeExplore } from '../../state/explorePane';
|
||||||
import { clearPanes, splitClose, splitOpen, syncTimesAction } from '../../state/main';
|
import { clearPanes, splitClose, splitOpen, syncTimesAction } from '../../state/main';
|
||||||
import { runQueries, setQueriesAction } from '../../state/query';
|
import { cancelQueries, runQueries, setQueriesAction } from '../../state/query';
|
||||||
import { selectPanes } from '../../state/selectors';
|
import { selectPanes } from '../../state/selectors';
|
||||||
import { changeRangeAction, updateTime } from '../../state/time';
|
import { changeRangeAction, updateTime } from '../../state/time';
|
||||||
import { DEFAULT_RANGE, fromURLRange } from '../../state/utils';
|
import { DEFAULT_RANGE, fromURLRange } from '../../state/utils';
|
||||||
@ -32,6 +32,7 @@ export function useStateSync(params: ExploreQueryParams) {
|
|||||||
const orgId = useSelector((state) => state.user.orgId);
|
const orgId = useSelector((state) => state.user.orgId);
|
||||||
const prevParams = useRef(params);
|
const prevParams = useRef(params);
|
||||||
const initState = useRef<'notstarted' | 'pending' | 'done'>('notstarted');
|
const initState = useRef<'notstarted' | 'pending' | 'done'>('notstarted');
|
||||||
|
const paused = useRef(false);
|
||||||
const { warning } = useAppNotification();
|
const { warning } = useAppNotification();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -47,20 +48,35 @@ export function useStateSync(params: ExploreQueryParams) {
|
|||||||
const unsubscribe = dispatch(
|
const unsubscribe = dispatch(
|
||||||
addListener({
|
addListener({
|
||||||
predicate: (action) =>
|
predicate: (action) =>
|
||||||
// We want to update the URL when:
|
/*
|
||||||
// - a pane is opened or closed
|
We want to update the URL when:
|
||||||
// - a query is run
|
- a pane is opened or closed
|
||||||
// - range is changed
|
- a query is run
|
||||||
// - panel state is updated
|
- range is changed
|
||||||
[
|
- panel state is updated
|
||||||
splitClose.type,
|
- a datasource change has completed.
|
||||||
splitOpen.fulfilled.type,
|
|
||||||
runQueries.pending.type,
|
Note: Changing datasource causes a bunch of actions to be dispatched, we want to update the URL
|
||||||
changeRangeAction.type,
|
only when the change set has completed. This is done by checking if the changeDatasource.pending action
|
||||||
changePanelsStateAction.type,
|
has been dispatched and pausing the listener until the changeDatasource.fulfilled action is dispatched.
|
||||||
].includes(action.type),
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
paused.current = changeDatasource.pending.type === action.type;
|
||||||
|
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
splitClose.type,
|
||||||
|
splitOpen.fulfilled.type,
|
||||||
|
runQueries.pending.type,
|
||||||
|
changeRangeAction.type,
|
||||||
|
changePanelsStateAction.type,
|
||||||
|
changeDatasource.fulfilled.type,
|
||||||
|
].includes(action.type) && !paused.current
|
||||||
|
);
|
||||||
|
},
|
||||||
effect: async (_, { cancelActiveListeners, delay, getState }) => {
|
effect: async (_, { cancelActiveListeners, delay, getState }) => {
|
||||||
// The following 2 lines will throttle updates to avoid creating history entries when rapid changes
|
// The following 2 lines will debounce updates to avoid creating history entries when rapid changes
|
||||||
// are committed to the store.
|
// are committed to the store.
|
||||||
cancelActiveListeners();
|
cancelActiveListeners();
|
||||||
await delay(200);
|
await delay(200);
|
||||||
@ -118,7 +134,7 @@ export function useStateSync(params: ExploreQueryParams) {
|
|||||||
dispatch(syncTimesAction({ syncedTimes: !paneTimesUnequal })); // if all time ranges are equal, keep them synced
|
dispatch(syncTimesAction({ syncedTimes: !paneTimesUnequal })); // if all time ranges are equal, keep them synced
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.entries(urlState.panes).forEach(([exploreId, urlPane], i) => {
|
Object.entries(urlState.panes).forEach(async ([exploreId, urlPane], i) => {
|
||||||
const { datasource, queries, range, panelsState } = urlPane;
|
const { datasource, queries, range, panelsState } = urlPane;
|
||||||
|
|
||||||
const paneState = panesState[exploreId];
|
const paneState = panesState[exploreId];
|
||||||
@ -129,11 +145,11 @@ export function useStateSync(params: ExploreQueryParams) {
|
|||||||
Promise.resolve()
|
Promise.resolve()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
if (update.datasource && datasource) {
|
if (update.datasource && datasource) {
|
||||||
await dispatch(changeDatasource(exploreId, datasource));
|
await dispatch(changeDatasource({ exploreId, datasource }));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(async () => {
|
||||||
if (update.range) {
|
if (update.range) {
|
||||||
dispatch(updateTime({ exploreId, rawRange: fromURLRange(range) }));
|
dispatch(updateTime({ exploreId, rawRange: fromURLRange(range) }));
|
||||||
}
|
}
|
||||||
@ -143,6 +159,7 @@ export function useStateSync(params: ExploreQueryParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (update.queries || update.range) {
|
if (update.queries || update.range) {
|
||||||
|
await dispatch(cancelQueries(exploreId));
|
||||||
dispatch(runQueries({ exploreId }));
|
dispatch(runQueries({ exploreId }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +298,9 @@ export function useStateSync(params: ExploreQueryParams) {
|
|||||||
|
|
||||||
prevParams.current = params;
|
prevParams.current = params;
|
||||||
|
|
||||||
isURLOutOfSync && initState.current === 'done' && sync();
|
if (isURLOutOfSync && initState.current === 'done') {
|
||||||
|
sync();
|
||||||
|
}
|
||||||
}, [dispatch, panesState, orgId, location, params, warning]);
|
}, [dispatch, panesState, orgId, location, params, warning]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { DataSourceRef } from '@grafana/schema';
|
|||||||
import { RefreshPicker } from '@grafana/ui';
|
import { RefreshPicker } from '@grafana/ui';
|
||||||
import { stopQueryState } from 'app/core/utils/explore';
|
import { stopQueryState } from 'app/core/utils/explore';
|
||||||
import { getCorrelationsBySourceUIDs } from 'app/features/correlations/utils';
|
import { getCorrelationsBySourceUIDs } from 'app/features/correlations/utils';
|
||||||
import { ExploreItemState, ThunkResult } from 'app/types';
|
import { ExploreItemState, createAsyncThunk } from 'app/types';
|
||||||
|
|
||||||
import { loadSupplementaryQueries } from '../utils/supplementaryQueries';
|
import { loadSupplementaryQueries } from '../utils/supplementaryQueries';
|
||||||
|
|
||||||
@ -39,12 +39,15 @@ export const updateDatasourceInstanceAction = createAction<UpdateDatasourceInsta
|
|||||||
/**
|
/**
|
||||||
* Loads a new datasource identified by the given name.
|
* Loads a new datasource identified by the given name.
|
||||||
*/
|
*/
|
||||||
export function changeDatasource(
|
|
||||||
exploreId: string,
|
interface ChangeDatasourcePayload {
|
||||||
datasource: string | DataSourceRef,
|
exploreId: string;
|
||||||
options?: { importQueries: boolean }
|
datasource: string | DataSourceRef;
|
||||||
): ThunkResult<Promise<void>> {
|
options?: { importQueries: boolean };
|
||||||
return async (dispatch, getState) => {
|
}
|
||||||
|
export const changeDatasource = createAsyncThunk(
|
||||||
|
'explore/changeDatasource',
|
||||||
|
async ({ datasource, exploreId, options }: ChangeDatasourcePayload, { getState, dispatch }) => {
|
||||||
const orgId = getState().user.orgId;
|
const orgId = getState().user.orgId;
|
||||||
const { history, instance } = await loadAndInitDatasource(orgId, datasource);
|
const { history, instance } = await loadAndInitDatasource(orgId, datasource);
|
||||||
const currentDataSourceInstance = getState().explore.panes[exploreId]!.datasourceInstance;
|
const currentDataSourceInstance = getState().explore.panes[exploreId]!.datasourceInstance;
|
||||||
@ -80,8 +83,8 @@ export function changeDatasource(
|
|||||||
if (options?.importQueries) {
|
if (options?.importQueries) {
|
||||||
dispatch(runQueries({ exploreId }));
|
dispatch(runQueries({ exploreId }));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Reducer
|
// Reducer
|
||||||
|
Loading…
Reference in New Issue
Block a user