Loki: Remove `lokiQueryHints` feature toggle (#106620)

* Loki: Remove lokiQueryHints feature toggle

* Remove unused imports

* Fix tests
pull/107508/head
Ivana Huckova 3 weeks ago committed by GitHub
parent 3674d952a6
commit 5cd3ad76ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
  2. 5
      packages/grafana-data/src/types/featureToggles.gen.ts
  3. 11
      pkg/services/featuremgmt/registry.go
  4. 2
      pkg/services/featuremgmt/toggles_gen.csv
  5. 6
      pkg/services/featuremgmt/toggles_gen.go
  6. 5
      pkg/services/featuremgmt/toggles_gen.json
  7. 14
      public/app/plugins/datasource/loki/LanguageProvider.test.ts
  8. 3
      public/app/plugins/datasource/loki/LanguageProvider.ts
  9. 9
      public/app/plugins/datasource/loki/querybuilder/components/LabelParamEditor.test.tsx
  10. 9
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx
  11. 3
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx
  12. 12
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderContainer.test.tsx
  13. 19
      public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.test.tsx
  14. 3
      public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx

@ -53,7 +53,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `ssoSettingsApi` | Enables the SSO settings API and the OAuth configuration UIs in Grafana | Yes |
| `logsInfiniteScrolling` | Enables infinite scrolling for the Logs panel in Explore and Dashboards | Yes |
| `logRowsPopoverMenu` | Enable filtering menu displayed when text of a log line is selected | Yes |
| `lokiQueryHints` | Enables query hints for Loki | Yes |
| `alertingQueryOptimization` | Optimizes eligible queries in order to reduce load on datasources | |
| `onPremToCloudMigrations` | Enable the Grafana Migration Assistant, which helps you easily migrate various on-prem resources to your Grafana Cloud stack. | Yes |
| `groupToNestedTableTransformation` | Enables the group to nested table transformation | Yes |
@ -103,6 +102,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `cloudWatchBatchQueries` | Runs CloudWatch metrics queries as separate batches |
| `pdfTables` | Enables generating table data as PDF in reporting |
| `canvasPanelPanZoom` | Allow pan and zoom in canvas panel |
| `regressionTransformation` | Enables regression analysis transformation |
| `alertingSaveStateCompressed` | Enables the compressed protobuf-based alert state storage |
| `tableNextGen` | Allows access to the new react-data-grid based table component. |
| `enableSCIM` | Enables SCIM support for user and group management |

@ -420,10 +420,9 @@ export interface FeatureToggles {
*/
tableSharedCrosshair?: boolean;
/**
* Enables query hints for Loki
* @default true
* Enables regression analysis transformation
*/
lokiQueryHints?: boolean;
regressionTransformation?: boolean;
/**
* Use the kubernetes API for feature toggle management in the frontend
*/

@ -698,14 +698,11 @@ var (
Owner: grafanaDatavizSquad,
},
{
// this is mainly used as a way to quickly disable query hints as a safeguard for our infrastructure
Name: "lokiQueryHints",
Description: "Enables query hints for Loki",
Stage: FeatureStageGeneralAvailability,
Name: "regressionTransformation",
Description: "Enables regression analysis transformation",
Stage: FeatureStagePublicPreview,
FrontendOnly: true,
Expression: "true",
Owner: grafanaObservabilityLogsSquad,
AllowSelfServe: false,
Owner: grafanaDatavizSquad,
},
{
Name: "kubernetesFeatureToggles",

@ -92,7 +92,7 @@ logsInfiniteScrolling,GA,@grafana/observability-logs,false,false,true
logRowsPopoverMenu,GA,@grafana/observability-logs,false,false,true
pluginsSkipHostEnvVars,experimental,@grafana/plugins-platform-backend,false,false,false
tableSharedCrosshair,experimental,@grafana/dataviz-squad,false,false,true
lokiQueryHints,GA,@grafana/observability-logs,false,false,true
regressionTransformation,preview,@grafana/dataviz-squad,false,false,true
kubernetesFeatureToggles,experimental,@grafana/grafana-operator-experience-squad,false,false,true
cloudRBACRoles,preview,@grafana/identity-access-team,false,true,false
alertingQueryOptimization,GA,@grafana/alerting-squad,false,false,false

1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
92 logRowsPopoverMenu GA @grafana/observability-logs false false true
93 pluginsSkipHostEnvVars experimental @grafana/plugins-platform-backend false false false
94 tableSharedCrosshair experimental @grafana/dataviz-squad false false true
95 lokiQueryHints regressionTransformation GA preview @grafana/observability-logs @grafana/dataviz-squad false false true
96 kubernetesFeatureToggles experimental @grafana/grafana-operator-experience-squad false false true
97 cloudRBACRoles preview @grafana/identity-access-team false true false
98 alertingQueryOptimization GA @grafana/alerting-squad false false false

@ -379,9 +379,9 @@ const (
// Enables shared crosshair in table panel
FlagTableSharedCrosshair = "tableSharedCrosshair"
// FlagLokiQueryHints
// Enables query hints for Loki
FlagLokiQueryHints = "lokiQueryHints"
// FlagRegressionTransformation
// Enables regression analysis transformation
FlagRegressionTransformation = "regressionTransformation"
// FlagKubernetesFeatureToggles
// Use the kubernetes API for feature toggle management in the frontend

@ -1868,8 +1868,9 @@
{
"metadata": {
"name": "lokiQueryHints",
"resourceVersion": "1750434297879",
"creationTimestamp": "2023-12-18T20:43:16Z"
"resourceVersion": "1743693517832",
"creationTimestamp": "2023-12-18T20:43:16Z",
"deletionTimestamp": "2025-06-12T12:14:47Z"
},
"spec": {
"description": "Enables query hints for Loki",

@ -681,14 +681,6 @@ describe('Query imports', () => {
});
describe('getParserAndLabelKeys()', () => {
const queryHintsFeatureToggle = config.featureToggles.lokiQueryHints;
beforeAll(() => {
config.featureToggles.lokiQueryHints = true;
});
afterAll(() => {
config.featureToggles.lokiQueryHints = queryHintsFeatureToggle;
});
let datasource: LokiDatasource, languageProvider: LanguageProvider;
const extractLogParserFromDataFrameMock = jest.mocked(extractLogParserFromDataFrame);
const extractedLabelKeys = ['extracted', 'label'];
@ -810,12 +802,6 @@ describe('Query imports', () => {
mockTimeRange
);
});
it('does not call dataSample with feature toggle disabled', async () => {
config.featureToggles.lokiQueryHints = false;
jest.spyOn(datasource, 'getDataSamples');
languageProvider.getParserAndLabelKeys('{place="luna"}', { timeRange: mockTimeRange });
expect(datasource.getDataSamples).not.toHaveBeenCalled();
});
});
});

@ -477,9 +477,6 @@ export default class LokiLanguageProvider extends LanguageProvider {
hasLogfmt: false,
hasPack: false,
};
if (!config.featureToggles.lokiQueryHints) {
return empty;
}
const series = await this.datasource.getDataSamples(
{

@ -4,7 +4,6 @@ import { ComponentProps } from 'react';
import { DataSourceApi } from '@grafana/data';
import { QueryBuilderOperation, QueryBuilderOperationParamDef } from '@grafana/plugin-ui';
import { config } from '@grafana/runtime';
import { LokiDatasource } from '../../datasource';
import { createLokiDatasource } from '../../mocks/datasource';
@ -14,14 +13,6 @@ import { LokiOperationId } from '../types';
import { LabelParamEditor } from './LabelParamEditor';
describe('LabelParamEditor', () => {
const queryHintsFeatureToggle = config.featureToggles.lokiQueryHints;
beforeAll(() => {
config.featureToggles.lokiQueryHints = true;
});
afterAll(() => {
config.featureToggles.lokiQueryHints = queryHintsFeatureToggle;
});
it('shows label options', async () => {
const props = createProps({}, ['label1', 'label2']);
render(<LabelParamEditor {...props} />);

@ -2,7 +2,6 @@ import { render, screen, getAllByRole, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { dateTime } from '@grafana/data';
import { config } from '@grafana/runtime';
import { createLokiDatasource } from '../../mocks/datasource';
import { LokiOperationId, LokiVisualQuery } from '../types';
@ -40,14 +39,6 @@ const createDefaultProps = () => {
};
describe('LokiQueryBuilder', () => {
const originalLokiQueryHints = config.featureToggles.lokiQueryHints;
beforeEach(() => {
config.featureToggles.lokiQueryHints = true;
});
afterEach(() => {
config.featureToggles.lokiQueryHints = originalLokiQueryHints;
});
it('tries to load label names', async () => {
const props = createDefaultProps();
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);

@ -15,7 +15,6 @@ import {
QueryBuilderLabelFilter,
QueryBuilderOperation,
} from '@grafana/plugin-ui';
import { config } from '@grafana/runtime';
import { testIds } from '../../components/LokiQueryEditor';
import { LokiDatasource } from '../../datasource';
@ -126,7 +125,7 @@ export const LokiQueryBuilder = memo<Props>(({ datasource, query, onChange, onRu
(Math.abs(timeRange.to.valueOf() - prevTimeRange.to.valueOf()) > TIME_SPAN_TO_TRIGGER_SAMPLES ||
Math.abs(timeRange.from.valueOf() - prevTimeRange.from.valueOf()) > TIME_SPAN_TO_TRIGGER_SAMPLES);
const updateBasedOnChangedQuery = !isEqual(prevQuery, query);
if (config.featureToggles.lokiQueryHints && (updateBasedOnChangedTimeRange || updateBasedOnChangedQuery)) {
if (updateBasedOnChangedTimeRange || updateBasedOnChangedQuery) {
onGetSampleData().catch(console.error);
}
}, [datasource, query, timeRange, prevQuery, prevTimeRange]);

@ -1,4 +1,4 @@
import { render, screen, waitFor, findAllByRole } from '@testing-library/react';
import { render, screen, waitFor, findAllByRole, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createLokiDatasource } from '../../mocks/datasource';
@ -19,7 +19,9 @@ describe('LokiQueryBuilderContainer', () => {
};
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
await act(async () => {
render(<LokiQueryBuilderContainer {...props} />);
});
const selector = await screen.findByLabelText('selector');
expect(selector.textContent).toBe('{job="testjob"}');
await addOperation('Range functions', 'Rate');
@ -45,7 +47,9 @@ describe('LokiQueryBuilderContainer', () => {
props.datasource.languageProvider.fetchLabelValues = jest.fn().mockReturnValue(['grafana', 'loki']);
props.onChange = jest.fn();
await act(async () => {
render(<LokiQueryBuilderContainer {...props} />);
});
await userEvent.click(screen.getByLabelText('Add'));
const labels = screen.getByText(/Label filters/);
const selects = await findAllByRole(getSelectParent(labels)!, 'combobox');
@ -78,7 +82,9 @@ describe('LokiQueryBuilderContainer', () => {
showExplain: false,
};
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
await act(async () => {
render(<LokiQueryBuilderContainer {...props} />);
});
expect(screen.getByText('{')).toHaveClass('token punctuation');
expect(screen.getByText('"baz"')).toHaveClass('token label-value attr-value');
expect(screen.getByText('|')).toHaveClass('token pipe-operator operator');
@ -98,7 +104,9 @@ describe('LokiQueryBuilderContainer', () => {
};
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
await act(async () => {
render(<LokiQueryBuilderContainer {...props} />);
});
expect(screen.getAllByText('You have conflicting label filters')).toHaveLength(2);
});
@ -115,7 +123,9 @@ describe('LokiQueryBuilderContainer', () => {
};
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
await act(async () => {
render(<LokiQueryBuilderContainer {...props} />);
});
expect(screen.getByText('<')).toBeInTheDocument();
expect(screen.getByText('expr')).toBeInTheDocument();
expect(screen.getByText('>')).toBeInTheDocument();

@ -4,7 +4,6 @@ import { ComponentProps } from 'react';
import { DataFrame, DataSourceApi, FieldType, toDataFrame } from '@grafana/data';
import { QueryBuilderOperation, QueryBuilderOperationParamDef } from '@grafana/plugin-ui';
import { config } from '@grafana/runtime';
import { LokiDatasource } from '../../datasource';
import { createLokiDatasource } from '../../mocks/datasource';
@ -14,14 +13,6 @@ import { LokiOperationId } from '../types';
import { UnwrapParamEditor } from './UnwrapParamEditor';
describe('UnwrapParamEditor', () => {
const queryHintsFeatureToggle = config.featureToggles.lokiQueryHints;
beforeAll(() => {
config.featureToggles.lokiQueryHints = true;
});
afterAll(() => {
config.featureToggles.lokiQueryHints = queryHintsFeatureToggle;
});
it('shows value if value present', () => {
const props = createProps({ value: 'unique' });
render(<UnwrapParamEditor {...props} />);
@ -60,16 +51,6 @@ describe('UnwrapParamEditor', () => {
expect(await screen.findByText('status')).toBeInTheDocument();
expect(await screen.findByText('duration')).toBeInTheDocument();
});
it('does not show labels with unwrap-friendly values when feature is disabled', async () => {
config.featureToggles.lokiQueryHints = false;
const props = createProps({}, frames);
render(<UnwrapParamEditor {...props} />);
const input = screen.getByRole('combobox');
await userEvent.click(input);
expect(screen.queryByText('status')).not.toBeInTheDocument();
expect(screen.queryByText('duration')).not.toBeInTheDocument();
});
});
const createProps = (

@ -2,7 +2,6 @@ import { useState } from 'react';
import { SelectableValue, getDefaultTimeRange, toOption } from '@grafana/data';
import { QueryBuilderOperationParamEditorProps, VisualQueryModeller } from '@grafana/plugin-ui';
import { config } from '@grafana/runtime';
import { Select } from '@grafana/ui';
import { placeHolderScopedVars } from '../../components/monaco-query-field/monaco-completion-provider/validation';
@ -32,7 +31,7 @@ export function UnwrapParamEditor({
inputId={getOperationParamId(operationId, index)}
onOpenMenu={async () => {
// This check is always true, we do it to make typescript happy
if (datasource instanceof LokiDatasource && config.featureToggles.lokiQueryHints) {
if (datasource instanceof LokiDatasource) {
setState({ isLoading: true });
const options = await loadUnwrapOptions(query, datasource, queryModeller, timeRange);
setState({ options, isLoading: undefined });

Loading…
Cancel
Save