Explore: Remove exploreMixedDatasource feature toggle (#71534)

This commit is contained in:
Giordano Ricci 2023-07-17 12:25:49 +01:00 committed by GitHub
parent 480ccf6e8f
commit c5d2d55654
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 122 additions and 264 deletions

View File

@ -66,11 +66,3 @@ Available in Grafana 7.3 and later versions.
{{% /admonition %}}
The Share shortened link capability allows you to create smaller and simpler URLs of the format /goto/:uid instead of using longer URLs with query parameters. To create a shortened link to the executed query, click the **Share** option in the Explore toolbar. A shortened link that is never used will automatically get deleted after seven (7) days.
## Available feature toggles
### exploreMixedDatasource
Enabled by default, allows users in Explore to have different data sources for different queries. If compatible, results will be combined.
Learn more about how to use [Mixed data source]({{< relref "../datasources/#special-data-sources" >}}).

View File

@ -53,6 +53,8 @@ Filter query history in Query history and Starred tab by data source name:
1. Click the **Filter queries for specific data source(s)** field.
1. Select the data source for which you would like to filter your history. You can select multiple data sources.
> **Note:** Queries ran using the Mixed data source will appear only when filtering for Mixed and not when filtering by their individual data sources.
In **Query history** tab it is also possible to filter queries by date using the slider:
- Use vertical slider to filter queries by date.

View File

@ -23,7 +23,6 @@ Some features are enabled by default. You can disable these feature by setting t
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `disableEnvelopeEncryption` | Disable envelope encryption (emergency only) | |
| `featureHighlights` | Highlight Grafana Enterprise features | |
| `exploreMixedDatasource` | Enable mixed datasource in Explore | Yes |
| `dataConnectionsConsole` | Enables a new top-level page called Connections. This page is an experiment that provides a better experience when you install and configure data sources and other plugins. | Yes |
| `topnav` | Enables new top navigation and page layouts | Yes |
| `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes |

View File

@ -29,7 +29,6 @@ export interface FeatureToggles {
featureHighlights?: boolean;
migrationLocking?: boolean;
storage?: boolean;
exploreMixedDatasource?: boolean;
newTraceViewHeader?: boolean;
correlations?: boolean;
datasourceQueryMultiStatus?: boolean;

View File

@ -79,14 +79,6 @@ var (
Stage: FeatureStageExperimental,
Owner: grafanaAppPlatformSquad,
},
{
Name: "exploreMixedDatasource",
Description: "Enable mixed datasource in Explore",
Stage: FeatureStageGeneralAvailability,
FrontendOnly: true,
Expression: "true", // turned on by default
Owner: grafanaExploreSquad,
},
{
Name: "newTraceViewHeader",
Description: "Shows the new trace view header",

View File

@ -10,7 +10,6 @@ lokiExperimentalStreaming,experimental,@grafana/observability-logs,false,false,f
featureHighlights,GA,@grafana/grafana-as-code,false,false,false,false
migrationLocking,preview,@grafana/backend-platform,false,false,false,false
storage,experimental,@grafana/grafana-app-platform-squad,false,false,false,false
exploreMixedDatasource,GA,@grafana/explore-squad,false,false,false,true
newTraceViewHeader,experimental,@grafana/observability-traces-and-profiling,false,false,false,true
correlations,preview,@grafana/explore-squad,false,false,false,false
datasourceQueryMultiStatus,experimental,@grafana/plugins-platform-backend,false,false,false,false

1 Name Stage Owner requiresDevMode RequiresLicense RequiresRestart FrontendOnly
10 featureHighlights GA @grafana/grafana-as-code false false false false
11 migrationLocking preview @grafana/backend-platform false false false false
12 storage experimental @grafana/grafana-app-platform-squad false false false false
exploreMixedDatasource GA @grafana/explore-squad false false false true
13 newTraceViewHeader experimental @grafana/observability-traces-and-profiling false false false true
14 correlations preview @grafana/explore-squad false false false false
15 datasourceQueryMultiStatus experimental @grafana/plugins-platform-backend false false false false

View File

@ -51,10 +51,6 @@ const (
// Configurable storage for dashboards, datasources, and resources
FlagStorage = "storage"
// FlagExploreMixedDatasource
// Enable mixed datasource in Explore
FlagExploreMixedDatasource = "exploreMixedDatasource"
// FlagNewTraceViewHeader
// Shows the new trace view header
FlagNewTraceViewHeader = "newTraceViewHeader"

View File

@ -74,22 +74,6 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise<strin
.map((t) => omit(t, 'legendFormat'))
.filter((t) => t.datasource?.uid !== ExpressionDatasourceUID);
let url: string | undefined;
// if the mixed datasource is not enabled for explore, choose only one datasource
if (
config.featureToggles.exploreMixedDatasource === false &&
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();

View File

@ -8,7 +8,6 @@ import { createErrorNotification, createWarningNotification } from 'app/core/cop
import { dispatch } from 'app/store/store';
import { RichHistoryQuery } from 'app/types/explore';
import { config } from '../config';
import {
RichHistoryResults,
RichHistoryServiceError,
@ -218,7 +217,7 @@ export function mapQueriesToHeadings(query: RichHistoryQuery[], sortOrder: SortO
*/
export function createDatasourcesList() {
return getDataSourceSrv()
.getList({ mixed: config.featureToggles.exploreMixedDatasource === true })
.getList({ mixed: true })
.map((dsSettings) => {
return {
name: dsSettings.name,

View File

@ -4,7 +4,7 @@ import React, { RefObject, useMemo } from 'react';
import { shallowEqual } from 'react-redux';
import { DataSourceInstanceSettings, RawTimeRange } from '@grafana/data';
import { config, reportInteraction } from '@grafana/runtime';
import { reportInteraction } from '@grafana/runtime';
import { defaultIntervals, PageToolbar, RefreshPicker, SetInterval, ToolbarButton, ButtonGroup } from '@grafana/ui';
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
import { createAndCopyShortLink } from 'app/core/utils/shortLinks';
@ -135,7 +135,7 @@ export function ExploreToolbar({ exploreId, topOfViewRef, onChangeTime }: Props)
leftItems={[
<DataSourcePicker
key={`${exploreId}-ds-picker`}
mixed={config.featureToggles.exploreMixedDatasource === true}
mixed
onChange={onChangeDatasource}
current={datasourceInstance?.getRef()}
hideTextValue={showSmallDataSourcePicker}

View File

@ -54,10 +54,9 @@ function defaultDsGetter(datasources: Array<ReturnType<typeof makeDatasourceSetu
interface SetupParams {
queryParams?: UrlQueryMap;
exploreMixedDatasource?: boolean;
datasourceGetter?: (datasources: Array<ReturnType<typeof makeDatasourceSetup>>) => DataSourceSrv['get'];
}
function setup({ queryParams = {}, exploreMixedDatasource = false, datasourceGetter = defaultDsGetter }: SetupParams) {
function setup({ queryParams = {}, datasourceGetter = defaultDsGetter }: SetupParams) {
const history = createMemoryHistory({
initialEntries: [{ pathname: '/explore', search: stringify(queryParams) }],
});
@ -67,12 +66,9 @@ function setup({ queryParams = {}, exploreMixedDatasource = false, datasourceGet
const datasources = [
makeDatasourceSetup({ name: 'loki', uid: 'loki-uid' }),
makeDatasourceSetup({ name: 'elastic', uid: 'elastic-uid' }),
makeDatasourceSetup({ name: MIXED_DATASOURCE_NAME, uid: MIXED_DATASOURCE_NAME, id: 999 }),
];
if (exploreMixedDatasource) {
datasources.push(makeDatasourceSetup({ name: MIXED_DATASOURCE_NAME, uid: MIXED_DATASOURCE_NAME, id: 999 }));
}
setDataSourceSrv({
get: datasourceGetter(datasources),
getInstanceSettings: jest.fn(),
@ -104,12 +100,6 @@ function setup({ queryParams = {}, exploreMixedDatasource = false, datasourceGet
grafanaContext={{
...context,
location,
config: {
...context.config,
featureToggles: {
exploreMixedDatasource,
},
},
}}
store={store}
>
@ -408,7 +398,6 @@ describe('useStateSync', () => {
it('uses the first query datasource if no root datasource is specified in the URL', async () => {
const { waitForNextUpdate, store } = setup({
exploreMixedDatasource: true,
queryParams: {
panes: JSON.stringify({
one: {
@ -429,7 +418,6 @@ describe('useStateSync', () => {
it('updates the URL opening and closing a pane datasource changes', async () => {
const { waitForNextUpdate, store, location } = setup({
exploreMixedDatasource: true,
queryParams: {
panes: JSON.stringify({
one: {
@ -467,28 +455,25 @@ describe('useStateSync', () => {
});
});
describe('with exploreMixedDatasource enabled', () => {
it('filters out queries from the URL that do not have a datasource', async () => {
const { waitForNextUpdate, store } = setup({
exploreMixedDatasource: true,
queryParams: {
panes: JSON.stringify({
one: {
datasource: MIXED_DATASOURCE_NAME,
queries: [
{ expr: 'a', refId: 'A' },
{ expr: 'b', datasource: { uid: 'loki-uid', type: 'logs' }, refId: 'B' },
],
},
}),
schemaVersion: 1,
},
});
await waitForNextUpdate();
expect(store.getState().explore.panes['one']?.queries.length).toBe(1);
expect(store.getState().explore.panes['one']?.queries[0]).toMatchObject({ expr: 'b', refId: 'B' });
it('filters out queries from the URL that do not have a datasource', async () => {
const { waitForNextUpdate, store } = setup({
queryParams: {
panes: JSON.stringify({
one: {
datasource: MIXED_DATASOURCE_NAME,
queries: [
{ expr: 'a', refId: 'A' },
{ expr: 'b', datasource: { uid: 'loki-uid', type: 'logs' }, refId: 'B' },
],
},
}),
schemaVersion: 1,
},
});
await waitForNextUpdate();
expect(store.getState().explore.panes['one']?.queries.length).toBe(1);
expect(store.getState().explore.panes['one']?.queries[0]).toMatchObject({ expr: 'b', refId: 'B' });
});
});

View File

@ -25,12 +25,7 @@ import { parseURL } from './parseURL';
* Bi-directionally syncs URL changes with Explore's state.
*/
export function useStateSync(params: ExploreQueryParams) {
const {
location,
config: {
featureToggles: { exploreMixedDatasource },
},
} = useGrafana();
const { location } = useGrafana();
const dispatch = useDispatch();
const panesState = useSelector(selectPanes);
const orgId = useSelector((state) => state.user.orgId);
@ -170,7 +165,7 @@ export function useStateSync(params: ExploreQueryParams) {
Promise.all(
Object.entries(urlState.panes).map(([exploreId, { datasource, queries, range, panelsState }]) => {
return getPaneDatasource(datasource, queries, orgId, !!exploreMixedDatasource).then((paneDatasource) => {
return getPaneDatasource(datasource, queries, orgId).then((paneDatasource) => {
return Promise.resolve(
// Given the Grafana datasource will always be present, this should always be defined.
paneDatasource
@ -254,7 +249,7 @@ export function useStateSync(params: ExploreQueryParams) {
prevParams.current = params;
isURLOutOfSync && initState.current === 'done' && sync();
}, [dispatch, panesState, exploreMixedDatasource, orgId, location, params]);
}, [dispatch, panesState, orgId, location, params]);
}
function getDefaultQuery(ds: DataSourceApi) {
@ -303,7 +298,7 @@ async function removeQueriesWithInvalidDatasource(queries: DataQuery[]) {
/**
* Returns the datasource that an explore pane should be using.
* If the URL specifies a datasource and that datasource exists, it will be used unless said datasource is mixed and `allowMixed` is false.
* If the URL specifies a datasource and that datasource exists, it will be used unless said datasource is mixed.
* Otherwise the datasource will be extracetd from the the first query specifying a valid datasource.
*
* If there's no datasource in the queries, the last used datasource will be used.
@ -312,28 +307,22 @@ async function removeQueriesWithInvalidDatasource(queries: DataQuery[]) {
* @param rootDatasource the top-level datasource specified in the URL
* @param queries the queries in the pane
* @param orgId the orgId of the user
* @param allowMixed whether mixed datasources are allowed
*
* @returns the datasource UID that the pane should use, undefined if no suitable datasource is found
*/
async function getPaneDatasource(
rootDatasource: DataSourceRef | string | null | undefined,
queries: DataQuery[],
orgId: number,
allowMixed: boolean
orgId: number
) {
// If there's a root datasource, use it unless it's mixed and we don't allow mixed.
// If there's a root datasource, use it unless it's unavailable
if (rootDatasource) {
try {
const ds = await getDatasourceSrv().get(rootDatasource);
if (!isMixedDatasource(ds) || allowMixed) {
return ds;
}
return await getDatasourceSrv().get(rootDatasource);
} catch (_) {}
}
// TODO: if queries have multiple datasources and allowMixed is true, we should return mixed datasource
// TODO: if queries have multiple datasources we should return mixed datasource
// Else we try to find a datasource in the queries, returning the first one that exists
const queriesWithDS = queries.filter((q) => q.datasource);
for (const query of queriesWithDS) {

View File

@ -25,12 +25,6 @@ describe('useTimeSrvFix', () => {
grafanaContext={{
...context,
location,
config: {
...context.config,
featureToggles: {
exploreMixedDatasource: true,
},
},
}}
>
{children}

View File

@ -18,7 +18,6 @@ import {
import {
setDataSourceSrv,
setEchoSrv,
config,
setLocationService,
HistoryWrapper,
LocationService,
@ -45,7 +44,6 @@ type SetupOptions = {
datasources?: DatasourceSetup[];
urlParams?: ExploreQueryParams;
prevUsedDatasource?: { orgId: number; datasource: string };
mixedEnabled?: boolean;
};
export function setupExplore(options?: SetupOptions): {
@ -71,12 +69,9 @@ export function setupExplore(options?: SetupOptions): {
const defaultDatasources: DatasourceSetup[] = [
makeDatasourceSetup(),
makeDatasourceSetup({ name: 'elastic', id: 2 }),
makeDatasourceSetup({ name: MIXED_DATASOURCE_NAME, uid: MIXED_DATASOURCE_NAME, id: 999 }),
];
if (config.featureToggles.exploreMixedDatasource) {
defaultDatasources.push(makeDatasourceSetup({ name: MIXED_DATASOURCE_NAME, uid: MIXED_DATASOURCE_NAME, id: 999 }));
}
const dsSettings = options?.datasources || defaultDatasources;
setDataSourceSrv({
@ -138,17 +133,7 @@ export function setupExplore(options?: SetupOptions): {
const { unmount, container } = render(
<Provider store={storeState}>
<GrafanaContext.Provider
value={{
...contextMock,
config: {
...contextMock.config,
featureToggles: {
exploreMixedDatasource: options?.mixedEnabled ?? false,
},
},
}}
>
<GrafanaContext.Provider value={contextMock}>
<Router history={history}>
<Route
path="/explore"

View File

@ -15,7 +15,6 @@ import {
RawTimeRange,
SupplementaryQueryType,
} from '@grafana/data';
import { config } from '@grafana/runtime';
import { DataQuery, DataSourceRef } from '@grafana/schema';
import { createAsyncThunk, ExploreItemState, StoreState, ThunkDispatch } from 'app/types';
@ -99,9 +98,6 @@ jest.mock('@grafana/runtime', () => ({
},
};
},
config: {
featureToggles: { exploreMixedDatasource: false },
},
}));
function setupQueryResponse(state: StoreState) {
@ -447,155 +443,102 @@ describe('importing queries', () => {
});
describe('adding new query rows', () => {
describe('with mixed datasources disabled', () => {
it('should add query row when there is not yet a row and meta.mixed === true (impossible in UI)', async () => {
const queries: DataQuery[] = [];
const datasourceInstance = {
query: jest.fn(),
getRef: () => {
return { type: 'loki', uid: 'uid-loki' };
},
meta: {
id: 'mixed',
mixed: true,
} as unknown as DataSourcePluginMeta<{}>,
};
it('should add another query row if there are two rows already', async () => {
const queries = [
{
datasource: { type: 'loki', uid: 'ds3' },
refId: 'C',
},
{
datasource: { type: 'loki', uid: 'ds4' },
refId: 'D',
},
];
const datasourceInstance = {
query: jest.fn(),
getRef: jest.fn(),
meta: {
id: 'loki',
mixed: false,
} as unknown as DataSourcePluginMeta<{}>,
};
const getState = await setupStore(queries, datasourceInstance);
const getState = await setupStore(queries, datasourceInstance);
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('mixed');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(true);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['uid-loki-0']);
});
it('should add query row when there is not yet a row and meta.mixed === false', async () => {
const queries: DataQuery[] = [];
const datasourceInstance = {
query: jest.fn(),
getRef: () => {
return { type: 'loki', uid: 'uid-loki' };
},
meta: {
id: 'loki',
mixed: false,
} as unknown as DataSourcePluginMeta<{}>,
};
const getState = await setupStore(queries, datasourceInstance);
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['uid-loki-0']);
});
it('should add another query row if there are two rows already', async () => {
const queries = [
{
datasource: { type: 'loki', uid: 'ds3' },
refId: 'C',
},
{
datasource: { type: 'loki', uid: 'ds4' },
refId: 'D',
},
];
const datasourceInstance = {
query: jest.fn(),
getRef: jest.fn(),
meta: {
id: 'loki',
mixed: false,
} as unknown as DataSourcePluginMeta<{}>,
};
const getState = await setupStore(queries, datasourceInstance);
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(3);
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
});
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(3);
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
});
describe('with mixed datasources enabled', () => {
beforeEach(() => {
config.featureToggles.exploreMixedDatasource = true;
});
afterEach(() => {
config.featureToggles.exploreMixedDatasource = false;
});
it('should add query row whith root ds (without overriding the default ds) when there is not yet a row', async () => {
const queries: DataQuery[] = [];
const datasourceInstance = {
query: jest.fn(),
getRef: jest.fn(),
meta: {
id: 'mixed',
mixed: true,
} as unknown as DataSourcePluginMeta<{}>,
};
it('should add query row whith root ds (without overriding the default ds) when there is not yet a row', async () => {
const queries: DataQuery[] = [];
const datasourceInstance = {
query: jest.fn(),
getRef: jest.fn(),
meta: {
id: 'mixed',
mixed: true,
} as unknown as DataSourcePluginMeta<{}>,
};
const getState = await setupStore(queries, datasourceInstance);
const getState = await setupStore(queries, datasourceInstance);
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('mixed');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(true);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
expect(getState().explore.panes[exploreId]!.queries[0]?.datasource?.type).toBe('postgres');
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds1-0']);
});
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('mixed');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(true);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
expect(getState().explore.panes[exploreId]!.queries[0]?.datasource?.type).toBe('postgres');
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds1-0']);
});
it('should add query row whith root ds (with overriding the default ds) when there is not yet a row', async () => {
const queries: DataQuery[] = [];
const datasourceInstance = {
query: jest.fn(),
getRef: () => {
return { type: 'loki', uid: 'uid-loki' };
},
meta: {
id: 'loki',
mixed: false,
} as unknown as DataSourcePluginMeta<{}>,
};
it('should add query row whith root ds (with overriding the default ds) when there is not yet a row', async () => {
const queries: DataQuery[] = [];
const datasourceInstance = {
query: jest.fn(),
getRef: () => {
return { type: 'loki', uid: 'uid-loki' };
},
meta: {
id: 'loki',
mixed: false,
} as unknown as DataSourcePluginMeta<{}>,
};
const getState = await setupStore(queries, datasourceInstance);
const getState = await setupStore(queries, datasourceInstance);
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
expect(getState().explore.panes[exploreId]!.queries[0]?.datasource?.type).toBe('loki');
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['uid-loki-0']);
});
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
expect(getState().explore.panes[exploreId]!.queries[0]?.datasource?.type).toBe('loki');
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['uid-loki-0']);
});
it('should add another query row if there are two rows already', async () => {
const queries = [
{
datasource: { type: 'postgres', uid: 'ds3' },
refId: 'C',
},
{
datasource: { type: 'loki', uid: 'ds4' },
refId: 'D',
},
];
const datasourceInstance = {
query: jest.fn(),
getRef: jest.fn(),
meta: {
id: 'mixed',
mixed: true,
} as unknown as DataSourcePluginMeta<{}>,
};
it('should add another query row if there are two rows already (impossible in UI)', async () => {
const queries = [
{
datasource: { type: 'postgres', uid: 'ds3' },
refId: 'C',
},
{
datasource: { type: 'loki', uid: 'ds4' },
refId: 'D',
},
];
const datasourceInstance = {
query: jest.fn(),
getRef: jest.fn(),
meta: {
// datasourceInstance.meta.id is set to postgres because it's the default datasource
id: 'postgres',
mixed: false,
} as unknown as DataSourcePluginMeta<{}>,
};
const getState = await setupStore(queries, datasourceInstance);
const getState = await setupStore(queries, datasourceInstance);
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('postgres');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(3);
expect(getState().explore.panes[exploreId]!.queries[2]?.datasource?.type).toBe('loki');
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
});
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('mixed');
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(true);
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(3);
expect(getState().explore.panes[exploreId]!.queries[2]?.datasource?.type).toBe('loki');
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
});
});

View File

@ -258,7 +258,7 @@ export function addQueryRow(exploreId: string, index: number): ThunkResult<void>
// if it's not mixed, send it as an override. generateEmptyQuery doesn't have access to state
if (queries.length === 0) {
const rootDatasource = getState().explore.panes[exploreId]!.datasourceInstance;
if (!config.featureToggles.exploreMixedDatasource || !rootDatasource?.meta.mixed) {
if (!rootDatasource?.meta.mixed) {
datasourceOverride = rootDatasource;
}
}