diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 49ec4d6..c60a6b6 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,8 +4,14 @@
-
+
+
+
+
+
+
+
@@ -46,7 +52,7 @@
"NIXITCH_NIX_PROFILES": "",
"NIXITCH_NIX_REMOTE": "",
"NIXITCH_NIX_USER_PROFILE_DIR": "",
- "Node.js.index.ts.executor": "Run",
+ "Node.js.index.ts.executor": "Debug",
"RunOnceActivity.MCP Project settings loaded": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
@@ -64,6 +70,7 @@
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_interpreter_path": "wsl://Ubuntu@/home/ahilbig/.nvm/versions/node/v24.12.0/bin/node",
"nodejs_package_manager_path": "npm",
+ "npm.codegen.executor": "Run",
"npm.compile.executor": "Run",
"npm.copy-schema.executor": "Run",
"npm.prod.executor": "Run",
@@ -152,7 +159,8 @@
1768273021451
-
+
+
@@ -258,7 +266,15 @@
1768591454965
-
+
+
+ 1768592274523
+
+
+
+ 1768592274523
+
+
@@ -291,6 +307,19 @@
-
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/src/datasources/zabbix-hosts.ts
+ 149
+
+
+
+
+
\ No newline at end of file
diff --git a/schema/devices.graphql b/schema/devices.graphql
index 2d38745..6684949 100644
--- a/schema/devices.graphql
+++ b/schema/devices.graphql
@@ -78,3 +78,4 @@ type GenericDevice implements Host & Device {
tags: DeviceConfig
state: GenericDeviceState
}
+
diff --git a/schema/queries.graphql b/schema/queries.graphql
index 9f53f76..acd128c 100644
--- a/schema/queries.graphql
+++ b/schema/queries.graphql
@@ -34,6 +34,16 @@ type Query {
allHosts(name_pattern: String = "", filter_host: String = null, hostids: Int,
groupids:[Int!] = null, with_items: Boolean = false, tag_deviceType:[String]=[], tag_hostType:[String!]): [Host]
+ """
+ Get all devices + corresponding items. Devices are modelled as hosts having a device type + a state.
+ If with_items==true only hosts with attached items are delivered
+ name_pattern: If provided this will perform a LIKE "%…%" search on the name attribute within the database.
+
+ Authentication: By zbx_session - cookie or zabbix-auth-token - header
+ """
+ allDevices(name_pattern: String = "", filter_host: String = null, hostids: Int,
+ groupids:[Int!] = null, with_items: Boolean = false, tag_deviceType:[String]=[], tag_hostType:[String!]): [Device]
+
"""
Get all host groups.
If with_hosts==true only groups with attached hosts are delivered.
diff --git a/src/api/resolvers.ts b/src/api/resolvers.ts
index 66616f6..62225dc 100644
--- a/src/api/resolvers.ts
+++ b/src/api/resolvers.ts
@@ -1,4 +1,5 @@
import {
+ Device,
DeviceCommunicationType,
DeviceStatus,
Host,
@@ -6,7 +7,7 @@ import {
MutationImportHostGroupsArgs,
MutationImportHostsArgs,
MutationImportUserRightsArgs,
- Permission,
+ Permission, QueryAllDevicesArgs,
QueryAllHostGroupsArgs,
QueryAllHostsArgs,
QueryExportHostValueHistoryArgs,
@@ -21,7 +22,11 @@ import {HostImporter} from "../execution/host_importer.js";
import {HostValueExporter} from "../execution/host_exporter.js";
import {logger} from "../logging/logger.js";
import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js";
-import {ZabbixCreateHostRequest, ZabbixQueryHostsRequestWithItemsAndInventory,} from "../datasources/zabbix-hosts.js";
+import {
+ ZabbixCreateHostRequest,
+ ZabbixQueryDevices, ZabbixQueryDevicesArgs,
+ ZabbixQueryHostsRequestWithItemsAndInventory,
+} from "../datasources/zabbix-hosts.js";
import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js";
import {
ZabbixExportUserGroupArgs,
@@ -82,7 +87,17 @@ export function createResolvers(): Resolvers {
dataSources.zabbixAPI, new ParsedArgs(args)
)
},
+ allDevices: async (_parent: any, args: QueryAllDevicesArgs, {
+ zabbixAuthToken,
+ cookie, dataSources
+ }: any) => {
+ args.tag_hostType ??= [ZABBIX_EDGE_DEVICE_BASE_GROUP];
+ return await new ZabbixQueryDevices(zabbixAuthToken, cookie)
+ .executeRequestThrowError(
+ dataSources.zabbixAPI, new ZabbixQueryDevicesArgs(args)
+ )
+ },
allHostGroups: async (_parent: any, args: QueryAllHostGroupsArgs, {
zabbixAuthToken,
cookie
@@ -179,6 +194,19 @@ export function createResolvers(): Resolvers {
return "GenericDevice"
}
},
+ Device: {
+ // @ts-ignore
+ __resolveType: function (host: Device, _context, info ): string {
+ const deviceType = host.deviceType!;
+ logger.info(`checking host ${host.name} for deviceType - found ${deviceType}`);
+ let interfaceType: GraphQLInterfaceType = (info.returnType instanceof GraphQLList ?
+ info.returnType.ofType : info.returnType) as GraphQLInterfaceType
+ if (info.schema.getImplementations(interfaceType).objects.some((impl: { name: string; }) => impl.name === deviceType)) {
+ return deviceType;
+ }
+ return "GenericDevice"
+ }
+ },
Inventory: {
/*
diff --git a/src/datasources/zabbix-hosts.ts b/src/datasources/zabbix-hosts.ts
index d981556..b583e48 100644
--- a/src/datasources/zabbix-hosts.ts
+++ b/src/datasources/zabbix-hosts.ts
@@ -1,4 +1,4 @@
-import {CreateHostResponse, Host, ZabbixHost} from "../schema/generated/graphql.js";
+import {CreateHostResponse, Device, Host, ZabbixHost} from "../schema/generated/graphql.js";
import {ZabbixAPI} from "./zabbix-api.js";
import {
isZabbixErrorResult,
@@ -9,16 +9,17 @@ import {
ZabbixResult
} from "./zabbix-request.js";
import {ZabbixHistoryGetParams, ZabbixQueryHistoryRequest} from "./zabbix-history.js";
+import {isArray} from "node:util";
-export class ZabbixQueryHostsGenericRequest extends ZabbixRequest {
+export class ZabbixQueryHostsGenericRequest extends ZabbixRequest {
public static PATH = "host.get";
constructor(path: string, authToken?: string | null, cookie?: string | null) {
super(path, authToken, cookie);
}
- createZabbixParams(args?: ParsedArgs): ZabbixParams {
+ createZabbixParams(args?: A): ZabbixParams {
return {
...super.createZabbixParams(args),
selectParentTemplates: [
@@ -63,12 +64,12 @@ export class ZabbixQueryHostsMetaRequest extends ZabbixQueryHostsGenericRequest<
}
-export class ZabbixQueryHostsGenericRequestWithItems extends ZabbixQueryHostsGenericRequest {
+export class ZabbixQueryHostsGenericRequestWithItems extends ZabbixQueryHostsGenericRequest {
constructor(path: string, authToken?: string | null, cookie?: string) {
super(path, authToken, cookie);
}
- createZabbixParams(args?: ParsedArgs): ZabbixParams {
+ createZabbixParams(args?: A): ZabbixParams {
return {
...super.createZabbixParams(args),
selectItems: [
@@ -93,7 +94,7 @@ export class ZabbixQueryHostsGenericRequestWithItems ext
};
}
- async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: ParsedArgs): Promise {
+ async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: A): Promise {
let result = await super.executeRequestReturnError(zabbixAPI, args);
if (result && !isZabbixErrorResult(result)) {
@@ -123,12 +124,12 @@ export class ZabbixQueryHostsGenericRequestWithItems ext
}
}
-export class ZabbixQueryHostsGenericRequestWithItemsAndInventory extends ZabbixQueryHostsGenericRequestWithItems {
+export class ZabbixQueryHostsGenericRequestWithItemsAndInventory extends ZabbixQueryHostsGenericRequestWithItems {
constructor(path: string, authToken?: string | null, cookie?: string) {
super(path, authToken, cookie);
}
- createZabbixParams(args?: ParsedArgs): ZabbixParams {
+ createZabbixParams(args?: A): ZabbixParams {
return {
...super.createZabbixParams(args),
selectInventory: [
@@ -144,6 +145,22 @@ export class ZabbixQueryHostsRequestWithItemsAndInventory extends ZabbixQueryHos
}
}
+export class ZabbixQueryDevicesArgs extends ParsedArgs {
+ constructor(public args?: any) {
+ if (!args?.tag_deviceType ||
+ (Array.isArray(args.tag_deviceType) && !args.tag_deviceType.length)) {
+ args.tag_deviceType_exists = true;
+ }
+ super(args);
+ }
+}
+
+export class ZabbixQueryDevices extends ZabbixQueryHostsGenericRequestWithItemsAndInventory {
+ constructor(authToken?: string | null, cookie?: string) {
+ super("host.get.with_items", authToken, cookie);
+ }
+}
+
const isZabbixCreateHostInputParams = (value: ZabbixParams): value is ZabbixCreateHostInputParams => "host" in value && !!value.host;
export interface ZabbixCreateHostInputParams extends ZabbixParams {
diff --git a/src/datasources/zabbix-request.ts b/src/datasources/zabbix-request.ts
index 80f4d75..ff77fc2 100644
--- a/src/datasources/zabbix-request.ts
+++ b/src/datasources/zabbix-request.ts
@@ -28,7 +28,7 @@ export interface ZabbixParams {
}
export interface ZabbixWithTagsParams extends ZabbixParams {
- tags?: { tag: string; operator: number; value: any; }[]
+ tags?: { tag: string; operator: number; value?: any; }[]
}
export class ParsedArgs {
@@ -72,20 +72,28 @@ export class ParsedArgs {
}
delete args.groupidsbase
}
- let filterTagStatements: { tag: string; operator: number; value: any; }[] = []
+ let filterTagStatements: { tag: string; operator: number; value?: any; }[] = []
let filterStatements = {}
for (let argsKey in args) {
// @ts-ignore
let argsValue = args[argsKey]
- if (argsKey.startsWith("tag_") && argsValue !== null) {
- let argsArray = Array.isArray(argsValue) ? argsValue : [argsValue]
- argsArray.forEach((tagValue) => {
+ if (argsKey.startsWith("tag_")) {
+ if (argsKey.endsWith("_exists")) {
filterTagStatements.push({
- "tag": argsKey.slice(4),
- "operator": 1,
- "value": tagValue,
+ "tag": argsKey.slice(4, -7),
+ "operator": argsValue ? 4 : 5
})
- })
+ } else if (argsValue !== null) {
+ let argsArray = Array.isArray(argsValue) ? argsValue : [argsValue]
+ argsArray.forEach((tagValue) => {
+ filterTagStatements.push({
+ "tag": argsKey.slice(4),
+ "operator": 1,
+ "value": tagValue,
+ })
+ })
+ }
+
// @ts-ignore
delete args[argsKey]
}
@@ -121,9 +129,9 @@ export class ParsedArgs {
if (this.name_pattern) {
if ("search" in result) {
- ( result.search).name = this.name_pattern
+ (result.search).name = this.name_pattern
} else {
- ( result).search = {
+ (result).search = {
name: this.name_pattern,
}
}
@@ -184,7 +192,6 @@ export class ZabbixRequest {
// If prepare returns something else than undefined, the execution will be skipped and the
// result returned
diff --git a/src/schema/generated/graphql.ts b/src/schema/generated/graphql.ts
index d47d361..737a5ab 100644
--- a/src/schema/generated/graphql.ts
+++ b/src/schema/generated/graphql.ts
@@ -93,11 +93,16 @@ export interface Device {
hostid: Scalars['ID']['output'];
name?: Maybe;
state?: Maybe;
- tags?: Maybe;
+ tags?: Maybe;
}
export { DeviceCommunicationType };
+export interface DeviceConfig {
+ __typename?: 'DeviceConfig';
+ deviceWidgetPreview?: Maybe;
+}
+
export interface DeviceState {
operational?: Maybe;
}
@@ -145,6 +150,17 @@ export interface DeviceValueMessage {
value?: Maybe;
}
+export interface DisplayFieldSpec {
+ __typename?: 'DisplayFieldSpec';
+ emptyValue?: Maybe;
+ g_unit_transform?: Maybe;
+ g_value_transform?: Maybe;
+ key?: Maybe;
+ unit?: Maybe;
+ unit_font_size?: Maybe;
+ value_font_size?: Maybe;
+}
+
export interface Error {
code?: Maybe;
data?: Maybe;
@@ -168,7 +184,7 @@ export interface GenericDevice extends Device, Host {
hostid: Scalars['ID']['output'];
name?: Maybe;
state?: Maybe;
- tags?: Maybe;
+ tags?: Maybe;
}
export interface GenericDeviceState extends DeviceState {
@@ -200,7 +216,6 @@ export interface Host {
hostgroups?: Maybe>;
hostid: Scalars['ID']['output'];
name?: Maybe;
- tags?: Maybe;
}
export interface HostGroup {
@@ -322,6 +337,14 @@ export interface PermissionRequest {
export interface Query {
__typename?: 'Query';
+ /**
+ * Get all devices + corresponding items. Devices are modelled as hosts having a device type + a state.
+ * If with_items==true only hosts with attached items are delivered
+ * name_pattern: If provided this will perform a LIKE "%…%" search on the name attribute within the database.
+ *
+ * Authentication: By zbx_session - cookie or zabbix-auth-token - header
+ */
+ allDevices?: Maybe>>;
/**
* Get all host groups.
* If with_hosts==true only groups with attached hosts are delivered.
@@ -388,6 +411,17 @@ export interface Query {
}
+export interface QueryAllDevicesArgs {
+ filter_host?: InputMaybe;
+ groupids?: InputMaybe>;
+ hostids?: InputMaybe;
+ name_pattern?: InputMaybe;
+ tag_deviceType?: InputMaybe>>;
+ tag_hostType?: InputMaybe>;
+ with_items?: InputMaybe;
+}
+
+
export interface QueryAllHostGroupsArgs {
search_name?: InputMaybe;
with_hosts?: InputMaybe;
@@ -564,6 +598,14 @@ export interface UserRoleRulesInput {
ui_default_access?: InputMaybe;
}
+export interface WidgetPreview {
+ __typename?: 'WidgetPreview';
+ BOTTOM_LEFT?: Maybe;
+ BOTTOM_RIGHT?: Maybe;
+ TOP_LEFT?: Maybe;
+ TOP_RIGHT?: Maybe;
+}
+
export interface ZabbixGroupRight {
__typename?: 'ZabbixGroupRight';
id: Scalars['Int']['output'];
@@ -710,10 +752,12 @@ export type ResolversTypes = {
DateTime: ResolverTypeWrapper;
Device: ResolverTypeWrapper['Device']>;
DeviceCommunicationType: DeviceCommunicationType;
+ DeviceConfig: ResolverTypeWrapper;
DeviceState: ResolverTypeWrapper['DeviceState']>;
DeviceStatus: DeviceStatus;
DeviceValue: ResolverTypeWrapper['DeviceValue']>;
DeviceValueMessage: ResolverTypeWrapper['DeviceValueMessage']>;
+ DisplayFieldSpec: ResolverTypeWrapper;
Error: ResolverTypeWrapper['Error']>;
ErrorPayload: ResolverTypeWrapper;
Float: ResolverTypeWrapper;
@@ -755,6 +799,7 @@ export type ResolversTypes = {
UserRoleRuleInput: UserRoleRuleInput;
UserRoleRules: ResolverTypeWrapper;
UserRoleRulesInput: UserRoleRulesInput;
+ WidgetPreview: ResolverTypeWrapper;
ZabbixGroupRight: ResolverTypeWrapper;
ZabbixGroupRightInput: ZabbixGroupRightInput;
ZabbixHost: ResolverTypeWrapper & { items?: Maybe> }>;
@@ -771,9 +816,11 @@ export type ResolversParentTypes = {
CreateHostResponse: CreateHostResponse;
DateTime: Scalars['DateTime']['output'];
Device: ResolversInterfaceTypes['Device'];
+ DeviceConfig: DeviceConfig;
DeviceState: ResolversInterfaceTypes['DeviceState'];
DeviceValue: ResolversInterfaceTypes['DeviceValue'];
DeviceValueMessage: ResolversInterfaceTypes['DeviceValueMessage'];
+ DisplayFieldSpec: DisplayFieldSpec;
Error: ResolversInterfaceTypes['Error'];
ErrorPayload: ErrorPayload;
Float: Scalars['Float']['output'];
@@ -812,6 +859,7 @@ export type ResolversParentTypes = {
UserRoleRuleInput: UserRoleRuleInput;
UserRoleRules: UserRoleRules;
UserRoleRulesInput: UserRoleRulesInput;
+ WidgetPreview: WidgetPreview;
ZabbixGroupRight: ZabbixGroupRight;
ZabbixGroupRightInput: ZabbixGroupRightInput;
ZabbixHost: Omit & { items?: Maybe> };
@@ -854,11 +902,16 @@ export type DeviceResolvers;
name?: Resolver, ParentType, ContextType>;
state?: Resolver, ParentType, ContextType>;
- tags?: Resolver, ParentType, ContextType>;
+ tags?: Resolver, ParentType, ContextType>;
};
export type DeviceCommunicationTypeResolvers = EnumResolverSignature<{ DATABASE_MONITOR?: any, DEPENDANT_ITEM?: any, HTTP_AGENT?: any, IPMI_AGENT?: any, JMX_AGENT?: any, SIMPLE_CHECK?: any, SIMULATOR_CALCULATED?: any, SIMULATOR_JAVASCRIPT?: any, SNMP_AGENT?: any, SNMP_TRAP?: any, ZABBIX_AGENT?: any, ZABBIX_AGENT_ACTIVE?: any, ZABBIX_INTERNAL_ITEM?: any, ZABBIX_TRAP?: any }, ResolversTypes['DeviceCommunicationType']>;
+export type DeviceConfigResolvers = {
+ deviceWidgetPreview?: Resolver, ParentType, ContextType>;
+ __isTypeOf?: IsTypeOfResolverFn;
+};
+
export type DeviceStateResolvers = {
__resolveType: TypeResolveFn<'GenericDeviceState', ParentType, ContextType>;
operational?: Resolver, ParentType, ContextType>;
@@ -881,6 +934,17 @@ export type DeviceValueMessageResolvers, ParentType, ContextType>;
};
+export type DisplayFieldSpecResolvers = {
+ emptyValue?: Resolver, ParentType, ContextType>;
+ g_unit_transform?: Resolver, ParentType, ContextType>;
+ g_value_transform?: Resolver, ParentType, ContextType>;
+ key?: Resolver, ParentType, ContextType>;
+ unit?: Resolver, ParentType, ContextType>;
+ unit_font_size?: Resolver, ParentType, ContextType>;
+ value_font_size?: Resolver, ParentType, ContextType>;
+ __isTypeOf?: IsTypeOfResolverFn;
+};
+
export type ErrorResolvers = {
__resolveType: TypeResolveFn<'ApiError', ParentType, ContextType>;
code?: Resolver, ParentType, ContextType>;
@@ -902,7 +966,7 @@ export type GenericDeviceResolvers;
name?: Resolver, ParentType, ContextType>;
state?: Resolver, ParentType, ContextType>;
- tags?: Resolver, ParentType, ContextType>;
+ tags?: Resolver, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
};
@@ -931,7 +995,6 @@ export type HostResolvers>, ParentType, ContextType>;
hostid?: Resolver;
name?: Resolver, ParentType, ContextType>;
- tags?: Resolver, ParentType, ContextType>;
};
export type HostGroupResolvers = {
@@ -998,6 +1061,7 @@ export type OperationalDeviceDataResolvers;
export type QueryResolvers = {
+ allDevices?: Resolver>>, ParentType, ContextType, RequireFields>;
allHostGroups?: Resolver>>, ParentType, ContextType, RequireFields>;
allHosts?: Resolver>>, ParentType, ContextType, RequireFields>;
apiVersion?: Resolver;
@@ -1081,6 +1145,14 @@ export type UserRoleRulesResolvers;
};
+export type WidgetPreviewResolvers = {
+ BOTTOM_LEFT?: Resolver, ParentType, ContextType>;
+ BOTTOM_RIGHT?: Resolver, ParentType, ContextType>;
+ TOP_LEFT?: Resolver, ParentType, ContextType>;
+ TOP_RIGHT?: Resolver, ParentType, ContextType>;
+ __isTypeOf?: IsTypeOfResolverFn;
+};
+
export type ZabbixGroupRightResolvers = {
id?: Resolver;
name?: Resolver, ParentType, ContextType>;
@@ -1124,10 +1196,12 @@ export type Resolvers = {
DateTime?: GraphQLScalarType;
Device?: DeviceResolvers;
DeviceCommunicationType?: DeviceCommunicationTypeResolvers;
+ DeviceConfig?: DeviceConfigResolvers;
DeviceState?: DeviceStateResolvers;
DeviceStatus?: DeviceStatusResolvers;
DeviceValue?: DeviceValueResolvers;
DeviceValueMessage?: DeviceValueMessageResolvers;
+ DisplayFieldSpec?: DisplayFieldSpecResolvers;
Error?: ErrorResolvers;
ErrorPayload?: ErrorPayloadResolvers;
GenericDevice?: GenericDeviceResolvers;
@@ -1156,6 +1230,7 @@ export type Resolvers = {
UserRoleModule?: UserRoleModuleResolvers;
UserRoleRule?: UserRoleRuleResolvers;
UserRoleRules?: UserRoleRulesResolvers;
+ WidgetPreview?: WidgetPreviewResolvers;
ZabbixGroupRight?: ZabbixGroupRightResolvers;
ZabbixHost?: ZabbixHostResolvers;
ZabbixItem?: ZabbixItemResolvers;