From 6ecc420534ba17e84bd98f2ff56eaf692f07370e Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 27 Jul 2022 10:17:31 -0500 Subject: [PATCH] Revert "Explore: Add Mixed Datasource (#51605)" (#52889) This reverts commit e2258120e742b31ecde50e8de93544220a0762a3. --- conf/defaults.ini | 2 +- docs/sources/explore/_index.md | 4 - .../src/types/featureToggles.gen.ts | 1 - pkg/services/featuremgmt/registry.go | 6 -- pkg/services/featuremgmt/toggles_gen.go | 4 - public/app/core/utils/explore.ts | 72 +++++----------- public/app/core/utils/richHistory.ts | 2 +- public/app/features/explore/Explore.tsx | 4 +- .../features/explore/ExplorePaneContainer.tsx | 11 +-- .../app/features/explore/ExploreToolbar.tsx | 1 - public/app/features/explore/Wrapper.test.tsx | 7 +- .../features/explore/spec/helper/setup.tsx | 26 +++--- .../app/features/explore/state/datasource.ts | 6 -- .../explore/state/explorePane.test.ts | 3 - .../app/features/explore/state/explorePane.ts | 4 +- .../app/features/explore/state/query.test.ts | 33 ++----- public/app/features/explore/state/query.ts | 86 +++++++------------ .../datasource/mixed/MixedDataSource.ts | 9 +- 18 files changed, 87 insertions(+), 194 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 218efc49054..51c835d1822 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1208,7 +1208,7 @@ lokiQueryBuilder = true # Experimental Explore to Dashboard workflow explore2Dashboard = true -# Command Palette +# Experimental Command Palette commandPalette = true # Use dynamic labels in CloudWatch datasource diff --git a/docs/sources/explore/_index.md b/docs/sources/explore/_index.md index 013e01db4c2..190fb78c2e4 100644 --- a/docs/sources/explore/_index.md +++ b/docs/sources/explore/_index.md @@ -73,7 +73,3 @@ The Share shortened link capability allows you to create smaller and simpler URL > **Note:** Available in Grafana 8.5.0 and later versions. Enabled by default, allows users to create panels in dashboards from within Explore. - -### exploreMixedDatasource - -Disabled by default, allows users in Explore to have different datasources for different queries. If compatible, results will be combined. diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index c69788424f4..c59f8e728f0 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -44,7 +44,6 @@ export interface FeatureToggles { export?: boolean; azureMonitorResourcePickerForMetrics?: boolean; explore2Dashboard?: boolean; - exploreMixedDatasource?: boolean; tracing?: boolean; commandPalette?: boolean; cloudWatchDynamicLabels?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index ed284fb8f8f..e38dc69157e 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -159,12 +159,6 @@ var ( State: FeatureStateBeta, FrontendOnly: true, }, - { - Name: "exploreMixedDatasource", - Description: "Enable mixed datasource in Explore", - State: FeatureStateAlpha, - FrontendOnly: true, - }, { Name: "tracing", Description: "Adds trace ID to error notifications", diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 239a454c9ab..35a6b40560c 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -119,10 +119,6 @@ const ( // Experimental Explore to Dashboard workflow FlagExplore2Dashboard = "explore2Dashboard" - // FlagExploreMixedDatasource - // Enable mixed datasource in Explore - FlagExploreMixedDatasource = "exploreMixedDatasource" - // FlagTracing // Adds trace ID to error notifications FlagTracing = "tracing" diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 27d60caa9e0..a28a58909bb 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -7,7 +7,6 @@ import { DataQuery, DataQueryRequest, DataSourceApi, - DataSourceRef, dateMath, DateTime, DefaultTimeZone, @@ -25,7 +24,7 @@ import { toUtc, urlUtil, } from '@grafana/data'; -import { DataSourceSrv, getDataSourceSrv } from '@grafana/runtime'; +import { DataSourceSrv } from '@grafana/runtime'; import { RefreshPicker } from '@grafana/ui'; import store from 'app/core/store'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; @@ -71,6 +70,19 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise omit(t, 'legendFormat')); let url: string | undefined; + // Mixed datasources need to choose only one datasource + if (exploreDatasource.meta?.id === 'mixed' && exploreTargets) { + // Find first explore datasource among targets + for (const t of exploreTargets) { + const datasource = await datasourceSrv.get(t.datasource || undefined); + if (datasource) { + exploreDatasource = datasource; + exploreTargets = panel.targets.filter((t) => t.datasource === datasource.name); + break; + } + } + } + if (exploreDatasource) { const range = timeSrv.timeRangeForUrl(); let state: Partial = { range }; @@ -87,7 +99,7 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise ({ ...t, datasource: exploreDatasource.getRef() })), }; } @@ -242,34 +254,8 @@ export function generateKey(index = 0): string { return `Q-${uuidv4()}-${index}`; } -export async function generateEmptyQuery( - queries: DataQuery[], - index = 0, - dataSourceOverride?: DataSourceRef -): Promise { - let datasourceInstance: DataSourceApi | undefined; - let datasourceRef: DataSourceRef | null | undefined; - let defaultQuery: Partial | undefined; - - // datasource override is if we have switched datasources with no carry-over - we want to create a new query with a datasource we define - if (dataSourceOverride) { - datasourceRef = dataSourceOverride; - } else if (queries.length > 0 && queries[queries.length - 1].datasource) { - // otherwise use last queries' datasource - datasourceRef = queries[queries.length - 1].datasource; - } else { - // if neither exists, use the default datasource - datasourceInstance = await getDataSourceSrv().get(); - defaultQuery = datasourceInstance.getDefaultQuery?.(CoreApp.Explore); - datasourceRef = datasourceInstance.getRef(); - } - - if (!datasourceInstance) { - datasourceInstance = await getDataSourceSrv().get(datasourceRef); - defaultQuery = datasourceInstance.getDefaultQuery?.(CoreApp.Explore); - } - - return { refId: getNextRefIdChar(queries), key: generateKey(index), datasource: datasourceRef, ...defaultQuery }; +export function generateEmptyQuery(queries: DataQuery[], index = 0): DataQuery { + return { refId: getNextRefIdChar(queries), key: generateKey(index) }; } export const generateNewKeyAndAddRefIdIfMissing = (target: DataQuery, queries: DataQuery[], index = 0): DataQuery => { @@ -281,13 +267,8 @@ export const generateNewKeyAndAddRefIdIfMissing = (target: DataQuery, queries: D /** * Ensure at least one target exists and that targets have the necessary keys - * - * This will return an empty array if there are no datasources, as Explore is not usable in that state */ -export async function ensureQueries( - queries?: DataQuery[], - newQueryDataSourceOverride?: DataSourceRef -): Promise { +export function ensureQueries(queries?: DataQuery[]): DataQuery[] { if (queries && typeof queries === 'object' && queries.length > 0) { const allQueries = []; for (let index = 0; index < queries.length; index++) { @@ -306,18 +287,7 @@ export async function ensureQueries( } return allQueries; } - - try { - // if a datasourse override get its ref, otherwise get the default datasource - const emptyQueryRef = newQueryDataSourceOverride ?? (await getDataSourceSrv().get()).getRef(); - - const emptyQuery = await generateEmptyQuery(queries ?? [], undefined, emptyQueryRef); - return [emptyQuery]; - } catch { - // if there are no datasources, return an empty array because we will not allow use of explore - // this will occur on init of explore with no datasources defined - return []; - } + return [{ ...generateEmptyQuery(queries ?? []) }]; } /** @@ -374,9 +344,9 @@ export function clearHistory(datasourceId: string) { store.delete(historyKey); } -export const getQueryKeys = (queries: DataQuery[]): string[] => { +export const getQueryKeys = (queries: DataQuery[], datasourceInstance?: DataSourceApi | null): string[] => { const queryKeys = queries.reduce((newQueryKeys, query, index) => { - const primaryKey = query.datasource?.uid || query.key; + const primaryKey = datasourceInstance && datasourceInstance.name ? datasourceInstance.name : query.key; return newQueryKeys.concat(`${primaryKey}-${index}`); }, []); diff --git a/public/app/core/utils/richHistory.ts b/public/app/core/utils/richHistory.ts index fc66871eedd..c2c34ef50e3 100644 --- a/public/app/core/utils/richHistory.ts +++ b/public/app/core/utils/richHistory.ts @@ -264,7 +264,7 @@ export function mapQueriesToHeadings(query: RichHistoryQuery[], sortOrder: SortO */ export function createDatasourcesList() { return getDataSourceSrv() - .getList({ mixed: true }) + .getList() .map((dsSettings) => { return { name: dsSettings.name, diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 32124d86453..55233403242 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -158,8 +158,8 @@ export class Explore extends React.PureComponent { }; onClickAddQueryRowButton = () => { - const { exploreId, queryKeys } = this.props; - this.props.addQueryRow(exploreId, queryKeys.length); + const { exploreId, queryKeys, datasourceInstance } = this.props; + this.props.addQueryRow(exploreId, queryKeys.length, datasourceInstance); }; onMakeAbsoluteTime = () => { diff --git a/public/app/features/explore/ExplorePaneContainer.tsx b/public/app/features/explore/ExplorePaneContainer.tsx index 44604f849d5..0e7dc124a1f 100644 --- a/public/app/features/explore/ExplorePaneContainer.tsx +++ b/public/app/features/explore/ExplorePaneContainer.tsx @@ -3,7 +3,7 @@ import memoizeOne from 'memoize-one'; import React from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import { ExploreUrlState, EventBusExtended, EventBusSrv, GrafanaTheme2 } from '@grafana/data'; +import { DataQuery, ExploreUrlState, EventBusExtended, EventBusSrv, GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Themeable2, withTheme2 } from '@grafana/ui'; import store from 'app/core/store'; @@ -64,17 +64,16 @@ class ExplorePaneContainerUnconnected extends React.PureComponent { }; } - async componentDidMount() { + componentDidMount() { const { initialized, exploreId, initialDatasource, initialQueries, initialRange, panelsState } = this.props; const width = this.el?.offsetWidth ?? 0; // initialize the whole explore first time we mount and if browser history contains a change in datasource if (!initialized) { - const queries = await ensureQueries(initialQueries); // this will return an empty array if there are no datasources this.props.initializeExplore( exploreId, initialDatasource, - queries, + initialQueries, initialRange, width, this.exploreEvents, @@ -117,6 +116,7 @@ class ExplorePaneContainerUnconnected extends React.PureComponent { } } +const ensureQueriesMemoized = memoizeOne(ensureQueries); const getTimeRangeFromUrlMemoized = memoizeOne(getTimeRangeFromUrl); function mapStateToProps(state: StoreState, props: OwnProps) { @@ -126,6 +126,7 @@ function mapStateToProps(state: StoreState, props: OwnProps) { const { datasource, queries, range: urlRange, panelsState } = (urlState || {}) as ExploreUrlState; const initialDatasource = datasource || store.get(lastUsedDatasourceKeyForOrgId(state.user.orgId)); + const initialQueries: DataQuery[] = ensureQueriesMemoized(queries); const initialRange = urlRange ? getTimeRangeFromUrlMemoized(urlRange, timeZone, fiscalYearStartMonth) : getTimeRange(timeZone, DEFAULT_RANGE, fiscalYearStartMonth); @@ -133,7 +134,7 @@ function mapStateToProps(state: StoreState, props: OwnProps) { return { initialized: state.explore[props.exploreId]?.initialized, initialDatasource, - initialQueries: queries, + initialQueries, initialRange, panelsState, }; diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 8bc68cbc009..34ded3050a7 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -145,7 +145,6 @@ class UnConnectedExploreToolbar extends PureComponent { !datasourceMissing && ( { it('shows warning if there are no data sources', async () => { setupExplore({ datasources: [] }); - await waitFor(() => screen.getByText(/Explore requires at least one data source/i)); + // Will throw if isn't found + screen.getByText(/Explore requires at least one data source/i); }); it('inits url and renders editor but does not call query on empty url', async () => { @@ -51,7 +52,7 @@ describe('Wrapper', () => { orgId: '1', left: serializeStateToUrlParam({ datasource: 'loki', - queries: [{ refId: 'A', datasource: { type: 'logs', uid: 'loki' } }], + queries: [{ refId: 'A' }], range: { from: 'now-1h', to: 'now' }, }), }); @@ -143,7 +144,7 @@ describe('Wrapper', () => { orgId: '1', left: serializeStateToUrlParam({ datasource: 'elastic', - queries: [{ refId: 'A', datasource: { type: 'logs', uid: 'elastic' } }], + queries: [{ refId: 'A' }], range: { from: 'now-1h', to: 'now' }, }), }); diff --git a/public/app/features/explore/spec/helper/setup.tsx b/public/app/features/explore/spec/helper/setup.tsx index 99c6041675a..1e9566e9614 100644 --- a/public/app/features/explore/spec/helper/setup.tsx +++ b/public/app/features/explore/spec/helper/setup.tsx @@ -32,7 +32,7 @@ type SetupOptions = { }; export function setupExplore(options?: SetupOptions): { - datasources: { [uid: string]: DataSourceApi }; + datasources: { [name: string]: DataSourceApi }; store: ReturnType; unmount: () => void; container: HTMLElement; @@ -58,19 +58,15 @@ export function setupExplore(options?: SetupOptions): { getInstanceSettings(ref: DataSourceRef) { return dsSettings.map((d) => d.settings).find((x) => x.name === ref || x.uid === ref || x.uid === ref.uid); }, - get(datasource?: string | DataSourceRef | null, scopedVars?: ScopedVars): Promise { - if (dsSettings.length === 0) { - return Promise.resolve(undefined); - } else { - const datasourceStr = typeof datasource === 'string'; - return Promise.resolve( - (datasource - ? dsSettings.find((d) => - datasourceStr ? d.api.name === datasource || d.api.uid === datasource : d.api.uid === datasource?.uid - ) - : dsSettings[0])!.api - ); - } + get(datasource?: string | DataSourceRef | null, scopedVars?: ScopedVars): Promise { + const datasourceStr = typeof datasource === 'string'; + return Promise.resolve( + (datasource + ? dsSettings.find((d) => + datasourceStr ? d.api.name === datasource || d.api.uid === datasource : d.api.uid === datasource?.uid + ) + : dsSettings[0])!.api + ); }, } as any); @@ -153,7 +149,7 @@ function makeDatasourceSetup({ name = 'loki', id = 1 }: { name?: string; id?: nu name: name, uid: name, query: jest.fn(), - getRef: jest.fn().mockReturnValue({ type: 'logs', uid: name }), + getRef: jest.fn().mockReturnValue(name), meta, } as any, }; diff --git a/public/app/features/explore/state/datasource.ts b/public/app/features/explore/state/datasource.ts index eb67e44f75b..d06e675ff2a 100644 --- a/public/app/features/explore/state/datasource.ts +++ b/public/app/features/explore/state/datasource.ts @@ -2,7 +2,6 @@ import { AnyAction, createAction } from '@reduxjs/toolkit'; import { DataSourceApi, HistoryItem } from '@grafana/data'; -import { reportInteraction } from '@grafana/runtime'; import { RefreshPicker } from '@grafana/ui'; import { stopQueryState } from 'app/core/utils/explore'; import { ExploreItemState, ThunkResult } from 'app/types'; @@ -45,11 +44,6 @@ export function changeDatasource( const { history, instance } = await loadAndInitDatasource(orgId, { uid: datasourceUid }); const currentDataSourceInstance = getState().explore[exploreId]!.datasourceInstance; - reportInteraction('explore_change_ds', { - from: (currentDataSourceInstance?.meta?.mixed ? 'mixed' : currentDataSourceInstance?.type) || 'unknown', - to: instance.meta.mixed ? 'mixed' : instance.type, - exploreId, - }); dispatch( updateDatasourceInstanceAction({ exploreId, diff --git a/public/app/features/explore/state/explorePane.test.ts b/public/app/features/explore/state/explorePane.test.ts index 478708e8ea6..70b957d17d6 100644 --- a/public/app/features/explore/state/explorePane.test.ts +++ b/public/app/features/explore/state/explorePane.test.ts @@ -72,9 +72,6 @@ function setup(state?: any) { testDatasource: jest.fn(), init: jest.fn(), name: 'default', - getRef() { - return { type: 'default', uid: 'default' }; - }, } ); }, diff --git a/public/app/features/explore/state/explorePane.ts b/public/app/features/explore/state/explorePane.ts index 74e2456b9c0..fc066dd1d07 100644 --- a/public/app/features/explore/state/explorePane.ts +++ b/public/app/features/explore/state/explorePane.ts @@ -222,7 +222,7 @@ export function refreshExplore(exploreId: ExploreId, newUrlQuery: string): Thunk // commit changes based on the diff of new url vs old url if (update.datasource) { - const initialQueries = await ensureQueries(queries); + const initialQueries = ensureQueries(queries); await dispatch( initializeExplore(exploreId, datasource, initialQueries, range, containerWidth, eventBridge, panelsState) ); @@ -304,7 +304,7 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac range, queries, initialized: true, - queryKeys: getQueryKeys(queries), + queryKeys: getQueryKeys(queries, datasourceInstance), datasourceInstance, history, datasourceMissing: !datasourceInstance, diff --git a/public/app/features/explore/state/query.test.ts b/public/app/features/explore/state/query.test.ts index a21c1307238..7a8b85aa51f 100644 --- a/public/app/features/explore/state/query.test.ts +++ b/public/app/features/explore/state/query.test.ts @@ -154,31 +154,12 @@ describe('running queries', () => { describe('importing queries', () => { describe('when importing queries between the same type of data source', () => { it('remove datasource property from all of the queries', async () => { - const datasources: DataSourceApi[] = [ - { - name: 'testDs', - type: 'postgres', - uid: 'ds1', - getRef: () => { - return { type: 'postgres', uid: 'ds1' }; - }, - } as DataSourceApi, - { - name: 'testDs2', - type: 'postgres', - uid: 'ds2', - getRef: () => { - return { type: 'postgres', uid: 'ds2' }; - }, - } as DataSourceApi, - ]; - const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({ ...(defaultInitialState as any), explore: { [ExploreId.left]: { ...defaultInitialState.explore[ExploreId.left], - datasourceInstance: datasources[0], + datasourceInstance: { name: 'testDs', type: 'postgres' }, }, }, }); @@ -187,18 +168,18 @@ describe('importing queries', () => { importQueries( ExploreId.left, [ - { datasource: { type: 'postgresql', uid: 'ds1' }, refId: 'refId_A' }, - { datasource: { type: 'postgresql', uid: 'ds1' }, refId: 'refId_B' }, + { datasource: { type: 'postgresql' }, refId: 'refId_A' }, + { datasource: { type: 'postgresql' }, refId: 'refId_B' }, ], - datasources[0], - datasources[1] + { name: 'Postgres1', type: 'postgres' } as DataSourceApi, + { name: 'Postgres2', type: 'postgres' } as DataSourceApi ) ); expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('refId', 'refId_A'); expect(getState().explore[ExploreId.left].queries[1]).toHaveProperty('refId', 'refId_B'); - expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('datasource.uid', 'ds2'); - expect(getState().explore[ExploreId.left].queries[1]).toHaveProperty('datasource.uid', 'ds2'); + expect(getState().explore[ExploreId.left].queries[0]).not.toHaveProperty('datasource'); + expect(getState().explore[ExploreId.left].queries[1]).not.toHaveProperty('datasource'); }); }); }); diff --git a/public/app/features/explore/state/query.ts b/public/app/features/explore/state/query.ts index 98d996b7745..bcb3fbfa34b 100644 --- a/public/app/features/explore/state/query.ts +++ b/public/app/features/explore/state/query.ts @@ -1,11 +1,11 @@ import { AnyAction, createAction, PayloadAction } from '@reduxjs/toolkit'; import deepEqual from 'fast-deep-equal'; -import { flatten, groupBy } from 'lodash'; import { identity, Observable, of, SubscriptionLike, Unsubscribable } from 'rxjs'; import { mergeMap, throttleTime } from 'rxjs/operators'; import { AbsoluteTimeRange, + CoreApp, DataQuery, DataQueryErrorType, DataQueryResponse, @@ -20,7 +20,7 @@ import { QueryFixAction, toLegacyResponseData, } from '@grafana/data'; -import { config, getDataSourceSrv, reportInteraction } from '@grafana/runtime'; +import { config, reportInteraction } from '@grafana/runtime'; import { buildQueryTransaction, ensureQueries, @@ -33,7 +33,6 @@ import { } from 'app/core/utils/explore'; import { getShiftedTimeRange } from 'app/core/utils/timePicker'; import { getTimeZone } from 'app/features/profile/state/selectors'; -import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; import { ExploreItemState, ExplorePanelData, ThunkDispatch, ThunkResult } from 'app/types'; import { ExploreId, ExploreState, QueryOptions } from 'app/types/explore'; @@ -215,10 +214,17 @@ export const clearCacheAction = createAction('explore/clearCa /** * Adds a query row after the row with the given index. */ -export function addQueryRow(exploreId: ExploreId, index: number): ThunkResult { - return async (dispatch, getState) => { +export function addQueryRow( + exploreId: ExploreId, + index: number, + datasource: DataSourceApi | undefined | null +): ThunkResult { + return (dispatch, getState) => { const queries = getState().explore[exploreId]!.queries; - const query = await generateEmptyQuery(queries, index); + const query = { + ...datasource?.getDefaultQuery?.(CoreApp.Explore), + ...generateEmptyQuery(queries, index), + }; dispatch(addQueryRowAction({ exploreId, index, query })); }; @@ -245,32 +251,6 @@ export function cancelQueries(exploreId: ExploreId): ThunkResult { }; } -const addDatasourceToQueries = (datasource: DataSourceApi, queries: DataQuery[]) => { - const dataSourceRef = datasource.getRef(); - return queries.map((query: DataQuery) => { - return { ...query, datasource: dataSourceRef }; - }); -}; - -const getImportableQueries = async ( - targetDataSource: DataSourceApi, - sourceDataSource: DataSourceApi, - queries: DataQuery[] -): Promise => { - let queriesOut: DataQuery[] = []; - if (sourceDataSource.meta?.id === targetDataSource.meta?.id) { - queriesOut = queries; - } else if (hasQueryExportSupport(sourceDataSource) && hasQueryImportSupport(targetDataSource)) { - const abstractQueries = await sourceDataSource.exportToAbstractQueries(queries); - queriesOut = await targetDataSource.importFromAbstractQueries(abstractQueries); - } else if (targetDataSource.importQueries) { - // Datasource-specific importers - queriesOut = await targetDataSource.importQueries(queries, sourceDataSource); - } - // add new datasource to queries before returning - return addDatasourceToQueries(targetDataSource, queriesOut); -}; - /** * Import queries from previous datasource if possible eg Loki and Prometheus have similar query language so the * labels part can be reused to get similar data. @@ -293,27 +273,23 @@ export const importQueries = ( } let importedQueries = queries; - // If going to mixed, keep queries with source datasource - if (targetDataSource.name === MIXED_DATASOURCE_NAME) { - importedQueries = queries.map((query) => { - return { ...query, datasource: sourceDataSource.getRef() }; - }); - } - // If going from mixed, see what queries you keep by their individual datasources - else if (sourceDataSource.name === MIXED_DATASOURCE_NAME) { - const groupedQueries = groupBy(queries, (query) => query.datasource?.uid); - const groupedImportableQueries = await Promise.all( - Object.keys(groupedQueries).map(async (key: string) => { - const queryDatasource = await getDataSourceSrv().get({ uid: key }); - return await getImportableQueries(targetDataSource, queryDatasource, groupedQueries[key]); - }) - ); - importedQueries = flatten(groupedImportableQueries.filter((arr) => arr.length > 0)); + // Check if queries can be imported from previously selected datasource + if (sourceDataSource.meta?.id === targetDataSource.meta?.id) { + // Keep same queries if same type of datasource, but delete datasource query property to prevent mismatch of new and old data source instance + importedQueries = queries.map(({ datasource, ...query }) => query); + } else if (hasQueryExportSupport(sourceDataSource) && hasQueryImportSupport(targetDataSource)) { + const abstractQueries = await sourceDataSource.exportToAbstractQueries(queries); + importedQueries = await targetDataSource.importFromAbstractQueries(abstractQueries); + } else if (targetDataSource.importQueries) { + // Datasource-specific importers + importedQueries = await targetDataSource.importQueries(queries, sourceDataSource); } else { - importedQueries = await getImportableQueries(targetDataSource, sourceDataSource, queries); + // Default is blank queries + importedQueries = ensureQueries(); } - const nextQueries = await ensureQueries(importedQueries, targetDataSource.getRef()); + const nextQueries = ensureQueries(importedQueries); + dispatch(queriesImportedAction({ exploreId, queries: nextQueries })); }; }; @@ -663,7 +639,7 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor return { ...state, queries: nextQueries, - queryKeys: getQueryKeys(nextQueries), + queryKeys: getQueryKeys(nextQueries, state.datasourceInstance), }; } @@ -709,7 +685,7 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor return { ...state, queries: nextQueries, - queryKeys: getQueryKeys(nextQueries), + queryKeys: getQueryKeys(nextQueries, state.datasourceInstance), }; } @@ -718,7 +694,7 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor return { ...state, queries: queries.slice(), - queryKeys: getQueryKeys(queries), + queryKeys: getQueryKeys(queries, state.datasourceInstance), }; } @@ -727,7 +703,7 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor return { ...state, queries, - queryKeys: getQueryKeys(queries), + queryKeys: getQueryKeys(queries, state.datasourceInstance), }; } @@ -784,7 +760,7 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor return { ...state, queries, - queryKeys: getQueryKeys(queries), + queryKeys: getQueryKeys(queries, state.datasourceInstance), }; } diff --git a/public/app/plugins/datasource/mixed/MixedDataSource.ts b/public/app/plugins/datasource/mixed/MixedDataSource.ts index b9d3761c273..a266215149c 100644 --- a/public/app/plugins/datasource/mixed/MixedDataSource.ts +++ b/public/app/plugins/datasource/mixed/MixedDataSource.ts @@ -1,4 +1,4 @@ -import { cloneDeep, groupBy, omit } from 'lodash'; +import { cloneDeep, groupBy } from 'lodash'; import { forkJoin, from, Observable, of, OperatorFunction } from 'rxjs'; import { catchError, map, mergeAll, mergeMap, reduce, toArray } from 'rxjs/operators'; @@ -98,13 +98,6 @@ export class MixedDatasource extends DataSourceApi { return Promise.resolve({}); } - getQueryDisplayText(query: DataQuery) { - const strippedQuery = omit(query, ['key', 'refId', 'datasource']); - const strippedQueryJSON = JSON.stringify(strippedQuery); - const prefix = query.datasource?.type ? `${query.datasource?.type}: ` : ''; - return `${prefix}${strippedQueryJSON}`; - } - private isQueryable(query: BatchedQueries): boolean { return query && Array.isArray(query.targets) && query.targets.length > 0; }