CloudWatch: Add default log groups to config page (#49286)

Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com>
This commit is contained in:
Isabella Siu 2022-07-07 13:03:02 -04:00 committed by GitHub
parent 3d68023606
commit 8dd8c50dc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 259 additions and 81 deletions

View File

@ -86,8 +86,8 @@ exports[`no enzyme tests`] = {
"public/app/features/folders/FolderSettingsPage.test.tsx:1109052730": [ "public/app/features/folders/FolderSettingsPage.test.tsx:1109052730": [
[0, 19, 13, "RegExp match", "2409514259"] [0, 19, 13, "RegExp match", "2409514259"]
], ],
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:227258837": [ "public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:4057721851": [
[0, 19, 13, "RegExp match", "2409514259"] [1, 19, 13, "RegExp match", "2409514259"]
], ],
"public/app/plugins/datasource/elasticsearch/configuration/ConfigEditor.test.tsx:3481855642": [ "public/app/plugins/datasource/elasticsearch/configuration/ConfigEditor.test.tsx:3481855642": [
[0, 26, 13, "RegExp match", "2409514259"] [0, 26, 13, "RegExp match", "2409514259"]
@ -6659,8 +6659,12 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"] [0, 0, 0, "Unexpected any. Specify a different type.", "2"]
], ],
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx:5381": [ "public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
], ],
"public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx:5381": [ "public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"] [0, 0, 0, "Do not use any type assertions.", "0"]
@ -6668,7 +6672,8 @@ exports[`better eslint`] = {
"public/app/plugins/datasource/cloudwatch/components/LogsQueryField.test.tsx:5381": [ "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.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [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.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
], ],
"public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx:5381": [ "public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@ -7568,10 +7573,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "13"], [0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"], [0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Unexpected any. Specify a different type.", "15"], [0, 0, 0, "Unexpected any. Specify a different type.", "15"],
[0, 0, 0, "Unexpected any. Specify a different type.", "16"], [0, 0, 0, "Unexpected any. Specify a different type.", "16"]
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
[0, 0, 0, "Unexpected any. Specify a different type.", "18"],
[0, 0, 0, "Unexpected any. Specify a different type.", "19"]
], ],
"public/app/plugins/datasource/influxdb/specs/influx_query_model.test.ts:5381": [ "public/app/plugins/datasource/influxdb/specs/influx_query_model.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],

View File

@ -52,6 +52,7 @@ export function setupMockedDataSource({
datasource.getNamespaces = jest.fn().mockResolvedValue([]); datasource.getNamespaces = jest.fn().mockResolvedValue([]);
datasource.getRegions = jest.fn().mockResolvedValue([]); datasource.getRegions = jest.fn().mockResolvedValue([]);
datasource.defaultLogGroups = [];
const fetchMock = jest.fn().mockReturnValue(of({ data })); const fetchMock = jest.fn().mockReturnValue(of({ data }));
setBackendSrv({ fetch: fetchMock } as any); setBackendSrv({ fetch: fetchMock } as any);

View File

@ -1,26 +1,43 @@
import { render, screen } from '@testing-library/react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import selectEvent from 'react-select-event';
import { AwsAuthType } from '@grafana/aws-sdk'; import { AwsAuthType } from '@grafana/aws-sdk';
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
import { ConfigEditor, Props } from './ConfigEditor'; import { ConfigEditor, Props } from './ConfigEditor';
const ds = setupMockedDataSource();
jest.mock('app/features/plugins/datasource_srv', () => ({ jest.mock('app/features/plugins/datasource_srv', () => ({
getDatasourceSrv: () => ({ getDatasourceSrv: () => ({
loadDatasource: jest.fn().mockImplementation(() => loadDatasource: jest.fn().mockResolvedValue({
Promise.resolve({ getRegions: jest.fn().mockResolvedValue([
getRegions: jest.fn().mockReturnValue([
{ {
label: 'ap-east-1', label: 'ap-east-1',
value: 'ap-east-1', value: 'ap-east-1',
}, },
]), ]),
}) describeLogGroups: jest.fn().mockResolvedValue(['logGroup-foo', 'logGroup-bar']),
), getActualRegion: jest.fn().mockReturnValue('ap-east-1'),
getVariables: jest.fn().mockReturnValue([]),
}),
}),
}));
jest.mock('./XrayLinkConfig', () => ({
XrayLinkConfig: () => <></>,
}));
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getBackendSrv: () => ({
put: jest.fn().mockResolvedValue({ datasource: ds.datasource }),
}), }),
})); }));
const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
options: { options: {
id: 1, id: 1,
@ -60,12 +77,16 @@ const setup = (propOverrides?: object) => {
onOptionsChange: jest.fn(), onOptionsChange: jest.fn(),
}; };
Object.assign(props, propOverrides); const setup = (propOverrides?: object) => {
const newProps = { ...props, ...propOverrides };
return shallow(<ConfigEditor {...props} />); return shallow(<ConfigEditor {...newProps} />);
}; };
describe('Render', () => { describe('Render', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('should render component', () => { it('should render component', () => {
const wrapper = setup(); const wrapper = setup();
@ -107,4 +128,15 @@ describe('Render', () => {
}); });
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('should load log groups when multiselect is opened', async () => {
(window as any).grafanaBootData = {
settings: {},
};
render(<ConfigEditor {...props} />);
const multiselect = await screen.findByLabelText('Log Groups');
selectEvent.openMenu(multiselect);
expect(await screen.findByText('logGroup-foo')).toBeInTheDocument();
});
}); });

View File

@ -7,7 +7,9 @@ import {
DataSourcePluginOptionsEditorProps, DataSourcePluginOptionsEditorProps,
onUpdateDatasourceJsonDataOption, onUpdateDatasourceJsonDataOption,
updateDatasourcePluginJsonDataOption, updateDatasourcePluginJsonDataOption,
updateDatasourcePluginOption,
} from '@grafana/data'; } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { Input, InlineField } from '@grafana/ui'; import { Input, InlineField } from '@grafana/ui';
import { notifyApp } from 'app/core/actions'; import { notifyApp } from 'app/core/actions';
import { createWarningNotification } from 'app/core/copy/appNotification'; import { createWarningNotification } from 'app/core/copy/appNotification';
@ -17,16 +19,43 @@ import { store } from 'app/store/store';
import { CloudWatchDatasource } from '../datasource'; import { CloudWatchDatasource } from '../datasource';
import { CloudWatchJsonData, CloudWatchSecureJsonData } from '../types'; import { CloudWatchJsonData, CloudWatchSecureJsonData } from '../types';
import { LogGroupSelector } from './LogGroupSelector';
import { XrayLinkConfig } from './XrayLinkConfig'; import { XrayLinkConfig } from './XrayLinkConfig';
export type Props = DataSourcePluginOptionsEditorProps<CloudWatchJsonData, CloudWatchSecureJsonData>; export type Props = DataSourcePluginOptionsEditorProps<CloudWatchJsonData, CloudWatchSecureJsonData>;
export const ConfigEditor: FC<Props> = (props: Props) => { export const ConfigEditor: FC<Props> = (props: Props) => {
const { options } = props; const { options } = props;
const { defaultLogGroups, logsTimeout, defaultRegion } = options.jsonData;
const [saved, setSaved] = useState(!!options.version && options.version > 1);
const datasource = useDatasource(options.name); const datasource = useDatasource(options.name, saved);
useAuthenticationWarning(options.jsonData); useAuthenticationWarning(options.jsonData);
const logsTimeoutError = useTimoutValidation(props.options.jsonData.logsTimeout); const logsTimeoutError = useTimoutValidation(logsTimeout);
useEffect(() => {
setSaved(false);
}, [
props.options.jsonData.assumeRoleArn,
props.options.jsonData.authType,
props.options.jsonData.defaultRegion,
props.options.jsonData.endpoint,
props.options.jsonData.externalId,
props.options.jsonData.profile,
props.options.secureJsonData?.accessKey,
props.options.secureJsonData?.secretKey,
]);
const saveOptions = async (): Promise<void> => {
if (saved) {
return;
}
await getBackendSrv()
.put(`/api/datasources/${options.id}`, options)
.then((result: { datasource: any }) => {
updateDatasourcePluginOption(props, 'version', result.datasource.version);
});
setSaved(true);
};
return ( return (
<> <>
@ -52,7 +81,7 @@ export const ConfigEditor: FC<Props> = (props: Props) => {
<InlineField <InlineField
label="Timeout" label="Timeout"
labelWidth={28} labelWidth={28}
tooltip='Custom timout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as "15m" "30s" "2000ms" etc.' tooltip='Custom timeout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as "15m" "30s" "2000ms" etc.'
invalid={Boolean(logsTimeoutError)} invalid={Boolean(logsTimeoutError)}
> >
<Input <Input
@ -63,6 +92,23 @@ export const ConfigEditor: FC<Props> = (props: Props) => {
title={'The timeout must be a valid duration string, such as "15m" "30s" "2000ms" etc.'} title={'The timeout must be a valid duration string, such as "15m" "30s" "2000ms" etc.'}
/> />
</InlineField> </InlineField>
<InlineField
label="Default Log Groups"
labelWidth={28}
tooltip="Optionally, specify default log groups for CloudWatch Logs queries."
>
<LogGroupSelector
region={defaultRegion ?? ''}
selectedLogGroups={defaultLogGroups ?? []}
datasource={datasource}
onChange={(logGroups) => {
updateDatasourcePluginJsonDataOption(props, 'defaultLogGroups', logGroups);
}}
onOpenMenu={saveOptions}
width={60}
saved={saved}
/>
</InlineField>
</div> </div>
<XrayLinkConfig <XrayLinkConfig
@ -91,10 +137,14 @@ function useAuthenticationWarning(jsonData: CloudWatchJsonData) {
}, [jsonData.authType, jsonData.database, jsonData.profile]); }, [jsonData.authType, jsonData.database, jsonData.profile]);
} }
function useDatasource(datasourceName: string) { function useDatasource(datasourceName: string, saved: boolean) {
const [datasource, setDatasource] = useState<CloudWatchDatasource>(); const [datasource, setDatasource] = useState<CloudWatchDatasource>();
useEffect(() => { useEffect(() => {
// reload the datasource when it's saved
if (!saved) {
return;
}
getDatasourceSrv() getDatasourceSrv()
.loadDatasource(datasourceName) .loadDatasource(datasourceName)
.then((datasource) => { .then((datasource) => {
@ -102,7 +152,7 @@ function useDatasource(datasourceName: string) {
// So a "as" type assertion here is a necessary evil. // So a "as" type assertion here is a necessary evil.
setDatasource(datasource as CloudWatchDatasource); setDatasource(datasource as CloudWatchDatasource);
}); });
}, [datasourceName]); }, [datasourceName, saved]);
return datasource; return datasource;
} }

View File

@ -85,7 +85,6 @@ describe('LogGroupSelector', () => {
'DeliciousGroup', 'DeliciousGroup',
'DeliciousGroup2', 'DeliciousGroup2',
'DeliciousGroup3', 'DeliciousGroup3',
'VelvetGroup', 'VelvetGroup',
'VelvetGroup2', 'VelvetGroup2',
'VelvetGroup3', 'VelvetGroup3',

View File

@ -1,4 +1,4 @@
import { render, screen, fireEvent } from '@testing-library/react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import _, { DebouncedFunc } from 'lodash'; // eslint-disable-line lodash/import-scope import _, { DebouncedFunc } from 'lodash'; // eslint-disable-line lodash/import-scope
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
@ -34,4 +34,25 @@ describe('CloudWatchLogsQueryField', () => {
}); });
expect(onRunQuery).toHaveBeenCalled(); expect(onRunQuery).toHaveBeenCalled();
}); });
it('loads defaultLogGroups', async () => {
const onRunQuery = jest.fn();
const ds = setupMockedDataSource();
ds.datasource.defaultLogGroups = ['foo'];
render(
<CloudWatchLogsQueryField
absoluteRange={{ from: 1, to: 10 }}
exploreId={ExploreId.left}
datasource={ds.datasource}
query={{} as any}
onRunQuery={onRunQuery}
onChange={() => {}}
/>
);
await waitFor(() => {
expect(screen.getByText('foo')).toBeInTheDocument();
});
});
}); });

