Loki: Remove`TimeSrv` dependency (#78642)

* Loki: Remove  dependency

* Update loadUnwrapOptions
pull/71168/head^2
Ivana Huckova 2 years ago committed by GitHub
parent f7ace22285
commit b3deef8f4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      public/app/plugins/datasource/loki/LanguageProvider.test.ts
  2. 2
      public/app/plugins/datasource/loki/LanguageProvider.ts
  3. 34
      public/app/plugins/datasource/loki/datasource.test.ts
  4. 22
      public/app/plugins/datasource/loki/datasource.ts
  5. 7
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx
  6. 10
      public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx
  7. 5
      public/app/plugins/datasource/prometheus/querybuilder/shared/OperationEditor.tsx
  8. 5
      public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.tsx
  9. 3
      public/app/plugins/datasource/prometheus/querybuilder/shared/types.ts

@ -407,7 +407,11 @@ describe('Query imports', () => {
maxLines: DEFAULT_MAX_LINES_SAMPLE, maxLines: DEFAULT_MAX_LINES_SAMPLE,
refId: 'data-samples', refId: 'data-samples',
}, },
undefined // mocked default time range
expect.objectContaining({
from: 0,
to: 1,
})
); );
}); });
@ -428,7 +432,11 @@ describe('Query imports', () => {
maxLines: 5, maxLines: 5,
refId: 'data-samples', refId: 'data-samples',
}, },
undefined // mocked default time range
expect.objectContaining({
from: 0,
to: 1,
})
); );
}); });

