diff --git a/.changeset/cuddly-cloths-join.md b/.changeset/cuddly-cloths-join.md new file mode 100644 index 0000000..70ac665 --- /dev/null +++ b/.changeset/cuddly-cloths-join.md @@ -0,0 +1,5 @@ +--- +'grafana-zabbix': minor +--- + +Fix support of applicationids filters with Zabbix problems for versions older than 5.0.x diff --git a/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.test.ts b/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.test.ts index 1cd3bea..05612aa 100644 --- a/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.test.ts +++ b/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.test.ts @@ -102,6 +102,58 @@ describe('Zabbix API connector', () => { expect(result).toBe(0); }); }); + + describe('getProblems', () => { + it('sends full filter payload with application ids when supported', async () => { + const zabbixAPIConnector = new ZabbixAPIConnector(true, true, 123); + zabbixAPIConnector.version = '7.0.0'; + zabbixAPIConnector.request = jest.fn(() => Promise.resolve([{ eventid: '1' }])); + + await zabbixAPIConnector.getProblems(['21'], ['31'], ['41'], true, { + timeFrom: 100, + timeTo: 200, + recent: 'true', + severities: [3, 4], + limit: 50, + acknowledged: 0, + tags: [{ tag: 'service', value: 'foo' }], + evaltype: 1, + }); + + expect(zabbixAPIConnector.request).toHaveBeenCalledWith('problem.get', { + output: 'extend', + selectAcknowledges: 'extend', + selectSuppressionData: 'extend', + selectTags: 'extend', + source: '0', + object: '0', + sortfield: ['eventid'], + sortorder: 'DESC', + evaltype: 1, + groupids: ['21'], + hostids: ['31'], + applicationids: ['41'], + recent: 'true', + severities: [3, 4], + acknowledged: 0, + tags: [{ tag: 'service', value: 'foo' }], + limit: 50, + time_from: 100, + time_till: 200, + }); + }); + + it('omits applicationids when applications are unsupported', async () => { + const zabbixAPIConnector = new ZabbixAPIConnector(true, true, 123); + zabbixAPIConnector.version = '7.0.0'; + zabbixAPIConnector.request = jest.fn(() => Promise.resolve([{ eventid: '1' }])); + + await zabbixAPIConnector.getProblems(['21'], ['31'], ['41'], false, {}); + + const [, params] = (zabbixAPIConnector.request as jest.Mock).mock.calls.at(-1)!; + expect(params.applicationids).toBeUndefined(); + }); + }); }); const triggers = [ diff --git a/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts b/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts index 6428584..a0cb700 100644 --- a/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts +++ b/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts @@ -482,7 +482,7 @@ export class ZabbixAPIConnector { return sliResponse; } - getProblems(groupids, hostids, applicationids, options): Promise { + getProblems(groupids, hostids, applicationids, supportsApplications, options): Promise { const { timeFrom, timeTo, recent, severities, limit, acknowledged, tags, evaltype } = options; const params: any = { @@ -498,7 +498,6 @@ export class ZabbixAPIConnector { // preservekeys: '1', groupids, hostids, - applicationids, recent, }; @@ -527,6 +526,10 @@ export class ZabbixAPIConnector { params.time_till = timeTo; } + if (supportsApplications) { + params.applicationids = applicationids; + } + return this.request('problem.get', params).then(utils.mustArray); } diff --git a/src/datasource/zabbix/zabbix.test.ts b/src/datasource/zabbix/zabbix.test.ts index 3c35a91..7132369 100644 --- a/src/datasource/zabbix/zabbix.test.ts +++ b/src/datasource/zabbix/zabbix.test.ts @@ -1,4 +1,10 @@ import { Zabbix } from './zabbix'; +import { joinTriggersWithEvents } from '../problemsHandler'; + +jest.mock('../problemsHandler', () => ({ + joinTriggersWithEvents: jest.fn(), + joinTriggersWithProblems: jest.fn(), +})); jest.mock( '@grafana/runtime', @@ -110,4 +116,42 @@ describe('Zabbix', () => { }); }); }); + + describe('getProblemsHistory', () => { + const ctx = { url: 'http://localhost' }; + let zabbix: Zabbix; + + beforeEach(() => { + zabbix = new Zabbix(ctx); + zabbix.getGroups = jest.fn().mockResolvedValue([{ groupid: '21' }]); + zabbix.getHosts = jest.fn().mockResolvedValue([{ hostid: '31' }]); + zabbix.getApps = jest.fn().mockResolvedValue([{ applicationid: '41' }]); + zabbix.supportsApplications = jest.fn().mockReturnValue(true); + zabbix.zabbixAPI.getEventsHistory = jest.fn().mockResolvedValue([{ objectid: '501' }]); + zabbix.zabbixAPI.getTriggersByIds = jest.fn().mockResolvedValue([{ triggerid: '501' }]); + (joinTriggersWithEvents as jest.Mock).mockReturnValue([{ triggerid: '501' }]); + zabbix.filterTriggersByProxy = jest.fn().mockResolvedValue([{ triggerid: '501' }]); + }); + + it('builds the history query and returns filtered triggers', async () => { + const result = await zabbix.getProblemsHistory('group.*', 'host.*', 'app.*', 'proxy-foo', { + valueFromEvent: true, + }); + + expect(zabbix.zabbixAPI.getEventsHistory).toHaveBeenCalledWith(['21'], ['31'], ['41'], { valueFromEvent: true }); + expect(joinTriggersWithEvents).toHaveBeenCalledWith([{ objectid: '501' }], [{ triggerid: '501' }], { + valueFromEvent: true, + }); + expect(zabbix.filterTriggersByProxy).toHaveBeenCalledWith([{ triggerid: '501' }], 'proxy-foo'); + expect(result).toEqual([{ triggerid: '501' }]); + }); + + it('omits applicationids when applications are unsupported', async () => { + (zabbix.supportsApplications as jest.Mock).mockReturnValue(false); + + await zabbix.getProblemsHistory('group.*', 'host.*', 'app.*', undefined, {}); + + expect(zabbix.zabbixAPI.getEventsHistory).toHaveBeenCalledWith(['21'], ['31'], undefined, {}); + }); + }); }); diff --git a/src/datasource/zabbix/zabbix.ts b/src/datasource/zabbix/zabbix.ts index 0dea2c4..8ee899e 100644 --- a/src/datasource/zabbix/zabbix.ts +++ b/src/datasource/zabbix/zabbix.ts @@ -509,7 +509,15 @@ export class Zabbix implements ZabbixConnector { return query; }) - .then((query) => this.zabbixAPI.getProblems(query.groupids, query.hostids, query.applicationids, options)) + .then((query) => + this.zabbixAPI.getProblems( + query.groupids, + query.hostids, + query.applicationids, + this.supportsApplications(), + options + ) + ) .then((problems) => { const triggerids = problems?.map((problem) => problem.objectid); return Promise.all([Promise.resolve(problems), this.zabbixAPI.getTriggersByIds(triggerids)]); @@ -533,7 +541,7 @@ export class Zabbix implements ZabbixConnector { const [filteredGroups, filteredHosts, filteredApps] = results; const query: any = {}; - if (appFilter) { + if (appFilter && this.supportsApplications()) { query.applicationids = _.flatten(_.map(filteredApps, 'applicationid')); } if (hostFilter) { @@ -579,7 +587,15 @@ export class Zabbix implements ZabbixConnector { return query; }) - .then((query) => this.zabbixAPI.getProblems(query.groupids, query.hostids, query.applicationids, options)) + .then((query) => + this.zabbixAPI.getProblems( + query.groupids, + query.hostids, + query.applicationids, + this.supportsApplications(), + options + ) + ) .then((problems) => findByFilter(problems, triggerFilter)) .then((problems) => { const triggerids = problems?.map((problem) => problem.objectid);