ModifyQuery: Improve typing for modifyQuery and query hints (#52326)

* ModifyQuery: Improve typing

* Elasticsearch: Added `modifyQuery` method to add filters in Explore (#52313)

* fixed elasticsearch `QueryFixAction` type

Co-authored-by: Sven Grossmann <Svennergr@gmail.com>
This commit is contained in:
Ivana Huckova 2022-07-18 14:13:34 +02:00 committed by GitHub
parent b2736ac1fe
commit 8ff152f98f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 68 additions and 44 deletions

View File

@ -7685,8 +7685,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"]
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
],
"public/app/plugins/datasource/loki/getDerivedFields.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@ -8246,8 +8245,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "32"],
[0, 0, 0, "Unexpected any. Specify a different type.", "33"],
[0, 0, 0, "Unexpected any. Specify a different type.", "34"],
[0, 0, 0, "Unexpected any. Specify a different type.", "35"],
[0, 0, 0, "Unexpected any. Specify a different type.", "36"]
[0, 0, 0, "Unexpected any. Specify a different type.", "35"]
],
"public/app/plugins/datasource/prometheus/language_provider.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],

View File

@ -506,6 +506,7 @@ export interface QueryFixAction {
type: string;
query?: string;
preventSubmit?: boolean;
options?: KeyValue<string>;
}
export interface QueryHint {

View File

@ -150,11 +150,11 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
};
onClickFilterLabel = (key: string, value: string) => {
this.onModifyQueries({ type: 'ADD_FILTER', key, value });
this.onModifyQueries({ type: 'ADD_FILTER', options: { key, value } });
};
onClickFilterOutLabel = (key: string, value: string) => {
this.onModifyQueries({ type: 'ADD_FILTER_OUT', key, value });
this.onModifyQueries({ type: 'ADD_FILTER_OUT', options: { key, value } });
};
onClickAddQueryRowButton = () => {

View File

@ -1033,15 +1033,19 @@ describe('modifyQuery', () => {
});
it('should add the filter', () => {
expect(ds.modifyQuery(query, { type: 'ADD_FILTER', key: 'foo', value: 'bar' }).query).toBe('foo:"bar"');
expect(ds.modifyQuery(query, { type: 'ADD_FILTER', options: { key: 'foo', value: 'bar' } }).query).toBe(
'foo:"bar"'
);
});
it('should add the negative filter', () => {
expect(ds.modifyQuery(query, { type: 'ADD_FILTER_OUT', key: 'foo', value: 'bar' }).query).toBe('-foo:"bar"');
expect(ds.modifyQuery(query, { type: 'ADD_FILTER_OUT', options: { key: 'foo', value: 'bar' } }).query).toBe(
'-foo:"bar"'
);
});
it('should do nothing on unknown type', () => {
expect(ds.modifyQuery(query, { type: 'unknown', key: 'foo', value: 'bar' }).query).toBe(query.query);
expect(ds.modifyQuery(query, { type: 'unknown', options: { key: 'foo', value: 'bar' } }).query).toBe(query.query);
});
});
@ -1052,19 +1056,19 @@ describe('modifyQuery', () => {
});
it('should add the filter', () => {
expect(ds.modifyQuery(query, { type: 'ADD_FILTER', key: 'foo', value: 'bar' }).query).toBe(
expect(ds.modifyQuery(query, { type: 'ADD_FILTER', options: { key: 'foo', value: 'bar' } }).query).toBe(
'test:"value" AND foo:"bar"'
);
});
it('should add the negative filter', () => {
expect(ds.modifyQuery(query, { type: 'ADD_FILTER_OUT', key: 'foo', value: 'bar' }).query).toBe(
expect(ds.modifyQuery(query, { type: 'ADD_FILTER_OUT', options: { key: 'foo', value: 'bar' } }).query).toBe(
'test:"value" AND -foo:"bar"'
);
});
it('should do nothing on unknown type', () => {
expect(ds.modifyQuery(query, { type: 'unknown', key: 'foo', value: 'bar' }).query).toBe(query.query);
expect(ds.modifyQuery(query, { type: 'unknown', options: { key: 'foo', value: 'bar' } }).query).toBe(query.query);
});
});
});

View File

