diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ArgQueryEditor/ArgQueryEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ArgQueryEditor/ArgQueryEditor.test.tsx new file mode 100644 index 000000000000..838e9d5da1aa --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ArgQueryEditor/ArgQueryEditor.test.tsx @@ -0,0 +1,84 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import createMockDatasource from '../../__mocks__/datasource'; +import createMockQuery from '../../__mocks__/query'; + +import ArgQueryEditor from './ArgQueryEditor'; + +jest.mock('@grafana/runtime', () => ({ + ...(jest.requireActual('@grafana/runtime') as unknown as object), + getTemplateSrv: () => ({ + replace: (val: string) => { + return val; + }, + }), +})); + +const variableOptionGroup = { + label: 'Template variables', + options: [], +}; + +const defaultProps = { + query: createMockQuery(), + datasource: createMockDatasource(), + variableOptionGroup: variableOptionGroup, + onChange: jest.fn(), + setError: jest.fn(), +}; + +describe('ArgQueryEditor', () => { + it('should render', async () => { + render(); + expect(await screen.findByTestId('azure-monitor-arg-query-editor-with-experimental-ui')).toBeInTheDocument(); + }); + + it('should select a subscription from the fetched array', async () => { + const datasource = createMockDatasource({ + getSubscriptions: jest.fn().mockResolvedValue([{ value: 'foo' }]), + }); + const onChange = jest.fn(); + render(); + expect(await screen.findByTestId('azure-monitor-arg-query-editor-with-experimental-ui')).toBeInTheDocument(); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ subscriptions: ['foo'] })); + }); + + it('should select a subscription from existing query', async () => { + const onChange = jest.fn(); + const query = createMockQuery({ + subscriptions: ['bar'], + }); + render(); + expect(await screen.findByTestId('azure-monitor-arg-query-editor-with-experimental-ui')).toBeInTheDocument(); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ subscriptions: ['bar'] })); + }); + + it('should change the subscription if the selected one is not part of the fetched array', async () => { + const onChange = jest.fn(); + const datasource = createMockDatasource({ + getSubscriptions: jest.fn().mockResolvedValue([{ value: 'foo' }]), + }); + const query = createMockQuery({ + subscriptions: ['bar'], + }); + render(); + expect(await screen.findByTestId('azure-monitor-arg-query-editor-with-experimental-ui')).toBeInTheDocument(); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ subscriptions: ['foo'] })); + expect(onChange).not.toHaveBeenCalledWith(expect.objectContaining({ subscriptions: ['bar'] })); + }); + + it('should keep a subset of subscriptions if the new list does not contain all the query subscriptions', async () => { + const onChange = jest.fn(); + const datasource = createMockDatasource({ + getSubscriptions: jest.fn().mockResolvedValue([{ value: 'foo' }, { value: 'bar' }]), + }); + const query = createMockQuery({ + subscriptions: ['foo', 'bar', 'foobar'], + }); + render(); + expect(await screen.findByTestId('azure-monitor-arg-query-editor-with-experimental-ui')).toBeInTheDocument(); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ subscriptions: ['foo', 'bar'] })); + expect(onChange).not.toHaveBeenCalledWith(expect.objectContaining({ subscriptions: ['foo', 'bar', 'foobar'] })); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ArgQueryEditor/ArgQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ArgQueryEditor/ArgQueryEditor.tsx index 7f4b06333525..be3905e0ca5e 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ArgQueryEditor/ArgQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ArgQueryEditor/ArgQueryEditor.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState, useRef } from 'react'; +import { intersection } from 'lodash'; +import React, { useState, useMemo } from 'react'; import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental'; @@ -18,6 +19,28 @@ interface ArgQueryEditorProps { } const ERROR_SOURCE = 'arg-subscriptions'; + +function selectSubscriptions( + fetchedSubscriptions: string[], + currentSubscriptions?: string[], + currentSubscription?: string +) { + let querySubscriptions = currentSubscriptions || []; + if (querySubscriptions.length === 0 && currentSubscription) { + querySubscriptions = [currentSubscription]; + } + if (querySubscriptions.length === 0 && fetchedSubscriptions.length) { + querySubscriptions = [fetchedSubscriptions[0]]; + } + const commonSubscriptions = intersection(querySubscriptions, fetchedSubscriptions); + if (fetchedSubscriptions.length && querySubscriptions.length > commonSubscriptions.length) { + // If not all of the query subscriptions are in the list of fetched subscriptions, then + // select only the ones present (or the first one if none is present) + querySubscriptions = commonSubscriptions.length > 0 ? commonSubscriptions : [fetchedSubscriptions[0]]; + } + return querySubscriptions; +} + const ArgQueryEditor: React.FC = ({ query, datasource, @@ -26,15 +49,8 @@ const ArgQueryEditor: React.FC = ({ onChange, setError, }) => { - const fetchedRef = useRef(false); const [subscriptions, setSubscriptions] = useState([]); - - useEffect(() => { - if (fetchedRef.current) { - return; - } - - fetchedRef.current = true; + useMemo(() => { datasource .getSubscriptions() .then((results) => { @@ -42,15 +58,19 @@ const ArgQueryEditor: React.FC = ({ setSubscriptions(fetchedSubscriptions); setError(ERROR_SOURCE, undefined); - if (!query.subscriptions?.length && fetchedSubscriptions?.length) { - onChange({ - ...query, - subscriptions: [query.subscription ?? fetchedSubscriptions[0].value], - }); - } + onChange({ + ...query, + subscriptions: selectSubscriptions( + fetchedSubscriptions.map((v) => v.value), + query.subscriptions, + query.subscription + ), + }); }) .catch((err) => setError(ERROR_SOURCE, err)); - }, [datasource, onChange, query, setError]); + // We are only interested in re-fetching subscriptions if the data source changes + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [datasource]); return (