feat: add GroundValueChecker and WeatherSensorDevice with public API integration
This commit introduces two new device types, GroundValueChecker and WeatherSensorDevice, which leverage public APIs (BORIS NRW and Open-Meteo) for real-time data collection. It also includes several API enhancements and fixes to support these new integrations. Detailed changes: - **New Device Types**: - Added GroundValueChecker schema and integration with BORIS NRW WMS via Zabbix Script items. - Added WeatherSensorDevice schema and integration with Open-Meteo via Zabbix HTTP Agent items. - **API Enhancements**: - Added error field to ZabbixItem for item-level error reporting. - Updated CreateTemplateItem mutation input to support params (for Script items) and timeout. - Registered missing scalar resolvers: JSONObject, DateTime, and Time. - **Performance & Reliability**: - Implemented batch fetching for item preprocessing in both host and template queries to reduce Zabbix API calls and ensure data visibility. - Updated template_importer.ts to correctly handle Script item parameters. - **Documentation**: - Consolidated public API device recipes in docs/howtos/cookbook.md. - Added guidance on analyzing data update frequency and setting reasonable update intervals (e.g., 1h for weather, 1d for ground values). - **Testing**: - Added new regression test REG-ITEM-META to verify item metadata (units, description, error, preprocessing) and JSONObject scalar support. - Enhanced RegressionTestExecutor with more detailed host-item relationship verification.
This commit is contained in:
parent
41e4c4da1f
commit
ad104acde2
13 changed files with 378 additions and 45 deletions
|
|
@ -9,6 +9,7 @@ import {
|
|||
ZabbixResult
|
||||
} from "./zabbix-request.js";
|
||||
import {ZabbixHistoryGetParams, ZabbixQueryHistoryRequest} from "./zabbix-history.js";
|
||||
import {ZabbixQueryItemRequest} from "./zabbix-templates.js";
|
||||
|
||||
|
||||
export class ZabbixQueryHostsGenericRequest<T extends ZabbixResult, A extends ParsedArgs = ParsedArgs> extends ZabbixRequest<T, A> {
|
||||
|
|
@ -80,6 +81,14 @@ export class ZabbixQueryHostsGenericRequestWithItems<T extends ZabbixResult, A e
|
|||
"type",
|
||||
"value_type",
|
||||
"status",
|
||||
"error",
|
||||
"units",
|
||||
"history",
|
||||
"delay",
|
||||
"description",
|
||||
"preprocessing",
|
||||
"tags",
|
||||
"master_itemid",
|
||||
],
|
||||
output: [
|
||||
"hostid",
|
||||
|
|
@ -97,26 +106,44 @@ export class ZabbixQueryHostsGenericRequestWithItems<T extends ZabbixResult, A e
|
|||
let result = await super.executeRequestReturnError(zabbixAPI, args);
|
||||
|
||||
if (result && !isZabbixErrorResult(result)) {
|
||||
for (let device of <ZabbixHost[]>result) {
|
||||
for (let item of device.items || []) {
|
||||
if (!item.lastclock ) {
|
||||
let values = await new ZabbixQueryHistoryRequest(this.authToken, this.cookie).executeRequestReturnError(
|
||||
zabbixAPI, new ZabbixHistoryGetParams(item.itemid, ["clock", "value", "itemid"], 1, item.value_type))
|
||||
if (isZabbixErrorResult(values)) {
|
||||
return values;
|
||||
}
|
||||
if (values.length) {
|
||||
let latestValue = values[0];
|
||||
item.lastvalue = latestValue.value;
|
||||
item.lastclock = latestValue.clock;
|
||||
} else {
|
||||
item.lastvalue = null;
|
||||
item.lastclock = null;
|
||||
const hosts = <ZabbixHost[]>result;
|
||||
const hostids = hosts.map(h => h.hostid);
|
||||
|
||||
if (hostids.length > 0) {
|
||||
// Batch fetch preprocessing for all items of these hosts
|
||||
const allItems = await new ZabbixQueryItemRequest(this.authToken, this.cookie).executeRequestReturnError(zabbixAPI, new ParsedArgs({
|
||||
hostids: hostids,
|
||||
selectPreprocessing: "extend"
|
||||
}));
|
||||
|
||||
if (!isZabbixErrorResult(allItems) && Array.isArray(allItems)) {
|
||||
const itemidToPreprocessing = new Map<string, any>();
|
||||
allItems.forEach((item: any) => {
|
||||
itemidToPreprocessing.set(item.itemid, item.preprocessing);
|
||||
});
|
||||
|
||||
for (let device of hosts) {
|
||||
for (let item of device.items || []) {
|
||||
item.preprocessing = itemidToPreprocessing.get(item.itemid.toString());
|
||||
if (!item.lastclock ) {
|
||||
let values = await new ZabbixQueryHistoryRequest(this.authToken, this.cookie).executeRequestReturnError(
|
||||
zabbixAPI, new ZabbixHistoryGetParams(item.itemid, ["clock", "value", "itemid"], 1, item.value_type))
|
||||
if (isZabbixErrorResult(values)) {
|
||||
return values;
|
||||
}
|
||||
if (values.length) {
|
||||
let latestValue = values[0];
|
||||
item.lastvalue = latestValue.value;
|
||||
item.lastclock = latestValue.clock;
|
||||
} else {
|
||||
item.lastvalue = null;
|
||||
item.lastclock = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {ZabbixRequest, ParsedArgs, isZabbixErrorResult, ZabbixParams} from "./zabbix-request.js";
|
||||
import {ZabbixRequest, ParsedArgs, isZabbixErrorResult, ZabbixParams, ZabbixErrorResult} from "./zabbix-request.js";
|
||||
import {ZabbixAPI} from "./zabbix-api.js";
|
||||
import {logger} from "../logging/logger.js";
|
||||
|
||||
|
|
@ -24,6 +24,36 @@ export class ZabbixQueryTemplatesRequest extends ZabbixRequest<ZabbixQueryTempla
|
|||
...args?.zabbix_params
|
||||
};
|
||||
}
|
||||
|
||||
async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: ParsedArgs): Promise<ZabbixErrorResult | ZabbixQueryTemplateResponse[]> {
|
||||
let result = await super.executeRequestReturnError(zabbixAPI, args);
|
||||
|
||||
if (result && !isZabbixErrorResult(result) && Array.isArray(result)) {
|
||||
const templateids = result.map(t => t.templateid);
|
||||
if (templateids.length > 0) {
|
||||
// Batch fetch preprocessing for all items of these templates
|
||||
const allItems = await new ZabbixQueryItemRequest(this.authToken, this.cookie).executeRequestReturnError(zabbixAPI, new ParsedArgs({
|
||||
templateids: templateids,
|
||||
selectPreprocessing: "extend"
|
||||
}));
|
||||
|
||||
if (!isZabbixErrorResult(allItems) && Array.isArray(allItems)) {
|
||||
const itemidToPreprocessing = new Map<string, any>();
|
||||
allItems.forEach((item: any) => {
|
||||
itemidToPreprocessing.set(item.itemid, item.preprocessing);
|
||||
});
|
||||
|
||||
for (let template of result) {
|
||||
for (let item of template.items || []) {
|
||||
item.preprocessing = itemidToPreprocessing.get(item.itemid.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -90,7 +120,7 @@ export class TemplateHelper {
|
|||
logger.error(`Unable to find templateName=${templateName}`)
|
||||
return null
|
||||
}
|
||||
result.push(...templates.map((t) => Number(t.templateid)))
|
||||
result.push(...templates.map((t: ZabbixQueryTemplateResponse) => Number(t.templateid)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue