migrateQuery.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import React from 'react';
  2. import { TemplateSrv } from '@grafana/runtime';
  3. import UrlBuilder from '../azure_monitor/url_builder';
  4. import { setKustoQuery } from '../components/LogsQueryEditor/setQueryValue';
  5. import {
  6. appendDimensionFilter,
  7. setTimeGrain as setMetricsTimeGrain,
  8. } from '../components/MetricsQueryEditor/setQueryValue';
  9. import TimegrainConverter from '../time_grain_converter';
  10. import { AzureMetricDimension, AzureMonitorErrorish, AzureMonitorQuery, AzureQueryType } from '../types';
  11. const OLD_DEFAULT_DROPDOWN_VALUE = 'select';
  12. export default function migrateQuery(
  13. query: AzureMonitorQuery,
  14. templateSrv: TemplateSrv,
  15. setError: (errorSource: string, error: AzureMonitorErrorish) => void
  16. ): AzureMonitorQuery {
  17. let workingQuery = query;
  18. // The old angular controller also had a `migrateApplicationInsightsKeys` migraiton that
  19. // migrated old properties to other properties that still do not appear to be used anymore, so
  20. // we decided to not include that migration anymore
  21. // See https://github.com/grafana/grafana/blob/a6a09add/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts#L269-L288
  22. workingQuery = migrateTimeGrains(workingQuery);
  23. workingQuery = migrateLogAnalyticsToFromTimes(workingQuery);
  24. workingQuery = migrateToDefaultNamespace(workingQuery);
  25. workingQuery = migrateDimensionToDimensionFilter(workingQuery);
  26. workingQuery = migrateResourceUri(workingQuery, templateSrv, setError);
  27. workingQuery = migrateDimensionFilterToArray(workingQuery);
  28. return workingQuery;
  29. }
  30. function migrateTimeGrains(query: AzureMonitorQuery): AzureMonitorQuery {
  31. let workingQuery = query;
  32. if (workingQuery.azureMonitor?.timeGrainUnit && workingQuery.azureMonitor.timeGrain !== 'auto') {
  33. const newTimeGrain = TimegrainConverter.createISO8601Duration(
  34. workingQuery.azureMonitor.timeGrain ?? 'auto',
  35. workingQuery.azureMonitor.timeGrainUnit
  36. );
  37. workingQuery = setMetricsTimeGrain(workingQuery, newTimeGrain);
  38. delete workingQuery.azureMonitor?.timeGrainUnit;
  39. }
  40. return workingQuery;
  41. }
  42. function migrateLogAnalyticsToFromTimes(query: AzureMonitorQuery): AzureMonitorQuery {
  43. let workingQuery = query;
  44. if (workingQuery.azureLogAnalytics?.query?.match(/\$__from\s/gi)) {
  45. workingQuery = setKustoQuery(
  46. workingQuery,
  47. workingQuery.azureLogAnalytics.query.replace(/\$__from\s/gi, '$__timeFrom() ')
  48. );
  49. }
  50. if (workingQuery.azureLogAnalytics?.query?.match(/\$__to\s/gi)) {
  51. workingQuery = setKustoQuery(
  52. workingQuery,
  53. workingQuery.azureLogAnalytics.query.replace(/\$__to\s/gi, '$__timeTo() ')
  54. );
  55. }
  56. return workingQuery;
  57. }
  58. function migrateToDefaultNamespace(query: AzureMonitorQuery): AzureMonitorQuery {
  59. const haveMetricNamespace =
  60. query.azureMonitor?.metricNamespace && query.azureMonitor.metricNamespace !== OLD_DEFAULT_DROPDOWN_VALUE;
  61. if (!haveMetricNamespace && query.azureMonitor?.metricDefinition) {
  62. return {
  63. ...query,
  64. azureMonitor: {
  65. ...query.azureMonitor,
  66. metricNamespace: query.azureMonitor.metricDefinition,
  67. },
  68. };
  69. }
  70. return query;
  71. }
  72. function migrateDimensionToDimensionFilter(query: AzureMonitorQuery): AzureMonitorQuery {
  73. let workingQuery = query;
  74. const oldDimension = workingQuery.azureMonitor?.dimension;
  75. if (oldDimension && oldDimension !== 'None') {
  76. workingQuery = appendDimensionFilter(workingQuery, oldDimension, 'eq', [
  77. workingQuery.azureMonitor?.dimensionFilter || '',
  78. ]);
  79. }
  80. return workingQuery;
  81. }
  82. // Azure Monitor metric queries prior to Grafana version 9 did not include a `resourceUri`.
  83. // The resourceUri was previously constructed with the subscription id, resource group,
  84. // metric definition (a.k.a. resource type), and the resource name.
  85. function migrateResourceUri(
  86. query: AzureMonitorQuery,
  87. templateSrv: TemplateSrv,
  88. setError?: (errorSource: string, error: AzureMonitorErrorish) => void
  89. ): AzureMonitorQuery {
  90. const azureMonitorQuery = query.azureMonitor;
  91. if (!azureMonitorQuery || azureMonitorQuery.resourceUri) {
  92. return query;
  93. }
  94. const { subscription } = query;
  95. const { resourceGroup, metricDefinition, resourceName } = azureMonitorQuery;
  96. if (!(subscription && resourceGroup && metricDefinition && resourceName)) {
  97. return query;
  98. }
  99. const metricDefinitionArray = metricDefinition.split('/');
  100. if (metricDefinitionArray.some((p) => templateSrv.replace(p).split('/').length > 2)) {
  101. // If a metric definition includes template variable with a subresource e.g.
  102. // Microsoft.Storage/storageAccounts/libraries, it's not possible to generate a valid
  103. // resource URI
  104. if (setError) {
  105. setError(
  106. 'Resource URI migration',
  107. React.createElement(
  108. 'div',
  109. null,
  110. `Failed to create resource URI. Validate the metric definition template variable against supported cases `,
  111. React.createElement(
  112. 'a',
  113. {
  114. href: 'https://grafana.com/docs/grafana/latest/datasources/azuremonitor/template-variables/',
  115. },
  116. 'here.'
  117. )
  118. )
  119. );
  120. }
  121. return query;
  122. }
  123. const resourceNameArray = resourceName.split('/');
  124. if (resourceNameArray.some((p) => templateSrv.replace(p).split('/').length > 1)) {
  125. // If a resource name includes template variable with a subresource e.g.
  126. // abc123/def456, it's not possible to generate a valid resource URI
  127. if (setError) {
  128. setError(
  129. 'Resource URI migration',
  130. React.createElement(
  131. 'div',
  132. null,
  133. `Failed to create resource URI. Validate the resource name template variable against supported cases `,
  134. React.createElement(
  135. 'a',
  136. {
  137. href: 'https://grafana.com/docs/grafana/latest/datasources/azuremonitor/template-variables/',
  138. },
  139. 'here.'
  140. )
  141. )
  142. );
  143. }
  144. return query;
  145. }
  146. const resourceUri = UrlBuilder.buildResourceUri(
  147. subscription,
  148. resourceGroup,
  149. metricDefinition,
  150. resourceName,
  151. templateSrv
  152. );
  153. return {
  154. ...query,
  155. azureMonitor: {
  156. ...azureMonitorQuery,
  157. resourceUri,
  158. },
  159. };
  160. }
  161. function migrateDimensionFilterToArray(query: AzureMonitorQuery): AzureMonitorQuery {
  162. const azureMonitorQuery = query.azureMonitor;
  163. if (!azureMonitorQuery) {
  164. return query;
  165. }
  166. const newFilters: AzureMetricDimension[] = [];
  167. const dimensionFilters = azureMonitorQuery.dimensionFilters;
  168. if (dimensionFilters && dimensionFilters.length > 0) {
  169. dimensionFilters.forEach((filter) => {
  170. const staticProps = { dimension: filter.dimension, operator: filter.operator };
  171. if (!filter.filters && filter.filter) {
  172. newFilters.push({ ...staticProps, filters: [filter.filter] });
  173. } else {
  174. let hasFilter = false;
  175. if (filter.filters && filter.filter) {
  176. for (const oldFilter of filter.filters) {
  177. if (filter.filter === oldFilter) {
  178. hasFilter = true;
  179. break;
  180. }
  181. }
  182. if (!hasFilter && filter.filter !== '*') {
  183. filter.filters.push(filter.filter);
  184. }
  185. newFilters.push({ ...staticProps, filters: filter.filters });
  186. }
  187. }
  188. });
  189. if (newFilters.length > 0) {
  190. return { ...query, azureMonitor: { ...azureMonitorQuery, dimensionFilters: newFilters } };
  191. }
  192. }
  193. return query;
  194. }
  195. // datasource.ts also contains some migrations, which have been moved to here. Unsure whether
  196. // they should also do all the other migrations...
  197. export function datasourceMigrations(query: AzureMonitorQuery, templateSrv: TemplateSrv): AzureMonitorQuery {
  198. let workingQuery = query;
  199. if (!workingQuery.queryType) {
  200. workingQuery = {
  201. ...workingQuery,
  202. queryType: AzureQueryType.AzureMonitor,
  203. };
  204. }
  205. if (workingQuery.queryType === AzureQueryType.AzureMonitor && workingQuery.azureMonitor) {
  206. workingQuery = migrateDimensionToDimensionFilter(workingQuery);
  207. workingQuery = migrateResourceUri(workingQuery, templateSrv);
  208. workingQuery = migrateDimensionFilterToArray(workingQuery);
  209. }
  210. return workingQuery;
  211. }