Files
grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts
Andreas Christou 2bd9e9aca5 AzureMonitor: Add support for selecting multiple options when using the equals and not equals dimension filters (#48650)
* Add support for multiselect

- Add filters param to Dimensions
- Update existing tests
- Add MultiSelect component
- Add helper function to determine valid options
- Update labels hook to account for custom values
- Update go type
- Add function to build valid filters string

* Additional go tests

- Ensure query targets are built correctly

* Update DimensionFields frontend test

- Corrently rerender components
- Additional test for multiple labels selection
- Better selection of options in react-select components

* Fix lint issue

* Reset filters when operator or dimension changes

* Terminology

* Update test

* Add backend migration

- Update types (deprecate Filter field)
- Add migration logic
- Update tests
- Update dimension filters buliding

* Add migration test code

* Simplify some logic

* Add frontend deprecation notice

* Add frontend migration logic and migration tests

* Update setting of filter values

* Update DimensionFields test

* Fix linting issues

* PR comment updates

- Remove unnecessary if/else condition
- Don't set filter default value as queries should be migrated
- Add comment explaining why sw operator only accepts one value
- Remove unnecessary test for merging of old and new filters

* Nit on terminology

Co-authored-by: Andres Martinez Gotor <andres.martinez@grafana.com>

* Rename migrations for clarity

Co-authored-by: Andres Martinez Gotor <andres.martinez@grafana.com>
2022-05-10 15:05:48 +01:00

180 lines
6.0 KiB
TypeScript

import UrlBuilder from '../azure_monitor/url_builder';
import { setKustoQuery } from '../components/LogsQueryEditor/setQueryValue';
import {
appendDimensionFilter,
setTimeGrain as setMetricsTimeGrain,
} from '../components/MetricsQueryEditor/setQueryValue';
import TimegrainConverter from '../time_grain_converter';
import { AzureMetricDimension, AzureMonitorQuery, AzureQueryType } from '../types';
const OLD_DEFAULT_DROPDOWN_VALUE = 'select';
export default function migrateQuery(query: AzureMonitorQuery): AzureMonitorQuery {
let workingQuery = query;
// The old angular controller also had a `migrateApplicationInsightsKeys` migraiton that
// migrated old properties to other properties that still do not appear to be used anymore, so
// we decided to not include that migration anymore
// See https://github.com/grafana/grafana/blob/a6a09add/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts#L269-L288
workingQuery = migrateTimeGrains(workingQuery);
workingQuery = migrateLogAnalyticsToFromTimes(workingQuery);
workingQuery = migrateToDefaultNamespace(workingQuery);
workingQuery = migrateDimensionToDimensionFilter(workingQuery);
workingQuery = migrateResourceUri(workingQuery);
workingQuery = migrateDimensionFilterToArray(workingQuery);
return workingQuery;
}
function migrateTimeGrains(query: AzureMonitorQuery): AzureMonitorQuery {
let workingQuery = query;
if (workingQuery.azureMonitor?.timeGrainUnit && workingQuery.azureMonitor.timeGrain !== 'auto') {
const newTimeGrain = TimegrainConverter.createISO8601Duration(
workingQuery.azureMonitor.timeGrain ?? 'auto',
workingQuery.azureMonitor.timeGrainUnit
);
workingQuery = setMetricsTimeGrain(workingQuery, newTimeGrain);
delete workingQuery.azureMonitor?.timeGrainUnit;
}
return workingQuery;
}
function migrateLogAnalyticsToFromTimes(query: AzureMonitorQuery): AzureMonitorQuery {
let workingQuery = query;
if (workingQuery.azureLogAnalytics?.query?.match(/\$__from\s/gi)) {
workingQuery = setKustoQuery(
workingQuery,
workingQuery.azureLogAnalytics.query.replace(/\$__from\s/gi, '$__timeFrom() ')
);
}
if (workingQuery.azureLogAnalytics?.query?.match(/\$__to\s/gi)) {
workingQuery = setKustoQuery(
workingQuery,
workingQuery.azureLogAnalytics.query.replace(/\$__to\s/gi, '$__timeTo() ')
);
}
return workingQuery;
}
function migrateToDefaultNamespace(query: AzureMonitorQuery): AzureMonitorQuery {
const haveMetricNamespace =
query.azureMonitor?.metricNamespace && query.azureMonitor.metricNamespace !== OLD_DEFAULT_DROPDOWN_VALUE;
if (!haveMetricNamespace && query.azureMonitor?.metricDefinition) {
return {
...query,
azureMonitor: {
...query.azureMonitor,
metricNamespace: query.azureMonitor.metricDefinition,
},
};
}
return query;
}
function migrateDimensionToDimensionFilter(query: AzureMonitorQuery): AzureMonitorQuery {
let workingQuery = query;
const oldDimension = workingQuery.azureMonitor?.dimension;
if (oldDimension && oldDimension !== 'None') {
workingQuery = appendDimensionFilter(workingQuery, oldDimension, 'eq', [
workingQuery.azureMonitor?.dimensionFilter || '',
]);
}
return workingQuery;
}
// Azure Monitor metric queries prior to Grafana version 9 did not include a `resourceUri`.
// The resourceUri was previously constructed with the subscription id, resource group,
// metric definition (a.k.a. resource type), and the resource name.
function migrateResourceUri(query: AzureMonitorQuery): AzureMonitorQuery {
const azureMonitorQuery = query.azureMonitor;
if (!azureMonitorQuery || azureMonitorQuery.resourceUri) {
return query;
}
const { subscription } = query;
const { resourceGroup, metricDefinition, resourceName } = azureMonitorQuery;
if (!(subscription && resourceGroup && metricDefinition && resourceName)) {
return query;
}
const resourceUri = UrlBuilder.buildResourceUri(subscription, resourceGroup, metricDefinition, resourceName);
return {
...query,
azureMonitor: {
...azureMonitorQuery,
resourceUri,
},
};
}
function migrateDimensionFilterToArray(query: AzureMonitorQuery): AzureMonitorQuery {
const azureMonitorQuery = query.azureMonitor;
if (!azureMonitorQuery) {
return query;
}
const newFilters: AzureMetricDimension[] = [];
const dimensionFilters = azureMonitorQuery.dimensionFilters;
if (dimensionFilters && dimensionFilters.length > 0) {
dimensionFilters.forEach((filter) => {
const staticProps = { dimension: filter.dimension, operator: filter.operator };
if (!filter.filters && filter.filter) {
newFilters.push({ ...staticProps, filters: [filter.filter] });
} else {
let hasFilter = false;
if (filter.filters && filter.filter) {
for (const oldFilter of filter.filters) {
if (filter.filter === oldFilter) {
hasFilter = true;
break;
}
}
if (!hasFilter && filter.filter !== '*') {
filter.filters.push(filter.filter);
}
newFilters.push({ ...staticProps, filters: filter.filters });
}
}
});
if (newFilters.length > 0) {
return { ...query, azureMonitor: { ...azureMonitorQuery, dimensionFilters: newFilters } };
}
}
return query;
}
// datasource.ts also contains some migrations, which have been moved to here. Unsure whether
// they should also do all the other migrations...
export function datasourceMigrations(query: AzureMonitorQuery): AzureMonitorQuery {
let workingQuery = query;
if (!workingQuery.queryType) {
workingQuery = {
...workingQuery,
queryType: AzureQueryType.AzureMonitor,
};
}
if (workingQuery.queryType === AzureQueryType.AzureMonitor && workingQuery.azureMonitor) {
workingQuery = migrateDimensionToDimensionFilter(workingQuery);
workingQuery = migrateResourceUri(workingQuery);
workingQuery = migrateDimensionFilterToArray(workingQuery);
}
return workingQuery;
}