mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Datasources: Use getDefaultQuery in annotations editors (#61870)
+ Add Cloudwatch default annotation
This commit is contained in:
parent
b6f477ae03
commit
f1a2a76897
@ -113,4 +113,9 @@ export interface AnnotationSupport<TQuery extends DataQuery = DataQuery, TAnno =
|
|||||||
* Specify a custom QueryEditor for the annotation page. If not specified, the standard one will be used
|
* Specify a custom QueryEditor for the annotation page. If not specified, the standard one will be used
|
||||||
*/
|
*/
|
||||||
QueryEditor?: ComponentType<AnnotationQueryEditorProps<TQuery>>;
|
QueryEditor?: ComponentType<AnnotationQueryEditorProps<TQuery>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define this method if you want to pre-populate the editor with a default query
|
||||||
|
*/
|
||||||
|
getDefaultQuery?(): Partial<TQuery>;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { AnnotationQuery, DataSourceApi, DataSourceInstanceSettings } from '@grafana/data/src';
|
||||||
|
|
||||||
|
import StandardAnnotationQueryEditor, { Props as EditorProps } from './StandardAnnotationQueryEditor';
|
||||||
|
|
||||||
|
const setup = (customProps: Partial<EditorProps>) => {
|
||||||
|
const props: EditorProps = {
|
||||||
|
datasource: {} as unknown as DataSourceApi,
|
||||||
|
datasourceInstanceSettings: {} as DataSourceInstanceSettings,
|
||||||
|
annotation: {} as AnnotationQuery,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
...customProps,
|
||||||
|
};
|
||||||
|
const { rerender } = render(<StandardAnnotationQueryEditor {...props} />);
|
||||||
|
return { rerender, props };
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('app/features/dashboard/services/DashboardSrv', () => ({
|
||||||
|
getDashboardSrv: jest.fn().mockReturnValue({
|
||||||
|
getCurrent: jest.fn().mockReturnValue(null),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('app/features/dashboard/services/TimeSrv', () => ({
|
||||||
|
getTimeSrv: jest.fn().mockReturnValue({
|
||||||
|
timeRange: jest.fn().mockReturnValue({}),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('StandardAnnotationQueryEditor', () => {
|
||||||
|
it('should fill out a default query if it is defined and pass it to the Query Editor', () => {
|
||||||
|
const { props } = setup({
|
||||||
|
annotation: { name: 'initialAnn', target: { refId: 'initialAnnotationRef' } } as AnnotationQuery,
|
||||||
|
|
||||||
|
datasource: {
|
||||||
|
annotations: {
|
||||||
|
QueryEditor: jest.fn(() => <div>Editor</div>),
|
||||||
|
getDefaultQuery: jest.fn().mockImplementation(() => ({ queryType: 'defaultAnnotationsQuery' })),
|
||||||
|
prepareAnnotation: (annotation: AnnotationQuery) => annotation,
|
||||||
|
},
|
||||||
|
} as unknown as DataSourceApi,
|
||||||
|
});
|
||||||
|
expect(props.datasource?.annotations?.getDefaultQuery).toBeDefined();
|
||||||
|
expect(props.datasource?.annotations?.QueryEditor).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
query: expect.objectContaining({ queryType: 'defaultAnnotationsQuery', refId: 'initialAnnotationRef' }),
|
||||||
|
}),
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should keep and pass the initial query if the defaultQuery is not defined', () => {
|
||||||
|
const { props } = setup({
|
||||||
|
annotation: { name: 'initialAnn', target: { refId: 'initialAnnotationRef' } } as AnnotationQuery,
|
||||||
|
datasource: {
|
||||||
|
annotations: {
|
||||||
|
QueryEditor: jest.fn(() => <div>Editor</div>),
|
||||||
|
prepareAnnotation: (annotation: AnnotationQuery) => annotation,
|
||||||
|
},
|
||||||
|
} as unknown as DataSourceApi,
|
||||||
|
});
|
||||||
|
expect(props.datasource?.annotations?.QueryEditor).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
query: expect.objectContaining({ refId: 'initialAnnotationRef' }),
|
||||||
|
}),
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -22,7 +22,7 @@ import { AnnotationQueryResponse } from '../types';
|
|||||||
|
|
||||||
import { AnnotationFieldMapper } from './AnnotationResultMapper';
|
import { AnnotationFieldMapper } from './AnnotationResultMapper';
|
||||||
|
|
||||||
interface Props {
|
export interface Props {
|
||||||
datasource: DataSourceApi;
|
datasource: DataSourceApi;
|
||||||
datasourceInstanceSettings: DataSourceInstanceSettings;
|
datasourceInstanceSettings: DataSourceInstanceSettings;
|
||||||
annotation: AnnotationQuery<DataQuery>;
|
annotation: AnnotationQuery<DataQuery>;
|
||||||
@ -186,7 +186,11 @@ export default class StandardAnnotationQueryEditor extends PureComponent<Props,
|
|||||||
return <div>Annotations are not supported. This datasource needs to export a QueryEditor</div>;
|
return <div>Annotations are not supported. This datasource needs to export a QueryEditor</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = annotation.target ?? { refId: 'Anno' };
|
const query = {
|
||||||
|
...datasource.annotations?.getDefaultQuery?.(),
|
||||||
|
...(annotation.target ?? { refId: 'Anno' }),
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DataSourcePluginContextProvider instanceSettings={datasourceInstanceSettings}>
|
<DataSourcePluginContextProvider instanceSettings={datasourceInstanceSettings}>
|
||||||
|
@ -23,7 +23,11 @@ export function executeAnnotationQuery(
|
|||||||
...datasource.annotations,
|
...datasource.annotations,
|
||||||
};
|
};
|
||||||
|
|
||||||
const annotation = processor.prepareAnnotation!(savedJsonAnno);
|
const annotationWithDefaults = {
|
||||||
|
...processor.getDefaultQuery?.(),
|
||||||
|
...savedJsonAnno,
|
||||||
|
};
|
||||||
|
const annotation = processor.prepareAnnotation!(annotationWithDefaults);
|
||||||
if (!annotation) {
|
if (!annotation) {
|
||||||
return of({});
|
return of({});
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import {
|
|||||||
import { toDataQueryError } from '@grafana/runtime';
|
import { toDataQueryError } from '@grafana/runtime';
|
||||||
import { isExpressionReference } from '@grafana/runtime/src/utils/DataSourceWithBackend';
|
import { isExpressionReference } from '@grafana/runtime/src/utils/DataSourceWithBackend';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
import { queryIsEmpty } from 'app/core/utils/query';
|
|
||||||
import { dataSource as expressionDatasource } from 'app/features/expressions/ExpressionDatasource';
|
import { dataSource as expressionDatasource } from 'app/features/expressions/ExpressionDatasource';
|
||||||
import { ExpressionQuery } from 'app/features/expressions/types';
|
import { ExpressionQuery } from 'app/features/expressions/types';
|
||||||
|
|
||||||
@ -176,10 +175,11 @@ export function callQueryMethod(
|
|||||||
request: DataQueryRequest,
|
request: DataQueryRequest,
|
||||||
queryFunction?: typeof datasource.query
|
queryFunction?: typeof datasource.query
|
||||||
) {
|
) {
|
||||||
// If the datasource has defined a default query, make sure it's applied if the query is empty
|
// If the datasource has defined a default query, make sure it's applied
|
||||||
request.targets = request.targets.map((t) =>
|
request.targets = request.targets.map((t) => ({
|
||||||
queryIsEmpty(t) ? { ...datasource?.getDefaultQuery?.(CoreApp.PanelEditor), ...t } : t
|
...datasource?.getDefaultQuery?.(CoreApp.PanelEditor),
|
||||||
);
|
...t,
|
||||||
|
}));
|
||||||
|
|
||||||
// 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 (datasource.type === 'public-ds') {
|
if (datasource.type === 'public-ds') {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { AnnotationQuery } from '@grafana/data';
|
import { AnnotationQuery } from '@grafana/data';
|
||||||
|
|
||||||
import { AnnotationQueryEditor } from './components/AnnotationQueryEditor';
|
import { AnnotationQueryEditor } from './components/AnnotationQueryEditor';
|
||||||
|
import { DEFAULT_ANNOTATIONS_QUERY } from './defaultQueries';
|
||||||
import { isCloudWatchAnnotation } from './guards';
|
import { isCloudWatchAnnotation } from './guards';
|
||||||
import { CloudWatchAnnotationQuery, CloudWatchQuery, LegacyAnnotationQuery } from './types';
|
import { CloudWatchAnnotationQuery, CloudWatchQuery, LegacyAnnotationQuery } from './types';
|
||||||
|
|
||||||
@ -24,8 +25,8 @@ export const CloudWatchAnnotationSupport = {
|
|||||||
target: {
|
target: {
|
||||||
...query.target,
|
...query.target,
|
||||||
...query,
|
...query,
|
||||||
statistic: query.statistic || 'Average',
|
statistic: query.statistic || DEFAULT_ANNOTATIONS_QUERY.statistic,
|
||||||
region: query.region || 'default',
|
region: query.region || DEFAULT_ANNOTATIONS_QUERY.region,
|
||||||
queryMode: 'Annotations',
|
queryMode: 'Annotations',
|
||||||
refId: query.refId || 'annotationQuery',
|
refId: query.refId || 'annotationQuery',
|
||||||
},
|
},
|
||||||
@ -56,5 +57,8 @@ export const CloudWatchAnnotationSupport = {
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
getDefaultQuery() {
|
||||||
|
return DEFAULT_ANNOTATIONS_QUERY;
|
||||||
|
},
|
||||||
QueryEditor: AnnotationQueryEditor,
|
QueryEditor: AnnotationQueryEditor,
|
||||||
};
|
};
|
||||||
|
@ -14,10 +14,10 @@ import { setupForLogs } from './__mocks__/logsTestContext';
|
|||||||
import { validLogsQuery, validMetricSearchBuilderQuery } from './__mocks__/queries';
|
import { validLogsQuery, validMetricSearchBuilderQuery } from './__mocks__/queries';
|
||||||
import { TimeRangeMock } from './__mocks__/timeRange';
|
import { TimeRangeMock } from './__mocks__/timeRange';
|
||||||
import {
|
import {
|
||||||
|
CloudWatchDefaultQuery,
|
||||||
CloudWatchLogsQuery,
|
CloudWatchLogsQuery,
|
||||||
CloudWatchMetricsQuery,
|
CloudWatchMetricsQuery,
|
||||||
CloudWatchQuery,
|
CloudWatchQuery,
|
||||||
CloudWatchDefaultQuery,
|
|
||||||
MetricEditorMode,
|
MetricEditorMode,
|
||||||
MetricQueryType,
|
MetricQueryType,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
CloudWatchAnnotationQuery,
|
||||||
CloudWatchLogsQuery,
|
CloudWatchLogsQuery,
|
||||||
CloudWatchMetricsQuery,
|
CloudWatchMetricsQuery,
|
||||||
LogGroup,
|
LogGroup,
|
||||||
@ -24,6 +25,13 @@ export const DEFAULT_METRICS_QUERY: Omit<CloudWatchMetricsQuery, 'refId'> = {
|
|||||||
matchExact: true,
|
matchExact: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_ANNOTATIONS_QUERY: Omit<CloudWatchAnnotationQuery, 'refId'> = {
|
||||||
|
queryMode: 'Annotations',
|
||||||
|
namespace: '',
|
||||||
|
region: 'default',
|
||||||
|
statistic: 'Average',
|
||||||
|
};
|
||||||
|
|
||||||
export const DEFAULT_LOGS_QUERY_STRING = 'fields @timestamp, @message |\n sort @timestamp desc |\n limit 20';
|
export const DEFAULT_LOGS_QUERY_STRING = 'fields @timestamp, @message |\n sort @timestamp desc |\n limit 20';
|
||||||
|
|
||||||
export const getDefaultLogsQuery = (
|
export const getDefaultLogsQuery = (
|
||||||
|
Loading…
Reference in New Issue
Block a user