@ -25,6 +25,7 @@ import {
ScopedVars,
TimeRange,
toUtc,
QueryFixAction,
} from '@grafana/data';
import { BackendSrvRequest, getBackendSrv, getDataSourceSrv } from '@grafana/runtime';
import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider';
@ -953,21 +954,25 @@ export class ElasticDatasource
return false;
}
modifyQuery(query: ElasticsearchQuery, action: { type: string; key: string; value: string }): ElasticsearchQuery {
modifyQuery(query: ElasticsearchQuery, action: QueryFixAction): ElasticsearchQuery {
if (!action.options) {
return query;
}
let expression = query.query ?? '';
switch (action.type) {
case 'ADD_FILTER': {
if (expression.length > 0) {
expression += ' AND ';
}
expression += `${action.key}:"${action.value}"`;
expression += `${action.options.key}:"${action.options.value}"`;
break;
}
case 'ADD_FILTER_OUT': {
if (expression.length > 0) {
expression += ' AND ';
}
expression += `-${action.key}:"${action.value}"`;
expression += `-${action.options.key}:"${action.options.value}"`;
break;
}
}

View File

@ -522,7 +522,7 @@ describe('LokiDatasource', () => {
describe('and query has no parser', () => {
it('then the correct label should be added for logs query', () => {
const query: LokiQuery = { refId: 'A', expr: '{bar="baz"}' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -532,7 +532,7 @@ describe('LokiDatasource', () => {
it('then the correctly escaped label should be added for logs query', () => {
const query: LokiQuery = { refId: 'A', expr: '{bar="baz"}' };
const action = { key: 'job', value: '\\test', type: 'ADD_FILTER' };
const action = { options: { key: 'job', value: '\\test' }, type: 'ADD_FILTER' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -542,7 +542,7 @@ describe('LokiDatasource', () => {
it('then the correct label should be added for metrics query', () => {
const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"}[5m])' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -552,7 +552,7 @@ describe('LokiDatasource', () => {
describe('and query has parser', () => {
it('then the correct label should be added for logs query', () => {
const query: LokiQuery = { refId: 'A', expr: '{bar="baz"} | logfmt' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -561,7 +561,7 @@ describe('LokiDatasource', () => {
});
it('then the correct label should be added for metrics query', () => {
const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"} | logfmt [5m])' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -576,7 +576,7 @@ describe('LokiDatasource', () => {
describe('and query has no parser', () => {
it('then the correct label should be added for logs query', () => {
const query: LokiQuery = { refId: 'A', expr: '{bar="baz"}' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER_OUT' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -586,7 +586,7 @@ describe('LokiDatasource', () => {
it('then the correctly escaped label should be added for logs query', () => {
const query: LokiQuery = { refId: 'A', expr: '{bar="baz"}' };
const action = { key: 'job', value: '"test', type: 'ADD_FILTER_OUT' };
const action = { options: { key: 'job', value: '"test' }, type: 'ADD_FILTER_OUT' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -596,7 +596,7 @@ describe('LokiDatasource', () => {
it('then the correct label should be added for metrics query', () => {
const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"}[5m])' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER_OUT' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -606,7 +606,7 @@ describe('LokiDatasource', () => {
describe('and query has parser', () => {
it('then the correct label should be added for logs query', () => {
const query: LokiQuery = { refId: 'A', expr: '{bar="baz"} | logfmt' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER_OUT' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);
@ -615,7 +615,7 @@ describe('LokiDatasource', () => {
});
it('then the correct label should be added for metrics query', () => {
const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"} | logfmt [5m])' };
const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' };
const action = { options: { key: 'job', value: 'grafana' }, type: 'ADD_FILTER_OUT' };
const ds = createLokiDSForTests();
const result = ds.modifyQuery(query, action);

View File

@ -32,6 +32,7 @@ import {
toUtc,
QueryHint,
getDefaultTimeRange,
QueryFixAction,
} from '@grafana/data';
import { FetchError, config, DataSourceWithBackend } from '@grafana/runtime';
import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider';
@ -389,15 +390,19 @@ export class LokiDatasource
return escapedValues.join('|');
}
modifyQuery(query: LokiQuery, action: any): LokiQuery {
modifyQuery(query: LokiQuery, action: QueryFixAction): LokiQuery {
let expression = query.expr ?? '';
switch (action.type) {
case 'ADD_FILTER': {
expression = this.addLabelToQuery(expression, action.key, '=', action.value);
if (action.options?.key && action.options?.value) {
expression = this.addLabelToQuery(expression, action.options.key, '=', action.options.value);
}
break;
}
case 'ADD_FILTER_OUT': {
expression = this.addLabelToQuery(expression, action.key, '!=', action.value);
if (action.options?.key && action.options?.value) {
expression = this.addLabelToQuery(expression, action.options.key, '!=', action.options.value);
}
break;
}
case 'ADD_LOGFMT_PARSER': {

View File

@ -224,8 +224,9 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
onClickHintFix = () => {
const { datasource, query, onChange, onRunQuery } = this.props;
const { hint } = this.state;
onChange(datasource.modifyQuery(query, hint!.fix!.action));
if (hint?.fix?.action) {
onChange(datasource.modifyQuery(query, hint.fix.action));
}
onRunQuery();
};

View File

@ -2154,7 +2154,7 @@ describe('modifyQuery', () => {
describe('and query has no labels', () => {
it('then the correct label should be added', () => {
const query: PromQuery = { refId: 'A', expr: 'go_goroutines' };
const action = { key: 'cluster', value: 'us-cluster', type: 'ADD_FILTER' };
const action = { options: { key: 'cluster', value: 'us-cluster' }, type: 'ADD_FILTER' };
const instanceSettings = { jsonData: {} } as unknown as DataSourceInstanceSettings<PromOptions>;
const ds = new PrometheusDatasource(instanceSettings, templateSrvStub as any, timeSrvStub as any);
@ -2168,7 +2168,7 @@ describe('modifyQuery', () => {
describe('and query has labels', () => {
it('then the correct label should be added', () => {
const query: PromQuery = { refId: 'A', expr: 'go_goroutines{cluster="us-cluster"}' };
const action = { key: 'pod', value: 'pod-123', type: 'ADD_FILTER' };
const action = { options: { key: 'pod', value: 'pod-123' }, type: 'ADD_FILTER' };
const instanceSettings = { jsonData: {} } as unknown as DataSourceInstanceSettings<PromOptions>;
const ds = new PrometheusDatasource(instanceSettings, templateSrvStub as any, timeSrvStub as any);
@ -2184,7 +2184,7 @@ describe('modifyQuery', () => {
describe('and query has no labels', () => {
it('then the correct label should be added', () => {
const query: PromQuery = { refId: 'A', expr: 'go_goroutines' };
const action = { key: 'cluster', value: 'us-cluster', type: 'ADD_FILTER_OUT' };
const action = { options: { key: 'cluster', value: 'us-cluster' }, type: 'ADD_FILTER_OUT' };
const instanceSettings = { jsonData: {} } as unknown as DataSourceInstanceSettings<PromOptions>;
const ds = new PrometheusDatasource(instanceSettings, templateSrvStub as any, timeSrvStub as any);
@ -2198,7 +2198,7 @@ describe('modifyQuery', () => {
describe('and query has labels', () => {
it('then the correct label should be added', () => {
const query: PromQuery = { refId: 'A', expr: 'go_goroutines{cluster="us-cluster"}' };
const action = { key: 'pod', value: 'pod-123', type: 'ADD_FILTER_OUT' };
const action = { options: { key: 'pod', value: 'pod-123' }, type: 'ADD_FILTER_OUT' };
const instanceSettings = { jsonData: {} } as unknown as DataSourceInstanceSettings<PromOptions>;
const ds = new PrometheusDatasource(instanceSettings, templateSrvStub as any, timeSrvStub as any);

View File

@ -22,6 +22,7 @@ import {
TimeRange,
DataFrame,
dateTime,
QueryFixAction,
} from '@grafana/data';
import {
BackendSrvRequest,
@ -1030,15 +1031,22 @@ export class PrometheusDatasource
}
}
modifyQuery(query: PromQuery, action: any): PromQuery {
modifyQuery(query: PromQuery, action: QueryFixAction): PromQuery {
let expression = query.expr ?? '';
switch (action.type) {
case 'ADD_FILTER': {
expression = addLabelToQuery(expression, action.key, action.value);
const { key, value } = action.options ?? {};
if (key && value) {
expression = addLabelToQuery(expression, key, value);
}
break;
}
case 'ADD_FILTER_OUT': {
expression = addLabelToQuery(expression, action.key, action.value, '!=');
const { key, value } = action.options ?? {};
if (key && value) {
expression = addLabelToQuery(expression, key, value, '!=');
}
break;
}
case 'ADD_HISTOGRAM_QUANTILE': {
@ -1054,8 +1062,8 @@ export class PrometheusDatasource
break;
}
case 'EXPAND_RULES': {
if (action.mapping) {
expression = expandRecordingRules(expression, action.mapping);
if (action.options) {
expression = expandRecordingRules(expression, action.options);
}
break;
}

View File

@ -103,7 +103,7 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet
action: {
type: 'EXPAND_RULES',
query,
mapping: mappingForQuery,
options: mappingForQuery,
},
} as any as QueryFix,
});

View File

@ -45,10 +45,12 @@ export const QueryBuilderHints = <T extends PromLokiVisualQuery>({
<Tooltip content={`${hint.label} ${hint.fix?.label}`} key={hint.type}>
<Button
onClick={() => {
const query = { expr: queryModeller.renderQuery(visualQuery), refId: '' };
const newQuery = datasource.modifyQuery(query, hint!.fix!.action);
const newVisualQuery = buildVisualQueryFromString(newQuery.expr);
return onChange(newVisualQuery.query);
if (hint?.fix?.action) {
const query = { expr: queryModeller.renderQuery(visualQuery), refId: '' };
const newQuery = datasource.modifyQuery(query, hint.fix.action);
const newVisualQuery = buildVisualQueryFromString(newQuery.expr);
return onChange(newVisualQuery.query);
}
}}
fill="outline"
size="sm"