DataSource: show the uid in edit url, not the local id (#33818)

This commit is contained in:
Ryan McKinley
2021-05-08 09:13:26 -07:00
committed by GitHub
parent 48f4b87349
commit ccc0f7fc22
25 changed files with 110 additions and 42 deletions

View File

@@ -546,6 +546,7 @@ export interface DataSourceJsonData {
*/ */
export interface DataSourceSettings<T extends DataSourceJsonData = DataSourceJsonData, S = {}> { export interface DataSourceSettings<T extends DataSourceJsonData = DataSourceJsonData, S = {}> {
id: number; id: number;
uid: string;
orgId: number; orgId: number;
name: string; name: string;
typeLogoUrl: string; typeLogoUrl: string;

View File

@@ -8,6 +8,7 @@ const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
dataSourceConfig: { dataSourceConfig: {
id: 4, id: 4,
uid: 'x',
orgId: 1, orgId: 1,
name: 'gdev-influxdb', name: 'gdev-influxdb',
type: 'influxdb', type: 'influxdb',

View File

@@ -8,6 +8,7 @@ import mdx from './DataSourceHttpSettings.mdx';
const settingsMock: DataSourceSettings<any, any> = { const settingsMock: DataSourceSettings<any, any> = {
id: 4, id: 4,
orgId: 1, orgId: 1,
uid: 'x',
name: 'gdev-influxdb', name: 'gdev-influxdb',
type: 'influxdb', type: 'influxdb',
typeName: 'Influxdb', typeName: 'Influxdb',

View File

@@ -61,7 +61,7 @@ export class InputQueryEditor extends PureComponent<Props, State> {
render() { render() {
const { datasource, query } = this.props; const { datasource, query } = this.props;
const { id, name } = datasource; const { uid, name } = datasource;
const { text } = this.state; const { text } = this.state;
const selected = query.data ? options[0] : options[1]; const selected = query.data ? options[0] : options[1];
@@ -73,7 +73,7 @@ export class InputQueryEditor extends PureComponent<Props, State> {
{query.data ? ( {query.data ? (
<div style={{ alignSelf: 'center' }}>{describeDataFrame(query.data)}</div> <div style={{ alignSelf: 'center' }}>{describeDataFrame(query.data)}</div>
) : ( ) : (
<LinkButton variant="link" href={`datasources/edit/${id}/`}> <LinkButton variant="link" href={`datasources/edit/${uid}/`}>
{name}: {describeDataFrame(datasource.data)} &nbsp;&nbsp; {name}: {describeDataFrame(datasource.data)} &nbsp;&nbsp;
<Icon name="pen" /> <Icon name="pen" />
</LinkButton> </LinkButton>

View File

@@ -96,13 +96,13 @@ export const RuleList: FC = () => {
)} )}
{promReqeustErrors.map(({ dataSource, error }) => ( {promReqeustErrors.map(({ dataSource, error }) => (
<div key={dataSource.name}> <div key={dataSource.name}>
Failed to load rules state from <a href={`datasources/edit/${dataSource.id}`}>{dataSource.name}</a>:{' '} Failed to load rules state from <a href={`datasources/edit/${dataSource.uid}`}>{dataSource.name}</a>:{' '}
{error.message || 'Unknown error.'} {error.message || 'Unknown error.'}
</div> </div>
))} ))}
{rulerRequestErrors.map(({ dataSource, error }) => ( {rulerRequestErrors.map(({ dataSource, error }) => (
<div key={dataSource.name}> <div key={dataSource.name}>
Failed to load rules config from <a href={'datasources/edit/${dataSource.id}'}>{dataSource.name}</a>:{' '} Failed to load rules config from <a href={'datasources/edit/${dataSource.uid}'}>{dataSource.name}</a>:{' '}
{error.message || 'Unknown error.'} {error.message || 'Unknown error.'}
</div> </div>
))} ))}

View File

@@ -11,7 +11,7 @@ const setup = (propOverrides?: object) => {
navModel: {} as NavModel, navModel: {} as NavModel,
dashboards: [] as PluginDashboard[], dashboards: [] as PluginDashboard[],
dataSource: {} as DataSourceSettings, dataSource: {} as DataSourceSettings,
dataSourceId: 1, dataSourceId: 'x',
importDashboard: jest.fn(), importDashboard: jest.fn(),
loadDataSource: jest.fn(), loadDataSource: jest.fn(),
loadPluginDashboards: jest.fn(), loadPluginDashboards: jest.fn(),

View File

@@ -17,10 +17,10 @@ import { getDataSource } from './state/selectors';
import { PluginDashboard, StoreState } from 'app/types'; import { PluginDashboard, StoreState } from 'app/types';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
export interface OwnProps extends GrafanaRouteComponentProps<{ id: string }> {} export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {}
function mapStateToProps(state: StoreState, props: OwnProps) { function mapStateToProps(state: StoreState, props: OwnProps) {
const dataSourceId = parseInt(props.match.params.id, 10); const dataSourceId = props.match.params.uid;
return { return {
navModel: getNavModel(state.navIndex, `datasource-dashboards-${dataSourceId}`), navModel: getNavModel(state.navIndex, `datasource-dashboards-${dataSourceId}`),

View File

@@ -19,7 +19,7 @@ export const DataSourcesList: FC<Props> = ({ dataSources, layoutMode }) => {
{dataSources.map((dataSource, index) => { {dataSources.map((dataSource, index) => {
return ( return (
<li key={dataSource.id}> <li key={dataSource.id}>
<Card heading={dataSource.name} href={`datasources/edit/${dataSource.id}`}> <Card heading={dataSource.name} href={`datasources/edit/${dataSource.uid}`}>
<Card.Figure> <Card.Figure>
<img src={dataSource.typeLogoUrl} alt={dataSource.name} /> <img src={dataSource.typeLogoUrl} alt={dataSource.name} />
</Card.Figure> </Card.Figure>

View File

@@ -34,6 +34,7 @@ export const getMockDataSource = (): DataSourceSettings => {
withCredentials: false, withCredentials: false,
database: '', database: '',
id: 13, id: 13,
uid: 'x',
isDefault: false, isDefault: false,
jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' }, jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' },
name: 'gdev-cloudwatch', name: 'gdev-cloudwatch',

View File

@@ -3,6 +3,7 @@ import { DataSourceSettings } from '@grafana/data';
export function createDatasourceSettings<T>(jsonData: T): DataSourceSettings<T> { export function createDatasourceSettings<T>(jsonData: T): DataSourceSettings<T> {
return { return {
id: 0, id: 0,
uid: 'x',
orgId: 0, orgId: 0,
name: 'datasource-test', name: 'datasource-test',
typeLogoUrl: '', typeLogoUrl: '',

View File

@@ -23,7 +23,7 @@ const getProps = (): Props => ({
}, },
dataSource: getMockDataSource(), dataSource: getMockDataSource(),
dataSourceMeta: getMockPlugin(), dataSourceMeta: getMockPlugin(),
dataSourceId: 1, dataSourceId: 'x',
deleteDataSource: jest.fn(), deleteDataSource: jest.fn(),
loadDataSource: jest.fn(), loadDataSource: jest.fn(),
setDataSourceName, setDataSourceName,

View File

@@ -32,10 +32,10 @@ import { connect, ConnectedProps } from 'react-redux';
import { cleanUpAction } from 'app/core/actions/cleanUp'; import { cleanUpAction } from 'app/core/actions/cleanUp';
import { ShowConfirmModalEvent } from '../../../types/events'; import { ShowConfirmModalEvent } from '../../../types/events';
export interface OwnProps extends GrafanaRouteComponentProps<{ id: string }> {} export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {}
function mapStateToProps(state: StoreState, props: OwnProps) { function mapStateToProps(state: StoreState, props: OwnProps) {
const dataSourceId = props.match.params.id; const dataSourceId = props.match.params.uid;
const params = new URLSearchParams(props.location.search); const params = new URLSearchParams(props.location.search);
const dataSource = getDataSource(state.dataSources, dataSourceId); const dataSource = getDataSource(state.dataSources, dataSourceId);
const { plugin, loadError, testingStatus } = state.dataSourceSettings; const { plugin, loadError, testingStatus } = state.dataSourceSettings;
@@ -49,7 +49,7 @@ function mapStateToProps(state: StoreState, props: OwnProps) {
), ),
dataSource: getDataSource(state.dataSources, dataSourceId), dataSource: getDataSource(state.dataSources, dataSourceId),
dataSourceMeta: getDataSourceMeta(state.dataSources, dataSource.type), dataSourceMeta: getDataSourceMeta(state.dataSources, dataSource.type),
dataSourceId: parseInt(dataSourceId, 10), dataSourceId: dataSourceId,
page, page,
plugin, plugin,
loadError, loadError,

View File

@@ -72,23 +72,21 @@ describe('Find new name', () => {
}); });
describe('initDataSourceSettings', () => { describe('initDataSourceSettings', () => {
describe('when pageId is not a number', () => { describe('when pageId is missing', () => {
it('then initDataSourceSettingsFailed should be dispatched', async () => { it('then initDataSourceSettingsFailed should be dispatched', async () => {
const dispatchedActions = await thunkTester({}) const dispatchedActions = await thunkTester({}).givenThunk(initDataSourceSettings).whenThunkIsDispatched('');
.givenThunk(initDataSourceSettings)
.whenThunkIsDispatched('some page');
expect(dispatchedActions).toEqual([initDataSourceSettingsFailed(new Error('Invalid ID'))]); expect(dispatchedActions).toEqual([initDataSourceSettingsFailed(new Error('Invalid ID'))]);
}); });
}); });
describe('when pageId is a number', () => { describe('when pageId is a valid', () => {
it('then initDataSourceSettingsSucceeded should be dispatched', async () => { it('then initDataSourceSettingsSucceeded should be dispatched', async () => {
const thunkMock = (): ThunkResult<void> => (dispatch: ThunkDispatch, getState) => {}; const thunkMock = (): ThunkResult<void> => (dispatch: ThunkDispatch, getState) => {};
const dataSource = { type: 'app' }; const dataSource = { type: 'app' };
const dataSourceMeta = { id: 'some id' }; const dataSourceMeta = { id: 'some id' };
const dependencies: InitDataSourceSettingDependencies = { const dependencies: InitDataSourceSettingDependencies = {
loadDataSource: jest.fn(thunkMock), loadDataSource: jest.fn(thunkMock) as any,
getDataSource: jest.fn().mockReturnValue(dataSource), getDataSource: jest.fn().mockReturnValue(dataSource),
getDataSourceMeta: jest.fn().mockReturnValue(dataSourceMeta), getDataSourceMeta: jest.fn().mockReturnValue(dataSourceMeta),
importDataSourcePlugin: jest.fn().mockReturnValue({} as GenericDataSourcePlugin), importDataSourcePlugin: jest.fn().mockReturnValue({} as GenericDataSourcePlugin),

View File

@@ -3,7 +3,7 @@ import { getBackendSrv } from 'app/core/services/backend_srv';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { updateNavIndex } from 'app/core/actions'; import { updateNavIndex } from 'app/core/actions';
import { buildNavModel } from './navModel'; import { buildNavModel } from './navModel';
import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data'; import { DataSourcePluginMeta, DataSourceSettings, locationUtil } from '@grafana/data';
import { DataSourcePluginCategory, ThunkResult, ThunkDispatch } from 'app/types'; import { DataSourcePluginCategory, ThunkResult, ThunkDispatch } from 'app/types';
import { getPluginSettings } from 'app/features/plugins/PluginSettingsCache'; import { getPluginSettings } from 'app/features/plugins/PluginSettingsCache';
import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader'; import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader';
@@ -41,7 +41,7 @@ export interface TestDataSourceDependencies {
} }
export const initDataSourceSettings = ( export const initDataSourceSettings = (
pageId: number, pageId: string,
dependencies: InitDataSourceSettingDependencies = { dependencies: InitDataSourceSettingDependencies = {
loadDataSource, loadDataSource,
getDataSource, getDataSource,
@@ -49,14 +49,16 @@ export const initDataSourceSettings = (
importDataSourcePlugin, importDataSourcePlugin,
} }
): ThunkResult<void> => { ): ThunkResult<void> => {
return async (dispatch: ThunkDispatch, getState) => { return async (dispatch, getState) => {
if (isNaN(pageId)) { if (!pageId) {
dispatch(initDataSourceSettingsFailed(new Error('Invalid ID'))); dispatch(initDataSourceSettingsFailed(new Error('Invalid ID')));
return; return;
} }
try { try {
await dispatch(dependencies.loadDataSource(pageId)); await dispatch(dependencies.loadDataSource(pageId));
// have we already loaded the plugin then we can skip the steps below?
if (getState().dataSourceSettings.plugin) { if (getState().dataSourceSettings.plugin) {
return; return;
} }
@@ -111,9 +113,9 @@ export function loadDataSources(): ThunkResult<void> {
}; };
} }
export function loadDataSource(id: number): ThunkResult<void> { export function loadDataSource(uid: string): ThunkResult<void> {
return async (dispatch) => { return async (dispatch) => {
const dataSource = (await getBackendSrv().get(`/api/datasources/${id}`)) as DataSourceSettings; const dataSource = await getDataSourceUsingUidOrId(uid);
const pluginInfo = (await getPluginSettings(dataSource.type)) as DataSourcePluginMeta; const pluginInfo = (await getPluginSettings(dataSource.type)) as DataSourcePluginMeta;
const plugin = await importDataSourcePlugin(pluginInfo); const plugin = await importDataSourcePlugin(pluginInfo);
@@ -123,6 +125,50 @@ export function loadDataSource(id: number): ThunkResult<void> {
}; };
} }
/**
* Get data source by uid or id, if old id detected handles redirect
*/
async function getDataSourceUsingUidOrId(uid: string): Promise<DataSourceSettings> {
// Try first with uid api
try {
const byUid = await getBackendSrv()
.fetch<DataSourceSettings>({
method: 'GET',
url: `/api/datasources/uid/${uid}`,
showErrorAlert: false,
})
.toPromise();
if (byUid.ok) {
return byUid.data;
}
} catch (err) {
console.log('Failed to lookup data source by uid', err);
}
// try lookup by old db id
const id = parseInt(uid, 10);
if (!Number.isNaN(id)) {
const response = await getBackendSrv()
.fetch<DataSourceSettings>({
method: 'GET',
url: `/api/datasources/${id}`,
showErrorAlert: false,
})
.toPromise();
// Not ideal to do a full page reload here but so tricky to handle this otherwise
// We can update the location using react router, but need to fully reload the route as the nav model
// page index is not matching with the url in that case. And react router has no way to unmount remount a route
if (response.ok && response.data.id.toString() === uid) {
window.location.href = locationUtil.assureBaseUrl(`/datasources/edit/${response.data.uid}`);
return {} as DataSourceSettings; // avoids flashing an error
}
}
throw Error('Could not find data source');
}
export function addDataSource(plugin: DataSourcePluginMeta): ThunkResult<void> { export function addDataSource(plugin: DataSourcePluginMeta): ThunkResult<void> {
return async (dispatch, getStore) => { return async (dispatch, getStore) => {
await dispatch(loadDataSources()); await dispatch(loadDataSources());
@@ -141,7 +187,7 @@ export function addDataSource(plugin: DataSourcePluginMeta): ThunkResult<void> {
} }
const result = await getBackendSrv().post('/api/datasources', newInstance); const result = await getBackendSrv().post('/api/datasources', newInstance);
locationService.push(`/datasources/edit/${result.id}`); locationService.push(`/datasources/edit/${result.datasource.uid}`);
}; };
} }
@@ -156,9 +202,9 @@ export function loadDataSourcePlugins(): ThunkResult<void> {
export function updateDataSource(dataSource: DataSourceSettings): ThunkResult<void> { export function updateDataSource(dataSource: DataSourceSettings): ThunkResult<void> {
return async (dispatch) => { return async (dispatch) => {
await getBackendSrv().put(`/api/datasources/${dataSource.id}`, dataSource); await getBackendSrv().put(`/api/datasources/${dataSource.id}`, dataSource); // by UID not yet supported
await updateFrontendSettings(); await updateFrontendSettings();
return dispatch(loadDataSource(dataSource.id)); return dispatch(loadDataSource(dataSource.uid));
}; };
} }

View File

@@ -7,7 +7,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
const navModel: NavModelItem = { const navModel: NavModelItem = {
img: pluginMeta.info.logos.large, img: pluginMeta.info.logos.large,
id: 'datasource-' + dataSource.id, id: 'datasource-' + dataSource.uid,
subTitle: `Type: ${pluginMeta.name}`, subTitle: `Type: ${pluginMeta.name}`,
url: '', url: '',
text: dataSource.name, text: dataSource.name,
@@ -16,9 +16,9 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
{ {
active: false, active: false,
icon: 'sliders-v-alt', icon: 'sliders-v-alt',
id: `datasource-settings-${dataSource.id}`, id: `datasource-settings-${dataSource.uid}`,
text: 'Settings', text: 'Settings',
url: `datasources/edit/${dataSource.id}/`, url: `datasources/edit/${dataSource.uid}/`,
}, },
], ],
}; };
@@ -29,7 +29,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
active: false, active: false,
text: page.title, text: page.title,
icon: page.icon, icon: page.icon,
url: `datasources/edit/${dataSource.id}/?page=${page.id}`, url: `datasources/edit/${dataSource.uid}/?page=${page.id}`,
id: `datasource-page-${page.id}`, id: `datasource-page-${page.id}`,
}); });
} }
@@ -39,9 +39,9 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
navModel.children!.push({ navModel.children!.push({
active: false, active: false,
icon: 'apps', icon: 'apps',
id: `datasource-dashboards-${dataSource.id}`, id: `datasource-dashboards-${dataSource.uid}`,
text: 'Dashboards', text: 'Dashboards',
url: `datasources/edit/${dataSource.id}/dashboards`, url: `datasources/edit/${dataSource.uid}/dashboards`,
}); });
} }
@@ -49,25 +49,25 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
navModel.children!.push({ navModel.children!.push({
active: false, active: false,
icon: 'lock', icon: 'lock',
id: `datasource-permissions-${dataSource.id}`, id: `datasource-permissions-${dataSource.uid}`,
text: 'Permissions', text: 'Permissions',
url: `datasources/edit/${dataSource.id}/permissions`, url: `datasources/edit/${dataSource.uid}/permissions`,
}); });
navModel.children!.push({ navModel.children!.push({
active: false, active: false,
icon: 'info-circle', icon: 'info-circle',
id: `datasource-insights-${dataSource.id}`, id: `datasource-insights-${dataSource.uid}`,
text: 'Insights', text: 'Insights',
url: `datasources/edit/${dataSource.id}/insights`, url: `datasources/edit/${dataSource.uid}/insights`,
}); });
navModel.children!.push({ navModel.children!.push({
active: false, active: false,
icon: 'database', icon: 'database',
id: `datasource-cache-${dataSource.id}`, id: `datasource-cache-${dataSource.uid}`,
text: 'Cache', text: 'Cache',
url: `datasources/edit/${dataSource.id}/cache`, url: `datasources/edit/${dataSource.uid}/cache`,
}); });
} }
@@ -84,6 +84,7 @@ export function getDataSourceLoadingNav(pageName: string): NavModel {
withCredentials: false, withCredentials: false,
database: '', database: '',
id: 1, id: 1,
uid: 'x',
isDefault: false, isDefault: false,
jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' }, jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' },
name: 'Loading', name: 'Loading',

View File

@@ -18,7 +18,7 @@ export const getDataSourcePlugins = (state: DataSourcesState) => {
}; };
export const getDataSource = (state: DataSourcesState, dataSourceId: UrlQueryValue): DataSourceSettings => { export const getDataSource = (state: DataSourcesState, dataSourceId: UrlQueryValue): DataSourceSettings => {
if (state.dataSource.id === parseInt(dataSourceId as string, 10)) { if (state.dataSource.uid === dataSourceId) {
return state.dataSource; return state.dataSource;
} }
return {} as DataSourceSettings; return {} as DataSourceSettings;

View File

@@ -22,6 +22,7 @@ const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
options: { options: {
id: 1, id: 1,
uid: 'z',
orgId: 1, orgId: 1,
typeLogoUrl: '', typeLogoUrl: '',
name: 'CloudWatch', name: 'CloudWatch',

View File

@@ -37,6 +37,7 @@ exports[`Render should disable access key id field 1`] = `
"type": "cloudwatch", "type": "cloudwatch",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Cloudwatch", "typeName": "Cloudwatch",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"withCredentials": false, "withCredentials": false,
@@ -101,6 +102,7 @@ exports[`Render should render component 1`] = `
"type": "cloudwatch", "type": "cloudwatch",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Cloudwatch", "typeName": "Cloudwatch",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"withCredentials": false, "withCredentials": false,
@@ -165,6 +167,7 @@ exports[`Render should show access key and secret access key fields 1`] = `
"type": "cloudwatch", "type": "cloudwatch",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Cloudwatch", "typeName": "Cloudwatch",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"withCredentials": false, "withCredentials": false,
@@ -229,6 +232,7 @@ exports[`Render should show arn role field 1`] = `
"type": "cloudwatch", "type": "cloudwatch",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Cloudwatch", "typeName": "Cloudwatch",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"withCredentials": false, "withCredentials": false,
@@ -293,6 +297,7 @@ exports[`Render should show credentials profile name field 1`] = `
"type": "cloudwatch", "type": "cloudwatch",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Cloudwatch", "typeName": "Cloudwatch",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"withCredentials": false, "withCredentials": false,

View File

@@ -6,6 +6,7 @@ const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
options: { options: {
id: 21, id: 21,
uid: 'x',
orgId: 1, orgId: 1,
name: 'Azure Monitor-10-10', name: 'Azure Monitor-10-10',
type: 'grafana-azure-monitor-datasource', type: 'grafana-azure-monitor-datasource',

View File

@@ -6,6 +6,7 @@ const setup = () => {
const props: Props = { const props: Props = {
options: { options: {
id: 21, id: 21,
uid: 'y',
orgId: 1, orgId: 1,
name: 'Azure Monitor-10-10', name: 'Azure Monitor-10-10',
type: 'grafana-azure-monitor-datasource', type: 'grafana-azure-monitor-datasource',

View File

@@ -6,6 +6,7 @@ const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
options: { options: {
id: 21, id: 21,
uid: 'x',
orgId: 1, orgId: 1,
name: 'Azure Monitor-10-10', name: 'Azure Monitor-10-10',
type: 'grafana-azure-monitor-datasource', type: 'grafana-azure-monitor-datasource',

View File

@@ -31,6 +31,7 @@ exports[`Render should render component 1`] = `
"type": "grafana-azure-monitor-datasource", "type": "grafana-azure-monitor-datasource",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Azure", "typeName": "Azure",
"uid": "y",
"url": "", "url": "",
"user": "", "user": "",
"version": 1, "version": 1,
@@ -69,6 +70,7 @@ exports[`Render should render component 1`] = `
"type": "grafana-azure-monitor-datasource", "type": "grafana-azure-monitor-datasource",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Azure", "typeName": "Azure",
"uid": "y",
"url": "/api/datasources/proxy/21", "url": "/api/datasources/proxy/21",
"user": "", "user": "",
"version": 1, "version": 1,
@@ -109,6 +111,7 @@ exports[`Render should render component 1`] = `
"type": "grafana-azure-monitor-datasource", "type": "grafana-azure-monitor-datasource",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Azure", "typeName": "Azure",
"uid": "y",
"url": "", "url": "",
"user": "", "user": "",
"version": 1, "version": 1,
@@ -145,6 +148,7 @@ exports[`Render should render component 1`] = `
"type": "grafana-azure-monitor-datasource", "type": "grafana-azure-monitor-datasource",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Azure", "typeName": "Azure",
"uid": "y",
"url": "", "url": "",
"user": "", "user": "",
"version": 1, "version": 1,

View File

@@ -6,6 +6,7 @@ const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
options: { options: {
id: 21, id: 21,
uid: 'z',
orgId: 1, orgId: 1,
name: 'InfluxDB-3', name: 'InfluxDB-3',
type: 'influxdb', type: 'influxdb',

View File

@@ -93,6 +93,7 @@ exports[`Render should disable basic auth password input 1`] = `
"type": "influxdb", "type": "influxdb",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Influx", "typeName": "Influx",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"version": 1, "version": 1,
@@ -389,6 +390,7 @@ exports[`Render should hide basic auth fields when switch off 1`] = `
"type": "influxdb", "type": "influxdb",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Influx", "typeName": "Influx",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"version": 1, "version": 1,
@@ -685,6 +687,7 @@ exports[`Render should hide white listed cookies input when browser access chose
"type": "influxdb", "type": "influxdb",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Influx", "typeName": "Influx",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"version": 1, "version": 1,
@@ -981,6 +984,7 @@ exports[`Render should render component 1`] = `
"type": "influxdb", "type": "influxdb",
"typeLogoUrl": "", "typeLogoUrl": "",
"typeName": "Influx", "typeName": "Influx",
"uid": "z",
"url": "", "url": "",
"user": "", "user": "",
"version": 1, "version": 1,

View File

@@ -94,7 +94,7 @@ export function getAppRoutes(): RouteDescriptor[] {
), ),
}, },
{ {
path: '/datasources/edit/:id/', path: '/datasources/edit/:uid/',
component: SafeDynamicImport( component: SafeDynamicImport(
() => () =>
import( import(
@@ -103,7 +103,7 @@ export function getAppRoutes(): RouteDescriptor[] {
), ),
}, },
{ {
path: '/datasources/edit/:id/dashboards', path: '/datasources/edit/:uid/dashboards',
component: SafeDynamicImport( component: SafeDynamicImport(
() => import(/* webpackChunkName: "DataSourceDashboards"*/ 'app/features/datasources/DataSourceDashboards') () => import(/* webpackChunkName: "DataSourceDashboards"*/ 'app/features/datasources/DataSourceDashboards')
), ),