Explore: Change datasourceName usage to datasourceUID or datasourceRef where appropriate (#52479)

* First pass at using datasource UID when appropriate

* Fix tests

* be more lenient with lookup to accomodate different URLs

* Make test setup get mock work like real datasource get

* Fix the typing issue and remaining tests

* Fix PR feedback
This commit is contained in:
Kristina 2022-07-25 13:05:57 -05:00 committed by GitHub
parent cb35729553
commit 0e13ee345d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 71 additions and 58 deletions

View File

@ -1,4 +1,4 @@
Right now Tempo is before stable release so this uses :latest tag which means there can be changes depending on when
you pull the image.
For adding some traces easily you can run Loki block and enable tracing (see ../loki/README.md)
For adding some traces easily you can run Loki block and enable tracing (see ../loki-promtail/README.md)

View File

@ -606,6 +606,7 @@ describe('getLinksSupplier', () => {
getTimeRangeForUrl: (() => {}) as any,
});
const datasourceUid = '1234';
const f0 = new MutableDataFrame({
name: 'A',
fields: [
@ -619,7 +620,7 @@ describe('getLinksSupplier', () => {
url: '',
title: '',
internal: {
datasourceUid: '0',
datasourceUid: datasourceUid,
datasourceName: 'testDS',
query: '12345',
},
@ -640,12 +641,12 @@ describe('getLinksSupplier', () => {
);
const links = supplier({ valueRowIndex: 0 });
const encodeURIParams = `{"datasource":"${datasourceUid}","queries":["12345"],"panelsState":{}}`;
expect(links.length).toBe(1);
expect(links[0]).toEqual(
expect.objectContaining({
title: 'testDS',
href: `/explore?left=${encodeURIComponent('{"datasource":"testDS","queries":["12345"],"panelsState":{}}')}`,
href: `/explore?left=${encodeURIComponent(encodeURIParams)}`,
onClick: undefined,
})
);

View File

@ -44,7 +44,7 @@ export interface DataLink<T extends DataQuery = any> {
export interface InternalDataLink<T extends DataQuery = any> {
query: T;
datasourceUid: string;
datasourceName: string;
datasourceName: string; // used as a title if `DataLink.title` is empty
panelsState?: ExplorePanelsState;
}

View File

@ -33,7 +33,7 @@ describe('mapInternalLinkToExplore', () => {
expect.objectContaining({
title: 'dsName',
href: `/explore?left=${encodeURIComponent(
'{"datasource":"dsName","queries":[{"query":"12344"}],"panelsState":{}}'
'{"datasource":"uid","queries":[{"query":"12344"}],"panelsState":{}}'
)}`,
onClick: undefined,
})
@ -76,7 +76,7 @@ describe('mapInternalLinkToExplore', () => {
expect.objectContaining({
title: 'dsName',
href: `/explore?left=${encodeURIComponent(
'{"datasource":"dsName","queries":[{"query":"12344"}],"panelsState":{"trace":{"spanId":"abcdef"}}}'
'{"datasource":"uid","queries":[{"query":"12344"}],"panelsState":{"trace":{"spanId":"abcdef"}}}'
)}`,
onClick: undefined,
})

View File

@ -51,7 +51,7 @@ export function mapInternalLinkToExplore(options: LinkToExploreOptions): LinkMod
title: replaceVariables(title, scopedVars),
// In this case this is meant to be internal link (opens split view by default) the href will also points
// to explore but this way you can open it in new tab.
href: generateInternalHref(internalLink.datasourceName, interpolatedQuery, range, interpolatedPanelsState),
href: generateInternalHref(internalLink.datasourceUid, interpolatedQuery, range, interpolatedPanelsState),
onClick: onClickFn
? () => {
onClickFn({
@ -71,7 +71,7 @@ export function mapInternalLinkToExplore(options: LinkToExploreOptions): LinkMod
* Generates href for internal derived field link.
*/
function generateInternalHref<T extends DataQuery = any>(
datasourceName: string,
datasourceUid: string,
query: T,
range: TimeRange,
panelsState?: ExplorePanelsState
@ -80,7 +80,7 @@ function generateInternalHref<T extends DataQuery = any>(
`/explore?left=${encodeURIComponent(
serializeStateToUrlParam({
range: range.raw,
datasource: datasourceName,
datasource: datasourceUid,
queries: [query],
panelsState: panelsState,
})

View File

@ -146,7 +146,7 @@ class UnConnectedExploreToolbar extends PureComponent<Props> {
<DataSourcePicker
key={`${exploreId}-ds-picker`}
onChange={this.onChangeDatasource}
current={this.props.datasourceName}
current={this.props.datasourceRef}
hideTextValue={showSmallDataSourcePicker}
width={showSmallDataSourcePicker ? 8 : undefined}
/>
@ -233,7 +233,7 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps) => {
return {
datasourceMissing,
datasourceName: datasourceInstance?.name,
datasourceRef: datasourceInstance?.getRef(),
datasourceType: datasourceInstance?.type,
loading,
range,

View File

@ -164,21 +164,21 @@ export function RichHistoryCard(props: Props) {
useEffect(() => {
const getQueryDsInstance = async () => {
const ds = await getDataSourceSrv().get(query.datasourceName);
const ds = await getDataSourceSrv().get(query.datasourceUid);
setQueryDsInstance(ds);
};
getQueryDsInstance();
}, [query.datasourceName]);
}, [query.datasourceUid]);
const theme = useTheme();
const styles = getStyles(theme, isRemoved);
const onRunQuery = async () => {
const queriesToRun = query.queries;
const differentDataSource = query.datasourceName !== datasourceInstance?.name;
const differentDataSource = query.datasourceUid !== datasourceInstance?.uid;
if (differentDataSource) {
await changeDatasource(exploreId, query.datasourceName, { importQueries: true });
await changeDatasource(exploreId, query.datasourceUid, { importQueries: true });
setQueries(exploreId, queriesToRun);
} else {
setQueries(exploreId, queriesToRun);
@ -329,7 +329,7 @@ export function RichHistoryCard(props: Props) {
{!activeUpdateComment && (
<div className={styles.runButton}>
<Button variant="secondary" onClick={onRunQuery} disabled={isRemoved}>
{datasourceInstance?.name === query.datasourceName ? 'Run query' : 'Switch data source and run query'}
{datasourceInstance?.uid === query.datasourceUid ? 'Run query' : 'Switch data source and run query'}
</Button>
</div>
)}

View File

@ -243,7 +243,7 @@ export function RichHistoryQueriesTab(props: Props) {
</span>
</div>
{mappedQueriesToHeadings[heading].map((q: RichHistoryQuery) => {
const idx = listOfDatasources.findIndex((d) => d.name === q.datasourceName);
const idx = listOfDatasources.findIndex((d) => d.uid === q.datasourceUid);
return (
<RichHistoryCard
query={q}

View File

@ -154,7 +154,7 @@ export function RichHistoryStarredTab(props: Props) {
{loading && <span>Loading results...</span>}
{!loading &&
queries.map((q) => {
const idx = listOfDatasources.findIndex((d) => d.name === q.datasourceName);
const idx = listOfDatasources.findIndex((d) => d.uid === q.datasourceUid);
return (
<RichHistoryCard
query={q}

View File

@ -24,7 +24,7 @@ describe('createSpanLinkFactory', () => {
beforeAll(() => {
setDataSourceSrv({
getInstanceSettings(uid: string): DataSourceInstanceSettings | undefined {
return { uid: 'loki1', name: 'loki1', type: 'loki' } as any;
return { uid: 'loki1_uid', name: 'loki1', type: 'loki' } as any;
},
} as any);
@ -40,7 +40,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1_uid","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}],"panelsState":{}}'
)}`
);
});
@ -65,7 +65,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\"}","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1_uid","queries":[{"expr":"{ip=\\"192.168.0.1\\"}","refId":""}],"panelsState":{}}'
)}`
);
});
@ -90,7 +90,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\", host=\\"host\\"}","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1_uid","queries":[{"expr":"{ip=\\"192.168.0.1\\", host=\\"host\\"}","refId":""}],"panelsState":{}}'
)}`
);
});
@ -116,7 +116,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:01:00.000Z","to":"2020-10-14T01:01:01.000Z"},"datasource":"loki1","queries":[{"expr":"{hostname=\\"hostname1\\"}","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:01:00.000Z","to":"2020-10-14T01:01:01.000Z"},"datasource":"loki1_uid","queries":[{"expr":"{hostname=\\"hostname1\\"}","refId":""}],"panelsState":{}}'
)}`
);
});
@ -133,7 +133,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"} |=\\"7946b05c2e2e4e5a\\" |=\\"6605c7b08e715d6c\\"","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1_uid","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"} |=\\"7946b05c2e2e4e5a\\" |=\\"6605c7b08e715d6c\\"","refId":""}],"panelsState":{}}'
)}`
);
});
@ -186,7 +186,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{service=\\"serviceName\\", pod=\\"podName\\"}","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1_uid","queries":[{"expr":"{service=\\"serviceName\\", pod=\\"podName\\"}","refId":""}],"panelsState":{}}'
)}`
);
});
@ -216,7 +216,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{service.name=\\"serviceName\\", pod=\\"podName\\"}","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1_uid","queries":[{"expr":"{service.name=\\"serviceName\\", pod=\\"podName\\"}","refId":""}],"panelsState":{}}'
)}`
);
});
@ -263,8 +263,8 @@ describe('createSpanLinkFactory', () => {
const linkDef = links?.logLinks?.[0];
expect(linkDef).toBeDefined();
expect(linkDef!.href).toContain(`${encodeURIComponent('datasource":"Splunk 8","queries":[{"query"')}`);
expect(linkDef!.href).not.toContain(`${encodeURIComponent('datasource":"Splunk 8","queries":[{"expr"')}`);
expect(linkDef!.href).toContain(`${encodeURIComponent('datasource":"splunkUID","queries":[{"query"')}`);
expect(linkDef!.href).not.toContain(`${encodeURIComponent('datasource":"splunkUID","queries":[{"expr"')}`);
});
it('automatically timeshifts the timerange by one second in a splunk query', () => {
@ -297,7 +297,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"Splunk 8","queries":[{"query":"cluster=\\"cluster1\\" hostname=\\"hostname1\\" \\"7946b05c2e2e4e5a\\" \\"6605c7b08e715d6c\\"","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"splunkUID","queries":[{"query":"cluster=\\"cluster1\\" hostname=\\"hostname1\\" \\"7946b05c2e2e4e5a\\" \\"6605c7b08e715d6c\\"","refId":""}],"panelsState":{}}'
)}`
);
});
@ -320,7 +320,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"Splunk 8","queries":[{"query":"ip=\\"192.168.0.1\\"","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"splunkUID","queries":[{"query":"ip=\\"192.168.0.1\\"","refId":""}],"panelsState":{}}'
)}`
);
});
@ -346,7 +346,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"Splunk 8","queries":[{"query":"hostname=\\"hostname1\\" ip=\\"192.168.0.1\\"","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"splunkUID","queries":[{"query":"hostname=\\"hostname1\\" ip=\\"192.168.0.1\\"","refId":""}],"panelsState":{}}'
)}`
);
});
@ -376,7 +376,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"Splunk 8","queries":[{"query":"service=\\"serviceName\\" pod=\\"podName\\"","refId":""}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"splunkUID","queries":[{"query":"service=\\"serviceName\\" pod=\\"podName\\"","refId":""}],"panelsState":{}}'
)}`
);
});
@ -386,7 +386,7 @@ describe('createSpanLinkFactory', () => {
beforeAll(() => {
setDataSourceSrv({
getInstanceSettings(uid: string): DataSourceInstanceSettings | undefined {
return { uid: 'prom1', name: 'prom1', type: 'prometheus' } as any;
return { uid: 'prom1Uid', name: 'prom1', type: 'prometheus' } as any;
},
} as any);
@ -399,7 +399,7 @@ describe('createSpanLinkFactory', () => {
const createLink = createSpanLinkFactory({
splitOpenFn,
traceToMetricsOptions: {
datasourceUid: 'prom1',
datasourceUid: 'prom1Uid',
queries: [{ query: 'customQuery' }],
},
});
@ -411,7 +411,7 @@ describe('createSpanLinkFactory', () => {
expect(linkDef).toBeDefined();
expect(linkDef!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1","queries":[{"expr":"customQuery","refId":"A"}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1Uid","queries":[{"expr":"customQuery","refId":"A"}],"panelsState":{}}'
)}`
);
});
@ -435,7 +435,7 @@ describe('createSpanLinkFactory', () => {
const createLink = createSpanLinkFactory({
splitOpenFn,
traceToMetricsOptions: {
datasourceUid: 'prom1',
datasourceUid: 'prom1Uid',
queries: [
{ name: 'Named Query', query: 'customQuery' },
{ name: 'defaultQuery', query: '' },
@ -454,7 +454,7 @@ describe('createSpanLinkFactory', () => {
expect(namedLink!.title).toBe('Named Query');
expect(namedLink!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1","queries":[{"expr":"customQuery","refId":"A"}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1Uid","queries":[{"expr":"customQuery","refId":"A"}],"panelsState":{}}'
)}`
);
@ -463,7 +463,7 @@ describe('createSpanLinkFactory', () => {
expect(defaultLink!.title).toBe('defaultQuery');
expect(defaultLink!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1","queries":[{"expr":"histogram_quantile(0.5, sum(rate(tempo_spanmetrics_latency_bucket{operation=\\"operation\\"}[5m])) by (le))","refId":"A"}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1Uid","queries":[{"expr":"histogram_quantile(0.5, sum(rate(tempo_spanmetrics_latency_bucket{operation=\\"operation\\"}[5m])) by (le))","refId":"A"}],"panelsState":{}}'
)}`
);
@ -472,7 +472,7 @@ describe('createSpanLinkFactory', () => {
expect(unnamedQuery!.title).toBeUndefined();
expect(unnamedQuery!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1","queries":[{"expr":"no_name_here","refId":"A"}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1Uid","queries":[{"expr":"no_name_here","refId":"A"}],"panelsState":{}}'
)}`
);
});
@ -483,7 +483,7 @@ describe('createSpanLinkFactory', () => {
const createLink = createSpanLinkFactory({
splitOpenFn,
traceToMetricsOptions: {
datasourceUid: 'prom1',
datasourceUid: 'prom1Uid',
queries: [{ name: 'Named Query', query: 'metric{$__tags}[5m]' }],
tags: [
{ key: 'job', value: '' },
@ -507,7 +507,7 @@ describe('createSpanLinkFactory', () => {
expect(links).toBeDefined();
expect(links!.metricLinks![0]!.href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1","queries":[{"expr":"metric{job=\\"tns/app\\", pod=\\"sample-pod\\"}[5m]","refId":"A"}],"panelsState":{}}'
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"prom1Uid","queries":[{"expr":"metric{job=\\"tns/app\\", pod=\\"sample-pod\\"}[5m]","refId":"A"}],"panelsState":{}}'
)}`
);
});

View File

@ -58,9 +58,14 @@ 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(name?: string | null, scopedVars?: ScopedVars): Promise<DataSourceApi> {
get(datasource?: string | DataSourceRef | null, scopedVars?: ScopedVars): Promise<DataSourceApi> {
const datasourceStr = typeof datasource === 'string';
return Promise.resolve(
(name ? dsSettings.find((d) => d.api.name === name || d.api.uid === name) : dsSettings[0])!.api
(datasource
? dsSettings.find((d) =>
datasourceStr ? d.api.name === datasource || d.api.uid === datasource : d.api.uid === datasource?.uid
)
: dsSettings[0])!.api
);
},
} as any);

View File

@ -41,7 +41,7 @@ export function changeDatasource(
): ThunkResult<void> {
return async (dispatch, getState) => {
const orgId = getState().user.orgId;
const { history, instance } = await loadAndInitDatasource(orgId, datasourceUid);
const { history, instance } = await loadAndInitDatasource(orgId, { uid: datasourceUid });
const currentDataSourceInstance = getState().explore[exploreId]!.datasourceInstance;
dispatch(

View File

@ -11,6 +11,7 @@ import {
DataSourceApi,
ExplorePanelsState,
PreferredVisualisationType,
DataSourceRef,
} from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import { keybindingSrv } from 'app/core/services/keybindingSrv';
@ -135,10 +136,14 @@ export function changeGraphStyle(exploreId: ExploreId, graphStyle: ExploreGraphS
/**
* Initialize Explore state with state from the URL and the React component.
* Call this only on components for with the Explore state has not been initialized.
*
* The `datasource` param will be passed to the datasource service `get` function
* and can be either a string that is the name or uid, or a datasourceRef
* This is to maximize compatability with how datasources are accessed from the URL param.
*/
export function initializeExplore(
exploreId: ExploreId,
datasourceNameOrUid: string,
datasource: DataSourceRef | string,
queries: DataQuery[],
range: TimeRange,
containerWidth: number,
@ -152,7 +157,7 @@ export function initializeExplore(
if (exploreDatasources.length >= 1) {
const orgId = getState().user.orgId;
const loadResult = await loadAndInitDatasource(orgId, datasourceNameOrUid);
const loadResult = await loadAndInitDatasource(orgId, datasource);
instance = loadResult.instance;
history = loadResult.history;
}
@ -201,6 +206,7 @@ export function refreshExplore(exploreId: ExploreId, newUrlQuery: string): Thunk
const { containerWidth, eventBridge } = itemState;
// datasource will either be name or UID here
const { datasource, queries, range: urlRange, panelsState } = newUrlState;
const refreshQueries: DataQuery[] = [];

View File

@ -2,7 +2,7 @@ import { createAction } from '@reduxjs/toolkit';
import { AnyAction } from 'redux';
import { ExploreUrlState, serializeStateToUrlParam, SplitOpen, UrlQueryMap } from '@grafana/data';
import { DataSourceSrv, getDataSourceSrv, locationService } from '@grafana/runtime';
import { DataSourceSrv, locationService } from '@grafana/runtime';
import { GetExploreUrlArguments, stopQueryState } from 'app/core/utils/explore';
import { PanelModel } from 'app/features/dashboard/state';
import { ExploreId, ExploreItemState, ExploreState } from 'app/types/explore';
@ -108,9 +108,8 @@ export const splitOpen: SplitOpen = (options): ThunkResult<void> => {
let rightUrlState: ExploreUrlState = leftUrlState;
if (options) {
const datasourceName = getDataSourceSrv().getInstanceSettings(options.datasourceUid)?.name || '';
rightUrlState = {
datasource: datasourceName,
datasource: options.datasourceUid,
queries: [options.query],
range: options.range || leftState.range,
panelsState: options.panelsState,

View File

@ -24,10 +24,10 @@ describe('loadAndInitDatasource', () => {
dataSourceMock.get.mockRejectedValueOnce(new Error('Datasource not found'));
dataSourceMock.get.mockResolvedValue(DEFAULT_DATASOURCE);
const { instance } = await loadAndInitDatasource(1, 'Unknown');
const { instance } = await loadAndInitDatasource(1, { uid: 'Unknown' });
expect(dataSourceMock.get).toBeCalledTimes(2);
expect(dataSourceMock.get).toBeCalledWith('Unknown');
expect(dataSourceMock.get).toBeCalledWith({ uid: 'Unknown' });
expect(dataSourceMock.get).toBeCalledWith();
expect(instance).toMatchObject(DEFAULT_DATASOURCE);
expect(store.set).toBeCalledWith(lastUsedDatasourceKeyForOrgId(1), DEFAULT_DATASOURCE.uid);
@ -36,10 +36,10 @@ describe('loadAndInitDatasource', () => {
it('saves last loaded data source uid', async () => {
dataSourceMock.get.mockResolvedValue(TEST_DATASOURCE);
const { instance } = await loadAndInitDatasource(1, 'Test');
const { instance } = await loadAndInitDatasource(1, { uid: 'Test' });
expect(dataSourceMock.get).toBeCalledTimes(1);
expect(dataSourceMock.get).toBeCalledWith('Test');
expect(dataSourceMock.get).toBeCalledWith({ uid: 'Test' });
expect(instance).toMatchObject(TEST_DATASOURCE);
expect(store.set).toBeCalledWith(lastUsedDatasourceKeyForOrgId(1), TEST_DATASOURCE.uid);
});

View File

@ -3,6 +3,7 @@ import { isEmpty, isObject, mapValues, omitBy } from 'lodash';
import {
AbsoluteTimeRange,
DataSourceApi,
DataSourceRef,
EventBusExtended,
ExploreUrlState,
getDefaultTimeRange,
@ -86,11 +87,12 @@ export const createEmptyQueryResponse = (): ExplorePanelData => ({
export async function loadAndInitDatasource(
orgId: number,
datasourceUid?: string
datasource: DataSourceRef | string
): Promise<{ history: HistoryItem[]; instance: DataSourceApi }> {
let instance;
try {
instance = await getDatasourceSrv().get(datasourceUid);
// let datasource be a ref if we have the info, otherwise a name or uid will do for lookup
instance = await getDatasourceSrv().get(datasource);
} catch (error) {
// Falling back to the default data source in case the provided data source was not found.
// It may happen if last used data source or the data source provided in the URL has been
@ -129,7 +131,7 @@ export function getUrlStateFromPaneState(pane: ExploreItemState): ExploreUrlStat
return {
// datasourceInstance should not be undefined anymore here but in case there is some path for it to be undefined
// lets just fallback instead of crashing.
datasource: pane.datasourceInstance?.name || '',
datasource: pane.datasourceInstance?.uid || '',
queries: pane.queries.map(clearQueryKeys),
range: toRawTimeRange(pane.range),
// don't include panelsState in the url unless a piece of state is actually set

View File

@ -73,7 +73,7 @@ describe('getFieldLinksForExplore', () => {
expect(links[0].href).toBe(
`/explore?left=${encodeURIComponent(
'{"range":{"from":"now-1h","to":"now"},"datasource":"test_ds","queries":[{"query":"query_1"}],"panelsState":{"trace":{"spanId":"abcdef"}}}'
'{"range":{"from":"now-1h","to":"now"},"datasource":"uid_1","queries":[{"query":"query_1"}],"panelsState":{"trace":{"spanId":"abcdef"}}}'
)}`
);
expect(links[0].title).toBe('test_ds');