mirror of https://github.com/grafana/grafana
AzureMonitor: Apply query migrations in QueryEditor (#37704)
* move query migrations out of the angular controller * Migrate queries in QueryEditor * finish up migrations * update deprecated comment * remove commentpull/37475/head
parent
6aba592741
commit
afabc617ed
@ -1,34 +0,0 @@ |
|||||||
import { useEffect, useMemo } from 'react'; |
|
||||||
import { AzureMonitorQuery, AzureQueryType } from '../../types'; |
|
||||||
|
|
||||||
const DEFAULT_QUERY_TYPE = AzureQueryType.AzureMonitor; |
|
||||||
|
|
||||||
const createQueryWithDefaults = (query: AzureMonitorQuery) => { |
|
||||||
// A quick and easy way to set just the default query type. If we want to set any other defaults,
|
|
||||||
// we might want to look into something more robust
|
|
||||||
if (!query.queryType) { |
|
||||||
return { |
|
||||||
...query, |
|
||||||
queryType: query.queryType ?? DEFAULT_QUERY_TYPE, |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
return query; |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns queries with some defaults, and calls onChange function to notify if it changes |
|
||||||
*/ |
|
||||||
const useDefaultQuery = (query: AzureMonitorQuery, onChangeQuery: (newQuery: AzureMonitorQuery) => void) => { |
|
||||||
const queryWithDefaults = useMemo(() => createQueryWithDefaults(query), [query]); |
|
||||||
|
|
||||||
useEffect(() => { |
|
||||||
if (queryWithDefaults !== query) { |
|
||||||
onChangeQuery(queryWithDefaults); |
|
||||||
} |
|
||||||
}, [queryWithDefaults, query, onChangeQuery]); |
|
||||||
|
|
||||||
return queryWithDefaults; |
|
||||||
}; |
|
||||||
|
|
||||||
export default useDefaultQuery; |
|
||||||
@ -0,0 +1,36 @@ |
|||||||
|
import { useEffect, useMemo } from 'react'; |
||||||
|
import { defaults } from 'lodash'; |
||||||
|
import { AzureMonitorQuery, AzureQueryType } from '../../types'; |
||||||
|
import deepEqual from 'fast-deep-equal'; |
||||||
|
import migrateQuery from '../../utils/migrateQuery'; |
||||||
|
|
||||||
|
const DEFAULT_QUERY = { |
||||||
|
queryType: AzureQueryType.AzureMonitor, |
||||||
|
}; |
||||||
|
|
||||||
|
const prepareQuery = (query: AzureMonitorQuery) => { |
||||||
|
// Note: _.defaults does not apply default values deeply.
|
||||||
|
const withDefaults = defaults({}, query, DEFAULT_QUERY); |
||||||
|
const migratedQuery = migrateQuery(withDefaults); |
||||||
|
|
||||||
|
// If we didn't make any changes to the object, then return the original object to keep the
|
||||||
|
// identity the same, and not trigger any other useEffects or anything.
|
||||||
|
return deepEqual(migratedQuery, query) ? query : migratedQuery; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns queries with some defaults + migrations, and calls onChange function to notify if it changes |
||||||
|
*/ |
||||||
|
const usePreparedQuery = (query: AzureMonitorQuery, onChangeQuery: (newQuery: AzureMonitorQuery) => void) => { |
||||||
|
const preparedQuery = useMemo(() => prepareQuery(query), [query]); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (preparedQuery !== query) { |
||||||
|
onChangeQuery(preparedQuery); |
||||||
|
} |
||||||
|
}, [preparedQuery, query, onChangeQuery]); |
||||||
|
|
||||||
|
return preparedQuery; |
||||||
|
}; |
||||||
|
|
||||||
|
export default usePreparedQuery; |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
import { AzureMonitorQuery, AzureQueryType } from '../types'; |
||||||
|
import migrateQuery from './migrateQuery'; |
||||||
|
|
||||||
|
const modernMetricsQuery: AzureMonitorQuery = { |
||||||
|
appInsights: { dimension: [], metricName: 'select', timeGrain: 'auto' }, |
||||||
|
azureLogAnalytics: { |
||||||
|
query: |
||||||
|
'//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', |
||||||
|
resultFormat: 'time_series', |
||||||
|
workspace: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2', |
||||||
|
}, |
||||||
|
azureMonitor: { |
||||||
|
aggregation: 'Average', |
||||||
|
alias: '{{ dimensionvalue }}', |
||||||
|
allowedTimeGrainsMs: [60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000], |
||||||
|
dimensionFilters: [{ dimension: 'dependency/success', filter: '', operator: 'eq' }], |
||||||
|
metricDefinition: 'microsoft.insights/components', |
||||||
|
metricName: 'dependencies/duration', |
||||||
|
metricNamespace: 'microsoft.insights/components', |
||||||
|
resourceGroup: 'cloud-datasources', |
||||||
|
resourceName: 'AppInsightsTestData', |
||||||
|
timeGrain: 'PT5M', |
||||||
|
top: '10', |
||||||
|
}, |
||||||
|
azureResourceGraph: { resultFormat: 'table' }, |
||||||
|
insightsAnalytics: { query: '', resultFormat: 'time_series' }, |
||||||
|
queryType: AzureQueryType.AzureMonitor, |
||||||
|
refId: 'A', |
||||||
|
subscription: '44693801-6ee6-49de-9b2d-9106972f9572', |
||||||
|
subscriptions: ['44693801-6ee6-49de-9b2d-9106972f9572'], |
||||||
|
}; |
||||||
|
|
||||||
|
describe('AzureMonitor: migrateQuery', () => { |
||||||
|
it('modern queries should not change', () => { |
||||||
|
const result = migrateQuery(modernMetricsQuery); |
||||||
|
|
||||||
|
// MUST use .toBe because we want to assert that the identity of unmigrated queries remains the same
|
||||||
|
expect(modernMetricsQuery).toBe(result); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,165 @@ |
|||||||
|
import { AzureMonitorQuery, AzureQueryType } from '../types'; |
||||||
|
import TimegrainConverter from '../time_grain_converter'; |
||||||
|
import { |
||||||
|
appendDimensionFilter, |
||||||
|
setTimeGrain as setMetricsTimeGrain, |
||||||
|
} from '../components/MetricsQueryEditor/setQueryValue'; |
||||||
|
import { setKustoQuery } from '../components/LogsQueryEditor/setQueryValue'; |
||||||
|
|
||||||
|
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 = migrateApplicationInsightsDimensions(workingQuery); |
||||||
|
workingQuery = migrateMetricsDimensionFilters(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; |
||||||
|
} |
||||||
|
|
||||||
|
if (workingQuery.appInsights?.timeGrainUnit && workingQuery.appInsights.timeGrain !== 'auto') { |
||||||
|
const appInsights = { |
||||||
|
...workingQuery.appInsights, |
||||||
|
}; |
||||||
|
|
||||||
|
if (workingQuery.appInsights.timeGrainCount) { |
||||||
|
appInsights.timeGrain = TimegrainConverter.createISO8601Duration( |
||||||
|
workingQuery.appInsights.timeGrainCount, |
||||||
|
workingQuery.appInsights.timeGrainUnit |
||||||
|
); |
||||||
|
} else { |
||||||
|
appInsights.timeGrainCount = workingQuery.appInsights.timeGrain; |
||||||
|
|
||||||
|
if (workingQuery.appInsights.timeGrain) { |
||||||
|
appInsights.timeGrain = TimegrainConverter.createISO8601Duration( |
||||||
|
workingQuery.appInsights.timeGrain, |
||||||
|
workingQuery.appInsights.timeGrainUnit |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
workingQuery = { |
||||||
|
...workingQuery, |
||||||
|
appInsights: appInsights, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
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 migrateApplicationInsightsDimensions(query: AzureMonitorQuery): AzureMonitorQuery { |
||||||
|
const dimension = query?.appInsights?.dimension as unknown; |
||||||
|
|
||||||
|
if (dimension && typeof dimension === 'string') { |
||||||
|
return { |
||||||
|
...query, |
||||||
|
appInsights: { |
||||||
|
...query.appInsights, |
||||||
|
dimension: [dimension], |
||||||
|
}, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
return query; |
||||||
|
} |
||||||
|
|
||||||
|
// Exported because its also used directly in the datasource.ts for some reason
|
||||||
|
function migrateMetricsDimensionFilters(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; |
||||||
|
} |
||||||
|
|
||||||
|
// 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 === AzureQueryType.ApplicationInsights && workingQuery.appInsights?.rawQuery) { |
||||||
|
workingQuery = { |
||||||
|
...workingQuery, |
||||||
|
queryType: AzureQueryType.InsightsAnalytics, |
||||||
|
appInsights: undefined, |
||||||
|
insightsAnalytics: { |
||||||
|
query: workingQuery.appInsights.rawQuery, |
||||||
|
resultFormat: 'time_series', |
||||||
|
}, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
if (!workingQuery.queryType) { |
||||||
|
workingQuery = { |
||||||
|
...workingQuery, |
||||||
|
queryType: AzureQueryType.AzureMonitor, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
if (workingQuery.queryType === AzureQueryType.AzureMonitor && workingQuery.azureMonitor) { |
||||||
|
workingQuery = migrateMetricsDimensionFilters(workingQuery); |
||||||
|
} |
||||||
|
|
||||||
|
return workingQuery; |
||||||
|
} |
||||||
Loading…
Reference in new issue