diff --git a/.betterer.results b/.betterer.results index 9a5c5722197..5886c801ee6 100644 --- a/.betterer.results +++ b/.betterer.results @@ -56,7 +56,7 @@ exports[`no enzyme tests`] = { "public/app/features/dimensions/editors/ThresholdsEditor/ThresholdsEditor.test.tsx:145048794": [ [0, 17, 13, "RegExp match", "2409514259"] ], - "public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:2210656642": [ + "public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:2983010995": [ [1, 19, 13, "RegExp match", "2409514259"] ] }` @@ -5890,15 +5890,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/cloudwatch/__mocks__/monarch/Monaco.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/plugins/datasource/cloudwatch/api.ts: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.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"] - ], "public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index d49d85b45cb..96d8a4c556b 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "regexp" "time" @@ -82,7 +83,6 @@ const ( var plog = log.New("tsdb.cloudwatch") var aliasFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`) -var baseLimit = int64(1) func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, features featuremgmt.FeatureToggles) *CloudWatchService { plog.Debug("initing") @@ -203,17 +203,12 @@ func (e *cloudWatchExecutor) checkHealthMetrics(pluginCtx backend.PluginContext) return err } -func (e *cloudWatchExecutor) checkHealthLogs(ctx context.Context, pluginCtx backend.PluginContext) error { - logsClient, err := e.getCWLogsClient(pluginCtx, defaultRegion) - if err != nil { - return err +func (e *cloudWatchExecutor) checkHealthLogs(pluginCtx backend.PluginContext) error { + parameters := url.Values{ + "limit": []string{"1"}, } - parameters := LogQueryJson{ - Limit: &baseLimit, - } - - _, err = e.handleDescribeLogGroups(ctx, logsClient, parameters) + _, err := e.handleGetLogGroups(pluginCtx, parameters) return err } @@ -228,7 +223,7 @@ func (e *cloudWatchExecutor) CheckHealth(ctx context.Context, req *backend.Check metricsTest = fmt.Sprintf("CloudWatch metrics query failed: %s", err.Error()) } - err = e.checkHealthLogs(ctx, req.PluginContext) + err = e.checkHealthLogs(req.PluginContext) if err != nil { status = backend.HealthStatusError logsTest = fmt.Sprintf("CloudWatch logs query failed: %s", err.Error()) diff --git a/pkg/tsdb/cloudwatch/cloudwatch_test.go b/pkg/tsdb/cloudwatch/cloudwatch_test.go index 12a940ef826..df2d0d11dce 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_test.go @@ -4,11 +4,11 @@ import ( "context" "encoding/json" "fmt" + "net/http" "testing" "time" "github.com/aws/aws-sdk-go/aws" - awsrequest "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" @@ -124,8 +124,7 @@ func Test_CheckHealth(t *testing.T) { t.Run("successfully queries metrics, fails during logs query", func(t *testing.T) { client = fakeCheckHealthClient{ - describeLogGroupsWithContext: func(ctx aws.Context, input *cloudwatchlogs.DescribeLogGroupsInput, - options ...awsrequest.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + describeLogGroups: func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { return nil, fmt.Errorf("some logs query error") }} im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { @@ -248,3 +247,140 @@ func Test_executeLogAlertQuery(t *testing.T) { assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions) }) } + +func TestQuery_ResourceRequest_DescribeLogGroups(t *testing.T) { + origNewCWLogsClient := NewCWLogsClient + t.Cleanup(func() { + NewCWLogsClient = origNewCWLogsClient + }) + + var cli fakeCWLogsClient + + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + return &cli + } + + im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return datasourceInfo{}, nil + }) + + executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) + sender := &mockedCallResourceResponseSenderForOauth{} + + t.Run("Should map log groups to SuggestData response", func(t *testing.T) { + cli = fakeCWLogsClient{ + logGroups: []cloudwatchlogs.DescribeLogGroupsOutput{ + {LogGroups: []*cloudwatchlogs.LogGroup{ + { + LogGroupName: aws.String("group_a"), + }, + { + LogGroupName: aws.String("group_b"), + }, + { + LogGroupName: aws.String("group_c"), + }, + }}, + }, + } + + req := &backend.CallResourceRequest{ + Method: "GET", + Path: "/log-groups", + PluginContext: backend.PluginContext{ + DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{ + ID: 0, + }, + PluginID: "cloudwatch", + }, + } + err := executor.CallResource(context.Background(), req, sender) + + require.NoError(t, err) + sent := sender.Response + require.NotNil(t, sent) + require.Equal(t, http.StatusOK, sent.Status) + + suggestDataResponse := []suggestData{} + err = json.Unmarshal(sent.Body, &suggestDataResponse) + require.Nil(t, err) + + assert.Equal(t, []suggestData{ + {Text: "group_a", Value: "group_a", Label: "group_a"}, {Text: "group_b", Value: "group_b", Label: "group_b"}, {Text: "group_c", Value: "group_c", Label: "group_c"}}, suggestDataResponse) + }) + + t.Run("Should call api with LogGroupNamePrefix if passed in resource call", func(t *testing.T) { + cli = fakeCWLogsClient{ + logGroups: []cloudwatchlogs.DescribeLogGroupsOutput{ + {LogGroups: []*cloudwatchlogs.LogGroup{}}, + }, + } + + im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return datasourceInfo{}, nil + }) + + executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) + + req := &backend.CallResourceRequest{ + Method: "GET", + Path: "/log-groups?logGroupNamePrefix=test", + PluginContext: backend.PluginContext{ + DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{ + ID: 0, + }, + PluginID: "cloudwatch", + }, + } + err := executor.CallResource(context.Background(), req, sender) + + require.NoError(t, err) + sent := sender.Response + require.NotNil(t, sent) + require.Equal(t, http.StatusOK, sent.Status) + + assert.Equal(t, []*cloudwatchlogs.DescribeLogGroupsInput{ + { + Limit: aws.Int64(defaultLogGroupLimit), + LogGroupNamePrefix: aws.String("test"), + }, + }, cli.calls.describeLogGroups) + }) + + t.Run("Should call api without LogGroupNamePrefix if not passed in resource call", func(t *testing.T) { + cli = fakeCWLogsClient{ + logGroups: []cloudwatchlogs.DescribeLogGroupsOutput{ + {LogGroups: []*cloudwatchlogs.LogGroup{}}, + }, + } + + im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return datasourceInfo{}, nil + }) + + executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) + + req := &backend.CallResourceRequest{ + Method: "GET", + Path: "/log-groups?limit=100", + PluginContext: backend.PluginContext{ + DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{ + ID: 0, + }, + PluginID: "cloudwatch", + }, + } + err := executor.CallResource(context.Background(), req, sender) + + require.NoError(t, err) + sent := sender.Response + require.NotNil(t, sent) + require.Equal(t, http.StatusOK, sent.Status) + + assert.Equal(t, []*cloudwatchlogs.DescribeLogGroupsInput{ + { + Limit: aws.Int64(100), + }, + }, cli.calls.describeLogGroups) + }) +} diff --git a/pkg/tsdb/cloudwatch/log_actions.go b/pkg/tsdb/cloudwatch/log_actions.go index 036fd4ff885..b90cad873cc 100644 --- a/pkg/tsdb/cloudwatch/log_actions.go +++ b/pkg/tsdb/cloudwatch/log_actions.go @@ -126,8 +126,6 @@ func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, model LogQuer var data *data.Frame = nil switch model.SubType { - case "DescribeLogGroups": - data, err = e.handleDescribeLogGroups(ctx, logsClient, model) case "DescribeAllLogGroups": data, err = e.handleDescribeAllLogGroups(ctx, logsClient, model) case "GetLogGroupFields": @@ -203,40 +201,6 @@ func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient return data.NewFrame("logEvents", timestampField, messageField), nil } -func (e *cloudWatchExecutor) handleDescribeLogGroups(ctx context.Context, - logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters LogQueryJson) (*data.Frame, error) { - logGroupLimit := defaultLogGroupLimit - if parameters.Limit != nil && *parameters.Limit != 0 { - logGroupLimit = *parameters.Limit - } - - var response *cloudwatchlogs.DescribeLogGroupsOutput = nil - var err error - if len(parameters.LogGroupNamePrefix) == 0 { - response, err = logsClient.DescribeLogGroupsWithContext(ctx, &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(logGroupLimit), - }) - } else { - response, err = logsClient.DescribeLogGroupsWithContext(ctx, &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(logGroupLimit), - LogGroupNamePrefix: aws.String(parameters.LogGroupNamePrefix), - }) - } - if err != nil || response == nil { - return nil, err - } - - logGroupNames := make([]*string, 0) - for _, logGroup := range response.LogGroups { - logGroupNames = append(logGroupNames, logGroup.LogGroupName) - } - - groupNamesField := data.NewField("logGroupName", nil, logGroupNames) - frame := data.NewFrame("logGroups", groupNamesField) - - return frame, nil -} - func (e *cloudWatchExecutor) handleDescribeAllLogGroups(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters LogQueryJson) (*data.Frame, error) { var namePrefix, nextToken *string if len(parameters.LogGroupNamePrefix) != 0 { diff --git a/pkg/tsdb/cloudwatch/log_actions_test.go b/pkg/tsdb/cloudwatch/log_actions_test.go index c621e4fd74b..94c3c158bcc 100644 --- a/pkg/tsdb/cloudwatch/log_actions_test.go +++ b/pkg/tsdb/cloudwatch/log_actions_test.go @@ -106,133 +106,6 @@ func TestQuery_GetLogEvents(t *testing.T) { } } -func TestQuery_DescribeLogGroups(t *testing.T) { - origNewCWLogsClient := NewCWLogsClient - t.Cleanup(func() { - NewCWLogsClient = origNewCWLogsClient - }) - - var cli fakeCWLogsClient - - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { - return &cli - } - - t.Run("Empty log group name prefix", func(t *testing.T) { - cli = fakeCWLogsClient{ - logGroups: []cloudwatchlogs.DescribeLogGroupsOutput{ - {LogGroups: []*cloudwatchlogs.LogGroup{ - { - LogGroupName: aws.String("group_a"), - }, - { - LogGroupName: aws.String("group_b"), - }, - { - LogGroupName: aws.String("group_c"), - }, - }}, - }, - } - - im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return datasourceInfo{}, nil - }) - - executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) - resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ - PluginContext: backend.PluginContext{ - DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, - }, - Queries: []backend.DataQuery{ - { - JSON: json.RawMessage(`{ - "type": "logAction", - "subtype": "DescribeLogGroups", - "limit": 50 - }`), - }, - }, - }) - require.NoError(t, err) - require.NotNil(t, resp) - - assert.Equal(t, &backend.QueryDataResponse{Responses: backend.Responses{ - "": backend.DataResponse{ - Frames: data.Frames{ - &data.Frame{ - Name: "logGroups", - Fields: []*data.Field{ - data.NewField("logGroupName", nil, []*string{ - aws.String("group_a"), aws.String("group_b"), aws.String("group_c"), - }), - }, - }, - }, - }, - }, - }, resp) - }) - - t.Run("Non-empty log group name prefix", func(t *testing.T) { - cli = fakeCWLogsClient{ - logGroups: []cloudwatchlogs.DescribeLogGroupsOutput{ - {LogGroups: []*cloudwatchlogs.LogGroup{ - { - LogGroupName: aws.String("group_a"), - }, - { - LogGroupName: aws.String("group_b"), - }, - { - LogGroupName: aws.String("group_c"), - }, - }}, - }, - } - - im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return datasourceInfo{}, nil - }) - - executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) - resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ - PluginContext: backend.PluginContext{ - DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, - }, - Queries: []backend.DataQuery{ - { - JSON: json.RawMessage(`{ - "type": "logAction", - "subtype": "DescribeLogGroups", - "limit": 50, - "region": "default", - "logGroupNamePrefix": "g" - }`), - }, - }, - }) - require.NoError(t, err) - require.NotNil(t, resp) - - assert.Equal(t, &backend.QueryDataResponse{Responses: backend.Responses{ - "": backend.DataResponse{ - Frames: data.Frames{ - &data.Frame{ - Name: "logGroups", - Fields: []*data.Field{ - data.NewField("logGroupName", nil, []*string{ - aws.String("group_a"), aws.String("group_b"), aws.String("group_c"), - }), - }, - }, - }, - }, - }, - }, resp) - }) -} - func TestQuery_DescribeAllLogGroups(t *testing.T) { origNewCWLogsClient := NewCWLogsClient t.Cleanup(func() { diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index 640ce475330..cb047b89dde 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -7,6 +7,7 @@ import ( "net/url" "reflect" "sort" + "strconv" "strings" "sync" "time" @@ -14,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -649,6 +651,41 @@ func (e *cloudWatchExecutor) getDimensionsForCustomMetrics(region, namespace str return customMetricsDimensionsMap[dsInfo.profile][dsInfo.region][namespace].Cache, nil } +func (e *cloudWatchExecutor) handleGetLogGroups(pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) { + region := parameters.Get("region") + limit := parameters.Get("limit") + logGroupNamePrefix := parameters.Get("logGroupNamePrefix") + + logsClient, err := e.getCWLogsClient(pluginCtx, region) + if err != nil { + return nil, err + } + + logGroupLimit := defaultLogGroupLimit + intLimit, err := strconv.ParseInt(limit, 10, 64) + if err == nil && intLimit > 0 { + logGroupLimit = intLimit + } + + var response *cloudwatchlogs.DescribeLogGroupsOutput = nil + input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(logGroupLimit)} + if len(logGroupNamePrefix) > 0 { + input.LogGroupNamePrefix = aws.String(logGroupNamePrefix) + } + response, err = logsClient.DescribeLogGroups(input) + if err != nil || response == nil { + return nil, err + } + + result := make([]suggestData, 0) + for _, logGroup := range response.LogGroups { + logGroupName := *logGroup.LogGroupName + result = append(result, suggestData{Text: logGroupName, Value: logGroupName, Label: logGroupName}) + } + + return result, nil +} + func isDuplicate(nameList []string, target string) bool { for _, name := range nameList { if name == target { diff --git a/pkg/tsdb/cloudwatch/resource_handler.go b/pkg/tsdb/cloudwatch/resource_handler.go index c2f76249748..e559d7c86f6 100644 --- a/pkg/tsdb/cloudwatch/resource_handler.go +++ b/pkg/tsdb/cloudwatch/resource_handler.go @@ -21,6 +21,7 @@ func (e *cloudWatchExecutor) newResourceMux() *http.ServeMux { mux.HandleFunc("/ebs-volume-ids", handleResourceReq(e.handleGetEbsVolumeIds)) mux.HandleFunc("/ec2-instance-attribute", handleResourceReq(e.handleGetEc2InstanceAttribute)) mux.HandleFunc("/resource-arns", handleResourceReq(e.handleGetResourceArns)) + mux.HandleFunc("/log-groups", handleResourceReq(e.handleGetLogGroups)) return mux } diff --git a/pkg/tsdb/cloudwatch/utils_test.go b/pkg/tsdb/cloudwatch/utils_test.go index 97a9ccc4283..73094be8d10 100644 --- a/pkg/tsdb/cloudwatch/utils_test.go +++ b/pkg/tsdb/cloudwatch/utils_test.go @@ -15,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/setting" ) @@ -33,6 +34,7 @@ type fakeCWLogsClient struct { type logsQueryCalls struct { startQueryWithContext []*cloudwatchlogs.StartQueryInput getEventsWithContext []*cloudwatchlogs.GetLogEventsInput + describeLogGroups []*cloudwatchlogs.DescribeLogGroupsInput } func (m *fakeCWLogsClient) GetQueryResultsWithContext(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, option ...request.Option) (*cloudwatchlogs.GetQueryResultsOutput, error) { @@ -53,6 +55,13 @@ func (m *fakeCWLogsClient) StopQueryWithContext(ctx context.Context, input *clou }, nil } +func (m *fakeCWLogsClient) DescribeLogGroups(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + m.calls.describeLogGroups = append(m.calls.describeLogGroups, input) + output := &m.logGroups[m.logGroupsIndex] + m.logGroupsIndex++ + return output, nil +} + func (m *fakeCWLogsClient) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { output := &m.logGroups[m.logGroupsIndex] m.logGroupsIndex++ @@ -192,9 +201,8 @@ type fakeCheckHealthClient struct { cloudwatchiface.CloudWatchAPI cloudwatchlogsiface.CloudWatchLogsAPI - listMetricsPages func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error - describeLogGroupsWithContext func(ctx aws.Context, input *cloudwatchlogs.DescribeLogGroupsInput, - options ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) + listMetricsPages func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error + describeLogGroups func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) } func (c fakeCheckHealthClient) ListMetricsPages(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error { @@ -204,9 +212,9 @@ func (c fakeCheckHealthClient) ListMetricsPages(input *cloudwatch.ListMetricsInp return nil } -func (c fakeCheckHealthClient) DescribeLogGroupsWithContext(ctx aws.Context, input *cloudwatchlogs.DescribeLogGroupsInput, options ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - if c.describeLogGroupsWithContext != nil { - return c.describeLogGroupsWithContext(ctx, input, options...) +func (c fakeCheckHealthClient) DescribeLogGroups(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + if c.describeLogGroups != nil { + return c.describeLogGroups(input) } return nil, nil } @@ -247,3 +255,12 @@ func (s *fakeSessionCache) GetSession(c awsds.SessionConfig) (*session.Session, Config: &aws.Config{}, }, nil } + +type mockedCallResourceResponseSenderForOauth struct { + Response *backend.CallResourceResponse +} + +func (s *mockedCallResourceResponseSenderForOauth) Send(resp *backend.CallResourceResponse) error { + s.Response = resp + return nil +} diff --git a/public/app/plugins/datasource/cloudwatch/__mocks__/API.ts b/public/app/plugins/datasource/cloudwatch/__mocks__/API.ts new file mode 100644 index 00000000000..2079094ad8c --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/__mocks__/API.ts @@ -0,0 +1,29 @@ +import { getBackendSrv, setBackendSrv } from '@grafana/runtime'; +import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; +import { TemplateSrv } from 'app/features/templating/template_srv'; +import { CustomVariableModel } from 'app/features/variables/types'; + +import { CloudWatchAPI } from '../api'; + +import { CloudWatchSettings, setupMockedTemplateService } from './CloudWatchDataSource'; + +export function setupMockedAPI({ + variables, + response, +}: { + response?: Array<{ text: string; label: string; value: string }>; + variables?: CustomVariableModel[]; + mockGetVariableName?: boolean; +} = {}) { + let templateService = variables ? setupMockedTemplateService(variables) : new TemplateSrv(); + + const timeSrv = getTimeSrv(); + const api = new CloudWatchAPI(CloudWatchSettings, templateService); + const resourceRequestMock = jest.fn().mockReturnValue(response); + setBackendSrv({ + ...getBackendSrv(), + get: resourceRequestMock, + }); + + return { api, resourceRequestMock, templateService, timeSrv }; +} diff --git a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts index b655946ff61..fe3fbe1f94b 100644 --- a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts +++ b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts @@ -76,7 +76,7 @@ export function setupMockedDataSource({ const timeSrv = getTimeSrv(); const datasource = new CloudWatchDatasource(CloudWatchSettings, templateService, timeSrv); datasource.getVariables = () => ['test']; - + datasource.api.describeLogGroups = jest.fn().mockResolvedValue([]); datasource.api.getNamespaces = jest.fn().mockResolvedValue([]); datasource.api.getRegions = jest.fn().mockResolvedValue([]); datasource.logsQueryRunner.defaultLogGroups = []; @@ -84,6 +84,7 @@ export function setupMockedDataSource({ setBackendSrv({ ...getBackendSrv(), fetch: fetchMock, + get: jest.fn(), }); return { datasource, fetchMock, templateService, timeSrv }; diff --git a/public/app/plugins/datasource/cloudwatch/api.test.ts b/public/app/plugins/datasource/cloudwatch/api.test.ts new file mode 100644 index 00000000000..146f11a3a55 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/api.test.ts @@ -0,0 +1,55 @@ +import { setupMockedAPI } from './__mocks__/API'; + +describe('describeLogGroup', () => { + it('replaces region correctly in the query', async () => { + const { api, resourceRequestMock } = setupMockedAPI(); + await api.describeLogGroups({ region: 'default' }); + expect(resourceRequestMock.mock.calls[0][1].region).toBe('us-west-1'); + + await api.describeLogGroups({ region: 'eu-east' }); + expect(resourceRequestMock.mock.calls[1][1].region).toBe('eu-east'); + }); + + it('should return log groups as an array of options', async () => { + const response = [ + { + text: '/aws/containerinsights/dev303-workshop/application', + value: '/aws/containerinsights/dev303-workshop/application', + label: '/aws/containerinsights/dev303-workshop/application', + }, + { + text: '/aws/containerinsights/dev303-workshop/flowlogs', + value: '/aws/containerinsights/dev303-workshop/flowlogs', + label: '/aws/containerinsights/dev303-workshop/flowlogs', + }, + { + text: '/aws/containerinsights/dev303-workshop/dataplane', + value: '/aws/containerinsights/dev303-workshop/dataplane', + label: '/aws/containerinsights/dev303-workshop/dataplane', + }, + ]; + + const { api } = setupMockedAPI({ response }); + const expectedLogGroups = [ + { + text: '/aws/containerinsights/dev303-workshop/application', + value: '/aws/containerinsights/dev303-workshop/application', + label: '/aws/containerinsights/dev303-workshop/application', + }, + { + text: '/aws/containerinsights/dev303-workshop/flowlogs', + value: '/aws/containerinsights/dev303-workshop/flowlogs', + label: '/aws/containerinsights/dev303-workshop/flowlogs', + }, + { + text: '/aws/containerinsights/dev303-workshop/dataplane', + value: '/aws/containerinsights/dev303-workshop/dataplane', + label: '/aws/containerinsights/dev303-workshop/dataplane', + }, + ]; + + const logGroups = await api.describeLogGroups({ region: 'default' }); + + expect(logGroups).toEqual(expectedLogGroups); + }); +}); diff --git a/public/app/plugins/datasource/cloudwatch/api.ts b/public/app/plugins/datasource/cloudwatch/api.ts index 7fbccb8a6c0..ed6c9a5887f 100644 --- a/public/app/plugins/datasource/cloudwatch/api.ts +++ b/public/app/plugins/datasource/cloudwatch/api.ts @@ -1,25 +1,30 @@ -import { DataSourceInstanceSettings } from '@grafana/data'; -import { getBackendSrv, BackendSrv } from '@grafana/runtime'; +import { DataSourceInstanceSettings, SelectableValue } from '@grafana/data'; +import { getBackendSrv } from '@grafana/runtime'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { CloudWatchRequest } from './query-runner/CloudWatchRequest'; -import { CloudWatchJsonData, Dimensions } from './types'; +import { CloudWatchJsonData, DescribeLogGroupsRequest, Dimensions, MultiFilters } from './types'; + +export interface SelectableResourceValue extends SelectableValue { + text: string; +} export class CloudWatchAPI extends CloudWatchRequest { - private backendSrv: BackendSrv; constructor(instanceSettings: DataSourceInstanceSettings, templateSrv: TemplateSrv) { super(instanceSettings, templateSrv); - this.backendSrv = getBackendSrv(); } - resourceRequest(subtype: string, parameters?: any): Promise> { - return this.backendSrv.get(`/api/datasources/${this.instanceSettings.id}/resources/${subtype}`, parameters); + resourceRequest( + subtype: string, + parameters?: Record + ): Promise { + return getBackendSrv().get(`/api/datasources/${this.instanceSettings.id}/resources/${subtype}`, parameters); } - getRegions(): Promise> { - return this.resourceRequest('regions').then((regions: any) => [ + getRegions() { + return this.resourceRequest('regions').then((regions) => [ { label: 'default', value: 'default', text: 'default' }, - ...regions, + ...regions.filter((r) => r.value), ]); } @@ -27,6 +32,13 @@ export class CloudWatchAPI extends CloudWatchRequest { return this.resourceRequest('namespaces'); } + async describeLogGroups(params: DescribeLogGroupsRequest) { + return this.resourceRequest('log-groups', { + ...params, + region: this.templateSrv.replace(this.getActualRegion(params.region)), + }); + } + async getMetrics(namespace: string | undefined, region?: string) { if (!namespace) { return []; @@ -38,7 +50,7 @@ export class CloudWatchAPI extends CloudWatchRequest { }); } - async getAllMetrics(region: string): Promise> { + async getAllMetrics(region: string): Promise> { const values = await this.resourceRequest('all-metrics', { region: this.templateSrv.replace(this.getActualRegion(region)), }); @@ -93,7 +105,7 @@ export class CloudWatchAPI extends CloudWatchRequest { }); } - getEc2InstanceAttribute(region: string, attributeName: string, filters: any) { + getEc2InstanceAttribute(region: string, attributeName: string, filters: MultiFilters) { return this.resourceRequest('ec2-instance-attribute', { region: this.templateSrv.replace(this.getActualRegion(region)), attributeName: this.templateSrv.replace(attributeName), @@ -101,7 +113,7 @@ export class CloudWatchAPI extends CloudWatchRequest { }); } - getResourceARNs(region: string, resourceType: string, tags: any) { + getResourceARNs(region: string, resourceType: string, tags: MultiFilters) { return this.resourceRequest('resource-arns', { region: this.templateSrv.replace(this.getActualRegion(region)), resourceType: this.templateSrv.replace(resourceType), diff --git a/public/app/plugins/datasource/cloudwatch/cloudwatch-sql/completion/CompletionItemProvider.ts b/public/app/plugins/datasource/cloudwatch/cloudwatch-sql/completion/CompletionItemProvider.ts index ea31e411c8f..80948f92068 100644 --- a/public/app/plugins/datasource/cloudwatch/cloudwatch-sql/completion/CompletionItemProvider.ts +++ b/public/app/plugins/datasource/cloudwatch/cloudwatch-sql/completion/CompletionItemProvider.ts @@ -116,11 +116,11 @@ export class SQLCompletionItemProvider extends CompletionItemProvider { this.templateSrv.replace(namespaceToken?.value.replace(/\"/g, '')), this.templateSrv.replace(this.region) ); - metrics.map((m) => addSuggestion(m.value)); + metrics.forEach((m) => m.value && addSuggestion(m.value)); } else { // If no namespace is specified in the query, just list all metrics const metrics = await this.api.getAllMetrics(this.templateSrv.replace(this.region)); - uniq(metrics.map((m) => m.metricName)).map((m) => addSuggestion(m, { insertText: m })); + uniq(metrics.map((m) => m.metricName)).forEach((m) => m && addSuggestion(m, { insertText: m })); } } break; @@ -186,8 +186,8 @@ export class SQLCompletionItemProvider extends CompletionItemProvider { metricNameToken?.value ?? '' ); keys.map((m) => { - const key = /[\s\.-]/.test(m.value) ? `"${m.value}"` : m.value; - addSuggestion(key); + const key = /[\s\.-]/.test(m.value ?? '') ? `"${m.value}"` : m.value; + key && addSuggestion(key); }); } } diff --git a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx index 41a6aa80ed2..9ec7d683a9d 100644 --- a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx @@ -4,6 +4,7 @@ import React from 'react'; import selectEvent from 'react-select-event'; import { AwsAuthType } from '@grafana/aws-sdk'; +import { toOption } from '@grafana/data'; import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource'; @@ -13,6 +14,7 @@ jest.mock('app/features/plugins/datasource_srv', () => ({ getDatasourceSrv: () => ({ loadDatasource: jest.fn().mockResolvedValue({ api: { + describeLogGroups: jest.fn().mockResolvedValue(['logGroup-foo', 'logGroup-bar'].map(toOption)), getRegions: jest.fn().mockResolvedValue([ { label: 'ap-east-1', @@ -22,9 +24,6 @@ jest.mock('app/features/plugins/datasource_srv', () => ({ }, getActualRegion: jest.fn().mockReturnValue('ap-east-1'), getVariables: jest.fn().mockReturnValue([]), - logsQueryRunner: { - describeLogGroups: jest.fn().mockResolvedValue(['logGroup-foo', 'logGroup-bar']), - }, }), }), })); diff --git a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx index 113fb1f99d6..e1ac9201997 100644 --- a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx @@ -16,6 +16,7 @@ import { createWarningNotification } from 'app/core/copy/appNotification'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { store } from 'app/store/store'; +import { SelectableResourceValue } from '../api'; import { CloudWatchDatasource } from '../datasource'; import { CloudWatchJsonData, CloudWatchSecureJsonData } from '../types'; @@ -63,7 +64,16 @@ export const ConfigEditor: FC = (props: Props) => { {...props} loadRegions={ datasource && - (() => datasource.api.getRegions().then((r) => r.filter((r) => r.value !== 'default').map((v) => v.value))) + (async () => { + return datasource.api + .getRegions() + .then((regions) => + regions.reduce( + (acc: string[], curr: SelectableResourceValue) => (curr.value ? [...acc, curr.value] : acc), + [] + ) + ); + }) } > diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.test.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.test.tsx index 84ba55f7116..4f2acd8f0be 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.test.tsx @@ -4,6 +4,8 @@ import lodash from 'lodash'; // eslint-disable-line lodash/import-scope import React from 'react'; import { openMenu, select } from 'react-select-event'; +import { toOption } from '@grafana/data'; + import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource'; import { DescribeLogGroupsRequest } from '../types'; @@ -25,15 +27,13 @@ describe('LogGroupSelector', () => { }); it('updates upstream query log groups on region change', async () => { - ds.datasource.logsQueryRunner.describeLogGroups = jest - .fn() - .mockImplementation(async (params: DescribeLogGroupsRequest) => { - if (params.region === 'region1') { - return Promise.resolve(['log_group_1']); - } else { - return Promise.resolve(['log_group_2']); - } - }); + ds.datasource.api.describeLogGroups = jest.fn().mockImplementation(async (params: DescribeLogGroupsRequest) => { + if (params.region === 'region1') { + return Promise.resolve(['log_group_1'].map(toOption)); + } else { + return Promise.resolve(['log_group_2'].map(toOption)); + } + }); const props = { ...defaultProps, selectedLogGroups: ['log_group_1'], @@ -50,15 +50,13 @@ describe('LogGroupSelector', () => { }); it('does not update upstream query log groups if saved is false', async () => { - ds.datasource.logsQueryRunner.describeLogGroups = jest - .fn() - .mockImplementation(async (params: DescribeLogGroupsRequest) => { - if (params.region === 'region1') { - return Promise.resolve(['log_group_1']); - } else { - return Promise.resolve(['log_group_2']); - } - }); + ds.datasource.api.describeLogGroups = jest.fn().mockImplementation(async (params: DescribeLogGroupsRequest) => { + if (params.region === 'region1') { + return Promise.resolve(['log_group_1'].map(toOption)); + } else { + return Promise.resolve(['log_group_2'].map(toOption)); + } + }); const props = { ...defaultProps, selectedLogGroups: ['log_group_1'], @@ -98,14 +96,12 @@ describe('LogGroupSelector', () => { ]; const testLimit = 10; - ds.datasource.logsQueryRunner.describeLogGroups = jest - .fn() - .mockImplementation(async (params: DescribeLogGroupsRequest) => { - const theLogGroups = allLogGroups - .filter((logGroupName) => logGroupName.startsWith(params.logGroupNamePrefix ?? '')) - .slice(0, Math.max(params.limit ?? testLimit, testLimit)); - return Promise.resolve(theLogGroups); - }); + ds.datasource.api.describeLogGroups = jest.fn().mockImplementation(async (params: DescribeLogGroupsRequest) => { + const theLogGroups = allLogGroups + .filter((logGroupName) => logGroupName.startsWith(params.logGroupNamePrefix ?? '')) + .slice(0, Math.max(params.limit ?? testLimit, testLimit)); + return Promise.resolve(theLogGroups.map(toOption)); + }); const props = { ...defaultProps, }; @@ -129,7 +125,7 @@ describe('LogGroupSelector', () => { it('should render template variables a selectable option', async () => { lodash.debounce = jest.fn().mockImplementation((fn) => fn); - ds.datasource.logsQueryRunner.describeLogGroups = jest.fn().mockResolvedValue([]); + ds.datasource.api.describeLogGroups = jest.fn().mockResolvedValue([]); const onChange = jest.fn(); const props = { ...defaultProps, diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx index 75086175582..5d3a5df1d2e 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogGroupSelector.tsx @@ -52,12 +52,12 @@ export const LogGroupSelector: React.FC = ({ return []; } try { - const logGroups: string[] = await datasource.logsQueryRunner.describeLogGroups({ + const logGroups = await datasource.api.describeLogGroups({ refId, region, logGroupNamePrefix, }); - return logGroups.map(toOption); + return logGroups; } catch (err) { dispatch(notifyApp(createErrorNotification(typeof err === 'string' ? err : JSON.stringify(err)))); return []; @@ -98,7 +98,7 @@ export const LogGroupSelector: React.FC = ({ } setLoadingLogGroups(true); - return fetchLogGroupOptions(datasource.getActualRegion(region) ?? '') + return fetchLogGroupOptions(datasource.getActualRegion(region)) .then((logGroups) => { const newSelectedLogGroups = intersection( selectedLogGroups, diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 7102c147dfa..216784e3807 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -48,7 +48,6 @@ export class CloudWatchDatasource private metricsQueryRunner: CloudWatchMetricsQueryRunner; private annotationQueryRunner: CloudWatchAnnotationQueryRunner; - // this member should be private too, but we need to fix https://github.com/grafana/grafana/issues/55243 to enable that logsQueryRunner: CloudWatchLogsQueryRunner; api: CloudWatchAPI; @@ -170,7 +169,7 @@ export class CloudWatchDatasource getActualRegion(region?: string) { if (region === 'default' || region === undefined || region === '') { - return this.defaultRegion; + return this.defaultRegion ?? ''; } return region; } diff --git a/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.test.ts b/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.test.ts index 2ed86f39d91..3d9d3fc4ecf 100644 --- a/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.test.ts +++ b/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.test.ts @@ -1,7 +1,6 @@ import { interval, lastValueFrom, of } from 'rxjs'; import { LogRowModel, MutableDataFrame, FieldType, LogLevel, dataFrameToJSON, DataQueryErrorType } from '@grafana/data'; -import { BackendDataSourceResponse } from '@grafana/runtime'; import { genMockFrames, setupMockedLogsQueryRunner } from '../__mocks__/LogsQueryRunner'; import { validLogsQuery } from '../__mocks__/queries'; @@ -14,114 +13,6 @@ describe('CloudWatchLogsQueryRunner', () => { beforeEach(() => { jest.clearAllMocks(); }); - describe('describeLogGroup', () => { - it('replaces region correctly in the query', async () => { - const { runner, fetchMock } = setupMockedLogsQueryRunner(); - await runner.describeLogGroups({ region: 'default' }); - expect(fetchMock.mock.calls[0][0].data.queries[0].region).toBe('us-west-1'); - - await runner.describeLogGroups({ region: 'eu-east' }); - expect(fetchMock.mock.calls[1][0].data.queries[0].region).toBe('eu-east'); - }); - - it('should return log groups as an array of strings', async () => { - const data: BackendDataSourceResponse = { - results: { - A: { - frames: [ - { - schema: { - name: 'logGroups', - refId: 'A', - fields: [{ name: 'logGroupName', type: FieldType.string }], - }, - data: { - values: [ - [ - '/aws/containerinsights/dev303-workshop/application', - '/aws/containerinsights/dev303-workshop/dataplane', - '/aws/containerinsights/dev303-workshop/flowlogs', - '/aws/containerinsights/dev303-workshop/host', - '/aws/containerinsights/dev303-workshop/performance', - '/aws/containerinsights/dev303-workshop/prometheus', - '/aws/containerinsights/ecommerce-sockshop/application', - '/aws/containerinsights/ecommerce-sockshop/dataplane', - '/aws/containerinsights/ecommerce-sockshop/host', - '/aws/containerinsights/ecommerce-sockshop/performance', - '/aws/containerinsights/watchdemo-perf/application', - '/aws/containerinsights/watchdemo-perf/dataplane', - '/aws/containerinsights/watchdemo-perf/host', - '/aws/containerinsights/watchdemo-perf/performance', - '/aws/containerinsights/watchdemo-perf/prometheus', - '/aws/containerinsights/watchdemo-prod-us-east-1/performance', - '/aws/containerinsights/watchdemo-staging/application', - '/aws/containerinsights/watchdemo-staging/dataplane', - '/aws/containerinsights/watchdemo-staging/host', - '/aws/containerinsights/watchdemo-staging/performance', - '/aws/ecs/containerinsights/bugbash-ec2/performance', - '/aws/ecs/containerinsights/ecs-demoworkshop/performance', - '/aws/ecs/containerinsights/ecs-workshop-dev/performance', - '/aws/eks/dev303-workshop/cluster', - '/aws/events/cloudtrail', - '/aws/events/ecs', - '/aws/lambda/cwsyn-mycanary-fac97ded-f134-499a-9d71-4c3be1f63182', - '/aws/lambda/cwsyn-watch-linkchecks-ef7ef273-5da2-4663-af54-d2f52d55b060', - '/ecs/ecs-cwagent-daemon-service', - '/ecs/ecs-demo-limitTask', - 'CloudTrail/DefaultLogGroup', - 'container-insights-prometheus-beta', - 'container-insights-prometheus-demo', - ], - ], - }, - }, - ], - }, - }, - }; - - const { runner } = setupMockedLogsQueryRunner({ data }); - const expectedLogGroups = [ - '/aws/containerinsights/dev303-workshop/application', - '/aws/containerinsights/dev303-workshop/dataplane', - '/aws/containerinsights/dev303-workshop/flowlogs', - '/aws/containerinsights/dev303-workshop/host', - '/aws/containerinsights/dev303-workshop/performance', - '/aws/containerinsights/dev303-workshop/prometheus', - '/aws/containerinsights/ecommerce-sockshop/application', - '/aws/containerinsights/ecommerce-sockshop/dataplane', - '/aws/containerinsights/ecommerce-sockshop/host', - '/aws/containerinsights/ecommerce-sockshop/performance', - '/aws/containerinsights/watchdemo-perf/application', - '/aws/containerinsights/watchdemo-perf/dataplane', - '/aws/containerinsights/watchdemo-perf/host', - '/aws/containerinsights/watchdemo-perf/performance', - '/aws/containerinsights/watchdemo-perf/prometheus', - '/aws/containerinsights/watchdemo-prod-us-east-1/performance', - '/aws/containerinsights/watchdemo-staging/application', - '/aws/containerinsights/watchdemo-staging/dataplane', - '/aws/containerinsights/watchdemo-staging/host', - '/aws/containerinsights/watchdemo-staging/performance', - '/aws/ecs/containerinsights/bugbash-ec2/performance', - '/aws/ecs/containerinsights/ecs-demoworkshop/performance', - '/aws/ecs/containerinsights/ecs-workshop-dev/performance', - '/aws/eks/dev303-workshop/cluster', - '/aws/events/cloudtrail', - '/aws/events/ecs', - '/aws/lambda/cwsyn-mycanary-fac97ded-f134-499a-9d71-4c3be1f63182', - '/aws/lambda/cwsyn-watch-linkchecks-ef7ef273-5da2-4663-af54-d2f52d55b060', - '/ecs/ecs-cwagent-daemon-service', - '/ecs/ecs-demo-limitTask', - 'CloudTrail/DefaultLogGroup', - 'container-insights-prometheus-beta', - 'container-insights-prometheus-demo', - ]; - - const logGroups = await runner.describeLogGroups({ region: 'default' }); - - expect(logGroups).toEqual(expectedLogGroups); - }); - }); describe('getLogRowContext', () => { it('replaces parameters correctly in the query', async () => { diff --git a/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.ts b/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.ts index 40a6df15278..c3a47814766 100644 --- a/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.ts +++ b/public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.ts @@ -425,13 +425,6 @@ export class CloudWatchLogsQueryRunner extends CloudWatchRequest { }; }; - async describeLogGroups(params: DescribeLogGroupsRequest): Promise { - const dataFrames = await lastValueFrom(this.makeLogActionRequest('DescribeLogGroups', [params])); - - const logGroupNames = dataFrames[0]?.fields[0]?.values.toArray() ?? []; - return logGroupNames; - } - async describeAllLogGroups(params: DescribeLogGroupsRequest): Promise { const dataFrames = await lastValueFrom(this.makeLogActionRequest('DescribeAllLogGroups', [params])); diff --git a/public/app/plugins/datasource/cloudwatch/variables.ts b/public/app/plugins/datasource/cloudwatch/variables.ts index 39ff5e02b3e..8d33179d47a 100644 --- a/public/app/plugins/datasource/cloudwatch/variables.ts +++ b/public/app/plugins/datasource/cloudwatch/variables.ts @@ -68,7 +68,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return regions.map((s) => ({ text: s.label, value: s.value, expandable: true, @@ -77,7 +77,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return namespaces.map((s) => ({ text: s.label, value: s.value, expandable: true, @@ -86,7 +86,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return metrics.map((s) => ({ text: s.label, value: s.value, expandable: true, @@ -95,7 +95,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return keys.map((s) => ({ text: s.label, value: s.value, expandable: true, @@ -107,7 +107,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return keys.map((s) => ({ text: s.label, value: s.value, expandable: true, @@ -119,7 +119,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return ids.map((s) => ({ text: s.label, value: s.value, expandable: true, @@ -131,7 +131,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return values.map((s) => ({ text: s.label, value: s.value, expandable: true, @@ -143,7 +143,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ + return keys.map((s) => ({ text: s.label, value: s.value, expandable: true,