Datasource Config Editor: Add event-based funnel for measuring CVR (#55105)

* Added start event on adding new DS from datasources/new page

* Added end event + tests

* revert analytics.ts changes

* revert azure tracking changes

* changed version in doc

* update props name for consistency with plugin-platform PR to come

* Updated tests
This commit is contained in:
Yaelle Chaudy 2022-10-24 11:09:26 +02:00 committed by GitHub
parent fce0a49284
commit 6ee69853dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 3 deletions

View File

@ -1,11 +1,12 @@
import { thunkTester } from 'test/core/thunk/thunkTester';
import { DataSourceSettings } from '@grafana/data';
import { AppPluginMeta, DataSourceSettings, PluginMetaInfo, PluginType } from '@grafana/data';
import { FetchError } from '@grafana/runtime';
import { ThunkResult, ThunkDispatch } from 'app/types';
import { getMockDataSource } from '../__mocks__';
import * as api from '../api';
import { trackDataSourceCreated, trackDataSourceTested } from '../tracking';
import { GenericDataSourcePlugin } from '../types';
import {
@ -14,6 +15,7 @@ import {
TestDataSourceDependencies,
initDataSourceSettings,
loadDataSource,
addDataSource,
} from './actions';
import {
initDataSourceSettingsSucceeded,
@ -22,13 +24,20 @@ import {
testDataSourceSucceeded,
testDataSourceFailed,
dataSourceLoaded,
dataSourcesLoaded,
} from './reducers';
jest.mock('../api');
jest.mock('app/core/services/backend_srv');
jest.mock('app/core/core');
jest.mock('@grafana/runtime', () => ({
...(jest.requireActual('@grafana/runtime') as unknown as object),
getBackendSrv: jest.fn(),
getDataSourceSrv: jest.fn().mockReturnValue({ reload: jest.fn() }),
getBackendSrv: jest.fn().mockReturnValue({ get: jest.fn() }),
}));
jest.mock('../tracking', () => ({
trackDataSourceCreated: jest.fn(),
trackDataSourceTested: jest.fn(),
}));
const getBackendSrvMock = () =>
@ -210,6 +219,8 @@ describe('testDataSource', () => {
status: '',
message: '',
}),
type: 'cloudwatch',
uid: 'CW1234',
}),
} as any),
getBackendSrv: getBackendSrvMock,
@ -222,9 +233,15 @@ describe('testDataSource', () => {
};
const dispatchedActions = await thunkTester(state)
.givenThunk(testDataSource)
.whenThunkIsDispatched('Azure Monitor', dependencies);
.whenThunkIsDispatched('CloudWatch', dependencies);
expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceSucceeded(state.testingStatus)]);
expect(trackDataSourceTested).toHaveBeenCalledWith({
plugin_id: 'cloudwatch',
datasource_uid: 'CW1234',
grafana_version: '1.0',
success: true,
});
});
it('then testDataSourceFailed should be dispatched', async () => {
@ -235,6 +252,8 @@ describe('testDataSource', () => {
testDatasource: jest.fn().mockImplementation(() => {
throw new Error('Error testing datasource');
}),
type: 'azure-monitor',
uid: 'azM0nit0R',
}),
} as any),
getBackendSrv: getBackendSrvMock,
@ -253,6 +272,12 @@ describe('testDataSource', () => {
.whenThunkIsDispatched('Azure Monitor', dependencies);
expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceFailed(result)]);
expect(trackDataSourceTested).toHaveBeenCalledWith({
plugin_id: 'azure-monitor',
datasource_uid: 'azM0nit0R',
grafana_version: '1.0',
success: false,
});
});
it('then testDataSourceFailed should be dispatched with response error message', async () => {
@ -304,3 +329,34 @@ describe('testDataSource', () => {
});
});
});
describe('addDataSource', () => {
it('it creates a datasource and calls trackDataSourceCreated ', async () => {
const meta: AppPluginMeta = {
id: 'azure-monitor',
module: '',
baseUrl: 'xxx',
info: { version: '1.2.3' } as PluginMetaInfo,
type: PluginType.datasource,
name: 'test DS',
};
const state = {
dataSources: {
dataSources: [],
},
};
const dataSourceMock = { datasource: { uid: 'azure23' }, meta };
(api.createDataSource as jest.Mock).mockResolvedValueOnce(dataSourceMock);
(api.getDataSources as jest.Mock).mockResolvedValueOnce([]);
const dispatchedActions = await thunkTester(state).givenThunk(addDataSource).whenThunkIsDispatched(meta);
expect(dispatchedActions).toEqual([dataSourcesLoaded([])]);
expect(trackDataSourceCreated).toHaveBeenCalledWith({
plugin_id: 'azure-monitor',
plugin_version: '1.2.3',
datasource_uid: 'azure23',
grafana_version: '1.0',
});
});
});

