The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/query/state/queryAnalytics.test.ts

284 lines
7.4 KiB

import {
CoreApp,
DataFrame,
DataQueryError,
getDefaultTimeRange,
DataSourceApi,
dateTime,
LoadingState,
PanelData,
DataQueryRequest,
} from '@grafana/data';
import { MetaAnalyticsEventName, reportMetaAnalytics } from '@grafana/runtime';
import { createDashboardModelFixture } from '../../dashboard/state/__fixtures__/dashboardFixtures';
import { emitDataRequestEvent } from './queryAnalytics';
beforeEach(() => {
jest.clearAllMocks();
});
const datasource = {
name: 'test',
id: 1,
uid: 'test',
} as DataSourceApi;
const dashboardModel = createDashboardModelFixture(
{ id: 1, title: 'Test Dashboard', uid: 'test' },
{ folderTitle: 'Test Folder' }
);
jest.mock('app/features/dashboard/services/DashboardSrv', () => ({
getDashboardSrv: () => {
return {
getCurrent: () => dashboardModel,
};
},
}));
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
reportMetaAnalytics: jest.fn(),
}));
const mockGetUrlSearchParams = jest.fn(() => {
return {};
});
jest.mock('@grafana/data', () => ({
...jest.requireActual('@grafana/data'),
urlUtil: {
getUrlSearchParams: () => mockGetUrlSearchParams(),
},
}));
const partiallyCachedSeries = [
{
refId: 'A',
meta: {
isCachedResponse: true,
},
fields: [],
length: 0,
},
{
refId: 'B',
fields: [],
length: 0,
},
];
const multipleDataframesWithSameRefId = [
{
refId: 'A',
meta: {
isCachedResponse: true,
},
fields: [],
length: 0,
},
{
refId: 'A',
fields: [],
length: 0,
},
];
function getTestData(
overrides: Partial<DataQueryRequest> = {},
series?: DataFrame[],
errors?: DataQueryError[]
): PanelData {
const now = dateTime();
return {
request: {
app: CoreApp.Dashboard,
startTime: now.unix(),
endTime: now.add(1, 's').unix(),
interval: '1s',
intervalMs: 1000,
range: getDefaultTimeRange(),
requestId: '1',
scopedVars: {},
targets: [],
timezone: 'utc',
...overrides,
},
series: series || [],
state: LoadingState.Done,
timeRange: getDefaultTimeRange(),
errors,
};
}
describe('emitDataRequestEvent', () => {
describe('From a dashboard panel', () => {
it('Should report meta analytics', () => {
const data = getTestData({
panelId: 2,
});
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
expect(reportMetaAnalytics).toBeCalledWith(
expect.objectContaining({
eventName: MetaAnalyticsEventName.DataRequest,
datasourceName: datasource.name,
datasourceUid: datasource.uid,
datasourceType: datasource.type,
source: CoreApp.Dashboard,
panelId: 2,
dashboardUid: 'test', // from dashboard srv
dataSize: 0,
duration: 1,
totalQueries: 0,
cachedQueries: 0,
})
);
});
it('Should report meta analytics with counts for cached and total queries', () => {
const data = getTestData(
{
panelId: 2,
},
partiallyCachedSeries
);
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
expect(reportMetaAnalytics).toBeCalledWith(
expect.objectContaining({
eventName: MetaAnalyticsEventName.DataRequest,
datasourceName: datasource.name,
datasourceUid: datasource.uid,
datasourceType: datasource.type,
source: CoreApp.Dashboard,
panelId: 2,
dashboardUid: 'test',
dataSize: 2,
duration: 1,
totalQueries: 2,
cachedQueries: 1,
})
);
});
it('Should report meta analytics with counts for cached and total queries when same refId spread across multiple DataFrames', () => {
const data = getTestData(
{
panelId: 2,
},
multipleDataframesWithSameRefId
);
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
expect(reportMetaAnalytics).toBeCalledWith(
expect.objectContaining({
eventName: MetaAnalyticsEventName.DataRequest,
datasourceName: datasource.name,
datasourceUid: datasource.uid,
datasourceType: datasource.type,
source: CoreApp.Dashboard,
panelId: 2,
dashboardUid: 'test', // from dashboard srv
dataSize: 2,
duration: 1,
totalQueries: 1,
cachedQueries: 1,
})
);
});
it('Should not report meta analytics twice if the request receives multiple responses', () => {
const data = getTestData();
const fn = emitDataRequestEvent(datasource);
fn(data);
fn(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
});
it('Should not report meta analytics in edit mode', () => {
mockGetUrlSearchParams.mockImplementationOnce(() => {
return { editPanel: 2 };
});
const data = getTestData();
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).not.toBeCalled();
});
});
// Previously we filtered out Explore and Correlations events due to too many errors being generated while a user is building a query
// This tests that we send an event for both queries but do not record errors
describe('From Explore', () => {
const data = getTestData(
{
app: CoreApp.Explore,
},
undefined,
[{ message: 'test error' }]
);
it('Should report meta analytics', () => {
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
expect(reportMetaAnalytics).toBeCalledWith(
expect.objectContaining({
eventName: MetaAnalyticsEventName.DataRequest,
source: CoreApp.Explore,
datasourceName: 'test',
datasourceUid: 'test',
dataSize: 0,
duration: 1,
totalQueries: 0,
})
);
});
it('Should not report errors', () => {
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
expect(reportMetaAnalytics).toBeCalledWith(expect.not.objectContaining({ error: 'test error' }));
});
});
// Previously we filtered out Explore and Correlations events due to too many errors being generated while a user is building a query
// This tests that we send an event for both queries but do not record errors
describe('From Correlations', () => {
const data = getTestData(
{
app: CoreApp.Correlations,
},
undefined,
[{ message: 'some error' }]
);
it('Should report meta analytics', () => {
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
expect(reportMetaAnalytics).toBeCalledWith(
expect.objectContaining({
eventName: MetaAnalyticsEventName.DataRequest,
source: CoreApp.Correlations,
datasourceName: 'test',
datasourceUid: 'test',
dataSize: 0,
duration: 1,
totalQueries: 0,
})
);
});
it('Should not report errors', () => {
emitDataRequestEvent(datasource)(data);
expect(reportMetaAnalytics).toBeCalledTimes(1);
expect(reportMetaAnalytics).toBeCalledWith(expect.not.objectContaining({ error: 'test error' }));
});
});
});