mirror of https://github.com/grafana/grafana.git
Querying: Pass dashboard and panel title as headers (#107032)
* WIP dashboard titile * Add tests * Fix tests * Fix tests * Remove redundant imports
This commit is contained in:
parent
3d1b820827
commit
4738957360
|
@ -574,6 +574,7 @@ export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
|
|||
panelName?: string;
|
||||
panelPluginId?: string;
|
||||
dashboardUID?: string;
|
||||
dashboardTitle?: string;
|
||||
headers?: Record<string, string>;
|
||||
|
||||
/** Filters to dynamically apply to all queries */
|
||||
|
|
|
@ -227,6 +227,59 @@ describe('DataSourceWithBackend', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('correctly passes dashboard and panel headers', () => {
|
||||
const { mock, ds } = createMockDatasource();
|
||||
ds.query({
|
||||
maxDataPoints: 10,
|
||||
intervalMs: 5000,
|
||||
targets: [{ refId: 'A' }],
|
||||
dashboardUID: 'dashA',
|
||||
dashboardTitle: 'My Test Dashboard',
|
||||
panelId: 123,
|
||||
panelName: 'CPU Usage Panel',
|
||||
range: getDefaultTimeRange(),
|
||||
} as DataQueryRequest);
|
||||
|
||||
const args = mock.calls[0][0];
|
||||
|
||||
expect(mock.calls.length).toBe(1);
|
||||
expect(args).toMatchInlineSnapshot(`
|
||||
{
|
||||
"data": {
|
||||
"from": "1697133600000",
|
||||
"queries": [
|
||||
{
|
||||
"applyTemplateVariablesCalled": true,
|
||||
"datasource": {
|
||||
"type": "dummy",
|
||||
"uid": "abc",
|
||||
},
|
||||
"datasourceId": 1234,
|
||||
"filters": undefined,
|
||||
"intervalMs": 5000,
|
||||
"maxDataPoints": 10,
|
||||
"queryCachingTTL": undefined,
|
||||
"refId": "A",
|
||||
},
|
||||
],
|
||||
"to": "1697155200000",
|
||||
},
|
||||
"headers": {
|
||||
"X-Dashboard-Title": "My Test Dashboard",
|
||||
"X-Dashboard-Uid": "dashA",
|
||||
"X-Datasource-Uid": "abc",
|
||||
"X-Panel-Id": "123",
|
||||
"X-Panel-Title": "CPU Usage Panel",
|
||||
"X-Plugin-Id": "dummy",
|
||||
},
|
||||
"hideFromInspector": false,
|
||||
"method": "POST",
|
||||
"requestId": undefined,
|
||||
"url": "/api/ds/query?ds_type=dummy",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly creates expression queries', () => {
|
||||
const { mock, ds } = createMockDatasource();
|
||||
ds.query({
|
||||
|
|
|
@ -87,6 +87,8 @@ enum PluginRequestHeaders {
|
|||
QueryGroupID = 'X-Query-Group-Id', // mainly useful to find related queries with query splitting
|
||||
FromExpression = 'X-Grafana-From-Expr', // used by datasources to identify expression queries
|
||||
SkipQueryCache = 'X-Cache-Skip', // used by datasources to skip the query cache
|
||||
DashboardTitle = 'X-Dashboard-Title', // used by datasources to identify the dashboard title
|
||||
PanelTitle = 'X-Panel-Title', // used by datasources to identify the panel title
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,9 +244,15 @@ class DataSourceWithBackend<
|
|||
|
||||
if (request.dashboardUID) {
|
||||
headers[PluginRequestHeaders.DashboardUID] = request.dashboardUID;
|
||||
if (request.dashboardTitle) {
|
||||
headers[PluginRequestHeaders.DashboardTitle] = request.dashboardTitle;
|
||||
}
|
||||
if (request.panelId) {
|
||||
headers[PluginRequestHeaders.PanelID] = `${request.panelId}`;
|
||||
}
|
||||
if (request.panelName) {
|
||||
headers[PluginRequestHeaders.PanelTitle] = request.panelName;
|
||||
}
|
||||
}
|
||||
if (request.panelPluginId) {
|
||||
headers[PluginRequestHeaders.PanelPluginId] = `${request.panelPluginId}`;
|
||||
|
|
|
@ -625,6 +625,7 @@ describe('DashboardScene', () => {
|
|||
expect(scene.enrichDataRequest(queryRunner)).toEqual({
|
||||
app: CoreApp.Dashboard,
|
||||
dashboardUID: 'dash-1',
|
||||
dashboardTitle: 'hello',
|
||||
panelId: 1,
|
||||
panelName: 'Panel A',
|
||||
panelPluginId: 'table',
|
||||
|
@ -641,6 +642,7 @@ describe('DashboardScene', () => {
|
|||
expect(scene.enrichDataRequest(queryRunner)).toEqual({
|
||||
app: CoreApp.Dashboard,
|
||||
dashboardUID: 'dash-1',
|
||||
dashboardTitle: 'hello',
|
||||
panelId: 1,
|
||||
panelName: 'Panel A',
|
||||
panelPluginId: 'table',
|
||||
|
|
|
@ -682,6 +682,7 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
|
|||
panelId,
|
||||
panelName: panel?.state?.title,
|
||||
panelPluginId: panel?.state.pluginId,
|
||||
dashboardTitle: this.state.title,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -146,6 +146,7 @@ describe('PanelEditorTableView', () => {
|
|||
// panel queries should have the updated time range
|
||||
expect(props.panel.runAllPanelQueries).toHaveBeenNthCalledWith(1, {
|
||||
dashboardTimezone: '',
|
||||
dashboardTitle: 'No Title',
|
||||
dashboardUID: props.dashboard.uid,
|
||||
timeData: timeRangeUpdated,
|
||||
width: 100,
|
||||
|
@ -166,6 +167,7 @@ describe('PanelEditorTableView', () => {
|
|||
// panel queries should have the updated time range
|
||||
expect(props.panel.runAllPanelQueries).toHaveBeenLastCalledWith({
|
||||
dashboardTimezone: '',
|
||||
dashboardTitle: 'No Title',
|
||||
dashboardUID: props.dashboard.uid,
|
||||
timeData: timeRangeUpdated2,
|
||||
width: 100,
|
||||
|
|
|
@ -38,6 +38,7 @@ export function PanelEditorTableView({ width, height, panel, dashboard }: Props)
|
|||
panel.runAllPanelQueries({
|
||||
dashboardUID: dashboard.uid,
|
||||
dashboardTimezone: dashboard.getTimezone(),
|
||||
dashboardTitle: dashboard.title,
|
||||
timeData,
|
||||
width,
|
||||
});
|
||||
|
|
|
@ -362,6 +362,7 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
|
|||
panel.runAllPanelQueries({
|
||||
dashboardUID: dashboard.uid,
|
||||
dashboardTimezone: dashboard.getTimezone(),
|
||||
dashboardTitle: dashboard.title,
|
||||
timeData,
|
||||
width,
|
||||
});
|
||||
|
|
|
@ -49,6 +49,7 @@ export interface GridPos {
|
|||
type RunPanelQueryOptions = {
|
||||
dashboardUID: string;
|
||||
dashboardTimezone: string;
|
||||
dashboardTitle: string;
|
||||
timeData: TimeOverrideResult;
|
||||
width: number;
|
||||
publicDashboardAccessToken?: string;
|
||||
|
@ -363,7 +364,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||
this.render();
|
||||
}
|
||||
|
||||
runAllPanelQueries({ dashboardUID, dashboardTimezone, timeData, width }: RunPanelQueryOptions) {
|
||||
runAllPanelQueries({ dashboardUID, dashboardTimezone, timeData, width, dashboardTitle }: RunPanelQueryOptions) {
|
||||
this.getQueryRunner().run({
|
||||
datasource: this.datasource,
|
||||
queries: this.targets,
|
||||
|
@ -371,6 +372,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||
panelName: this.title,
|
||||
panelPluginId: this.type,
|
||||
dashboardUID: dashboardUID,
|
||||
dashboardTitle: dashboardTitle,
|
||||
timezone: dashboardTimezone,
|
||||
timeRange: timeData.timeRange,
|
||||
timeInfo: timeData.timeInfo,
|
||||
|
|
|
@ -53,6 +53,7 @@ export interface QueryRunnerOptions<
|
|||
panelName?: string;
|
||||
panelPluginId?: string;
|
||||
dashboardUID?: string;
|
||||
dashboardTitle?: string;
|
||||
timezone: TimeZone;
|
||||
timeRange: TimeRange;
|
||||
timeInfo?: string; // String description of time range for display
|
||||
|
@ -262,6 +263,7 @@ export class PanelQueryRunner {
|
|||
panelName,
|
||||
panelPluginId,
|
||||
dashboardUID,
|
||||
dashboardTitle,
|
||||
timeRange,
|
||||
timeInfo,
|
||||
cacheTimeout,
|
||||
|
@ -288,6 +290,7 @@ export class PanelQueryRunner {
|
|||
panelName,
|
||||
panelPluginId,
|
||||
dashboardUID,
|
||||
dashboardTitle,
|
||||
range: timeRange,
|
||||
timeInfo,
|
||||
interval: '',
|
||||
|
|
|
@ -31,7 +31,6 @@ import {
|
|||
setBackendSrv,
|
||||
TemplateSrv,
|
||||
} from '@grafana/runtime';
|
||||
import { DashboardSrv, setDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
|
||||
import { LokiVariableSupport } from './LokiVariableSupport';
|
||||
import { createLokiDatasource } from './__mocks__/datasource';
|
||||
|
@ -1810,41 +1809,6 @@ describe('LokiDatasource', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('query', () => {
|
||||
beforeEach(() => {
|
||||
setDashboardSrv({
|
||||
getCurrent: () => ({
|
||||
title: 'dashboard_title',
|
||||
panels: [{ title: 'panel_title', id: 0 }],
|
||||
}),
|
||||
} as unknown as DashboardSrv);
|
||||
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
|
||||
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
|
||||
});
|
||||
|
||||
it('adds dashboard headers', async () => {
|
||||
const ds = createLokiDatasource(templateSrvStub);
|
||||
jest.spyOn(ds, 'runQuery');
|
||||
const query: DataQueryRequest<LokiQuery> = {
|
||||
...baseRequestOptions,
|
||||
panelId: 0,
|
||||
targets: [{ expr: '{a="b"}', refId: 'A' }],
|
||||
app: CoreApp.Dashboard,
|
||||
};
|
||||
|
||||
await expect(ds.query(query)).toEmitValuesWith(() => {
|
||||
expect(ds.runQuery).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
'X-Dashboard-Title': 'dashboard_title',
|
||||
'X-Panel-Title': 'panel_title',
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getQueryStats', () => {
|
||||
let ds: LokiDatasource;
|
||||
let query: LokiQuery;
|
||||
|
|
|
@ -46,7 +46,6 @@ import {
|
|||
import { Duration } from '@grafana/lezer-logql';
|
||||
import { BackendSrvRequest, config, DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
|
||||
import LanguageProvider from './LanguageProvider';
|
||||
import { LiveStreams, LokiLiveTarget } from './LiveStreams';
|
||||
|
@ -295,30 +294,6 @@ export class LokiDatasource
|
|||
return { ...logsSampleRequest, targets };
|
||||
}
|
||||
|
||||
private getQueryHeaders(request: DataQueryRequest<LokiQuery>): Record<string, string> {
|
||||
const headers: Record<string, string> = {};
|
||||
// only add headers if we are in the context of a dashboard
|
||||
if (
|
||||
[CoreApp.Dashboard.toString(), CoreApp.PanelEditor.toString(), CoreApp.PanelViewer.toString()].includes(
|
||||
request.app
|
||||
) === false
|
||||
) {
|
||||
return headers;
|
||||
}
|
||||
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
const dashboardTitle = dashboard?.title;
|
||||
const panelTitle = dashboard?.panels.find((p) => p.id === request?.panelId)?.title;
|
||||
if (dashboardTitle) {
|
||||
headers['X-Dashboard-Title'] = dashboardTitle;
|
||||
}
|
||||
if (panelTitle) {
|
||||
headers['X-Panel-Title'] = panelTitle;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by DataSourceApi. It executes queries based on the provided DataQueryRequest.
|
||||
* @returns An Observable of DataQueryResponse containing the query results.
|
||||
|
@ -333,8 +308,6 @@ export class LokiDatasource
|
|||
targets: queries,
|
||||
};
|
||||
|
||||
fixedRequest.headers = this.getQueryHeaders(request);
|
||||
|
||||
const streamQueries = fixedRequest.targets.filter((q) => q.queryType === LokiQueryType.Stream);
|
||||
if (
|
||||
config.featureToggles.lokiExperimentalStreaming &&
|
||||
|
|
Loading…
Reference in New Issue