diff --git a/.betterer.results b/.betterer.results
index b329dcfffa5..fafff607bb6 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -5346,8 +5346,7 @@ exports[`better eslint`] = {
"public/app/plugins/datasource/cloudwatch/components/LogsQueryField.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "2"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "3"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
@@ -5361,14 +5360,11 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
- "public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.test.tsx:5381": [
+ "public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
- "public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"]
- ],
"public/app/plugins/datasource/cloudwatch/datasource.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
diff --git a/public/app/plugins/datasource/cloudwatch/__mocks__/queries.ts b/public/app/plugins/datasource/cloudwatch/__mocks__/queries.ts
index ecfd25d3f1b..00c5d4c0d18 100644
--- a/public/app/plugins/datasource/cloudwatch/__mocks__/queries.ts
+++ b/public/app/plugins/datasource/cloudwatch/__mocks__/queries.ts
@@ -38,32 +38,30 @@ export const validMetricSearchBuilderQuery: CloudWatchMetricsQuery = {
};
export const validMetricQueryBuilderQuery: CloudWatchMetricsQuery = {
- id: '',
queryMode: 'Metrics',
- region: 'us-east-2',
- namespace: 'AWS/EC2',
- period: '3000',
- alias: '',
- metricName: 'CPUUtilization',
- dimensions: { InstanceId: 'i-123' },
- matchExact: true,
- statistic: 'Average',
+ refId: '',
+ id: '',
+ region: 'us-east-1',
+ namespace: 'ec2',
+ dimensions: { somekey: 'somevalue' },
+ metricQueryType: MetricQueryType.Query,
+ metricEditorMode: MetricEditorMode.Builder,
sql: {
- select: {
+ from: {
type: QueryEditorExpressionType.Function,
- name: 'AVERAGE',
+ name: 'SCHEMA',
parameters: [
{
type: QueryEditorExpressionType.FunctionParameter,
- name: 'CPUUtilization',
+ name: 'AWS/EC2',
+ },
+ {
+ type: QueryEditorExpressionType.FunctionParameter,
+ name: 'InstanceId',
},
],
},
},
- refId: 'A',
- metricQueryType: MetricQueryType.Query,
- metricEditorMode: MetricEditorMode.Builder,
- hide: false,
};
export const validMetricQueryCodeQuery: CloudWatchMetricsQuery = {
diff --git a/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx
index 6d52f7d2728..9d6f8405d47 100644
--- a/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx
@@ -45,7 +45,6 @@ export const AnnotationQueryEditor = (props: Props) => {
metricStat={query}
disableExpressions={true}
onChange={(metricStat: MetricStat) => onChange({ ...query, ...metricStat })}
- onRunQuery={() => {}}
>
diff --git a/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.test.tsx b/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.test.tsx
index 98ab3909d11..5dc04fc59fb 100644
--- a/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.test.tsx
@@ -35,7 +35,6 @@ const defaultProps = {
},
]),
onChange: jest.fn(),
- onRunQuery: jest.fn(),
};
const originalDebounce = lodash.debounce;
@@ -178,13 +177,4 @@ describe('CrossAccountLogsQueryField', () => {
},
]);
});
-
- it('runs the query on close of the modal', async () => {
- const onRunQuery = jest.fn();
- render();
- await userEvent.click(screen.getByText('Select Log Groups'));
- expect(screen.getByText('Log Group Name')).toBeInTheDocument();
- await userEvent.click(screen.getByLabelText('Close dialogue'));
- expect(onRunQuery).toBeCalledTimes(1);
- });
});
diff --git a/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.tsx b/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.tsx
index 7a20f6267be..6dacc9fd6d6 100644
--- a/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/CrossAccountLogsQueryField.tsx
@@ -16,7 +16,6 @@ type CrossAccountLogsQueryProps = {
accountOptions: Array>;
fetchLogGroups: (params: Partial) => Promise;
onChange: (selectedLogGroups: SelectableResourceValue[]) => void;
- onRunQuery: () => void;
};
export const CrossAccountLogsQueryField = (props: CrossAccountLogsQueryProps) => {
@@ -30,7 +29,6 @@ export const CrossAccountLogsQueryField = (props: CrossAccountLogsQueryProps) =>
const toggleModal = () => {
setIsModalOpen(!isModalOpen);
if (isModalOpen) {
- props.onRunQuery();
} else {
setSelectedLogGroups(props.selectedLogGroups);
searchFn(searchPhrase, searchAccountId);
diff --git a/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx b/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx
index 1829732b5d8..7b038b53379 100644
--- a/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx
@@ -33,7 +33,6 @@ const props = {
query: q,
disableExpressions: false,
onChange: jest.fn(),
- onRunQuery: jest.fn(),
};
describe('Dimensions', () => {
diff --git a/public/app/plugins/datasource/cloudwatch/components/DynamicLabelsField.tsx b/public/app/plugins/datasource/cloudwatch/components/DynamicLabelsField.tsx
index 63dcc0b3370..0a69a062266 100644
--- a/public/app/plugins/datasource/cloudwatch/components/DynamicLabelsField.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/DynamicLabelsField.tsx
@@ -13,12 +13,11 @@ const dynamicLabelsCompletionItemProvider = new DynamicLabelsCompletionItemProvi
export interface Props {
onChange: (query: string) => void;
- onRunQuery: () => void;
label: string;
width: number;
}
-export function DynamicLabelsField({ label, width, onChange, onRunQuery }: Props) {
+export function DynamicLabelsField({ label, width, onChange }: Props) {
const theme = useTheme2();
const styles = getInputStyles({ theme, width });
const containerRef = useRef(null);
@@ -28,13 +27,12 @@ export function DynamicLabelsField({ label, width, onChange, onRunQuery }: Props
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => {
const text = editor.getValue();
onChange(text);
- onRunQuery();
});
const containerDiv = containerRef.current;
containerDiv !== null && editor.layout({ width: containerDiv.clientWidth, height: containerDiv.clientHeight });
},
- [onChange, onRunQuery]
+ [onChange]
);
return (
@@ -69,7 +67,6 @@ export function DynamicLabelsField({ label, width, onChange, onRunQuery }: Props
onBlur={(value) => {
if (value !== label) {
onChange(value);
- onRunQuery();
}
}}
onBeforeEditorMount={(monaco: Monaco) =>
diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.test.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.test.tsx
index 63c4c904bbc..d68dc464452 100644
--- a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.test.tsx
@@ -22,7 +22,6 @@ const defaultProps = {
refId: '',
} as CloudWatchLogsQuery,
onChange: jest.fn(),
- onRunQuery: jest.fn(),
};
describe('LogGroupSelection', () => {
beforeEach(() => {
diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.tsx
index 36b0cc7ef88..bc0e633c112 100644
--- a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelection.tsx
@@ -16,14 +16,13 @@ type Props = {
datasource: CloudWatchDatasource;
query: CloudWatchLogsQuery;
onChange: (value: CloudWatchQuery) => void;
- onRunQuery: () => void;
};
const rowGap = css`
gap: 3px;
`;
-export const LogGroupSelection = ({ datasource, query, onChange, onRunQuery }: Props) => {
+export const LogGroupSelection = ({ datasource, query, onChange }: Props) => {
const accountState = useAccountOptions(datasource.api, query.region);
return (
@@ -37,7 +36,6 @@ export const LogGroupSelection = ({ datasource, query, onChange, onRunQuery }: P
onChange({ ...query, logGroups: selectedLogGroups, logGroupNames: [] });
}}
accountOptions={accountState.value}
- onRunQuery={onRunQuery}
selectedLogGroups={query.logGroups ?? []} /* todo handle defaults */
/>
) : (
@@ -53,7 +51,6 @@ export const LogGroupSelection = ({ datasource, query, onChange, onRunQuery }: P
onChange={function (logGroupNames: string[]): void {
onChange({ ...query, logGroupNames, logGroups: [] });
}}
- onRunQuery={onRunQuery}
refId={query.refId}
/>
}
diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx
index b3aa44e9c85..29013b78047 100644
--- a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx
@@ -21,7 +21,6 @@ export interface LogGroupSelectorProps {
onChange: (logGroups: string[]) => void;
datasource?: CloudWatchDatasource;
- onRunQuery?: () => void;
onOpenMenu?: () => Promise;
refId?: string;
width?: number | 'auto';
@@ -33,7 +32,6 @@ export const LogGroupSelector: React.FC = ({
selectedLogGroups,
onChange,
datasource,
- onRunQuery,
onOpenMenu,
refId,
width,
@@ -135,7 +133,6 @@ export const LogGroupSelector: React.FC = ({
options={datasource ? appendTemplateVariables(datasource, logGroupOptions) : logGroupOptions}
value={selectedLogGroups}
onChange={(v) => onChange(v.filter(({ value }) => value).map(({ value }) => value))}
- onBlur={onRunQuery}
closeMenuOnSelect={false}
isClearable
isOptionDisabled={() => selectedLogGroups.length >= MAX_LOG_GROUPS}
diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx
index 69dfe3077f9..69f35a5dd64 100644
--- a/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx
@@ -2,7 +2,6 @@
import { css } from '@emotion/css';
import React, { memo } from 'react';
-// Types
import { AbsoluteTimeRange, QueryEditorProps } from '@grafana/data';
import { InlineFormLabel } from '@grafana/ui';
@@ -22,7 +21,7 @@ const labelClass = css`
`;
export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor(props: Props) {
- const { query, data, datasource, onRunQuery, onChange, exploreId } = props;
+ const { query, data, datasource, exploreId } = props;
let absolute: AbsoluteTimeRange;
if (data?.request?.range?.from) {
@@ -40,13 +39,9 @@ export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor
return (
diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.test.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.test.tsx
index 099b153f8bf..95fdb2f42d8 100644
--- a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.test.tsx
@@ -1,7 +1,6 @@
-import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
import _, { DebouncedFunc } from 'lodash'; // eslint-disable-line lodash/import-scope
import React from 'react';
-import { act } from 'react-dom/test-utils';
import { ExploreId } from '../../../../types';
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
@@ -13,28 +12,6 @@ jest
.mockImplementation((func: (...args: any) => any, wait?: number) => func as DebouncedFunc);
describe('CloudWatchLogsQueryField', () => {
- it('runs onRunQuery on blur of Log Groups', async () => {
- const onRunQuery = jest.fn();
- const ds = setupMockedDataSource();
-
- render(
- {}}
- />
- );
-
- const multiSelect = screen.getByLabelText('Log Groups');
- await act(async () => {
- fireEvent.blur(multiSelect);
- });
- expect(onRunQuery).toHaveBeenCalled();
- });
-
it('loads defaultLogGroups', async () => {
const onRunQuery = jest.fn();
const ds = setupMockedDataSource();
diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx
index 77c0fc7d7f2..b1f09053028 100644
--- a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx
@@ -24,7 +24,6 @@ import { CloudWatchJsonData, CloudWatchLogsQuery, CloudWatchQuery } from '../typ
import { getStatsGroups } from '../utils/query/getStatsGroups';
import { LogGroupSelection } from './LogGroupSelection';
-import QueryHeader from './QueryHeader';
export interface CloudWatchLogsQueryFieldProps
extends QueryEditorProps,
@@ -46,7 +45,7 @@ const plugins: Array> = [
),
];
export const CloudWatchLogsQueryField = (props: CloudWatchLogsQueryFieldProps) => {
- const { query, datasource, onChange, onRunQuery, ExtraFieldElement, data } = props;
+ const { query, datasource, onChange, ExtraFieldElement, data } = props;
const showError = data?.error?.refId === query.refId;
const cleanText = datasource.languageProvider.cleanText;
@@ -85,21 +84,13 @@ export const CloudWatchLogsQueryField = (props: CloudWatchLogsQueryFieldProps) =
return (
<>
-
-
+
void;
- onRunQuery: () => void;
expression: string;
datasource: CloudWatchDatasource;
}
-export function MathExpressionQueryField({
- expression: query,
- onChange,
- onRunQuery,
- datasource,
-}: React.PropsWithChildren) {
+export function MathExpressionQueryField({ expression: query, onChange, datasource }: React.PropsWithChildren) {
const containerRef = useRef(null);
const onEditorMount = useCallback(
(editor: monacoType.editor.IStandaloneCodeEditor, monaco: Monaco) => {
@@ -28,7 +22,6 @@ export function MathExpressionQueryField({
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => {
const text = editor.getValue();
onChange(text);
- onRunQuery();
});
// auto resizes the editor to be the height of the content it holds
@@ -48,7 +41,7 @@ export function MathExpressionQueryField({
editor.onDidContentSizeChange(updateElementHeight);
updateElementHeight();
},
- [onChange, onRunQuery]
+ [onChange]
);
return (
@@ -77,7 +70,6 @@ export function MathExpressionQueryField({
onBlur={(value) => {
if (value !== query) {
onChange(value);
- onRunQuery();
}
}}
onBeforeEditorMount={(monaco: Monaco) =>
diff --git a/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx
index e97032161c8..815cbdda001 100644
--- a/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx
@@ -33,7 +33,6 @@ const props = {
datasource: ds.datasource,
metricStat,
onChange: jest.fn(),
- onRunQuery: jest.fn(),
};
describe('MetricStatEditor', () => {
@@ -43,10 +42,9 @@ describe('MetricStatEditor', () => {
describe('statistics field', () => {
test.each([['Average', 'p23.23', 'p34', '$statistic']])('should accept valid values', async (statistic) => {
const onChange = jest.fn();
- const onRunQuery = jest.fn();
props.datasource.getVariables = jest.fn().mockReturnValue(['$statistic']);
- render();
+ render();
const statisticElement = await screen.findByLabelText('Statistic');
expect(statisticElement).toBeInTheDocument();
@@ -54,14 +52,12 @@ describe('MetricStatEditor', () => {
await userEvent.type(statisticElement, statistic);
fireEvent.keyDown(statisticElement, { keyCode: 13 });
expect(onChange).toHaveBeenCalledWith({ ...props.metricStat, statistic });
- expect(onRunQuery).toHaveBeenCalled();
});
test.each([['CustomStat', 'p23,23', '$statistic']])('should not accept invalid values', async (statistic) => {
const onChange = jest.fn();
- const onRunQuery = jest.fn();
- render();
+ render();
const statisticElement = await screen.findByLabelText('Statistic');
expect(statisticElement).toBeInTheDocument();
@@ -69,7 +65,6 @@ describe('MetricStatEditor', () => {
await userEvent.type(statisticElement, statistic);
fireEvent.keyDown(statisticElement, { keyCode: 13 });
expect(onChange).not.toHaveBeenCalled();
- expect(onRunQuery).not.toHaveBeenCalled();
});
});
@@ -120,18 +115,15 @@ describe('MetricStatEditor', () => {
{ value: 'm2', label: 'm2', text: 'm2' },
];
const onChange = jest.fn();
- const onRunQuery = jest.fn();
const propsNamespaceMetrics = {
...props,
onChange,
- onRunQuery,
};
beforeEach(() => {
propsNamespaceMetrics.datasource.api.getNamespaces = jest.fn().mockResolvedValue(namespaces);
propsNamespaceMetrics.datasource.api.getMetrics = jest.fn().mockResolvedValue(metrics);
onChange.mockClear();
- onRunQuery.mockClear();
});
it('should select namespace and metric name correctly', async () => {
@@ -151,7 +143,6 @@ describe('MetricStatEditor', () => {
[{ ...propsNamespaceMetrics.metricStat, namespace: 'n1' }], // First call, namespace select
[{ ...propsNamespaceMetrics.metricStat, metricName: 'm1' }], // Second call, metric select
]);
- expect(onRunQuery).toHaveBeenCalledTimes(2);
});
it('should remove metricName from metricStat if it does not exist in new namespace', async () => {
diff --git a/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.tsx
index 72fc3b17817..fe26e36cf6a 100644
--- a/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.tsx
@@ -19,7 +19,6 @@ export type Props = {
datasource: CloudWatchDatasource;
disableExpressions?: boolean;
onChange: (value: MetricStat) => void;
- onRunQuery: () => void;
};
export function MetricStatEditor({
@@ -28,7 +27,6 @@ export function MetricStatEditor({
datasource,
disableExpressions = false,
onChange,
- onRunQuery,
}: React.PropsWithChildren) {
const namespaces = useNamespaces(datasource);
const metrics = useMetrics(datasource, metricStat);
@@ -47,14 +45,9 @@ export function MetricStatEditor({
});
}, [accountState, metricStat, onChange, datasource.api]);
- const onMetricStatChange = (metricStat: MetricStat) => {
- onChange(metricStat);
- onRunQuery();
- };
-
const onNamespaceChange = async (metricStat: MetricStat) => {
const validatedQuery = await validateMetricName(metricStat);
- onMetricStatChange(validatedQuery);
+ onChange(validatedQuery);
};
const validateMetricName = async (metricStat: MetricStat) => {
@@ -78,7 +71,6 @@ export function MetricStatEditor({
accountId={metricStat.accountId}
onChange={(accountId?: string) => {
onChange({ ...metricStat, accountId });
- onRunQuery();
}}
accountOptions={accountState?.value || []}
>
@@ -105,7 +97,7 @@ export function MetricStatEditor({
options={metrics}
onChange={({ value: metricName }) => {
if (metricName) {
- onMetricStatChange({ ...metricStat, metricName });
+ onChange({ ...metricStat, metricName });
}
}}
/>
@@ -130,7 +122,7 @@ export function MetricStatEditor({
return;
}
- onMetricStatChange({ ...metricStat, statistic });
+ onChange({ ...metricStat, statistic });
}}
/>
@@ -141,7 +133,7 @@ export function MetricStatEditor({
onMetricStatChange({ ...metricStat, dimensions })}
+ onChange={(dimensions) => onChange({ ...metricStat, dimensions })}
dimensionKeys={dimensionKeys}
disableExpressions={disableExpressions}
datasource={datasource}
@@ -157,7 +149,7 @@ export function MetricStatEditor({
id={`${refId}-cloudwatch-match-exact`}
value={!!metricStat.matchExact}
onChange={(e) => {
- onMetricStatChange({
+ onChange({
...metricStat,
matchExact: e.currentTarget.checked,
});
diff --git a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx
index 0b8b92f488f..bac03da437f 100644
--- a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx
@@ -69,6 +69,8 @@ const setup = () => {
metricQueryType: MetricQueryType.Search,
metricEditorMode: MetricEditorMode.Builder,
},
+ extraHeaderElementLeft: () => {},
+ extraHeaderElementRight: () => {},
datasource,
history: [],
onChange: jest.fn(),
@@ -79,66 +81,6 @@ const setup = () => {
};
describe('QueryEditor', () => {
- describe('should handle editor modes correctly', () => {
- it('when metric query type is metric search and editor mode is builder', async () => {
- await act(async () => {
- const props = setup();
- render();
-
- expect(screen.getByText('Metric Search')).toBeInTheDocument();
- const radio = screen.getByLabelText('Builder');
- expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
- });
- });
-
- it('when metric query type is metric search and editor mode is raw', async () => {
- await act(async () => {
- const props = setup();
- if (props.query.queryMode !== 'Metrics') {
- fail(`expected props.query.queryMode to be 'Metrics', got '${props.query.queryMode}' instead`);
- }
- props.query.metricEditorMode = MetricEditorMode.Code;
- render();
-
- expect(screen.getByText('Metric Search')).toBeInTheDocument();
- const radio = screen.getByLabelText('Code');
- expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
- });
- });
-
- it('when metric query type is metric query and editor mode is builder', async () => {
- await act(async () => {
- const props = setup();
- if (props.query.queryMode !== 'Metrics') {
- fail(`expected props.query.queryMode to be 'Metrics', got '${props.query.queryMode}' instead`);
- }
- props.query.metricQueryType = MetricQueryType.Query;
- props.query.metricEditorMode = MetricEditorMode.Builder;
- render();
-
- expect(screen.getByText('Metric Query')).toBeInTheDocument();
- const radio = screen.getByLabelText('Builder');
- expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
- });
- });
-
- it('when metric query type is metric query and editor mode is raw', async () => {
- await act(async () => {
- const props = setup();
- if (props.query.queryMode !== 'Metrics') {
- fail(`expected props.query.queryMode to be 'Metrics', got '${props.query.queryMode}' instead`);
- }
- props.query.metricQueryType = MetricQueryType.Query;
- props.query.metricEditorMode = MetricEditorMode.Code;
- render();
-
- expect(screen.getByText('Metric Query')).toBeInTheDocument();
- const radio = screen.getByLabelText('Code');
- expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
- });
- });
- });
-
describe('should handle expression options correctly', () => {
it('should display match exact switch', async () => {
const props = setup();
diff --git a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.tsx
index 0a389fd6484..eb4b90524aa 100644
--- a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.tsx
@@ -1,13 +1,12 @@
-import React, { ChangeEvent, useState } from 'react';
+import React, { ChangeEvent, useCallback, useEffect, useState } from 'react';
-import { QueryEditorProps } from '@grafana/data';
-import { EditorField, EditorRow, Space } from '@grafana/experimental';
+import { QueryEditorProps, SelectableValue } from '@grafana/data';
+import { EditorField, EditorRow, InlineSelect, Space } from '@grafana/experimental';
import { config } from '@grafana/runtime';
-import { Input } from '@grafana/ui';
+import { ConfirmModal, Input, RadioButtonGroup } from '@grafana/ui';
-import { MathExpressionQueryField, MetricStatEditor, SQLBuilderEditor, SQLCodeEditor } from '../';
+import { MathExpressionQueryField, MetricStatEditor, SQLBuilderEditor } from '../';
import { CloudWatchDatasource } from '../../datasource';
-import { isCloudWatchMetricsQuery } from '../../guards';
import useMigratedMetricsQuery from '../../migrations/useMigratedMetricsQuery';
import {
CloudWatchJsonData,
@@ -18,39 +17,99 @@ import {
MetricStat,
} from '../../types';
import { DynamicLabelsField } from '../DynamicLabelsField';
-import QueryHeader from '../QueryHeader';
+import { SQLCodeEditor } from '../SQLCodeEditor';
import { Alias } from './Alias';
export interface Props extends QueryEditorProps {
query: CloudWatchMetricsQuery;
+ extraHeaderElementLeft?: React.Dispatch;
+ extraHeaderElementRight?: React.Dispatch;
}
+const metricEditorModes: Array> = [
+ { label: 'Metric Search', value: MetricQueryType.Search },
+ { label: 'Metric Query', value: MetricQueryType.Query },
+];
+const editorModes = [
+ { label: 'Builder', value: MetricEditorMode.Builder },
+ { label: 'Code', value: MetricEditorMode.Code },
+];
+
export const MetricsQueryEditor = (props: Props) => {
- const { query, onRunQuery, datasource } = props;
+ const { query, datasource, extraHeaderElementLeft, extraHeaderElementRight, onChange } = props;
+ const [showConfirm, setShowConfirm] = useState(false);
const [sqlCodeEditorIsDirty, setSQLCodeEditorIsDirty] = useState(false);
const migratedQuery = useMigratedMetricsQuery(query, props.onChange);
- const onChange = (query: CloudWatchQuery) => {
- const { onChange, onRunQuery } = props;
- onChange(query);
- onRunQuery();
- };
+ const onEditorModeChange = useCallback(
+ (newMetricEditorMode: MetricEditorMode) => {
+ if (
+ sqlCodeEditorIsDirty &&
+ query.metricQueryType === MetricQueryType.Query &&
+ query.metricEditorMode === MetricEditorMode.Code
+ ) {
+ setShowConfirm(true);
+ return;
+ }
+ onChange({ ...query, metricEditorMode: newMetricEditorMode });
+ },
+ [setShowConfirm, onChange, sqlCodeEditorIsDirty, query]
+ );
+
+ useEffect(() => {
+ extraHeaderElementLeft?.(
+ m.value === query.metricQueryType)}
+ options={metricEditorModes}
+ onChange={({ value }) => {
+ onChange({ ...query, metricQueryType: value });
+ }}
+ />
+ );
+
+ extraHeaderElementRight?.(
+ <>
+
+ {
+ setShowConfirm(false);
+ onChange({ ...query, metricEditorMode: MetricEditorMode.Builder });
+ }}
+ onDismiss={() => setShowConfirm(false)}
+ />
+ >
+ );
+
+ return () => {
+ extraHeaderElementLeft?.(undefined);
+ extraHeaderElementRight?.(undefined);
+ };
+ }, [
+ query,
+ sqlCodeEditorIsDirty,
+ datasource,
+ onChange,
+ extraHeaderElementLeft,
+ extraHeaderElementRight,
+ showConfirm,
+ onEditorModeChange,
+ ]);
return (
<>
- {
- if (isCloudWatchMetricsQuery(newQuery) && newQuery.metricEditorMode !== query.metricEditorMode) {
- setSQLCodeEditorIsDirty(false);
- }
- onChange(newQuery);
- }}
- sqlCodeEditorIsDirty={sqlCodeEditorIsDirty}
- />
{query.metricQueryType === MetricQueryType.Search && (
@@ -65,7 +124,6 @@ export const MetricsQueryEditor = (props: Props) => {
)}
{query.metricEditorMode === MetricEditorMode.Code && (
props.onChange({ ...query, expression })}
datasource={datasource}
@@ -85,19 +143,13 @@ export const MetricsQueryEditor = (props: Props) => {
}
props.onChange({ ...migratedQuery, sqlExpression });
}}
- onRunQuery={onRunQuery}
datasource={datasource}
/>
)}
{query.metricEditorMode === MetricEditorMode.Builder && (
<>
-
+
>
)}
>
@@ -113,7 +165,6 @@ export const MetricsQueryEditor = (props: Props) => {
>
) => onChange({ ...migratedQuery, id: event.target.value })}
type="text"
value={query.id}
@@ -125,7 +176,6 @@ export const MetricsQueryEditor = (props: Props) => {
id={`${query.refId}-cloudwatch-metric-query-editor-period`}
value={query.period || ''}
placeholder="auto"
- onBlur={onRunQuery}
onChange={(event: ChangeEvent) =>
onChange({ ...migratedQuery, period: event.target.value })
}
@@ -141,7 +191,6 @@ export const MetricsQueryEditor = (props: Props) => {
>
props.onChange({ ...query, label })}
>
diff --git a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryHeader.test.tsx b/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryHeader.test.tsx
deleted file mode 100644
index 1f0d639620e..00000000000
--- a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryHeader.test.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import React from 'react';
-import { act } from 'react-dom/test-utils';
-
-import { setupMockedDataSource } from '../../__mocks__/CloudWatchDataSource';
-import { CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType } from '../../types';
-
-import MetricsQueryHeader from './MetricsQueryHeader';
-
-const ds = setupMockedDataSource({
- variables: [],
-});
-ds.datasource.api.getRegions = jest.fn().mockResolvedValue([]);
-const query: CloudWatchMetricsQuery = {
- id: '',
- region: 'us-east-2',
- namespace: '',
- period: '',
- alias: '',
- metricName: '',
- dimensions: {},
- matchExact: true,
- statistic: '',
- expression: '',
- refId: '',
-};
-
-describe('MetricsQueryHeader', () => {
- describe('confirm modal', () => {
- it('should be shown when moving from code editor to builder when in sql mode', async () => {
- const onChange = jest.fn();
- const onRunQuery = jest.fn();
- query.metricEditorMode = MetricEditorMode.Code;
- query.metricQueryType = MetricQueryType.Query;
-
- render(
-
- );
-
- const builderElement = screen.getByLabelText('Builder');
- expect(builderElement).toBeInTheDocument();
- await act(async () => {
- await builderElement.click();
- });
-
- const modalTitleElem = screen.getByText('Are you sure?');
- expect(modalTitleElem).toBeInTheDocument();
- expect(onChange).not.toHaveBeenCalled();
- });
-
- it('should not be shown when moving from builder to code when in sql mode', async () => {
- const onChange = jest.fn();
- const onRunQuery = jest.fn();
- query.metricEditorMode = MetricEditorMode.Builder;
- query.metricQueryType = MetricQueryType.Query;
-
- render(
-
- );
-
- const builderElement = screen.getByLabelText('Code');
- expect(builderElement).toBeInTheDocument();
- await act(async () => {
- await builderElement.click();
- });
-
- const modalTitleElem = screen.queryByText('Are you sure?');
- expect(modalTitleElem).toBeNull();
- expect(onChange).toHaveBeenCalled();
- });
-
- it('should not be shown when moving from code to builder when in standard mode', async () => {
- const onChange = jest.fn();
- const onRunQuery = jest.fn();
- query.metricEditorMode = MetricEditorMode.Code;
- query.metricQueryType = MetricQueryType.Search;
-
- render(
-
- );
-
- const builderElement = screen.getByLabelText('Builder');
- expect(builderElement).toBeInTheDocument();
- await act(async () => {
- await builderElement.click();
- });
-
- const modalTitleElem = screen.queryByText('Are you sure?');
- expect(modalTitleElem).toBeNull();
- expect(onChange).toHaveBeenCalled();
- });
- });
-
- it('should call run query when run button is clicked when in metric query mode', async () => {
- const onChange = jest.fn();
- const onRunQuery = jest.fn();
- query.metricEditorMode = MetricEditorMode.Code;
- query.metricQueryType = MetricQueryType.Query;
-
- render(
-
- );
-
- const runQueryButton = screen.getByText('Run query');
- expect(runQueryButton).toBeInTheDocument();
- await act(async () => {
- await runQueryButton.click();
- });
- expect(onRunQuery).toHaveBeenCalled();
- });
-});
diff --git a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryHeader.tsx b/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryHeader.tsx
deleted file mode 100644
index 6520db778ae..00000000000
--- a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryHeader.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import React, { useCallback, useState } from 'react';
-
-import { SelectableValue } from '@grafana/data';
-import { FlexItem, InlineSelect } from '@grafana/experimental';
-import { config } from '@grafana/runtime';
-import { Badge, Button, ConfirmModal, RadioButtonGroup } from '@grafana/ui';
-
-import { CloudWatchDatasource } from '../../datasource';
-import { CloudWatchMetricsQuery, CloudWatchQuery, MetricEditorMode, MetricQueryType } from '../../types';
-
-interface MetricsQueryHeaderProps {
- query: CloudWatchMetricsQuery;
- datasource: CloudWatchDatasource;
- onChange: (query: CloudWatchQuery) => void;
- onRunQuery: () => void;
- sqlCodeEditorIsDirty: boolean;
- isMonitoringAccount: boolean;
-}
-
-const metricEditorModes: Array> = [
- { label: 'Metric Search', value: MetricQueryType.Search },
- { label: 'Metric Query', value: MetricQueryType.Query },
-];
-
-const editorModes = [
- { label: 'Builder', value: MetricEditorMode.Builder },
- { label: 'Code', value: MetricEditorMode.Code },
-];
-
-const MetricsQueryHeader: React.FC = ({
- query,
- sqlCodeEditorIsDirty,
- onChange,
- onRunQuery,
- isMonitoringAccount,
-}) => {
- const { metricEditorMode, metricQueryType } = query;
- const [showConfirm, setShowConfirm] = useState(false);
-
- const onEditorModeChange = useCallback(
- (newMetricEditorMode: MetricEditorMode) => {
- if (
- sqlCodeEditorIsDirty &&
- metricQueryType === MetricQueryType.Query &&
- metricEditorMode === MetricEditorMode.Code
- ) {
- setShowConfirm(true);
- return;
- }
- onChange({ ...query, metricEditorMode: newMetricEditorMode });
- },
- [setShowConfirm, onChange, sqlCodeEditorIsDirty, query, metricEditorMode, metricQueryType]
- );
-
- const shouldDisplayMonitoringBadge =
- query.metricQueryType === MetricQueryType.Search &&
- isMonitoringAccount &&
- config.featureToggles.cloudWatchCrossAccountQuerying;
-
- return (
- <>
- m.value === metricQueryType)}
- options={metricEditorModes}
- onChange={({ value }) => {
- onChange({ ...query, metricQueryType: value });
- }}
- />
-
-
- {shouldDisplayMonitoringBadge && (
-
- )}
-
-
-
- {query.metricQueryType === MetricQueryType.Query && query.metricEditorMode === MetricEditorMode.Code && (
-
- )}
-
- {
- setShowConfirm(false);
- onChange({ ...query, metricEditorMode: MetricEditorMode.Builder });
- }}
- onDismiss={() => setShowConfirm(false)}
- />
- >
- );
-};
-
-export default MetricsQueryHeader;
diff --git a/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.tsx
deleted file mode 100644
index ef9979991f1..00000000000
--- a/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React, { PureComponent } from 'react';
-
-import { QueryEditorProps } from '@grafana/data';
-
-import { CloudWatchDatasource } from '../datasource';
-import { isCloudWatchLogsQuery, isCloudWatchMetricsQuery } from '../guards';
-import { CloudWatchJsonData, CloudWatchQuery } from '../types';
-
-import { MetricsQueryEditor } from '././MetricsQueryEditor/MetricsQueryEditor';
-import LogsQueryEditor from './LogsQueryEditor';
-
-export type Props = QueryEditorProps;
-
-export class PanelQueryEditor extends PureComponent {
- render() {
- const { query } = this.props;
-
- return (
- <>
- {isCloudWatchMetricsQuery(query) && }
- {isCloudWatchLogsQuery(query) && }
- >
- );
- }
-}
diff --git a/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx
similarity index 52%
rename from public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.test.tsx
rename to public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx
index 0a62600b4d2..c0d12af2044 100644
--- a/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx
@@ -1,4 +1,4 @@
-import { act, render, screen } from '@testing-library/react';
+import { act, fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { QueryEditorProps } from '@grafana/data';
@@ -15,7 +15,7 @@ import {
import { CloudWatchDatasource } from '../datasource';
import { CloudWatchQuery, CloudWatchJsonData, MetricEditorMode, MetricQueryType } from '../types';
-import { PanelQueryEditor } from './PanelQueryEditor';
+import { QueryEditor } from './QueryEditor';
// the following three fields are added to legacy queries in the dashboard migrator
const migratedFields = {
@@ -31,7 +31,22 @@ const props: QueryEditorProps {
+const FAKE_EDITOR_LABEL = 'FakeEditor';
+
+jest.mock('./SQLCodeEditor', () => ({
+ SQLCodeEditor: ({ sql, onChange }: { sql: string; onChange: (val: string) => void }) => {
+ return (
+ <>
+
+ onChange(e.currentTarget.value)}>
+ >
+ );
+ },
+}));
+
+export { SQLCodeEditor } from './SQLCodeEditor';
+
+describe('QueryEditor should render right editor', () => {
describe('when using grafana 6.3.0 metric query', () => {
it('should render the metrics query editor', async () => {
const query = {
@@ -50,7 +65,7 @@ describe('PanelQueryEditor should render right editor', () => {
returnData: false,
};
await act(async () => {
- render();
+ render();
});
expect(screen.getByText('Metric name')).toBeInTheDocument();
});
@@ -78,7 +93,7 @@ describe('PanelQueryEditor should render right editor', () => {
statistics: 'Average',
} as any;
await act(async () => {
- render();
+ render();
});
expect(screen.getByText('Choose Log Groups')).toBeInTheDocument();
});
@@ -106,7 +121,7 @@ describe('PanelQueryEditor should render right editor', () => {
statistic: 'Average',
} as any;
await act(async () => {
- render();
+ render();
});
expect(screen.getByText('Log Groups')).toBeInTheDocument();
});
@@ -133,7 +148,7 @@ describe('PanelQueryEditor should render right editor', () => {
statistic: 'Average',
} as any;
await act(async () => {
- render();
+ render();
});
expect(screen.getByText('Metric name')).toBeInTheDocument();
});
@@ -177,7 +192,7 @@ describe('PanelQueryEditor should render right editor', () => {
test.each(cases)('$name', async ({ query, toggle }) => {
config.featureToggles.cloudWatchCrossAccountQuerying = toggle;
await act(async () => {
- render();
+ render();
});
expect(await screen.getByText('Monitoring account')).toBeInTheDocument();
});
@@ -193,7 +208,7 @@ describe('PanelQueryEditor should render right editor', () => {
{
name: 'it is metric query code query and toggle is not enabled',
query: validMetricQueryCodeQuery,
- toggle: true,
+ toggle: false,
},
{ name: 'it is logs query and feature is not enabled', query: validLogsQuery, toggle: false },
{
@@ -210,10 +225,128 @@ describe('PanelQueryEditor should render right editor', () => {
test.each(cases)('$name', async ({ query, toggle }) => {
config.featureToggles.cloudWatchCrossAccountQuerying = toggle;
await act(async () => {
- render();
+ render();
});
expect(await screen.queryByText('Monitoring account')).toBeNull();
});
});
});
+
+ describe('QueryHeader', () => {
+ it('should display metric actions in header when metric query is used', async () => {
+ await act(async () => {
+ render();
+ });
+
+ expect(screen.getByLabelText(/Region.*/)).toBeInTheDocument();
+ expect(screen.getByText('CloudWatch Metrics')).toBeInTheDocument();
+ expect(screen.getByLabelText('Builder')).toBeInTheDocument();
+ expect(screen.getByLabelText('Code')).toBeInTheDocument();
+ expect(screen.getByText('Metric Query')).toBeInTheDocument();
+ });
+
+ it('should display metric actions in header when metric query is used', async () => {
+ await act(async () => {
+ render();
+ });
+
+ expect(screen.getByLabelText(/Region.*/)).toBeInTheDocument();
+ expect(screen.getByText('CloudWatch Logs')).toBeInTheDocument();
+ expect(screen.queryByLabelText('Builder')).not.toBeInTheDocument();
+ expect(screen.queryByLabelText('Code')).not.toBeInTheDocument();
+ expect(screen.queryByText('Metric Query')).not.toBeInTheDocument();
+ });
+ });
+
+ describe('metrics editor should handle editor modes correctly', () => {
+ it('when metric query type is metric search and editor mode is builder', async () => {
+ await act(async () => {
+ render();
+ });
+
+ expect(screen.getByText('Metric Search')).toBeInTheDocument();
+ const radio = screen.getByLabelText('Builder');
+ expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
+ });
+
+ it('when metric query type is metric search and editor mode is raw', async () => {
+ await act(async () => {
+ render();
+ });
+ expect(screen.getByText('Metric Search')).toBeInTheDocument();
+ const radio = screen.getByLabelText('Code');
+ expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
+ });
+
+ it('when metric query type is metric query and editor mode is builder', async () => {
+ await act(async () => {
+ await act(async () => {
+ render();
+ });
+
+ expect(screen.getByText('Metric Query')).toBeInTheDocument();
+ const radio = screen.getByLabelText('Builder');
+ expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
+ });
+ });
+
+ it('when metric query type is metric query and editor mode is raw', async () => {
+ await act(async () => {
+ render();
+ });
+
+ expect(screen.getByText('Metric Query')).toBeInTheDocument();
+ const radio = screen.getByLabelText('Code');
+ expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
+ });
+ });
+
+ describe('confirm modal', () => {
+ it('should be shown when moving from code editor to builder when in sql mode', async () => {
+ const sqlQuery = 'SELECT * FROM test';
+ render(
+
+ );
+
+ // the modal should not be shown unless the code editor is "dirty", so need to trigger a change
+ const codeEditorElement = screen.getByLabelText(FAKE_EDITOR_LABEL);
+ fireEvent.change(codeEditorElement, { target: { value: 'select * from ' } });
+ const builderElement = screen.getByLabelText('Builder');
+ expect(builderElement).toBeInTheDocument();
+ await act(async () => {
+ await builderElement.click();
+ });
+
+ const modalTitleElem = screen.getByText('Are you sure?');
+ expect(modalTitleElem).toBeInTheDocument();
+ });
+
+ it('should not be shown when moving from builder to code when in sql mode', async () => {
+ render(
+
+ );
+ const builderElement = screen.getByLabelText('Builder');
+ expect(builderElement).toBeInTheDocument();
+ await act(async () => {
+ await builderElement.click();
+ });
+ expect(screen.queryByText('Are you sure?')).toBeNull();
+ });
+
+ it('should not be shown when moving from code to builder when in search mode', async () => {
+ render();
+
+ const builderElement = screen.getByLabelText('Builder');
+ expect(builderElement).toBeInTheDocument();
+ await act(async () => {
+ await builderElement.click();
+ });
+ expect(screen.queryByText('Are you sure?')).toBeNull();
+ });
+ });
});
diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.tsx
new file mode 100644
index 00000000000..bb5fc239f1c
--- /dev/null
+++ b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.tsx
@@ -0,0 +1,55 @@
+import React, { useCallback, useEffect, useState } from 'react';
+
+import { QueryEditorProps } from '@grafana/data';
+
+import { CloudWatchDatasource } from '../datasource';
+import { isCloudWatchLogsQuery, isCloudWatchMetricsQuery } from '../guards';
+import { CloudWatchJsonData, CloudWatchQuery } from '../types';
+
+import LogsQueryEditor from './LogsQueryEditor';
+import { MetricsQueryEditor } from './MetricsQueryEditor/MetricsQueryEditor';
+import QueryHeader from './QueryHeader';
+
+export type Props = QueryEditorProps;
+
+export const QueryEditor = (props: Props) => {
+ const { query, onChange, data } = props;
+ const [dataIsStale, setDataIsStale] = useState(false);
+ const [extraHeaderElementLeft, setExtraHeaderElementLeft] = useState();
+ const [extraHeaderElementRight, setExtraHeaderElementRight] = useState();
+
+ useEffect(() => {
+ setDataIsStale(false);
+ }, [data]);
+
+ const onChangeInternal = useCallback(
+ (query: CloudWatchQuery) => {
+ setDataIsStale(true);
+ onChange(query);
+ },
+ [onChange]
+ );
+
+ return (
+ <>
+
+
+ {isCloudWatchMetricsQuery(query) && (
+ {}}
+ onChange={onChangeInternal}
+ extraHeaderElementLeft={setExtraHeaderElementLeft}
+ extraHeaderElementRight={setExtraHeaderElementRight}
+ />
+ )}
+ {isCloudWatchLogsQuery(query) && }
+ >
+ );
+};
diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx
index 7a2a0b10b40..3466b9fcae0 100644
--- a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx
@@ -6,7 +6,6 @@ import { config } from '@grafana/runtime';
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
import { validLogsQuery, validMetricSearchBuilderQuery } from '../__mocks__/queries';
-import { CloudWatchLogsQuery, CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType } from '../types';
import QueryHeader from './QueryHeader';
@@ -20,74 +19,6 @@ describe('QueryHeader', () => {
afterEach(() => {
config.featureToggles.cloudWatchCrossAccountQuerying = originalFeatureToggleValue;
});
- it('should display metric options for metrics', async () => {
- const query: CloudWatchMetricsQuery = {
- queryMode: 'Metrics',
- id: '',
- region: 'us-east-2',
- namespace: '',
- period: '',
- alias: '',
- metricName: '',
- dimensions: {},
- matchExact: true,
- statistic: '',
- expression: '',
- refId: '',
- };
- const onChange = jest.fn();
- const onRunQuery = jest.fn();
- query.metricEditorMode = MetricEditorMode.Code;
- query.metricQueryType = MetricQueryType.Query;
-
- render(
-
- );
-
- const builderElement = screen.getByLabelText('Builder');
- expect(builderElement).toBeInTheDocument();
- await act(async () => {
- await builderElement.click();
- });
-
- const modalTitleElem = screen.getByText('Are you sure?');
- expect(modalTitleElem).toBeInTheDocument();
- expect(onChange).not.toHaveBeenCalled();
- });
-
- it('should not display metric options for logs', async () => {
- const onChange = jest.fn();
- const onRunQuery = jest.fn();
- const query: CloudWatchLogsQuery = {
- queryType: 'Metrics',
- id: '',
- region: 'us-east-2',
- expression: '',
- refId: '',
- queryMode: 'Logs',
- };
-
- render(
-
- );
-
- await waitFor(() => {
- expect(screen.queryByLabelText('Builder')).toBeNull();
- expect(screen.queryByLabelText('Code')).toBeNull();
- });
- });
describe('when changing region', () => {
const { datasource } = setupMockedDataSource();
@@ -101,11 +32,11 @@ describe('QueryHeader', () => {
datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(false);
render(
);
await waitFor(() => expect(screen.queryByText('us-east-1')).toBeInTheDocument());
@@ -126,11 +57,11 @@ describe('QueryHeader', () => {
render(
);
await waitFor(() => expect(screen.queryByText('us-east-1')).toBeInTheDocument());
@@ -151,7 +82,7 @@ describe('QueryHeader', () => {
render(
{
render(
void;
- onRunQuery: () => void;
- sqlCodeEditorIsDirty: boolean;
+export interface Props extends QueryEditorProps {
+ extraHeaderElementLeft?: JSX.Element;
+ extraHeaderElementRight?: JSX.Element;
+ dataIsStale: boolean;
}
const apiModes: Array> = [
@@ -25,18 +21,27 @@ const apiModes: Array> = [
{ label: 'CloudWatch Logs', value: 'Logs' },
];
-const QueryHeader: React.FC = ({ query, sqlCodeEditorIsDirty, datasource, onChange, onRunQuery }) => {
+const QueryHeader: React.FC = ({
+ query,
+ onChange,
+ datasource,
+ extraHeaderElementLeft,
+ extraHeaderElementRight,
+ dataIsStale,
+ data,
+ onRunQuery,
+}) => {
const { queryMode, region } = query;
const isMonitoringAccount = useIsMonitoringAccount(datasource.api, query.region);
-
const [regions, regionIsLoading] = useRegions(datasource);
const onQueryModeChange = ({ value }: SelectableValue) => {
- if (value !== queryMode) {
+ if (value && value !== queryMode) {
onChange({
+ ...datasource.getDefaultQuery(CoreApp.Unknown),
...query,
queryMode: value,
- } as CloudWatchQuery);
+ });
}
};
const onRegionChange = async (region: string) => {
@@ -49,44 +54,60 @@ const QueryHeader: React.FC = ({ query, sqlCodeEditorIsDirty,
};
const shouldDisplayMonitoringBadge =
- queryMode === 'Logs' && isMonitoringAccount && config.featureToggles.cloudWatchCrossAccountQuerying;
+ config.featureToggles.cloudWatchCrossAccountQuerying &&
+ isMonitoringAccount &&
+ (query.queryMode === 'Logs' ||
+ (isCloudWatchMetricsQuery(query) && query.metricQueryType === MetricQueryType.Search));
return (
-
- region && onRegionChange(region)}
- options={regions}
- isLoading={regionIsLoading}
- />
-
-
-
- {shouldDisplayMonitoringBadge && (
- <>
-
-
- >
- )}
-
- {queryMode === ExploreMode.Metrics && (
-
+
+ region && onRegionChange(region)}
+ options={regions}
+ isLoading={regionIsLoading}
/>
- )}
-
+
+
+
+ {extraHeaderElementLeft}
+
+
+
+ {shouldDisplayMonitoringBadge && (
+ <>
+
+ >
+ )}
+
+
+
+ {extraHeaderElementRight}
+
+ >
);
};
diff --git a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.test.tsx
index fa4060aaa12..bffc5440a59 100644
--- a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.test.tsx
@@ -32,7 +32,6 @@ describe('Cloudwatch SQLBuilderEditor', () => {
query: makeSQLQuery(),
datasource,
onChange: () => {},
- onRunQuery: () => {},
};
it('Displays the namespace', async () => {
diff --git a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.tsx
index 174fdbb5a6c..f44d1c20146 100644
--- a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.tsx
@@ -17,10 +17,9 @@ export type Props = {
query: CloudWatchMetricsQuery;
datasource: CloudWatchDatasource;
onChange: (value: CloudWatchMetricsQuery) => void;
- onRunQuery: () => void;
};
-export function SQLBuilderEditor({ query, datasource, onChange, onRunQuery }: React.PropsWithChildren) {
+export function SQLBuilderEditor({ query, datasource, onChange }: React.PropsWithChildren) {
const sql = query.sql ?? {};
const onQueryChange = useCallback(
@@ -33,9 +32,8 @@ export function SQLBuilderEditor({ query, datasource, onChange, onRunQuery }: Re
};
onChange(fullQuery);
- onRunQuery();
},
- [onChange, onRunQuery]
+ [onChange]
);
const [sqlPreview, setSQLPreview] = useState();
diff --git a/public/app/plugins/datasource/cloudwatch/components/SQLCodeEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/SQLCodeEditor.tsx
index 03ca4b998fc..aaef0cc651e 100644
--- a/public/app/plugins/datasource/cloudwatch/components/SQLCodeEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/SQLCodeEditor.tsx
@@ -12,11 +12,10 @@ export interface Props {
region: string;
sql: string;
onChange: (sql: string) => void;
- onRunQuery: () => void;
datasource: CloudWatchDatasource;
}
-export const SQLCodeEditor: FunctionComponent = ({ region, sql, onChange, onRunQuery, datasource }) => {
+export const SQLCodeEditor: FunctionComponent = ({ region, sql, onChange, datasource }) => {
useEffect(() => {
datasource.sqlCompletionItemProvider.setRegion(region);
}, [region, datasource]);
@@ -27,10 +26,9 @@ export const SQLCodeEditor: FunctionComponent = ({ region, sql, onChange,
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => {
const text = editor.getValue();
onChange(text);
- onRunQuery();
});
},
- [onChange, onRunQuery]
+ [onChange]
);
return (
diff --git a/public/app/plugins/datasource/cloudwatch/components/index.ts b/public/app/plugins/datasource/cloudwatch/components/index.ts
index 3c3d07f66f0..9ea328575b4 100644
--- a/public/app/plugins/datasource/cloudwatch/components/index.ts
+++ b/public/app/plugins/datasource/cloudwatch/components/index.ts
@@ -1,8 +1,7 @@
export { Dimensions } from './Dimensions/Dimensions';
export { QueryInlineField, QueryField } from './Forms';
-export { PanelQueryEditor } from './PanelQueryEditor';
+export { QueryEditor as PanelQueryEditor } from './QueryEditor';
export { CloudWatchLogsQueryEditor } from './LogsQueryEditor';
export { MetricStatEditor } from './MetricStatEditor';
export { SQLBuilderEditor } from './SQLBuilderEditor';
export { MathExpressionQueryField } from './MathExpressionQueryField';
-export { SQLCodeEditor } from './SQLCodeEditor';
diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts
index 5e6cab8bd84..67d0eb62b96 100644
--- a/public/app/plugins/datasource/cloudwatch/datasource.ts
+++ b/public/app/plugins/datasource/cloudwatch/datasource.ts
@@ -159,7 +159,7 @@ export class CloudWatchDatasource
}
getQueryDisplayText(query: CloudWatchQuery) {
- if (query.queryMode === 'Logs') {
+ if (isCloudWatchLogsQuery(query)) {
return query.expression ?? '';
} else {
return JSON.stringify(query);
diff --git a/public/app/plugins/datasource/cloudwatch/module.tsx b/public/app/plugins/datasource/cloudwatch/module.tsx
index e922010b5f7..c8a62b1a134 100644
--- a/public/app/plugins/datasource/cloudwatch/module.tsx
+++ b/public/app/plugins/datasource/cloudwatch/module.tsx
@@ -4,7 +4,7 @@ import { getAppEvents } from '@grafana/runtime';
import { ConfigEditor } from './components/ConfigEditor';
import LogsCheatSheet from './components/LogsCheatSheet';
import { MetaInspector } from './components/MetaInspector';
-import { PanelQueryEditor } from './components/PanelQueryEditor';
+import { QueryEditor } from './components/QueryEditor';
import { CloudWatchDatasource } from './datasource';
import { onDashboardLoadedHandler } from './tracking';
import { CloudWatchJsonData, CloudWatchQuery } from './types';
@@ -14,7 +14,7 @@ export const plugin = new DataSourcePlugin