Loki: Don't request stats with invalid query (#65287)

* fix: dont call endpoint if invalid query

* fix: use latest lezer-logql

* refactor: use null instead of undefined

* test: returns null when query is invalid

* update
pull/66780/head
Gareth Dawson 2 years ago committed by GitHub
parent 98c695c68f
commit 13c5aadd5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx
  2. 4
      public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx
  3. 13
      public/app/plugins/datasource/loki/components/stats.test.ts
  4. 11
      public/app/plugins/datasource/loki/components/stats.ts
  5. 7
      public/app/plugins/datasource/loki/datasource.ts
  6. 2
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx
  7. 2
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryCodeEditor.tsx
  8. 2
      public/app/plugins/datasource/prometheus/querybuilder/shared/QueryOptionGroup.tsx

@ -34,7 +34,7 @@ export const LokiQueryEditor = React.memo<LokiQueryEditorProps>((props) => {
const [queryPatternsModalOpen, setQueryPatternsModalOpen] = useState(false); const [queryPatternsModalOpen, setQueryPatternsModalOpen] = useState(false);
const [dataIsStale, setDataIsStale] = useState(false); const [dataIsStale, setDataIsStale] = useState(false);
const [labelBrowserVisible, setLabelBrowserVisible] = useState(false); const [labelBrowserVisible, setLabelBrowserVisible] = useState(false);
const [queryStats, setQueryStats] = useState<QueryStats>(); const [queryStats, setQueryStats] = useState<QueryStats | null>(null);
const { flag: explain, setFlag: setExplain } = useFlag(lokiQueryEditorExplainKey); const { flag: explain, setFlag: setExplain } = useFlag(lokiQueryEditorExplainKey);
const timerange = datasource.getTimeRange(); const timerange = datasource.getTimeRange();

@ -9,8 +9,6 @@ import { selectors } from '@grafana/e2e-selectors';
import { languageConfiguration, monarchlanguage } from '@grafana/monaco-logql'; import { languageConfiguration, monarchlanguage } from '@grafana/monaco-logql';
import { useTheme2, ReactMonacoEditor, Monaco, monacoTypes, MonacoEditor } from '@grafana/ui'; import { useTheme2, ReactMonacoEditor, Monaco, monacoTypes, MonacoEditor } from '@grafana/ui';
import { isValidQuery } from '../../queryUtils';
import { Props } from './MonacoQueryFieldProps'; import { Props } from './MonacoQueryFieldProps';
import { getOverrideServices } from './getOverrideServices'; import { getOverrideServices } from './getOverrideServices';
import { getCompletionProvider, getSuggestOptions } from './monaco-completion-provider'; import { getCompletionProvider, getSuggestOptions } from './monaco-completion-provider';
@ -153,7 +151,7 @@ const MonacoQueryField = ({
}; };
const onTypeDebounced = debounce(async (query: string) => { const onTypeDebounced = debounce(async (query: string) => {
if (!onQueryType || (isValidQuery(query) === false && query !== '')) { if (!onQueryType) {
return; return;
} }

@ -54,15 +54,20 @@ describe('shouldUpdateStats', () => {
describe('makeStatsRequest', () => { describe('makeStatsRequest', () => {
const datasource = createLokiDatasource(); const datasource = createLokiDatasource();
it('should return undefined if there is no query', () => { it('should return null if there is no query', () => {
const query = ''; const query = '';
expect(getStats(datasource, query)).resolves.toBe(undefined); // change expect(getStats(datasource, query)).resolves.toBe(null);
}); });
it('should return undefined if the response has no data', () => { it('should return null if the query is invalid', () => {
const query = '{job="grafana",';
expect(getStats(datasource, query)).resolves.toBe(null);
});
it('should return null if the response has no data', () => {
const query = '{job="grafana"}'; const query = '{job="grafana"}';
datasource.getQueryStats = jest.fn().mockResolvedValue({ streams: 0, chunks: 0, bytes: 0, entries: 0 }); datasource.getQueryStats = jest.fn().mockResolvedValue({ streams: 0, chunks: 0, bytes: 0, entries: 0 });
expect(getStats(datasource, query)).resolves.toBe(undefined); expect(getStats(datasource, query)).resolves.toBe(null);
}); });
it('should return the stats if the response has data', () => { it('should return the stats if the response has data', () => {

@ -3,13 +3,18 @@ import { DateTime, isDateTime, TimeRange } from '@grafana/data';
import { LokiDatasource } from '../datasource'; import { LokiDatasource } from '../datasource';
import { QueryStats } from '../types'; import { QueryStats } from '../types';
export async function getStats(datasource: LokiDatasource, query: string): Promise<QueryStats | undefined> { export async function getStats(datasource: LokiDatasource, query: string): Promise<QueryStats | null> {
if (!query) { if (!query) {
return undefined; return null;
} }
const response = await datasource.getQueryStats(query); const response = await datasource.getQueryStats(query);
return Object.values(response).every((v) => v === 0) ? undefined : response;
if (!response) {
return null;
}
return Object.values(response).every((v) => v === 0) ? null : response;
} }
/** /**

@ -419,7 +419,12 @@ export class LokiDatasource
return res.data ?? (res || []); return res.data ?? (res || []);
} }
async getQueryStats(query: string): Promise<QueryStats> { async getQueryStats(query: string): Promise<QueryStats | undefined> {
// if query is invalid, clear stats, and don't request
if (!isValidQuery(query)) {
return undefined;
}
const { start, end } = this.getTimeRangeParams(); const { start, end } = this.getTimeRangeParams();
const labelMatchers = getStreamSelectorsFromQuery(query); const labelMatchers = getStreamSelectorsFromQuery(query);

@ -18,7 +18,7 @@ export interface Props {
maxLines: number; maxLines: number;
app?: CoreApp; app?: CoreApp;
datasource: LokiDatasource; datasource: LokiDatasource;
queryStats: QueryStats | undefined; queryStats: QueryStats | null;
} }
export const LokiQueryBuilderOptions = React.memo<Props>( export const LokiQueryBuilderOptions = React.memo<Props>(

@ -14,7 +14,7 @@ import { LokiQueryBuilderExplained } from './LokiQueryBuilderExplained';
type Props = LokiQueryEditorProps & { type Props = LokiQueryEditorProps & {
showExplain: boolean; showExplain: boolean;
setQueryStats: React.Dispatch<React.SetStateAction<QueryStats | undefined>>; setQueryStats: React.Dispatch<React.SetStateAction<QueryStats | null>>;
}; };
export function LokiQueryCodeEditor({ export function LokiQueryCodeEditor({

@ -10,7 +10,7 @@ import { QueryStats } from 'app/plugins/datasource/loki/types';
export interface Props { export interface Props {
title: string; title: string;
collapsedInfo: string[]; collapsedInfo: string[];
queryStats?: QueryStats; queryStats?: QueryStats | null;
children: React.ReactNode; children: React.ReactNode;
} }

Loading…
Cancel
Save