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;
|
||||
if (exploreId && changeDatasourceUid) {
|
||||
dispatch(changeDatasource(exploreId, changeDatasourceUid, { importQueries: true }));
|
||||
dispatch(
|
||||
changeDatasource({ exploreId, datasource: changeDatasourceUid, options: { importQueries: true } })
|
||||
);
|
||||
dispatch(
|
||||
changeCorrelationEditorDetails({
|
||||
isExiting: false,
|
||||
@ -143,7 +145,7 @@ export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, Expl
|
||||
|
||||
const changeDatasourcePostAction = (exploreId: string, datasourceUid: string) => {
|
||||
setSaveMessage(undefined);
|
||||
dispatch(changeDatasource(exploreId, datasourceUid, { importQueries: true }));
|
||||
dispatch(changeDatasource({ exploreId, datasource: datasourceUid, options: { importQueries: true } }));
|
||||
};
|
||||
|
||||
const saveCorrelationPostAction = (skipPostConfirmAction: boolean) => {
|
||||
@ -163,7 +165,7 @@ export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, Expl
|
||||
action === CORRELATION_EDITOR_POST_CONFIRM_ACTION.CHANGE_DATASOURCE &&
|
||||
changeDatasourceUid !== undefined
|
||||
) {
|
||||
changeDatasource(exploreId, changeDatasourceUid);
|
||||
changeDatasource({ exploreId, datasource: changeDatasourceUid });
|
||||
resetEditor();
|
||||
}
|
||||
} else {
|
||||
|
@ -100,7 +100,7 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
||||
|
||||
const onChangeDatasource = async (dsSettings: DataSourceInstanceSettings) => {
|
||||
if (!isCorrelationsEditorMode) {
|
||||
dispatch(changeDatasource(exploreId, dsSettings.uid, { importQueries: true }));
|
||||
dispatch(changeDatasource({ exploreId, datasource: dsSettings.uid, options: { importQueries: true } }));
|
||||
} else {
|
||||
if (correlationDetails?.correlationDirty || correlationDetails?.queryEditorDirty) {
|
||||
// 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(() => {
|
||||
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 differentDataSource = queryHistoryItem.datasourceUid !== datasourceInstance?.uid;
|
||||
if (differentDataSource) {
|
||||
await changeDatasource(exploreId, queryHistoryItem.datasourceUid);
|
||||
await changeDatasource({ exploreId, datasource: queryHistoryItem.datasourceUid });
|
||||
}
|
||||
setQueries(exploreId, queriesToRun);
|
||||
|
||||
|
@ -13,7 +13,7 @@ import { addListener, ExploreItemState, ExploreQueryParams, useDispatch, useSele
|
||||
import { changeDatasource } from '../../state/datasource';
|
||||
import { changePanelsStateAction, initializeExplore } from '../../state/explorePane';
|
||||
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 { changeRangeAction, updateTime } from '../../state/time';
|
||||
import { DEFAULT_RANGE, fromURLRange } from '../../state/utils';
|
||||
@ -32,6 +32,7 @@ export function useStateSync(params: ExploreQueryParams) {
|
||||
const orgId = useSelector((state) => state.user.orgId);
|
||||
const prevParams = useRef(params);
|
||||
const initState = useRef<'notstarted' | 'pending' | 'done'>('notstarted');
|
||||
const paused = useRef(false);
|
||||
const { warning } = useAppNotification();
|
||||
|
||||
useEffect(() => {
|
||||
@ -47,20 +48,35 @@ export function useStateSync(params: ExploreQueryParams) {
|
||||
const unsubscribe = dispatch(
|
||||
addListener({
|
||||
predicate: (action) =>
|
||||
// We want to update the URL when:
|
||||
// - a pane is opened or closed
|
||||
// - a query is run
|
||||
// - range is changed
|
||||
// - panel state is updated
|
||||
/*
|
||||
We want to update the URL when:
|
||||
- a pane is opened or closed
|
||||
- a query is run
|
||||
- range is changed
|
||||
- panel state is updated
|
||||
- a datasource change has completed.
|
||||
|
||||
Note: Changing datasource causes a bunch of actions to be dispatched, we want to update the URL
|
||||
only when the change set has completed. This is done by checking if the changeDatasource.pending action
|
||||
has been dispatched and pausing the listener until the changeDatasource.fulfilled action is dispatched.
|
||||
*/
|
||||
|
||||
{
|
||||
paused.current = changeDatasource.pending.type === action.type;
|
||||
|
||||
return (
|
||||
[
|
||||
splitClose.type,
|
||||
splitOpen.fulfilled.type,
|
||||
runQueries.pending.type,
|
||||
changeRangeAction.type,
|
||||
changePanelsStateAction.type,
|
||||
].includes(action.type),
|
||||
changeDatasource.fulfilled.type,
|
||||
].includes(action.type) && !paused.current
|
||||
);
|
||||
},
|
||||
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.
|
||||
cancelActiveListeners();
|
||||
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
|
||||
}
|
||||
|
||||
Object.entries(urlState.panes).forEach(([exploreId, urlPane], i) => {
|
||||
Object.entries(urlState.panes).forEach(async ([exploreId, urlPane], i) => {
|
||||
const { datasource, queries, range, panelsState } = urlPane;
|
||||
|
||||
const paneState = panesState[exploreId];
|
||||
@ -129,11 +145,11 @@ export function useStateSync(params: ExploreQueryParams) {
|
||||
Promise.resolve()
|
||||
.then(async () => {
|
||||
if (update.datasource && datasource) {
|
||||
await dispatch(changeDatasource(exploreId, datasource));
|
||||
await dispatch(changeDatasource({ exploreId, datasource }));
|
||||
}
|
||||
return;
|
||||
})
|
||||
.then(() => {
|
||||
.then(async () => {
|
||||
if (update.range) {
|
||||
dispatch(updateTime({ exploreId, rawRange: fromURLRange(range) }));
|
||||
}
|
||||
@ -143,6 +159,7 @@ export function useStateSync(params: ExploreQueryParams) {
|
||||
}
|
||||
|
||||
if (update.queries || update.range) {
|
||||
await dispatch(cancelQueries(exploreId));
|
||||
dispatch(runQueries({ exploreId }));
|
||||
}
|
||||
|
||||
@ -281,7 +298,9 @@ export function useStateSync(params: ExploreQueryParams) {
|
||||
|
||||
prevParams.current = params;
|
||||
|
||||
isURLOutOfSync && initState.current === 'done' && sync();
|
||||
if (isURLOutOfSync && initState.current === 'done') {
|
||||
sync();
|
||||
}
|
||||
}, [dispatch, panesState, orgId, location, params, warning]);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { DataSourceRef } from '@grafana/schema';
|
||||
import { RefreshPicker } from '@grafana/ui';
|
||||
import { stopQueryState } from 'app/core/utils/explore';
|
||||
import { getCorrelationsBySourceUIDs } from 'app/features/correlations/utils';
|
||||
import { ExploreItemState, ThunkResult } from 'app/types';
|
||||
import { ExploreItemState, createAsyncThunk } from 'app/types';
|
||||
|
||||
import { loadSupplementaryQueries } from '../utils/supplementaryQueries';
|
||||
|
||||
@ -39,12 +39,15 @@ export const updateDatasourceInstanceAction = createAction<UpdateDatasourceInsta
|
||||
/**
|
||||
* Loads a new datasource identified by the given name.
|
||||
*/
|
||||
export function changeDatasource(
|
||||
exploreId: string,
|
||||
datasource: string | DataSourceRef,
|
||||
options?: { importQueries: boolean }
|
||||
): ThunkResult<Promise<void>> {
|
||||
return async (dispatch, getState) => {
|
||||
|
||||
interface ChangeDatasourcePayload {
|
||||
exploreId: string;
|
||||
datasource: string | DataSourceRef;
|
||||
options?: { importQueries: boolean };
|
||||
}
|
||||
export const changeDatasource = createAsyncThunk(
|
||||
'explore/changeDatasource',
|
||||
async ({ datasource, exploreId, options }: ChangeDatasourcePayload, { getState, dispatch }) => {
|
||||
const orgId = getState().user.orgId;
|
||||
const { history, instance } = await loadAndInitDatasource(orgId, datasource);
|
||||
const currentDataSourceInstance = getState().explore.panes[exploreId]!.datasourceInstance;
|
||||
@ -80,8 +83,8 @@ export function changeDatasource(
|
||||
if (options?.importQueries) {
|
||||
dispatch(runQueries({ exploreId }));
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
//
|
||||
// Reducer
|
||||
|
Loading…
Reference in New Issue
Block a user