mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Update labels in log browser when time range changes (#37520)
* Refresh label when time range changes * Refactor solution * Add tests * Update tests comments * Update test * Update public/app/plugins/datasource/loki/components/LokiQueryField.tsx * Update public/app/plugins/datasource/loki/components/LokiQueryField.tsx
This commit is contained in:
@@ -11,7 +11,7 @@ import { LokiOptionFields } from './LokiOptionFields';
|
|||||||
type Props = ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions>;
|
type Props = ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions>;
|
||||||
|
|
||||||
export function LokiExploreQueryEditor(props: Props) {
|
export function LokiExploreQueryEditor(props: Props) {
|
||||||
const { query, data, datasource, history, onChange, onRunQuery } = props;
|
const { query, data, datasource, history, onChange, onRunQuery, range } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LokiQueryField
|
<LokiQueryField
|
||||||
@@ -22,6 +22,7 @@ export function LokiExploreQueryEditor(props: Props) {
|
|||||||
onRunQuery={onRunQuery}
|
onRunQuery={onRunQuery}
|
||||||
history={history}
|
history={history}
|
||||||
data={data}
|
data={data}
|
||||||
|
range={range}
|
||||||
ExtraFieldElement={
|
ExtraFieldElement={
|
||||||
<LokiOptionFields
|
<LokiOptionFields
|
||||||
queryType={query.instant ? 'instant' : 'range'}
|
queryType={query.instant ? 'instant' : 'range'}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import React, { ComponentProps } from 'react';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { LokiQueryField } from './LokiQueryField';
|
||||||
|
import { dateTime } from '@grafana/data';
|
||||||
|
|
||||||
|
type Props = ComponentProps<typeof LokiQueryField>;
|
||||||
|
|
||||||
|
const defaultProps: Props = {
|
||||||
|
datasource: {
|
||||||
|
languageProvider: {
|
||||||
|
start: () => Promise.resolve(['label1']),
|
||||||
|
fetchLabels: Promise.resolve(['label1']),
|
||||||
|
getSyntax: () => {},
|
||||||
|
getLabelKeys: () => ['label1'],
|
||||||
|
getLabelValues: () => ['value1'],
|
||||||
|
} as any,
|
||||||
|
getInitHints: () => [],
|
||||||
|
} as any,
|
||||||
|
range: {
|
||||||
|
from: dateTime([2021, 1, 11, 12, 0, 0]),
|
||||||
|
to: dateTime([2021, 1, 11, 18, 0, 0]),
|
||||||
|
raw: {
|
||||||
|
from: 'now-1h',
|
||||||
|
to: 'now',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
query: { expr: '', refId: '' },
|
||||||
|
onRunQuery: () => {},
|
||||||
|
onChange: () => {},
|
||||||
|
history: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('LokiQueryField', () => {
|
||||||
|
it('refreshes metrics when time range changes over 1 minute', async () => {
|
||||||
|
const fetchLabelsMock = jest.fn();
|
||||||
|
const props = defaultProps;
|
||||||
|
props.datasource.languageProvider.fetchLabels = fetchLabelsMock;
|
||||||
|
|
||||||
|
const { rerender } = render(<LokiQueryField {...props} />);
|
||||||
|
|
||||||
|
expect(fetchLabelsMock).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 2 minutes difference over the initial time
|
||||||
|
const newRange = {
|
||||||
|
from: dateTime([2021, 1, 11, 12, 2, 0]),
|
||||||
|
to: dateTime([2021, 1, 11, 18, 2, 0]),
|
||||||
|
raw: {
|
||||||
|
from: 'now-1h',
|
||||||
|
to: 'now',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
rerender(<LokiQueryField {...props} range={newRange} />);
|
||||||
|
expect(fetchLabelsMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not refreshes metrics when time range change by less than 1 minute', async () => {
|
||||||
|
const fetchLabelsMock = jest.fn();
|
||||||
|
const props = defaultProps;
|
||||||
|
props.datasource.languageProvider.fetchLabels = fetchLabelsMock;
|
||||||
|
|
||||||
|
const { rerender } = render(<LokiQueryField {...props} />);
|
||||||
|
|
||||||
|
expect(fetchLabelsMock).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 20 seconds difference over the initial time
|
||||||
|
const newRange = {
|
||||||
|
from: dateTime([2021, 1, 11, 12, 0, 20]),
|
||||||
|
to: dateTime([2021, 1, 11, 18, 0, 20]),
|
||||||
|
raw: {
|
||||||
|
from: 'now-1h',
|
||||||
|
to: 'now',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
rerender(<LokiQueryField {...props} range={newRange} />);
|
||||||
|
expect(fetchLabelsMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
// Libraries
|
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SlatePrism,
|
SlatePrism,
|
||||||
TypeaheadOutput,
|
TypeaheadOutput,
|
||||||
@@ -11,17 +9,13 @@ import {
|
|||||||
DOMUtil,
|
DOMUtil,
|
||||||
Icon,
|
Icon,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
|
||||||
// Utils & Services
|
|
||||||
// dom also includes Element polyfills
|
|
||||||
import { Plugin, Node } from 'slate';
|
import { Plugin, Node } from 'slate';
|
||||||
import { LokiLabelBrowser } from './LokiLabelBrowser';
|
import { LokiLabelBrowser } from './LokiLabelBrowser';
|
||||||
|
|
||||||
// Types
|
|
||||||
import { ExploreQueryFieldProps } from '@grafana/data';
|
import { ExploreQueryFieldProps } from '@grafana/data';
|
||||||
import { LokiQuery, LokiOptions } from '../types';
|
import { LokiQuery, LokiOptions } from '../types';
|
||||||
import { LanguageMap, languages as prismLanguages } from 'prismjs';
|
import { LanguageMap, languages as prismLanguages } from 'prismjs';
|
||||||
import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider';
|
import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider';
|
||||||
|
import { shouldRefreshLabels } from '../language_utils';
|
||||||
import LokiDatasource from '../datasource';
|
import LokiDatasource from '../datasource';
|
||||||
|
|
||||||
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean) {
|
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean) {
|
||||||
@@ -98,6 +92,18 @@ export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, Lok
|
|||||||
this.setState({ labelsLoaded: true });
|
this.setState({ labelsLoaded: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: LokiQueryFieldProps) {
|
||||||
|
const {
|
||||||
|
range,
|
||||||
|
datasource: { languageProvider },
|
||||||
|
} = this.props;
|
||||||
|
const refreshLabels = shouldRefreshLabels(range, prevProps.range);
|
||||||
|
// We want to refresh labels when range changes (we round up intervals to a minute)
|
||||||
|
if (refreshLabels) {
|
||||||
|
languageProvider.fetchLabels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onChangeLabelBrowser = (selector: string) => {
|
onChangeLabelBrowser = (selector: string) => {
|
||||||
this.onChangeQuery(selector, true);
|
this.onChangeQuery(selector, true);
|
||||||
this.setState({ labelBrowserVisible: false });
|
this.setState({ labelBrowserVisible: false });
|
||||||
|
|||||||
@@ -115,5 +115,15 @@ exports[`LokiExploreQueryEditor should render component 1`] = `
|
|||||||
"refId": "A",
|
"refId": "A",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
range={
|
||||||
|
Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"raw": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
|||||||
19
public/app/plugins/datasource/loki/language_utils.ts
Normal file
19
public/app/plugins/datasource/loki/language_utils.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { TimeRange } from '@grafana/data';
|
||||||
|
|
||||||
|
function roundMsToMin(milliseconds: number): number {
|
||||||
|
return roundSecToMin(milliseconds / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function roundSecToMin(seconds: number): number {
|
||||||
|
return Math.floor(seconds / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldRefreshLabels(range?: TimeRange, prevRange?: TimeRange): boolean {
|
||||||
|
if (range && prevRange) {
|
||||||
|
const sameMinuteFrom = roundMsToMin(range.from.valueOf()) === roundMsToMin(prevRange.from.valueOf());
|
||||||
|
const sameMinuteTo = roundMsToMin(range.to.valueOf()) === roundMsToMin(prevRange.to.valueOf());
|
||||||
|
// If both are same, don't need to refresh
|
||||||
|
return !(sameMinuteFrom && sameMinuteTo);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user