diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx
index 2e782cc07a1..9cc6fef9582 100644
--- a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx
@@ -6,6 +6,7 @@ import { config } from '@grafana/runtime';
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
import { validLogsQuery, validMetricSearchBuilderQuery } from '../__mocks__/queries';
+import { DEFAULT_LOGS_QUERY_STRING } from '../defaultQueries';
import QueryHeader from './QueryHeader';
@@ -16,11 +17,10 @@ const ds = setupMockedDataSource({
ds.datasource.resources.getRegions = jest.fn().mockResolvedValue([]);
describe('QueryHeader', () => {
- afterEach(() => {
- config.featureToggles.cloudWatchCrossAccountQuerying = originalFeatureToggleValue;
- });
-
describe('when changing region', () => {
+ afterEach(() => {
+ config.featureToggles.cloudWatchCrossAccountQuerying = originalFeatureToggleValue;
+ });
const { datasource } = setupMockedDataSource();
datasource.resources.getRegions = jest.fn().mockResolvedValue([
{ value: 'us-east-2', label: 'us-east-2' },
@@ -117,4 +117,91 @@ describe('QueryHeader', () => {
expect(datasource.resources.isMonitoringAccount).not.toHaveBeenCalledWith();
});
});
+
+ describe('when changing query mode', () => {
+ const { datasource } = setupMockedDataSource();
+ it('should set default log query when switching to log mode', async () => {
+ const onChange = jest.fn();
+ datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false);
+ render(
+
+ );
+ expect(await screen.findByText('CloudWatch Metrics')).toBeInTheDocument();
+ await selectEvent.select(await screen.findByLabelText('Query mode'), 'CloudWatch Logs', {
+ container: document.body,
+ });
+ expect(onChange).toHaveBeenCalledWith({
+ ...validMetricSearchBuilderQuery,
+ logGroupNames: undefined,
+ logGroups: [],
+ queryMode: 'Logs',
+ sqlExpression: '',
+ expression: DEFAULT_LOGS_QUERY_STRING,
+ });
+ });
+
+ it('should set expression to empty when switching to metrics mode', async () => {
+ const onChange = jest.fn();
+ datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false);
+ render(
+
+ );
+ expect(await screen.findByText('CloudWatch Logs')).toBeInTheDocument();
+ await selectEvent.select(await screen.findByLabelText('Query mode'), 'CloudWatch Metrics', {
+ container: document.body,
+ });
+ expect(onChange).toHaveBeenCalledWith({
+ ...validMetricSearchBuilderQuery,
+ logGroupNames: undefined,
+ logGroups: [],
+ sqlExpression: '',
+ expression: '',
+ });
+ });
+ });
+ describe('log expression', () => {
+ const { datasource } = setupMockedDataSource();
+ it('should disable run query button when empty', async () => {
+ const onChange = jest.fn();
+ datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false);
+ render(
+
+ );
+ expect(await screen.findByText('Run queries')).toBeInTheDocument();
+ expect(screen.getByText('Run queries').closest('button')).toBeDisabled();
+ });
+ it('should enable run query button when set', async () => {
+ const onChange = jest.fn();
+ datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false);
+ render(
+
+ );
+ expect(await screen.findByText('Run queries')).toBeInTheDocument();
+ expect(screen.getByText('Run queries').closest('button')).not.toBeDisabled();
+ });
+ });
});
diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx
index 76f05ee0468..e5f1e5eb4e6 100644
--- a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx
@@ -6,7 +6,8 @@ import { config } from '@grafana/runtime';
import { Badge, Button } from '@grafana/ui';
import { CloudWatchDatasource } from '../datasource';
-import { isCloudWatchMetricsQuery } from '../guards';
+import { DEFAULT_LOGS_QUERY_STRING } from '../defaultQueries';
+import { isCloudWatchLogsQuery, isCloudWatchMetricsQuery } from '../guards';
import { useIsMonitoringAccount, useRegions } from '../hooks';
import { CloudWatchJsonData, CloudWatchQuery, CloudWatchQueryMode, MetricQueryType } from '../types';
@@ -34,12 +35,19 @@ const QueryHeader: React.FC = ({
const { queryMode, region } = query;
const isMonitoringAccount = useIsMonitoringAccount(datasource.resources, query.region);
const [regions, regionIsLoading] = useRegions(datasource);
+ const emptyLogsExpression = isCloudWatchLogsQuery(query) ? !query.expression : false;
const onQueryModeChange = ({ value }: SelectableValue) => {
if (value && value !== queryMode) {
+ // reset expression to a default string when the query mode changes
+ let expression = '';
+ if (value === 'Logs') {
+ expression = DEFAULT_LOGS_QUERY_STRING;
+ }
onChange({
...datasource.getDefaultQuery(CoreApp.Unknown),
...query,
+ expression,
queryMode: value,
});
}
@@ -100,7 +108,7 @@ const QueryHeader: React.FC = ({
size="sm"
onClick={onRunQuery}
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : undefined}
- disabled={data?.state === LoadingState.Loading}
+ disabled={data?.state === LoadingState.Loading || emptyLogsExpression}
>
Run queries
diff --git a/public/app/plugins/datasource/cloudwatch/defaultQueries.ts b/public/app/plugins/datasource/cloudwatch/defaultQueries.ts
index 5eff9bbd8cb..e921f9d75cc 100644
--- a/public/app/plugins/datasource/cloudwatch/defaultQueries.ts
+++ b/public/app/plugins/datasource/cloudwatch/defaultQueries.ts
@@ -16,13 +16,14 @@ export const DEFAULT_METRICS_QUERY: Omit = {
matchExact: true,
};
+export const DEFAULT_LOGS_QUERY_STRING = 'fields @timestamp, @message |\n sort @timestamp desc |\n limit 20';
+
export const getDefaultLogsQuery = (
defaultLogGroups?: LogGroup[],
legacyDefaultLogGroups?: string[]
): Omit => ({
id: '',
region: 'default',
- expression: '',
// in case legacy default log groups have been defined in the ConfigEditor, they will be migrated in the LogGroupsField component or the next time the ConfigEditor is opened.
// the migration requires async backend calls, so we don't want to do it here as it would block the UI.
logGroupNames: legacyDefaultLogGroups,