@ -267,7 +267,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
refId: 'data-samples', refId: 'data-samples',
maxLines: options?.maxLines || DEFAULT_MAX_LINES_SAMPLE, maxLines: options?.maxLines || DEFAULT_MAX_LINES_SAMPLE,
}, },
options?.timeRange options?.timeRange ?? this.getDefaultTimeRange()
); );
if (!series.length) { if (!series.length) {

@ -1474,26 +1474,26 @@ describe('LokiDatasource', () => {
}); });
it('ignores invalid queries', () => { it('ignores invalid queries', () => {
const spy = jest.spyOn(ds, 'query'); const spy = jest.spyOn(ds, 'query');
ds.getDataSamples({ expr: 'not a query', refId: 'A' }); ds.getDataSamples({ expr: 'not a query', refId: 'A' }, mockTimeRange);
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
}); });
it('ignores metric queries', () => { it('ignores metric queries', () => {
const spy = jest.spyOn(ds, 'query'); const spy = jest.spyOn(ds, 'query');
ds.getDataSamples({ expr: 'count_over_time({a="b"}[1m])', refId: 'A' }); ds.getDataSamples({ expr: 'count_over_time({a="b"}[1m])', refId: 'A' }, mockTimeRange);
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
}); });
it('uses the current interval in the request', () => { it('uses the current interval in the request', () => {
const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse)); const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse));
ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }); ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }, mockTimeRange);
expect(spy).toHaveBeenCalledWith( expect(spy).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
range: ds.getTimeRange(), range: mockTimeRange,
}) })
); );
}); });
it('hides the request from the inspector', () => { it('hides the request from the inspector', () => {
const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse)); const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse));
ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }); ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }, mockTimeRange);
expect(spy).toHaveBeenCalledWith( expect(spy).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
hideFromInspector: true, hideFromInspector: true,
@ -1503,7 +1503,7 @@ describe('LokiDatasource', () => {
}); });
it('sets the supporting query type in the request', () => { it('sets the supporting query type in the request', () => {
const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse)); const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse));
ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }); ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }, mockTimeRange);
expect(spy).toHaveBeenCalledWith( expect(spy).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
targets: [expect.objectContaining({ supportingQueryType: SupportingQueryType.DataSample })], targets: [expect.objectContaining({ supportingQueryType: SupportingQueryType.DataSample })],
@ -1828,18 +1828,18 @@ describe('makeStatsRequest', () => {
}); });
}); });
describe('getTimeRange*()', () => { describe('getTimeRangeParams()', () => {
it('exposes the current time range', () => { it('turns time range into a Loki range parameters', () => {
const ds = createLokiDatasource(); const ds = createLokiDatasource();
const timeRange = ds.getTimeRange(); const range = {
from: dateTime(1524650400000),
expect(timeRange.from).toBeDefined(); to: dateTime(1524654000000),
expect(timeRange.to).toBeDefined(); raw: {
}); from: 'now-1h',
to: 'now',
it('exposes time range as params', () => { },
const ds = createLokiDatasource(); };
const params = ds.getTimeRangeParams(); const params = ds.getTimeRangeParams(range);
// Returns a very big integer, so we stringify it for the assertion // Returns a very big integer, so we stringify it for the assertion
expect(JSON.stringify(params)).toEqual('{"start":1524650400000000000,"end":1524654000000000000}'); expect(JSON.stringify(params)).toEqual('{"start":1524650400000000000,"end":1524654000000000000}');

@ -45,7 +45,6 @@ import {
import { Duration } from '@grafana/lezer-logql'; import { Duration } from '@grafana/lezer-logql';
import { BackendSrvRequest, config, DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import { BackendSrvRequest, config, DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema'; import { DataQuery } from '@grafana/schema';
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { queryLogsSample, queryLogsVolume } from '../../../features/logs/logsModel'; import { queryLogsSample, queryLogsVolume } from '../../../features/logs/logsModel';
import { getLogLevelFromKey } from '../../../features/logs/utils'; import { getLogLevelFromKey } from '../../../features/logs/utils';
@ -152,8 +151,7 @@ export class LokiDatasource
constructor( constructor(
private instanceSettings: DataSourceInstanceSettings<LokiOptions>, private instanceSettings: DataSourceInstanceSettings<LokiOptions>,
private readonly templateSrv: TemplateSrv = getTemplateSrv(), private readonly templateSrv: TemplateSrv = getTemplateSrv()
private readonly timeSrv: TimeSrv = getTimeSrv()
) { ) {
super(instanceSettings); super(instanceSettings);
@ -455,21 +453,12 @@ export class LokiDatasource
return query.expr; return query.expr;
} }
/**
* Retrieve the current time range.
* @returns The current time range as provided by the timeSrv.
*/
getTimeRange() {
return this.timeSrv.timeRange();
}
/** /**
* Given a time range, returns it as Loki parameters. * Given a time range, returns it as Loki parameters.
* @returns An object containing the start and end times in nanoseconds since the Unix epoch. * @returns An object containing the start and end times in nanoseconds since the Unix epoch.
*/ */
getTimeRangeParams(timeRange?: TimeRange) { getTimeRangeParams(timeRange: TimeRange) {
const range = timeRange ?? this.getTimeRange(); return { start: timeRange.from.valueOf() * NS_IN_MS, end: timeRange.to.valueOf() * NS_IN_MS };
return { start: range.from.valueOf() * NS_IN_MS, end: range.to.valueOf() * NS_IN_MS };
} }
/** /**
@ -734,7 +723,7 @@ export class LokiDatasource
* Currently, it works for logs data only. * Currently, it works for logs data only.
* @returns A Promise that resolves to an array of DataFrames containing data samples. * @returns A Promise that resolves to an array of DataFrames containing data samples.
*/ */
async getDataSamples(query: LokiQuery, timeRange?: TimeRange): Promise<DataFrame[]> { async getDataSamples(query: LokiQuery, timeRange: TimeRange): Promise<DataFrame[]> {
// Currently works only for logs sample // Currently works only for logs sample
if (!isLogsQuery(query.expr) || isQueryWithError(this.interpolateString(query.expr, placeHolderScopedVars))) { if (!isLogsQuery(query.expr) || isQueryWithError(this.interpolateString(query.expr, placeHolderScopedVars))) {
return []; return [];
@ -748,8 +737,7 @@ export class LokiDatasource
supportingQueryType: SupportingQueryType.DataSample, supportingQueryType: SupportingQueryType.DataSample,
}; };
const range = timeRange ?? this.getTimeRange(); const request = makeRequest(lokiLogsQuery, timeRange, CoreApp.Unknown, REF_ID_DATA_SAMPLES, true);
const request = makeRequest(lokiLogsQuery, range, CoreApp.Unknown, REF_ID_DATA_SAMPLES, true);
return await lastValueFrom(this.query(request).pipe(switchMap((res) => of(res.data)))); return await lastValueFrom(this.query(request).pipe(switchMap((res) => of(res.data))));
} }

@ -99,13 +99,14 @@ export const LokiQueryBuilder = React.memo<Props>(
useEffect(() => { useEffect(() => {
const onGetSampleData = async () => { const onGetSampleData = async () => {
const lokiQuery = { expr: lokiQueryModeller.renderQuery(query), refId: 'data-samples' }; const lokiQuery = { expr: lokiQueryModeller.renderQuery(query), refId: 'data-samples' };
const series = await datasource.getDataSamples(lokiQuery); const range = timeRange ?? getDefaultTimeRange();
const sampleData = { series, state: LoadingState.Done, timeRange: getDefaultTimeRange() }; const series = await datasource.getDataSamples(lokiQuery, range);
const sampleData = { series, state: LoadingState.Done, timeRange: range };
setSampleData(sampleData); setSampleData(sampleData);
}; };
onGetSampleData().catch(console.error); onGetSampleData().catch(console.error);
}, [datasource, query]); }, [datasource, query, timeRange]);
const lang = { grammar: logqlGrammar, name: 'logql' }; const lang = { grammar: logqlGrammar, name: 'logql' };
return ( return (

@ -1,6 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { SelectableValue, toOption } from '@grafana/data'; import { SelectableValue, getDefaultTimeRange, toOption } from '@grafana/data';
import { Select } from '@grafana/ui'; import { Select } from '@grafana/ui';
import { getOperationParamId } from '../../../prometheus/querybuilder/shared/operationUtils'; import { getOperationParamId } from '../../../prometheus/querybuilder/shared/operationUtils';
@ -19,6 +19,7 @@ export function UnwrapParamEditor({
value, value,
query, query,
datasource, datasource,
timeRange,
}: QueryBuilderOperationParamEditorProps) { }: QueryBuilderOperationParamEditorProps) {
const [state, setState] = useState<{ const [state, setState] = useState<{
options?: Array<SelectableValue<string>>; options?: Array<SelectableValue<string>>;
@ -32,7 +33,7 @@ export function UnwrapParamEditor({
// This check is always true, we do it to make typescript happy // This check is always true, we do it to make typescript happy
if (datasource instanceof LokiDatasource) { if (datasource instanceof LokiDatasource) {
setState({ isLoading: true }); setState({ isLoading: true });
const options = await loadUnwrapOptions(query, datasource); const options = await loadUnwrapOptions(query, datasource, timeRange);
setState({ options, isLoading: undefined }); setState({ options, isLoading: undefined });
} }
}} }}
@ -53,7 +54,8 @@ export function UnwrapParamEditor({
async function loadUnwrapOptions( async function loadUnwrapOptions(
query: LokiVisualQuery, query: LokiVisualQuery,
datasource: LokiDatasource datasource: LokiDatasource,
timeRange = getDefaultTimeRange()
): Promise<Array<SelectableValue<string>>> { ): Promise<Array<SelectableValue<string>>> {
const queryExpr = lokiQueryModeller.renderQuery(query); const queryExpr = lokiQueryModeller.renderQuery(query);
const logExpr = getLogQueryFromMetricsQuery(queryExpr); const logExpr = getLogQueryFromMetricsQuery(queryExpr);
@ -61,7 +63,7 @@ async function loadUnwrapOptions(
return []; return [];
} }
const samples = await datasource.getDataSamples({ expr: logExpr, refId: 'unwrap_samples' }); const samples = await datasource.getDataSamples({ expr: logExpr, refId: 'unwrap_samples' }, timeRange);
const unwrapLabels = extractUnwrapLabelKeysFromDataFrame(samples[0]); const unwrapLabels = extractUnwrapLabelKeysFromDataFrame(samples[0]);
const labelOptions = unwrapLabels.map((label) => ({ const labelOptions = unwrapLabels.map((label) => ({

@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
import React, { useEffect, useId, useState } from 'react'; import React, { useEffect, useId, useState } from 'react';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { DataSourceApi, GrafanaTheme2 } from '@grafana/data'; import { DataSourceApi, GrafanaTheme2, TimeRange } from '@grafana/data';
import { Button, Icon, InlineField, Tooltip, useTheme2, Stack } from '@grafana/ui'; import { Button, Icon, InlineField, Tooltip, useTheme2, Stack } from '@grafana/ui';
import { isConflictingFilter } from 'app/plugins/datasource/loki/querybuilder/operationUtils'; import { isConflictingFilter } from 'app/plugins/datasource/loki/querybuilder/operationUtils';
import { LokiOperationId } from 'app/plugins/datasource/loki/querybuilder/types'; import { LokiOperationId } from 'app/plugins/datasource/loki/querybuilder/types';
@ -29,6 +29,7 @@ export interface Props {
onRunQuery: () => void; onRunQuery: () => void;
flash?: boolean; flash?: boolean;
highlight?: boolean; highlight?: boolean;
timeRange?: TimeRange;
} }
export function OperationEditor({ export function OperationEditor({
@ -42,6 +43,7 @@ export function OperationEditor({
datasource, datasource,
flash, flash,
highlight, highlight,
timeRange,
}: Props) { }: Props) {
const def = queryModeller.getOperationDef(operation.id); const def = queryModeller.getOperationDef(operation.id);
const shouldFlash = useFlash(flash); const shouldFlash = useFlash(flash);
@ -106,6 +108,7 @@ export function OperationEditor({
onRunQuery={onRunQuery} onRunQuery={onRunQuery}
query={query} query={query}
datasource={datasource} datasource={datasource}
timeRange={timeRange}
/> />
{paramDef.restParam && (operation.params.length > def.params.length || paramDef.optional) && ( {paramDef.restParam && (operation.params.length > def.params.length || paramDef.optional) && (
<Button <Button

@ -3,7 +3,7 @@ import React, { useState } from 'react';
import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
import { useMountedState, usePrevious } from 'react-use'; import { useMountedState, usePrevious } from 'react-use';
import { DataSourceApi, GrafanaTheme2 } from '@grafana/data'; import { DataSourceApi, GrafanaTheme2, TimeRange } from '@grafana/data';
import { Button, Cascader, CascaderOption, useStyles2, Stack } from '@grafana/ui'; import { Button, Cascader, CascaderOption, useStyles2, Stack } from '@grafana/ui';
import { OperationEditor } from './OperationEditor'; import { OperationEditor } from './OperationEditor';
@ -17,6 +17,7 @@ export interface Props<T extends QueryWithOperations> {
queryModeller: VisualQueryModeller; queryModeller: VisualQueryModeller;
explainMode?: boolean; explainMode?: boolean;
highlightedOp?: QueryBuilderOperation; highlightedOp?: QueryBuilderOperation;
timeRange?: TimeRange;
} }
export function OperationList<T extends QueryWithOperations>({ export function OperationList<T extends QueryWithOperations>({
@ -26,6 +27,7 @@ export function OperationList<T extends QueryWithOperations>({
onChange, onChange,
onRunQuery, onRunQuery,
highlightedOp, highlightedOp,
timeRange,
}: Props<T>) { }: Props<T>) {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const { operations } = query; const { operations } = query;
@ -104,6 +106,7 @@ export function OperationList<T extends QueryWithOperations>({
onRunQuery={onRunQuery} onRunQuery={onRunQuery}
flash={opsToHighlight[index]} flash={opsToHighlight[index]}
highlight={highlightedOp === op} highlight={highlightedOp === op}
timeRange={timeRange}
/> />
); );
})} })}

@ -4,7 +4,7 @@
import { ComponentType } from 'react'; import { ComponentType } from 'react';
import { DataSourceApi, RegistryItem, SelectableValue } from '@grafana/data'; import { DataSourceApi, RegistryItem, SelectableValue, TimeRange } from '@grafana/data';
export interface QueryBuilderLabelFilter { export interface QueryBuilderLabelFilter {
label: string; label: string;
@ -92,6 +92,7 @@ export interface QueryBuilderOperationParamEditorProps {
operationId: string; operationId: string;
query: any; query: any;
datasource: DataSourceApi; datasource: DataSourceApi;
timeRange?: TimeRange;
onChange: (index: number, value: QueryBuilderOperationParamValue) => void; onChange: (index: number, value: QueryBuilderOperationParamValue) => void;
onRunQuery: () => void; onRunQuery: () => void;
} }

Loading…
Cancel
Save