mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Datasource Plugins: Allow tracking for configuration usage (#72650)
Datasource Plugins: Allow tracking for configuration usage
This commit is contained in:
parent
867162b64a
commit
6ac3348021
@ -54,7 +54,13 @@ export type DashboardLoadedEventPayload<T> = {
|
||||
export class DashboardLoadedEvent<T> extends BusEventWithPayload<DashboardLoadedEventPayload<T>> {
|
||||
static type = 'dashboard-loaded';
|
||||
}
|
||||
|
||||
export class DataSourceUpdatedSuccessfully extends BusEventBase {
|
||||
static type = 'datasource-updated-successfully';
|
||||
}
|
||||
export class DataSourceTestSucceeded extends BusEventBase {
|
||||
static type = 'datasource-test-succeeded';
|
||||
}
|
||||
|
||||
export class DataSourceTestFailed extends BusEventBase {
|
||||
static type = 'datasource-test-failed';
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { thunkTester } from 'test/core/thunk/thunkTester';
|
||||
|
||||
import { AppPluginMeta, DataSourceSettings, PluginMetaInfo, PluginType } from '@grafana/data';
|
||||
import { FetchError } from '@grafana/runtime';
|
||||
import { appEvents } from 'app/core/core';
|
||||
import { ThunkResult, ThunkDispatch } from 'app/types';
|
||||
|
||||
import { getMockDataSource } from '../__mocks__';
|
||||
@ -30,7 +31,12 @@ import {
|
||||
|
||||
jest.mock('../api');
|
||||
jest.mock('app/core/services/backend_srv');
|
||||
jest.mock('app/core/core');
|
||||
jest.mock('app/core/core', () => ({
|
||||
...jest.requireActual('app/core/core'),
|
||||
appEvents: {
|
||||
publish: jest.fn(),
|
||||
},
|
||||
}));
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getDataSourceSrv: jest.fn().mockReturnValue({ reload: jest.fn() }),
|
||||
@ -331,6 +337,39 @@ describe('testDataSource', () => {
|
||||
const dispatchedActions = await failDataSourceTest(error);
|
||||
expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceFailed(result)]);
|
||||
});
|
||||
|
||||
it('publishes an app event when the test succeeds', async () => {
|
||||
const dependencies: TestDataSourceDependencies = {
|
||||
getDatasourceSrv: () => ({
|
||||
get: jest.fn().mockReturnValue({
|
||||
testDatasource: jest.fn().mockReturnValue({
|
||||
status: 'success',
|
||||
message: '',
|
||||
}),
|
||||
type: 'cloudwatch',
|
||||
uid: 'CW1234',
|
||||
}),
|
||||
}),
|
||||
getBackendSrv: getBackendSrvMock,
|
||||
};
|
||||
await thunkTester({})
|
||||
.givenThunk(testDataSource)
|
||||
.whenThunkIsDispatched('CloudWatch', DATASOURCES_ROUTES.Edit, dependencies);
|
||||
expect(appEvents.publish).toHaveBeenCalledWith({ type: 'datasource-test-succeeded' });
|
||||
});
|
||||
|
||||
it('publishes an app event when the test fails', async () => {
|
||||
const error: FetchError = {
|
||||
config: {
|
||||
url: '',
|
||||
},
|
||||
data: {},
|
||||
statusText: 'Bad Request',
|
||||
status: 400,
|
||||
};
|
||||
await failDataSourceTest(error);
|
||||
expect(appEvents.publish).toHaveBeenCalledWith({ type: 'datasource-test-failed' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,15 +1,22 @@
|
||||
import { DataSourcePluginMeta, DataSourceSettings, locationUtil, TestDataSourceResponse } from '@grafana/data';
|
||||
import {
|
||||
DataSourcePluginMeta,
|
||||
DataSourceSettings,
|
||||
locationUtil,
|
||||
TestDataSourceResponse,
|
||||
DataSourceTestSucceeded,
|
||||
DataSourceTestFailed,
|
||||
} from '@grafana/data';
|
||||
import {
|
||||
config,
|
||||
DataSourceSrv,
|
||||
DataSourceWithBackend,
|
||||
getDataSourceSrv,
|
||||
HealthCheckError,
|
||||
HealthCheckResultDetails,
|
||||
isFetchError,
|
||||
locationService,
|
||||
} from '@grafana/runtime';
|
||||
import { updateNavIndex } from 'app/core/actions';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { appEvents, contextSrv } from 'app/core/core';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { ROUTES as CONNECTIONS_ROUTES } from 'app/features/connections/constants';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
@ -53,7 +60,7 @@ export interface InitDataSourceSettingDependencies {
|
||||
}
|
||||
|
||||
export interface TestDataSourceDependencies {
|
||||
getDatasourceSrv: typeof getDataSourceSrv;
|
||||
getDatasourceSrv: () => Pick<DataSourceSrv, 'get'>;
|
||||
getBackendSrv: typeof getBackendSrv;
|
||||
}
|
||||
|
||||
@ -150,6 +157,7 @@ export const testDataSource = (
|
||||
success: true,
|
||||
path: editLink,
|
||||
});
|
||||
appEvents.publish(new DataSourceTestSucceeded());
|
||||
} catch (err) {
|
||||
const formattedError = parseHealthCheckError(err);
|
||||
|
||||
@ -161,6 +169,7 @@ export const testDataSource = (
|
||||
success: false,
|
||||
path: editLink,
|
||||
});
|
||||
appEvents.publish(new DataSourceTestFailed());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { AwsAuthType } from '@grafana/aws-sdk';
|
||||
import { PluginContextProvider, PluginMeta, PluginMetaInfo, PluginType } from '@grafana/data';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
import { CloudWatchSettings, setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
|
||||
@ -25,12 +26,16 @@ jest.mock('./XrayLinkConfig', () => ({
|
||||
|
||||
const putMock = jest.fn();
|
||||
const getMock = jest.fn();
|
||||
const mockAppEvents = {
|
||||
subscribe: () => ({ unsubscribe: jest.fn() }),
|
||||
};
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getBackendSrv: () => ({
|
||||
put: putMock,
|
||||
get: getMock,
|
||||
}),
|
||||
getAppEvents: () => mockAppEvents,
|
||||
}));
|
||||
|
||||
const props: Props = {
|
||||
@ -82,11 +87,21 @@ const setup = (optionOverrides?: Partial<Props['options']>) => {
|
||||
...optionOverrides,
|
||||
},
|
||||
};
|
||||
const meta: PluginMeta = {
|
||||
...newProps.options,
|
||||
id: 'cloudwatch',
|
||||
type: PluginType.datasource,
|
||||
info: {} as PluginMetaInfo,
|
||||
module: '',
|
||||
baseUrl: '',
|
||||
};
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<ConfigEditor {...newProps} />
|
||||
</Provider>
|
||||
return render(
|
||||
<PluginContextProvider meta={meta}>
|
||||
<Provider store={store}>
|
||||
<ConfigEditor {...newProps} />
|
||||
</Provider>
|
||||
</PluginContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -177,43 +192,19 @@ describe('Render', () => {
|
||||
|
||||
it('should load the data source if it was saved before', async () => {
|
||||
const SAVED_VERSION = 2;
|
||||
const newProps = {
|
||||
...props,
|
||||
options: {
|
||||
...props.options,
|
||||
version: SAVED_VERSION,
|
||||
},
|
||||
};
|
||||
|
||||
render(<ConfigEditor {...newProps} />);
|
||||
setup({ version: SAVED_VERSION });
|
||||
await waitFor(async () => expect(loadDataSourceMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('should not load the data source if it wasnt saved before', async () => {
|
||||
const SAVED_VERSION = undefined;
|
||||
const newProps = {
|
||||
...props,
|
||||
options: {
|
||||
...props.options,
|
||||
version: SAVED_VERSION,
|
||||
},
|
||||
};
|
||||
|
||||
render(<ConfigEditor {...newProps} />);
|
||||
setup({ version: SAVED_VERSION });
|
||||
await waitFor(async () => expect(loadDataSourceMock).not.toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('should show error message if Select log group button is clicked when data source is never saved', async () => {
|
||||
const SAVED_VERSION = undefined;
|
||||
const newProps = {
|
||||
...props,
|
||||
options: {
|
||||
...props.options,
|
||||
version: SAVED_VERSION,
|
||||
},
|
||||
};
|
||||
|
||||
render(<ConfigEditor {...newProps} />);
|
||||
setup({ version: SAVED_VERSION });
|
||||
|
||||
await waitFor(() => expect(screen.getByText('Select log groups')).toBeInTheDocument());
|
||||
await userEvent.click(screen.getByText('Select log groups'));
|
||||
@ -231,7 +222,20 @@ describe('Render', () => {
|
||||
version: SAVED_VERSION,
|
||||
},
|
||||
};
|
||||
const { rerender } = render(<ConfigEditor {...newProps} />);
|
||||
const meta: PluginMeta = {
|
||||
...newProps.options,
|
||||
id: 'cloudwatch',
|
||||
type: PluginType.datasource,
|
||||
info: {} as PluginMetaInfo,
|
||||
module: '',
|
||||
baseUrl: '',
|
||||
};
|
||||
|
||||
const { rerender } = render(
|
||||
<PluginContextProvider meta={meta}>
|
||||
<ConfigEditor {...newProps} />
|
||||
</PluginContextProvider>
|
||||
);
|
||||
await waitFor(() => expect(screen.getByText('Select log groups')).toBeInTheDocument());
|
||||
const rerenderProps = {
|
||||
...newProps,
|
||||
@ -243,7 +247,11 @@ describe('Render', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
rerender(<ConfigEditor {...rerenderProps} />);
|
||||
rerender(
|
||||
<PluginContextProvider meta={meta}>
|
||||
<ConfigEditor {...rerenderProps} />
|
||||
</PluginContextProvider>
|
||||
);
|
||||
await waitFor(() => expect(screen.getByText('AWS SDK Default')).toBeInTheDocument());
|
||||
await userEvent.click(screen.getByText('Select log groups'));
|
||||
await waitFor(() =>
|
||||
@ -264,7 +272,19 @@ describe('Render', () => {
|
||||
version: SAVED_VERSION,
|
||||
},
|
||||
};
|
||||
const { rerender } = render(<ConfigEditor {...newProps} />);
|
||||
const meta: PluginMeta = {
|
||||
...newProps.options,
|
||||
id: 'cloudwatch',
|
||||
type: PluginType.datasource,
|
||||
info: {} as PluginMetaInfo,
|
||||
module: '',
|
||||
baseUrl: '',
|
||||
};
|
||||
const { rerender } = render(
|
||||
<PluginContextProvider meta={meta}>
|
||||
<ConfigEditor {...newProps} />
|
||||
</PluginContextProvider>
|
||||
);
|
||||
await waitFor(() => expect(screen.getByText('Select log groups')).toBeInTheDocument());
|
||||
const rerenderProps = {
|
||||
...newProps,
|
||||
@ -273,7 +293,11 @@ describe('Render', () => {
|
||||
version: 1,
|
||||
},
|
||||
};
|
||||
rerender(<ConfigEditor {...rerenderProps} />);
|
||||
rerender(
|
||||
<PluginContextProvider meta={meta}>
|
||||
<ConfigEditor {...rerenderProps} />
|
||||
</PluginContextProvider>
|
||||
);
|
||||
await userEvent.click(screen.getByText('Select log groups'));
|
||||
await waitFor(() => expect(screen.getByText('Log group name prefix')).toBeInTheDocument());
|
||||
});
|
||||
|
@ -7,7 +7,10 @@ import {
|
||||
DataSourcePluginOptionsEditorProps,
|
||||
onUpdateDatasourceJsonDataOption,
|
||||
updateDatasourcePluginJsonDataOption,
|
||||
DataSourceTestSucceeded,
|
||||
DataSourceTestFailed,
|
||||
} from '@grafana/data';
|
||||
import { getAppEvents, usePluginInteractionReporter } from '@grafana/runtime';
|
||||
import { Input, InlineField, FieldProps, SecureSocksProxySettings } from '@grafana/ui';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { config } from 'app/core/config';
|
||||
@ -37,6 +40,23 @@ export const ConfigEditor = (props: Props) => {
|
||||
invalid: false,
|
||||
});
|
||||
useEffect(() => setLogGroupFieldState({ invalid: false }), [props.options]);
|
||||
const report = usePluginInteractionReporter();
|
||||
useEffect(() => {
|
||||
const successSubscription = getAppEvents().subscribe<DataSourceTestSucceeded>(DataSourceTestSucceeded, () => {
|
||||
report('grafana_plugin_cloudwatch_save_succeeded', {
|
||||
auth_type: options.jsonData.authType,
|
||||
});
|
||||
});
|
||||
const failSubscription = getAppEvents().subscribe<DataSourceTestFailed>(DataSourceTestFailed, () => {
|
||||
report('grafana_plugin_cloudwatch_save_failed', {
|
||||
auth_type: options.jsonData.authType,
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
successSubscription.unsubscribe();
|
||||
failSubscription.unsubscribe();
|
||||
};
|
||||
}, [options.jsonData.authType, report]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
Loading…
Reference in New Issue
Block a user