mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Fix typehead after binary operators (#21152)
- no longer do right-trim to prevent suggestions after space - rely on splitting on syntax elements to identify relevant prefix - added tests
This commit is contained in:
parent
ff422be961
commit
34c1433ba4
@ -12,6 +12,56 @@ describe('Language completion provider', () => {
|
||||
getTimeRange: () => ({ start: 0, end: 1 }),
|
||||
} as any) as PrometheusDatasource;
|
||||
|
||||
describe('cleanText', () => {
|
||||
const cleanText = new LanguageProvider(datasource).cleanText;
|
||||
it('does not remove metric or label keys', () => {
|
||||
expect(cleanText('foo')).toBe('foo');
|
||||
expect(cleanText('foo_bar')).toBe('foo_bar');
|
||||
});
|
||||
|
||||
it('keeps trailing space but removes leading', () => {
|
||||
expect(cleanText('foo ')).toBe('foo ');
|
||||
expect(cleanText(' foo')).toBe('foo');
|
||||
});
|
||||
|
||||
it('removes label syntax', () => {
|
||||
expect(cleanText('foo="bar')).toBe('bar');
|
||||
expect(cleanText('foo!="bar')).toBe('bar');
|
||||
expect(cleanText('foo=~"bar')).toBe('bar');
|
||||
expect(cleanText('foo!~"bar')).toBe('bar');
|
||||
expect(cleanText('{bar')).toBe('bar');
|
||||
});
|
||||
|
||||
it('removes previous operators', () => {
|
||||
expect(cleanText('foo + bar')).toBe('bar');
|
||||
expect(cleanText('foo+bar')).toBe('bar');
|
||||
expect(cleanText('foo - bar')).toBe('bar');
|
||||
expect(cleanText('foo * bar')).toBe('bar');
|
||||
expect(cleanText('foo / bar')).toBe('bar');
|
||||
expect(cleanText('foo % bar')).toBe('bar');
|
||||
expect(cleanText('foo ^ bar')).toBe('bar');
|
||||
expect(cleanText('foo and bar')).toBe('bar');
|
||||
expect(cleanText('foo or bar')).toBe('bar');
|
||||
expect(cleanText('foo unless bar')).toBe('bar');
|
||||
expect(cleanText('foo == bar')).toBe('bar');
|
||||
expect(cleanText('foo != bar')).toBe('bar');
|
||||
expect(cleanText('foo > bar')).toBe('bar');
|
||||
expect(cleanText('foo < bar')).toBe('bar');
|
||||
expect(cleanText('foo >= bar')).toBe('bar');
|
||||
expect(cleanText('foo <= bar')).toBe('bar');
|
||||
});
|
||||
|
||||
it('removes aggregation syntax', () => {
|
||||
expect(cleanText('(bar')).toBe('bar');
|
||||
expect(cleanText('(foo,bar')).toBe('bar');
|
||||
expect(cleanText('(foo, bar')).toBe('bar');
|
||||
});
|
||||
|
||||
it('removes range syntax', () => {
|
||||
expect(cleanText('[1m')).toBe('1m');
|
||||
});
|
||||
});
|
||||
|
||||
describe('empty query suggestions', () => {
|
||||
it('returns no suggestions on empty context', async () => {
|
||||
const instance = new LanguageProvider(datasource);
|
||||
@ -96,8 +146,9 @@ describe('Language completion provider', () => {
|
||||
query: { refId: '1', expr: 'metric' },
|
||||
},
|
||||
];
|
||||
let value = Plain.deserialize('a');
|
||||
let value = Plain.deserialize('m');
|
||||
value = value.setSelection({ anchor: { offset: 1 }, focus: { offset: 1 } });
|
||||
// Even though no metric with `m` is present, we still get metric completion items, filtering is done by the consumer
|
||||
const result = await instance.provideCompletionItems(
|
||||
{ text: 'm', prefix: 'm', value, wrapperClasses: [] },
|
||||
{ history }
|
||||
|
@ -1,5 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import LRU from 'lru-cache';
|
||||
import { Value } from 'slate';
|
||||
|
||||
import { dateTime, LanguageProvider, HistoryItem } from '@grafana/data';
|
||||
import { CompletionItem, TypeaheadInput, TypeaheadOutput, CompletionItemGroup } from '@grafana/ui';
|
||||
@ -50,6 +51,8 @@ function addMetricsMetadata(metric: string, metadata?: PromMetricsMetadata): Com
|
||||
return item;
|
||||
}
|
||||
|
||||
const PREFIX_DELIMITER_REGEX = /(="|!="|=~"|!~"|\{|\[|\(|\+|-|\/|\*|%|\^|and|or|unless|==|>=|!=|<=|>|<|=|~|,)/;
|
||||
|
||||
export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
histogramMetrics?: string[];
|
||||
timeRange?: { start: number; end: number };
|
||||
@ -81,15 +84,12 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
Object.assign(this, initialValues);
|
||||
}
|
||||
|
||||
// Strip syntax chars
|
||||
cleanText = (s: string) =>
|
||||
s
|
||||
.replace(/[{}[\]="(),!]/g, '')
|
||||
.replace(/^\s*[~+\-*/^%]/, '')
|
||||
.trim()
|
||||
.split(' ')
|
||||
.pop()
|
||||
.trim();
|
||||
// Strip syntax chars so that typeahead suggestions can work on clean inputs
|
||||
cleanText(s: string) {
|
||||
const parts = s.split(PREFIX_DELIMITER_REGEX);
|
||||
const last = parts.pop();
|
||||
return last.trimLeft().replace(/"$/, '');
|
||||
}
|
||||
|
||||
get syntax() {
|
||||
return PromqlSyntax;
|
||||
@ -159,7 +159,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
return this.getLabelCompletionItems({ prefix, text, value, labelKey, wrapperClasses });
|
||||
} else if (wrapperClasses.includes('context-aggregation')) {
|
||||
// Suggestions for sum(metric) by (|)
|
||||
return this.getAggregationCompletionItems({ prefix, text, value, labelKey, wrapperClasses });
|
||||
return this.getAggregationCompletionItems(value);
|
||||
} else if (empty) {
|
||||
// Suggestions for empty query field
|
||||
return this.getEmptyCompletionItems(context);
|
||||
@ -239,7 +239,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
};
|
||||
}
|
||||
|
||||
getAggregationCompletionItems = async ({ value }: TypeaheadInput): Promise<TypeaheadOutput> => {
|
||||
getAggregationCompletionItems = async (value: Value): Promise<TypeaheadOutput> => {
|
||||
const suggestions: CompletionItemGroup[] = [];
|
||||
|
||||
// Stitch all query lines together to support multi-line queries
|
||||
|
Loading…
Reference in New Issue
Block a user