From 8c826cd78550252cc53ccfc96050e0484de9a819 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 17 Jan 2023 20:27:53 +0100 Subject: [PATCH] Cloudwatch: Cleanup resource api (#61465) Co-authored-by: Isabella Siu --- .../__mocks__/CloudWatchDataSource.ts | 12 ++-- .../__mocks__/{API.ts => ResourcesAPI.ts} | 6 +- .../completion/CompletionItemProvider.ts | 20 +++--- .../components/AnnotationQueryEditor.test.tsx | 20 ++++-- .../cloudwatch/components/CloudWatchLink.tsx | 2 +- .../components/ConfigEditor.test.tsx | 2 +- .../cloudwatch/components/ConfigEditor.tsx | 4 +- .../components/Dimensions/Dimensions.test.tsx | 6 +- .../components/Dimensions/FilterItem.tsx | 2 +- .../LogGroups/LogGroupsField.test.tsx | 14 ++-- .../components/LogGroups/LogGroupsField.tsx | 15 +++-- .../LogGroups/LogGroupsSelector.test.tsx | 2 +- .../LogGroups/LogGroupsSelector.tsx | 3 +- .../MetricStatEditor.test.tsx | 20 +++--- .../MetricStatEditor/MetricStatEditor.tsx | 8 +-- .../MetricsQueryEditor.test.tsx | 14 ++-- .../components/QueryEditor.test.tsx | 6 +- .../components/QueryHeader.test.tsx | 16 ++--- .../cloudwatch/components/QueryHeader.tsx | 4 +- .../SQLBuilderEditor.test.tsx | 20 +++--- .../SQLBuilderSelectRow.test.tsx | 12 ++-- .../SQLBuilderEditor/SQLBuilderSelectRow.tsx | 2 +- .../components/SQLBuilderEditor/SQLFilter.tsx | 2 +- .../SQLBuilderEditor/SQLGroupBy.test.tsx | 2 +- .../VariableQueryEditor.test.tsx | 21 +++--- .../VariableQueryEditor.tsx | 16 +++-- .../datasource/cloudwatch/datasource.test.ts | 2 +- .../datasource/cloudwatch/datasource.ts | 14 ++-- .../datasource/cloudwatch/hooks.test.ts | 14 ++-- .../plugins/datasource/cloudwatch/hooks.ts | 34 +++++----- .../cloudwatch/language_provider.test.ts | 5 +- .../cloudwatch/language_provider.ts | 2 +- .../completion/CompletionItemProvider.test.ts | 4 +- .../completion/CompletionItemProvider.ts | 6 +- .../monarch/CompletionItemProvider.ts | 8 +-- .../ResourceAPI.test.ts} | 14 ++-- .../{api.ts => resources/ResourcesAPI.ts} | 26 +++---- .../datasource/cloudwatch/resources/types.ts | 63 +++++++++++++++++ .../plugins/datasource/cloudwatch/types.ts | 67 ------------------- .../datasource/cloudwatch/variables.test.ts | 28 ++++---- .../datasource/cloudwatch/variables.ts | 28 ++++---- 41 files changed, 289 insertions(+), 277 deletions(-) rename public/app/plugins/datasource/cloudwatch/__mocks__/{API.ts => ResourcesAPI.ts} (84%) rename public/app/plugins/datasource/cloudwatch/{api.test.ts => resources/ResourceAPI.test.ts} (90%) rename public/app/plugins/datasource/cloudwatch/{api.ts => resources/ResourcesAPI.ts} (95%) create mode 100644 public/app/plugins/datasource/cloudwatch/resources/types.ts diff --git a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts index 52f31219faf..72221bc968a 100644 --- a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts +++ b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts @@ -80,12 +80,12 @@ export function setupMockedDataSource({ const timeSrv = getTimeSrv(); const datasource = new CloudWatchDatasource(customInstanceSettings, templateService, timeSrv); datasource.getVariables = () => ['test']; - datasource.api.getNamespaces = jest.fn().mockResolvedValue([]); - datasource.api.getRegions = jest.fn().mockResolvedValue([]); - datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); - datasource.api.getMetrics = jest.fn().mockResolvedValue([]); - datasource.api.getAccounts = jest.fn().mockResolvedValue([]); - datasource.api.getLogGroups = jest.fn().mockResolvedValue([]); + datasource.resources.getNamespaces = jest.fn().mockResolvedValue([]); + datasource.resources.getRegions = jest.fn().mockResolvedValue([]); + datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); + datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); + datasource.resources.getAccounts = jest.fn().mockResolvedValue([]); + datasource.resources.getLogGroups = jest.fn().mockResolvedValue([]); const fetchMock = jest.fn().mockReturnValue(of({})); setBackendSrv({ ...getBackendSrv(), diff --git a/public/app/plugins/datasource/cloudwatch/__mocks__/API.ts b/public/app/plugins/datasource/cloudwatch/__mocks__/ResourcesAPI.ts similarity index 84% rename from public/app/plugins/datasource/cloudwatch/__mocks__/API.ts rename to public/app/plugins/datasource/cloudwatch/__mocks__/ResourcesAPI.ts index 662fc010006..69effa8d23b 100644 --- a/public/app/plugins/datasource/cloudwatch/__mocks__/API.ts +++ b/public/app/plugins/datasource/cloudwatch/__mocks__/ResourcesAPI.ts @@ -3,11 +3,11 @@ 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 { ResourcesAPI } from '../resources/ResourcesAPI'; import { CloudWatchSettings, setupMockedTemplateService } from './CloudWatchDataSource'; -export function setupMockedAPI({ +export function setupMockedResourcesAPI({ variables, response, getMock, @@ -20,7 +20,7 @@ export function setupMockedAPI({ let templateService = variables ? setupMockedTemplateService(variables) : new TemplateSrv(); const timeSrv = getTimeSrv(); - const api = new CloudWatchAPI(CloudWatchSettings, templateService); + const api = new ResourcesAPI(CloudWatchSettings, templateService); let resourceRequestMock = getMock ? getMock : jest.fn().mockReturnValue(response); setBackendSrv({ ...getBackendSrv(), 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 75e2a192c03..27e0670047d 100644 --- a/public/app/plugins/datasource/cloudwatch/cloudwatch-sql/completion/CompletionItemProvider.ts +++ b/public/app/plugins/datasource/cloudwatch/cloudwatch-sql/completion/CompletionItemProvider.ts @@ -3,11 +3,11 @@ import { uniq } from 'lodash'; import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import type { Monaco, monacoTypes } from '@grafana/ui'; -import { CloudWatchAPI } from '../../api'; import { CompletionItemProvider } from '../../monarch/CompletionItemProvider'; import { LinkedToken } from '../../monarch/LinkedToken'; import { TRIGGER_SUGGEST } from '../../monarch/commands'; import { SuggestionKind, CompletionItemPriority, StatementPosition } from '../../monarch/types'; +import { ResourcesAPI } from '../../resources/ResourcesAPI'; import { BY, FROM, @@ -34,9 +34,9 @@ type CompletionItem = monacoTypes.languages.CompletionItem; export class SQLCompletionItemProvider extends CompletionItemProvider { region: string; - constructor(api: CloudWatchAPI, templateSrv: TemplateSrv = getTemplateSrv()) { - super(api, templateSrv); - this.region = api.getActualRegion() ?? ''; + constructor(resources: ResourcesAPI, templateSrv: TemplateSrv = getTemplateSrv()) { + super(resources, templateSrv); + this.region = resources.getActualRegion() ?? ''; this.getStatementPosition = getStatementPosition; this.getSuggestionKinds = getSuggestionKinds; this.tokenTypes = SQLTokenTypes; @@ -112,14 +112,14 @@ export class SQLCompletionItemProvider extends CompletionItemProvider { const namespaceToken = getNamespaceToken(currentToken); if (namespaceToken?.value) { // if a namespace is specified, only suggest metrics for the namespace - const metrics = await this.api.getMetrics({ + const metrics = await this.resources.getMetrics({ namespace: namespaceToken?.value.replace(/\"/g, ''), region: this.region, }); 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({ region: this.region }); + const metrics = await this.resources.getAllMetrics({ region: this.region }); uniq(metrics.map((m) => m.metricName)).forEach((m) => m && addSuggestion(m, { insertText: m })); } } @@ -147,12 +147,12 @@ export class SQLCompletionItemProvider extends CompletionItemProvider { let namespaces = []; if (metricNameToken?.value) { // if a metric is specified, only suggest namespaces that actually have that metric - const metrics = await this.api.getMetrics({ region: this.region }); + const metrics = await this.resources.getMetrics({ region: this.region }); const metricName = this.templateSrv.replace(metricNameToken.value); namespaces = metrics.filter((m) => m.metricName === metricName).map((m) => m.namespace); } else { // if no metric is specified, just suggest all namespaces - const ns = await this.api.getNamespaces(); + const ns = await this.resources.getNamespaces(); namespaces = ns.map((n) => n.value); } namespaces.map((n) => addSuggestion(`"${n}"`, { insertText: `"${n}"` })); @@ -179,7 +179,7 @@ export class SQLCompletionItemProvider extends CompletionItemProvider { dimensionFilters = (labelKeyTokens || []).reduce((acc, curr) => { return { ...acc, [curr.value]: null }; }, {}); - const keys = await this.api.getDimensionKeys({ + const keys = await this.resources.getDimensionKeys({ namespace: this.templateSrv.replace(namespaceToken.value.replace(/\"/g, '')), region: this.templateSrv.replace(this.region), metricName: metricNameToken?.value, @@ -199,7 +199,7 @@ export class SQLCompletionItemProvider extends CompletionItemProvider { const metricNameToken = getMetricNameToken(currentToken); const labelKey = currentToken?.getPreviousNonWhiteSpaceToken()?.getPreviousNonWhiteSpaceToken(); if (namespaceToken?.value && labelKey?.value && metricNameToken?.value) { - const values = await this.api.getDimensionValues({ + const values = await this.resources.getDimensionValues({ region: this.region, namespace: namespaceToken.value.replace(/\"/g, ''), metricName: metricNameToken.value, diff --git a/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.test.tsx index 082d8ee0583..65de0fc59f7 100644 --- a/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.test.tsx @@ -29,10 +29,10 @@ const q: CloudWatchQuery = { alarmNamePrefix: '', }; -ds.datasource.api.getRegions = jest.fn().mockResolvedValue([]); -ds.datasource.api.getNamespaces = jest.fn().mockResolvedValue([]); -ds.datasource.api.getMetrics = jest.fn().mockResolvedValue([]); -ds.datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getRegions = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getNamespaces = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); ds.datasource.getVariables = jest.fn().mockReturnValue([]); const props: QueryEditorProps = { @@ -51,7 +51,9 @@ describe('AnnotationQueryEditor', () => { }); it('should return an error component in case CloudWatchQuery is not CloudWatchAnnotationQuery', async () => { - ds.datasource.api.getDimensionValues = jest.fn().mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); + ds.datasource.resources.getDimensionValues = jest + .fn() + .mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); render( ); @@ -59,7 +61,9 @@ describe('AnnotationQueryEditor', () => { }); it('should not display wildcard option in dimension value dropdown', async () => { - ds.datasource.api.getDimensionValues = jest.fn().mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); + ds.datasource.resources.getDimensionValues = jest + .fn() + .mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); (props.query as CloudWatchAnnotationQuery).dimensions = { instanceId: 'instance-123' }; render(); const valueElement = screen.getByText('instance-123'); @@ -72,7 +76,9 @@ describe('AnnotationQueryEditor', () => { }); it('should not display Accounts component', async () => { - ds.datasource.api.getDimensionValues = jest.fn().mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); + ds.datasource.resources.getDimensionValues = jest + .fn() + .mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); (props.query as CloudWatchAnnotationQuery).dimensions = { instanceId: 'instance-123' }; await waitFor(() => render()); expect(await screen.queryByText('Account')).toBeNull(); diff --git a/public/app/plugins/datasource/cloudwatch/components/CloudWatchLink.tsx b/public/app/plugins/datasource/cloudwatch/components/CloudWatchLink.tsx index e535aa91d38..2ba3364df49 100644 --- a/public/app/plugins/datasource/cloudwatch/components/CloudWatchLink.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/CloudWatchLink.tsx @@ -40,7 +40,7 @@ export function CloudWatchLink({ panelData, query, datasource }: Props) { source: sources ?? [], }; - setHref(encodeUrl(urlProps, datasource.api.getActualRegion(query.region))); + setHref(encodeUrl(urlProps, datasource.resources.getActualRegion(query.region))); } }, [panelData, prevPanelData, datasource, query]); diff --git a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx index d2579d9fc21..d44e1755b7e 100644 --- a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx @@ -99,7 +99,7 @@ describe('Render', () => { putMock.mockImplementation(async () => ({ datasource: setupMockedDataSource().datasource })); getMock.mockImplementation(async () => ({ datasource: setupMockedDataSource().datasource })); loadDataSourceMock.mockResolvedValue(datasource); - datasource.api.getRegions = jest.fn().mockResolvedValue([ + datasource.resources.getRegions = jest.fn().mockResolvedValue([ { label: 'ap-east-1', value: 'ap-east-1', diff --git a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx index e13ce093c1d..61a01609f39 100644 --- a/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx @@ -14,8 +14,8 @@ 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 { SelectableResourceValue } from '../resources/types'; import { CloudWatchJsonData, CloudWatchSecureJsonData } from '../types'; import { LogGroupsField } from './LogGroups/LogGroupsField'; @@ -45,7 +45,7 @@ export const ConfigEditor: FC = (props: Props) => { loadRegions={ datasource && (async () => { - return datasource.api + return datasource.resources .getRegions() .then((regions) => regions.reduce( 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 7b038b53379..6132944f607 100644 --- a/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx @@ -10,9 +10,9 @@ const ds = setupMockedDataSource({ variables: [], }); -ds.datasource.api.getNamespaces = jest.fn().mockResolvedValue([]); -ds.datasource.api.getMetrics = jest.fn().mockResolvedValue([]); -ds.datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getNamespaces = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); ds.datasource.getVariables = jest.fn().mockReturnValue([]); const q: CloudWatchMetricsQuery = { id: '', diff --git a/public/app/plugins/datasource/cloudwatch/components/Dimensions/FilterItem.tsx b/public/app/plugins/datasource/cloudwatch/components/Dimensions/FilterItem.tsx index 4dea46fead0..ed814a6d99a 100644 --- a/public/app/plugins/datasource/cloudwatch/components/Dimensions/FilterItem.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/Dimensions/FilterItem.tsx @@ -51,7 +51,7 @@ export const FilterItem: FunctionComponent = ({ return []; } - return datasource.api + return datasource.resources .getDimensionValues({ dimensionKey: filter.key, dimensionFilters: dimensionsExcludingCurrentKey, diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.test.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.test.tsx index 4e8d6684a76..b670c8486ae 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.test.tsx @@ -32,13 +32,13 @@ describe('LogGroupSelection', () => { it('should call getLogGroups to get associated log group arns and then update props if rendered with legacy log group names', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; - defaultProps.datasource.api.getLogGroups = jest + defaultProps.datasource.resources.getLogGroups = jest .fn() .mockResolvedValue([{ value: { arn: 'arn', name: 'loggroupname' } }]); render(); await waitFor(async () => expect(screen.getByText('Select Log Groups')).toBeInTheDocument()); - expect(defaultProps.datasource.api.getLogGroups).toHaveBeenCalledWith({ + expect(defaultProps.datasource.resources.getLogGroups).toHaveBeenCalledWith({ region: defaultProps.region, logGroupNamePrefix: 'loggroupname', }); @@ -48,14 +48,14 @@ describe('LogGroupSelection', () => { it('should not call getLogGroups to get associated log group arns for template variables that were part of the legacy log group names array, only include them in the call to onChange', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; defaultProps.datasource = setupMockedDataSource({ variables: [logGroupNamesVariable] }).datasource; - defaultProps.datasource.api.getLogGroups = jest + defaultProps.datasource.resources.getLogGroups = jest .fn() .mockResolvedValue([{ value: { arn: 'arn', name: 'loggroupname' } }]); render(); await waitFor(async () => expect(screen.getByText('Select Log Groups')).toBeInTheDocument()); - expect(defaultProps.datasource.api.getLogGroups).toHaveBeenCalledTimes(1); - expect(defaultProps.datasource.api.getLogGroups).toHaveBeenCalledWith({ + expect(defaultProps.datasource.resources.getLogGroups).toHaveBeenCalledTimes(1); + expect(defaultProps.datasource.resources.getLogGroups).toHaveBeenCalledWith({ region: defaultProps.region, logGroupNamePrefix: 'loggroupname', }); @@ -67,12 +67,12 @@ describe('LogGroupSelection', () => { it('should not call getLogGroups and update props if rendered with log groups', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; - defaultProps.datasource.api.getLogGroups = jest + defaultProps.datasource.resources.getLogGroups = jest .fn() .mockResolvedValue([{ value: { arn: 'arn', name: 'loggroupname' } }]); render(); await waitFor(() => expect(screen.getByText('Select Log Groups')).toBeInTheDocument()); - expect(defaultProps.datasource.api.getLogGroups).not.toHaveBeenCalled(); + expect(defaultProps.datasource.resources.getLogGroups).not.toHaveBeenCalled(); expect(defaultProps.onChange).not.toHaveBeenCalled(); }); }); diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.tsx index 1ec17498541..efb53a7c8fb 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsField.tsx @@ -3,7 +3,8 @@ import React, { useEffect, useState } from 'react'; import { CloudWatchDatasource } from '../../datasource'; import { useAccountOptions } from '../../hooks'; -import { DescribeLogGroupsRequest, LogGroup } from '../../types'; +import { DescribeLogGroupsRequest } from '../../resources/types'; +import { LogGroup } from '../../types'; import { isTemplateVariable } from '../../utils/templateVariableUtils'; import { LogGroupsSelector } from './LogGroupsSelector'; @@ -32,7 +33,7 @@ export const LogGroupsField = ({ maxNoOfVisibleLogGroups, onBeforeOpen, }: Props) => { - const accountState = useAccountOptions(datasource?.api, region); + const accountState = useAccountOptions(datasource?.resources, region); const [loadingLogGroupsStarted, setLoadingLogGroupsStarted] = useState(false); useEffect(() => { @@ -41,13 +42,15 @@ export const LogGroupsField = ({ setLoadingLogGroupsStarted(true); // there's no need to migrate variables, they will be taken care of in the logs query runner - const variables = legacyLogGroupNames.filter((lgn) => isTemplateVariable(datasource.api.templateSrv, lgn)); + const variables = legacyLogGroupNames.filter((lgn) => isTemplateVariable(datasource.resources.templateSrv, lgn)); const legacyLogGroupNameValues = legacyLogGroupNames.filter( - (lgn) => !isTemplateVariable(datasource.api.templateSrv, lgn) + (lgn) => !isTemplateVariable(datasource.resources.templateSrv, lgn) ); Promise.all( - legacyLogGroupNameValues.map((lg) => datasource.api.getLogGroups({ region: region, logGroupNamePrefix: lg })) + legacyLogGroupNameValues.map((lg) => + datasource.resources.getLogGroups({ region: region, logGroupNamePrefix: lg }) + ) ) .then((results) => { const logGroups = results.flatMap((r) => @@ -70,7 +73,7 @@ export const LogGroupsField = ({
) => - datasource?.api.getLogGroups({ region: region, ...params }) ?? [] + datasource?.resources.getLogGroups({ region: region, ...params }) ?? [] } onChange={onChange} accountOptions={accountState.value} diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.test.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.test.tsx index f65f4551d4a..62b22c6a361 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.test.tsx @@ -5,7 +5,7 @@ import lodash from 'lodash'; import React from 'react'; import selectEvent from 'react-select-event'; -import { ResourceResponse, LogGroupResponse } from '../../types'; +import { ResourceResponse, LogGroupResponse } from '../../resources/types'; import { LogGroupsSelector } from './LogGroupsSelector'; diff --git a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.tsx b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.tsx index 03b471beeca..a9949551ea4 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogGroups/LogGroupsSelector.tsx @@ -5,7 +5,8 @@ import { EditorField, Space } from '@grafana/experimental'; import { Button, Checkbox, Icon, Label, LoadingPlaceholder, Modal, Select, useStyles2 } from '@grafana/ui'; import Search from '../../Search'; -import { DescribeLogGroupsRequest, LogGroup, LogGroupResponse, ResourceResponse } from '../../types'; +import { DescribeLogGroupsRequest, ResourceResponse, LogGroupResponse } from '../../resources/types'; +import { LogGroup } from '../../types'; import { Account, ALL_ACCOUNTS_OPTION } from '../Account'; import getStyles from '../styles'; 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 815cbdda001..9393625583b 100644 --- a/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx @@ -15,9 +15,9 @@ const ds = setupMockedDataSource({ variables: [], }); -ds.datasource.api.getNamespaces = jest.fn().mockResolvedValue([]); -ds.datasource.api.getMetrics = jest.fn().mockResolvedValue([]); -ds.datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getNamespaces = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); ds.datasource.getVariables = jest.fn().mockReturnValue([]); const metricStat: MetricStat = { region: 'us-east-2', @@ -121,8 +121,8 @@ describe('MetricStatEditor', () => { }; beforeEach(() => { - propsNamespaceMetrics.datasource.api.getNamespaces = jest.fn().mockResolvedValue(namespaces); - propsNamespaceMetrics.datasource.api.getMetrics = jest.fn().mockResolvedValue(metrics); + propsNamespaceMetrics.datasource.resources.getNamespaces = jest.fn().mockResolvedValue(namespaces); + propsNamespaceMetrics.datasource.resources.getMetrics = jest.fn().mockResolvedValue(metrics); onChange.mockClear(); }); @@ -146,7 +146,7 @@ describe('MetricStatEditor', () => { }); it('should remove metricName from metricStat if it does not exist in new namespace', async () => { - propsNamespaceMetrics.datasource.api.getMetrics = jest.fn().mockImplementation(({ namespace, region }) => { + propsNamespaceMetrics.datasource.resources.getMetrics = jest.fn().mockImplementation(({ namespace, region }) => { let mockMetrics = namespace === 'n1' && region === props.metricStat.region ? metrics @@ -202,8 +202,8 @@ describe('MetricStatEditor', () => { it('should set value to "all" when its a monitoring account and no account id is defined in the query', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; const onChange = jest.fn(); - props.datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(true); - props.datasource.api.getAccounts = jest.fn().mockResolvedValue([ + props.datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(true); + props.datasource.resources.getAccounts = jest.fn().mockResolvedValue([ { value: '123456789', label: 'test-account1', @@ -231,8 +231,8 @@ describe('MetricStatEditor', () => { it('should unset value when no accounts were found and an account id is defined in the query', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; const onChange = jest.fn(); - props.datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(false); - props.datasource.api.getAccounts = jest.fn().mockResolvedValue([]); + props.datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false); + props.datasource.resources.getAccounts = jest.fn().mockResolvedValue([]); await act(async () => { render( { - datasource.api.isMonitoringAccount(metricStat.region).then((isMonitoringAccount) => { + datasource.resources.isMonitoringAccount(metricStat.region).then((isMonitoringAccount) => { if (isMonitoringAccount && !accountState.loading && accountState.value?.length && !metricStat.accountId) { onChange({ ...metricStat, accountId: 'all' }); } @@ -43,7 +43,7 @@ export function MetricStatEditor({ onChange({ ...metricStat, accountId: undefined }); } }); - }, [accountState, metricStat, onChange, datasource.api]); + }, [accountState, metricStat, onChange, datasource.resources]); const onNamespaceChange = async (metricStat: MetricStat) => { const validatedQuery = await validateMetricName(metricStat); @@ -55,7 +55,7 @@ export function MetricStatEditor({ if (!metricName) { return metricStat; } - await datasource.api.getMetrics({ namespace, region }).then((result: Array>) => { + await datasource.resources.getMetrics({ namespace, region }).then((result: Array>) => { if (!result.find((metric) => metric.value === metricName)) { metricName = ''; } 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 bac03da437f..42be2690b75 100644 --- a/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx @@ -46,11 +46,11 @@ const setup = () => { const datasource = new CloudWatchDatasource(instanceSettings, templateSrv as any, {} as any); datasource.metricFindQuery = async () => [{ value: 'test', label: 'test', text: 'test' }]; - datasource.api.getNamespaces = jest.fn().mockResolvedValue([]); - datasource.api.getMetrics = jest.fn().mockResolvedValue([]); - datasource.api.getRegions = jest.fn().mockResolvedValue([]); - datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); - datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(false); + datasource.resources.getNamespaces = jest.fn().mockResolvedValue([]); + datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); + datasource.resources.getRegions = jest.fn().mockResolvedValue([]); + datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); + datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false); const props: Props = { query: { @@ -93,7 +93,9 @@ describe('QueryEditor', () => { if (props.query.queryMode !== 'Metrics') { fail(`expected props.query.queryMode to be 'Metrics', got '${props.query.queryMode}' instead`); } - props.datasource.api.getDimensionValues = jest.fn().mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); + props.datasource.resources.getDimensionValues = jest + .fn() + .mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]); props.query.metricQueryType = MetricQueryType.Search; props.query.metricEditorMode = MetricEditorMode.Builder; props.query.dimensions = { instanceId: 'instance-123' }; diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx index a7782c0f3a8..dd1ad95e8d6 100644 --- a/public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx @@ -165,9 +165,9 @@ describe('QueryEditor should render right editor', () => { let datasourceMock: ReturnType; beforeEach(() => { datasourceMock = setupMockedDataSource(); - datasourceMock.datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(true); - datasourceMock.datasource.api.getMetrics = jest.fn().mockResolvedValue([]); - datasourceMock.datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); + datasourceMock.datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(true); + datasourceMock.datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); + datasourceMock.datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); originalValue = config.featureToggles.cloudWatchCrossAccountQuerying; }); afterEach(() => { diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx index 3466b9fcae0..2e782cc07a1 100644 --- a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.test.tsx @@ -13,7 +13,7 @@ const originalFeatureToggleValue = config.featureToggles.cloudWatchCrossAccountQ const ds = setupMockedDataSource({ variables: [], }); -ds.datasource.api.getRegions = jest.fn().mockResolvedValue([]); +ds.datasource.resources.getRegions = jest.fn().mockResolvedValue([]); describe('QueryHeader', () => { afterEach(() => { @@ -22,14 +22,14 @@ describe('QueryHeader', () => { describe('when changing region', () => { const { datasource } = setupMockedDataSource(); - datasource.api.getRegions = jest.fn().mockResolvedValue([ + datasource.resources.getRegions = jest.fn().mockResolvedValue([ { value: 'us-east-2', label: 'us-east-2' }, { value: 'us-east-1', label: 'us-east-1' }, ]); it('should reset account id if new region is not monitoring account', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; const onChange = jest.fn(); - datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(false); + datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false); render( { it('should not reset account id if new region is a monitoring account', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; const onChange = jest.fn(); - datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(true); + datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(true); render( { it('should not call isMonitoringAccount if its a logs query', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; const onChange = jest.fn(); - datasource.api.isMonitoringAccount = jest.fn().mockResolvedValue(true); + datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(true); render( { await act(async () => { await selectEvent.select(screen.getByLabelText(/Region/), 'us-east-2', { container: document.body }); }); - expect(datasource.api.isMonitoringAccount).not.toHaveBeenCalledWith('us-east-2'); + expect(datasource.resources.isMonitoringAccount).not.toHaveBeenCalledWith('us-east-2'); }); it('should not call isMonitoringAccount if feature toggle is not enabled', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = false; const onChange = jest.fn(); - datasource.api.isMonitoringAccount = jest.fn(); + datasource.resources.isMonitoringAccount = jest.fn(); render( { await act(async () => { await selectEvent.select(screen.getByLabelText(/Region/), 'us-east-2', { container: document.body }); }); - expect(datasource.api.isMonitoringAccount).not.toHaveBeenCalledWith(); + expect(datasource.resources.isMonitoringAccount).not.toHaveBeenCalledWith(); }); }); }); diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx index 77235f8a7bc..76f05ee0468 100644 --- a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx @@ -32,7 +32,7 @@ const QueryHeader: React.FC = ({ onRunQuery, }) => { const { queryMode, region } = query; - const isMonitoringAccount = useIsMonitoringAccount(datasource.api, query.region); + const isMonitoringAccount = useIsMonitoringAccount(datasource.resources, query.region); const [regions, regionIsLoading] = useRegions(datasource); const onQueryModeChange = ({ value }: SelectableValue) => { @@ -46,7 +46,7 @@ const QueryHeader: React.FC = ({ }; const onRegionChange = async (region: string) => { if (config.featureToggles.cloudWatchCrossAccountQuerying && isCloudWatchMetricsQuery(query)) { - const isMonitoringAccount = await datasource.api.isMonitoringAccount(region); + const isMonitoringAccount = await datasource.resources.isMonitoringAccount(region); onChange({ ...query, region, accountId: isMonitoringAccount ? query.accountId : undefined }); } else { onChange({ ...query, region }); 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 bffc5440a59..c9bc216a967 100644 --- a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderEditor.test.tsx @@ -22,10 +22,10 @@ export const makeSQLQuery = (sql?: SQLExpression): CloudWatchMetricsQuery => ({ describe('Cloudwatch SQLBuilderEditor', () => { beforeEach(() => { - datasource.api.getNamespaces = jest.fn().mockResolvedValue([]); - datasource.api.getMetrics = jest.fn().mockResolvedValue([]); - datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); - datasource.api.getDimensionValues = jest.fn().mockResolvedValue([]); + datasource.resources.getNamespaces = jest.fn().mockResolvedValue([]); + datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); + datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); + datasource.resources.getDimensionValues = jest.fn().mockResolvedValue([]); }); const baseProps = { @@ -46,7 +46,7 @@ describe('Cloudwatch SQLBuilderEditor', () => { }); render(); - await waitFor(() => expect(datasource.api.getNamespaces).toHaveBeenCalled()); + await waitFor(() => expect(datasource.resources.getNamespaces).toHaveBeenCalled()); expect(screen.getByText('AWS/EC2')).toBeInTheDocument(); expect(screen.getByLabelText('With schema')).not.toBeChecked(); @@ -67,7 +67,7 @@ describe('Cloudwatch SQLBuilderEditor', () => { }); render(); - await waitFor(() => expect(datasource.api.getNamespaces).toHaveBeenCalled()); + await waitFor(() => expect(datasource.resources.getNamespaces).toHaveBeenCalled()); expect(screen.getByText('AWS/EC2')).toBeInTheDocument(); expect(screen.getByLabelText('With schema')).toBeChecked(); @@ -94,7 +94,7 @@ describe('Cloudwatch SQLBuilderEditor', () => { render(); await waitFor(() => - expect(datasource.api.getDimensionKeys).toHaveBeenCalledWith({ + expect(datasource.resources.getDimensionKeys).toHaveBeenCalledWith({ namespace: 'AWS/EC2', region: query.region, dimensionFilters: { InstanceId: null }, @@ -121,7 +121,7 @@ describe('Cloudwatch SQLBuilderEditor', () => { }); render(); - await waitFor(() => expect(datasource.api.getNamespaces).toHaveBeenCalled()); + await waitFor(() => expect(datasource.resources.getNamespaces).toHaveBeenCalled()); expect(screen.getByText('AVERAGE')).toBeInTheDocument(); expect(screen.getByText('CPUUtilization')).toBeInTheDocument(); @@ -137,7 +137,7 @@ describe('Cloudwatch SQLBuilderEditor', () => { }); render(); - await waitFor(() => expect(datasource.api.getNamespaces).toHaveBeenCalled()); + await waitFor(() => expect(datasource.resources.getNamespaces).toHaveBeenCalled()); expect(screen.getByText('AVG')).toBeInTheDocument(); const directionElement = screen.getByLabelText('Direction'); @@ -149,7 +149,7 @@ describe('Cloudwatch SQLBuilderEditor', () => { const query = makeSQLQuery({}); render(); - await waitFor(() => expect(datasource.api.getNamespaces).toHaveBeenCalled()); + await waitFor(() => expect(datasource.resources.getNamespaces).toHaveBeenCalled()); expect(screen.queryByText('AVG')).toBeNull(); const directionElement = screen.getByLabelText('Direction'); diff --git a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.test.tsx b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.test.tsx index 8f8c2762340..7e9f61025b7 100644 --- a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.test.tsx @@ -60,15 +60,15 @@ const metrics = [ describe('Cloudwatch SQLBuilderSelectRow', () => { beforeEach(() => { - datasource.api.getNamespaces = jest.fn().mockResolvedValue(namespaces); - datasource.api.getMetrics = jest.fn().mockResolvedValue([]); - datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); - datasource.api.getDimensionValues = jest.fn().mockResolvedValue([]); + datasource.resources.getNamespaces = jest.fn().mockResolvedValue(namespaces); + datasource.resources.getMetrics = jest.fn().mockResolvedValue([]); + datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); + datasource.resources.getDimensionValues = jest.fn().mockResolvedValue([]); onQueryChange.mockReset(); }); it('Should not reset metricName when selecting a namespace if metric exist in new namespace', async () => { - datasource.api.getMetrics = jest.fn().mockResolvedValue(metrics); + datasource.resources.getMetrics = jest.fn().mockResolvedValue(metrics); await act(async () => { render(); @@ -103,7 +103,7 @@ describe('Cloudwatch SQLBuilderSelectRow', () => { }); it('Should reset metricName when selecting a namespace if metric does not exist in new namespace', async () => { - datasource.api.getMetrics = jest.fn().mockImplementation((namespace: string, region: string) => { + datasource.resources.getMetrics = jest.fn().mockImplementation((namespace: string, region: string) => { let mockMetrics = namespace === 'n1' && region === baseProps.query.region ? metrics diff --git a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.tsx b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.tsx index ab1a8551dc2..350d36e5cb2 100644 --- a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLBuilderSelectRow.tsx @@ -68,7 +68,7 @@ const SQLBuilderSelectRow: React.FC = ({ datasource, q const validateMetricName = async (query: CloudWatchMetricsQuery) => { let { region, sql, namespace } = query; - await datasource.api.getMetrics({ namespace, region }).then((result: Array>) => { + await datasource.resources.getMetrics({ namespace, region }).then((result: Array>) => { if (!result.some((metric) => metric.value === metricName)) { sql = removeMetricName(query).sql; } diff --git a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLFilter.tsx b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLFilter.tsx index c5856f3632b..1bbea200ba5 100644 --- a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLFilter.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLFilter.tsx @@ -109,7 +109,7 @@ const FilterItem: React.FC = (props) => { return []; } - return datasource.api + return datasource.resources .getDimensionValues({ region: query.region, namespace, metricName, dimensionKey: filter.property.name }) .then((result: Array>) => { return appendTemplateVariables(datasource, result); diff --git a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLGroupBy.test.tsx b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLGroupBy.test.tsx index 2492bcb2b11..6ca453eef3f 100644 --- a/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLGroupBy.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/SQLBuilderEditor/SQLGroupBy.test.tsx @@ -22,7 +22,7 @@ const makeSQLQuery = (sql?: SQLExpression): CloudWatchMetricsQuery => ({ sql: sql, }); -datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([]); +datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]); describe('Cloudwatch SQLGroupBy', () => { const baseProps = { diff --git a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx index e8cc5db0a2a..b2a065e8050 100644 --- a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx @@ -4,7 +4,8 @@ import React from 'react'; import { select } from 'react-select-event'; import { setupMockedDataSource } from '../../__mocks__/CloudWatchDataSource'; -import { GetDimensionKeysRequest, VariableQueryType } from '../../types'; +import { GetDimensionKeysRequest } from '../../resources/types'; +import { VariableQueryType } from '../../types'; import { VariableQueryEditor, Props } from './VariableQueryEditor'; @@ -22,22 +23,22 @@ const defaultQuery = { const ds = setupMockedDataSource(); -ds.datasource.api.getRegions = jest.fn().mockResolvedValue([ +ds.datasource.resources.getRegions = jest.fn().mockResolvedValue([ { label: 'a1', value: 'a1' }, { label: 'b1', value: 'b1' }, { label: 'c1', value: 'c1' }, ]); -ds.datasource.api.getNamespaces = jest.fn().mockResolvedValue([ +ds.datasource.resources.getNamespaces = jest.fn().mockResolvedValue([ { label: 'x2', value: 'x2' }, { label: 'y2', value: 'y2' }, { label: 'z2', value: 'z2' }, ]); -ds.datasource.api.getMetrics = jest.fn().mockResolvedValue([ +ds.datasource.resources.getMetrics = jest.fn().mockResolvedValue([ { label: 'h3', value: 'h3' }, { label: 'i3', value: 'i3' }, { label: 'j3', value: 'j3' }, ]); -ds.datasource.api.getDimensionKeys = jest +ds.datasource.resources.getDimensionKeys = jest .fn() .mockImplementation(({ namespace: region, dimensionFilters }: GetDimensionKeysRequest) => { if (!!dimensionFilters) { @@ -55,12 +56,12 @@ ds.datasource.api.getDimensionKeys = jest } return Promise.resolve([{ label: 't4', value: 't4' }]); }); -ds.datasource.api.getDimensionValues = jest.fn().mockResolvedValue([ +ds.datasource.resources.getDimensionValues = jest.fn().mockResolvedValue([ { label: 'foo', value: 'foo' }, { label: 'bar', value: 'bar' }, ]); ds.datasource.getVariables = jest.fn().mockReturnValue([]); -ds.datasource.api.getEc2InstanceAttribute = jest.fn().mockReturnValue([]); +ds.datasource.resources.getEc2InstanceAttribute = jest.fn().mockReturnValue([]); const onChange = jest.fn(); const defaultProps: Props = { @@ -142,7 +143,7 @@ describe('VariableEditor', () => { await select(keySelect, 'v4', { container: document.body, }); - expect(ds.datasource.api.getDimensionKeys).toHaveBeenCalledWith({ + expect(ds.datasource.resources.getDimensionKeys).toHaveBeenCalledWith({ namespace: 'z2', region: 'a1', metricName: 'i3', @@ -232,8 +233,8 @@ describe('VariableEditor', () => { }) ); - expect(ds.datasource.api.getMetrics).toHaveBeenCalledWith({ namespace: 'z2', region: 'b1' }); - expect(ds.datasource.api.getDimensionKeys).toHaveBeenCalledWith({ namespace: 'z2', region: 'b1' }); + expect(ds.datasource.resources.getMetrics).toHaveBeenCalledWith({ namespace: 'z2', region: 'b1' }); + expect(ds.datasource.resources.getDimensionKeys).toHaveBeenCalledWith({ namespace: 'z2', region: 'b1' }); expect(props.onChange).toHaveBeenCalledWith({ ...defaultQuery, refId: 'CloudWatchVariableQueryEditor-VariableQuery', diff --git a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx index 5e474a1cf51..02355d2566d 100644 --- a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx @@ -69,19 +69,21 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => { const sanitizeQuery = async (query: VariableQuery) => { let { metricName, dimensionKey, dimensionFilters, namespace, region } = query; if (metricName) { - await datasource.api.getMetrics({ namespace, region }).then((result: Array>) => { + await datasource.resources.getMetrics({ namespace, region }).then((result: Array>) => { if (!result.find((metric) => metric.value === metricName)) { metricName = ''; } }); } if (dimensionKey) { - await datasource.api.getDimensionKeys({ namespace, region }).then((result: Array>) => { - if (!result.find((key) => key.value === dimensionKey)) { - dimensionKey = ''; - dimensionFilters = {}; - } - }); + await datasource.resources + .getDimensionKeys({ namespace, region }) + .then((result: Array>) => { + if (!result.find((key) => key.value === dimensionKey)) { + dimensionKey = ''; + dimensionFilters = {}; + } + }); } return { ...query, metricName, dimensionKey, dimensionFilters }; }; diff --git a/public/app/plugins/datasource/cloudwatch/datasource.test.ts b/public/app/plugins/datasource/cloudwatch/datasource.test.ts index 2be3be9381b..92bf5f4f1e8 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.test.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.test.ts @@ -280,7 +280,7 @@ describe('datasource', () => { }, ]), }).datasource; - const allMetrics = await datasource.api.getAllMetrics({ region: 'us-east-2' }); + const allMetrics = await datasource.resources.getAllMetrics({ region: 'us-east-2' }); expect(allMetrics[0].metricName).toEqual('CPUUtilization'); expect(allMetrics[0].namespace).toEqual('AWS/EC2'); expect(allMetrics[1].metricName).toEqual('CPUPercentage'); diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 835c57385db..9651a795da9 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -19,7 +19,6 @@ import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_sr import { RowContextOptions } from '../../../features/logs/components/LogRowContextProvider'; import { CloudWatchAnnotationSupport } from './annotationSupport'; -import { CloudWatchAPI } from './api'; import { SQLCompletionItemProvider } from './cloudwatch-sql/completion/CompletionItemProvider'; import { DEFAULT_METRICS_QUERY, getDefaultLogsQuery } from './defaultQueries'; import { isCloudWatchAnnotationQuery, isCloudWatchLogsQuery, isCloudWatchMetricsQuery } from './guards'; @@ -28,6 +27,7 @@ import { MetricMathCompletionItemProvider } from './metric-math/completion/Compl import { CloudWatchAnnotationQueryRunner } from './query-runner/CloudWatchAnnotationQueryRunner'; import { CloudWatchLogsQueryRunner } from './query-runner/CloudWatchLogsQueryRunner'; import { CloudWatchMetricsQueryRunner } from './query-runner/CloudWatchMetricsQueryRunner'; +import { ResourcesAPI } from './resources/ResourcesAPI'; import { CloudWatchAnnotationQuery, CloudWatchJsonData, @@ -52,7 +52,7 @@ export class CloudWatchDatasource private metricsQueryRunner: CloudWatchMetricsQueryRunner; private annotationQueryRunner: CloudWatchAnnotationQueryRunner; logsQueryRunner: CloudWatchLogsQueryRunner; - api: CloudWatchAPI; + resources: ResourcesAPI; constructor( private instanceSettings: DataSourceInstanceSettings, @@ -61,14 +61,14 @@ export class CloudWatchDatasource ) { super(instanceSettings); this.defaultRegion = instanceSettings.jsonData.defaultRegion; - this.api = new CloudWatchAPI(instanceSettings, templateSrv); + this.resources = new ResourcesAPI(instanceSettings, templateSrv); this.languageProvider = new CloudWatchLanguageProvider(this); - this.sqlCompletionItemProvider = new SQLCompletionItemProvider(this.api, this.templateSrv); - this.metricMathCompletionItemProvider = new MetricMathCompletionItemProvider(this.api, this.templateSrv); + this.sqlCompletionItemProvider = new SQLCompletionItemProvider(this.resources, this.templateSrv); + this.metricMathCompletionItemProvider = new MetricMathCompletionItemProvider(this.resources, this.templateSrv); this.metricsQueryRunner = new CloudWatchMetricsQueryRunner(instanceSettings, templateSrv); this.logsQueryRunner = new CloudWatchLogsQueryRunner(instanceSettings, templateSrv, timeSrv); this.annotationQueryRunner = new CloudWatchAnnotationQueryRunner(instanceSettings, templateSrv); - this.variables = new CloudWatchVariableSupport(this.api); + this.variables = new CloudWatchVariableSupport(this.resources); this.annotations = CloudWatchAnnotationSupport; } @@ -167,7 +167,7 @@ export class CloudWatchDatasource // public getVariables() { - return this.api.getVariables(); + return this.resources.getVariables(); } getActualRegion(region?: string) { diff --git a/public/app/plugins/datasource/cloudwatch/hooks.test.ts b/public/app/plugins/datasource/cloudwatch/hooks.test.ts index 4512ee8723c..97349de75a5 100644 --- a/public/app/plugins/datasource/cloudwatch/hooks.test.ts +++ b/public/app/plugins/datasource/cloudwatch/hooks.test.ts @@ -2,7 +2,6 @@ import { renderHook } from '@testing-library/react-hooks'; import { config } from '@grafana/runtime'; -import { setupMockedAPI } from './__mocks__/API'; import { accountIdVariable, dimensionVariable, @@ -11,6 +10,7 @@ import { regionVariable, setupMockedDataSource, } from './__mocks__/CloudWatchDataSource'; +import { setupMockedResourcesAPI } from './__mocks__/ResourcesAPI'; import { useAccountOptions, useDimensionKeys, useIsMonitoringAccount, useMetrics } from './hooks'; const WAIT_OPTIONS = { @@ -26,7 +26,7 @@ describe('hooks', () => { describe('useIsMonitoringAccount', () => { it('should interpolate variables before calling api', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; - const { api } = setupMockedAPI({ + const { api } = setupMockedResourcesAPI({ variables: [regionVariable], }); const isMonitoringAccountMock = jest.fn().mockResolvedValue(true); @@ -44,7 +44,7 @@ describe('hooks', () => { variables: [regionVariable, namespaceVariable, accountIdVariable], }); const getMetricsMock = jest.fn().mockResolvedValue([]); - datasource.api.getMetrics = getMetricsMock; + datasource.resources.getMetrics = getMetricsMock; const { waitForNextUpdate } = renderHook(() => useMetrics(datasource, { @@ -70,7 +70,7 @@ describe('hooks', () => { variables: [regionVariable, namespaceVariable, accountIdVariable, metricVariable, dimensionVariable], }); const getDimensionKeysMock = jest.fn().mockResolvedValue([]); - datasource.api.getDimensionKeys = getDimensionKeysMock; + datasource.resources.getDimensionKeys = getDimensionKeysMock; const { waitForNextUpdate } = renderHook(() => useDimensionKeys(datasource, { @@ -100,7 +100,7 @@ describe('hooks', () => { describe('useAccountOptions', () => { it('does not call the api if the feature toggle is off', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = false; - const { api } = setupMockedAPI({ + const { api } = setupMockedResourcesAPI({ variables: [regionVariable], }); const getAccountsMock = jest.fn().mockResolvedValue([{ id: '123', label: 'accountLabel' }]); @@ -112,7 +112,7 @@ describe('hooks', () => { it('interpolates region variables before calling the api', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; - const { api } = setupMockedAPI({ + const { api } = setupMockedResourcesAPI({ variables: [regionVariable], }); const getAccountsMock = jest.fn().mockResolvedValue([{ id: '123', label: 'accountLabel' }]); @@ -125,7 +125,7 @@ describe('hooks', () => { it('returns properly formatted account options, and template variables', async () => { config.featureToggles.cloudWatchCrossAccountQuerying = true; - const { api } = setupMockedAPI({ + const { api } = setupMockedResourcesAPI({ variables: [regionVariable], }); const getAccountsMock = jest.fn().mockResolvedValue([{ id: '123', label: 'accountLabel' }]); diff --git a/public/app/plugins/datasource/cloudwatch/hooks.ts b/public/app/plugins/datasource/cloudwatch/hooks.ts index 04fefb1b5d4..a4a6738580a 100644 --- a/public/app/plugins/datasource/cloudwatch/hooks.ts +++ b/public/app/plugins/datasource/cloudwatch/hooks.ts @@ -4,9 +4,9 @@ import { useAsyncFn, useDeepCompareEffect } from 'react-use'; import { SelectableValue, toOption } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { CloudWatchAPI } from './api'; import { CloudWatchDatasource } from './datasource'; -import { GetDimensionKeysRequest, GetMetricsRequest } from './types'; +import { ResourcesAPI } from './resources/ResourcesAPI'; +import { GetMetricsRequest, GetDimensionKeysRequest } from './resources/types'; import { appendTemplateVariables } from './utils/utils'; export const useRegions = (datasource: CloudWatchDatasource): [Array>, boolean] => { @@ -21,7 +21,7 @@ export const useRegions = (datasource: CloudWatchDatasource): [Array>) => setRegions([...regions, variableOptionGroup])) .finally(() => setRegionsIsLoading(false)); @@ -33,7 +33,7 @@ export const useRegions = (datasource: CloudWatchDatasource): [Array { const [namespaces, setNamespaces] = useState>>([]); useEffect(() => { - datasource.api.getNamespaces().then((namespaces) => { + datasource.resources.getNamespaces().then((namespaces) => { setNamespaces(appendTemplateVariables(datasource, namespaces)); }); }, [datasource]); @@ -56,7 +56,7 @@ export const useMetrics = (datasource: CloudWatchDatasource, { region, namespace accountId = datasource.templateSrv.replace(accountId, {}); } useEffect(() => { - datasource.api.getMetrics({ namespace, region, accountId }).then((result: Array>) => { + datasource.resources.getMetrics({ namespace, region, accountId }).then((result: Array>) => { setMetrics(appendTemplateVariables(datasource, result)); }); }, [datasource, region, namespace, accountId]); @@ -87,12 +87,12 @@ export const useDimensionKeys = ( } if (dimensionFilters) { - dimensionFilters = datasource.api.convertDimensionFormat(dimensionFilters, {}); + dimensionFilters = datasource.resources.convertDimensionFormat(dimensionFilters, {}); } // doing deep comparison to avoid making new api calls to list metrics unless dimension filter object props changes useDeepCompareEffect(() => { - datasource.api + datasource.resources .getDimensionKeys({ namespace, region, metricName, accountId, dimensionFilters }) .then((result: Array>) => { setDimensionKeys(appendTemplateVariables(datasource, result)); @@ -102,37 +102,37 @@ export const useDimensionKeys = ( return dimensionKeys; }; -export const useIsMonitoringAccount = (api: CloudWatchAPI, region: string) => { +export const useIsMonitoringAccount = (resources: ResourcesAPI, region: string) => { const [isMonitoringAccount, setIsMonitoringAccount] = useState(false); // we call this before the use effect to ensure dependency array below // receives the interpolated value so that the effect is triggered when a variable is changed if (region) { - region = api.templateSrv.replace(region, {}); + region = resources.templateSrv.replace(region, {}); } useEffect(() => { if (config.featureToggles.cloudWatchCrossAccountQuerying) { - api.isMonitoringAccount(region).then((result) => setIsMonitoringAccount(result)); + resources.isMonitoringAccount(region).then((result) => setIsMonitoringAccount(result)); } - }, [region, api]); + }, [region, resources]); return isMonitoringAccount; }; export const useAccountOptions = ( - api: Pick | undefined, + resources: Pick | undefined, region: string ) => { // we call this before the use effect to ensure dependency array below // receives the interpolated value so that the effect is triggered when a variable is changed if (region) { - region = api?.templateSrv.replace(region, {}) ?? ''; + region = resources?.templateSrv.replace(region, {}) ?? ''; } const fetchAccountOptions = async () => { if (!config.featureToggles.cloudWatchCrossAccountQuerying) { return Promise.resolve([]); } - const accounts = (await api?.getAccounts({ region })) ?? []; + const accounts = (await resources?.getAccounts({ region })) ?? []; if (accounts.length === 0) { return []; } @@ -143,7 +143,7 @@ export const useAccountOptions = ( description: a.id, })); - const variableOptions = api?.getVariables().map(toOption) || []; + const variableOptions = resources?.getVariables().map(toOption) || []; const variableOptionGroup: SelectableValue = { label: 'Template Variables', @@ -153,11 +153,11 @@ export const useAccountOptions = ( return [...options, variableOptionGroup]; }; - const [state, doFetch] = useAsyncFn(fetchAccountOptions, [api, region]); + const [state, doFetch] = useAsyncFn(fetchAccountOptions, [resources, region]); useEffect(() => { doFetch(); - }, [api, region, doFetch]); + }, [resources, region, doFetch]); return state; }; diff --git a/public/app/plugins/datasource/cloudwatch/language_provider.test.ts b/public/app/plugins/datasource/cloudwatch/language_provider.test.ts index 23ddde3b747..d72ab5ed262 100644 --- a/public/app/plugins/datasource/cloudwatch/language_provider.test.ts +++ b/public/app/plugins/datasource/cloudwatch/language_provider.test.ts @@ -5,6 +5,7 @@ import { TypeaheadOutput } from '@grafana/ui'; import { CloudWatchDatasource } from './datasource'; import { CloudWatchLanguageProvider } from './language_provider'; +import { ResourceResponse } from './resources/types'; import { AGGREGATION_FUNCTIONS_STATS, BOOLEAN_FUNCTIONS, @@ -15,7 +16,7 @@ import { STRING_FUNCTIONS, FIELD_AND_FILTER_FUNCTIONS, } from './syntax'; -import { LogGroupField, ResourceResponse } from './types'; +import { LogGroupField } from './types'; const fields = ['field1', '@message']; @@ -109,7 +110,7 @@ async function runSuggestionTest(query: string, expectedItems: string[][]) { function makeDatasource(): CloudWatchDatasource { return { - api: { + resources: { getLogGroupFields(): Promise>> { return Promise.resolve([{ value: { name: 'field1' } }, { value: { name: '@message' } }]); }, diff --git a/public/app/plugins/datasource/cloudwatch/language_provider.ts b/public/app/plugins/datasource/cloudwatch/language_provider.ts index 6203d27412a..eca53233a6b 100644 --- a/public/app/plugins/datasource/cloudwatch/language_provider.ts +++ b/public/app/plugins/datasource/cloudwatch/language_provider.ts @@ -136,7 +136,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider { ); const results = await Promise.all( interpolatedLogGroups.map((logGroupName) => - this.datasource.api + this.datasource.resources .getLogGroupFields({ logGroupName, region }) .then((fields) => fields.filter((f) => f).map((f) => f.value.name ?? '')) ) diff --git a/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.test.ts b/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.test.ts index 0761ed05aa5..2fa961019f4 100644 --- a/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.test.ts +++ b/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.test.ts @@ -4,7 +4,7 @@ import { setupMockedTemplateService } from '../../__mocks__/CloudWatchDataSource import * as MetricMathTestData from '../../__mocks__/metric-math-test-data'; import MonacoMock from '../../__mocks__/monarch/Monaco'; import TextModel from '../../__mocks__/monarch/TextModel'; -import { CloudWatchAPI } from '../../api'; +import { ResourcesAPI } from '../../resources/ResourcesAPI'; import cloudWatchMetricMathLanguageDefinition from '../definition'; import { METRIC_MATH_FNS, @@ -20,7 +20,7 @@ const getSuggestions = async (value: string, position: monacoTypes.IPosition) => const setup = new MetricMathCompletionItemProvider( { getActualRegion: () => 'us-east-2', - } as CloudWatchAPI, + } as ResourcesAPI, setupMockedTemplateService([]) ); const monaco = MonacoMock as Monaco; diff --git a/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.ts b/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.ts index 9ebfa2729a5..17c061ac534 100644 --- a/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.ts +++ b/public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.ts @@ -1,11 +1,11 @@ import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import type { Monaco, monacoTypes } from '@grafana/ui'; -import { CloudWatchAPI } from '../../api'; import { CompletionItemProvider } from '../../monarch/CompletionItemProvider'; import { LinkedToken } from '../../monarch/LinkedToken'; import { TRIGGER_SUGGEST } from '../../monarch/commands'; import { SuggestionKind, CompletionItemPriority, StatementPosition } from '../../monarch/types'; +import { ResourcesAPI } from '../../resources/ResourcesAPI'; import { METRIC_MATH_FNS, METRIC_MATH_KEYWORDS, @@ -21,8 +21,8 @@ import { MetricMathTokenTypes } from './types'; type CompletionItem = monacoTypes.languages.CompletionItem; export class MetricMathCompletionItemProvider extends CompletionItemProvider { - constructor(api: CloudWatchAPI, templateSrv: TemplateSrv = getTemplateSrv()) { - super(api, templateSrv); + constructor(resources: ResourcesAPI, templateSrv: TemplateSrv = getTemplateSrv()) { + super(resources, templateSrv); this.getStatementPosition = getStatementPosition; this.getSuggestionKinds = getSuggestionKinds; this.tokenTypes = MetricMathTokenTypes; diff --git a/public/app/plugins/datasource/cloudwatch/monarch/CompletionItemProvider.ts b/public/app/plugins/datasource/cloudwatch/monarch/CompletionItemProvider.ts index e06b54dac1c..0b74cd46bb2 100644 --- a/public/app/plugins/datasource/cloudwatch/monarch/CompletionItemProvider.ts +++ b/public/app/plugins/datasource/cloudwatch/monarch/CompletionItemProvider.ts @@ -1,7 +1,7 @@ import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import type { Monaco, monacoTypes } from '@grafana/ui'; -import { CloudWatchAPI } from '../api'; +import { ResourcesAPI } from '../resources/ResourcesAPI'; import { LinkedToken } from './LinkedToken'; import { linkedTokenBuilder } from './linkedTokenBuilder'; @@ -18,12 +18,12 @@ CompletionItemProvider is an extendable class which needs to implement : - getSuggestions */ export class CompletionItemProvider implements Completeable { - api: CloudWatchAPI; + resources: ResourcesAPI; templateSrv: TemplateSrv; tokenTypes: TokenTypes; - constructor(api: CloudWatchAPI, templateSrv: TemplateSrv = getTemplateSrv()) { - this.api = api; + constructor(resources: ResourcesAPI, templateSrv: TemplateSrv = getTemplateSrv()) { + this.resources = resources; this.templateSrv = templateSrv; this.templateSrv = templateSrv; diff --git a/public/app/plugins/datasource/cloudwatch/api.test.ts b/public/app/plugins/datasource/cloudwatch/resources/ResourceAPI.test.ts similarity index 90% rename from public/app/plugins/datasource/cloudwatch/api.test.ts rename to public/app/plugins/datasource/cloudwatch/resources/ResourceAPI.test.ts index e786a3f0b4d..371228043ce 100644 --- a/public/app/plugins/datasource/cloudwatch/api.test.ts +++ b/public/app/plugins/datasource/cloudwatch/resources/ResourceAPI.test.ts @@ -1,9 +1,9 @@ -import { setupMockedAPI } from './__mocks__/API'; +import { setupMockedResourcesAPI } from '../__mocks__/ResourcesAPI'; -describe('api', () => { +describe('ResourcesAPI', () => { describe('describeLogGroup', () => { it('replaces region correctly in the query', async () => { - const { api, resourceRequestMock } = setupMockedAPI(); + const { api, resourceRequestMock } = setupMockedResourcesAPI(); await api.getLogGroups({ region: 'default' }); expect(resourceRequestMock.mock.calls[0][1].region).toBe('us-west-1'); @@ -30,7 +30,7 @@ describe('api', () => { }, ]; - const { api } = setupMockedAPI({ response }); + const { api } = setupMockedResourcesAPI({ response }); const expectedLogGroups = [ { text: '/aws/containerinsights/dev303-workshop/application', @@ -58,7 +58,7 @@ describe('api', () => { describe('memoization', () => { it('should not initiate new api request in case a previous request had same args', async () => { const getMock = jest.fn(); - const { api, resourceRequestMock } = setupMockedAPI({ getMock }); + const { api, resourceRequestMock } = setupMockedResourcesAPI({ getMock }); resourceRequestMock.mockResolvedValue([]); await Promise.all([ api.getMetrics({ namespace: 'AWS/EC2', region: 'us-east-1' }), @@ -87,7 +87,7 @@ describe('api', () => { }, }, ]); - const { api } = setupMockedAPI({ getMock }); + const { api } = setupMockedResourcesAPI({ getMock }); const allMetrics = await api.getAllMetrics({ region: 'us-east-2' }); expect(allMetrics).toEqual([ { metricName: 'CPUUtilization', namespace: 'AWS/EC2' }, @@ -110,7 +110,7 @@ describe('api', () => { }, }, ]); - const { api } = setupMockedAPI({ getMock }); + const { api } = setupMockedResourcesAPI({ getMock }); const allMetrics = await api.getMetrics({ region: 'us-east-2', namespace: 'AWS/EC2' }); expect(allMetrics).toEqual([ { label: 'CPUUtilization', value: 'CPUUtilization' }, diff --git a/public/app/plugins/datasource/cloudwatch/api.ts b/public/app/plugins/datasource/cloudwatch/resources/ResourcesAPI.ts similarity index 95% rename from public/app/plugins/datasource/cloudwatch/api.ts rename to public/app/plugins/datasource/cloudwatch/resources/ResourcesAPI.ts index 719d32c30b0..e90a65236c6 100644 --- a/public/app/plugins/datasource/cloudwatch/api.ts +++ b/public/app/plugins/datasource/cloudwatch/resources/ResourcesAPI.ts @@ -4,28 +4,24 @@ 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 { CloudWatchRequest } from '../query-runner/CloudWatchRequest'; +import { CloudWatchJsonData, LogGroupField, MultiFilters } from '../types'; + import { - CloudWatchJsonData, + ResourceRequest, + Account, + ResourceResponse, DescribeLogGroupsRequest, + LogGroupResponse, + GetLogGroupFieldsRequest, + GetMetricsRequest, GetDimensionKeysRequest, GetDimensionValuesRequest, - GetMetricsRequest, - LogGroupResponse, MetricResponse, - MultiFilters, - Account, - ResourceRequest, - ResourceResponse, - GetLogGroupFieldsRequest, - LogGroupField, + SelectableResourceValue, } from './types'; -export interface SelectableResourceValue extends SelectableValue { - text: string; -} - -export class CloudWatchAPI extends CloudWatchRequest { +export class ResourcesAPI extends CloudWatchRequest { private memoizedGetRequest; constructor(instanceSettings: DataSourceInstanceSettings, templateSrv: TemplateSrv) { diff --git a/public/app/plugins/datasource/cloudwatch/resources/types.ts b/public/app/plugins/datasource/cloudwatch/resources/types.ts new file mode 100644 index 00000000000..72dd51844d4 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/resources/types.ts @@ -0,0 +1,63 @@ +import { SelectableValue } from '@grafana/data'; + +import { Dimensions } from '../types'; + +export interface ResourceResponse { + accountId?: string; + value: T; +} + +export interface ResourceRequest { + region: string; + accountId?: string; +} + +export interface GetLogGroupFieldsRequest extends ResourceRequest { + arn?: string; + logGroupName: string; +} + +export interface GetDimensionKeysRequest extends ResourceRequest { + metricName?: string; + namespace?: string; + dimensionFilters?: Dimensions; +} + +export interface GetDimensionValuesRequest extends ResourceRequest { + dimensionKey: string; + namespace: string; + metricName?: string; + dimensionFilters?: Dimensions; +} + +export interface GetMetricsRequest extends ResourceRequest { + namespace?: string; +} + +export interface DescribeLogGroupsRequest extends ResourceRequest { + logGroupNamePrefix?: string; + logGroupPattern?: string; + limit?: number; + listAllLogGroups?: boolean; +} + +export interface Account { + arn: string; + id: string; + label: string; + isMonitoringAccount: boolean; +} + +export interface LogGroupResponse { + arn: string; + name: string; +} + +export interface MetricResponse { + name: string; + namespace: string; +} + +export interface SelectableResourceValue extends SelectableValue { + text: string; +} diff --git a/public/app/plugins/datasource/cloudwatch/types.ts b/public/app/plugins/datasource/cloudwatch/types.ts index bd8609cf5fc..ed445ecd9e5 100644 --- a/public/app/plugins/datasource/cloudwatch/types.ts +++ b/public/app/plugins/datasource/cloudwatch/types.ts @@ -336,73 +336,6 @@ export interface LegacyAnnotationQuery extends MetricStat, DataQuery { type: string; } -export interface MetricResponse { - name: string; - namespace: string; -} - -export interface ResourceRequest { - region: string; - accountId?: string; -} - -export interface GetLogGroupFieldsRequest extends ResourceRequest { - /** - * The log group identifier - */ - arn?: string; - /** - * The name of the log group to search. - */ - logGroupName: string; -} - -export interface GetDimensionKeysRequest extends ResourceRequest { - metricName?: string; - namespace?: string; - dimensionFilters?: Dimensions; -} - -export interface GetDimensionValuesRequest extends ResourceRequest { - dimensionKey: string; - namespace: string; - metricName?: string; - dimensionFilters?: Dimensions; -} - -export interface GetMetricsRequest extends ResourceRequest { - namespace?: string; -} - -export interface DescribeLogGroupsRequest extends ResourceRequest { - logGroupNamePrefix?: string; - logGroupPattern?: string; - limit?: number; - listAllLogGroups?: boolean; -} - -export interface Account { - arn: string; - id: string; - label: string; - isMonitoringAccount: boolean; -} - -export interface LogGroupResponse { - arn: string; - name: string; -} - -export interface MetricResponse { - name: string; - namespace: string; -} - -export interface ResourceResponse { - accountId?: string; - value: T; -} - export interface LogGroup { arn: string; name: string; diff --git a/public/app/plugins/datasource/cloudwatch/variables.test.ts b/public/app/plugins/datasource/cloudwatch/variables.test.ts index 2d9b3c456e2..d9b4496a3d6 100644 --- a/public/app/plugins/datasource/cloudwatch/variables.test.ts +++ b/public/app/plugins/datasource/cloudwatch/variables.test.ts @@ -1,5 +1,5 @@ -import { setupMockedAPI } from './__mocks__/API'; import { dimensionVariable, labelsVariable, setupMockedDataSource } from './__mocks__/CloudWatchDataSource'; +import { setupMockedResourcesAPI } from './__mocks__/ResourcesAPI'; import { VariableQuery, VariableQueryType } from './types'; import { CloudWatchVariableSupport } from './variables'; @@ -16,20 +16,20 @@ const defaultQuery: VariableQuery = { }; const mock = setupMockedDataSource({ variables: [labelsVariable, dimensionVariable] }); -mock.datasource.api.getRegions = jest.fn().mockResolvedValue([{ label: 'a', value: 'a' }]); -mock.datasource.api.getNamespaces = jest.fn().mockResolvedValue([{ label: 'b', value: 'b' }]); -mock.datasource.api.getMetrics = jest.fn().mockResolvedValue([{ label: 'c', value: 'c' }]); -mock.datasource.api.getDimensionKeys = jest.fn().mockResolvedValue([{ label: 'd', value: 'd' }]); -mock.datasource.api.getLogGroups = jest +mock.datasource.resources.getRegions = jest.fn().mockResolvedValue([{ label: 'a', value: 'a' }]); +mock.datasource.resources.getNamespaces = jest.fn().mockResolvedValue([{ label: 'b', value: 'b' }]); +mock.datasource.resources.getMetrics = jest.fn().mockResolvedValue([{ label: 'c', value: 'c' }]); +mock.datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([{ label: 'd', value: 'd' }]); +mock.datasource.resources.getLogGroups = jest .fn() .mockResolvedValue([{ value: { arn: 'a', name: 'a' } }, { value: { arn: 'b', name: 'b' } }]); -mock.datasource.api.getAccounts = jest.fn().mockResolvedValue([]); +mock.datasource.resources.getAccounts = jest.fn().mockResolvedValue([]); const getDimensionValues = jest.fn().mockResolvedValue([{ label: 'e', value: 'e' }]); const getEbsVolumeIds = jest.fn().mockResolvedValue([{ label: 'f', value: 'f' }]); const getEc2InstanceAttribute = jest.fn().mockResolvedValue([{ label: 'g', value: 'g' }]); const getResourceARNs = jest.fn().mockResolvedValue([{ label: 'h', value: 'h' }]); -const variables = new CloudWatchVariableSupport(mock.datasource.api); +const variables = new CloudWatchVariableSupport(mock.datasource.resources); describe('variables', () => { it('should run regions', async () => { @@ -54,7 +54,7 @@ describe('variables', () => { describe('accounts', () => { it('should run accounts', async () => { - const { api } = setupMockedAPI(); + const { api } = setupMockedResourcesAPI(); const getAccountMock = jest.fn().mockResolvedValue([]); api.getAccounts = getAccountMock; const variables = new CloudWatchVariableSupport(api); @@ -63,7 +63,7 @@ describe('variables', () => { }); it('should map accounts to metric find value and insert "all" option', async () => { - const { api } = setupMockedAPI(); + const { api } = setupMockedResourcesAPI(); api.getAccounts = jest.fn().mockResolvedValue([{ id: '123', label: 'Account1' }]); const variables = new CloudWatchVariableSupport(api); const result = await variables.execute({ ...defaultQuery, queryType: VariableQueryType.Accounts }); @@ -83,7 +83,7 @@ describe('variables', () => { dimensionFilters: { a: 'b' }, }; beforeEach(() => { - mock.datasource.api.getDimensionValues = getDimensionValues; + mock.datasource.resources.getDimensionValues = getDimensionValues; getDimensionValues.mockClear(); }); @@ -113,7 +113,7 @@ describe('variables', () => { describe('EBS volume ids', () => { beforeEach(() => { - mock.datasource.api.getEbsVolumeIds = getEbsVolumeIds; + mock.datasource.resources.getEbsVolumeIds = getEbsVolumeIds; getEbsVolumeIds.mockClear(); }); @@ -142,7 +142,7 @@ describe('variables', () => { ec2Filters: { a: ['b'] }, }; beforeEach(() => { - mock.datasource.api.getEc2InstanceAttribute = getEc2InstanceAttribute; + mock.datasource.resources.getEc2InstanceAttribute = getEc2InstanceAttribute; getEc2InstanceAttribute.mockClear(); }); @@ -167,7 +167,7 @@ describe('variables', () => { tags: { a: ['b'] }, }; beforeEach(() => { - mock.datasource.api.getResourceARNs = getResourceARNs; + mock.datasource.resources.getResourceARNs = getResourceARNs; getResourceARNs.mockClear(); }); diff --git a/public/app/plugins/datasource/cloudwatch/variables.ts b/public/app/plugins/datasource/cloudwatch/variables.ts index 2b7fa2e6614..0089878a150 100644 --- a/public/app/plugins/datasource/cloudwatch/variables.ts +++ b/public/app/plugins/datasource/cloudwatch/variables.ts @@ -9,16 +9,16 @@ import { SelectableValue, } from '@grafana/data'; -import { CloudWatchAPI } from './api'; import { ALL_ACCOUNTS_OPTION } from './components/Account'; import { VariableQueryEditor } from './components/VariableQueryEditor/VariableQueryEditor'; import { CloudWatchDatasource } from './datasource'; import { migrateVariableQuery } from './migrations/variableQueryMigrations'; +import { ResourcesAPI } from './resources/ResourcesAPI'; import { standardStatistics } from './standardStatistics'; import { VariableQuery, VariableQueryType } from './types'; export class CloudWatchVariableSupport extends CustomVariableSupport { - constructor(private readonly api: CloudWatchAPI) { + constructor(private readonly resources: ResourcesAPI) { super(); this.query = this.query.bind(this); } @@ -62,7 +62,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport regions.map(selectableValueToMetricFindOption)); + return this.resources.getRegions().then((regions) => regions.map(selectableValueToMetricFindOption)); } async handleNamespacesQuery() { - return this.api.getNamespaces().then((namespaces) => namespaces.map(selectableValueToMetricFindOption)); + return this.resources.getNamespaces().then((namespaces) => namespaces.map(selectableValueToMetricFindOption)); } async handleMetricsQuery({ namespace, region }: VariableQuery) { - return this.api.getMetrics({ namespace, region }).then((metrics) => metrics.map(selectableValueToMetricFindOption)); + return this.resources + .getMetrics({ namespace, region }) + .then((metrics) => metrics.map(selectableValueToMetricFindOption)); } async handleDimensionKeysQuery({ namespace, region }: VariableQuery) { - return this.api.getDimensionKeys({ namespace, region }).then((keys) => keys.map(selectableValueToMetricFindOption)); + return this.resources + .getDimensionKeys({ namespace, region }) + .then((keys) => keys.map(selectableValueToMetricFindOption)); } async handleDimensionValuesQuery({ namespace, region, dimensionKey, metricName, dimensionFilters }: VariableQuery) { if (!dimensionKey || !metricName) { return []; } - return this.api + return this.resources .getDimensionValues({ region, namespace, @@ -114,14 +118,14 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ids.map(selectableValueToMetricFindOption)); + return this.resources.getEbsVolumeIds(region, instanceID).then((ids) => ids.map(selectableValueToMetricFindOption)); } async handleEc2InstanceAttributeQuery({ region, attributeName, ec2Filters }: VariableQuery) { if (!attributeName) { return []; } - return this.api + return this.resources .getEc2InstanceAttribute(region, attributeName, ec2Filters ?? {}) .then((values) => values.map(selectableValueToMetricFindOption)); } @@ -130,7 +134,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport { + return this.resources.getAccounts({ region }).then((accounts) => { const metricFindOptions = accounts.map((account) => ({ text: account.label, value: account.id,