mirror of
https://github.com/grafana/grafana.git
synced 2026-09-05 04:40:13 -05:00
Query History: Remove migration (#67470)
This commit is contained in:
@@ -173,18 +173,6 @@ describe('RichHistoryRemoteStorage', () => {
|
||||
} as Partial<UserPreferencesDTO>);
|
||||
});
|
||||
|
||||
it('migrates provided rich history items', async () => {
|
||||
const { richHistoryQuery, dto } = setup();
|
||||
fetchMock.mockReturnValue(of({}));
|
||||
await storage.migrate([richHistoryQuery]);
|
||||
expect(fetchMock).toBeCalledWith({
|
||||
url: '/api/query-history/migrate',
|
||||
method: 'POST',
|
||||
data: { queries: [dto] },
|
||||
showSuccessAlert: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('stars query history items', async () => {
|
||||
const { richHistoryQuery, dto } = setup();
|
||||
postMock.mockResolvedValue({
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PreferencesService } from '../services/PreferencesService';
|
||||
import { RichHistorySearchFilters, RichHistorySettings, SortOrder } from '../utils/richHistoryTypes';
|
||||
|
||||
import RichHistoryStorage, { RichHistoryStorageWarningDetails } from './RichHistoryStorage';
|
||||
import { fromDTO, toDTO } from './remoteStorageConverter';
|
||||
import { fromDTO } from './remoteStorageConverter';
|
||||
|
||||
export type RichHistoryRemoteStorageDTO = {
|
||||
uid: string;
|
||||
@@ -19,18 +19,6 @@ export type RichHistoryRemoteStorageDTO = {
|
||||
queries: DataQuery[];
|
||||
};
|
||||
|
||||
type RichHistoryRemoteStorageMigrationDTO = {
|
||||
datasourceUid: string;
|
||||
queries: DataQuery[];
|
||||
createdAt: number;
|
||||
starred: boolean;
|
||||
comment: string;
|
||||
};
|
||||
|
||||
type RichHistoryRemoteStorageMigrationPayloadDTO = {
|
||||
queries: RichHistoryRemoteStorageMigrationDTO[];
|
||||
};
|
||||
|
||||
type RichHistoryRemoteStorageResultsPayloadDTO = {
|
||||
result: {
|
||||
queryHistory: RichHistoryRemoteStorageDTO[];
|
||||
@@ -122,21 +110,6 @@ export default class RichHistoryRemoteStorage implements RichHistoryStorage {
|
||||
}
|
||||
return fromDTO(dto.result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Used only for migration purposes. Will be removed in future.
|
||||
*/
|
||||
async migrate(richHistory: RichHistoryQuery[]) {
|
||||
const data: RichHistoryRemoteStorageMigrationPayloadDTO = { queries: richHistory.map(toDTO) };
|
||||
await lastValueFrom(
|
||||
getBackendSrv().fetch({
|
||||
url: '/api/query-history/migrate',
|
||||
method: 'POST',
|
||||
data,
|
||||
showSuccessAlert: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function buildQueryParams(filters: RichHistorySearchFilters): string {
|
||||
|
||||
@@ -96,5 +96,4 @@ export const RICH_HISTORY_SETTING_KEYS = {
|
||||
starredTabAsFirstTab: 'grafana.explore.richHistory.starredTabAsFirstTab',
|
||||
activeDatasourceOnly: 'grafana.explore.richHistory.activeDatasourceOnly',
|
||||
datasourceFilters: 'grafana.explore.richHistory.datasourceFilters',
|
||||
migrated: 'grafana.explore.richHistory.migrated',
|
||||
};
|
||||
|
||||
@@ -13,9 +13,7 @@ import {
|
||||
createQueryHeading,
|
||||
deleteAllFromRichHistory,
|
||||
deleteQueryInRichHistory,
|
||||
migrateQueryHistoryFromLocalStorage,
|
||||
SortOrder,
|
||||
LocalStorageMigrationStatus,
|
||||
} from './richHistory';
|
||||
|
||||
const richHistoryStorageMock: RichHistoryStorage = {} as RichHistoryStorage;
|
||||
@@ -179,35 +177,6 @@ describe('richHistory', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('migration', () => {
|
||||
beforeEach(() => {
|
||||
richHistoryRemoteStorageMock.migrate.mockReset();
|
||||
});
|
||||
|
||||
it('migrates history', async () => {
|
||||
const history = { richHistory: [{ id: 'test' }, { id: 'test2' }], total: 2 };
|
||||
|
||||
richHistoryLocalStorageMock.getRichHistory.mockReturnValue(history);
|
||||
const migrationResult = await migrateQueryHistoryFromLocalStorage();
|
||||
expect(richHistoryRemoteStorageMock.migrate).toBeCalledWith(history.richHistory);
|
||||
expect(migrationResult.status).toBe(LocalStorageMigrationStatus.Successful);
|
||||
expect(migrationResult.error).toBeUndefined();
|
||||
});
|
||||
it('does not migrate if there are no entries', async () => {
|
||||
richHistoryLocalStorageMock.getRichHistory.mockReturnValue({ richHistory: [] });
|
||||
const migrationResult = await migrateQueryHistoryFromLocalStorage();
|
||||
expect(richHistoryRemoteStorageMock.migrate).not.toBeCalled();
|
||||
expect(migrationResult.status).toBe(LocalStorageMigrationStatus.NotNeeded);
|
||||
expect(migrationResult.error).toBeUndefined();
|
||||
});
|
||||
it('propagates thrown errors', async () => {
|
||||
richHistoryLocalStorageMock.getRichHistory.mockRejectedValue(new Error('migration failed'));
|
||||
const migrationResult = await migrateQueryHistoryFromLocalStorage();
|
||||
expect(migrationResult.status).toBe(LocalStorageMigrationStatus.Failed);
|
||||
expect(migrationResult.error?.message).toBe('migration failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapNumbertoTimeInSlider', () => {
|
||||
it('should correctly map number to value', () => {
|
||||
const value = mapNumbertoTimeInSlider(25);
|
||||
|
||||
@@ -4,17 +4,11 @@ import { DataQuery, DataSourceApi, dateTimeFormat, ExploreUrlState, urlUtil } fr
|
||||
import { serializeStateToUrlParam } from '@grafana/data/src/utils/url';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import {
|
||||
createErrorNotification,
|
||||
createSuccessNotification,
|
||||
createWarningNotification,
|
||||
} from 'app/core/copy/appNotification';
|
||||
import { createErrorNotification, createWarningNotification } from 'app/core/copy/appNotification';
|
||||
import { dispatch } from 'app/store/store';
|
||||
import { RichHistoryQuery } from 'app/types/explore';
|
||||
|
||||
import { config } from '../config';
|
||||
import RichHistoryLocalStorage from '../history/RichHistoryLocalStorage';
|
||||
import RichHistoryRemoteStorage from '../history/RichHistoryRemoteStorage';
|
||||
import {
|
||||
RichHistoryResults,
|
||||
RichHistoryServiceError,
|
||||
@@ -134,43 +128,6 @@ export async function deleteQueryInRichHistory(id: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export enum LocalStorageMigrationStatus {
|
||||
Successful = 'successful',
|
||||
Failed = 'failed',
|
||||
NotNeeded = 'not-needed',
|
||||
}
|
||||
|
||||
export interface LocalStorageMigrationResult {
|
||||
status: LocalStorageMigrationStatus;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export async function migrateQueryHistoryFromLocalStorage(): Promise<LocalStorageMigrationResult> {
|
||||
const richHistoryLocalStorage = new RichHistoryLocalStorage();
|
||||
const richHistoryRemoteStorage = new RichHistoryRemoteStorage();
|
||||
|
||||
try {
|
||||
const { richHistory } = await richHistoryLocalStorage.getRichHistory({
|
||||
datasourceFilters: [],
|
||||
from: 0,
|
||||
search: '',
|
||||
sortOrder: SortOrder.Descending,
|
||||
starred: false,
|
||||
to: 14,
|
||||
});
|
||||
if (richHistory.length === 0) {
|
||||
return { status: LocalStorageMigrationStatus.NotNeeded };
|
||||
}
|
||||
await richHistoryRemoteStorage.migrate(richHistory);
|
||||
dispatch(notifyApp(createSuccessNotification('Query history successfully migrated from local storage')));
|
||||
return { status: LocalStorageMigrationStatus.Successful };
|
||||
} catch (error) {
|
||||
const errorToThrow = error instanceof Error ? error : new Error('Uknown error occurred.');
|
||||
dispatch(notifyApp(createWarningNotification(`Query history migration failed. ${errorToThrow.message}`)));
|
||||
return { status: LocalStorageMigrationStatus.Failed, error: errorToThrow };
|
||||
}
|
||||
}
|
||||
|
||||
export const createUrlFromRichHistory = (query: RichHistoryQuery) => {
|
||||
const exploreState: ExploreUrlState = {
|
||||
/* Default range, as we are not saving timerange in rich history */
|
||||
|
||||
@@ -61,7 +61,6 @@ function setup(queries: DataQuery[]) {
|
||||
right: undefined,
|
||||
richHistoryStorageFull: false,
|
||||
richHistoryLimitExceededWarningShown: false,
|
||||
richHistoryMigrationFailed: false,
|
||||
};
|
||||
const store = configureStore({ explore: initialState, user: { orgId: 1 } as UserState });
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ import { lastUsedDatasourceKeyForOrgId } from 'app/core/utils/explore';
|
||||
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
import { RICH_HISTORY_KEY, RichHistoryLocalStorageDTO } from '../../../../core/history/RichHistoryLocalStorage';
|
||||
import { RICH_HISTORY_SETTING_KEYS } from '../../../../core/history/richHistoryLocalStorageUtils';
|
||||
import { LokiDatasource } from '../../../../plugins/datasource/loki/datasource';
|
||||
import { LokiQuery } from '../../../../plugins/datasource/loki/types';
|
||||
import { ExploreId } from '../../../../types';
|
||||
@@ -196,18 +194,3 @@ export const withinExplore = (exploreId: ExploreId) => {
|
||||
const container = screen.getAllByTestId('data-testid Explore');
|
||||
return within(container[exploreId === ExploreId.left ? 0 : 1]);
|
||||
};
|
||||
|
||||
export const localStorageHasAlreadyBeenMigrated = () => {
|
||||
window.localStorage.setItem(RICH_HISTORY_SETTING_KEYS.migrated, 'true');
|
||||
};
|
||||
|
||||
export const setupLocalStorageRichHistory = (dsName: string) => {
|
||||
const richHistoryDTO: RichHistoryLocalStorageDTO = {
|
||||
ts: Date.now(),
|
||||
datasourceName: dsName,
|
||||
starred: true,
|
||||
comment: '',
|
||||
queries: [{ refId: 'A' }],
|
||||
};
|
||||
window.localStorage.setItem(RICH_HISTORY_KEY, JSON.stringify([richHistoryDTO]));
|
||||
};
|
||||
|
||||
@@ -31,13 +31,7 @@ import {
|
||||
switchToQueryHistoryTab,
|
||||
} from './helper/interactions';
|
||||
import { makeLogsQueryResponse } from './helper/query';
|
||||
import {
|
||||
localStorageHasAlreadyBeenMigrated,
|
||||
setupExplore,
|
||||
setupLocalStorageRichHistory,
|
||||
tearDown,
|
||||
waitForExplore,
|
||||
} from './helper/setup';
|
||||
import { setupExplore, tearDown, waitForExplore } from './helper/setup';
|
||||
|
||||
const fetchMock = jest.fn();
|
||||
const postMock = jest.fn();
|
||||
@@ -225,56 +219,8 @@ describe('Explore: Query History', () => {
|
||||
assertDataSourceFilterVisibility(false);
|
||||
});
|
||||
|
||||
describe('local storage migration', () => {
|
||||
it('does not migrate if query history is not enabled', async () => {
|
||||
config.queryHistoryEnabled = false;
|
||||
const { datasources } = setupExplore();
|
||||
setupLocalStorageRichHistory('loki');
|
||||
(datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
getMock.mockReturnValue({ result: { queryHistory: [] } });
|
||||
await waitForExplore();
|
||||
|
||||
await openQueryHistory();
|
||||
expect(postMock).not.toBeCalledWith('/api/query-history/migrate', { queries: [] });
|
||||
expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', {
|
||||
queryHistoryEnabled: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('migrates query history from local storage', async () => {
|
||||
config.queryHistoryEnabled = true;
|
||||
const { datasources } = setupExplore();
|
||||
setupLocalStorageRichHistory('loki');
|
||||
(datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
fetchMock.mockReturnValue(of({ data: { result: { queryHistory: [] } } }));
|
||||
await waitForExplore();
|
||||
|
||||
await openQueryHistory();
|
||||
expect(fetchMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
url: expect.stringMatching('/api/query-history/migrate'),
|
||||
data: { queries: [expect.objectContaining({ datasourceUid: 'loki-uid' })] },
|
||||
})
|
||||
);
|
||||
fetchMock.mockReset();
|
||||
fetchMock.mockReturnValue(of({ data: { result: { queryHistory: [] } } }));
|
||||
|
||||
await closeQueryHistory();
|
||||
await openQueryHistory();
|
||||
expect(fetchMock).not.toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
url: expect.stringMatching('/api/query-history/migrate'),
|
||||
})
|
||||
);
|
||||
expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', {
|
||||
queryHistoryEnabled: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('pagination', async () => {
|
||||
config.queryHistoryEnabled = true;
|
||||
localStorageHasAlreadyBeenMigrated();
|
||||
const { datasources } = setupExplore();
|
||||
(datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
fetchMock.mockReturnValue(
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { AnyAction, createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { HistoryItem } from '@grafana/data';
|
||||
import { config, logError } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { RICH_HISTORY_SETTING_KEYS } from 'app/core/history/richHistoryLocalStorageUtils';
|
||||
import store from 'app/core/store';
|
||||
import {
|
||||
addToRichHistory,
|
||||
deleteAllFromRichHistory,
|
||||
deleteQueryInRichHistory,
|
||||
getRichHistory,
|
||||
getRichHistorySettings,
|
||||
LocalStorageMigrationStatus,
|
||||
migrateQueryHistoryFromLocalStorage,
|
||||
updateCommentInRichHistory,
|
||||
updateRichHistorySettings,
|
||||
updateStarredInRichHistory,
|
||||
@@ -24,7 +19,6 @@ import { RichHistorySearchFilters, RichHistorySettings } from '../../../core/uti
|
||||
|
||||
import {
|
||||
richHistoryLimitExceededAction,
|
||||
richHistoryMigrationFailedAction,
|
||||
richHistorySearchFiltersUpdatedAction,
|
||||
richHistorySettingsUpdatedAction,
|
||||
richHistoryStorageFullAction,
|
||||
@@ -173,21 +167,6 @@ export const clearRichHistoryResults = (exploreId: ExploreId): ThunkResult<void>
|
||||
*/
|
||||
export const initRichHistory = (): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const queriesMigrated = store.getBool(RICH_HISTORY_SETTING_KEYS.migrated, false);
|
||||
const migrationFailedDuringThisSession = getState().explore.richHistoryMigrationFailed;
|
||||
|
||||
// Query history migration should always be successful, but in case of unexpected errors we ensure
|
||||
// the migration attempt happens only once per session, and the user is informed about the failure
|
||||
// in a way that can help with potential investigation.
|
||||
if (config.queryHistoryEnabled && !queriesMigrated && !migrationFailedDuringThisSession) {
|
||||
const migrationResult = await migrateQueryHistoryFromLocalStorage();
|
||||
if (migrationResult.status === LocalStorageMigrationStatus.Failed) {
|
||||
dispatch(richHistoryMigrationFailedAction());
|
||||
logError(migrationResult.error!, { explore: { event: 'QueryHistoryMigrationFailed' } });
|
||||
} else {
|
||||
store.set(RICH_HISTORY_SETTING_KEYS.migrated, true);
|
||||
}
|
||||
}
|
||||
let settings = getState().explore.richHistorySettings;
|
||||
if (!settings) {
|
||||
settings = await getRichHistorySettings();
|
||||
|
||||
@@ -31,7 +31,6 @@ export const richHistoryUpdatedAction = createAction<{ richHistoryResults: RichH
|
||||
);
|
||||
export const richHistoryStorageFullAction = createAction('explore/richHistoryStorageFullAction');
|
||||
export const richHistoryLimitExceededAction = createAction('explore/richHistoryLimitExceededAction');
|
||||
export const richHistoryMigrationFailedAction = createAction('explore/richHistoryMigrationFailedAction');
|
||||
|
||||
export const richHistorySettingsUpdatedAction = createAction<RichHistorySettings>('explore/richHistorySettingsUpdated');
|
||||
export const richHistorySearchFiltersUpdatedAction = createAction<{
|
||||
@@ -175,7 +174,6 @@ export const initialExploreState: ExploreState = {
|
||||
correlations: undefined,
|
||||
richHistoryStorageFull: false,
|
||||
richHistoryLimitExceededWarningShown: false,
|
||||
richHistoryMigrationFailed: false,
|
||||
largerExploreId: undefined,
|
||||
maxedExploreId: undefined,
|
||||
evenSplitPanes: true,
|
||||
@@ -256,13 +254,6 @@ export const exploreReducer = (state = initialExploreState, action: AnyAction):
|
||||
};
|
||||
}
|
||||
|
||||
if (richHistoryMigrationFailedAction.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
richHistoryMigrationFailed: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (resetExploreAction.match(action)) {
|
||||
const leftState = state[ExploreId.left];
|
||||
const rightState = state[ExploreId.right];
|
||||
|
||||
@@ -65,11 +65,6 @@ export interface ExploreState {
|
||||
*/
|
||||
richHistoryLimitExceededWarningShown: boolean;
|
||||
|
||||
/**
|
||||
* True if a warning message about failed rich history has been shown already in this session.
|
||||
*/
|
||||
richHistoryMigrationFailed: boolean;
|
||||
|
||||
/**
|
||||
* On a split manual resize, we calculate which pane is larger, or if they are roughly the same size. If undefined, it is not split or they are roughly the same size
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user