feat(query-optimization): implement GraphQL query optimization and enhance regression suite

- **Optimization**: Implemented automatic Zabbix parameter optimization by analyzing GraphQL selection sets.

- **ZabbixRequest**: Added optimizeZabbixParams with support for skippable parameters and implied field dependencies (e.g., state -> items).

- **Resolvers**: Updated allHosts, allDevices, allHostGroups, and templates to pass requested fields to data sources.

- **Data Sources**: Optimized ZabbixQueryHostsGenericRequest and ZabbixQueryTemplatesRequest to skip unnecessary Zabbix API calls.

- **Regression Tests**: Enhanced RegressionTestExecutor with new tests for optimization (REG-OPT, REG-OPT-NEG), state retrieval (REG-STATE), dependent items (REG-DEP), and empty results (REG-EMPTY).

- **Documentation**: Created query_optimization.md How-To guide and updated roadmap.md, README.md, and tests.md.

- **Bug Fixes**: Fixed deviceType tag assignment during host import and corrected ZabbixCreateHostRequest to support tags.
This commit is contained in:
Andreas Hilbig 2026-02-02 06:23:35 +01:00
parent ad104acde2
commit 97a0f70fd6
16 changed files with 835 additions and 69 deletions

31
src/api/graphql_utils.ts Normal file
View file

@ -0,0 +1,31 @@
import {GraphQLResolveInfo, FieldNode, FragmentDefinitionNode, InlineFragmentNode} from "graphql";
export function getRequestedFields(info: GraphQLResolveInfo): string[] {
if (!info || !info.fieldNodes) return [];
const fields: string[] = [];
const fieldNode = info.fieldNodes[0];
function extractFields(selectionSet: any, prefix: string = "") {
if (!selectionSet) return;
for (const selection of selectionSet.selections) {
if (selection.kind === 'Field') {
const fieldName = (selection as FieldNode).name.value;
const fullPath = prefix ? `${prefix}.${fieldName}` : fieldName;
fields.push(fullPath);
if (selection.selectionSet) {
extractFields(selection.selectionSet, fullPath);
}
} else if (selection.kind === 'InlineFragment') {
extractFields((selection as InlineFragmentNode).selectionSet, prefix);
} else if (selection.kind === 'FragmentSpread') {
const fragment = info.fragments[selection.name.value];
if (fragment) {
extractFields(fragment.selectionSet, prefix);
}
}
}
}
extractFields(fieldNode.selectionSet);
return fields;
}