mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Datasource: Change query filtering (#84656)
* call filterQuery from queryrunner * test query hide filtering * fix more broken tests * lint errrors * remove redundant filterQuery call * skip filter in variable queries * fix broken cypress test * change tooltip text * fix translations * fix comments * do not execute query is targets are empty * add more tests * remove unsued import * update translations * revert id change * change header text * update comment for hide prop * rename hide query prop * change tooltip and introduce different toggle state text * update tests * update comment and regenerate types * run extract again * fix broken e2e test * track event * fix build issues * revert changes in wire file
This commit is contained in:
parent
410f5e3e3a
commit
29d4f6a217
@ -62,16 +62,16 @@ describe('Panel edit tests - queries', () => {
|
|||||||
expect(resultIds.has('B:')).equals(true);
|
expect(resultIds.has('B:')).equals(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disable row with refId A
|
// Hide response for row with refId A
|
||||||
e2e.components.QueryEditorRow.actionButton('Disable query').eq(1).should('be.visible').click();
|
e2e.components.QueryEditorRow.actionButton('Hide response').eq(1).should('be.visible').click();
|
||||||
|
|
||||||
expectInspectorResultAndClose((keys) => {
|
expectInspectorResultAndClose((keys) => {
|
||||||
const length = keys.length;
|
const length = keys.length;
|
||||||
expect(keys[length - 1].innerText).equals('B:');
|
expect(keys[length - 1].innerText).equals('B:');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enable row with refId B
|
// Show response for row with refId A
|
||||||
e2e.components.QueryEditorRow.actionButton('Disable query').eq(1).should('be.visible').click();
|
e2e.components.QueryEditorRow.actionButton('Hide response').eq(1).should('be.visible').click();
|
||||||
|
|
||||||
expectInspectorResultAndClose((keys) => {
|
expectInspectorResultAndClose((keys) => {
|
||||||
const length = keys.length;
|
const length = keys.length;
|
||||||
|
@ -190,9 +190,6 @@ type VariableSupport<TQuery extends DataQuery, TOptions extends DataSourceJsonDa
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The main data source abstraction interface, represents an instance of a data source
|
* The main data source abstraction interface, represents an instance of a data source
|
||||||
*
|
|
||||||
* Although this is a class, datasource implementations do not *yet* need to extend it.
|
|
||||||
* As such, we can not yet add functions with default implementations.
|
|
||||||
*/
|
*/
|
||||||
abstract class DataSourceApi<
|
abstract class DataSourceApi<
|
||||||
TQuery extends DataQuery = DataQuery,
|
TQuery extends DataQuery = DataQuery,
|
||||||
@ -263,11 +260,12 @@ abstract class DataSourceApi<
|
|||||||
abstract testDatasource(): Promise<TestDataSourceResponse>;
|
abstract testDatasource(): Promise<TestDataSourceResponse>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is not called automatically unless running within the DataSourceWithBackend
|
* Optionally, you can implement this method to prevent certain queries from being executed.
|
||||||
*
|
* Return false to prevent the query from being executed.
|
||||||
* @deprecated
|
|
||||||
*/
|
*/
|
||||||
filterQuery?(query: TQuery): boolean;
|
filterQuery?(query: TQuery): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get hints for query improvements
|
* Get hints for query improvements
|
||||||
|
69
packages/grafana-data/src/utils/tests/mockDataSource.ts
Normal file
69
packages/grafana-data/src/utils/tests/mockDataSource.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataQuery,
|
||||||
|
DataQueryRequest,
|
||||||
|
DataQueryResponse,
|
||||||
|
DataSourceApi,
|
||||||
|
DataSourceInstanceSettings,
|
||||||
|
DataSourceJsonData,
|
||||||
|
DataSourcePluginMeta,
|
||||||
|
PluginMetaInfo,
|
||||||
|
PluginType,
|
||||||
|
TestDataSourceResponse,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
|
export interface TestQuery extends DataQuery {
|
||||||
|
query: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestJsonData extends DataSourceJsonData {
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info: PluginMetaInfo = {
|
||||||
|
author: {
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
links: [],
|
||||||
|
logos: {
|
||||||
|
large: '',
|
||||||
|
small: '',
|
||||||
|
},
|
||||||
|
screenshots: [],
|
||||||
|
updated: '',
|
||||||
|
version: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const meta: DataSourcePluginMeta<DataSourceJsonData> = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
type: PluginType.datasource,
|
||||||
|
info,
|
||||||
|
module: '',
|
||||||
|
baseUrl: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TestDataSettings: DataSourceInstanceSettings<TestJsonData> = {
|
||||||
|
jsonData: { url: 'http://localhost:3000' },
|
||||||
|
id: 0,
|
||||||
|
uid: '',
|
||||||
|
type: '',
|
||||||
|
name: 'Test Datasource',
|
||||||
|
meta,
|
||||||
|
readOnly: false,
|
||||||
|
access: 'direct',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class TestDataSource extends DataSourceApi<TestQuery, DataSourceJsonData> {
|
||||||
|
query(request: DataQueryRequest<TestQuery>): Promise<DataQueryResponse> | Observable<DataQueryResponse> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
testDatasource(): Promise<TestDataSourceResponse> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
constructor(instanceSettings: DataSourceInstanceSettings<TestJsonData> = TestDataSettings) {
|
||||||
|
super(instanceSettings);
|
||||||
|
}
|
||||||
|
}
|
@ -134,10 +134,6 @@ class DataSourceWithBackend<
|
|||||||
const { intervalMs, maxDataPoints, queryCachingTTL, range, requestId, hideFromInspector = false } = request;
|
const { intervalMs, maxDataPoints, queryCachingTTL, range, requestId, hideFromInspector = false } = request;
|
||||||
let targets = request.targets;
|
let targets = request.targets;
|
||||||
|
|
||||||
if (this.filterQuery) {
|
|
||||||
targets = targets.filter((q) => this.filterQuery!(q));
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasExpr = false;
|
let hasExpr = false;
|
||||||
const pluginIDs = new Set<string>();
|
const pluginIDs = new Set<string>();
|
||||||
const dsUIDs = new Set<string>();
|
const dsUIDs = new Set<string>();
|
||||||
@ -275,16 +271,6 @@ class DataSourceWithBackend<
|
|||||||
return queries.map((q) => this.applyTemplateVariables(q, scopedVars, filters));
|
return queries.map((q) => this.applyTemplateVariables(q, scopedVars, filters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Override to skip executing a query. Note this function may not be called
|
|
||||||
* if the query method is overwritten.
|
|
||||||
*
|
|
||||||
* @returns false if the query should be skipped
|
|
||||||
*
|
|
||||||
* @virtual
|
|
||||||
*/
|
|
||||||
filterQuery?(query: TQuery): boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override to apply template variables and adhoc filters. The result is usually also `TQuery`, but sometimes this can
|
* Override to apply template variables and adhoc filters. The result is usually also `TQuery`, but sometimes this can
|
||||||
* be used to modify the query structure before sending to the backend.
|
* be used to modify the query structure before sending to the backend.
|
||||||
|
@ -43,9 +43,7 @@ export interface DataQuery {
|
|||||||
*/
|
*/
|
||||||
datasource?: unknown;
|
datasource?: unknown;
|
||||||
/**
|
/**
|
||||||
* true if query is disabled (ie should not be returned to the dashboard)
|
* If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
* Note this does not always imply that the query should not be executed since
|
|
||||||
* the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
*/
|
*/
|
||||||
hide?: boolean;
|
hide?: boolean;
|
||||||
/**
|
/**
|
||||||
|
@ -23,9 +23,7 @@ DataQuery: {
|
|||||||
// By default, the UI will assign A->Z; however setting meaningful names may be useful.
|
// By default, the UI will assign A->Z; however setting meaningful names may be useful.
|
||||||
refId: string
|
refId: string
|
||||||
|
|
||||||
// true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
hide?: bool
|
hide?: bool
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
|
@ -234,9 +234,7 @@ type AzureMonitorQuery struct {
|
|||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
GrafanaTemplateVariableFn *any `json:"grafanaTemplateVariableFn,omitempty"`
|
GrafanaTemplateVariableFn *any `json:"grafanaTemplateVariableFn,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
Namespace *string `json:"namespace,omitempty"`
|
Namespace *string `json:"namespace,omitempty"`
|
||||||
|
|
||||||
@ -331,9 +329,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
|
@ -98,9 +98,7 @@ type CloudMonitoringQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Time interval in milliseconds.
|
// Time interval in milliseconds.
|
||||||
@ -138,9 +136,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
|
@ -134,9 +134,7 @@ type CloudWatchAnnotationQuery struct {
|
|||||||
// A name/value pair that is part of the identity of a metric. For example, you can get statistics for a specific EC2 instance by specifying the InstanceId dimension when you search for metrics.
|
// A name/value pair that is part of the identity of a metric. For example, you can get statistics for a specific EC2 instance by specifying the InstanceId dimension when you search for metrics.
|
||||||
Dimensions *Dimensions `json:"dimensions,omitempty"`
|
Dimensions *Dimensions `json:"dimensions,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Only show metrics that exactly match all defined dimension names.
|
// Only show metrics that exactly match all defined dimension names.
|
||||||
@ -188,9 +186,7 @@ type CloudWatchLogsQuery struct {
|
|||||||
// The CloudWatch Logs Insights query to execute
|
// The CloudWatch Logs Insights query to execute
|
||||||
Expression *string `json:"expression,omitempty"`
|
Expression *string `json:"expression,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
Id *string `json:"id,omitempty"`
|
Id *string `json:"id,omitempty"`
|
||||||
|
|
||||||
@ -238,9 +234,7 @@ type CloudWatchMetricsQuery struct {
|
|||||||
// Math expression query
|
// Math expression query
|
||||||
Expression *string `json:"expression,omitempty"`
|
Expression *string `json:"expression,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// ID can be used to reference other queries in math expressions. The ID can include numbers, letters, and underscore, and must start with a lowercase letter.
|
// ID can be used to reference other queries in math expressions. The ID can include numbers, letters, and underscore, and must start with a lowercase letter.
|
||||||
@ -300,9 +294,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
|
@ -172,9 +172,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
|
@ -26,9 +26,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
@ -52,9 +50,7 @@ type GrafanaPyroscopeDataQuery struct {
|
|||||||
// Allows to group the results.
|
// Allows to group the results.
|
||||||
GroupBy []string `json:"groupBy,omitempty"`
|
GroupBy []string `json:"groupBy,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specifies the query label selectors.
|
// Specifies the query label selectors.
|
||||||
|
@ -84,9 +84,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
@ -171,9 +169,7 @@ type TestDataDataQuery struct {
|
|||||||
ErrorType *TestDataDataQueryErrorType `json:"errorType,omitempty"`
|
ErrorType *TestDataDataQueryErrorType `json:"errorType,omitempty"`
|
||||||
FlamegraphDiff *bool `json:"flamegraphDiff,omitempty"`
|
FlamegraphDiff *bool `json:"flamegraphDiff,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
Labels *string `json:"labels,omitempty"`
|
Labels *string `json:"labels,omitempty"`
|
||||||
LevelColumn *bool `json:"levelColumn,omitempty"`
|
LevelColumn *bool `json:"levelColumn,omitempty"`
|
||||||
|
@ -46,9 +46,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
@ -73,9 +71,7 @@ type LokiDataQuery struct {
|
|||||||
// The LogQL query.
|
// The LogQL query.
|
||||||
Expr *string `json:"expr,omitempty"`
|
Expr *string `json:"expr,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// @deprecated, now use queryType.
|
// @deprecated, now use queryType.
|
||||||
|
@ -26,9 +26,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
@ -49,9 +47,7 @@ type ParcaDataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specifies the query label selectors.
|
// Specifies the query label selectors.
|
||||||
|
@ -52,9 +52,7 @@ type DataQuery struct {
|
|||||||
// TODO this shouldn't be unknown but DataSourceRef | null
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
Datasource *any `json:"datasource,omitempty"`
|
Datasource *any `json:"datasource,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Specify the query flavor
|
// Specify the query flavor
|
||||||
@ -88,9 +86,7 @@ type TempoQuery struct {
|
|||||||
// Filters that are used to query the metrics summary
|
// Filters that are used to query the metrics summary
|
||||||
GroupBy []TraceqlFilter `json:"groupBy,omitempty"`
|
GroupBy []TraceqlFilter `json:"groupBy,omitempty"`
|
||||||
|
|
||||||
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
// If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel.
|
||||||
// Note this does not always imply that the query should not be executed since
|
|
||||||
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Defines the maximum number of traces that are returned from Tempo
|
// Defines the maximum number of traces that are returned from Tempo
|
||||||
|
@ -161,7 +161,7 @@ export const QueryWrapper = ({
|
|||||||
queries={editorQueries}
|
queries={editorQueries}
|
||||||
renderHeaderExtras={() => <HeaderExtras query={query} index={index} error={error} />}
|
renderHeaderExtras={() => <HeaderExtras query={query} index={index} error={error} />}
|
||||||
app={CoreApp.UnifiedAlerting}
|
app={CoreApp.UnifiedAlerting}
|
||||||
hideDisableQuery={true}
|
hideHideQueryButton={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{showVizualisation && (
|
{showVizualisation && (
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import { DataSourceApi, dateTime, DataQuery } from '@grafana/data';
|
||||||
|
|
||||||
|
import { PanelModel } from '../dashboard/state';
|
||||||
|
import { createDashboardModelFixture } from '../dashboard/state/__fixtures__/dashboardFixtures';
|
||||||
|
import { TestQuery, getMockDataSource } from '../query/state/__mocks__/mockDataSource';
|
||||||
|
|
||||||
|
import { executeAnnotationQuery } from './executeAnnotationQuery';
|
||||||
|
import { AnnotationQueryOptions } from './types';
|
||||||
|
|
||||||
|
describe('executeAnnotationQuery', () => {
|
||||||
|
let filterQuerySpy: jest.SpyInstance;
|
||||||
|
let querySpy: jest.SpyInstance;
|
||||||
|
let ds: DataSourceApi;
|
||||||
|
|
||||||
|
const setup = ({ query, filterQuery }: { query: TestQuery; filterQuery?: typeof ds.filterQuery }) => {
|
||||||
|
const options: AnnotationQueryOptions = {
|
||||||
|
range: { from: dateTime(), to: dateTime(), raw: { from: '1h', to: 'now' } },
|
||||||
|
dashboard: createDashboardModelFixture({
|
||||||
|
panels: [{ id: 1, type: 'graph' }],
|
||||||
|
}),
|
||||||
|
panel: {} as PanelModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ds = getMockDataSource();
|
||||||
|
if (filterQuery) {
|
||||||
|
ds.filterQuery = filterQuery;
|
||||||
|
filterQuerySpy = jest.spyOn(ds, 'filterQuery');
|
||||||
|
}
|
||||||
|
querySpy = jest.spyOn(ds, 'query');
|
||||||
|
executeAnnotationQuery(options, ds, {
|
||||||
|
name: '',
|
||||||
|
enable: false,
|
||||||
|
iconColor: '',
|
||||||
|
target: query,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not call query method in case query is filtered out', async () => {
|
||||||
|
setup({
|
||||||
|
query: { q: 'SUM(foo)', refId: 'A' },
|
||||||
|
filterQuery: (query: TestQuery) => query.q !== 'SUM(foo)',
|
||||||
|
});
|
||||||
|
expect(filterQuerySpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(querySpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should call backend in case query is not filtered out', async () => {
|
||||||
|
setup({
|
||||||
|
filterQuery: (_: DataQuery) => true,
|
||||||
|
query: { q: 'SUM(foo)', refId: 'A' },
|
||||||
|
});
|
||||||
|
expect(filterQuerySpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(querySpy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
@ -22,7 +22,7 @@ import {
|
|||||||
toLegacyResponseData,
|
toLegacyResponseData,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { AngularComponent, getAngularLoader, getDataSourceSrv } from '@grafana/runtime';
|
import { AngularComponent, getAngularLoader, getDataSourceSrv, reportInteraction } from '@grafana/runtime';
|
||||||
import { Badge, ErrorBoundaryAlert } from '@grafana/ui';
|
import { Badge, ErrorBoundaryAlert } from '@grafana/ui';
|
||||||
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
|
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
|
||||||
import {
|
import {
|
||||||
@ -57,7 +57,7 @@ export interface Props<TQuery extends DataQuery> {
|
|||||||
onChange: (query: TQuery) => void;
|
onChange: (query: TQuery) => void;
|
||||||
onRunQuery: () => void;
|
onRunQuery: () => void;
|
||||||
visualization?: ReactNode;
|
visualization?: ReactNode;
|
||||||
hideDisableQuery?: boolean;
|
hideHideQueryButton?: boolean;
|
||||||
app?: CoreApp;
|
app?: CoreApp;
|
||||||
history?: Array<HistoryItem<TQuery>>;
|
history?: Array<HistoryItem<TQuery>>;
|
||||||
eventBus?: EventBusExtended;
|
eventBus?: EventBusExtended;
|
||||||
@ -341,7 +341,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onDisableQuery = () => {
|
onHideQuery = () => {
|
||||||
const { query, onChange, onRunQuery, onQueryToggled } = this.props;
|
const { query, onChange, onRunQuery, onQueryToggled } = this.props;
|
||||||
onChange({ ...query, hide: !query.hide });
|
onChange({ ...query, hide: !query.hide });
|
||||||
onRunQuery();
|
onRunQuery();
|
||||||
@ -349,6 +349,10 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
if (onQueryToggled) {
|
if (onQueryToggled) {
|
||||||
onQueryToggled(query.hide);
|
onQueryToggled(query.hide);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reportInteraction('query_editor_row_hide_query_clicked', {
|
||||||
|
hide: !query.hide,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onToggleHelp = () => {
|
onToggleHelp = () => {
|
||||||
@ -440,9 +444,9 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderActions = (props: QueryOperationRowRenderProps) => {
|
renderActions = (props: QueryOperationRowRenderProps) => {
|
||||||
const { query, hideDisableQuery = false } = this.props;
|
const { query, hideHideQueryButton: hideHideQueryButton = false } = this.props;
|
||||||
const { hasTextEditMode, datasource, showingHelp } = this.state;
|
const { hasTextEditMode, datasource, showingHelp } = this.state;
|
||||||
const isDisabled = !!query.hide;
|
const isHidden = !!query.hide;
|
||||||
|
|
||||||
const hasEditorHelp = datasource?.components?.QueryEditorHelp;
|
const hasEditorHelp = datasource?.components?.QueryEditorHelp;
|
||||||
|
|
||||||
@ -471,12 +475,17 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
icon="copy"
|
icon="copy"
|
||||||
onClick={this.onCopyQuery}
|
onClick={this.onCopyQuery}
|
||||||
/>
|
/>
|
||||||
{!hideDisableQuery ? (
|
{!hideHideQueryButton ? (
|
||||||
<QueryOperationToggleAction
|
<QueryOperationToggleAction
|
||||||
title={t('query-operation.header.disable-query', 'Disable query')}
|
dataTestId={selectors.components.QueryEditorRow.actionButton('Hide response')}
|
||||||
icon={isDisabled ? 'eye-slash' : 'eye'}
|
title={
|
||||||
active={isDisabled}
|
query.hide
|
||||||
onClick={this.onDisableQuery}
|
? t('query-operation.header.show-response', 'Show response')
|
||||||
|
: t('query-operation.header.hide-response', 'Hide response')
|
||||||
|
}
|
||||||
|
icon={isHidden ? 'eye-slash' : 'eye'}
|
||||||
|
active={isHidden}
|
||||||
|
onClick={this.onHideQuery}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<QueryOperationAction
|
<QueryOperationAction
|
||||||
@ -497,7 +506,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
queries={queries}
|
queries={queries}
|
||||||
onChangeDataSource={onChangeDataSource}
|
onChangeDataSource={onChangeDataSource}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
disabled={query.hide}
|
hidden={query.hide}
|
||||||
onClick={(e) => this.onToggleEditMode(e, props)}
|
onClick={(e) => this.onToggleEditMode(e, props)}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
collapsedText={!props.isOpen ? this.renderCollapsedText() : null}
|
collapsedText={!props.isOpen ? this.renderCollapsedText() : null}
|
||||||
@ -510,12 +519,12 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
render() {
|
render() {
|
||||||
const { query, index, visualization, collapsable } = this.props;
|
const { query, index, visualization, collapsable } = this.props;
|
||||||
const { datasource, showingHelp, data } = this.state;
|
const { datasource, showingHelp, data } = this.state;
|
||||||
const isDisabled = query.hide;
|
const isHidden = query.hide;
|
||||||
const error =
|
const error =
|
||||||
data?.error && data.error.refId === query.refId ? data.error : data?.errors?.find((e) => e.refId === query.refId);
|
data?.error && data.error.refId === query.refId ? data.error : data?.errors?.find((e) => e.refId === query.refId);
|
||||||
const rowClasses = classNames('query-editor-row', {
|
const rowClasses = classNames('query-editor-row', {
|
||||||
'query-editor-row--disabled': isDisabled,
|
'query-editor-row--disabled': isHidden,
|
||||||
'gf-form-disabled': isDisabled,
|
'gf-form-disabled': isHidden,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!datasource) {
|
if (!datasource) {
|
||||||
|
@ -102,7 +102,7 @@ function renderScenario(overrides: Partial<Props>) {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
dataSource: {} as DataSourceInstanceSettings,
|
dataSource: {} as DataSourceInstanceSettings,
|
||||||
disabled: false,
|
hidden: false,
|
||||||
onChange: jest.fn(),
|
onChange: jest.fn(),
|
||||||
onClick: jest.fn(),
|
onClick: jest.fn(),
|
||||||
collapsedText: '',
|
collapsedText: '',
|
||||||
|
@ -9,7 +9,7 @@ import { DataSourcePicker } from 'app/features/datasources/components/picker/Dat
|
|||||||
export interface Props<TQuery extends DataQuery = DataQuery> {
|
export interface Props<TQuery extends DataQuery = DataQuery> {
|
||||||
query: TQuery;
|
query: TQuery;
|
||||||
queries: TQuery[];
|
queries: TQuery[];
|
||||||
disabled?: boolean;
|
hidden?: boolean;
|
||||||
dataSource: DataSourceInstanceSettings;
|
dataSource: DataSourceInstanceSettings;
|
||||||
renderExtras?: () => ReactNode;
|
renderExtras?: () => ReactNode;
|
||||||
onChangeDataSource?: (settings: DataSourceInstanceSettings) => void;
|
onChangeDataSource?: (settings: DataSourceInstanceSettings) => void;
|
||||||
@ -20,7 +20,7 @@ export interface Props<TQuery extends DataQuery = DataQuery> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const QueryEditorRowHeader = <TQuery extends DataQuery>(props: Props<TQuery>) => {
|
export const QueryEditorRowHeader = <TQuery extends DataQuery>(props: Props<TQuery>) => {
|
||||||
const { query, queries, onChange, collapsedText, renderExtras, disabled } = props;
|
const { query, queries, onChange, collapsedText, renderExtras, hidden } = props;
|
||||||
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||||
@ -117,7 +117,7 @@ export const QueryEditorRowHeader = <TQuery extends DataQuery>(props: Props<TQue
|
|||||||
)}
|
)}
|
||||||
{renderDataSource(props, styles)}
|
{renderDataSource(props, styles)}
|
||||||
{renderExtras && <div className={styles.itemWrapper}>{renderExtras()}</div>}
|
{renderExtras && <div className={styles.itemWrapper}>{renderExtras()}</div>}
|
||||||
{disabled && <em className={styles.contextInfo}>Disabled</em>}
|
{hidden && <em className={styles.contextInfo}>Hidden</em>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{collapsedText && <div className={styles.collapsedText}>{collapsedText}</div>}
|
{collapsedText && <div className={styles.collapsedText}>{collapsedText}</div>}
|
||||||
|
77
public/app/features/query/state/__mocks__/mockDataSource.ts
Normal file
77
public/app/features/query/state/__mocks__/mockDataSource.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataQuery,
|
||||||
|
DataSourceJsonData,
|
||||||
|
PluginMetaInfo,
|
||||||
|
DataSourcePluginMeta,
|
||||||
|
PluginType,
|
||||||
|
DataSourceInstanceSettings,
|
||||||
|
DataSourceApi,
|
||||||
|
DataQueryRequest,
|
||||||
|
DataQueryResponse,
|
||||||
|
TestDataSourceResponse,
|
||||||
|
} from '@grafana/data';
|
||||||
|
|
||||||
|
export interface TestQuery extends DataQuery {
|
||||||
|
q?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestJsonData extends DataSourceJsonData {
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info: PluginMetaInfo = {
|
||||||
|
author: {
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
links: [],
|
||||||
|
logos: {
|
||||||
|
large: '',
|
||||||
|
small: '',
|
||||||
|
},
|
||||||
|
screenshots: [],
|
||||||
|
updated: '',
|
||||||
|
version: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const meta: DataSourcePluginMeta<DataSourceJsonData> = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
type: PluginType.datasource,
|
||||||
|
info,
|
||||||
|
module: '',
|
||||||
|
baseUrl: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TestDataSettings: DataSourceInstanceSettings<TestJsonData> = {
|
||||||
|
jsonData: { url: 'http://localhost:3000' },
|
||||||
|
id: 0,
|
||||||
|
uid: '',
|
||||||
|
type: '',
|
||||||
|
name: 'Test Datasource',
|
||||||
|
meta,
|
||||||
|
readOnly: false,
|
||||||
|
access: 'direct',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class TestDataSource extends DataSourceApi<TestQuery, DataSourceJsonData, {}> {
|
||||||
|
constructor(instanceSettings: DataSourceInstanceSettings<TestJsonData> = TestDataSettings) {
|
||||||
|
super(instanceSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
query(request: DataQueryRequest<TestQuery>): Promise<DataQueryResponse> | Observable<DataQueryResponse> {
|
||||||
|
return Promise.resolve({
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testDatasource(): Promise<TestDataSourceResponse> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMockDataSource = () => {
|
||||||
|
return new TestDataSource();
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
import { Observable, Subscriber, Subscription } from 'rxjs';
|
import { Observable, Subscriber, Subscription } from 'rxjs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CoreApp,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
DataQueryRequest,
|
DataQueryRequest,
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
@ -11,12 +12,14 @@ import {
|
|||||||
PanelData,
|
PanelData,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { setEchoSrv } from '@grafana/runtime';
|
import { setEchoSrv } from '@grafana/runtime';
|
||||||
|
import { DataQuery } from '@grafana/schema';
|
||||||
|
|
||||||
import { deepFreeze } from '../../../../test/core/redux/reducerTester';
|
import { deepFreeze } from '../../../../test/core/redux/reducerTester';
|
||||||
import { Echo } from '../../../core/services/echo/Echo';
|
import { Echo } from '../../../core/services/echo/Echo';
|
||||||
import { createDashboardModelFixture } from '../../dashboard/state/__fixtures__/dashboardFixtures';
|
import { createDashboardModelFixture } from '../../dashboard/state/__fixtures__/dashboardFixtures';
|
||||||
|
|
||||||
import { runRequest } from './runRequest';
|
import { getMockDataSource, TestQuery } from './__mocks__/mockDataSource';
|
||||||
|
import { callQueryMethod, runRequest } from './runRequest';
|
||||||
|
|
||||||
jest.mock('app/core/services/backend_srv');
|
jest.mock('app/core/services/backend_srv');
|
||||||
|
|
||||||
@ -371,6 +374,193 @@ describe('runRequest', () => {
|
|||||||
expect(ctx.results[1].series.length).toBe(1);
|
expect(ctx.results[1].series.length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
runRequestScenario('When some queries are hidden', (ctx) => {
|
||||||
|
ctx.setup(() => {
|
||||||
|
ctx.request.targets = [{ refId: 'A', hide: true }, { refId: 'B' }];
|
||||||
|
ctx.start();
|
||||||
|
ctx.emitPacket({
|
||||||
|
data: [
|
||||||
|
{ name: 'DataA-1', refId: 'A' },
|
||||||
|
{ name: 'DataA-2', refId: 'A' },
|
||||||
|
{ name: 'DataB-1', refId: 'B' },
|
||||||
|
{ name: 'DataB-2', refId: 'B' },
|
||||||
|
],
|
||||||
|
key: 'A',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter out responses that are associated with the hidden queries', () => {
|
||||||
|
expect(ctx.results[0].series.length).toBe(2);
|
||||||
|
expect(ctx.results[0].series[0].name).toBe('DataB-1');
|
||||||
|
expect(ctx.results[0].series[1].name).toBe('DataB-2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('callQueryMethod', () => {
|
||||||
|
let request: DataQueryRequest<TestQuery>;
|
||||||
|
let filterQuerySpy: jest.SpyInstance;
|
||||||
|
let querySpy: jest.SpyInstance;
|
||||||
|
let defaultQuerySpy: jest.SpyInstance;
|
||||||
|
let ds: DataSourceApi;
|
||||||
|
|
||||||
|
const setup = ({
|
||||||
|
targets,
|
||||||
|
filterQuery,
|
||||||
|
getDefaultQuery,
|
||||||
|
queryFunction,
|
||||||
|
}: {
|
||||||
|
targets: TestQuery[];
|
||||||
|
getDefaultQuery?: (app: CoreApp) => Partial<TestQuery>;
|
||||||
|
filterQuery?: typeof ds.filterQuery;
|
||||||
|
queryFunction?: typeof ds.query;
|
||||||
|
}) => {
|
||||||
|
request = {
|
||||||
|
range: {
|
||||||
|
from: dateTime(),
|
||||||
|
to: dateTime(),
|
||||||
|
raw: { from: '1h', to: 'now' },
|
||||||
|
},
|
||||||
|
targets,
|
||||||
|
requestId: '',
|
||||||
|
interval: '',
|
||||||
|
intervalMs: 0,
|
||||||
|
scopedVars: {},
|
||||||
|
timezone: '',
|
||||||
|
app: '',
|
||||||
|
startTime: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ds = getMockDataSource();
|
||||||
|
if (filterQuery) {
|
||||||
|
ds.filterQuery = filterQuery;
|
||||||
|
filterQuerySpy = jest.spyOn(ds, 'filterQuery');
|
||||||
|
}
|
||||||
|
if (getDefaultQuery) {
|
||||||
|
ds.getDefaultQuery = getDefaultQuery;
|
||||||
|
defaultQuerySpy = jest.spyOn(ds, 'getDefaultQuery');
|
||||||
|
}
|
||||||
|
querySpy = jest.spyOn(ds, 'query');
|
||||||
|
callQueryMethod(ds, request, queryFunction);
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should call filterQuery and exclude them from the request', async () => {
|
||||||
|
setup({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
refId: 'A',
|
||||||
|
q: 'SUM(foo)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'B',
|
||||||
|
q: 'SUM(foo2)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'C',
|
||||||
|
q: 'SUM(foo3)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filterQuery: (query: DataQuery) => query.refId !== 'A',
|
||||||
|
});
|
||||||
|
expect(filterQuerySpy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(querySpy).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
targets: [
|
||||||
|
{ q: 'SUM(foo2)', refId: 'B' },
|
||||||
|
{ q: 'SUM(foo3)', refId: 'C' },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not call query function in case targets are empty', async () => {
|
||||||
|
setup({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
refId: 'A',
|
||||||
|
q: 'SUM(foo)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'B',
|
||||||
|
q: 'SUM(foo2)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'C',
|
||||||
|
q: 'SUM(foo3)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filterQuery: (_: DataQuery) => false,
|
||||||
|
});
|
||||||
|
expect(filterQuerySpy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(querySpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not call filterQuery in case a custom query method is provided', async () => {
|
||||||
|
const queryFunctionMock = jest.fn().mockResolvedValue({ data: [] });
|
||||||
|
setup({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
refId: 'A',
|
||||||
|
q: 'SUM(foo)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'B',
|
||||||
|
q: 'SUM(foo2)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'C',
|
||||||
|
q: 'SUM(foo3)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
queryFunction: queryFunctionMock,
|
||||||
|
filterQuery: (query: DataQuery) => query.refId !== 'A',
|
||||||
|
});
|
||||||
|
expect(filterQuerySpy).not.toHaveBeenCalled();
|
||||||
|
expect(queryFunctionMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
targets: [
|
||||||
|
{ q: 'SUM(foo)', refId: 'A' },
|
||||||
|
{ q: 'SUM(foo2)', refId: 'B' },
|
||||||
|
{ q: 'SUM(foo3)', refId: 'C' },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should get ds default query when query is empty', async () => {
|
||||||
|
setup({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
refId: 'A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'C',
|
||||||
|
q: 'SUM(foo3)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
getDefaultQuery: (_: CoreApp) => ({
|
||||||
|
q: 'SUM(foo2)',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(defaultQuerySpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(querySpy).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
targets: [
|
||||||
|
{ q: 'SUM(foo2)', refId: 'A' },
|
||||||
|
{ q: 'SUM(foo2)', refId: 'B' },
|
||||||
|
{ q: 'SUM(foo3)', refId: 'C' },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const expectThatRangeHasNotMutated = (ctx: ScenarioCtx) => {
|
const expectThatRangeHasNotMutated = (ctx: ScenarioCtx) => {
|
||||||
|
@ -150,6 +150,12 @@ export function runRequest(
|
|||||||
throw new Error(`Expected response data to be array, got ${typeof packet.data}.`);
|
throw new Error(`Expected response data to be array, got ${typeof packet.data}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filter out responses for hidden queries
|
||||||
|
const hiddenQueries = request.targets.filter((q) => q.hide);
|
||||||
|
for (const query of hiddenQueries) {
|
||||||
|
packet.data = packet.data.filter((d) => d.refId !== query.refId);
|
||||||
|
}
|
||||||
|
|
||||||
request.endTime = Date.now();
|
request.endTime = Date.now();
|
||||||
|
|
||||||
state = processResponsePacket(packet, state);
|
state = processResponsePacket(packet, state);
|
||||||
@ -195,6 +201,15 @@ export function callQueryMethod(
|
|||||||
: t
|
: t
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// do not filter queries in case a custom query function is provided (for example in variable queries)
|
||||||
|
if (!queryFunction) {
|
||||||
|
request.targets = request.targets.filter((t) => datasource.filterQuery?.(t) ?? true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.targets.length === 0) {
|
||||||
|
return of<DataQueryResponse>({ data: [] });
|
||||||
|
}
|
||||||
|
|
||||||
// If its a public datasource, just return the result. Expressions will be handled on the backend.
|
// If its a public datasource, just return the result. Expressions will be handled on the backend.
|
||||||
if (config.publicDashboardAccessToken) {
|
if (config.publicDashboardAccessToken) {
|
||||||
return from(datasource.query(request));
|
return from(datasource.query(request));
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { DataQueryRequest, DataSourceInstanceSettings, toUtc } from '@grafana/data';
|
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||||
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; // will use the version in __mocks__
|
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; // will use the version in __mocks__
|
||||||
|
|
||||||
import CloudMonitoringDataSource from '../datasource';
|
import CloudMonitoringDataSource from '../datasource';
|
||||||
import { CloudMonitoringQuery } from '../types/query';
|
|
||||||
import { CloudMonitoringOptions, CustomVariableModel } from '../types/types';
|
import { CloudMonitoringOptions, CustomVariableModel } from '../types/types';
|
||||||
|
|
||||||
let getTempVars = () => [] as CustomVariableModel[];
|
let getTempVars = () => [] as CustomVariableModel[];
|
||||||
@ -36,48 +35,6 @@ function getTestcontext({ response = {}, throws = false, templateSrv = getTempla
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('CloudMonitoringDataSource', () => {
|
describe('CloudMonitoringDataSource', () => {
|
||||||
describe('When performing query', () => {
|
|
||||||
describe('and no time series data is returned', () => {
|
|
||||||
it('should return a list of datapoints', async () => {
|
|
||||||
const options = {
|
|
||||||
range: {
|
|
||||||
from: toUtc('2017-08-22T20:00:00Z'),
|
|
||||||
to: toUtc('2017-08-22T23:59:00Z'),
|
|
||||||
},
|
|
||||||
rangeRaw: {
|
|
||||||
from: 'now-4h',
|
|
||||||
to: 'now',
|
|
||||||
},
|
|
||||||
targets: [
|
|
||||||
{
|
|
||||||
refId: 'A',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as DataQueryRequest<CloudMonitoringQuery>;
|
|
||||||
|
|
||||||
const response = {
|
|
||||||
results: {
|
|
||||||
A: {
|
|
||||||
refId: 'A',
|
|
||||||
meta: {
|
|
||||||
rawQuery: 'arawquerystring',
|
|
||||||
},
|
|
||||||
series: null,
|
|
||||||
tables: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { ds } = getTestcontext({ response });
|
|
||||||
|
|
||||||
await expect(ds.query(options)).toEmitValuesWith((received) => {
|
|
||||||
const results = received[0];
|
|
||||||
expect(results.data.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when interpolating a template variable for the filter', () => {
|
describe('when interpolating a template variable for the filter', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getTempVars = () => [] as CustomVariableModel[];
|
getTempVars = () => [] as CustomVariableModel[];
|
||||||
|
@ -286,34 +286,6 @@ describe('PostgreSQLDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When performing a query with hidden target', () => {
|
|
||||||
it('should return empty result and backendSrv.fetch should not be called', async () => {
|
|
||||||
const options = {
|
|
||||||
range: {
|
|
||||||
from: dateTime(1432288354),
|
|
||||||
to: dateTime(1432288401),
|
|
||||||
},
|
|
||||||
targets: [
|
|
||||||
{
|
|
||||||
format: 'table',
|
|
||||||
rawQuery: true,
|
|
||||||
rawSql: 'select time, metric, value from grafana_metric',
|
|
||||||
refId: 'A',
|
|
||||||
datasource: 'gdev-ds',
|
|
||||||
hide: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as unknown as DataQueryRequest<SQLQuery>;
|
|
||||||
|
|
||||||
const { ds } = setupTestContext({});
|
|
||||||
|
|
||||||
await expect(ds.query(options)).toEmitValuesWith((received) => {
|
|
||||||
expect(received[0]).toEqual({ data: [] });
|
|
||||||
expect(fetchMock).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When runSql returns an empty dataframe', () => {
|
describe('When runSql returns an empty dataframe', () => {
|
||||||
const response = {
|
const response = {
|
||||||
results: {
|
results: {
|
||||||
|
@ -3,9 +3,7 @@ import { of } from 'rxjs';
|
|||||||
import {
|
import {
|
||||||
dataFrameToJSON,
|
dataFrameToJSON,
|
||||||
getDefaultTimeRange,
|
getDefaultTimeRange,
|
||||||
DataQueryRequest,
|
|
||||||
DataSourceInstanceSettings,
|
DataSourceInstanceSettings,
|
||||||
dateTime,
|
|
||||||
FieldType,
|
FieldType,
|
||||||
createDataFrame,
|
createDataFrame,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
@ -47,34 +45,6 @@ describe('MySQLDatasource', () => {
|
|||||||
replace: (text: string) => text,
|
replace: (text: string) => text,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('When performing a query with hidden target', () => {
|
|
||||||
it('should return empty result and backendSrv.fetch should not be called', async () => {
|
|
||||||
const options = {
|
|
||||||
range: {
|
|
||||||
from: dateTime(1432288354),
|
|
||||||
to: dateTime(1432288401),
|
|
||||||
},
|
|
||||||
targets: [
|
|
||||||
{
|
|
||||||
format: 'table',
|
|
||||||
rawQuery: true,
|
|
||||||
rawSql: 'select time, metric, value from grafana_metric',
|
|
||||||
refId: 'A',
|
|
||||||
datasource: 'gdev-ds',
|
|
||||||
hide: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as unknown as DataQueryRequest<SQLQuery>;
|
|
||||||
|
|
||||||
const { ds, fetchMock } = setupTestContext({});
|
|
||||||
|
|
||||||
await expect(ds.query(options)).toEmitValuesWith((received) => {
|
|
||||||
expect(received[0]).toEqual({ data: [] });
|
|
||||||
expect(fetchMock).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When runSql returns an empty dataframe', () => {
|
describe('When runSql returns an empty dataframe', () => {
|
||||||
const response = {
|
const response = {
|
||||||
results: {
|
results: {
|
||||||
|
@ -1343,11 +1343,12 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"collapse-row": "Collapse query row",
|
"collapse-row": "Collapse query row",
|
||||||
"datasource-help": "Show data source help",
|
"datasource-help": "Show data source help",
|
||||||
"disable-query": "Disable query",
|
|
||||||
"drag-and-drop": "Drag and drop to reorder",
|
"drag-and-drop": "Drag and drop to reorder",
|
||||||
"duplicate-query": "Duplicate query",
|
"duplicate-query": "Duplicate query",
|
||||||
"expand-row": "Expand query row",
|
"expand-row": "Expand query row",
|
||||||
|
"hide-response": "Hide response",
|
||||||
"remove-query": "Remove query",
|
"remove-query": "Remove query",
|
||||||
|
"show-response": "Show response",
|
||||||
"toggle-edit-mode": "Toggle text edit mode"
|
"toggle-edit-mode": "Toggle text edit mode"
|
||||||
},
|
},
|
||||||
"query-editor-not-exported": "Data source plugin does not export any Query Editor component"
|
"query-editor-not-exported": "Data source plugin does not export any Query Editor component"
|
||||||
|
@ -1343,11 +1343,12 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"collapse-row": "Cőľľäpşę qūęřy řőŵ",
|
"collapse-row": "Cőľľäpşę qūęřy řőŵ",
|
||||||
"datasource-help": "Ŝĥőŵ đäŧä şőūřčę ĥęľp",
|
"datasource-help": "Ŝĥőŵ đäŧä şőūřčę ĥęľp",
|
||||||
"disable-query": "Đįşäþľę qūęřy",
|
|
||||||
"drag-and-drop": "Đřäģ äʼnđ đřőp ŧő řęőřđęř",
|
"drag-and-drop": "Đřäģ äʼnđ đřőp ŧő řęőřđęř",
|
||||||
"duplicate-query": "Đūpľįčäŧę qūęřy",
|
"duplicate-query": "Đūpľįčäŧę qūęřy",
|
||||||
"expand-row": "Ēχpäʼnđ qūęřy řőŵ",
|
"expand-row": "Ēχpäʼnđ qūęřy řőŵ",
|
||||||
|
"hide-response": "Ħįđę řęşpőʼnşę",
|
||||||
"remove-query": "Ŗęmővę qūęřy",
|
"remove-query": "Ŗęmővę qūęřy",
|
||||||
|
"show-response": "Ŝĥőŵ řęşpőʼnşę",
|
||||||
"toggle-edit-mode": "Ŧőģģľę ŧęχŧ ęđįŧ mőđę"
|
"toggle-edit-mode": "Ŧőģģľę ŧęχŧ ęđįŧ mőđę"
|
||||||
},
|
},
|
||||||
"query-editor-not-exported": "Đäŧä şőūřčę pľūģįʼn đőęş ʼnőŧ ęχpőřŧ äʼny Qūęřy Ēđįŧőř čőmpőʼnęʼnŧ"
|
"query-editor-not-exported": "Đäŧä şőūřčę pľūģįʼn đőęş ʼnőŧ ęχpőřŧ äʼny Qūęřy Ēđįŧőř čőmpőʼnęʼnŧ"
|
||||||
|
Loading…
Reference in New Issue
Block a user