migrateQuery.test.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import React from 'react';
  2. import { getTemplateSrv } from '@grafana/runtime';
  3. import { AzureMetricDimension, AzureMonitorErrorish, AzureMonitorQuery, AzureQueryType } from '../types';
  4. import migrateQuery from './migrateQuery';
  5. let replaceMock = jest.fn().mockImplementation((s: string) => s);
  6. jest.mock('@grafana/runtime', () => {
  7. const original = jest.requireActual('@grafana/runtime');
  8. return {
  9. ...original,
  10. getTemplateSrv: () => ({
  11. replace: replaceMock,
  12. }),
  13. };
  14. });
  15. let templateSrv = getTemplateSrv();
  16. let setErrorMock = jest.fn();
  17. const azureMonitorQueryV7 = {
  18. appInsights: { dimension: [], metricName: 'select', timeGrain: 'auto' },
  19. azureLogAnalytics: {
  20. query:
  21. '//change this example to create your own time series query\n<table name> //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by <group by column>, bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc',
  22. resultFormat: 'time_series',
  23. workspace: 'mock-workspace-id',
  24. },
  25. azureMonitor: {
  26. aggregation: 'Average',
  27. allowedTimeGrainsMs: [60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000],
  28. dimensionFilters: [{ dimension: 'dependency/success', filter: '', operator: 'eq' }],
  29. metricDefinition: 'microsoft.insights/components',
  30. metricName: 'dependencies/duration',
  31. metricNamespace: 'microsoft.insights/components',
  32. resourceGroup: 'cloud-datasources',
  33. resourceName: 'AppInsightsTestData',
  34. timeGrain: 'auto',
  35. top: '10',
  36. },
  37. insightsAnalytics: {
  38. query: '',
  39. resultFormat: 'time_series',
  40. },
  41. queryType: AzureQueryType.AzureMonitor,
  42. refId: 'A',
  43. subscription: '44693801-6ee6-49de-9b2d-9106972f9572',
  44. };
  45. const azureMonitorQueryV8 = {
  46. azureMonitor: {
  47. aggregation: 'Average',
  48. dimensionFilters: [],
  49. metricDefinition: 'microsoft.insights/components',
  50. metricName: 'dependencies/duration',
  51. metricNamespace: 'microsoft.insights/components',
  52. resourceGroup: 'cloud-datasources',
  53. resourceName: 'AppInsightsTestData',
  54. timeGrain: 'auto',
  55. },
  56. datasource: {
  57. type: 'grafana-azure-monitor-datasource',
  58. uid: 'sD-ZuB87k',
  59. },
  60. queryType: AzureQueryType.AzureMonitor,
  61. refId: 'A',
  62. subscription: '44693801-6ee6-49de-9b2d-9106972f9572',
  63. };
  64. const modernMetricsQuery: AzureMonitorQuery = {
  65. azureLogAnalytics: {
  66. query:
  67. '//change this example to create your own time series query\n<table name> //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by <group by column>, bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc',
  68. resultFormat: 'time_series',
  69. workspace: 'mock-workspace-id',
  70. },
  71. azureMonitor: {
  72. aggregation: 'Average',
  73. alias: '{{ dimensionvalue }}',
  74. allowedTimeGrainsMs: [60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000],
  75. dimensionFilters: [{ dimension: 'dependency/success', filters: ['*'], operator: 'eq' }],
  76. metricDefinition: 'microsoft.insights/components',
  77. metricName: 'dependencies/duration',
  78. metricNamespace: 'microsoft.insights/components',
  79. resourceGroup: 'cloud-datasources',
  80. resourceName: 'AppInsightsTestData',
  81. resourceUri:
  82. '/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/cloud-datasources/providers/microsoft.insights/components/AppInsightsTestData',
  83. timeGrain: 'PT5M',
  84. top: '10',
  85. },
  86. azureResourceGraph: { resultFormat: 'table' },
  87. queryType: AzureQueryType.AzureMonitor,
  88. refId: 'A',
  89. subscription: '44693801-6ee6-49de-9b2d-9106972f9572',
  90. subscriptions: ['44693801-6ee6-49de-9b2d-9106972f9572'],
  91. };
  92. describe('AzureMonitor: migrateQuery', () => {
  93. it('modern queries should not change', () => {
  94. const result = migrateQuery(modernMetricsQuery, templateSrv, setErrorMock);
  95. // MUST use .toBe because we want to assert that the identity of unmigrated queries remains the same
  96. expect(modernMetricsQuery).toBe(result);
  97. });
  98. describe('migrating from a v7 query to the latest query version', () => {
  99. it('should build a resource uri', () => {
  100. const result = migrateQuery(azureMonitorQueryV7, templateSrv, setErrorMock);
  101. expect(result).toMatchObject(
  102. expect.objectContaining({
  103. azureMonitor: expect.objectContaining({
  104. resourceUri:
  105. '/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/cloud-datasources/providers/microsoft.insights/components/AppInsightsTestData',
  106. }),
  107. })
  108. );
  109. });
  110. });
  111. describe('migrating from a v8 query to the latest query version', () => {
  112. it('should build a resource uri', () => {
  113. const result = migrateQuery(azureMonitorQueryV8, templateSrv, setErrorMock);
  114. expect(result).toMatchObject(
  115. expect.objectContaining({
  116. azureMonitor: expect.objectContaining({
  117. resourceUri:
  118. '/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/cloud-datasources/providers/microsoft.insights/components/AppInsightsTestData',
  119. }),
  120. })
  121. );
  122. });
  123. it('should not build a resource uri with an unsupported namespace template variable', () => {
  124. replaceMock = jest
  125. .fn()
  126. .mockImplementation((s: string) => s.replace('$ns', 'Microsoft.Storage/storageAccounts/tableServices'));
  127. setErrorMock = jest
  128. .fn()
  129. .mockImplementation((errorSource: string, error: AzureMonitorErrorish) => 'Template Var error');
  130. const errorElement = React.createElement(
  131. 'div',
  132. null,
  133. `Failed to create resource URI. Validate the metric definition 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. templateSrv = getTemplateSrv();
  143. const query = {
  144. ...azureMonitorQueryV8,
  145. azureMonitor: {
  146. ...azureMonitorQueryV8.azureMonitor,
  147. metricDefinition: '$ns',
  148. },
  149. };
  150. const result = migrateQuery(query, templateSrv, setErrorMock);
  151. expect(result.azureMonitor?.resourceUri).toBeUndefined();
  152. expect(setErrorMock).toHaveBeenCalledWith('Resource URI migration', errorElement);
  153. });
  154. it('should not build a resource uri with unsupported resource name template variable', () => {
  155. replaceMock = jest.fn().mockImplementation((s: string) => s.replace('$resource', 'resource/default'));
  156. setErrorMock = jest
  157. .fn()
  158. .mockImplementation((errorSource: string, error: AzureMonitorErrorish) => 'Template Var error');
  159. const errorElement = React.createElement(
  160. 'div',
  161. null,
  162. `Failed to create resource URI. Validate the resource name template variable against supported cases `,
  163. React.createElement(
  164. 'a',
  165. {
  166. href: 'https://grafana.com/docs/grafana/latest/datasources/azuremonitor/template-variables/',
  167. },
  168. 'here.'
  169. )
  170. );
  171. templateSrv = getTemplateSrv();
  172. const query = {
  173. ...azureMonitorQueryV8,
  174. azureMonitor: {
  175. ...azureMonitorQueryV8.azureMonitor,
  176. resourceName: '$resource',
  177. },
  178. };
  179. const result = migrateQuery(query, templateSrv, setErrorMock);
  180. expect(result.azureMonitor?.resourceUri).toBeUndefined();
  181. expect(setErrorMock).toHaveBeenCalledWith('Resource URI migration', errorElement);
  182. });
  183. });
  184. describe('migrating from a v9 query to the latest query version', () => {
  185. it('will not change valid dimension filters', () => {
  186. const dimensionFilters: AzureMetricDimension[] = [
  187. { dimension: 'TestDimension', operator: 'eq', filters: ['testFilter'] },
  188. ];
  189. const result = migrateQuery(
  190. { ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } },
  191. templateSrv,
  192. setErrorMock
  193. );
  194. expect(result).toMatchObject(
  195. expect.objectContaining({
  196. azureMonitor: expect.objectContaining({
  197. dimensionFilters,
  198. }),
  199. })
  200. );
  201. });
  202. it('correctly updates old filter containing wildcard', () => {
  203. const dimensionFilters: AzureMetricDimension[] = [{ dimension: 'TestDimension', operator: 'eq', filter: '*' }];
  204. const result = migrateQuery(
  205. { ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } },
  206. templateSrv,
  207. setErrorMock
  208. );
  209. expect(result).toMatchObject(
  210. expect.objectContaining({
  211. azureMonitor: expect.objectContaining({
  212. dimensionFilters: [
  213. { dimension: dimensionFilters[0].dimension, operator: dimensionFilters[0].operator, filters: ['*'] },
  214. ],
  215. }),
  216. })
  217. );
  218. });
  219. it('correctly updates old filter containing value', () => {
  220. const dimensionFilters: AzureMetricDimension[] = [{ dimension: 'TestDimension', operator: 'eq', filter: 'test' }];
  221. const result = migrateQuery(
  222. { ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } },
  223. templateSrv,
  224. setErrorMock
  225. );
  226. expect(result).toMatchObject(
  227. expect.objectContaining({
  228. azureMonitor: expect.objectContaining({
  229. dimensionFilters: [
  230. { dimension: dimensionFilters[0].dimension, operator: dimensionFilters[0].operator, filters: ['test'] },
  231. ],
  232. }),
  233. })
  234. );
  235. });
  236. it('correctly ignores wildcard if filters has a value', () => {
  237. const dimensionFilters: AzureMetricDimension[] = [
  238. { dimension: 'TestDimension', operator: 'eq', filter: '*', filters: ['testFilter'] },
  239. ];
  240. const result = migrateQuery(
  241. { ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } },
  242. templateSrv,
  243. setErrorMock
  244. );
  245. expect(result).toMatchObject(
  246. expect.objectContaining({
  247. azureMonitor: expect.objectContaining({
  248. dimensionFilters: [
  249. {
  250. dimension: dimensionFilters[0].dimension,
  251. operator: dimensionFilters[0].operator,
  252. filters: ['testFilter'],
  253. },
  254. ],
  255. }),
  256. })
  257. );
  258. });
  259. it('correctly ignores duplicates', () => {
  260. const dimensionFilters: AzureMetricDimension[] = [
  261. { dimension: 'TestDimension', operator: 'eq', filter: 'testFilter', filters: ['testFilter'] },
  262. ];
  263. const result = migrateQuery(
  264. { ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } },
  265. templateSrv,
  266. setErrorMock
  267. );
  268. expect(result).toMatchObject(
  269. expect.objectContaining({
  270. azureMonitor: expect.objectContaining({
  271. dimensionFilters: [
  272. {
  273. dimension: dimensionFilters[0].dimension,
  274. operator: dimensionFilters[0].operator,
  275. filters: ['testFilter'],
  276. },
  277. ],
  278. }),
  279. })
  280. );
  281. });
  282. });
  283. });