diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx index f6c612caffe..a4bdb54302c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx @@ -53,11 +53,7 @@ const LogsQueryEditor: React.FC = ({ ResourceRowType.Resource, ResourceRowType.Variable, ]} - resource={ - query.azureLogAnalytics?.resources && query.azureLogAnalytics.resources.length - ? query.azureLogAnalytics.resources[0] - : '' - } + resources={query.azureLogAnalytics?.resources ?? []} queryType="logs" /> diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx index 40ef8bf7025..bae55c8340b 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx @@ -77,6 +77,41 @@ describe('MetricsQueryEditor', () => { expect(await screen.findByTestId('azure-monitor-metrics-query-editor-with-experimental-ui')).toBeInTheDocument(); }); + it('should show the current resource in the ResourcePicker', async () => { + const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() }); + const query = createMockQuery({ + subscription: 'def-456', + azureMonitor: { + metricNamespace: 'Microsoft.Compute/virtualMachines', + resources: [ + { + resourceGroup: 'dev-3', + resourceName: 'web-server', + }, + ], + }, + }); + const onChange = jest.fn(); + + render( + {}} + /> + ); + + const resourcePickerButton = await screen.findByRole('button', { name: 'web-server' }); + expect(resourcePickerButton).toBeInTheDocument(); + resourcePickerButton.click(); + + const selection = await screen.findAllByLabelText('web-server'); + expect(selection).toHaveLength(2); + }); + it('should change resource when a resource is selected in the ResourcePicker', async () => { const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() }); const query = createMockQuery(); @@ -125,10 +160,10 @@ describe('MetricsQueryEditor', () => { azureMonitor: expect.objectContaining({ metricNamespace: 'microsoft.compute/virtualmachines', resources: [ - { + expect.objectContaining({ resourceGroup: 'dev-3', resourceName: 'web-server', - }, + }), ], }), }) diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx index 1d1c8c89f4d..d34a47acecb 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx @@ -4,7 +4,7 @@ import { PanelData } from '@grafana/data/src/types'; import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental'; import type Datasource from '../../datasource'; -import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish, AzureMetricResource } from '../../types'; +import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish } from '../../types'; import ResourceField from '../ResourceField'; import { ResourceRowType } from '../ResourcePicker/types'; @@ -37,13 +37,14 @@ const MetricsQueryEditor: React.FC = ({ const metricsMetadata = useMetricMetadata(query, datasource, onChange); const metricNamespaces = useMetricNamespaces(query, datasource, onChange, setError); const metricNames = useMetricNames(query, datasource, onChange, setError); - const resource: AzureMetricResource = { - subscription: query.subscription, - resourceGroup: query.azureMonitor?.resources?.[0]?.resourceGroup, - metricNamespace: query.azureMonitor?.metricNamespace, - resourceName: query.azureMonitor?.resources?.[0]?.resourceName, - region: query.azureMonitor?.region, - }; + const resources = + query.azureMonitor?.resources?.map((r) => ({ + subscription: query.subscription, + resourceGroup: r.resourceGroup, + metricNamespace: query.azureMonitor?.metricNamespace, + resourceName: r.resourceName, + region: query.azureMonitor?.region, + })) ?? []; return ( @@ -56,7 +57,7 @@ const MetricsQueryEditor: React.FC = ({ onQueryChange={onChange} setError={setError} selectableEntryTypes={[ResourceRowType.Resource]} - resource={resource} + resources={resources ?? []} queryType={'metrics'} /> extends AzureQueryEditorFieldProps { selectableEntryTypes: ResourceRowType[]; queryType: ResourcePickerQueryType; - resource: T; + resources: T[]; inlineField?: boolean; labelWidth?: number; } @@ -27,7 +27,7 @@ const ResourceField: React.FC> onQueryChange, selectableEntryTypes, queryType, - resource, + resources, inlineField, labelWidth, }) => { @@ -43,11 +43,11 @@ const ResourceField: React.FC> }, []); const handleApply = useCallback( - (resource: string | AzureMetricResource | undefined) => { - onQueryChange(setResource(query, resource)); + (resources: Array) => { + onQueryChange(setResources(query, queryType, resources)); closePicker(); }, - [closePicker, onQueryChange, query] + [closePicker, onQueryChange, query, queryType] ); return ( @@ -63,9 +63,8 @@ const ResourceField: React.FC> > resources && handleApply(resources[0])} + resources={resources} + onApply={handleApply} onCancel={closePicker} selectableEntryTypes={selectableEntryTypes} queryType={queryType} @@ -73,7 +72,7 @@ const ResourceField: React.FC> @@ -81,58 +80,53 @@ const ResourceField: React.FC> }; interface ResourceLabelProps { - resource: T; + resources: T[]; datasource: Datasource; } -const ResourceLabel = ({ resource, datasource }: ResourceLabelProps) => { - const [resourceComponents, setResourceComponents] = useState(parseResourceDetails(resource ?? '')); +const ResourceLabel = ({ resources, datasource }: ResourceLabelProps) => { + const [resourcesComponents, setResourcesComponents] = useState(parseMultipleResourceDetails(resources)); useEffect(() => { - if (resource && parseResourceDetails(resource)) { - typeof resource === 'string' - ? datasource.resourcePickerData.getResourceURIDisplayProperties(resource).then(setResourceComponents) - : setResourceComponents(resource); - } else { - setResourceComponents({}); - } - }, [datasource.resourcePickerData, resource]); + setResourcesComponents(parseMultipleResourceDetails(resources)); + }, [resources]); - if (!resource || (typeof resource === 'object' && !resource.subscription)) { + if (!resources.length) { return <>Select a resource; } - if (resourceComponents) { - return ; - } - - return <>{resource}; + return ; }; interface FormattedResourceProps { - resource: AzureMetricResource; + resources: AzureMetricResource[]; } -const FormattedResource = ({ resource }: FormattedResourceProps) => { +const FormattedResource = ({ resources }: FormattedResourceProps) => { const styles = useStyles2(getStyles); - if (resource.resourceName) { - return ( - - {resource.resourceName.split('/')[0]} - - ); - } - if (resource.resourceGroup) { - return ( - - {resource.resourceGroup} - - ); - } + let icon: IconName = 'cube'; + const items: string[] = []; + resources.forEach((resource) => { + if (resource.resourceName) { + items.push(resource.resourceName.split('/')[0]); + return; + } + if (resource.resourceGroup) { + icon = 'folder'; + items.push(resource.resourceGroup); + return; + } + if (resource.subscription) { + icon = 'layer-group'; + items.push(resource.subscription); + return; + } + }); return ( - - {resource.subscription} + + + {items.join(', ')} ); }; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/Advanced.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/Advanced.tsx index 15c20289dd7..fd12b294a6f 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/Advanced.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/Advanced.tsx @@ -28,7 +28,7 @@ const Advanced = ({ resources, onChange }: ResourcePickerProps setIsAdvancedOpen(!isAdvancedOpen)} > - {resources.map((resource, index) => ( + {(resources.length ? resources : [{}]).map((resource, index) => (
{typeof resource === 'string' ? ( <> diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.test.ts index a8210d20a00..6a2901f700b 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.test.ts @@ -8,7 +8,7 @@ import { parseResourceDetails, parseResourceURI, resourcesToStrings, - setResource, + setResources, } from './utils'; jest.mock('@grafana/runtime', () => ({ @@ -191,22 +191,24 @@ describe('AzureMonitor ResourcePicker utils', () => { }); }); - describe('setResource', () => { + describe('setResources', () => { it('updates a resource with a resource URI for Log Analytics', () => { - expect(setResource(createMockQuery(), '/subscription/sub')).toMatchObject({ + expect(setResources(createMockQuery(), 'logs', ['/subscription/sub'])).toMatchObject({ azureLogAnalytics: { resources: ['/subscription/sub'] }, }); }); it('updates a resource with a resource parameters for Metrics', () => { expect( - setResource(createMockQuery(), { - subscription: 'sub', - resourceGroup: 'rg', - metricNamespace: 'Microsoft.Storage/storageAccounts', - resourceName: 'testacct', - region: 'westus', - }) + setResources(createMockQuery(), 'metrics', [ + { + subscription: 'sub', + resourceGroup: 'rg', + metricNamespace: 'Microsoft.Storage/storageAccounts', + resourceName: 'testacct', + region: 'westus', + }, + ]) ).toMatchObject({ subscription: 'sub', azureMonitor: { diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.ts index b9f1195172f..fec1132ae8e 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/utils.ts @@ -3,6 +3,7 @@ import produce from 'immer'; import { getTemplateSrv } from '@grafana/runtime'; import UrlBuilder from '../../azure_monitor/url_builder'; +import { ResourcePickerQueryType } from '../../resourcePicker/resourcePickerData'; import { AzureMetricResource, AzureMonitorQuery } from '../../types'; import { ResourceRow, ResourceRowGroup } from './types'; @@ -153,26 +154,31 @@ export function addResources(rows: ResourceRowGroup, targetParentId: string, new }); } -export function setResource(query: AzureMonitorQuery, resource?: string | AzureMetricResource): AzureMonitorQuery { - if (typeof resource === 'string') { +export function setResources( + query: AzureMonitorQuery, + type: ResourcePickerQueryType, + resources: Array +): AzureMonitorQuery { + if (type === 'logs') { // Resource URI for LogAnalytics return { ...query, azureLogAnalytics: { ...query.azureLogAnalytics, - resources: [resource], + resources: resourcesToStrings(resources), }, }; } // Resource object for metrics + const parsedResource = resources.length ? parseResourceDetails(resources[0]) : {}; return { ...query, - subscription: resource?.subscription, + subscription: parsedResource.subscription, azureMonitor: { ...query.azureMonitor, - metricNamespace: resource?.metricNamespace?.toLocaleLowerCase(), - region: resource?.region, - resources: [{ resourceGroup: resource?.resourceGroup, resourceName: resource?.resourceName }], + metricNamespace: parsedResource.metricNamespace?.toLocaleLowerCase(), + region: parsedResource.region, + resources: parseMultipleResourceDetails(resources), metricName: undefined, aggregation: undefined, timeGrain: '',