Mix backend and frontend queries from one panel

This commit is contained in:
Alexander Zobnin
2021-06-01 20:47:16 +03:00
parent 16fe4795b3
commit c546f2143d
3 changed files with 47 additions and 70 deletions

View File

@@ -15,7 +15,7 @@ import problemsHandler from './problemsHandler';
import { Zabbix } from './zabbix/zabbix'; import { Zabbix } from './zabbix/zabbix';
import { ZabbixAPIError } from './zabbix/connectors/zabbix_api/zabbixAPIConnector'; import { ZabbixAPIError } from './zabbix/connectors/zabbix_api/zabbixAPIConnector';
import { ZabbixMetricsQuery, ZabbixDSOptions, VariableQueryTypes, ShowProblemTypes, ProblemDTO } from './types'; import { ZabbixMetricsQuery, ZabbixDSOptions, VariableQueryTypes, ShowProblemTypes, ProblemDTO } from './types';
import { getBackendSrv, getTemplateSrv, toDataQueryResponse } from '@grafana/runtime'; import { getBackendSrv, getTemplateSrv, toDataQueryError, toDataQueryResponse } from '@grafana/runtime';
import { DataFrame, DataQueryRequest, DataQueryResponse, DataSourceApi, DataSourceInstanceSettings, FieldType, isDataFrame, LoadingState } from '@grafana/data'; import { DataFrame, DataQueryRequest, DataQueryResponse, DataSourceApi, DataSourceInstanceSettings, FieldType, isDataFrame, LoadingState } from '@grafana/data';
export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDSOptions> { export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDSOptions> {
@@ -101,18 +101,19 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
*/ */
query(request: DataQueryRequest<any>): Promise<DataQueryResponse> | Observable<DataQueryResponse> { query(request: DataQueryRequest<any>): Promise<DataQueryResponse> | Observable<DataQueryResponse> {
// Migrate old targets // Migrate old targets
request.targets = request.targets.map(t => { const requestTargets = request.targets.map(t => {
// Prevent changes of original object // Prevent changes of original object
const target = _.cloneDeep(t); const target = _.cloneDeep(t);
return migrations.migrate(target); return migrations.migrate(target);
}); });
if (isBackendQuery(request)) { const backendResponsePromise = this.backendQuery({...request, targets: requestTargets});
return this.backendQuery(request); // if (isBackendQuery(request)) {
} // }
// Create request for each target // Create request for each target
const promises = _.map(request.targets, target => { const frontendTargets = requestTargets.filter(t => !isBackendTarget(t));
const promises = _.map(frontendTargets, target => {
// Don't request for hidden targets // Don't request for hidden targets
if (target.hide) { if (target.hide) {
return []; return [];
@@ -172,7 +173,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
}); });
// Data for panel (all targets) // Data for panel (all targets)
return Promise.all(_.flatten(promises)) const frontendResponsePromise: Promise<DataQueryResponse> = Promise.all(_.flatten(promises))
.then(_.flatten) .then(_.flatten)
.then(data => { .then(data => {
if (data && data.length > 0 && isDataFrame(data[0]) && !utils.isProblemsDataFrame(data[0])) { if (data && data.length > 0 && isDataFrame(data[0]) && !utils.isProblemsDataFrame(data[0])) {
@@ -182,19 +183,28 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
data = responseHandler.convertToWide(data); data = responseHandler.convertToWide(data);
} }
} }
return data; return { data };
}).then(data => { });
return Promise.all([backendResponsePromise, frontendResponsePromise])
.then(rsp => {
// Merge backend and frontend queries results
const [backendRes, frontendRes] = rsp;
if (frontendRes.data) {
backendRes.data = backendRes.data.concat(frontendRes.data);
}
return { return {
data, data: backendRes.data,
state: LoadingState.Done, state: LoadingState.Done,
key: request.requestId, key: request.requestId,
}; };
}); });
} }
backendQuery(request: DataQueryRequest<any>): Observable<DataQueryResponse> { async backendQuery(request: DataQueryRequest<any>): Promise<DataQueryResponse> {
const { intervalMs, maxDataPoints, range, requestId } = request; const { intervalMs, maxDataPoints, range, requestId } = request;
const targets = request.targets; const targets = request.targets.filter(isBackendTarget);
// Add range variables // Add range variables
request.scopedVars = Object.assign({}, request.scopedVars, utils.getRangeScopedVars(request.range)); request.scopedVars = Object.assign({}, request.scopedVars, utils.getRangeScopedVars(request.range));
@@ -224,7 +234,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
// Return early if no queries exist // Return early if no queries exist
if (!queries.length) { if (!queries.length) {
return of({ data: [] }); return Promise.resolve({ data: [] });
} }
const body: any = { queries }; const body: any = { queries };
@@ -235,64 +245,27 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
body.to = range.to.valueOf().toString(); body.to = range.to.valueOf().toString();
} }
return getBackendSrv() let rsp: any;
.fetch({ try {
rsp = await getBackendSrv().fetch({
url: '/api/ds/query', url: '/api/ds/query',
method: 'POST', method: 'POST',
data: body, data: body,
requestId, requestId,
}) }).toPromise();
.pipe( } catch (err) {
map((rsp: any) => { return toDataQueryResponse(err);
const resp = toDataQueryResponse(rsp);
this.sortByRefId(resp);
this.applyFrontendFunctions(resp, request);
if (responseHandler.isConvertibleToWide(resp.data)) {
console.log('Converting response to the wide format');
resp.data = responseHandler.convertToWide(resp.data);
}
return resp;
}),
catchError((err) => {
return of(toDataQueryResponse(err));
})
);
}
doTsdbRequest(options) {
const tsdbRequestData: any = {
queries: options.targets.map(target => {
target.datasourceId = this.datasourceId;
target.queryType = 'zabbixAPI';
return target;
}),
};
if (options.range) {
tsdbRequestData.from = options.range.from.valueOf().toString();
tsdbRequestData.to = options.range.to.valueOf().toString();
} }
return getBackendSrv().post('/api/ds/query', tsdbRequestData); const resp = toDataQueryResponse(rsp);
} this.sortByRefId(resp);
this.applyFrontendFunctions(resp, request);
if (responseHandler.isConvertibleToWide(resp.data)) {
console.log('Converting response to the wide format');
resp.data = responseHandler.convertToWide(resp.data);
}
/** return resp;
* @returns {Promise<TSDBResponse>}
*/
doTSDBConnectionTest() {
/**
* @type {{ queries: ZabbixConnectionTestQuery[] }}
*/
const tsdbRequestData = {
queries: [
{
datasourceId: this.datasourceId,
queryType: 'connectionTest'
}
]
};
return getBackendSrv().post('/api/tsdb/query', tsdbRequestData);
} }
/** /**
@@ -972,8 +945,10 @@ function getRequestTarget(request: DataQueryRequest<any>, refId: string): any {
} }
function isBackendQuery(request: DataQueryRequest<any>): boolean { function isBackendQuery(request: DataQueryRequest<any>): boolean {
return request.targets.every(q => return request.targets.every(isBackendTarget);
q.queryType === c.MODE_METRICS || }
q.queryType === c.MODE_ITEMID
); function isBackendTarget(target: any): boolean {
return target.queryType === c.MODE_METRICS ||
target.queryType === c.MODE_ITEMID;
} }

View File

@@ -51,7 +51,9 @@ function migrateQueryType(target) {
} }
// queryType is a string in query model // queryType is a string in query model
target.queryType = (target.queryType as number).toString(); if (typeof target.queryType === 'number') {
target.queryType = (target.queryType as number)?.toString();
}
} }
function migrateSLA(target) { function migrateSLA(target) {

View File

@@ -76,7 +76,7 @@ export class ZabbixAPIConnector {
requestOptions.headers.Authorization = this.requestOptions.basicAuth; requestOptions.headers.Authorization = this.requestOptions.basicAuth;
} }
const response = await getBackendSrv().datasourceRequest(requestOptions); const response = await getBackendSrv().fetch<any>(requestOptions).toPromise();
return response?.data?.result; return response?.data?.result;
} }