ソースを参照

Fix history rendering for DataQuery

David Kaltschmidt 7 年 前
コミット
bbaa5b63c8

+ 44 - 1
public/app/core/utils/explore.test.ts

@@ -1,5 +1,13 @@
-import { DEFAULT_RANGE, serializeStateToUrlParam, parseUrlState } from './explore';
+import {
+  DEFAULT_RANGE,
+  serializeStateToUrlParam,
+  parseUrlState,
+  updateHistory,
+  clearHistory,
+  hasNonEmptyQuery,
+} from './explore';
 import { ExploreState } from 'app/types/explore';
+import store from 'app/core/store';
 
 const DEFAULT_EXPLORE_STATE: ExploreState = {
   datasource: null,
@@ -144,3 +152,38 @@ describe('state functions', () => {
     });
   });
 });
+
+describe('updateHistory()', () => {
+  const datasourceId = 'myDatasource';
+  const key = `grafana.explore.history.${datasourceId}`;
+
+  beforeEach(() => {
+    clearHistory(datasourceId);
+    expect(store.exists(key)).toBeFalsy();
+  });
+
+  test('should save history item to localStorage', () => {
+    const expected = [
+      {
+        query: { refId: '1', expr: 'metric' },
+      },
+    ];
+    expect(updateHistory([], datasourceId, [{ refId: '1', expr: 'metric' }])).toMatchObject(expected);
+    expect(store.exists(key)).toBeTruthy();
+    expect(store.getObject(key)).toMatchObject(expected);
+  });
+});
+
+describe('hasNonEmptyQuery', () => {
+  test('should return true if one query is non-empty', () => {
+    expect(hasNonEmptyQuery([{ refId: '1', key: '2', expr: 'foo' }])).toBeTruthy();
+  });
+
+  test('should return false if query is empty', () => {
+    expect(hasNonEmptyQuery([{ refId: '1', key: '2' }])).toBeFalsy();
+  });
+
+  test('should return false if no queries exist', () => {
+    expect(hasNonEmptyQuery([])).toBeFalsy();
+  });
+});

+ 8 - 1
public/app/core/utils/explore.ts

@@ -66,6 +66,8 @@ export async function getExploreUrl(
   return url;
 }
 
