grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts
Ashley Harrison 3004a650b5
Chore: Fix more TypeScript strict errors (#37066)
* Chore: Fix more TypeScript strict errors

* whoops
2021-07-22 08:09:55 +01:00

570 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { defaultsDeep, find, map, isString } from 'lodash';
import { QueryCtrl } from 'app/plugins/sdk';
import TimegrainConverter from './time_grain_converter';
import './editor/editor_component';
import { TemplateSrv } from '@grafana/runtime';
import { auto } from 'angular';
import { DataFrame, PanelEvents } from '@grafana/data';
import { AzureQueryType, AzureMetricQuery, AzureMonitorQuery } from './types';
import { convertTimeGrainsToMs } from './utils/common';
import Datasource from './datasource';
export interface ResultFormat {
text: string;
value: string;
}
export class AzureMonitorQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html';
defaultDropdownValue = 'select';
dummyDiminsionString = '+';
queryQueryTypeOptions = [
{ id: AzureQueryType.AzureMonitor, label: 'Metrics' },
{ id: AzureQueryType.LogAnalytics, label: 'Logs' },
{ id: AzureQueryType.ApplicationInsights, label: 'Application Insights' },
{ id: AzureQueryType.InsightsAnalytics, label: 'Insights Analytics' },
{ id: AzureQueryType.AzureResourceGraph, label: 'Azure Resource Graph' },
];
// Query types that have been migrated to React
reactQueryEditors = [
AzureQueryType.AzureMonitor,
AzureQueryType.LogAnalytics,
AzureQueryType.ApplicationInsights,
AzureQueryType.InsightsAnalytics,
AzureQueryType.AzureResourceGraph,
];
// target: AzureMonitorQuery;
declare target: {
// should be: AzureMonitorQuery
refId: string;
queryType: AzureQueryType;
subscription: string;
subscriptions: string[];
azureMonitor: AzureMetricQuery;
azureLogAnalytics: {
query: string;
resultFormat: string;
workspace: string;
};
azureResourceGraph: {
query: string;
resultFormat: string;
};
appInsights: {
// metric style query when rawQuery == false
metricName: string;
dimension: any;
dimensionFilter: string;
dimensions: string[];
aggOptions: string[];
aggregation: string;
timeGrainType: string;
timeGrainCount: string;
timeGrainUnit: string;
timeGrain: string;
timeGrains: Array<{ text: string; value: string }>;
allowedTimeGrainsMs: number[];
};
insightsAnalytics: {
query: any;
resultFormat: string;
};
};
defaults = {
queryType: 'Azure Monitor',
azureMonitor: {
resourceGroup: undefined,
metricDefinition: undefined,
resourceName: undefined,
metricNamespace: undefined,
metricName: undefined,
dimensionFilter: '*',
timeGrain: 'auto',
top: '10',
aggOptions: [] as string[],
timeGrains: [] as string[],
},
azureLogAnalytics: {
query: [
'//change this example to create your own time series query',
'<table name> ' +
'//the table to query (e.g. Usage, Heartbeat, Perf)',
'| where $__timeFilter(TimeGenerated) ' +
'//this is a macro used to show the full charts time range, choose the datetime column here',
'| 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.',
'| order by TimeGenerated asc',
].join('\n'),
resultFormat: 'time_series',
workspace:
this.datasource && this.datasource.azureLogAnalyticsDatasource
? this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace
: '',
},
azureResourceGraph: {
resultFormat: 'table',
},
appInsights: {
metricName: this.defaultDropdownValue,
// dimension: [],
timeGrain: 'auto',
},
insightsAnalytics: {
query: '',
resultFormat: 'time_series',
},
};
resultFormats: ResultFormat[];
workspaces: any[] = [];
showHelp = false;
showLastQuery = false;
lastQuery = '';
lastQueryError?: string;
subscriptions: Array<{ text: string; value: string }> = [];
/** @ngInject */
constructor($scope: any, $injector: auto.IInjectorService, private templateSrv: TemplateSrv) {
super($scope, $injector);
defaultsDeep(this.target, this.defaults);
this.migrateTimeGrains();
this.migrateToFromTimes();
this.migrateToDefaultNamespace();
this.migrateApplicationInsightsKeys();
this.migrateApplicationInsightsDimensions();
migrateMetricsDimensionFilters(this.target.azureMonitor);
this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope);
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
this.resultFormats = [
{ text: 'Time series', value: 'time_series' },
{ text: 'Table', value: 'table' },
];
this.getSubscriptions();
if (this.target.queryType === 'Azure Log Analytics') {
this.getWorkspaces();
}
}
onDataReceived(dataList: DataFrame[]) {
this.lastQueryError = undefined;
this.lastQuery = '';
const anySeriesFromQuery: any = find(dataList, { refId: this.target.refId });
if (anySeriesFromQuery && anySeriesFromQuery.meta) {
this.lastQuery = anySeriesFromQuery.meta.query;
}
}
onDataError(err: any) {
this.handleQueryCtrlError(err);
}
handleQueryCtrlError(err: any) {
if (err.query && err.query.refId && err.query.refId !== this.target.refId) {
return;
}
if (err.error && err.error.data && err.error.data.error && err.error.data.error.innererror) {
if (err.error.data.error.innererror.innererror) {
this.lastQueryError = err.error.data.error.innererror.innererror.message;
} else {
this.lastQueryError = err.error.data.error.innererror.message;
}
} else if (err.error && err.error.data && err.error.data.error) {
this.lastQueryError = err.error.data.error.message;
} else if (err.error && err.error.data) {
this.lastQueryError = err.error.data.message;
} else if (err.data && err.data.error) {
this.lastQueryError = err.data.error.message;
} else if (err.data && err.data.message) {
this.lastQueryError = err.data.message;
} else {
this.lastQueryError = err;
}
}
migrateTimeGrains() {
if (this.target.azureMonitor.timeGrainUnit) {
if (this.target.azureMonitor.timeGrain !== 'auto') {
this.target.azureMonitor.timeGrain = TimegrainConverter.createISO8601Duration(
this.target.azureMonitor.timeGrain ?? 'auto',
this.target.azureMonitor.timeGrainUnit
);
}
delete this.target.azureMonitor.timeGrainUnit;
}
if (this.target.appInsights.timeGrainUnit) {
if (this.target.appInsights.timeGrain !== 'auto') {
if (this.target.appInsights.timeGrainCount) {
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
this.target.appInsights.timeGrainCount,
this.target.appInsights.timeGrainUnit
);
} else {
this.target.appInsights.timeGrainCount = this.target.appInsights.timeGrain;
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
this.target.appInsights.timeGrain,
this.target.appInsights.timeGrainUnit
);
}
}
}
const oldAzureTimeGrains = (this.target.azureMonitor as any).timeGrains;
if (
oldAzureTimeGrains &&
oldAzureTimeGrains.length > 0 &&
(!this.target.azureMonitor.allowedTimeGrainsMs || this.target.azureMonitor.allowedTimeGrainsMs.length === 0)
) {
this.target.azureMonitor.allowedTimeGrainsMs = convertTimeGrainsToMs(oldAzureTimeGrains);
}
if (
this.target.appInsights.timeGrains &&
this.target.appInsights.timeGrains.length > 0 &&
(!this.target.appInsights.allowedTimeGrainsMs || this.target.appInsights.allowedTimeGrainsMs.length === 0)
) {
this.target.appInsights.allowedTimeGrainsMs = convertTimeGrainsToMs(this.target.appInsights.timeGrains);
}
}
migrateToFromTimes() {
this.target.azureLogAnalytics.query = this.target.azureLogAnalytics.query.replace(/\$__from\s/gi, '$__timeFrom() ');
this.target.azureLogAnalytics.query = this.target.azureLogAnalytics.query.replace(/\$__to\s/gi, '$__timeTo() ');
}
async migrateToDefaultNamespace() {
if (
this.target.azureMonitor.metricNamespace &&
this.target.azureMonitor.metricNamespace !== this.defaultDropdownValue &&
this.target.azureMonitor.metricDefinition
) {
return;
}
this.target.azureMonitor.metricNamespace = this.target.azureMonitor.metricDefinition;
}
migrateApplicationInsightsKeys(): void {
const appInsights = this.target.appInsights as any;
// Migrate old app insights data keys to match other datasources
const mappings = {
xaxis: 'timeColumn',
yaxis: 'valueColumn',
spliton: 'segmentColumn',
groupBy: 'dimension',
groupByOptions: 'dimensions',
filter: 'dimensionFilter',
} as { [old: string]: string };
for (const old in mappings) {
if (appInsights[old]) {
appInsights[mappings[old]] = appInsights[old];
delete appInsights[old];
}
}
}
migrateApplicationInsightsDimensions() {
const { appInsights } = this.target;
if (!appInsights.dimension) {
appInsights.dimension = [];
}
if (isString(appInsights.dimension)) {
appInsights.dimension = [appInsights.dimension as string];
}
}
replace = (variable: string) => {
return this.templateSrv.replace(variable, this.panelCtrl.panel.scopedVars);
};
onQueryTypeChange() {
if (this.target.queryType === 'Azure Log Analytics') {
return this.getWorkspaces();
}
}
getSubscriptions() {
if (!this.datasource.azureMonitorDatasource.isConfigured()) {
return;
}
// assert the type
if (!(this.datasource instanceof Datasource)) {
return;
}
return this.datasource.azureMonitorDatasource.getSubscriptions().then((subscriptions) => {
// We changed the format in the datasource for the new react stuff, so here we change it back
const subs = subscriptions.map((v) => ({
text: `${v.text} - ${v.value}`,
value: v.value,
}));
this.subscriptions = subs;
if (!this.target.subscription && this.target.queryType === 'Azure Monitor') {
this.target.subscription = this.datasource.azureMonitorDatasource.subscriptionId;
} else if (!this.target.subscription && this.target.queryType === 'Azure Log Analytics') {
this.target.subscription = this.datasource.azureLogAnalyticsDatasource.subscriptionId;
}
if (!this.target.subscription && this.subscriptions.length > 0) {
this.target.subscription = this.subscriptions[0].value;
}
if (!this.target.subscriptions) {
this.target.subscriptions = subscriptions.map((sub) => sub.value);
}
return this.subscriptions;
});
}
onSubscriptionChange() {
if (this.target.queryType === 'Azure Log Analytics') {
return this.getWorkspaces();
}
}
generateAutoUnits(timeGrain: string, timeGrains: Array<{ value: string }>) {
if (timeGrain === 'auto') {
return TimegrainConverter.findClosestTimeGrain(
'1m',
map(timeGrains, (o) => TimegrainConverter.createKbnUnitFromISO8601Duration(o.value)) || [
'1m',
'5m',
'15m',
'30m',
'1h',
'6h',
'12h',
'1d',
]
);
}
return '';
}
getAzureMonitorAutoInterval() {
return this.generateAutoUnits(
this.target.azureMonitor.timeGrain ?? 'auto',
(this.target.azureMonitor as any).timeGrains
);
}
getApplicationInsightAutoInterval() {
return this.generateAutoUnits(this.target.appInsights.timeGrain, this.target.appInsights.timeGrains);
}
azureMonitorAddDimensionFilter() {
this.target.azureMonitor = this.target.azureMonitor ?? {};
this.target.azureMonitor.dimensionFilters = this.target.azureMonitor.dimensionFilters ?? [];
this.target.azureMonitor.dimensionFilters.push({
dimension: '',
operator: 'eq',
filter: '',
});
}
azureMonitorRemoveDimensionFilter(index: number) {
this.target.azureMonitor = this.target.azureMonitor ?? {};
this.target.azureMonitor.dimensionFilters = this.target.azureMonitor.dimensionFilters ?? [];
this.target.azureMonitor.dimensionFilters.splice(index, 1);
this.refresh();
}
/* Azure Log Analytics */
getWorkspaces = () => {
return this.datasource.azureLogAnalyticsDatasource
.getWorkspaces(this.target.subscription)
.then((list: any[]) => {
this.workspaces = list;
if (list.length > 0 && !this.target.azureLogAnalytics.workspace) {
if (this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace) {
this.target.azureLogAnalytics.workspace = this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace;
}
if (!this.target.azureLogAnalytics.workspace) {
this.target.azureLogAnalytics.workspace = list[0].value;
}
}
return this.workspaces;
})
.catch(this.handleQueryCtrlError.bind(this));
};
getAzureLogAnalyticsSchema = () => {
return this.getWorkspaces()
.then(() => {
return this.datasource.azureLogAnalyticsDatasource.getSchema(this.target.azureLogAnalytics.workspace);
})
.catch(this.handleQueryCtrlError.bind(this));
};
onLogAnalyticsQueryChange = (nextQuery: string) => {
this.target.azureLogAnalytics.query = nextQuery;
};
onLogAnalyticsQueryExecute = () => {
this.panelCtrl.refresh();
};
get templateVariables() {
return this.templateSrv.getVariables().map((t) => '$' + t.name);
}
getAppInsightsMetricNames() {
if (!this.datasource.appInsightsDatasource.isConfigured()) {
return;
}
return this.datasource.getAppInsightsMetricNames().catch(this.handleQueryCtrlError.bind(this));
}
getAppInsightsColumns() {
return this.datasource.getAppInsightsColumns(this.target.refId);
}
onAppInsightsColumnChange() {
return this.refresh();
}
onAppInsightsMetricNameChange() {
if (!this.target.appInsights.metricName || this.target.appInsights.metricName === this.defaultDropdownValue) {
return;
}
return this.datasource
.getAppInsightsMetricMetadata(this.replace(this.target.appInsights.metricName))
.then((aggData: { supportedAggTypes: string[]; supportedGroupBy: string[]; primaryAggType: string }) => {
this.target.appInsights.aggOptions = aggData.supportedAggTypes;
this.target.appInsights.dimensions = aggData.supportedGroupBy;
this.target.appInsights.aggregation = aggData.primaryAggType;
return this.refresh();
})
.catch(this.handleQueryCtrlError.bind(this));
}
onInsightsAnalyticsQueryChange = (nextQuery: string) => {
this.target.insightsAnalytics.query = nextQuery;
};
onQueryExecute = () => {
return this.refresh();
};
getAppInsightsQuerySchema = () => {
return this.datasource.appInsightsDatasource.getQuerySchema().catch(this.handleQueryCtrlError.bind(this));
};
removeGroupBy = (index: number) => {
const { appInsights } = this.target;
appInsights.dimension.splice(index, 1);
this.refresh();
};
getAppInsightsGroupBySegments(query: any) {
const { appInsights } = this.target;
// HACK alert... there must be a better way!
if (this.dummyDiminsionString && this.dummyDiminsionString.length && '+' !== this.dummyDiminsionString) {
if (!appInsights.dimension) {
appInsights.dimension = [];
}
appInsights.dimension.push(this.dummyDiminsionString);
this.dummyDiminsionString = '+';
this.refresh();
}
// Return the list of dimensions stored on the query object from the last request :(
return map(appInsights.dimensions, (option: string) => {
return { text: option, value: option };
});
}
resetAppInsightsGroupBy() {
this.target.appInsights.dimension = 'none';
this.refresh();
}
updateTimeGrainType() {
if (this.target.appInsights.timeGrainType === 'specific') {
this.target.appInsights.timeGrainCount = '1';
this.target.appInsights.timeGrainUnit = 'minute';
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
this.target.appInsights.timeGrainCount,
this.target.appInsights.timeGrainUnit
);
} else {
this.target.appInsights.timeGrainCount = '';
this.target.appInsights.timeGrainUnit = '';
}
}
updateAppInsightsTimeGrain() {
if (this.target.appInsights.timeGrainUnit && this.target.appInsights.timeGrainCount) {
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
this.target.appInsights.timeGrainCount,
this.target.appInsights.timeGrainUnit
);
}
this.refresh();
}
/**
* Receives a full new query object from React and updates it into the Angular controller
*/
handleNewQuery = (newQuery: AzureMonitorQuery) => {
Object.assign(this.target, newQuery);
this.refresh();
};
}
// Modifies the actual query object
export function migrateMetricsDimensionFilters(item: AzureMetricQuery) {
if (!item.dimensionFilters) {
item.dimensionFilters = [];
}
const oldDimension = (item as any).dimension;
if (oldDimension && oldDimension !== 'None') {
item.dimensionFilters.push({
dimension: oldDimension,
operator: 'eq',
filter: (item as any).dimensionFilter,
});
delete (item as any).dimension;
delete (item as any).dimensionFilter;
}
}