From 3b383149db7408b1669159ad2edad9c141498ece Mon Sep 17 00:00:00 2001 From: kay delaney <45561153+kaydelaney@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:54:29 +0100 Subject: [PATCH] CloudWatch Logs: Adds template variable support to log groups (#25604) * CloudWatch Logs: Adds template variable support to log groups Closes #25099 --- .../cloudwatch/components/LogsQueryEditor.tsx | 5 ++- .../cloudwatch/components/LogsQueryField.tsx | 40 +++++++++++++------ .../components/PanelQueryEditor.tsx | 6 ++- .../datasource/cloudwatch/datasource.ts | 7 ++++ .../cloudwatch/specs/datasource.test.ts | 16 ++++++++ 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx index 1c19d45e3a5..961115e16f1 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx @@ -12,7 +12,7 @@ import { CloudWatchLanguageProvider } from '../language_provider'; import CloudWatchLink from './CloudWatchLink'; import { css } from 'emotion'; -type Props = QueryEditorProps; +type Props = QueryEditorProps & { allowCustomValue?: boolean }; const labelClass = css` margin-left: 3px; @@ -20,7 +20,7 @@ const labelClass = css` `; export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor(props: Props) { - const { query, data, datasource, onRunQuery, onChange, exploreId, exploreMode } = props; + const { query, data, datasource, onRunQuery, onChange, exploreId, exploreMode, allowCustomValue = false } = props; let absolute: AbsoluteTimeRange; if (data?.request?.range?.from) { @@ -55,6 +55,7 @@ export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor absoluteRange={absolute} syntaxLoaded={isSyntaxReady} syntax={syntax} + allowCustomValue={allowCustomValue} ExtraFieldElement={ diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx index 734c3aee5a8..e28b21a1a15 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx @@ -42,6 +42,7 @@ export interface CloudWatchLogsQueryFieldProps extends ExploreQueryFieldProps { this.setState(state => { - const selectedLogGroups = intersectionBy(state.selectedLogGroups, logGroups, 'value'); + const selectedLogGroups = state.selectedLogGroups; if (onChange) { const nextQuery = { ...query, @@ -200,21 +209,22 @@ export class CloudWatchLogsQueryField extends React.PureComponent>) => { + setSelectedLogGroups = (selectedLogGroups: Array>) => { this.setState({ - selectedLogGroups: v, + selectedLogGroups, }); const { onChange, query } = this.props; + onChange?.({ + ...(query as CloudWatchLogsQuery), + logGroupNames: selectedLogGroups.map(logGroupName => logGroupName.value!) ?? [], + }); + }; - if (onChange) { - const nextQuery = { - ...query, - logGroupNames: v.map(logGroupName => logGroupName.value!) ?? [], - }; - - onChange(nextQuery); - } + setCustomLogGroups = (v: string) => { + const customLogGroup: SelectableValue = { value: v, label: v }; + const selectedLogGroups = [...this.state.selectedLogGroups, customLogGroup]; + this.setSelectedLogGroups(selectedLogGroups); }; setSelectedRegion = async (v: SelectableValue) => { @@ -327,7 +337,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent { this.setSelectedLogGroups(v); }} + onCreateOption={v => { + this.setCustomLogGroups(v); + }} className={containerClass} closeMenuOnSelect={false} isClearable={true} diff --git a/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.tsx index b64c74a67b3..23084e30d98 100644 --- a/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/PanelQueryEditor.tsx @@ -30,7 +30,11 @@ export class PanelQueryEditor extends PureComponent { } /> - {apiMode === ExploreMode.Logs ? : } + {apiMode === ExploreMode.Logs ? ( + + ) : ( + + )} ); } diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 1a77eb999d9..eed9553b2f6 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -580,6 +580,13 @@ export class CloudWatchDatasource extends DataSourceApi + this.replace(logGroup, scopedVars, true, 'log groups') + ); + } }); } diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts index 772a9a4dc03..09ee6cfd977 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts @@ -163,6 +163,7 @@ describe('CloudWatchDatasource', () => { ], }); }); + it('should stop querying when no more data retrieved past max attempts', async () => { const fakeFrames = genMockFrames(10); for (let i = 7; i < fakeFrames.length; i++) { @@ -243,6 +244,21 @@ describe('CloudWatchDatasource', () => { }); expect(i).toBe(3); }); + + it('should call the replace method on provided log groups', () => { + const replaceSpy = jest.spyOn(ctx.ds, 'replace').mockImplementation((target: string) => target); + ctx.ds.makeLogActionRequest('StartQuery', [ + { + queryString: 'test query string', + region: 'default', + logGroupNames: ['log-group', '${my_var}Variable', 'Cool${other_var}'], + }, + ]); + + expect(replaceSpy).toBeCalledWith('log-group', undefined, true, 'log groups'); + expect(replaceSpy).toBeCalledWith('${my_var}Variable', undefined, true, 'log groups'); + expect(replaceSpy).toBeCalledWith('Cool${other_var}', undefined, true, 'log groups'); + }); }); describe('When performing CloudWatch metrics query', () => {