View File

@ -66,10 +66,10 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
} }
componentDidMount = () => { componentDidMount = () => {
const { query, onChange } = this.props; const { query, datasource, onChange } = this.props;
if (onChange) { if (onChange) {
onChange({ ...query, logGroupNames: query.logGroupNames ?? [] }); onChange({ ...query, logGroupNames: query.logGroupNames ?? datasource.defaultLogGroups });
} }
}; };
@ -135,7 +135,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
inputEl={ inputEl={
<LogGroupSelector <LogGroupSelector
region={region} region={region}
selectedLogGroups={logGroupNames ?? []} selectedLogGroups={logGroupNames ?? datasource.defaultLogGroups}
datasource={datasource} datasource={datasource}
onChange={function (logGroups: string[]): void { onChange={function (logGroups: string[]): void {
onChange({ ...query, logGroupNames: logGroups }); onChange({ ...query, logGroupNames: logGroups });

View File

@ -72,7 +72,7 @@ exports[`Render should disable access key id field 1`] = `
invalid={false} invalid={false}
label="Timeout" label="Timeout"
labelWidth={28} labelWidth={28}
tooltip="Custom timout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc." tooltip="Custom timeout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc."
> >
<Input <Input
onChange={[Function]} onChange={[Function]}
@ -82,6 +82,20 @@ exports[`Render should disable access key id field 1`] = `
width={60} width={60}
/> />
</InlineField> </InlineField>
<InlineField
label="Default Log Groups"
labelWidth={28}
tooltip="Optionally, specify default log groups for CloudWatch Logs queries."
>
<LogGroupSelector
onChange={[Function]}
onOpenMenu={[Function]}
region="us-east-2"
saved={false}
selectedLogGroups={Array []}
width={60}
/>
</InlineField>
</div> </div>
<XrayLinkConfig <XrayLinkConfig
onChange={[Function]} onChange={[Function]}
@ -156,7 +170,7 @@ exports[`Render should render component 1`] = `
invalid={false} invalid={false}
label="Timeout" label="Timeout"
labelWidth={28} labelWidth={28}
tooltip="Custom timout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc." tooltip="Custom timeout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc."
> >
<Input <Input
onChange={[Function]} onChange={[Function]}
@ -166,6 +180,20 @@ exports[`Render should render component 1`] = `
width={60} width={60}
/> />
</InlineField> </InlineField>
<InlineField
label="Default Log Groups"
labelWidth={28}
tooltip="Optionally, specify default log groups for CloudWatch Logs queries."
>
<LogGroupSelector
onChange={[Function]}
onOpenMenu={[Function]}
region="us-east-2"
saved={false}
selectedLogGroups={Array []}
width={60}
/>
</InlineField>
</div> </div>
<XrayLinkConfig <XrayLinkConfig
onChange={[Function]} onChange={[Function]}
@ -245,7 +273,7 @@ exports[`Render should show access key and secret access key fields 1`] = `
invalid={false} invalid={false}
label="Timeout" label="Timeout"
labelWidth={28} labelWidth={28}
tooltip="Custom timout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc." tooltip="Custom timeout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc."
> >
<Input <Input
onChange={[Function]} onChange={[Function]}
@ -255,6 +283,20 @@ exports[`Render should show access key and secret access key fields 1`] = `
width={60} width={60}
/> />
</InlineField> </InlineField>
<InlineField
label="Default Log Groups"
labelWidth={28}
tooltip="Optionally, specify default log groups for CloudWatch Logs queries."
>
<LogGroupSelector
onChange={[Function]}
onOpenMenu={[Function]}
region="us-east-2"
saved={false}
selectedLogGroups={Array []}
width={60}
/>
</InlineField>
</div> </div>
<XrayLinkConfig <XrayLinkConfig
onChange={[Function]} onChange={[Function]}
@ -334,7 +376,7 @@ exports[`Render should show arn role field 1`] = `
invalid={false} invalid={false}
label="Timeout" label="Timeout"
labelWidth={28} labelWidth={28}
tooltip="Custom timout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc." tooltip="Custom timeout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc."
> >
<Input <Input
onChange={[Function]} onChange={[Function]}
@ -344,6 +386,20 @@ exports[`Render should show arn role field 1`] = `
width={60} width={60}
/> />
</InlineField> </InlineField>
<InlineField
label="Default Log Groups"
labelWidth={28}
tooltip="Optionally, specify default log groups for CloudWatch Logs queries."
>
<LogGroupSelector
onChange={[Function]}
onOpenMenu={[Function]}
region="us-east-2"
saved={false}
selectedLogGroups={Array []}
width={60}
/>
</InlineField>
</div> </div>
<XrayLinkConfig <XrayLinkConfig
onChange={[Function]} onChange={[Function]}
@ -423,7 +479,7 @@ exports[`Render should show credentials profile name field 1`] = `
invalid={false} invalid={false}
label="Timeout" label="Timeout"
labelWidth={28} labelWidth={28}
tooltip="Custom timout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc." tooltip="Custom timeout for CloudWatch Logs insights queries which have max concurrency limits. Default is 15 minutes. Must be a valid duration string, such as \\"15m\\" \\"30s\\" \\"2000ms\\" etc."
> >
<Input <Input
onChange={[Function]} onChange={[Function]}
@ -433,6 +489,20 @@ exports[`Render should show credentials profile name field 1`] = `
width={60} width={60}
/> />
</InlineField> </InlineField>
<InlineField
label="Default Log Groups"
labelWidth={28}
tooltip="Optionally, specify default log groups for CloudWatch Logs queries."
>
<LogGroupSelector
onChange={[Function]}
onOpenMenu={[Function]}
region="us-east-2"
saved={false}
selectedLogGroups={Array []}
width={60}
/>
</InlineField>
</div> </div>
<XrayLinkConfig <XrayLinkConfig
onChange={[Function]} onChange={[Function]}

View File

@ -101,6 +101,7 @@ export class CloudWatchDatasource
tracingDataSourceUid?: string; tracingDataSourceUid?: string;
logsTimeout: string; logsTimeout: string;
defaultLogGroups: string[];
type = 'cloudwatch'; type = 'cloudwatch';
standardStatistics = ['Average', 'Maximum', 'Minimum', 'Sum', 'SampleCount']; standardStatistics = ['Average', 'Maximum', 'Minimum', 'Sum', 'SampleCount'];
@ -127,6 +128,7 @@ export class CloudWatchDatasource
this.languageProvider = new CloudWatchLanguageProvider(this); this.languageProvider = new CloudWatchLanguageProvider(this);
this.tracingDataSourceUid = instanceSettings.jsonData.tracingDatasourceUid; this.tracingDataSourceUid = instanceSettings.jsonData.tracingDatasourceUid;
this.logsTimeout = instanceSettings.jsonData.logsTimeout || '15m'; this.logsTimeout = instanceSettings.jsonData.logsTimeout || '15m';
this.defaultLogGroups = instanceSettings.jsonData.defaultLogGroups || [];
this.sqlCompletionItemProvider = new SQLCompletionItemProvider(this, this.templateSrv); this.sqlCompletionItemProvider = new SQLCompletionItemProvider(this, this.templateSrv);
this.metricMathCompletionItemProvider = new MetricMathCompletionItemProvider(this, this.templateSrv); this.metricMathCompletionItemProvider = new MetricMathCompletionItemProvider(this, this.templateSrv);
this.variables = new CloudWatchVariableSupport(this); this.variables = new CloudWatchVariableSupport(this);
@ -176,7 +178,14 @@ export class CloudWatchDatasource
logQueries: CloudWatchLogsQuery[], logQueries: CloudWatchLogsQuery[],
options: DataQueryRequest<CloudWatchQuery> options: DataQueryRequest<CloudWatchQuery>
): Observable<DataQueryResponse> => { ): Observable<DataQueryResponse> => {
const validLogQueries = logQueries.filter((item) => item.logGroupNames?.length); const queryParams = logQueries.map((target: CloudWatchLogsQuery) => ({
queryString: target.expression || '',
refId: target.refId,
logGroupNames: target.logGroupNames || this.defaultLogGroups,
region: this.replace(this.getActualRegion(target.region), options.scopedVars, true, 'region'),
}));
const validLogQueries = queryParams.filter((item) => item.logGroupNames?.length);
if (logQueries.length > validLogQueries.length) { if (logQueries.length > validLogQueries.length) {
return of({ data: [], error: { message: 'Log group is required' } }); return of({ data: [], error: { message: 'Log group is required' } });
} }
@ -186,13 +195,6 @@ export class CloudWatchDatasource
return of({ data: [], state: LoadingState.Done }); return of({ data: [], state: LoadingState.Done });
} }
const queryParams = logQueries.map((target: CloudWatchLogsQuery) => ({
queryString: target.expression || '',
refId: target.refId,
logGroupNames: target.logGroupNames,
region: this.replace(this.getActualRegion(target.region), options.scopedVars, true, 'region'),
}));
const startTime = new Date(); const startTime = new Date();
const timeoutFunc = () => { const timeoutFunc = () => {
return Date.now() >= startTime.valueOf() + rangeUtil.intervalToMs(this.logsTimeout); return Date.now() >= startTime.valueOf() + rangeUtil.intervalToMs(this.logsTimeout);

View File

@ -121,6 +121,7 @@ export interface CloudWatchJsonData extends AwsAuthDataSourceJsonData {
logsTimeout?: string; logsTimeout?: string;
// Used to create links if logs contain traceId. // Used to create links if logs contain traceId.
tracingDatasourceUid?: string; tracingDatasourceUid?: string;
defaultLogGroups?: string[];
} }
export interface CloudWatchSecureJsonData extends AwsAuthDataSourceSecureJsonData { export interface CloudWatchSecureJsonData extends AwsAuthDataSourceSecureJsonData {