+const clearQueryKeys: ((query: DataQuery) => object) = ({ key, refId, ...rest }) => rest;
+
 export function parseUrlState(initial: string | undefined): ExploreUrlState {
   if (initial) {
     try {
@@ -93,7 +95,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
 export function serializeStateToUrlParam(state: ExploreState, compact?: boolean): string {
   const urlState: ExploreUrlState = {
     datasource: state.datasourceName,
-    queries: state.initialQueries.map(({ key, refId, ...rest }) => rest),
+    queries: state.initialQueries.map(clearQueryKeys),
     range: state.range,
   };
   if (compact) {
@@ -182,3 +184,8 @@ export function updateHistory(history: HistoryItem[], datasourceId: string, quer
   store.setObject(historyKey, history);
   return history;
 }
+
+export function clearHistory(datasourceId: string) {
+  const historyKey = `grafana.explore.history.${datasourceId}`;
+  store.delete(historyKey);
+}

+ 31 - 6
public/app/plugins/datasource/logging/language_provider.test.ts

@@ -7,12 +7,37 @@ describe('Language completion provider', () => {
     metadataRequest: () => ({ data: { data: [] } }),
   };
 
-  it('returns default suggestions on emtpty context', () => {
-    const instance = new LanguageProvider(datasource);
-    const result = instance.provideCompletionItems({ text: '', prefix: '', wrapperClasses: [] });
-    expect(result.context).toBeUndefined();
-    expect(result.refresher).toBeUndefined();
-    expect(result.suggestions.length).toEqual(0);
+  describe('empty query suggestions', () => {
+    it('returns default suggestions on emtpty context', () => {
+      const instance = new LanguageProvider(datasource);
+      const result = instance.provideCompletionItems({ text: '', prefix: '', wrapperClasses: [] });
+      expect(result.context).toBeUndefined();
+      expect(result.refresher).toBeUndefined();
+      expect(result.suggestions.length).toEqual(0);
+    });
+
+    it('returns default suggestions with history on emtpty context when history was provided', () => {
+      const instance = new LanguageProvider(datasource);
+      const value = Plain.deserialize('');
+      const history = [
+        {
+          query: { refId: '1', expr: '{app="foo"}' },
+        },
+      ];
+      const result = instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] }, { history });
+      expect(result.context).toBeUndefined();
+      expect(result.refresher).toBeUndefined();
+      expect(result.suggestions).toMatchObject([
+        {
+          label: 'History',
+          items: [
+            {
+              label: '{app="foo"}',
+            },
+          ],
+        },
+      ]);
+    });
   });
 
   describe('label suggestions', () => {

+ 5 - 4
public/app/plugins/datasource/logging/language_provider.ts

@@ -7,6 +7,7 @@ import {
   LanguageProvider,
   TypeaheadInput,
   TypeaheadOutput,
+  HistoryItem,
 } from 'app/types/explore';
 import { parseSelector, labelRegexp, selectorRegexp } from 'app/plugins/datasource/prometheus/language_utils';
 import PromqlSyntax from 'app/plugins/datasource/prometheus/promql';
@@ -19,9 +20,9 @@ const HISTORY_COUNT_CUTOFF = 1000 * 60 * 60 * 24; // 24h
 
 const wrapLabel = (label: string) => ({ label });
 
-export function addHistoryMetadata(item: CompletionItem, history: any[]): CompletionItem {
+export function addHistoryMetadata(item: CompletionItem, history: HistoryItem[]): CompletionItem {
   const cutoffTs = Date.now() - HISTORY_COUNT_CUTOFF;
-  const historyForItem = history.filter(h => h.ts > cutoffTs && h.query === item.label);
+  const historyForItem = history.filter(h => h.ts > cutoffTs && (h.query.expr as string) === item.label);
   const count = historyForItem.length;
   const recent = historyForItem[0];
   let hint = `Queried ${count} times in the last 24h.`;
@@ -96,9 +97,9 @@ export default class LoggingLanguageProvider extends LanguageProvider {
 
     if (history && history.length > 0) {
       const historyItems = _.chain(history)
-        .uniqBy('query')
+        .uniqBy('query.expr')
         .take(HISTORY_ITEM_COUNT)
-        .map(h => h.query)
+        .map(h => h.query.expr)
         .map(wrapLabel)
         .map(item => addHistoryMetadata(item, history))
         .value();

+ 2 - 2
public/app/plugins/datasource/prometheus/language_provider.ts

@@ -125,9 +125,9 @@ export default class PromQlLanguageProvider extends LanguageProvider {
 
     if (history && history.length > 0) {
       const historyItems = _.chain(history)
-        .uniqBy('query')
+        .uniqBy('query.expr')
         .take(HISTORY_ITEM_COUNT)
-        .map(h => h.query)
+        .map(h => h.query.expr)
         .map(wrapLabel)
         .map(item => addHistoryMetadata(item, history))
         .value();

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

@@ -36,6 +36,32 @@ describe('Language completion provider', () => {
         },
       ]);
     });
+
+    it('returns default suggestions with history on emtpty context when history was provided', () => {
+      const instance = new LanguageProvider(datasource);
+      const value = Plain.deserialize('');
+      const history = [
+        {
+          query: { refId: '1', expr: 'metric' },
+        },
+      ];
+      const result = instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] }, { history });
+      expect(result.context).toBeUndefined();
+      expect(result.refresher).toBeUndefined();
+      expect(result.suggestions).toMatchObject([
+        {
+          label: 'History',
+          items: [
+            {
+              label: 'metric',
+            },
+          ],
+        },
+        {
+          label: 'Functions',
+        },
+      ]);
+    });
   });
 
   describe('range suggestions', () => {