Commit e0448513 by Andrej Ocenas Committed by GitHub

Influx: Make max series limit configurable and show the limiting message if applied (#31025)

* Add configuration in ConfigEditor and default to 1000

* Show data in explore if any even if there is an error

* Update pkg/tsdb/influxdb/flux/executor.go

* Better handling of defaults

* Add test for runQuery to show data even with error

* Update public/app/store/configureStore.ts

Co-authored-by: Giordano Ricci <gio.ricci@grafana.com>

* Update public/app/plugins/datasource/influxdb/components/ConfigEditor.tsx

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* Update tooltip

* Update input

* Lint fixes

* Update snapshots

* Update decorator tests

Co-authored-by: Giordano Ricci <gio.ricci@grafana.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
parent fd5fa402
......@@ -85,7 +85,9 @@ func readDataFrames(result *api.QueryTableResult, maxPoints int, maxSeries int)
}
}
// Attach any errors (may be null)
dr.Error = result.Err()
// result.Err() is probably more important then the other errors
if result.Err() != nil {
dr.Error = result.Err()
}
return dr
}
......@@ -39,7 +39,9 @@ func Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQ
continue
}
res := executeQuery(ctx, *qm, r, 50)
// If the default changes also update labels/placeholder in config page.
maxSeries := dsInfo.JsonData.Get("maxSeries").MustInt(1000)
res := executeQuery(ctx, *qm, r, maxSeries)
tRes.Results[query.RefId] = backendDataResponseToTSDBResponse(&res, query.RefId)
}
......
......@@ -4,15 +4,19 @@ import {
cancelQueriesAction,
queryReducer,
removeQueryRowAction,
runQueries,
scanStartAction,
scanStopAction,
} from './query';
import { ExploreId, ExploreItemState } from 'app/types';
import { interval } from 'rxjs';
import { RawTimeRange, toUtc } from '@grafana/data';
import { interval, of } from 'rxjs';
import { ArrayVector, DataQueryResponse, DefaultTimeZone, MutableDataFrame, RawTimeRange, toUtc } from '@grafana/data';
import { thunkTester } from 'test/core/thunk/thunkTester';
import { makeExplorePaneState } from './utils';
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { configureStore } from '../../../store/configureStore';
import { setTimeSrv } from '../../dashboard/services/TimeSrv';
import Mock = jest.Mock;
const QUERY_KEY_REGEX = /Q-(?:[a-z0-9]+-){5}(?:[0-9]+)/;
const t = toUtc();
......@@ -24,6 +28,58 @@ const testRange = {
to: t,
},
};
const defaultInitialState = {
user: {
orgId: '1',
timeZone: DefaultTimeZone,
},
explore: {
[ExploreId.left]: {
datasourceInstance: {
query: jest.fn(),
meta: {
id: 'something',
},
},
initialized: true,
containerWidth: 1920,
eventBridge: { emit: () => {} } as any,
queries: [{ expr: 'test' }] as any[],
range: testRange,
refreshInterval: {
label: 'Off',
value: 0,
},
},
},
};
describe('runQueries', () => {
it('should pass dataFrames to state even if there is error in response', async () => {
setTimeSrv({
init() {},
} as any);
const store = configureStore({
...(defaultInitialState as any),
});
(store.getState().explore[ExploreId.left].datasourceInstance?.query as Mock).mockReturnValueOnce(
of({
error: { message: 'test error' },
data: [
new MutableDataFrame({
fields: [{ name: 'test', values: new ArrayVector() }],
meta: {
preferredVisualisationType: 'graph',
},
}),
],
} as DataQueryResponse)
);
await store.dispatch(runQueries(ExploreId.left));
expect(store.getState().explore[ExploreId.left].showMetrics).toBeTruthy();
expect(store.getState().explore[ExploreId.left].graphResult).toBeDefined();
});
});
describe('running queries', () => {
it('should cancel running query when cancelQueries is dispatched', async () => {
......
......@@ -664,16 +664,6 @@ export const processQueryResponse = (
// For Angular editors
state.eventBridge.emit(PanelEvents.dataError, error);
return {
...state,
loading: loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming,
queryResponse: response,
graphResult: null,
tableResult: null,
logsResult: null,
update: makeInitialUpdateState(),
};
}
if (!request) {
......
......@@ -132,7 +132,7 @@ describe('decorateWithGraphLogsTraceAndTable', () => {
});
});
it('should handle query error', () => {
it('should return frames even if there is an error', () => {
const { timeSeries, logs, table } = getTestContext();
const series: DataFrame[] = [timeSeries, logs, table];
const panelData: PanelData = {
......@@ -147,9 +147,9 @@ describe('decorateWithGraphLogsTraceAndTable', () => {
error: {},
state: LoadingState.Error,
timeRange: {},
graphFrames: [],
tableFrames: [],
logsFrames: [],
graphFrames: [timeSeries],
tableFrames: [table],
logsFrames: [logs],
traceFrames: [],
nodeGraphFrames: [],
graphResult: null,
......@@ -171,10 +171,10 @@ describe('decorateWithGraphResult', () => {
expect(decorateWithGraphResult(panelData).graphResult).toBeNull();
});
it('returns null if panelData has error', () => {
it('returns data if panelData has error', () => {
const { timeSeries } = getTestContext();
const panelData = createExplorePanelData({ error: {}, graphFrames: [timeSeries] });
expect(decorateWithGraphResult(panelData).graphResult).toBeNull();
expect(decorateWithGraphResult(panelData).graphResult).toMatchObject([timeSeries]);
});
});
......@@ -272,11 +272,11 @@ describe('decorateWithTableResult', () => {
expect(panelResult.tableResult).toBeNull();
});
it('returns null if panelData has error', async () => {
it('returns data if panelData has error', async () => {
const { table, emptyTable } = getTestContext();
const panelData = createExplorePanelData({ error: {}, tableFrames: [table, emptyTable] });
const panelResult = await decorateWithTableResult(panelData).toPromise();
expect(panelResult.tableResult).toBeNull();
expect(panelResult.tableResult).not.toBeNull();
});
});
......@@ -386,9 +386,9 @@ describe('decorateWithLogsResult', () => {
expect(decorateWithLogsResult()(panelData).logsResult).toBeNull();
});
it('returns null if panelData has error', () => {
it('returns data if panelData has error', () => {
const { logs } = getTestContext();
const panelData = createExplorePanelData({ error: {}, logsFrames: [logs] });
expect(decorateWithLogsResult()(panelData).logsResult).toBeNull();
expect(decorateWithLogsResult()(panelData).logsResult).not.toBeNull();
});
});
......@@ -21,20 +21,6 @@ import { ExplorePanelData } from '../../../types';
* Observable pipeline, it decorates the existing panelData to pass the results to later processing stages.
*/
export const decorateWithFrameTypeMetadata = (data: PanelData): ExplorePanelData => {
if (data.error) {
return {
...data,
graphFrames: [],
tableFrames: [],
logsFrames: [],
traceFrames: [],
nodeGraphFrames: [],
graphResult: null,
tableResult: null,
logsResult: null,
};
}
const graphFrames: DataFrame[] = [];
const tableFrames: DataFrame[] = [];
const logsFrames: DataFrame[] = [];
......@@ -83,7 +69,7 @@ export const decorateWithFrameTypeMetadata = (data: PanelData): ExplorePanelData
};
export const decorateWithGraphResult = (data: ExplorePanelData): ExplorePanelData => {
if (data.error || !data.graphFrames.length) {
if (!data.graphFrames.length) {
return { ...data, graphResult: null };
}
......@@ -96,10 +82,6 @@ export const decorateWithGraphResult = (data: ExplorePanelData): ExplorePanelDat
* multiple results and so this should be used with mergeMap or similar to unbox the internal observable.
*/
export const decorateWithTableResult = (data: ExplorePanelData): Observable<ExplorePanelData> => {
if (data.error) {
return of({ ...data, tableResult: null });
}
if (data.tableFrames.length === 0) {
return of({ ...data, tableResult: null });
}
......@@ -149,10 +131,6 @@ export const decorateWithTableResult = (data: ExplorePanelData): Observable<Expl
export const decorateWithLogsResult = (
options: { absoluteRange?: AbsoluteTimeRange; refreshInterval?: string } = {}
) => (data: ExplorePanelData): ExplorePanelData => {
if (data.error) {
return { ...data, logsResult: null };
}
if (data.logsFrames.length === 0) {
return { ...data, logsResult: null };
}
......
......@@ -14,7 +14,7 @@ export function addRootReducer(reducers: any) {
addReducer(reducers);
}
export function configureStore() {
export function configureStore(initialState?: Partial<StoreState>) {
const logger = createLogger({
predicate: (getState) => {
return getState().application.logActions;
......@@ -35,6 +35,7 @@ export function configureStore() {
devTools: process.env.NODE_ENV !== 'production',
preloadedState: {
navIndex: buildInitialState(),
...initialState,
},
});
......@@ -42,7 +43,7 @@ export function configureStore() {
return store;
}
/*
/*
function getActionsToIgnoreSerializableCheckOn() {
return [
'dashboard/setPanelAngularComponent',
......@@ -58,7 +59,7 @@ function getActionsToIgnoreSerializableCheckOn() {
}
function getPathsToIgnoreMutationAndSerializableCheckOn() {
return [
return [
'plugins.panels',
'dashboard.panels',
'dashboard.getModel',
......@@ -75,7 +76,7 @@ function getPathsToIgnoreMutationAndSerializableCheckOn() {
'explore.right.eventBridge',
'explore.right.range',
'explore.left.querySubscription',
'explore.right.querySubscription',
'explore.right.querySubscription',
];
}
*/
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