[--Dashboard-- data source] AdHoc filtering: Remove feature toggle (#113674)
* Remove dashboardDsAdHocFiltering feature toggle The dashboardDsAdHocFiltering feature toggle has been enabled by default and is now in General Availability stage. This commit removes the feature toggle and makes the AdHoc filtering functionality for the dashboard datasource permanently available. Changes: - Remove feature toggle from registry.go - Regenerate feature toggle files - Remove conditional checks in frontend code - Update tests to reflect permanent enablement - Always show AdHoc Filters toggle in dashboard query editor - Always enable dashboard datasource in DataSourcePicker for variables * Remove unused imports * Fix Prettier formatting issues
This commit is contained in:
@@ -79,7 +79,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
||||
| `grafanaAssistantInProfilesDrilldown` | Enables integration with Grafana Assistant in Profiles Drilldown | Yes |
|
||||
| `sharingDashboardImage` | Enables image sharing functionality for dashboards | Yes |
|
||||
| `tabularNumbers` | Use fixed-width numbers globally in the UI | |
|
||||
| `dashboardDsAdHocFiltering` | Enables adhoc filtering support for the dashboard datasource | Yes |
|
||||
| `adhocFiltersInTooltips` | Enable adhoc filter buttons in visualization tooltips | Yes |
|
||||
| `tempoSearchBackendMigration` | Run search queries through the tempo backend | |
|
||||
|
||||
|
||||
@@ -1119,11 +1119,6 @@ export interface FeatureToggles {
|
||||
*/
|
||||
unifiedStorageSearchDualReaderEnabled?: boolean;
|
||||
/**
|
||||
* Enables adhoc filtering support for the dashboard datasource
|
||||
* @default true
|
||||
*/
|
||||
dashboardDsAdHocFiltering?: boolean;
|
||||
/**
|
||||
* Supports __from and __to macros that always use the dashboard level time range
|
||||
*/
|
||||
dashboardLevelTimeMacros?: boolean;
|
||||
|
||||
@@ -1942,14 +1942,6 @@ var (
|
||||
HideFromAdminPage: true,
|
||||
HideFromDocs: true,
|
||||
},
|
||||
{
|
||||
Name: "dashboardDsAdHocFiltering",
|
||||
Description: "Enables adhoc filtering support for the dashboard datasource",
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
Owner: grafanaDataProSquad,
|
||||
FrontendOnly: true,
|
||||
Expression: "true",
|
||||
},
|
||||
{
|
||||
Name: "dashboardLevelTimeMacros",
|
||||
Description: "Supports __from and __to macros that always use the dashboard level time range",
|
||||
|
||||
@@ -250,7 +250,6 @@ foldersAppPlatformAPI,experimental,@grafana/grafana-search-navigate-organise,fal
|
||||
otelLogsFormatting,experimental,@grafana/observability-logs,false,false,true
|
||||
alertingNotificationHistory,experimental,@grafana/alerting-squad,false,false,false
|
||||
unifiedStorageSearchDualReaderEnabled,experimental,@grafana/search-and-storage,false,false,false
|
||||
dashboardDsAdHocFiltering,GA,@grafana/datapro,false,false,true
|
||||
dashboardLevelTimeMacros,experimental,@grafana/dashboards-squad,false,false,true
|
||||
alertmanagerRemoteSecondaryWithRemoteState,experimental,@grafana/alerting-squad,false,false,false
|
||||
restrictedPluginApis,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
|
||||
|
Generated
-4
@@ -1010,10 +1010,6 @@ const (
|
||||
// Enable dual reader for unified storage search
|
||||
FlagUnifiedStorageSearchDualReaderEnabled = "unifiedStorageSearchDualReaderEnabled"
|
||||
|
||||
// FlagDashboardDsAdHocFiltering
|
||||
// Enables adhoc filtering support for the dashboard datasource
|
||||
FlagDashboardDsAdHocFiltering = "dashboardDsAdHocFiltering"
|
||||
|
||||
// FlagDashboardLevelTimeMacros
|
||||
// Supports __from and __to macros that always use the dashboard level time range
|
||||
FlagDashboardLevelTimeMacros = "dashboardLevelTimeMacros"
|
||||
|
||||
@@ -1061,6 +1061,7 @@
|
||||
"name": "dashboardDsAdHocFiltering",
|
||||
"resourceVersion": "1756814786992",
|
||||
"creationTimestamp": "2025-07-23T08:12:25Z",
|
||||
"deletionTimestamp": "2025-09-27T19:59:33Z",
|
||||
"annotations": {
|
||||
"grafana.app/updatedTimestamp": "2025-09-02 12:06:26.992384 +0000 UTC"
|
||||
}
|
||||
|
||||
+1
-2
@@ -4,7 +4,6 @@ import { DataSourceInstanceSettings, MetricFindValue, readCSV } from '@grafana/d
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { EditorField } from '@grafana/plugin-ui';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { DataSourceRef } from '@grafana/schema';
|
||||
import { Alert, CodeEditor, Field, Switch, Box } from '@grafana/ui';
|
||||
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
|
||||
@@ -67,7 +66,7 @@ export function AdHocVariableForm({
|
||||
onChange={onDataSourceChange}
|
||||
width={30}
|
||||
variables={true}
|
||||
dashboard={config.featureToggles.dashboardDsAdHocFiltering}
|
||||
dashboard={true}
|
||||
noDefault
|
||||
/>
|
||||
</EditorField>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { getDefaultTimeRange, LoadingState } from '@grafana/data';
|
||||
import config from 'app/core/config';
|
||||
import { mockDataSource } from 'app/features/alerting/unified/mocks';
|
||||
import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
@@ -30,9 +29,6 @@ jest.mock('app/core/config', () => ({
|
||||
},
|
||||
},
|
||||
},
|
||||
featureToggles: {
|
||||
dashboardDsAdHocFiltering: false, // Default to false, can be overridden in tests
|
||||
},
|
||||
}));
|
||||
|
||||
setupDataSources(mockDataSource({ isDefault: true }));
|
||||
@@ -179,11 +175,7 @@ describe('DashboardQueryEditor', () => {
|
||||
jest.spyOn(getDashboardSrv(), 'getCurrent').mockImplementation(() => mockDashboard);
|
||||
});
|
||||
|
||||
it('shows the AdHoc Filters toggle when feature toggle is enabled', async () => {
|
||||
await act(async () => {
|
||||
config.featureToggles.dashboardDsAdHocFiltering = true;
|
||||
});
|
||||
|
||||
it('shows the AdHoc Filters toggle', async () => {
|
||||
const query: DashboardQuery = { refId: 'A', panelId: 1, adHocFiltersEnabled: false };
|
||||
|
||||
await act(async () => {
|
||||
@@ -201,30 +193,5 @@ describe('DashboardQueryEditor', () => {
|
||||
const adhocFiltersToggle = await screen.findByText('AdHoc Filters');
|
||||
expect(adhocFiltersToggle).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show the AdHoc Filters toggle when feature toggle is disabled', async () => {
|
||||
await act(async () => {
|
||||
config.featureToggles.dashboardDsAdHocFiltering = false;
|
||||
});
|
||||
|
||||
const query: DashboardQuery = { refId: 'A', panelId: 1, adHocFiltersEnabled: false };
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<DashboardQueryEditor
|
||||
datasource={{} as DashboardDatasource}
|
||||
query={query}
|
||||
data={mockPanelData}
|
||||
onChange={mockOnChange}
|
||||
onRunQuery={mockOnRunQueries}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
// Wait for any async operations to complete
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('AdHoc Filters')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -205,15 +205,13 @@ export function DashboardQueryEditor({ data, query, onChange, onRunQuery }: Prop
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{config.featureToggles.dashboardDsAdHocFiltering && (
|
||||
<Field
|
||||
label="AdHoc Filters"
|
||||
description="Apply --Dashboard-- data source AdHoc filters to this panel"
|
||||
noMargin
|
||||
>
|
||||
<InlineSwitch value={Boolean(query.adHocFiltersEnabled)} onChange={onAdHocFiltersToggle} />
|
||||
</Field>
|
||||
)}
|
||||
<Field
|
||||
label="AdHoc Filters"
|
||||
description="Apply --Dashboard-- data source AdHoc filters to this panel"
|
||||
noMargin
|
||||
>
|
||||
<InlineSwitch value={Boolean(query.adHocFiltersEnabled)} onChange={onAdHocFiltersToggle} />
|
||||
</Field>
|
||||
</Stack>
|
||||
|
||||
{loadingResults ? (
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
AdHocVariableFilter,
|
||||
} from '@grafana/data';
|
||||
import { getPanelPlugin } from '@grafana/data/test';
|
||||
import { setPluginImportUtils, config } from '@grafana/runtime';
|
||||
import { setPluginImportUtils } from '@grafana/runtime';
|
||||
import {
|
||||
SafeSerializableSceneObject,
|
||||
SceneDataNode,
|
||||
@@ -178,16 +178,6 @@ describe('DashboardDatasource', () => {
|
||||
|
||||
// Test AdHoc filtering via the Public API first, to ensure Integration
|
||||
describe('Integration (Public API)', () => {
|
||||
const originalToggleValue = config.featureToggles.dashboardDsAdHocFiltering;
|
||||
|
||||
beforeEach(() => {
|
||||
config.featureToggles.dashboardDsAdHocFiltering = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.featureToggles.dashboardDsAdHocFiltering = originalToggleValue;
|
||||
});
|
||||
|
||||
it('should apply basic filtering end-to-end through public query method', async () => {
|
||||
const testFrame = createTestFrame([
|
||||
{ name: 'name', type: FieldType.string, values: ['John', 'Jane', 'Bob'] },
|
||||
@@ -224,46 +214,6 @@ describe('DashboardDatasource', () => {
|
||||
expect(result?.data[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should respect feature toggle and not filter when disabled', async () => {
|
||||
// Temporarily disable the feature toggle for this test
|
||||
config.featureToggles.dashboardDsAdHocFiltering = false;
|
||||
|
||||
const testFrame = createTestFrame([
|
||||
{ name: 'name', type: FieldType.string, values: ['John', 'Jane', 'Bob'] },
|
||||
{ name: 'age', type: FieldType.number, values: [25, 30, 35] },
|
||||
]);
|
||||
|
||||
const scene = new SceneFlexLayout({
|
||||
children: [
|
||||
new SceneFlexItem({
|
||||
body: new VizPanel({
|
||||
key: getVizPanelKeyForPanelId(1),
|
||||
$data: new SceneDataNode({
|
||||
data: {
|
||||
series: [testFrame],
|
||||
state: LoadingState.Done,
|
||||
timeRange: getDefaultTimeRange(),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const ds = new DashboardDatasource({} as DataSourceInstanceSettings);
|
||||
const filters: AdHocVariableFilter[] = [{ key: 'name', operator: '=', value: 'John' }];
|
||||
|
||||
const observable = ds.query(createQueryRequest(filters, scene));
|
||||
|
||||
let result: DataQueryResponse | undefined;
|
||||
observable.subscribe({ next: (data) => (result = data) });
|
||||
|
||||
// Should return unfiltered data since feature toggle is disabled
|
||||
expect(result?.data[0].fields[0].values).toEqual(['John', 'Jane', 'Bob']);
|
||||
expect(result?.data[0].fields[1].values).toEqual([25, 30, 35]);
|
||||
expect(result?.data[0].length).toBe(3);
|
||||
});
|
||||
|
||||
it('should respect per-panel adHocFiltersEnabled setting and not filter when disabled', async () => {
|
||||
const testFrame = createTestFrame([
|
||||
{ name: 'name', type: FieldType.string, values: ['John', 'Jane', 'Bob'] },
|
||||
@@ -723,27 +673,8 @@ describe('DashboardDatasource', () => {
|
||||
});
|
||||
|
||||
describe('getDrilldownsApplicability', () => {
|
||||
const originalToggleValue = config.featureToggles.dashboardDsAdHocFiltering;
|
||||
const ds = new DashboardDatasource({} as DataSourceInstanceSettings);
|
||||
|
||||
beforeEach(() => {
|
||||
config.featureToggles.dashboardDsAdHocFiltering = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.featureToggles.dashboardDsAdHocFiltering = originalToggleValue;
|
||||
});
|
||||
|
||||
it('should return empty array when feature toggle is disabled', async () => {
|
||||
config.featureToggles.dashboardDsAdHocFiltering = false;
|
||||
|
||||
const result = await ds.getDrilldownsApplicability({
|
||||
filters: [{ key: 'name', operator: '=', value: 'test' }],
|
||||
});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should mark supported operators as applicable', async () => {
|
||||
const result = await ds.getDrilldownsApplicability({
|
||||
filters: [
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
DataSourceGetDrilldownsApplicabilityOptions,
|
||||
DrilldownsApplicability,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { isSceneObject, SceneDataProvider, SceneDataTransformer, SceneObject } from '@grafana/scenes';
|
||||
import {
|
||||
activateSceneObjectAndParentTree,
|
||||
@@ -140,11 +139,10 @@ export class DashboardDatasource extends DataSourceApi<DashboardQuery> {
|
||||
...field,
|
||||
config: {
|
||||
...field.config,
|
||||
// Enable AdHoc filtering for string and numeric fields only when feature toggle and per-panel setting are enabled
|
||||
filterable:
|
||||
config.featureToggles.dashboardDsAdHocFiltering && query.adHocFiltersEnabled
|
||||
? field.type === FieldType.string || field.type === FieldType.number
|
||||
: field.config.filterable,
|
||||
// Enable AdHoc filtering for string and numeric fields only when per-panel setting is enabled
|
||||
filterable: query.adHocFiltersEnabled
|
||||
? field.type === FieldType.string || field.type === FieldType.number
|
||||
: field.config.filterable,
|
||||
},
|
||||
state: {
|
||||
...field.state,
|
||||
@@ -153,7 +151,7 @@ export class DashboardDatasource extends DataSourceApi<DashboardQuery> {
|
||||
};
|
||||
});
|
||||
|
||||
if (!config.featureToggles.dashboardDsAdHocFiltering || !query.adHocFiltersEnabled || filters.length === 0) {
|
||||
if (!query.adHocFiltersEnabled || filters.length === 0) {
|
||||
return [...series, ...annotations];
|
||||
}
|
||||
|
||||
@@ -247,11 +245,9 @@ export class DashboardDatasource extends DataSourceApi<DashboardQuery> {
|
||||
|
||||
const field = frame.fields[fieldIndex];
|
||||
|
||||
// Only support string and numeric fields when feature toggle is enabled
|
||||
if (config.featureToggles.dashboardDsAdHocFiltering) {
|
||||
if (field.type !== FieldType.string && field.type !== FieldType.number) {
|
||||
return null;
|
||||
}
|
||||
// Only support string and numeric fields
|
||||
if (field.type !== FieldType.string && field.type !== FieldType.number) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Map operator to matcher ID
|
||||
@@ -357,10 +353,6 @@ export class DashboardDatasource extends DataSourceApi<DashboardQuery> {
|
||||
async getDrilldownsApplicability(
|
||||
options?: DataSourceGetDrilldownsApplicabilityOptions<DashboardQuery>
|
||||
): Promise<DrilldownsApplicability[]> {
|
||||
if (!config.featureToggles.dashboardDsAdHocFiltering) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check if any query has adhoc filters enabled
|
||||
const hasAdHocFiltersEnabled = options?.queries?.some((query) => query.adHocFiltersEnabled);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user