Browse Source

Merge pull request #13847 from miqh/fix/multi-line-aggregation

Fix label suggestions inside multi-line aggregation contexts
David 7 năm trước cách đây
mục cha
commit
121ac93d08

+ 20 - 7
public/app/plugins/datasource/prometheus/language_provider.ts

@@ -162,16 +162,29 @@ export default class PromQlLanguageProvider extends LanguageProvider {
     let refresher: Promise<any> = null;
     const suggestions: CompletionItemGroup[] = [];
 
-    // sum(foo{bar="1"}) by (|)
-    const line = value.anchorBlock.getText();
-    const cursorOffset: number = value.anchorOffset;
-    // sum(foo{bar="1"}) by (
-    const leftSide = line.slice(0, cursorOffset);
+    // Stitch all query lines together to support multi-line queries
+    let queryOffset;
+    const queryText = value.document.getBlocks().reduce((text, block) => {
+      const blockText = block.getText();
+      if (value.anchorBlock.key === block.key) {
+        // Newline characters are not accounted for but this is irrelevant
+        // for the purpose of extracting the selector string
+        queryOffset = value.anchorOffset + text.length;
+      }
+      text += blockText;
+      return text;
+    }, '');
+
+    const leftSide = queryText.slice(0, queryOffset);
     const openParensAggregationIndex = leftSide.lastIndexOf('(');
     const openParensSelectorIndex = leftSide.slice(0, openParensAggregationIndex).lastIndexOf('(');
     const closeParensSelectorIndex = leftSide.slice(openParensSelectorIndex).indexOf(')') + openParensSelectorIndex;
-    // foo{bar="1"}
-    const selectorString = leftSide.slice(openParensSelectorIndex + 1, closeParensSelectorIndex);
+
+    let selectorString = leftSide.slice(openParensSelectorIndex + 1, closeParensSelectorIndex);
+
+    // Range vector syntax not accounted for by subsequent parse so discard it if present
+    selectorString = selectorString.replace(/\[[^\]]+\]$/, '');
+
     const selector = parseSelector(selectorString, selectorString.length - 2).selector;
 
     const labelKeys = this.labelKeys[selector];

+ 71 - 0
public/app/plugins/datasource/prometheus/specs/language_provider.test.ts

@@ -198,5 +198,76 @@ describe('Language completion provider', () => {
       expect(result.context).toBe('context-aggregation');
       expect(result.suggestions).toEqual([{ items: [{ label: 'bar' }], label: 'Labels' }]);
     });
+
+    it('returns label suggestions inside a multi-line aggregation context', () => {
+      const instance = new LanguageProvider(datasource, {
+        labelKeys: { '{__name__="metric"}': ['label1', 'label2', 'label3'] },
+      });
+      const value = Plain.deserialize('sum(\nmetric\n)\nby ()');
+      const aggregationTextBlock = value.document.getBlocksAsArray()[3];
+      const range = value.selection.moveToStartOf(aggregationTextBlock).merge({ anchorOffset: 4 });
+      const valueWithSelection = value.change().select(range).value;
+      const result = instance.provideCompletionItems({
+        text: '',
+        prefix: '',
+        wrapperClasses: ['context-aggregation'],
+        value: valueWithSelection,
+      });
+      expect(result.context).toBe('context-aggregation');
+      expect(result.suggestions).toEqual([
+        {
+          items: [{ label: 'label1' }, { label: 'label2' }, { label: 'label3' }],
+          label: 'Labels',
+        },
+      ]);
+    });
+
+    it('returns label suggestions inside an aggregation context with a range vector', () => {
+      const instance = new LanguageProvider(datasource, {
+        labelKeys: { '{__name__="metric"}': ['label1', 'label2', 'label3'] },
+      });
+      const value = Plain.deserialize('sum(rate(metric[1h])) by ()');
+      const range = value.selection.merge({
+        anchorOffset: 26,
+      });
+      const valueWithSelection = value.change().select(range).value;
+      const result = instance.provideCompletionItems({
+        text: '',
+        prefix: '',
+        wrapperClasses: ['context-aggregation'],
+        value: valueWithSelection,
+      });
+      expect(result.context).toBe('context-aggregation');
+      expect(result.suggestions).toEqual([
+        {
+          items: [{ label: 'label1' }, { label: 'label2' }, { label: 'label3' }],
+          label: 'Labels',
+        },
+      ]);
+    });
+
+    it('returns label suggestions inside an aggregation context with a range vector and label', () => {
+      const instance = new LanguageProvider(datasource, {
+        labelKeys: { '{__name__="metric",label1="value"}': ['label1', 'label2', 'label3'] },
+      });
+      const value = Plain.deserialize('sum(rate(metric{label1="value"}[1h])) by ()');
+      const range = value.selection.merge({
+        anchorOffset: 42,
+      });
+      const valueWithSelection = value.change().select(range).value;
+      const result = instance.provideCompletionItems({
+        text: '',
+        prefix: '',
+        wrapperClasses: ['context-aggregation'],
+        value: valueWithSelection,
+      });
+      expect(result.context).toBe('context-aggregation');
+      expect(result.suggestions).toEqual([
+        {
+          items: [{ label: 'label1' }, { label: 'label2' }, { label: 'label3' }],
+          label: 'Labels',
+        },
+      ]);
+    });
   });
 });