This PR migrates the use of `DatasourceApi` to `DatasourceWithBackend`, with this a couple additional improvements were made: 1. Migrate to use `interpolateVariablesInQuery` everywhere instead of the custom `replaceTemplateVariables` we were using 2. Moves util functions out of `datasource.ts` and into the existing `utils.ts` <img width="1261" height="406" alt="Screenshot 2025-11-20 at 11 37 56 AM" src="https://github.com/user-attachments/assets/9e396cf2-eab0-49d1-958c-963a2e896eba" /> Now we can see the `query` calls being made to the backend: <img width="367" height="102" alt="Screenshot 2025-11-20 at 11 38 18 AM" src="https://github.com/user-attachments/assets/a5a9a337-7f19-4f7c-9d04-9d30c0216fb2" /> Tested: - By running queries from Explore and Dashboards (with and without variables) - By interacting with all the different Editors to make sure `ComboBox` was working as expected Next: Once this is merged, we will next be able to slowly move away from using the `ZabbixConnector` to make backend datasource calls. Fixes: [#131](https://github.com/orgs/grafana/projects/457/views/40?pane=issue&itemId=139450234&issue=grafana%7Coss-big-tent-squad%7C131)
265 lines
7.7 KiB
TypeScript
265 lines
7.7 KiB
TypeScript
import _ from 'lodash';
|
|
import * as utils from '../utils';
|
|
import { replaceTemplateVars, zabbixTemplateFormat } from '../utils';
|
|
|
|
describe('Utils', () => {
|
|
describe('expandItemName()', () => {
|
|
it('should properly expand unquoted params', (done) => {
|
|
let test_cases = [
|
|
{
|
|
name: `CPU $2 time`,
|
|
key: `system.cpu.util[,user,avg1]`,
|
|
expected: 'CPU user time',
|
|
},
|
|
{
|
|
name: `CPU $2 time - $3`,
|
|
key: `system.cpu.util[,system,avg1]`,
|
|
expected: 'CPU system time - avg1',
|
|
},
|
|
{
|
|
name: `CPU - $1 - $2 - $3`,
|
|
key: `system.cpu.util[,system,avg1]`,
|
|
expected: 'CPU - - system - avg1',
|
|
},
|
|
];
|
|
|
|
_.each(test_cases, (test_case) => {
|
|
let expandedName = utils.expandItemName(test_case.name, test_case.key);
|
|
expect(expandedName).toBe(test_case.expected);
|
|
});
|
|
done();
|
|
});
|
|
|
|
it('should properly expand quoted params with commas', (done) => {
|
|
let test_cases = [
|
|
{
|
|
name: `CPU $2 time`,
|
|
key: `system.cpu.util["type=user,value=avg",user]`,
|
|
expected: 'CPU user time',
|
|
},
|
|
{
|
|
name: `CPU $1 time`,
|
|
key: `system.cpu.util["type=user,value=avg","user"]`,
|
|
expected: 'CPU type=user,value=avg time',
|
|
},
|
|
{
|
|
name: `CPU $1 time $3`,
|
|
key: `system.cpu.util["type=user,value=avg",,"user"]`,
|
|
expected: 'CPU type=user,value=avg time user',
|
|
},
|
|
{
|
|
name: `CPU $1 $2 $3`,
|
|
key: `system.cpu.util["type=user,value=avg",time,"user"]`,
|
|
expected: 'CPU type=user,value=avg time user',
|
|
},
|
|
];
|
|
|
|
_.each(test_cases, (test_case) => {
|
|
let expandedName = utils.expandItemName(test_case.name, test_case.key);
|
|
expect(expandedName).toBe(test_case.expected);
|
|
});
|
|
done();
|
|
});
|
|
|
|
it('should properly expand array params', (done) => {
|
|
let test_cases = [
|
|
{
|
|
name: `CPU $2 - $3 time`,
|
|
key: `system.cpu.util[,[user,system],avg1]`,
|
|
expected: 'CPU user,system - avg1 time',
|
|
},
|
|
{
|
|
name: `CPU $2 - $3 time`,
|
|
key: `system.cpu.util[,["user,system",iowait],avg1]`,
|
|
expected: `CPU "user,system",iowait - avg1 time`,
|
|
},
|
|
{
|
|
name: `CPU - $2 - $3 - $4`,
|
|
key: `system.cpu.util[,[],["user,system",iowait],avg1]`,
|
|
expected: `CPU - - "user,system",iowait - avg1`,
|
|
},
|
|
];
|
|
|
|
_.each(test_cases, (test_case) => {
|
|
let expandedName = utils.expandItemName(test_case.name, test_case.key);
|
|
expect(expandedName).toBe(test_case.expected);
|
|
});
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe('splitTemplateQuery()', () => {
|
|
// Backward compatibility
|
|
it('should properly split query in old format', (done) => {
|
|
let test_cases = [
|
|
{
|
|
query: `/alu/./tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9{2}/`,
|
|
expected: ['/alu/', '/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9{2}/'],
|
|
},
|
|
{
|
|
query: `a.b.c.d`,
|
|
expected: ['a', 'b', 'c', 'd'],
|
|
},
|
|
];
|
|
|
|
_.each(test_cases, (test_case) => {
|
|
let splitQuery = utils.splitTemplateQuery(test_case.query);
|
|
expect(splitQuery).toEqual(test_case.expected);
|
|
});
|
|
done();
|
|
});
|
|
|
|
it('should properly split query', (done) => {
|
|
let test_cases = [
|
|
{
|
|
query: `{alu}{/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]*/}`,
|
|
expected: ['alu', '/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]*/'],
|
|
},
|
|
{
|
|
query: `{alu}{/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]{2}/}`,
|
|
expected: ['alu', '/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]{2}/'],
|
|
},
|
|
{
|
|
query: `{a}{b}{c}{d}`,
|
|
expected: ['a', 'b', 'c', 'd'],
|
|
},
|
|
{
|
|
query: `{a}{b.c.d}`,
|
|
expected: ['a', 'b.c.d'],
|
|
},
|
|
];
|
|
|
|
_.each(test_cases, (test_case) => {
|
|
let splitQuery = utils.splitTemplateQuery(test_case.query);
|
|
expect(splitQuery).toEqual(test_case.expected);
|
|
});
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe('getArrayDepth()', () => {
|
|
it('should calculate proper array depth', () => {
|
|
const test_cases = [
|
|
{
|
|
array: [],
|
|
depth: 1,
|
|
},
|
|
{
|
|
array: [1, 2, 3],
|
|
depth: 1,
|
|
},
|
|
{
|
|
array: [
|
|
[1, 2],
|
|
[3, 4],
|
|
],
|
|
depth: 2,
|
|
},
|
|
{
|
|
array: [
|
|
[
|
|
[1, 2],
|
|
[3, 4],
|
|
],
|
|
[
|
|
[1, 2],
|
|
[3, 4],
|
|
],
|
|
],
|
|
depth: 3,
|
|
},
|
|
];
|
|
|
|
for (const test_case of test_cases) {
|
|
expect(utils.getArrayDepth(test_case.array)).toBe(test_case.depth);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('replaceTemplateVars()', () => {
|
|
function testReplacingVariable(target, varValue, expectedResult, done) {
|
|
const templateSrv = {
|
|
replace: jest.fn((target) => zabbixTemplateFormat(varValue)),
|
|
getVariables: jest.fn(),
|
|
containsTemplate: jest.fn(),
|
|
updateTimeRange: jest.fn(),
|
|
};
|
|
|
|
let result = replaceTemplateVars(templateSrv, target, {});
|
|
expect(result).toBe(expectedResult);
|
|
done();
|
|
}
|
|
|
|
/*
|
|
* Alphanumerics, spaces, dots, dashes and underscores
|
|
* are allowed in Zabbix host name.
|
|
* 'AaBbCc0123 .-_'
|
|
*/
|
|
it('should return properly escaped regex', (done) => {
|
|
let target = '$host';
|
|
let template_var_value = 'AaBbCc0123 .-_';
|
|
let expected_result = '/^AaBbCc0123 \\.-_$/';
|
|
|
|
testReplacingVariable(target, template_var_value, expected_result, done);
|
|
});
|
|
|
|
/*
|
|
* Single-value variable
|
|
* $host = backend01
|
|
* $host => /^backend01|backend01$/
|
|
*/
|
|
it('should return proper regex for single value', (done) => {
|
|
let target = '$host';
|
|
let template_var_value = 'backend01';
|
|
let expected_result = '/^backend01$/';
|
|
|
|
testReplacingVariable(target, template_var_value, expected_result, done);
|
|
});
|
|
|
|
/*
|
|
* Multi-value variable
|
|
* $host = [backend01, backend02]
|
|
* $host => /^(backend01|backend01)$/
|
|
*/
|
|
it('should return proper regex for multi-value', (done) => {
|
|
let target = '$host';
|
|
let template_var_value = ['backend01', 'backend02'];
|
|
let expected_result = '/^(backend01|backend02)$/';
|
|
|
|
testReplacingVariable(target, template_var_value, expected_result, done);
|
|
});
|
|
});
|
|
|
|
describe('replaceVariablesInFuncParams()', () => {
|
|
it('should interpolate numeric and string params with templateSrv', () => {
|
|
const replaceMock = jest
|
|
.fn()
|
|
.mockImplementation((value) => (value === '42' ? '100' : value.replace('$var', 'result')));
|
|
const templateSrv = { replace: replaceMock };
|
|
const scopedVars = { some: 'var' } as any;
|
|
const functions = [
|
|
{
|
|
def: { name: 'test' },
|
|
params: [42, '$var'],
|
|
},
|
|
];
|
|
|
|
const [fn] = utils.replaceVariablesInFuncParams(templateSrv as any, functions as any, scopedVars);
|
|
|
|
expect(replaceMock).toHaveBeenCalledWith('42', scopedVars);
|
|
expect(replaceMock).toHaveBeenCalledWith('$var', scopedVars);
|
|
expect(fn.params).toEqual([100, 'result']);
|
|
});
|
|
|
|
it('should keep params undefined when function has none', () => {
|
|
const templateSrv = { replace: jest.fn() };
|
|
const functions = [{ def: { name: 'noop' } }];
|
|
|
|
const [fn] = utils.replaceVariablesInFuncParams(templateSrv as any, functions as any, {} as any);
|
|
|
|
expect(fn.params).toBeUndefined();
|
|
expect(templateSrv.replace).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|