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 %}} {{% /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. 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. 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. 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: In **Query history** tab it is also possible to filter queries by date using the slider:
- Use vertical slider to filter queries by date. - 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) | | | `disableEnvelopeEncryption` | Disable envelope encryption (emergency only) | |
| `featureHighlights` | Highlight Grafana Enterprise features | | | `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 | | `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 | | `topnav` | Enables new top navigation and page layouts | Yes |
| `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes | | `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes |

View File

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

View File

@ -79,14 +79,6 @@ var (
Stage: FeatureStageExperimental, Stage: FeatureStageExperimental,
Owner: grafanaAppPlatformSquad, Owner: grafanaAppPlatformSquad,
}, },
{
Name: "exploreMixedDatasource",
Description: "Enable mixed datasource in Explore",
Stage: FeatureStageGeneralAvailability,
FrontendOnly: true,
Expression: "true", // turned on by default
Owner: grafanaExploreSquad,
},
{ {
Name: "newTraceViewHeader", Name: "newTraceViewHeader",
Description: "Shows the new trace view header", 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 featureHighlights,GA,@grafana/grafana-as-code,false,false,false,false
migrationLocking,preview,@grafana/backend-platform,false,false,false,false migrationLocking,preview,@grafana/backend-platform,false,false,false,false
storage,experimental,@grafana/grafana-app-platform-squad,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 newTraceViewHeader,experimental,@grafana/observability-traces-and-profiling,false,false,false,true
correlations,preview,@grafana/explore-squad,false,false,false,false correlations,preview,@grafana/explore-squad,false,false,false,false
datasourceQueryMultiStatus,experimental,@grafana/plugins-platform-backend,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 // Configurable storage for dashboards, datasources, and resources
FlagStorage = "storage" FlagStorage = "storage"
// FlagExploreMixedDatasource
// Enable mixed datasource in Explore
FlagExploreMixedDatasource = "exploreMixedDatasource"
// FlagNewTraceViewHeader // FlagNewTraceViewHeader
// Shows the new trace view header // Shows the new trace view header
FlagNewTraceViewHeader = "newTraceViewHeader" FlagNewTraceViewHeader = "newTraceViewHeader"

View File

@ -74,22 +74,6 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise<strin
.map((t) => omit(t, 'legendFormat')) .map((t) => omit(t, 'legendFormat'))
.filter((t) => t.datasource?.uid !== ExpressionDatasourceUID); .filter((t) => t.datasource?.uid !== ExpressionDatasourceUID);
let url: string | undefined; 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) { if (exploreDatasource) {
const range = timeSrv.timeRangeForUrl(); const range = timeSrv.timeRangeForUrl();

View File

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

View File

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

View File

@ -54,10 +54,9 @@ function defaultDsGetter(datasources: Array<ReturnType<typeof makeDatasourceSetu
interface SetupParams { interface SetupParams {
queryParams?: UrlQueryMap; queryParams?: UrlQueryMap;
exploreMixedDatasource?: boolean;
datasourceGetter?: (datasources: Array<ReturnType<typeof makeDatasourceSetup>>) => DataSourceSrv['get']; 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({ const history = createMemoryHistory({
initialEntries: [{ pathname: '/explore', search: stringify(queryParams) }], initialEntries: [{ pathname: '/explore', search: stringify(queryParams) }],
}); });
@ -67,12 +66,9 @@ function setup({ queryParams = {}, exploreMixedDatasource = false, datasourceGet
const datasources = [ const datasources = [
makeDatasourceSetup({ name: 'loki', uid: 'loki-uid' }), makeDatasourceSetup({ name: 'loki', uid: 'loki-uid' }),
makeDatasourceSetup({ name: 'elastic', uid: 'elastic-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({ setDataSourceSrv({
get: datasourceGetter(datasources), get: datasourceGetter(datasources),
getInstanceSettings: jest.fn(), getInstanceSettings: jest.fn(),
@ -104,12 +100,6 @@ function setup({ queryParams = {}, exploreMixedDatasource = false, datasourceGet
grafanaContext={{ grafanaContext={{
...context, ...context,
location, location,
config: {
...context.config,
featureToggles: {
exploreMixedDatasource,
},
},
}} }}
store={store} store={store}
> >
@ -408,7 +398,6 @@ describe('useStateSync', () => {
it('uses the first query datasource if no root datasource is specified in the URL', async () => { it('uses the first query datasource if no root datasource is specified in the URL', async () => {
const { waitForNextUpdate, store } = setup({ const { waitForNextUpdate, store } = setup({
exploreMixedDatasource: true,
queryParams: { queryParams: {
panes: JSON.stringify({ panes: JSON.stringify({
one: { one: {
@ -429,7 +418,6 @@ describe('useStateSync', () => {
it('updates the URL opening and closing a pane datasource changes', async () => { it('updates the URL opening and closing a pane datasource changes', async () => {
const { waitForNextUpdate, store, location } = setup({ const { waitForNextUpdate, store, location } = setup({
exploreMixedDatasource: true,
queryParams: { queryParams: {
panes: JSON.stringify({ panes: JSON.stringify({
one: { 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 () => {
it('filters out queries from the URL that do not have a datasource', async () => { const { waitForNextUpdate, store } = setup({
const { waitForNextUpdate, store } = setup({ queryParams: {
exploreMixedDatasource: true, panes: JSON.stringify({
queryParams: { one: {
panes: JSON.stringify({ datasource: MIXED_DATASOURCE_NAME,
one: { queries: [
datasource: MIXED_DATASOURCE_NAME, { expr: 'a', refId: 'A' },
queries: [ { expr: 'b', datasource: { uid: 'loki-uid', type: 'logs' }, refId: 'B' },
{ expr: 'a', refId: 'A' }, ],
{ expr: 'b', datasource: { uid: 'loki-uid', type: 'logs' }, refId: 'B' }, },
], }),
}, schemaVersion: 1,
}), },
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' });
}); });
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. * Bi-directionally syncs URL changes with Explore's state.
*/ */
export function useStateSync(params: ExploreQueryParams) { export function useStateSync(params: ExploreQueryParams) {
const { const { location } = useGrafana();
location,
config: {
featureToggles: { exploreMixedDatasource },
},
} = useGrafana();
const dispatch = useDispatch(); const dispatch = useDispatch();
const panesState = useSelector(selectPanes); const panesState = useSelector(selectPanes);
const orgId = useSelector((state) => state.user.orgId); const orgId = useSelector((state) => state.user.orgId);
@ -170,7 +165,7 @@ export function useStateSync(params: ExploreQueryParams) {
Promise.all( Promise.all(
Object.entries(urlState.panes).map(([exploreId, { datasource, queries, range, panelsState }]) => { 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( return Promise.resolve(
// Given the Grafana datasource will always be present, this should always be defined. // Given the Grafana datasource will always be present, this should always be defined.
paneDatasource paneDatasource
@ -254,7 +249,7 @@ export function useStateSync(params: ExploreQueryParams) {
prevParams.current = params; prevParams.current = params;
isURLOutOfSync && initState.current === 'done' && sync(); isURLOutOfSync && initState.current === 'done' && sync();
}, [dispatch, panesState, exploreMixedDatasource, orgId, location, params]); }, [dispatch, panesState, orgId, location, params]);
} }
function getDefaultQuery(ds: DataSourceApi) { function getDefaultQuery(ds: DataSourceApi) {
@ -303,7 +298,7 @@ async function removeQueriesWithInvalidDatasource(queries: DataQuery[]) {
/** /**
* Returns the datasource that an explore pane should be using. * 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. * 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. * 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 rootDatasource the top-level datasource specified in the URL
* @param queries the queries in the pane * @param queries the queries in the pane
* @param orgId the orgId of the user * @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 * @returns the datasource UID that the pane should use, undefined if no suitable datasource is found
*/ */
async function getPaneDatasource( async function getPaneDatasource(
rootDatasource: DataSourceRef | string | null | undefined, rootDatasource: DataSourceRef | string | null | undefined,
queries: DataQuery[], queries: DataQuery[],
orgId: number, orgId: number
allowMixed: boolean
) { ) {
// 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) { if (rootDatasource) {
try { try {
const ds = await getDatasourceSrv().get(rootDatasource); return await getDatasourceSrv().get(rootDatasource);
if (!isMixedDatasource(ds) || allowMixed) {
return ds;
}
} catch (_) {} } 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 // Else we try to find a datasource in the queries, returning the first one that exists
const queriesWithDS = queries.filter((q) => q.datasource); const queriesWithDS = queries.filter((q) => q.datasource);
for (const query of queriesWithDS) { for (const query of queriesWithDS) {

View File

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

View File

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

View File

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