Commit 4c103663 by Ivana Huckova Committed by GitHub

Loki/Explore: Add query type selector (#28817)

* Create query type switcher

* Add and update tests

* Add handling in datasource

* Refactor

* Update tests, when checking higlighting, suppy logs

* Remove both option as redundant

* Add tooltip, remove old comments

* Remove unused importts

* Remove console.log, update width

* Update public/app/plugins/datasource/loki/components/LokiExploreExtraField.tsx

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

* Update tests

* Prettier fixes

* Fix test

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
parent 9ba8114b
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { render, screen } from '@testing-library/react';
import { LokiExploreExtraField, LokiExploreExtraFieldProps } from './LokiExploreExtraField'; import { LokiExploreExtraFieldProps, LokiExploreExtraField } from './LokiExploreExtraField';
const setup = (propOverrides?: LokiExploreExtraFieldProps) => { const setup = (propOverrides?: LokiExploreExtraFieldProps) => {
const label = 'Loki Explore Extra Field'; const queryType = 'range';
const value = '123'; const lineLimitValue = '1';
const type = 'number'; const onLineLimitChange = jest.fn();
const min = 0; const onQueryTypeChange = jest.fn();
const onChangeFunc = jest.fn();
const onKeyDownFunc = jest.fn(); const onKeyDownFunc = jest.fn();
const props: any = { const props: any = {
label, queryType,
value, lineLimitValue,
type, onLineLimitChange,
min, onQueryTypeChange,
onChangeFunc,
onKeyDownFunc, onKeyDownFunc,
}; };
Object.assign(props, propOverrides); Object.assign(props, propOverrides);
return shallow(<LokiExploreExtraField {...props} />); return render(<LokiExploreExtraField {...props} />);
}; };
describe('LokiExploreExtraField', () => { describe('LokiExploreExtraField', () => {
it('should render component', () => { it('should render step field', () => {
const wrapper = setup(); setup();
expect(wrapper).toMatchSnapshot(); expect(screen.getByTestId('lineLimitField')).toBeInTheDocument();
});
it('should render query type field', () => {
setup();
expect(screen.getByTestId('queryTypeField')).toBeInTheDocument();
}); });
}); });
// Libraries // Libraries
import React, { memo } from 'react'; import React, { memo } from 'react';
import { css, cx } from 'emotion';
// Types // Types
import { InlineFormLabel } from '@grafana/ui'; import { InlineFormLabel, RadioButtonGroup } from '@grafana/ui';
export interface LokiExploreExtraFieldProps { export interface LokiExploreExtraFieldProps {
label: string; lineLimitValue: string;
onChangeFunc: (e: React.SyntheticEvent<HTMLInputElement>) => void; queryType: string;
onLineLimitChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
onKeyDownFunc: (e: React.KeyboardEvent<HTMLInputElement>) => void; onKeyDownFunc: (e: React.KeyboardEvent<HTMLInputElement>) => void;
value: string; onQueryTypeChange: (value: string) => void;
type?: string;
min?: number;
} }
export function LokiExploreExtraField(props: LokiExploreExtraFieldProps) { export function LokiExploreExtraField(props: LokiExploreExtraFieldProps) {
const { label, onChangeFunc, onKeyDownFunc, value, type, min } = props; const { onLineLimitChange, onKeyDownFunc, lineLimitValue, queryType, onQueryTypeChange } = props;
const rangeOptions = [
{ value: 'range', label: 'Range' },
{ value: 'instant', label: 'Instant' },
];
return ( return (
<div className="gf-form-inline"> <div aria-label="Loki extra field" className="gf-form-inline">
<div className="gf-form"> {/*Query type field*/}
<InlineFormLabel width={5}>{label}</InlineFormLabel> <div
data-testid="queryTypeField"
className={cx(
'gf-form explore-input-margin',
css`
flex-wrap: nowrap;
`
)}
aria-label="Query type field"
>
<InlineFormLabel
tooltip="Choose the type of query you would like to run. An instant query queries against a single point in time. A range query queries over a range of time."
width="auto"
>
Query type
</InlineFormLabel>
<RadioButtonGroup options={rangeOptions} value={queryType} onChange={onQueryTypeChange} />
</div>
{/*Line limit field*/}
<div
data-testid="lineLimitField"
className={cx(
'gf-form',
css`
flex-wrap: nowrap;
`
)}
aria-label="Line limit field"
>
<InlineFormLabel width={5}>Line limit</InlineFormLabel>
<input <input
type={type} type="number"
className="gf-form-input width-4" className="gf-form-input width-4"
placeholder={'auto'} placeholder={'auto'}
onChange={onChangeFunc} min={0}
onChange={onLineLimitChange}
onKeyDown={onKeyDownFunc} onKeyDown={onKeyDownFunc}
min={min} value={lineLimitValue}
value={value}
/> />
</div> </div>
</div> </div>
......
...@@ -20,6 +20,17 @@ export function LokiExploreQueryEditor(props: Props) { ...@@ -20,6 +20,17 @@ export function LokiExploreQueryEditor(props: Props) {
onChange(nextQuery); onChange(nextQuery);
} }
function onQueryTypeChange(value: string) {
const { query, onChange } = props;
let nextQuery;
if (value === 'instant') {
nextQuery = { ...query, instant: true, range: false };
} else {
nextQuery = { ...query, instant: false, range: true };
}
onChange(nextQuery);
}
function preprocessMaxLines(value: string): number { function preprocessMaxLines(value: string): number {
if (value.length === 0) { if (value.length === 0) {
// empty input - falls back to dataSource.maxLines limit // empty input - falls back to dataSource.maxLines limit
...@@ -58,12 +69,11 @@ export function LokiExploreQueryEditor(props: Props) { ...@@ -58,12 +69,11 @@ export function LokiExploreQueryEditor(props: Props) {
range={range} range={range}
ExtraFieldElement={ ExtraFieldElement={
<LokiExploreExtraField <LokiExploreExtraField
label={'Line limit'} queryType={query.instant ? 'instant' : 'range'}
onChangeFunc={onMaxLinesChange} lineLimitValue={query?.maxLines?.toString() || ''}
onQueryTypeChange={onQueryTypeChange}
onLineLimitChange={onMaxLinesChange}
onKeyDownFunc={onReturnKeyDown} onKeyDownFunc={onReturnKeyDown}
value={query?.maxLines?.toString() || ''}
type={'number'}
min={0}
/> />
} }
/> />
......
...@@ -150,7 +150,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr ...@@ -150,7 +150,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
return ( return (
<> <>
<div className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1"> <div className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1">
<div className="gf-form flex-shrink-0"> <div className="gf-form flex-shrink-0 min-width-5">
<ButtonCascader <ButtonCascader
options={logLabelOptions || []} options={logLabelOptions || []}
disabled={buttonDisabled} disabled={buttonDisabled}
...@@ -161,7 +161,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr ...@@ -161,7 +161,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
{chooserText} {chooserText}
</ButtonCascader> </ButtonCascader>
</div> </div>
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15 explore-input-margin"> <div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
<QueryField <QueryField
additionalPlugins={this.plugins} additionalPlugins={this.plugins}
cleanText={cleanText} cleanText={cleanText}
...@@ -176,8 +176,8 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr ...@@ -176,8 +176,8 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
syntaxLoaded={syntaxLoaded} syntaxLoaded={syntaxLoaded}
/> />
</div> </div>
{ExtraFieldElement}
</div> </div>
{ExtraFieldElement}
</> </>
); );
} }
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LokiExploreExtraField should render component 1`] = `
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<Component
width={5}
>
Loki Explore Extra Field
</Component>
<input
className="gf-form-input width-4"
min={0}
onChange={[MockFunction]}
onKeyDown={[MockFunction]}
placeholder="auto"
type="number"
value="123"
/>
</div>
</div>
`;
...@@ -4,12 +4,11 @@ exports[`LokiExploreQueryEditor should render component 1`] = ` ...@@ -4,12 +4,11 @@ exports[`LokiExploreQueryEditor should render component 1`] = `
<Component <Component
ExtraFieldElement={ ExtraFieldElement={
<Memo(LokiExploreExtraField) <Memo(LokiExploreExtraField)
label="Line limit" lineLimitValue="0"
min={0}
onChangeFunc={[Function]}
onKeyDownFunc={[Function]} onKeyDownFunc={[Function]}
type="number" onLineLimitChange={[Function]}
value="0" onQueryTypeChange={[Function]}
queryType="range"
/> />
} }
data={ data={
......
import { of, throwError } from 'rxjs'; import { of, throwError } from 'rxjs';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { omit } from 'lodash'; import { AnnotationQueryRequest, CoreApp, DataFrame, dateTime, FieldCache, TimeRange, TimeSeries } from '@grafana/data';
import { AnnotationQueryRequest, CoreApp, DataFrame, dateTime, FieldCache, TimeRange } from '@grafana/data';
import { BackendSrvRequest, FetchResponse } from '@grafana/runtime'; import { BackendSrvRequest, FetchResponse } from '@grafana/runtime';
import LokiDatasource from './datasource'; import LokiDatasource from './datasource';
...@@ -26,7 +25,7 @@ const timeSrvStub = { ...@@ -26,7 +25,7 @@ const timeSrvStub = {
}), }),
}; };
const testResponse: FetchResponse<LokiResponse> = { const testLogsResponse: FetchResponse<LokiResponse> = {
data: { data: {
data: { data: {
resultType: LokiResultType.Stream, resultType: LokiResultType.Stream,
...@@ -49,6 +48,29 @@ const testResponse: FetchResponse<LokiResponse> = { ...@@ -49,6 +48,29 @@ const testResponse: FetchResponse<LokiResponse> = {
config: ({} as unknown) as BackendSrvRequest, config: ({} as unknown) as BackendSrvRequest,
}; };
const testMetricsResponse: FetchResponse<LokiResponse> = {
data: {
data: {
resultType: LokiResultType.Matrix,
result: [
{
metric: {},
values: [[1605715380, '1.1']],
},
],
},
status: 'success',
},
ok: true,
headers: ({} as unknown) as Headers,
redirected: false,
status: 200,
statusText: 'OK',
type: 'basic',
url: '',
config: ({} as unknown) as BackendSrvRequest,
};
describe('LokiDatasource', () => { describe('LokiDatasource', () => {
const fetchMock = jest.spyOn(backendSrv, 'fetch'); const fetchMock = jest.spyOn(backendSrv, 'fetch');
...@@ -96,7 +118,7 @@ describe('LokiDatasource', () => { ...@@ -96,7 +118,7 @@ describe('LokiDatasource', () => {
}); });
}); });
describe('when querying with limits', () => { describe('when doing logs queries with limits', () => {
const runLimitTest = async ({ const runLimitTest = async ({
maxDataPoints = 123, maxDataPoints = 123,
queryMaxLines, queryMaxLines,
...@@ -121,7 +143,7 @@ describe('LokiDatasource', () => { ...@@ -121,7 +143,7 @@ describe('LokiDatasource', () => {
const options = getQueryOptions<LokiQuery>({ targets: [{ expr, refId: 'B', maxLines: queryMaxLines }] }); const options = getQueryOptions<LokiQuery>({ targets: [{ expr, refId: 'B', maxLines: queryMaxLines }] });
options.maxDataPoints = maxDataPoints; options.maxDataPoints = maxDataPoints;
fetchMock.mockImplementation(() => of(testResponse)); fetchMock.mockImplementation(() => of(testLogsResponse));
await expect(ds.query(options).pipe(take(1))).toEmitValuesWith(() => { await expect(ds.query(options).pipe(take(1))).toEmitValuesWith(() => {
expect(fetchMock.mock.calls.length).toBe(1); expect(fetchMock.mock.calls.length).toBe(1);
...@@ -151,10 +173,10 @@ describe('LokiDatasource', () => { ...@@ -151,10 +173,10 @@ describe('LokiDatasource', () => {
}); });
describe('when querying', () => { describe('when querying', () => {
function setup(expr: string, app: CoreApp) { function setup(expr: string, app: CoreApp, instant?: boolean, range?: boolean) {
const ds = createLokiDSForTests(); const ds = createLokiDSForTests();
const options = getQueryOptions<LokiQuery>({ const options = getQueryOptions<LokiQuery>({
targets: [{ expr, refId: 'B' }], targets: [{ expr, refId: 'B', instant, range }],
app, app,
}); });
ds.runInstantQuery = jest.fn(() => of({ data: [] })); ds.runInstantQuery = jest.fn(() => of({ data: [] }));
...@@ -162,68 +184,95 @@ describe('LokiDatasource', () => { ...@@ -162,68 +184,95 @@ describe('LokiDatasource', () => {
return { ds, options }; return { ds, options };
} }
it('should run range and instant query in Explore if running metric query', async () => { const metricsQuery = 'rate({job="grafana"}[10m])';
const { ds, options } = setup('rate({job="grafana"}[10m])', CoreApp.Explore); const logsQuery = '{job="grafana"} |= "foo"';
it('should run logs instant if only instant is selected', async () => {
const { ds, options } = setup(logsQuery, CoreApp.Explore, true, false);
await ds.query(options).toPromise();
expect(ds.runInstantQuery).toBeCalled();
expect(ds.runRangeQuery).not.toBeCalled();
});
it('should run metrics instant if only instant is selected', async () => {
const { ds, options } = setup(metricsQuery, CoreApp.Explore, true, false);
await ds.query(options).toPromise(); await ds.query(options).toPromise();
expect(ds.runInstantQuery).toBeCalled(); expect(ds.runInstantQuery).toBeCalled();
expect(ds.runRangeQuery).not.toBeCalled();
});
it('should run only logs range query if only range is selected', async () => {
const { ds, options } = setup(logsQuery, CoreApp.Explore, false, true);
await ds.query(options).toPromise();
expect(ds.runInstantQuery).not.toBeCalled();
expect(ds.runRangeQuery).toBeCalled();
});
it('should run only metrics range query if only range is selected', async () => {
const { ds, options } = setup(metricsQuery, CoreApp.Explore, false, true);
await ds.query(options).toPromise();
expect(ds.runInstantQuery).not.toBeCalled();
expect(ds.runRangeQuery).toBeCalled();
});
it('should run only logs range query if no query type is selected in Explore', async () => {
const { ds, options } = setup(logsQuery, CoreApp.Explore);
await ds.query(options).toPromise();
expect(ds.runInstantQuery).not.toBeCalled();
expect(ds.runRangeQuery).toBeCalled(); expect(ds.runRangeQuery).toBeCalled();
}); });
it('should run only range query in Explore if running logs query', async () => { it('should run only metrics range query if no query type is selected in Explore', async () => {
const { ds, options } = setup('{job="grafana"}', CoreApp.Explore); const { ds, options } = setup(metricsQuery, CoreApp.Explore);
await ds.query(options).toPromise(); await ds.query(options).toPromise();
expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runInstantQuery).not.toBeCalled();
expect(ds.runRangeQuery).toBeCalled(); expect(ds.runRangeQuery).toBeCalled();
}); });
it('should run only range query in Dashboard', async () => { it('should run only logs range query in Dashboard', async () => {
const { ds, options } = setup('rate({job="grafana"}[10m])', CoreApp.Dashboard); const { ds, options } = setup(logsQuery, CoreApp.Dashboard);
await ds.query(options).toPromise(); await ds.query(options).toPromise();
expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runInstantQuery).not.toBeCalled();
expect(ds.runRangeQuery).toBeCalled(); expect(ds.runRangeQuery).toBeCalled();
}); });
it('should return series data for both queries in Explore if metrics query', async () => { it('should run only metrics range query in Dashboard', async () => {
const { ds, options } = setup(metricsQuery, CoreApp.Dashboard);
await ds.query(options).toPromise();
expect(ds.runInstantQuery).not.toBeCalled();
expect(ds.runRangeQuery).toBeCalled();
});
it('should return series data for metrics range queries', async () => {
const ds = createLokiDSForTests(); const ds = createLokiDSForTests();
const options = getQueryOptions<LokiQuery>({ const options = getQueryOptions<LokiQuery>({
targets: [{ expr: 'rate({job="grafana"} |= "foo" [10m])', refId: 'B' }], targets: [{ expr: metricsQuery, refId: 'B', range: true }],
app: CoreApp.Explore, app: CoreApp.Explore,
}); });
fetchMock fetchMock.mockImplementation(() => of(testMetricsResponse));
.mockImplementationOnce(() => of(testResponse))
.mockImplementation(() => of(omit(testResponse, 'data.status')));
await expect(ds.query(options)).toEmitValuesWith(received => { await expect(ds.query(options)).toEmitValuesWith(received => {
// first result always comes from runInstantQuery const result = received[0];
const firstResult = received[0]; const timeSeries = result.data[0] as TimeSeries;
expect(firstResult).toEqual({ data: [], key: 'B_instant' });
// second result always comes from runRangeQuery expect(timeSeries.meta?.preferredVisualisationType).toBe('graph');
const secondResult = received[1]; expect(timeSeries.refId).toBe('B');
const dataFrame = secondResult.data[0] as DataFrame; expect(timeSeries.datapoints[0]).toEqual([1.1, 1605715380000]);
const fieldCache = new FieldCache(dataFrame);
expect(fieldCache.getFieldByName('line')?.values.get(0)).toBe('hello');
expect(dataFrame.meta?.limit).toBe(500);
expect(dataFrame.meta?.searchWords).toEqual([]);
}); });
}); });
it('should return series data for range query in Dashboard', async () => { it('should return series data for logs range query', async () => {
const ds = createLokiDSForTests(); const ds = createLokiDSForTests();
const options = getQueryOptions<LokiQuery>({ const options = getQueryOptions<LokiQuery>({
targets: [{ expr: '{job="grafana"} |= "foo"', refId: 'B' }], targets: [{ expr: logsQuery, refId: 'B' }],
}); });
fetchMock fetchMock.mockImplementation(() => of(testLogsResponse));
.mockImplementationOnce(() => of(testResponse))
.mockImplementation(() => of(omit(testResponse, 'data.status')));
await expect(ds.query(options)).toEmitValuesWith(received => { await expect(ds.query(options)).toEmitValuesWith(received => {
// first result will come from runRangeQuery const result = received[0];
const firstResult = received[0]; const dataFrame = result.data[0] as DataFrame;
const dataFrame = firstResult.data[0] as DataFrame;
const fieldCache = new FieldCache(dataFrame); const fieldCache = new FieldCache(dataFrame);
expect(fieldCache.getFieldByName('line')?.values.get(0)).toBe('hello'); expect(fieldCache.getFieldByName('line')?.values.get(0)).toBe('hello');
......
...@@ -24,13 +24,17 @@ import { ...@@ -24,13 +24,17 @@ import {
QueryResultMeta, QueryResultMeta,
ScopedVars, ScopedVars,
TimeRange, TimeRange,
CoreApp,
} from '@grafana/data'; } from '@grafana/data';
import { getTemplateSrv, TemplateSrv, BackendSrvRequest, FetchError, getBackendSrv } from '@grafana/runtime'; import { getTemplateSrv, TemplateSrv, BackendSrvRequest, FetchError, getBackendSrv } from '@grafana/runtime';
import { addLabelToQuery } from 'app/plugins/datasource/prometheus/add_label_to_query'; import { addLabelToQuery } from 'app/plugins/datasource/prometheus/add_label_to_query';
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { convertToWebSocketUrl } from 'app/core/utils/explore'; import { convertToWebSocketUrl } from 'app/core/utils/explore';
import { lokiResultsToTableModel, lokiStreamResultToDataFrame, processRangeQueryResponse } from './result_transformer'; import {
lokiResultsToTableModel,
lokiStreamResultToDataFrame,
lokiStreamsToDataFrames,
processRangeQueryResponse,
} from './result_transformer';
import { getHighlighterExpressionsFromQuery } from './query_utils'; import { getHighlighterExpressionsFromQuery } from './query_utils';
import { import {
...@@ -99,12 +103,11 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> { ...@@ -99,12 +103,11 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
})); }));
for (const target of filteredTargets) { for (const target of filteredTargets) {
// In explore we want to show result of metrics instant query in a table under the graph panel to mimic behaviour of prometheus. if (target.instant) {
// We don't want to do that in dashboards though as user would have to pick the correct data frame.
if (options.app === CoreApp.Explore && isMetricsQuery(target.expr)) {
subQueries.push(this.runInstantQuery(target, options, filteredTargets.length)); subQueries.push(this.runInstantQuery(target, options, filteredTargets.length));
} else {
subQueries.push(this.runRangeQuery(target, options, filteredTargets.length));
} }
subQueries.push(this.runRangeQuery(target, options, filteredTargets.length));
} }
// No valid targets, return the empty result to save a round trip. // No valid targets, return the empty result to save a round trip.
...@@ -124,12 +127,14 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> { ...@@ -124,12 +127,14 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
responseListLength: number responseListLength: number
): Observable<DataQueryResponse> => { ): Observable<DataQueryResponse> => {
const timeNs = this.getTime(options.range.to, true); const timeNs = this.getTime(options.range.to, true);
const queryLimit = isMetricsQuery(target.expr) ? options.maxDataPoints : target.maxLines;
const query = { const query = {
query: target.expr, query: target.expr,
time: `${timeNs + (1e9 - (timeNs % 1e9))}`, time: `${timeNs + (1e9 - (timeNs % 1e9))}`,
limit: Math.min(options.maxDataPoints || Infinity, this.maxLines), limit: Math.min(queryLimit || Infinity, this.maxLines),
}; };
/** Show results of Loki instant queries only in table */
/** Used only for results of metrics instant queries */
const meta: QueryResultMeta = { const meta: QueryResultMeta = {
preferredVisualisationType: 'table', preferredVisualisationType: 'table',
}; };
...@@ -138,7 +143,14 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> { ...@@ -138,7 +143,14 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
map((response: { data: LokiResponse }) => { map((response: { data: LokiResponse }) => {
if (response.data.data.resultType === LokiResultType.Stream) { if (response.data.data.resultType === LokiResultType.Stream) {
return { return {
data: [], data: response.data
? lokiStreamsToDataFrames(
response.data as LokiStreamResponse,
target,
query.limit,
this.instanceSettings.jsonData
)
: [],
key: `${target.refId}_instant`, key: `${target.refId}_instant`,
}; };
} }
......
...@@ -61,10 +61,10 @@ describe('loki result transformer', () => { ...@@ -61,10 +61,10 @@ describe('loki result transformer', () => {
}); });
}); });
describe('lokiStreamsToDataframes', () => { describe('lokiStreamsToDataFrames', () => {
it('should enhance data frames', () => { it('should enhance data frames', () => {
jest.spyOn(ResultTransformer, 'enhanceDataFrame'); jest.spyOn(ResultTransformer, 'enhanceDataFrame');
const dataFrames = ResultTransformer.lokiStreamsToDataframes(lokiResponse, { refId: 'B' }, 500, { const dataFrames = ResultTransformer.lokiStreamsToDataFrames(lokiResponse, { refId: 'B' }, 500, {
derivedFields: [ derivedFields: [
{ {
matcherRegex: 'trace=(w+)', matcherRegex: 'trace=(w+)',
......
...@@ -305,7 +305,7 @@ function lokiStatsToMetaStat(stats: LokiStats | undefined): QueryResultMetaStat[ ...@@ -305,7 +305,7 @@ function lokiStatsToMetaStat(stats: LokiStats | undefined): QueryResultMetaStat[
return result; return result;
} }
export function lokiStreamsToDataframes( export function lokiStreamsToDataFrames(
response: LokiStreamResponse, response: LokiStreamResponse,
target: { refId: string; expr?: string }, target: { refId: string; expr?: string },
limit: number, limit: number,
...@@ -472,7 +472,7 @@ export function processRangeQueryResponse( ...@@ -472,7 +472,7 @@ export function processRangeQueryResponse(
switch (response.data.resultType) { switch (response.data.resultType) {
case LokiResultType.Stream: case LokiResultType.Stream:
return of({ return of({
data: lokiStreamsToDataframes(response as LokiStreamResponse, target, limit, config, reverse), data: lokiStreamsToDataFrames(response as LokiStreamResponse, target, limit, config, reverse),
key: `${target.refId}_log`, key: `${target.refId}_log`,
}); });
......
...@@ -30,6 +30,8 @@ export interface LokiQuery extends DataQuery { ...@@ -30,6 +30,8 @@ export interface LokiQuery extends DataQuery {
legendFormat?: string; legendFormat?: string;
valueWithRefId?: boolean; valueWithRefId?: boolean;
maxLines?: number; maxLines?: number;
range?: boolean;
instant?: boolean;
} }
export interface LokiOptions extends DataSourceJsonData { export interface LokiOptions extends DataSourceJsonData {
......
...@@ -23,7 +23,7 @@ export const PromExploreExtraField: React.FC<PromExploreExtraFieldProps> = memo( ...@@ -23,7 +23,7 @@ export const PromExploreExtraField: React.FC<PromExploreExtraFieldProps> = memo(
return ( return (
<div aria-label="Prometheus extra field" className="gf-form-inline"> <div aria-label="Prometheus extra field" className="gf-form-inline">
{/*QueryTypeField */} {/*Query type field*/}
<div <div
data-testid="queryTypeField" data-testid="queryTypeField"
className={cx( className={cx(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment