From 5644555833abd7cb42750c35a0a88dc9722c4e66 Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Thu, 21 Jul 2022 13:21:07 +0200 Subject: [PATCH] Azure Monitor: Migrate template variables to new format (#52584) --- .../VariableEditor/VariableEditor.test.tsx | 32 +-- .../VariableEditor/VariableEditor.tsx | 25 +- .../grafanaTemplateVariableFns.ts | 70 +++++ .../grafanaTemplateVariables.test.ts | 272 +++++++++++++++++- .../variables.test.ts | 2 +- .../variables.ts | 5 +- 6 files changed, 362 insertions(+), 44 deletions(-) diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx index 6291edb1814..a25a4868ca6 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx @@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event'; import React from 'react'; import { select, openMenu } from 'react-select-event'; -import * as grafanaRuntime from '@grafana/runtime'; import * as ui from '@grafana/ui'; import createMockDatasource from '../../__mocks__/datasource'; @@ -41,16 +40,11 @@ const defaultProps = { }), }; -const originalConfigValue = grafanaRuntime.config.featureToggles.azTemplateVars; -beforeEach(() => { - // reset config - grafanaRuntime.config.featureToggles.azTemplateVars = originalConfigValue; -}); - describe('VariableEditor:', () => { - it('can select a query type', async () => { + it('can view a legacy Grafana query function', async () => { const onChange = jest.fn(); - const { rerender } = render(); + const legacyQuery = { ...defaultProps.query, queryType: AzureQueryType.GrafanaTemplateVariableFn }; + render(); await waitFor(() => screen.getByLabelText('select query type')); expect(screen.getByLabelText('select query type')).toBeInTheDocument(); screen.getByLabelText('select query type').click(); @@ -62,10 +56,6 @@ describe('VariableEditor:', () => { queryType: AzureQueryType.GrafanaTemplateVariableFn, }) ); - const newQuery = onChange.mock.calls.at(-1)[0]; - rerender(); - expect(screen.queryByText('Logs')).not.toBeInTheDocument(); - expect(screen.queryByText('Grafana Query Function')).toBeInTheDocument(); }); describe('log queries:', () => { it('should render', async () => { @@ -157,22 +147,6 @@ describe('VariableEditor:', () => { rerender(); await waitFor(() => expect(screen.getByText(text)).toBeInTheDocument()); }; - beforeEach(() => { - grafanaRuntime.config.featureToggles.azTemplateVars = true; - }); - - it('should show the new query types if feature gate is enabled', async () => { - render(); - openMenu(screen.getByLabelText('select query type')); - await waitFor(() => expect(screen.getByText('Subscriptions')).toBeInTheDocument()); - }); - - it('should not show the new query types if feature gate is disabled', async () => { - grafanaRuntime.config.featureToggles.azTemplateVars = false; - render(); - openMenu(screen.getByLabelText('select query type')); - await waitFor(() => expect(screen.queryByText('Subscriptions')).not.toBeInTheDocument()); - }); it('should run the query if requesting subscriptions', async () => { const onChange = jest.fn(); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.tsx index 9626f13685f..049377dd479 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.tsx @@ -3,11 +3,10 @@ import React, { useEffect, useState } from 'react'; import { useEffectOnce } from 'react-use'; import { SelectableValue } from '@grafana/data'; -import { config } from '@grafana/runtime'; import { Alert, InlineField, Select } from '@grafana/ui'; import DataSource from '../../datasource'; -import { migrateStringQueriesToObjectQueries } from '../../grafanaTemplateVariableFns'; +import { migrateQuery } from '../../grafanaTemplateVariableFns'; import { AzureMonitorOption, AzureMonitorQuery, AzureQueryType } from '../../types'; import useLastError from '../../utils/useLastError'; import LogsQueryEditor from '../LogsQueryEditor'; @@ -26,16 +25,20 @@ const removeOption: SelectableValue = { label: '-', value: '' }; const VariableEditor = (props: Props) => { const { query, onChange, datasource } = props; const AZURE_QUERY_VARIABLE_TYPE_OPTIONS = [ - { label: 'Grafana Query Function', value: AzureQueryType.GrafanaTemplateVariableFn }, + { label: 'Subscriptions', value: AzureQueryType.SubscriptionsQuery }, + { label: 'Resource Groups', value: AzureQueryType.ResourceGroupsQuery }, + { label: 'Namespaces', value: AzureQueryType.NamespacesQuery }, + { label: 'Resource Names', value: AzureQueryType.ResourceNamesQuery }, + { label: 'Metric Names', value: AzureQueryType.MetricNamesQuery }, + { label: 'Workspaces', value: AzureQueryType.WorkspacesQuery }, { label: 'Logs', value: AzureQueryType.LogAnalytics }, ]; - if (config.featureToggles.azTemplateVars) { - AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Subscriptions', value: AzureQueryType.SubscriptionsQuery }); - AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Resource Groups', value: AzureQueryType.ResourceGroupsQuery }); - AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Namespaces', value: AzureQueryType.NamespacesQuery }); - AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Resource Names', value: AzureQueryType.ResourceNamesQuery }); - AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Metric Names', value: AzureQueryType.MetricNamesQuery }); - AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Workspaces', value: AzureQueryType.WorkspacesQuery }); + if (typeof props.query === 'object' && props.query.queryType === AzureQueryType.GrafanaTemplateVariableFn) { + // Add the option for the GrafanaTemplateVariableFn only if it's already in use + AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ + label: 'Grafana Query Function', + value: AzureQueryType.GrafanaTemplateVariableFn, + }); } const [variableOptionGroup, setVariableOptionGroup] = useState<{ label: string; options: AzureMonitorOption[] }>({ label: 'Template Variables', @@ -55,7 +58,7 @@ const VariableEditor = (props: Props) => { const queryType = typeof query === 'string' ? '' : query.queryType; useEffect(() => { - migrateStringQueriesToObjectQueries(query, { datasource: datasource }).then((migratedQuery) => { + migrateQuery(query, { datasource: datasource }).then((migratedQuery) => { onChange(migratedQuery); }); }, [query, datasource, onChange]); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariableFns.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariableFns.ts index 3cdc108ba53..a443073162c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariableFns.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariableFns.ts @@ -1,3 +1,5 @@ +import { config } from '@grafana/runtime'; + import { isGUIDish } from './components/ResourcePicker/utils'; import DataSource from './datasource'; import { AzureMonitorQuery, AzureQueryType } from './types'; @@ -253,6 +255,58 @@ const createLogAnalyticsTemplateVariableQuery = async ( }; }; +const migrateGrafanaTemplateVariableFn = (query: AzureMonitorQuery) => { + const { queryType, grafanaTemplateVariableFn } = query; + if (queryType !== AzureQueryType.GrafanaTemplateVariableFn || !grafanaTemplateVariableFn) { + return query; + } + + const migratedQuery: AzureMonitorQuery = { + ...query, + }; + if ('subscription' in grafanaTemplateVariableFn) { + migratedQuery.subscription = grafanaTemplateVariableFn.subscription; + } + if ('resourceGroup' in grafanaTemplateVariableFn) { + migratedQuery.resourceGroup = grafanaTemplateVariableFn.resourceGroup; + } + if ('metricDefinition' in grafanaTemplateVariableFn) { + migratedQuery.namespace = grafanaTemplateVariableFn.metricDefinition; + } + if ('metricNamespace' in grafanaTemplateVariableFn) { + migratedQuery.namespace = grafanaTemplateVariableFn.metricNamespace; + } + if ('resourceName' in grafanaTemplateVariableFn) { + migratedQuery.resource = grafanaTemplateVariableFn.resourceName; + } + + switch (grafanaTemplateVariableFn.kind) { + case 'SubscriptionsQuery': + migratedQuery.queryType = AzureQueryType.SubscriptionsQuery; + break; + case 'ResourceGroupsQuery': + migratedQuery.queryType = AzureQueryType.ResourceGroupsQuery; + break; + case 'MetricDefinitionsQuery': + migratedQuery.queryType = AzureQueryType.NamespacesQuery; + break; + case 'ResourceNamesQuery': + migratedQuery.queryType = AzureQueryType.ResourceNamesQuery; + break; + case 'MetricNamespaceQuery': + migratedQuery.queryType = AzureQueryType.NamespacesQuery; + break; + case 'MetricNamesQuery': + migratedQuery.queryType = AzureQueryType.MetricNamesQuery; + break; + case 'WorkspacesQuery': + migratedQuery.queryType = AzureQueryType.WorkspacesQuery; + break; + } + + return migratedQuery; +}; + export const migrateStringQueriesToObjectQueries = async ( rawQuery: string | AzureMonitorQuery, options: { datasource: DataSource } @@ -266,3 +320,19 @@ export const migrateStringQueriesToObjectQueries = async ( ? createGrafanaTemplateVariableQuery(rawQuery, options.datasource) : createLogAnalyticsTemplateVariableQuery(rawQuery, options.datasource); }; + +export const migrateQuery = async ( + rawQuery: string | AzureMonitorQuery, + options: { datasource: DataSource } +): Promise => { + let query = await migrateStringQueriesToObjectQueries(rawQuery, options); + + if ( + !config.featureToggles.azLegacyTemplateVariables && + query.queryType === AzureQueryType.GrafanaTemplateVariableFn + ) { + query = migrateGrafanaTemplateVariableFn(query); + } + + return query; +}; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariables.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariables.test.ts index f3e1df0554c..d478ea5d893 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariables.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/grafanaTemplateVariables.test.ts @@ -1,5 +1,5 @@ import createMockDatasource from './__mocks__/datasource'; -import { migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns'; +import { migrateQuery, migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns'; import { AzureMonitorQuery, AzureQueryType } from './types'; describe('migrateStringQueriesToObjectQueries', () => { @@ -239,3 +239,273 @@ describe('migrateStringQueriesToObjectQueries', () => { }); }); }); + +describe('migrateStringQueriesToObjectQueries', () => { + const expectedMigrations: Array<{ input: AzureMonitorQuery; output: AzureMonitorQuery }> = [ + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { kind: 'SubscriptionsQuery', rawQuery: 'Subscriptions()' }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.SubscriptionsQuery, + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'ResourceGroupsQuery', + rawQuery: 'ResourceGroups()', + subscription: 'defaultSubscriptionId', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.ResourceGroupsQuery, + subscription: 'defaultSubscriptionId', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'ResourceGroupsQuery', + rawQuery: 'ResourceGroups(subId)', + subscription: 'subId', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.ResourceGroupsQuery, + subscription: 'subId', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'MetricDefinitionsQuery', + rawQuery: 'Namespaces(rg)', + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.NamespacesQuery, + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'MetricDefinitionsQuery', + rawQuery: 'Namespaces(subId, rg)', + subscription: 'subId', + resourceGroup: 'rg', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.NamespacesQuery, + subscription: 'subId', + resourceGroup: 'rg', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'ResourceNamesQuery', + rawQuery: 'ResourceNames(rg, md)', + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + metricDefinition: 'md', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.ResourceNamesQuery, + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + namespace: 'md', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'ResourceNamesQuery', + rawQuery: 'ResourceNames(subId, rg, md)', + subscription: 'subId', + resourceGroup: 'rg', + metricDefinition: 'md', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.ResourceNamesQuery, + subscription: 'subId', + resourceGroup: 'rg', + namespace: 'md', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'MetricNamespaceQuery', + rawQuery: 'MetricNamespace(rg, md, rn)', + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + metricDefinition: 'md', + resourceName: 'rn', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.NamespacesQuery, + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + namespace: 'md', + resource: 'rn', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'MetricNamespaceQuery', + rawQuery: 'MetricNamespace(subId, rg, md, rn)', + subscription: 'subId', + resourceGroup: 'rg', + metricDefinition: 'md', + resourceName: 'rn', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.NamespacesQuery, + subscription: 'subId', + resourceGroup: 'rg', + namespace: 'md', + resource: 'rn', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'MetricNamesQuery', + rawQuery: 'MetricNames(rg, md, rn, mn)', + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + metricDefinition: 'md', + resourceName: 'rn', + metricNamespace: 'mn', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.MetricNamesQuery, + subscription: 'defaultSubscriptionId', + resourceGroup: 'rg', + namespace: 'mn', + resource: 'rn', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'MetricNamesQuery', + rawQuery: 'MetricNames(subId, rg, md, rn, mn)', + subscription: 'subId', + resourceGroup: 'rg', + metricDefinition: 'md', + resourceName: 'rn', + metricNamespace: 'mn', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.MetricNamesQuery, + subscription: 'subId', + resourceGroup: 'rg', + namespace: 'mn', + resource: 'rn', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'WorkspacesQuery', + rawQuery: 'workspaces()', + subscription: 'defaultSubscriptionId', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.WorkspacesQuery, + subscription: 'defaultSubscriptionId', + }, + }, + { + input: { + refId: 'A', + queryType: AzureQueryType.GrafanaTemplateVariableFn, + grafanaTemplateVariableFn: { + kind: 'WorkspacesQuery', + rawQuery: 'workspaces(subId)', + subscription: 'subId', + }, + subscription: 'defaultSubscriptionId', + }, + output: { + refId: 'A', + queryType: AzureQueryType.WorkspacesQuery, + subscription: 'subId', + }, + }, + ]; + it('successfully converts all old variable functions into formatted predefined queries', async () => { + return expectedMigrations.map(async ({ input, output }) => { + const datasource = createMockDatasource({ + azureMonitorDatasource: { + defaultSubscriptionId: 'defaultSubscriptionId', + }, + }); + const actual = await migrateQuery(input, { datasource }); + expect(actual).toMatchObject(output); + }); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts index 2fc9c36f002..339bde53363 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts @@ -389,7 +389,7 @@ describe('VariableSupport', () => { azureMonitorDatasource: { defaultSubscriptionId: 'defaultSubscriptionId', }, - getMetricDefinitions: jest.fn((sub: string, rg: string) => { + getMetricNamespaces: jest.fn((sub: string, rg: string) => { if (sub === 'subscriptionId' && rg === 'resourceGroup') { return Promise.resolve(expectedResults); } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts index 0315cda03db..19375c755ac 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts @@ -11,7 +11,7 @@ import { getTemplateSrv } from '@grafana/runtime'; import VariableEditor from './components/VariableEditor/VariableEditor'; import DataSource from './datasource'; -import { migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns'; +import { migrateQuery } from './grafanaTemplateVariableFns'; import { AzureMonitorQuery, AzureQueryType } from './types'; import { GrafanaTemplateVariableQuery } from './types/templateVariables'; import messageFromError from './utils/messageFromError'; @@ -27,7 +27,7 @@ export class VariableSupport extends CustomVariableSupport): Observable { const promisedResults = async () => { - const queryObj = await migrateStringQueriesToObjectQueries(request.targets[0], { datasource: this.datasource }); + const queryObj = await migrateQuery(request.targets[0], { datasource: this.datasource }); try { switch (queryObj.queryType) { @@ -101,6 +101,7 @@ export class VariableSupport extends CustomVariableSupport | null { if (query.kind === 'SubscriptionsQuery') { return this.datasource.getSubscriptions();