grafanaTemplateVariableFns.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import { isGUIDish } from './components/ResourcePicker/utils';
  2. import DataSource from './datasource';
  3. import { AzureMonitorQuery, AzureQueryType } from './types';
  4. import {
  5. AppInsightsGroupByQuery,
  6. AppInsightsMetricNameQuery,
  7. GrafanaTemplateVariableQuery,
  8. MetricDefinitionsQuery,
  9. MetricNamespaceQuery,
  10. MetricNamesQuery,
  11. ResourceGroupsQuery,
  12. ResourceNamesQuery,
  13. SubscriptionsQuery,
  14. WorkspacesQuery,
  15. } from './types/templateVariables';
  16. /*
  17. Grafana Template Variable Functions
  18. ex: Subscriptions()
  19. These are helper functions we have created and exposed to users to make the writing of template variables easier.
  20. Due to legacy reasons, we still need to parse strings to determine if a query is a Grafana Template Variable Function
  21. or if it's a KQL-type query
  22. */
  23. export const grafanaTemplateVariableFnMatches = (query: string) => {
  24. return {
  25. subscriptions: query.match(/^Subscriptions\(\)/i),
  26. resourceGroups: query.match(/^ResourceGroups\(\)/i),
  27. resourceGroupsWithSub: query.match(/^ResourceGroups\(([^\)]+?)(,\s?([^,]+?))?\)/i),
  28. metricDefinitions: query.match(/^Namespaces\(([^\)]+?)(,\s?([^,]+?))?\)/i),
  29. metricDefinitionsWithSub: query.match(/^Namespaces\(([^,]+?),\s?([^,]+?)\)/i),
  30. resourceNames: query.match(/^ResourceNames\(([^,]+?),\s?([^,]+?)\)/i),
  31. resourceNamesWithSub: query.match(/^ResourceNames\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/i),
  32. metricNamespace: query.match(/^MetricNamespace\(([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/i),
  33. metricNamespaceWithSub: query.match(/^metricnamespace\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/i),
  34. metricNames: query.match(/^MetricNames\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/i),
  35. metricNamesWithSub: query.match(/^MetricNames\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?(.+?)\)/i),
  36. appInsightsMetricNameQuery: query.match(/^AppInsightsMetricNames\(\)/i),
  37. appInsightsGroupByQuery: query.match(/^AppInsightsGroupBys\(([^\)]+?)(,\s?([^,]+?))?\)/i),
  38. workspacesQuery: query.match(/^workspaces\(\)/i),
  39. workspacesQueryWithSub: query.match(/^workspaces\(["']?([^\)]+?)["']?\)/i),
  40. };
  41. };
  42. const isGrafanaTemplateVariableFnQuery = (query: string) => {
  43. const matches: Record<string, RegExpMatchArray | null> = grafanaTemplateVariableFnMatches(query);
  44. return Object.keys(matches).some((key) => !!matches[key]);
  45. };
  46. const createGrafanaTemplateVariableQuery = (rawQuery: string, datasource: DataSource): AzureMonitorQuery => {
  47. const matchesForQuery = grafanaTemplateVariableFnMatches(rawQuery);
  48. const defaultSubscriptionId = datasource.azureMonitorDatasource.defaultSubscriptionId;
  49. const createGrafanaTemplateVariableDetails = (): GrafanaTemplateVariableQuery => {
  50. // deprecated app insights template variables (will most likely remove in grafana 9)
  51. if (matchesForQuery.appInsightsMetricNameQuery) {
  52. const queryDetails: AppInsightsMetricNameQuery = { rawQuery, kind: 'AppInsightsMetricNameQuery' };
  53. return queryDetails;
  54. }
  55. if (matchesForQuery.appInsightsGroupByQuery) {
  56. const queryDetails: AppInsightsGroupByQuery = {
  57. kind: 'AppInsightsGroupByQuery',
  58. rawQuery,
  59. metricName: matchesForQuery.appInsightsGroupByQuery[1],
  60. };
  61. return queryDetails;
  62. }
  63. if (matchesForQuery.subscriptions) {
  64. const queryDetails: SubscriptionsQuery = {
  65. kind: 'SubscriptionsQuery',
  66. rawQuery,
  67. };
  68. return queryDetails;
  69. }
  70. if (matchesForQuery.resourceGroupsWithSub) {
  71. const queryDetails: ResourceGroupsQuery = {
  72. kind: 'ResourceGroupsQuery',
  73. rawQuery,
  74. subscription: matchesForQuery.resourceGroupsWithSub[1],
  75. };
  76. return queryDetails;
  77. }
  78. if (matchesForQuery.resourceGroups && defaultSubscriptionId) {
  79. const queryDetails: ResourceGroupsQuery = {
  80. kind: 'ResourceGroupsQuery',
  81. rawQuery,
  82. subscription: defaultSubscriptionId,
  83. };
  84. return queryDetails;
  85. }
  86. if (matchesForQuery.metricDefinitionsWithSub) {
  87. const queryDetails: MetricDefinitionsQuery = {
  88. kind: 'MetricDefinitionsQuery',
  89. rawQuery,
  90. subscription: matchesForQuery.metricDefinitionsWithSub[1],
  91. resourceGroup: matchesForQuery.metricDefinitionsWithSub[2],
  92. };
  93. return queryDetails;
  94. }
  95. if (matchesForQuery.metricDefinitions && defaultSubscriptionId) {
  96. const queryDetails: MetricDefinitionsQuery = {
  97. kind: 'MetricDefinitionsQuery',
  98. rawQuery,
  99. subscription: defaultSubscriptionId,
  100. resourceGroup: matchesForQuery.metricDefinitions[1],
  101. };
  102. return queryDetails;
  103. }
  104. if (matchesForQuery.resourceNamesWithSub) {
  105. const queryDetails: ResourceNamesQuery = {
  106. kind: 'ResourceNamesQuery',
  107. rawQuery,
  108. subscription: matchesForQuery.resourceNamesWithSub[1],
  109. resourceGroup: matchesForQuery.resourceNamesWithSub[2],
  110. metricDefinition: matchesForQuery.resourceNamesWithSub[3],
  111. };
  112. return queryDetails;
  113. }
  114. if (matchesForQuery.resourceNames && defaultSubscriptionId) {
  115. const queryDetails: ResourceNamesQuery = {
  116. kind: 'ResourceNamesQuery',
  117. rawQuery,
  118. subscription: defaultSubscriptionId,
  119. resourceGroup: matchesForQuery.resourceNames[1],
  120. metricDefinition: matchesForQuery.resourceNames[2],
  121. };
  122. return queryDetails;
  123. }
  124. if (matchesForQuery.metricNamespaceWithSub) {
  125. const queryDetails: MetricNamespaceQuery = {
  126. kind: 'MetricNamespaceQuery',
  127. rawQuery,
  128. subscription: matchesForQuery.metricNamespaceWithSub[1],
  129. resourceGroup: matchesForQuery.metricNamespaceWithSub[2],
  130. metricDefinition: matchesForQuery.metricNamespaceWithSub[3],
  131. resourceName: matchesForQuery.metricNamespaceWithSub[4],
  132. };
  133. return queryDetails;
  134. }
  135. if (matchesForQuery.metricNamespace && defaultSubscriptionId) {
  136. const queryDetails: MetricNamespaceQuery = {
  137. kind: 'MetricNamespaceQuery',
  138. rawQuery,
  139. subscription: defaultSubscriptionId,
  140. resourceGroup: matchesForQuery.metricNamespace[1],
  141. metricDefinition: matchesForQuery.metricNamespace[2],
  142. resourceName: matchesForQuery.metricNamespace[3],
  143. };
  144. return queryDetails;
  145. }
  146. if (matchesForQuery.metricNames && defaultSubscriptionId) {
  147. if (matchesForQuery.metricNames[3].indexOf(',') === -1) {
  148. const queryDetails: MetricNamesQuery = {
  149. kind: 'MetricNamesQuery',
  150. rawQuery,
  151. subscription: defaultSubscriptionId,
  152. resourceGroup: matchesForQuery.metricNames[1],
  153. metricDefinition: matchesForQuery.metricNames[2],
  154. resourceName: matchesForQuery.metricNames[3],
  155. metricNamespace: matchesForQuery.metricNames[4],
  156. };
  157. return queryDetails;
  158. }
  159. }
  160. if (matchesForQuery.metricNamesWithSub) {
  161. const queryDetails: MetricNamesQuery = {
  162. kind: 'MetricNamesQuery',
  163. rawQuery,
  164. subscription: matchesForQuery.metricNamesWithSub[1],
  165. resourceGroup: matchesForQuery.metricNamesWithSub[2],
  166. metricDefinition: matchesForQuery.metricNamesWithSub[3],
  167. resourceName: matchesForQuery.metricNamesWithSub[4],
  168. metricNamespace: matchesForQuery.metricNamesWithSub[5],
  169. };
  170. return queryDetails;
  171. }
  172. if (matchesForQuery.workspacesQueryWithSub) {
  173. const queryDetails: WorkspacesQuery = {
  174. kind: 'WorkspacesQuery',
  175. rawQuery,
  176. subscription: (matchesForQuery.workspacesQueryWithSub[1] || '').trim(),
  177. };
  178. return queryDetails;
  179. }
  180. if (matchesForQuery.workspacesQuery && defaultSubscriptionId) {
  181. const queryDetails: WorkspacesQuery = {
  182. kind: 'WorkspacesQuery',
  183. rawQuery,
  184. subscription: defaultSubscriptionId,
  185. };
  186. return queryDetails;
  187. }
  188. // fallback
  189. const queryDetails: SubscriptionsQuery = { kind: 'SubscriptionsQuery', rawQuery };
  190. return queryDetails;
  191. };
  192. const query: AzureMonitorQuery = {
  193. refId: 'A',
  194. queryType: AzureQueryType.GrafanaTemplateVariableFn,
  195. grafanaTemplateVariableFn: createGrafanaTemplateVariableDetails(),
  196. subscription: defaultSubscriptionId,
  197. };
  198. return query;
  199. };
  200. const createLogAnalyticsTemplateVariableQuery = async (
  201. rawQuery: string,
  202. datasource: DataSource
  203. ): Promise<AzureMonitorQuery> => {
  204. const defaultSubscriptionId = datasource.azureMonitorDatasource.defaultSubscriptionId;
  205. let resource = '';
  206. // if there's an existing query, we try to get the resourcesuri from a deprecated default workspace
  207. // a note this is very similar logic to what is used in useMigrations but moved out of the react-hook land
  208. if (rawQuery) {
  209. const defaultWorkspaceId = datasource.azureLogAnalyticsDatasource.getDeprecatedDefaultWorkSpace();
  210. if (defaultWorkspaceId) {
  211. const isWorkspaceGUID = isGUIDish(defaultWorkspaceId);
  212. if (isWorkspaceGUID) {
  213. resource = await datasource.resourcePickerData.getResourceURIFromWorkspace(defaultWorkspaceId);
  214. } else {
  215. resource = defaultWorkspaceId;
  216. }
  217. } else {
  218. const maybeFirstWorkspace = await datasource.azureLogAnalyticsDatasource.getFirstWorkspace();
  219. resource = maybeFirstWorkspace || '';
  220. }
  221. }
  222. return {
  223. refId: 'A',
  224. queryType: AzureQueryType.LogAnalytics,
  225. azureLogAnalytics: {
  226. query: rawQuery,
  227. resource,
  228. },
  229. subscription: defaultSubscriptionId,
  230. };
  231. };
  232. export const migrateStringQueriesToObjectQueries = async (
  233. rawQuery: string | AzureMonitorQuery,
  234. options: { datasource: DataSource }
  235. ): Promise<AzureMonitorQuery> => {
  236. // no need to migrate already migrated queries
  237. if (typeof rawQuery !== 'string') {
  238. return rawQuery;
  239. }
  240. return isGrafanaTemplateVariableFnQuery(rawQuery)
  241. ? createGrafanaTemplateVariableQuery(rawQuery, options.datasource)
  242. : createLogAnalyticsTemplateVariableQuery(rawQuery, options.datasource);
  243. };