View File

@ -1,5 +1,6 @@
import { DataSourcePluginMeta, DataSourceSettings, locationUtil } from '@grafana/data';
import {
config,
DataSourceWithBackend,
getDataSourceSrv,
HealthCheckError,
@ -17,6 +18,7 @@ import { DataSourcePluginCategory, ThunkDispatch, ThunkResult } from 'app/types'
import * as api from '../api';
import { DATASOURCES_ROUTES } from '../constants';
import { trackDataSourceCreated, trackDataSourceTested } from '../tracking';
import { nameExits, findNewName } from '../utils';
import { buildCategories } from './buildCategories';
@ -107,6 +109,12 @@ export const testDataSource = (
const result = await dsApi.testDatasource();
dispatch(testDataSourceSucceeded(result));
trackDataSourceTested({
grafana_version: config.buildInfo.version,
plugin_id: dsApi.type,
datasource_uid: dsApi.uid,
success: true,
});
} catch (err) {
let message: string | undefined;
let details: HealthCheckResultDetails;
@ -121,6 +129,12 @@ export const testDataSource = (
}
dispatch(testDataSourceFailed({ message, details }));
trackDataSourceTested({
grafana_version: config.buildInfo.version,
plugin_id: dsApi.type,
datasource_uid: dsApi.uid,
success: false,
});
}
});
};
@ -197,6 +211,13 @@ export function addDataSource(plugin: DataSourcePluginMeta, editLink = DATASOURC
await getDatasourceSrv().reload();
await contextSrv.fetchUserPermissions();
trackDataSourceCreated({
grafana_version: config.buildInfo.version,
plugin_id: plugin.id,
datasource_uid: result.datasource.uid,
plugin_version: result.meta?.info?.version,
});
locationService.push(editLink.replace(/:uid/gi, result.datasource.uid));
};
}

View File

@ -0,0 +1,54 @@
import { reportInteraction } from '@grafana/runtime';
/**
* Used to track data source creation via either the specific plugin page `/plugins/<the-data-source>`
* or the general datasources page `/datasources/new`
*
* This event corresponds to the start event of our data source creation funnel.
* Combined with the end event, it allows answering questions about:
* - Conversion (percentage of user that successfully set up a data source)
* - Time spent on the config page
*
* Changelog:
* - v9.1.7 : logging datasource, datasource_uid, grafana version
*/
export const trackDataSourceCreated = (props: DataSourceCreatedProps) => {
reportInteraction('grafana_ds_add_datasource_clicked', props);
};
type DataSourceCreatedProps = {
grafana_version?: string;
/** The unique id of the newly created data source */
datasource_uid: string;
/** The datasource id (e.g. Cloudwatch, Loki, Prometheus) */
plugin_id: string;
/** The plugin version (especially interesting in external plugins - core plugins are aligned with grafana version) */
plugin_version?: string;
};
/**
* Used to track data source testing
*
* This event corresponds to the end event of our data source creation funnel.
* Combined with the start event, it allows answering questions about:
* - Conversion (percentage of user that successfully set up a data source)
* - Time spent on the config page
*
* Changelog:
* - v9.1.7 : logging datasource, datasource_uid, grafana version and success
*/
export const trackDataSourceTested = (props: DataSourceTestedProps) => {
reportInteraction('grafana_ds_test_datasource_clicked', props);
};
type DataSourceTestedProps = {
grafana_version?: string;
/** The unique id of the newly created data source */
datasource_uid: string;
/** The datasource id (e.g. Cloudwatch, Loki, Prometheus) */
plugin_id: string;
/** The plugin version (especially interesting in external plugins - core plugins are aligned with grafana version) */
plugin_version?: string;
/** Whether or not the datasource test succeeded = the datasource was successfully configured */
success: boolean;
};