Compare commits

..

12 commits

Author SHA1 Message Date
41e4c4da1f feat: add weather sensor support and fix device status mapping
This commit introduces support for provisioning weather sensors with geo-coordinates
via user macros and fixes a critical mapping bug in device status.

Changes:
- fix: Corrected DeviceStatus enum mapping (0=ENABLED, 1=DISABLED).
- feat: Added 'status' field to CreateTemplateItem input in GraphQL schema.
- feat: Enabled user macro assignment during host and template creation/import.
- feat: Added regression tests for user macro assignment and HTTP agent URL support.
- docs: Updated cookbook and sample queries to use {$LAT} and {$LON} macros.
- test: Added unit tests for macro assignment in HostImporter and TemplateImporter.
- chore: Regenerated GraphQL types.
2026-02-01 16:23:35 +01:00
5da4a17e36 feat: implement weather sensor extension and enhance device interfaces
This change introduces the Weather Sensor device type which retrieves data from public APIs, and enhances the core Host/Device interfaces to provide consistent access to inventory and items across all specialized device types. It also improves search logic and fixes several bugs identified during implementation.

- Weather Sensor Extension: Added schema and recipe for a device retrieving weather data via Zabbix HTTP agent items.

- Interface Enhancements: Added inventory and items fields to Host and Device interfaces to ensure all device specialized types have consistent access to monitoring and inventory data.

- Search Logic Improvements: Enhanced ParsedArgs to support searchByAny and technical name (host) searches when a name pattern is provided.

- Bug Fixes:

  - Fixed getLocations argument order in the Zabbix API datasource.

  - Implemented deduplication for groupids and templateids in HostImporter to prevent Zabbix duplicate value errors.

  - Added missing url field to CreateTemplateItem for HTTP Agent item support.

- Testing:

  - Extended the regression test suite with 4 new automated checks covering the fixed bugs.

  - Updated Jest tests to accommodate the improved search parameters.

- Documentation: Updated cookbook and test specifications to reflect new features and regression testing obligations.
2026-02-01 06:56:23 +01:00
b84e4c0734 feat: implement comprehensive testing framework and regression suite
- Established a centralized test specification in docs/tests.md that defines test categories, cases, and a coverage checklist to ensure consistent quality and maintainability across the project.

- Implemented RegressionTestExecutor for managing automated regression tests on a live Zabbix system.

- Updated GraphQL schema and resolvers with a generic runAllRegressionTests mutation.

- Enhanced MCP integration with new operation files and detailed documentation for AI-driven automation.

- Updated README.md and How-To guides (Cookbook, Maintenance, MCP) to reflect the new testing framework and MCP capabilities.

- Verified all changes with a full Jest suite (74 tests) and live end-to-end smoketests.
2026-02-01 05:05:55 +01:00
ef7afe65ab feat: implement template cloning and extended item data retrieval
- Extend Template and ZabbixItem types in GraphQL schema to support full item hierarchy and cloning.

- Update ZabbixQueryTemplatesRequest in src/datasources/zabbix-templates.ts to fetch comprehensive item configurations (type, status, history, delay, units, preprocessing, tags).

- Implement raw value resolvers for ZabbixItem.type_int and ZabbixItem.status_int in src/api/resolvers.ts.

- Add new MCP operations: mcp/operations/getTemplates.graphql and mcp/operations/importTemplates.graphql for template management via AI agents.

- Add 'Cloning a Template with Items' recipe to docs/howtos/cookbook.md.

- Update src/test/template_query.test.ts to ensure compatibility with extended datasource output.
2026-01-31 12:15:18 +01:00
67357d0bc3 feat: implement smoketest and extend host provisioning with template linking
- Add runSmoketest mutation to automate end-to-end verification.

- Add SmoketestExecutor and HostDeleter to support automated testing and cleanup.

- Extend createHost and importHosts to allow linking templates by name or ID.

- Update docs/howtos/cookbook.md with new recipe steps and AI/MCP guidance.

- Update .junie/guidelines.md with new verification and deployment standards.

- Add src/test/template_link.test.ts and update existing tests to cover new functionality.

- Regenerate GraphQL types to match schema updates.
2026-01-31 11:46:02 +01:00
b56255ffaa feat: add Zabbix 7.4 documentation samples and importHostGroups MCP tool
This commit introduces a comprehensive set of GraphQL query and mutation samples based on the official Zabbix 7.4 API documentation, along with testing and automation improvements.

Changes:

- Documentation:

  - Added 21 GraphQL sample files in docs/queries/from_zabbix_docs/ covering various Zabbix API operations.

  - Updated docs/howtos/cookbook.md with a new recipe for executing these documentation samples.

- AI & MCP:

  - Added mcp/operations/importHostGroups.graphql to enable host group import via MCP tools.

- Testing:

  - Added src/test/zabbix_docs_samples.test.ts to automatically validate all documentation samples against the GraphQL schema.
2026-01-31 10:52:56 +01:00
9a79fc8e4c feat: add MCP tools, refined recipe steps for schema extension verification and update Docker requirements
- Add mcp/operations/importHosts.graphql for flexible, name-based host provisioning.
- Split schema verification operations into createVerificationHost.graphql and verifySchemaExtension.graphql to support Apollo MCP server requirements.
- Improve mcp/operations/createHost.graphql with better type mapping and error message retrieval.
- Fix syntax error in importHosts.graphql by replacing unsupported triple-quote docstrings with standard comments.
- Add src/test/mcp_operations_validation.test.ts to automatically validate MCP operations against the GraphQL schema.
- Update docs/howtos/cookbook.md with 🤖 AI/MCP guidance and refined recipe steps for schema extension verification.
- Update README.md, docs/howtos/mcp.md, and .junie/guidelines.md to:
  - Add Docker (v27+) and Docker Compose (v2.29+) version requirements to the Tech Stack.
  - Enforce the use of docker compose (without hyphen) for all commands in the Environment guidelines.
  - Replace legacy docker-compose references across all documentation with the new command format.
2026-01-31 03:31:40 +01:00
a9940063e9 docs: enhance cookbook recipes with verification steps and templates
This commit refines the project documentation by adding result verification steps to all recipes in the cookbook and formalizing documentation standards in the project guidelines.

Changes:
- docs/howtos/cookbook.md:
  - Added 'Verify' steps to all recipes using GraphQL queries or agent checks.
  - Enhanced 'Extending Schema with a New Device Type' recipe:
    - Updated Step 1 with a realistic schema using 'DistanceTrackerDevice' implementing 'Host' and 'Device' interfaces.
    - Added mandatory advice for new device types to implement 'Host' and 'Device'.
    - Referenced the existing 'location_tracker_devices.graphql' as a prepared real-world sample.
    - Split Step 3 into Method A (Manual Creation) and Method B (Automated Import).
    - Expanded Step 4 with a host creation step (using importHosts) and a filtered allDevices query using tag_deviceType.
  - Standardized icons for preparation (🛠️), configuration (⚙️), execution (🚀), and verification ().
- .junie/guidelines.md:
  - Added 'Documentation Templates' section defining a formal Recipe Template.
  - Standardized icons and step naming to maintain visual consistency across all guides.
  - Added 'Review & Approval' rule to prevent automatic commits without user review.
- readme.improvement.plan.md:
  - Added and completed 'Priority 7: Standardization & Verification' tasks.
  - Updated implementation order to reflect the new standardization phase.
2026-01-31 02:11:15 +01:00
bbf7357e93 docs: enhance documentation and automate MCP setup
This commit introduces several improvements to the project's documentation,
roadmap, and AI agent integration (MCP).

Key changes:
- Created and styled roadmap.md to track project milestones and future plans.
- Updated .junie/guidelines.md with strict documentation style standards.
- Automated GraphQL schema concatenation for the MCP server using a schema-gen init-container.
- Updated MCP setup recipes in cookbook.md and mcp.md to reflect the new automation.
- Added .ai/mcp/mcp.json for connecting to existing MCP services via HTTP.
- Improved development workflow by updating package.json to watch .graphql files.
- Cleaned up the root directory by moving schema.graphql to .gitignore and removing redundant files.
- Standardized visual style and formatting across all markdown files.
2026-01-30 19:04:05 +01:00
91a1523d71 docs: complete documentation refactoring and structure optimization
This commit finalizes the documentation improvement plan by:
- Centralizing reference material in README.md.
- Creating a dedicated Technical Maintenance guide (docs/howtos/maintenance.md).
- Creating a categorized Sample Queries & Mutations overview (docs/queries/README.md).
- Eliminating redundant information across the doc set (DRY principle).
- Optimizing cross-references between reference documentation and the Cookbook.
- Updating the improvement plan to reflect all tasks as completed.
2026-01-30 15:08:19 +01:00
a01bfabfba docs: refactor documentation and upgrade to Node.js 24
This commit upgrades the project to Node.js 24 (LTS) and performs a major refactoring of the documentation to support both advanced users and AI-based automation (MCP).

Changes:
- Environment & CI/CD:
  - Set Node.js version to >=24 in package.json and .nvmrc.
  - Updated Dockerfile to use Node 24 base image.
  - Updated @types/node to ^24.10.9.
- Documentation:
  - Refactored README.md with comprehensive technical reference, configuration details, and Zabbix-to-GraphQL mapping.
  - Created docs/howtos/cookbook.md with practical recipes for common tasks and AI test generation.
  - Updated docs/howtos/mcp.md to emphasize GraphQL's advantages for AI agents and Model Context Protocol.
  - Added readme.improvement.plan.md to track documentation evolution.
  - Enhanced all how-to guides with improved cross-references and up-to-date information.
- Guidelines:
  - Updated .junie/guidelines.md with Node 24 requirements and enhanced commit message standards (Conventional Commits 1.0.0).
- Infrastructure & Code:
  - Updated docker-compose.yml with Apollo MCP server integration.
  - Refined configuration and schema handling in src/api/ and src/datasources/.
  - Synchronized generated TypeScript types with schema updates.
2026-01-30 14:35:31 +01:00
4ec61ffba1 chore: add MCP integration and refactor documentation into modular how-to guides
- Moved GraphQL query samples into a new `docs/queries` directory for better organization.
- Added new queries and mutations, including `createHost.graphql` and `GetApiVersion.graphql`.
- Introduced `mcp-config.yaml` and updated `docker-compose.yml` for MCP integration.
- Updated IntelliJ `.idea/workspace.xml` settings to reflect project changes.
- Added new how-to guides (`docs/howtos`) for permissions, tags, MCP integration, and schema usage.
- Enhanced tests by updating file paths and improving sample data locations.
- Refined permissions and host group structures in `zabbix-hostgroups.ts` and `resolvers.ts`.
2026-01-30 00:47:02 +01:00
113 changed files with 3746 additions and 1079 deletions

7
.ai/mcp/mcp.json Normal file
View file

@ -0,0 +1,7 @@
{
"mcpServers": {
"zabbix-graphql": {
"url": "http://localhost:3000/mcp"
}
}
}

3
.gitignore vendored
View file

@ -130,3 +130,6 @@ dist
.pnp.* .pnp.*
.vscode/settings.json .vscode/settings.json
# Generated schema for MCP
schema.graphql

View file

@ -4,7 +4,7 @@
<env name="ADDITIONAL_RESOLVERS" value="SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice" /> <env name="ADDITIONAL_RESOLVERS" value="SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice" />
<env name="ADDITIONAL_SCHEMAS" value="./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql" /> <env name="ADDITIONAL_SCHEMAS" value="./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql" />
<env name="DEBUG" value="device-control-center-api:*" /> <env name="DEBUG" value="device-control-center-api:*" />
<env name="ZABBIX_AUTH_TOKEN" value="$ZABBIX_AUTH_TOKEN_VCR_DEV$" /> <env name="ZABBIX_PRIVILEGE_ESCALATION_TOKEN" value="$ZABBIX_AUTH_TOKEN_VCR_DEV$" />
<env name="ZABBIX_BASE_URL" value="http://cockpit.vcr.develop.hilbigit.com/" /> <env name="ZABBIX_BASE_URL" value="http://cockpit.vcr.develop.hilbigit.com/" />
<env name="ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX" value="Permissions" /> <env name="ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX" value="Permissions" />
<env name="ZABBIX_ROADWORK_BASE_GROUP" value="Roadwork/Devices" /> <env name="ZABBIX_ROADWORK_BASE_GROUP" value="Roadwork/Devices" />

99
.idea/workspace.xml generated
View file

@ -4,9 +4,19 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="d7a71994-2699-4ae4-9fd2-ee13b7f33d35" name="Changes" comment="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file"> <list default="true" id="d7a71994-2699-4ae4-9fd2-ee13b7f33d35" name="Changes" comment="docs: refactor documentation and upgrade to Node.js 24&#10;&#10;This commit upgrades the project to Node.js 24 (LTS) and performs a major refactoring of the documentation to support both advanced users and AI-based automation (MCP).&#10;&#10;Changes:&#10;- Environment &amp; CI/CD:&#10; - Set Node.js version to &gt;=24 in package.json and .nvmrc.&#10; - Updated Dockerfile to use Node 24 base image.&#10; - Updated @types/node to ^24.10.9.&#10;- Documentation:&#10; - Refactored README.md with comprehensive technical reference, configuration details, and Zabbix-to-GraphQL mapping.&#10; - Created docs/howtos/cookbook.md with practical recipes for common tasks and AI test generation.&#10; - Updated docs/howtos/mcp.md to emphasize GraphQL's advantages for AI agents and Model Context Protocol.&#10; - Added readme.improvement.plan.md to track documentation evolution.&#10; - Enhanced all how-to guides with improved cross-references and up-to-date information.&#10;- Guidelines:&#10; - Updated .junie/guidelines.md with Node 24 requirements and enhanced commit message standards (Conventional Commits 1.0.0).&#10;- Infrastructure &amp; Code:&#10; - Updated docker-compose.yml with Apollo MCP server integration.&#10; - Refined configuration and schema handling in src/api/ and src/datasources/.&#10; - Synchronized generated TypeScript types with schema updates.">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.junie/guidelines.md" beforeDir="false" afterPath="$PROJECT_DIR$/.junie/guidelines.md" afterDir="false" /> <change beforePath="$PROJECT_DIR$/docs/howtos/cookbook.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/howtos/cookbook.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docs/queries/sample_import_weather_sensor_template.graphql" beforeDir="false" afterPath="$PROJECT_DIR$/docs/queries/sample_import_weather_sensor_template.graphql" afterDir="false" />
<change beforePath="$PROJECT_DIR$/schema/mutations.graphql" beforeDir="false" afterPath="$PROJECT_DIR$/schema/mutations.graphql" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/datasources/zabbix-hosts.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/datasources/zabbix-hosts.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/execution/host_importer.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/execution/host_importer.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/execution/regression_test_executor.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/execution/regression_test_executor.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/execution/template_importer.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/execution/template_importer.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/model/model_enum_values.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/model/model_enum_values.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/schema/generated/graphql.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/schema/generated/graphql.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/test/host_importer.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/host_importer.test.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/test/template_importer.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/template_importer.test.ts" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -17,7 +27,7 @@
<execution /> <execution />
</component> </component>
<component name="EmbeddingIndexingInfo"> <component name="EmbeddingIndexingInfo">
<option name="cachedIndexableFilesCount" value="70" /> <option name="cachedIndexableFilesCount" value="149" />
<option name="fileBasedEmbeddingIndicesEnabled" value="true" /> <option name="fileBasedEmbeddingIndicesEnabled" value="true" />
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
@ -29,6 +39,7 @@
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
<option name="RESET_MODE" value="HARD" /> <option name="RESET_MODE" value="HARD" />
<option name="SWAP_SIDES_IN_COMPARE_BRANCHES" value="true" /> <option name="SWAP_SIDES_IN_COMPARE_BRANCHES" value="true" />
<option name="UPDATE_TYPE" value="REBASE" />
</component> </component>
<component name="GitRewordedCommitMessages"> <component name="GitRewordedCommitMessages">
<option name="commitMessagesMapping"> <option name="commitMessagesMapping">
@ -42,7 +53,13 @@
</component> </component>
<component name="McpProjectServerCommands"> <component name="McpProjectServerCommands">
<commands /> <commands />
<urls /> <urls>
<McpServerConfigurationProperties>
<option name="allowedToolsNames" />
<option name="enabled" value="true" />
<option name="name" value="zabbix-graphql" />
</McpServerConfigurationProperties>
</urls>
</component> </component>
<component name="ProblemsViewState"> <component name="ProblemsViewState">
<option name="selectedTabId" value="CurrentFile" /> <option name="selectedTabId" value="CurrentFile" />
@ -90,19 +107,19 @@
"npm.copy-schema.executor": "Run", "npm.copy-schema.executor": "Run",
"npm.prod.executor": "Run", "npm.prod.executor": "Run",
"npm.test.executor": "Run", "npm.test.executor": "Run",
"settings.editor.selected.configurable": "ml.llm.LLMConfigurable", "settings.editor.selected.configurable": "ml.llm.mcp",
"settings.editor.splitter.proportion": "0.3839406", "settings.editor.splitter.proportion": "0.28812414",
"to.speed.mode.migration.done": "true", "to.speed.mode.migration.done": "true",
"ts.external.directory.path": "\\\\wsl.localhost\\Ubuntu\\home\\ahilbig\\git\\vcr\\zabbix-graphql-api\\node_modules\\typescript\\lib", "ts.external.directory.path": "\\\\wsl.localhost\\Ubuntu\\home\\ahilbig\\git\\vcr\\zabbix-graphql-api\\node_modules\\typescript\\lib",
"vue.rearranger.settings.migration": "true" "vue.rearranger.settings.migration": "true"
} }
}]]></component> }]]></component>
<component name="RecapSpentCounter"> <component name="RecapSpentCounter">
<option name="endsOfQuotaMs" value="1768327208764" /> <option name="endsOfQuotaMs" value="1772398800000" />
<option name="spentUsd" value="0.04010335" /> <option name="spentUsd" value="0.01011225" />
</component> </component>
<component name="RecapUselessUpdatesCounter"> <component name="RecapUselessUpdatesCounter">
<option name="suspendCountdown" value="0" /> <option name="suspendCountdown" value="8" />
</component> </component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
@ -116,7 +133,7 @@
<recent name="\\wsl.localhost\Ubuntu\home\ahilbig\git\vcr\zabbix-graphql-api\schema" /> <recent name="\\wsl.localhost\Ubuntu\home\ahilbig\git\vcr\zabbix-graphql-api\schema" />
</key> </key>
</component> </component>
<component name="RunManager" selected="npm.test"> <component name="RunManager" selected="Node.js.index.ts">
<configuration name="copy-schema" type="js.build_tools.npm" temporary="true" nameIsGenerated="true"> <configuration name="copy-schema" type="js.build_tools.npm" temporary="true" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" /> <package-json value="$PROJECT_DIR$/package.json" />
<command value="run" /> <command value="run" />
@ -138,7 +155,7 @@
<env name="ADDITIONAL_RESOLVERS" value="SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice" /> <env name="ADDITIONAL_RESOLVERS" value="SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice" />
<env name="ADDITIONAL_SCHEMAS" value="./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql" /> <env name="ADDITIONAL_SCHEMAS" value="./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql" />
<env name="DEBUG" value="device-control-center-api:*" /> <env name="DEBUG" value="device-control-center-api:*" />
<env name="ZABBIX_AUTH_TOKEN" value="$ZABBIX_AUTH_TOKEN_VCR_DEV$" /> <env name="ZABBIX_PRIVILEGE_ESCALATION_TOKEN" value="$ZABBIX_AUTH_TOKEN_VCR_DEV$" />
<env name="ZABBIX_BASE_URL" value="http://cockpit.vcr.develop.hilbigit.com/" /> <env name="ZABBIX_BASE_URL" value="http://cockpit.vcr.develop.hilbigit.com/" />
<env name="ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX" value="Permissions" /> <env name="ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX" value="Permissions" />
<env name="ZABBIX_ROADWORK_BASE_GROUP" value="Roadwork/Devices" /> <env name="ZABBIX_ROADWORK_BASE_GROUP" value="Roadwork/Devices" />
@ -179,7 +196,7 @@
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-WS-253.29346.242" /> <option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-WS-253.30387.83" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>
@ -196,6 +213,12 @@
<workItem from="1768913192173" duration="14627000" /> <workItem from="1768913192173" duration="14627000" />
<workItem from="1769095609607" duration="1390000" /> <workItem from="1769095609607" duration="1390000" />
<workItem from="1769256682556" duration="8928000" /> <workItem from="1769256682556" duration="8928000" />
<workItem from="1769699975260" duration="75000" />
<workItem from="1769700092648" duration="5212000" />
<workItem from="1769724930397" duration="16056000" />
<workItem from="1769789496322" duration="14281000" />
<workItem from="1769849767328" duration="18404000" />
<workItem from="1769955114366" duration="3276000" />
</task> </task>
<task id="LOCAL-00001" summary="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment"> <task id="LOCAL-00001" summary="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment">
<option name="closed" value="true" /> <option name="closed" value="true" />
@ -373,7 +396,31 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1769582068426</updated> <updated>1769582068426</updated>
</task> </task>
<option name="localTasksCounter" value="23" /> <task id="LOCAL-00023" summary="chore: update `.idea/workspace.xml` and project guidelines &#10;&#10;- Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. &#10;- Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows.">
<option name="closed" value="true" />
<created>1769597550348</created>
<option name="number" value="00023" />
<option name="presentableId" value="LOCAL-00023" />
<option name="project" value="LOCAL" />
<updated>1769597550348</updated>
</task>
<task id="LOCAL-00024" summary="chore: add MCP integration and refactor documentation into modular how-to guides &#10;&#10;- Moved GraphQL query samples into a new `docs/queries` directory for better organization. &#10;- Added new queries and mutations, including `createHost.graphql` and `GetApiVersion.graphql`. &#10;- Introduced `mcp-config.yaml` and updated `docker-compose.yml` for MCP integration. &#10;- Updated IntelliJ `.idea/workspace.xml` settings to reflect project changes. &#10;- Added new how-to guides (`docs/howtos`) for permissions, tags, MCP integration, and schema usage. &#10;- Enhanced tests by updating file paths and improving sample data locations. &#10;- Refined permissions and host group structures in `zabbix-hostgroups.ts` and `resolvers.ts`.">
<option name="closed" value="true" />
<created>1769730441542</created>
<option name="number" value="00024" />
<option name="presentableId" value="LOCAL-00024" />
<option name="project" value="LOCAL" />
<updated>1769730441542</updated>
</task>
<task id="LOCAL-00025" summary="docs: refactor documentation and upgrade to Node.js 24&#10;&#10;This commit upgrades the project to Node.js 24 (LTS) and performs a major refactoring of the documentation to support both advanced users and AI-based automation (MCP).&#10;&#10;Changes:&#10;- Environment &amp; CI/CD:&#10; - Set Node.js version to &gt;=24 in package.json and .nvmrc.&#10; - Updated Dockerfile to use Node 24 base image.&#10; - Updated @types/node to ^24.10.9.&#10;- Documentation:&#10; - Refactored README.md with comprehensive technical reference, configuration details, and Zabbix-to-GraphQL mapping.&#10; - Created docs/howtos/cookbook.md with practical recipes for common tasks and AI test generation.&#10; - Updated docs/howtos/mcp.md to emphasize GraphQL's advantages for AI agents and Model Context Protocol.&#10; - Added readme.improvement.plan.md to track documentation evolution.&#10; - Enhanced all how-to guides with improved cross-references and up-to-date information.&#10;- Guidelines:&#10; - Updated .junie/guidelines.md with Node 24 requirements and enhanced commit message standards (Conventional Commits 1.0.0).&#10;- Infrastructure &amp; Code:&#10; - Updated docker-compose.yml with Apollo MCP server integration.&#10; - Refined configuration and schema handling in src/api/ and src/datasources/.&#10; - Synchronized generated TypeScript types with schema updates.">
<option name="closed" value="true" />
<created>1769780136862</created>
<option name="number" value="00025" />
<option name="presentableId" value="LOCAL-00025" />
<option name="project" value="LOCAL" />
<updated>1769780136862</updated>
</task>
<option name="localTasksCounter" value="26" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@ -384,17 +431,25 @@
<map> <map>
<entry key="MAIN"> <entry key="MAIN">
<value> <value>
<State /> <State>
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="public/main" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value> </value>
</entry> </entry>
</map> </map>
</option> </option>
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="refactor!: Update Node.js version to 24.12.0, enhance GraphQL schema structure, and improve dynamic schema loading logic" />
<MESSAGE value="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment" />
<MESSAGE value="chore: Add missing &quot;.js&quot; extensions to imports and improve Node.js compatibility for dynamic schema loading" />
<MESSAGE value="chore: Update IntelliJ workspace and fix Docker image tag in workflow" />
<MESSAGE value="chore: Add dry-run mode and configure logger for operation mode logging" /> <MESSAGE value="chore: Add dry-run mode and configure logger for operation mode logging" />
<MESSAGE value="chore: Add test for Zabbix API arguments parsing" /> <MESSAGE value="chore: Add test for Zabbix API arguments parsing" />
<MESSAGE value="chore: Replace copying of `schema.graphql` with `extensions` in Dockerfile" /> <MESSAGE value="chore: Replace copying of `schema.graphql` with `extensions` in Dockerfile" />
@ -416,7 +471,11 @@
<MESSAGE value="chore: add default filters for host and host group queries&#10;&#10;- Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class.&#10;- Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided.&#10;- Added corresponding tests to verify default behavior in host and host group queries.&#10;- Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query.&#10;- Explained the usage of the '*' wildcard in 'search_name' with a concrete example for subgroup matching." /> <MESSAGE value="chore: add default filters for host and host group queries&#10;&#10;- Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class.&#10;- Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided.&#10;- Added corresponding tests to verify default behavior in host and host group queries.&#10;- Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query.&#10;- Explained the usage of the '*' wildcard in 'search_name' with a concrete example for subgroup matching." />
<MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files" /> <MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files" />
<MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file" /> <MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file" />
<option name="LAST_COMMIT_MESSAGE" value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file" /> <MESSAGE value="chore: update `.idea/workspace.xml` and project guidelines &#10;&#10;- Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. &#10;- Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows." />
<MESSAGE value="feat: add MCP integration and refactor documentation into modular how-to guides&quot; &#10;&#10;- Moved query files to a centralized `docs/queries/` directory for better organization. &#10;- Added example files under `mcp/operations` to support MCP integration. &#10;- Introduced a `docker-compose.yml` file for easier local development and deployment of services. &#10;- Updated tests to reflect the relocation of query files. &#10;- Enhanced IntelliJ `.idea/workspace.xml` settings for project consistency. " />
<MESSAGE value="chore: add MCP integration and refactor documentation into modular how-to guides &#10;&#10;- Moved GraphQL query samples into a new `docs/queries` directory for better organization. &#10;- Added new queries and mutations, including `createHost.graphql` and `GetApiVersion.graphql`. &#10;- Introduced `mcp-config.yaml` and updated `docker-compose.yml` for MCP integration. &#10;- Updated IntelliJ `.idea/workspace.xml` settings to reflect project changes. &#10;- Added new how-to guides (`docs/howtos`) for permissions, tags, MCP integration, and schema usage. &#10;- Enhanced tests by updating file paths and improving sample data locations. &#10;- Refined permissions and host group structures in `zabbix-hostgroups.ts` and `resolvers.ts`." />
<MESSAGE value="docs: refactor documentation and upgrade to Node.js 24&#10;&#10;This commit upgrades the project to Node.js 24 (LTS) and performs a major refactoring of the documentation to support both advanced users and AI-based automation (MCP).&#10;&#10;Changes:&#10;- Environment &amp; CI/CD:&#10; - Set Node.js version to &gt;=24 in package.json and .nvmrc.&#10; - Updated Dockerfile to use Node 24 base image.&#10; - Updated @types/node to ^24.10.9.&#10;- Documentation:&#10; - Refactored README.md with comprehensive technical reference, configuration details, and Zabbix-to-GraphQL mapping.&#10; - Created docs/howtos/cookbook.md with practical recipes for common tasks and AI test generation.&#10; - Updated docs/howtos/mcp.md to emphasize GraphQL's advantages for AI agents and Model Context Protocol.&#10; - Added readme.improvement.plan.md to track documentation evolution.&#10; - Enhanced all how-to guides with improved cross-references and up-to-date information.&#10;- Guidelines:&#10; - Updated .junie/guidelines.md with Node 24 requirements and enhanced commit message standards (Conventional Commits 1.0.0).&#10;- Infrastructure &amp; Code:&#10; - Updated docker-compose.yml with Apollo MCP server integration.&#10; - Refined configuration and schema handling in src/api/ and src/datasources/.&#10; - Synchronized generated TypeScript types with schema updates." />
<option name="LAST_COMMIT_MESSAGE" value="docs: refactor documentation and upgrade to Node.js 24&#10;&#10;This commit upgrades the project to Node.js 24 (LTS) and performs a major refactoring of the documentation to support both advanced users and AI-based automation (MCP).&#10;&#10;Changes:&#10;- Environment &amp; CI/CD:&#10; - Set Node.js version to &gt;=24 in package.json and .nvmrc.&#10; - Updated Dockerfile to use Node 24 base image.&#10; - Updated @types/node to ^24.10.9.&#10;- Documentation:&#10; - Refactored README.md with comprehensive technical reference, configuration details, and Zabbix-to-GraphQL mapping.&#10; - Created docs/howtos/cookbook.md with practical recipes for common tasks and AI test generation.&#10; - Updated docs/howtos/mcp.md to emphasize GraphQL's advantages for AI agents and Model Context Protocol.&#10; - Added readme.improvement.plan.md to track documentation evolution.&#10; - Enhanced all how-to guides with improved cross-references and up-to-date information.&#10;- Guidelines:&#10; - Updated .junie/guidelines.md with Node 24 requirements and enhanced commit message standards (Conventional Commits 1.0.0).&#10;- Infrastructure &amp; Code:&#10; - Updated docker-compose.yml with Apollo MCP server integration.&#10; - Refined configuration and schema handling in src/api/ and src/datasources/.&#10; - Synchronized generated TypeScript types with schema updates." />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" /> <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
@ -424,7 +483,7 @@
<breakpoints> <breakpoints>
<line-breakpoint enabled="true" type="javascript"> <line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/src/datasources/zabbix-request.ts</url> <url>file://$PROJECT_DIR$/src/datasources/zabbix-request.ts</url>
<line>133</line> <line>134</line>
<option name="timeStamp" value="5" /> <option name="timeStamp" value="5" />
</line-breakpoint> </line-breakpoint>
<line-breakpoint enabled="true" type="javascript"> <line-breakpoint enabled="true" type="javascript">

View file

@ -2,12 +2,20 @@
This document provides concise information and best practices for developers working on the Zabbix GraphQL API project. This document provides concise information and best practices for developers working on the Zabbix GraphQL API project.
## Roadmap
The [Roadmap](../roadmap.md) is to be considered as outlook giving constraints on architectural and design decisions for current work on the project.
### Environment
- **Operating System**: Windows with WSL + Ubuntu installed.
- **Commands**: Always execute Linux commands (e.g. use `ls` instead of `dir`) and use `docker compose` (without hyphen) instead of `docker-compose`.
## Tech Stack ## Tech Stack
- **Runtime**: Node.js (v18+) - **Runtime**: Node.js (v24+)
- **Language**: TypeScript (ESM) - **Language**: TypeScript (ESM)
- **API**: GraphQL (Apollo Server 4) - **API**: GraphQL (Apollo Server 4)
- **Testing**: Jest - **Testing**: Jest
- **Deployment**: Docker - **Deployment**: Docker (v27+) and Docker Compose (v2.29+)
## Project Structure ## Project Structure
- `src/api/`: GraphQL server configuration, schema loading, and root resolvers (see `createResolvers` in `resolvers.ts`). - `src/api/`: GraphQL server configuration, schema loading, and root resolvers (see `createResolvers` in `resolvers.ts`).
@ -20,18 +28,79 @@ This document provides concise information and best practices for developers wor
## Common Scripts ## Common Scripts
- `npm run start`: Launches the development server with `tsx` and `nodemon` for hot-reloading. - `npm run start`: Launches the development server with `tsx` and `nodemon` for hot-reloading.
- `npm run test`: Executes the Jest test suite. - `npm run test`: Executes the Jest test suite.
- `npm run codegen`: Generates TypeScript types based on the GraphQL schema definitions. - `npm run codegen`: Starts GraphQL Codegen in watch mode (for continuous development).
- `npx graphql-codegen --config codegen.ts`: Generates TypeScript types once (use this for one-off updates).
- `npm run compile`: Compiles TypeScript source files into the `dist/` directory. - `npm run compile`: Compiles TypeScript source files into the `dist/` directory.
- `npm run prod`: Prepares the schema and runs the compiled production build. - `npm run prod`: Prepares the schema and runs the compiled production build.
## Best Practices & Standards ## Best Practices & Standards
- **ESM & Imports**: The project uses ECMAScript Modules (ESM). Always use the `.js` extension when importing local files (e.g., `import { Config } from "../common_utils.js";`), even though the source files are `.ts`. - **ESM & Imports**: The project uses ECMAScript Modules (ESM). Always use the `.js` extension when importing local files (e.g. `import { Config } from "../common_utils.js";`), even though the source files are `.ts`.
- **Configuration**: Always use the `Config` class to access environment variables. Avoid direct `process.env` calls. - **Configuration**: Always use the `Config` class to access environment variables. Avoid direct `process.env` calls.
- **Type Safety**: Leverage types generated via `npm run codegen` for resolvers and data handling to ensure consistency with the schema. - **Type Safety**: Leverage types generated via `npx graphql-codegen --config codegen.ts` (or `npm run codegen` for watch mode) for resolvers and data handling to ensure consistency with the schema.
- **Import Optimization**: - **Import Optimization**:
- Always optimize imports before committing. - Always optimize imports before committing.
- Project setting `OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT` is enabled. - Project setting `OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT` is enabled.
- Junie should include "Optimize imports" as a step in every plan.
- **Modular Datasources**: When adding support for new Zabbix features, create a new datasource class in `src/datasources/` extending `ZabbixRESTDataSource`. - **Modular Datasources**: When adding support for new Zabbix features, create a new datasource class in `src/datasources/` extending `ZabbixRESTDataSource`.
- **Schema Organization**: Place GraphQL SDL files in the `schema/` directory. Use descriptive comments in SDL as they are used for API documentation. - **Schema Organization**: Place GraphQL SDL files in the `schema/` directory. Use descriptive comments in SDL as they are used for API documentation.
- **Testing**: Write reproduction tests for bugs and cover new features with both unit and integration tests in `src/test/`. - **Testing**: Write reproduction tests for bugs and cover new features with both unit and integration tests in `src/test/`.
- **Grammar & Style**: Avoid using a comma after "e.g." or "i.e." (e.g. use "e.g. example" instead of "e.g., example").
## Verification & Deployment
- **Pre-commit Verification**: Always add a verification stage to your plan before committing.
- *Action*: Run the `Smoketest` tool using MCP to ensure basic functionality is intact.
- *Action*: Monitor the API logs for errors after each service restart.
- **Environment Restart**: Always include a step to rebuild and restart the API and MCP server as a final check.
- *Command*: `docker compose up -d --build`
- *Requirement*: Ask the user if everything looks okay before executing the restart, and offer the option to skip this step.
### Documentation Style
- **Bullet Points**: Use bullet points instead of enumerations for lists to maintain consistency across all documentation.
- **Visual Style**: Use icons in headers and bold subjects for primary list items (e.g. `- **Feature**: Description`) to match the `README.md` style.
- *Note*: Standardize colon placement (outside bold tags) for primary list items.
- **Sub-points**: Use indented bullet points for detailed descriptions or additional context.
- *Format*: Use the `- *Subject*: Description` format for specific references or categorized sub-items (e.g. `- *Reference*: ...`).
- *Format*: Use plain text for general descriptions that follow a primary list item.
- **Formatting**: Avoid extra blank lines between headers and list items.
## Documentation Templates
### Cookbook & Recipes
Follow this template for all cookbook entries to ensure consistency and AI-parsability.
#### Recipe Template
```markdown
## 🍳 Recipe: [Action-Oriented Title]
[Brief description of what this recipe achieves.]
### 📋 Prerequisites
- [Constraint 1]
- [Constraint 2]
### 🛠️ Step 1: [Preparation/Definition]
[Instructions for preparing data or environment.]
### ⚙️ Step 2: [Configuration/Settings]
[Instructions for configuring settings or environment variables.]
### 🚀 Step 3: [Execution/Action]
[The main GraphQL operation or command to execute.]
### ✅ Step 4: [Verification]
[How to verify that the operation was successful using a query or agent check.]
```
#### Standard Icons for Steps
- 🍳 **Recipe**: Main header icon.
- 📋 **Prerequisites**: Necessary conditions before starting.
- 🛠️ **Preparation**: Initial setup, data definition, or file creation.
- ⚙️ **Configuration**: Changes to settings, `.env`, or Zabbix UI.
- 🚀 **Execution**: The primary action or mutation.
- ✅ **Verification**: Result checking and outcome validation.
- 💡 **Tip/Alternative**: Helpful hints or alternative methods.
- 🤖 **AI/MCP**: Agent-specific instructions.
### Git Standards
- **Commit Messages:** Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) (e.g. `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `style:`).
- If a commit is complex and covers different aspects, the message **must** always contain a detailed list of what was changed within the optional "body" section, in addition to the short "description" in the header.
- **Review & Approval**: Never commit changes automatically. Always present the proposed changes and the updated plan to the user for review. Allow the user to provide feedback or add tasks to the plan before proceeding with a commit.

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
24

124
AGENTS.md
View file

@ -1,124 +0,0 @@
# Zabbix GraphQL API - AI Coding Instructions
## Project Overview
This is a **GraphQL wrapper for Zabbix API** that provides enhanced mass import/export operations, hierarchical host group management, and dynamic schema extensibility. It's built with **Apollo Server**, TypeScript, and uses ES modules throughout.
## Architecture & Key Patterns
### Core Components
- **Entry Point**: `src/index.ts``src/api/start.ts` (Apollo Server setup)
- **Schema Loading**: `src/api/schema.ts` - Dynamic schema merging with extensibility via env vars
- **Resolvers**: `src/api/resolvers.ts` + dynamic hierarchical resolvers via `resolver_helpers.ts`
- **DataSources**: `src/datasources/` - Zabbix API wrappers extending `RESTDataSource`
- **Execution**: `src/execution/` - Complex business logic (importers, deleters)
### Critical Patterns
#### 1. Hierarchical Data Mapping
The core innovation is **automatic mapping** of Zabbix items/tags to GraphQL nested objects:
- Zabbix item key `state.current.values.temperature` → GraphQL `{ state: { current: { values: { temperature } } } }`
- Use `createHierarchicalValueFieldResolver()` in `resolver_helpers.ts`
- Type hints: `json_`, `str_`, `bool_`, `float_` prefixes for value conversion
#### 2. Dynamic Schema Extension (No-Code)
Configure via environment variables:
```bash
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
```
Schema loads dynamically in `schema.ts`, resolvers auto-generated for listed types.
#### 3. Mass Import/Export Pattern
All importers follow hierarchy-first approach:
- **Host Groups**: Process parent groups before children (`getHostGroupHierarchyNames()`)
- **Template Dependencies**: Handle dependent items via deferred creation logic
- **Batch Processing**: Each importer returns array of `Response` objects with success/error details
#### 4. Permission System
Uses Zabbix template groups as permission containers:
- Template groups with prefix `Permissions/` (configurable via `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX`)
- Queries: `hasPermissions()`, `userPermissions()` in resolvers
- Integration: `ZabbixRequestWithPermissions` wrapper class
## Development Workflow
### Essential Commands
```bash
# Development with hot reload
npm run start
# Production build & run
npm run compile && npm run prod
# GraphQL code generation (watch mode)
npm run codegen
# Testing (with ES modules + mocking setup)
npm test
```
### Environment Setup
Key variables (see README for complete list):
```bash
ZABBIX_BASE_URL=http://zabbix.example.com/zabbix
ZABBIX_AUTH_TOKEN=your-super-admin-token
ZABBIX_EDGE_DEVICE_BASE_GROUP=Roadwork
SCHEMA_PATH=./schema/
```
### Testing Patterns
- **ES Module Mocking**: Uses `jest.mock()` with proper module resolution
- **API Mocking**: Mock `zabbixAPI.executeRequest()` and `zabbixAPI.post()`
- **Permission Bypass**: Mock `ZabbixRequestWithPermissions` for unit tests
- **Integration Tests**: Use real GraphQL queries against mocked Zabbix responses
## File Organization & Conventions
### Schema Files (`schema/`)
- `queries.graphql`, `mutations.graphql` - Main API surface
- `devices.graphql`, `zabbix.graphql` - Core types
- `extensions/` - Custom device types for schema extension
### DataSource Naming
- Pattern: `zabbix-{entity}.ts` (e.g., `zabbix-hosts.ts`, `zabbix-templates.ts`)
- Each exports specialized request classes extending `ZabbixRequest<T>`
- Helper classes: `{Entity}Helper` for common operations
### Generated Code
- `src/schema/generated/graphql.ts` - Auto-generated from schema via GraphQL Code Generator
- Enum values imported from `src/model/model_enum_values.ts`
- **Never edit generated files directly**
### Import Statements
**Always use `.js` extensions** in imports due to ES modules:
```typescript
import {logger} from "../logging/logger.js"; // ✅ Correct
import {logger} from "../logging/logger"; // ❌ Will fail
```
## Debugging & Extensions
### Adding New Device Types
1. Create schema in `schema/extensions/{name}.graphql`
2. Add to `ADDITIONAL_SCHEMAS` env var
3. Add type name to `ADDITIONAL_RESOLVERS` env var
4. Ensure Zabbix items use dot-separated keys matching GraphQL field names
### Common Issues
- **Module Resolution**: Check `.js` extensions in imports
- **Permission Errors**: Verify `ZABBIX_AUTH_TOKEN` has Super Admin rights
- **Schema Changes**: Run `npm run codegen` after modifying `.graphql` files
- **Hierarchical Mapping**: Debug via `createHierarchicalValueFieldResolver()` logs
### Integration Points
- **Zabbix API**: All requests go through `ZabbixAPI` class (extends `RESTDataSource`)
- **Authentication**: Supports both API tokens and session cookies
- **WebSocket**: GraphQL subscriptions enabled via `graphql-ws`
- **Docker**: Multi-stage build with `API_VERSION` build arg
## Testing Strategy
Focus on:
1. **Unit Tests**: Mock Zabbix API responses, test business logic
2. **Integration Tests**: Real GraphQL operations with mocked datasources
3. **Permission Tests**: Verify access control logic
4. **Import/Export Tests**: Test hierarchical processing and error handling

View file

@ -1,7 +1,7 @@
# Hint: With node_version>=21.6.0 there are problems with debugging, # Hint: With node_version>=21.6.0 there are problems with debugging,
# therefore the development node version is set to 21.5.0 + in order to keep dev + prod versions aligned # therefore the development node version is set to 24 + in order to keep dev + prod versions aligned
# this was also reflected in the Dockerfile # this was also reflected in the Dockerfile
ARG node_version=21.5.0 ARG node_version=24
#stage1 #stage1
FROM node:${node_version} as builder FROM node:${node_version} as builder

View file

@ -1,14 +0,0 @@
local auth token: 47d92d4c88ed2ac842357e342e8da9c1f272f4ca0d046d1d6a615a5f7a6b1ab2
alt: 9228fc973883eeb5cab421d5b7ecefeb
## Notizen:
- Get Hosts/Hostgroups funktioniert nicht nativ. Custom Group stört
- TODO: Mutation test
- TODO: Erweiterung test und Anleitung
- Übersicht weche zabbix-api funktionen Abgedeckt sind und welche nicht. gg.f mit einem todo versehen um zu sugerieren, dass es kommen könnte
-
- Devices und Hosts nicht hinreichend erklärt im Kontext allDevices
- Integrated management of user roles and user groups.: aber keiner queries zu Roles und groups, hier explizit auf extension hinweisen

829
README.md
View file

@ -1,6 +1,10 @@
# Zabbix GraphQL API # Zabbix GraphQL API
A GraphQL wrapper for the Zabbix API that provides enhanced mass import/export operations, hierarchical host group management, and dynamic schema extensibility. Built with Apollo Server, TypeScript, and ES modules. A modern GraphQL interface for Zabbix, providing enhanced features and easier integration for automation and management.
## Purpose
The Zabbix GraphQL API acts as a wrapper and enhancer for the native Zabbix JSON-RPC API. It simplifies complex operations, provides a strongly-typed schema, and adds advanced logic for importing, querying, and managing Zabbix entities like hosts, templates, and user rights.
## 🚀 Features ## 🚀 Features
@ -19,296 +23,109 @@ A GraphQL wrapper for the Zabbix API that provides enhanced mass import/export o
- **Permission System**: Role-based access control using Zabbix template groups - **Permission System**: Role-based access control using Zabbix template groups
- *Reference*: `schema/api_commons.graphql` (Permission enum, PermissionRequest), `src/api/resolvers.ts` (hasPermissions, userPermissions), `docs/sample_import_permissions_template_groups_mutation.graphql` - *Reference*: `schema/api_commons.graphql` (Permission enum, PermissionRequest), `src/api/resolvers.ts` (hasPermissions, userPermissions), `docs/sample_import_permissions_template_groups_mutation.graphql`
- **Real-time Subscriptions**: WebSocket support for GraphQL subscriptions (infrastructure implemented, but no actual subscriptions defined in schema)
- *Reference*: `src/api/start.ts` (WebSocketServer integration with `graphql-ws`), `AGENTS.md` (mentions WebSocket support), `package.json` (graphql-ws, subscriptions dependencies)
- **Type Safety**: Full TypeScript support with generated types - **Type Safety**: Full TypeScript support with generated types
- *Reference*: `codegen.ts`, `src/schema/generated/graphql.ts`, `tsconfig.json`, `package.json` (devDependencies for GraphQL Codegen) - *Reference*: `codegen.ts`, `src/schema/generated/graphql.ts`, `tsconfig.json`, `package.json` (devDependencies for GraphQL Codegen)
## 📋 Prerequisites - **AI Agent & MCP Enablement**: Native support for Model Context Protocol (MCP) and AI-driven automation. GraphQL's strongly-typed, introspectable nature provides a superior interface for AI agents compared to traditional REST APIs.
- *Reference*: `docs/howtos/mcp.md`, `.ai/mcp/mcp.json` (Sample Config), `mcp-config.yaml`, `docker compose` (MCP service)
> **Planned features**: For an overview of achieved milestones and planned enhancements have a look at the [**Roadmap**](./roadmap.md).
## How-To Guides
For detailed information on specific topics and practical step-by-step instructions, please refer to our guides:
- [**Cookbook**](./docs/howtos/cookbook.md): Practical, task-oriented recipes for quick start and AI test generation.
- [**Schema & Extension Overview**](./docs/howtos/schema.md): Detailed explanation of the schema structure and extension mechanism.
- [**Hierarchical Data Mapping**](./docs/howtos/hierarchical_data_mapping.md): How Zabbix items are mapped to nested GraphQL fields.
- [**Roles & Permissions**](./docs/howtos/permissions.md): Managing user rights through Zabbix template groups.
- [**Technical Maintenance Guide**](./docs/howtos/maintenance.md): Guide on code generation, testing, and Docker maintenance.
- [**Test Specification**](./docs/tests.md): Detailed list of test cases and coverage checklist.
- [**MCP & Agent Integration**](./docs/howtos/mcp.md): Connecting LLMs and autonomous agents via Model Context Protocol.
See the [How-To Overview](./docs/howtos/README.md) for a complete list of documentation.
## How to Install and Start
### 📋 Prerequisites
Before you begin, ensure you have met the following requirements: Before you begin, ensure you have met the following requirements:
- **Node.js** >= 18.x (tested with v21.5.0) - **Node.js**: Version 24 (LTS) or higher recommended.
- **NPM** >= 8.x - **Docker**: Version 27 or higher and **Docker Compose** v2.29 or higher (use `docker compose` instead of `docker-compose`).
- **Zabbix Server** with API access - **Zabbix**: A running Zabbix instance (compatible with Zabbix 6.0+) with API access.
- **Zabbix Super Admin Token** (for full functionality) - **Zabbix Super Admin Token** (for full functionality / privilege escalation).
- **Zabbix User Access** (groups and roles depending on your use case).
## 🛠️ Installation
### Clone the repository
### 🛠️ Installation
- Clone the repository:
```bash ```bash
git clone https://github.com/your-repo/zabbix-graphql-api.git git clone <repository-url>
cd zabbix-graphql-api cd zabbix-graphql-api
``` ```
### Install dependencies - Install dependencies:
```bash ```bash
npm install npm install
``` ```
### Starting the API
#### Development Mode
Starts the server with `nodemon` and `tsx` for automatic reloading:
```bash
npm run start
```
#### Production Mode
Builds the project and runs the compiled code:
```bash
npm run prod
```
The API will be available at `http://localhost:4000/`.
## ⚙️ Configuration ## ⚙️ Configuration
### Environment Variables ### Environment Variables
Create a `.env` file in the project root with the following variables: The API is configured via environment variables. Create a `.env` file or set them in your environment:
```bash
# Zabbix connection
ZABBIX_BASE_URL=http://your-zabbix-server/zabbix
ZABBIX_AUTH_TOKEN=your-super-admin-token
ZABBIX_AUTH_TOKEN_FOR_REQUESTS=your-super-admin-token
# Optional configuration
ZABBIX_EDGE_DEVICE_BASE_GROUP=Roadwork
ZABBIX_ROADWORK_BASE_GROUP=Roadwork
ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX=Permissions
SCHEMA_PATH=./schema/
# Dynamic schema extension (optional)
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
# Logging
LOG_LEVELS=info
```
### Environment Variable Details
| Variable | Description | Default | Required | | Variable | Description | Default | Required |
|----------|-------------|---------|----------| |----------|-------------|---------|----------|
| `ZABBIX_BASE_URL` | URL to your Zabbix server (include `/zabbix` path) | - | Yes | | `ZABBIX_BASE_URL` | URL to your Zabbix server (include `/zabbix` path) | - | Yes |
| `ZABBIX_AUTH_TOKEN` | Zabbix Super Admin API token for administrative operations (full permissions needed for import/export operations) | - | Yes | | `ZABBIX_PRIVILEGE_ESCALATION_TOKEN` | Zabbix Super Admin API token used for privilege escalation (e.g. during import operations) | - | Yes |
| `ZABBIX_AUTH_TOKEN_FOR_REQUESTS` | Token used for automated requests (can be same as ZABBIX_AUTH_TOKEN or a different token with limited permissions for routine operations) | - | Yes* | | `ZABBIX_DEVELOPMENT_TOKEN` | Token used ONLY for local development and isolated testing to simulate a "real end user" | - | No |
| `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base group for edge devices | - | No | | `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base group for edge devices | `Roadwork` | No |
| `ZABBIX_ROADWORK_BASE_GROUP` | Base group for roadwork devices | - | No |
| `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for permission template groups (used to identify permission-related template groups in Zabbix) | `Permissions` | No | | `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for permission template groups (used to identify permission-related template groups in Zabbix) | `Permissions` | No |
| `SCHEMA_PATH` | Path to schema files | `./schema/` | No | | `SCHEMA_PATH` | Path to schema files | `./schema/` | No |
| `ADDITIONAL_SCHEMAS` | Comma-separated list of additional schema files | - | No | | `ADDITIONAL_SCHEMAS` | Comma-separated list of additional schema files | - | No |
| `ADDITIONAL_RESOLVERS` | Comma-separated list of resolver types to generate | - | No | | `ADDITIONAL_RESOLVERS` | Comma-separated list of resolver types to generate | - | No |
| `LOG_LEVELS` | Log level configuration | - | No | | `LOG_LEVEL` | Log level configuration (e.g. `debug`, `info`, `warn`, `error`) | `info` | No |
| `DRY_RUN` | If set, runs in dry run mode without starting the server (exits immediately after initialization, useful for testing configuration) | - | No |
| `HOST_TYPE_FILTER_DEFAULT` | Default filter for host types | - | No | | `HOST_TYPE_FILTER_DEFAULT` | Default filter for host types | - | No |
| `HOST_GROUP_FILTER_DEFAULT` | Default filter for host groups | - | No | | `HOST_GROUP_FILTER_DEFAULT` | Default filter for host groups | - | No |
*Note: `ZABBIX_AUTH_TOKEN_FOR_REQUESTS` is required when different from `ZABBIX_AUTH_TOKEN`, otherwise the same token will be used for all operations. ## 🔐 Authorization
### Authentication Tokens Explanation The application operates with different authentication and authorization mechanisms depending on the scenario:
The API supports two different authentication tokens for security separation: ### 🛡️ Production Use
- `ZABBIX_AUTH_TOKEN`: Used for administrative operations like import/export, requires Super Admin privileges
- `ZABBIX_AUTH_TOKEN_FOR_REQUESTS`: Used for routine read operations, can have limited permissions
### Edge Device Group Pattern In production environments, the `ZABBIX_DEVELOPMENT_TOKEN` should **always be unset** to ensure proper security.
The `ZABBIX_EDGE_DEVICE_BASE_GROUP` (or `ZABBIX_ROADWORK_BASE_GROUP`) is used to create a regex pattern `FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX` that identifies edge device groups in Zabbix. This pattern follows the format `^(${ZABBIX_EDGE_DEVICE_BASE_GROUP})\/` and is used to filter and process edge device related data. - **Zabbix Frontend Widgets**: When accessing the API from widgets embedded in the Zabbix frontend, no static token is needed. Authentication is automatically derived from the `zbx_session` cookie provided by the Zabbix web login, which is forwarded to the Zabbix API.
- **Other Production Tools**: For other purposes (e.g. accessing the API from external tools or scripts), a Zabbix session or auth token must be passed via the `zabbix-auth-token` HTTP header.
### Configuration Examples > **Recipe**: See [Managing User Permissions](./docs/howtos/cookbook.md#recipe-managing-user-permissions) for setup instructions.
#### Development Configuration (.env.development) ### 🔑 Privilege Escalation
```bash
# Zabbix connection
ZABBIX_BASE_URL=http://localhost:8080/zabbix
ZABBIX_AUTH_TOKEN=your-dev-super-admin-token
ZABBIX_AUTH_TOKEN_FOR_REQUESTS=your-dev-super-admin-token
# Optional configuration for development - **`ZABBIX_PRIVILEGE_ESCALATION_TOKEN`**: Certain operations, such as importing hosts, templates, or user rights, require Super Admin access to Zabbix for specific parts of the process. This token allows the API to perform these administrative tasks even when the initiating user does not have Super Admin rights themselves.
ZABBIX_EDGE_DEVICE_BASE_GROUP=DevEdge
ZABBIX_ROADWORK_BASE_GROUP=DevRoadwork
ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX=Permissions
SCHEMA_PATH=./schema/
LOG_LEVELS=debug
# Dynamic schema extension (optional) ### 🧪 Local Development and Testing
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
```
#### Production Configuration (.env.production) - **`ZABBIX_DEVELOPMENT_TOKEN`**: This environment variable is intended **only** for local development and isolated testing. It allows developers to "simulate" a Zabbix access token representing a "real end user" without needing to provide the HTTP header in every request.
```bash - **Warning**: This should be avoided in production as it undermines security by bypassing per-request authentication.
# Zabbix connection
ZABBIX_BASE_URL=https://zabbix.company.com/zabbix
ZABBIX_AUTH_TOKEN=your-prod-super-admin-token
ZABBIX_AUTH_TOKEN_FOR_REQUESTS=your-prod-read-only-token
# Production configuration ### 🗺️ Zabbix to GraphQL Mapping
ZABBIX_EDGE_DEVICE_BASE_GROUP=ProductionEdge
ZABBIX_ROADWORK_BASE_GROUP=ProductionRoadwork
ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX=Permissions
SCHEMA_PATH=./schema/
LOG_LEVELS=info
# Dynamic schema extension for production
ADDITIONAL_SCHEMAS=/app/schema/extensions/display_devices.graphql,/app/schema/extensions/location_tracker_devices.graphql
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
# Performance and operational settings
HOST_TYPE_FILTER_DEFAULT=device
HOST_GROUP_FILTER_DEFAULT=production
```
## ▶️ Running the Application
### Development Mode
```bash
# Start with hot reloading
npm run start
```
The GraphQL API will be available at `http://localhost:4000/`
#### Available Queries and Mutations after start
#### Queries
- `apiVersion` - Returns the API build version
- `zabbixVersion` - Returns the version of the connected Zabbix instance
- `login` - Authenticates with Zabbix and returns an authentication token
- `logout` - Logs out from Zabbix, invalidating the current session
- `allHosts` - Returns all hosts and their items
- `allDevices` - Returns all devices and their items (hosts with deviceType)
- `allHostGroups` - Returns all host groups
- `locations` - Returns all locations used by hosts
- `exportHostValueHistory` - Exports value history for Zabbix items
- `userPermissions` - Returns all user permissions
- `hasPermissions` - Checks if the current user has requested permissions
- `exportUserRights` - Exports user rights (roles and groups)
- `templates` - Returns templates
- `allTemplateGroups` - Returns all template groups
#### Mutations
- `createHost` - Creates a single host in Zabbix
- `importHostGroups` - Mass import Zabbix host groups
- `importHosts` - Mass import hosts and assign them to host groups
- `importUserRights` - Import user rights (roles and groups) into Zabbix
- `importTemplateGroups` - Mass import template groups
- `importTemplates` - Mass import templates
- `deleteTemplates` - Delete templates by their IDs or by a name pattern
- `deleteTemplateGroups` - Delete template groups by their IDs or by a name pattern
### Production Build
```bash
# Compile TypeScript
npm run compile
# Run production build
npm run prod
```
### Available Scripts
| Script | Description |
|--------|-------------|
| `npm run start` | Start development server with hot reload |
| `npm run compile` | Compile TypeScript to JavaScript |
| `npm run prod` | Run production build |
| `npm run test` | Run tests |
| `npm run codegen` | Generate TypeScript types from GraphQL schema |
| `npm run nodemon` | Alternative development runner |
## 🐳 Docker Deployment
### Using Pre-built Images
The application can be deployed using Docker:
```bash
# Build the image
docker build -t zabbix-graphql-api .
# Run the container
docker run -d \
--name zabbix-graphql \
-p 4000:4000 \
-e ZABBIX_BASE_URL=http://your-zabbix-server/zabbix \
-e ZABBIX_AUTH_TOKEN=your-super-admin-token \
zabbix-graphql-api
```
### Building Your Own Image
```bash
# Build with custom API version
docker build --build-arg API_VERSION=v1.0.0 -t zabbix-graphql-api .
# Run with environment file
docker run -d \
--name zabbix-graphql \
-p 4000:4000 \
--env-file .env \
zabbix-graphql-api
```
## 🔐 Roles and Permissions Extension
The API implements a permission system using Zabbix template groups:
### Permission Template Groups
- Template groups with prefix `Permissions/` (configurable via `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX`) are used for permissions
- Users gain permissions by being assigned to user groups that have access to these permission template groups
### Available Permissions
The system supports three permission levels defined in `schema/api_commons.graphql`:
- `DENY`: Explicitly denies access (supersedes other permissions)
- `READ`: Allows viewing/reading access
- `READ_WRITE`: Allows both reading and writing (implies READ permission)
### Permission Object Names
Permission object names map to Zabbix template group paths: `Permissions/{objectName}`
### GraphQL Permission Queries
```graphql
# Check if current user has specific permissions
query HasPermissions {
hasPermissions(permissions: [
{ objectName: "hosts", permission: READ },
{ objectName: "templates", permission: READ_WRITE }
])
}
# Get all user permissions
query GetUserPermissions {
userPermissions(objectNames: ["hosts", "templates"])
}
```
### Setting Up Permissions
1. Create template groups with the prefix `Permissions/` (e.g., `Permissions/hosts`, `Permissions/templates`)
2. Assign these template groups to user groups in Zabbix with appropriate permission levels
3. Users in those user groups will inherit the permissions
### Detailed Permission Usage Examples
For comprehensive examples of permission usage patterns, see `schema/api_commons.graphql` which contains detailed documentation in the `PermissionRequest` input type comments, including real-world examples of how to model permissions for buttons, status controls, and application features.
See also `docs/sample_import_permissions_template_groups_mutation.graphql` for examples of importing permission template groups.
## 📊 Schema and Schema Extension
### Main Schema Structure
The GraphQL schema is located in the `schema/` directory and consists of:
- `queries.graphql` - Query definitions (see detailed documentation in file comments)
- `mutations.graphql` - Mutation definitions (see detailed documentation in file comments)
- `devices.graphql` - Device-related types (see detailed documentation in file comments)
- `zabbix.graphql` - Zabbix-specific types (see detailed documentation in file comments)
- `device_value_commons.graphql` - Common value types (see detailed documentation in file comments)
- `api_commons.graphql` - Common API types and permission system (see detailed documentation in file comments)
- `extensions/` - Custom device type extensions
For comprehensive understanding of each operation, read the detailed comments in the respective schema files.
### Zabbix to GraphQL Mapping
The API maps Zabbix entities to GraphQL types as follows: The API maps Zabbix entities to GraphQL types as follows:
@ -318,441 +135,115 @@ The API maps Zabbix entities to GraphQL types as follows:
| Host Group | `HostGroup` | Represents a Zabbix host group | | Host Group | `HostGroup` | Represents a Zabbix host group |
| Template | `Template` | Represents a Zabbix template | | Template | `Template` | Represents a Zabbix template |
| Template Group | `HostGroup` | Represents a Zabbix template group | | Template Group | `HostGroup` | Represents a Zabbix template group |
| Item | Nested fields in `Device` | Zabbix items become nested fields in the device based on their key names | | Item | Nested fields | Zabbix items become nested fields based on their key names (hierarchical mapping) |
| Tag | `Tag` | Represents a Zabbix tag associated with a host or template | | Tag | `Tag` | Represents a Zabbix tag associated with a host or template |
| Inventory | `Location` | Host inventory information maps to location data | | Inventory | `Location` | Host inventory information maps to location data |
### Zabbix Entity Relationships > **Detailed Guide**: For a deeper dive into how Zabbix items are transformed into GraphQL fields, see [Hierarchical Data Mapping](./docs/howtos/hierarchical_data_mapping.md).
- **Host Groups**: Organize hosts and templates hierarchically; represented as `HostGroup` objects in GraphQL
- **Templates**: Contain items and other configuration that can be applied to hosts; linked via template groups
- **Items**: Individual metrics collected from hosts; automatically mapped to nested GraphQL fields based on their key names
- **Tags**: Metadata associated with hosts/templates; used for classification and filtering
### Location Type Usage ## Running with Docker
The `Location` type represents geographical information from Zabbix host inventory: ### 📦 Using the Pre-built Image
- **Fields**: Includes `name`, `location_lat`, `location_lon`, and other inventory attributes You can run the API without building it locally by pulling the latest image from the Hilbig IT Forgejo infrastructure:
- **Usage**: Available through the `locations` query and as part of host/device objects
- **Access**: Retrieved via the `getLocations` method in the Zabbix API datasource
### Dynamic Schema Extension
Extend the schema without code changes using environment variables:
```bash ```bash
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql docker pull forgejo.tooling.hilbigit.com/zabbix/zabbix-graphql-api:latest
```
Start the container by passing the required environment variables:
```bash
docker run -d \
--name zabbix-graphql-api \
-p 4000:4000 \
-e ZABBIX_BASE_URL=http://your-zabbix-instance/zabbix \
-e ZABBIX_PRIVILEGE_ESCALATION_TOKEN=your-super-admin-token \
forgejo.tooling.hilbigit.com/zabbix/zabbix-graphql-api:latest
```
### 🏗️ Building Locally
If you prefer to build the image yourself using the provided `Dockerfile`:
* Build the image (ensure you provide an `API_VERSION`):
```bash
docker build -t zabbix-graphql-api --build-arg API_VERSION=1.0.0 .
```
* Run the container:
```bash
docker run -d \
--name zabbix-graphql-api \
-p 4000:4000 \
-e ZABBIX_BASE_URL=http://your-zabbix-instance/zabbix \
-e ZABBIX_PRIVILEGE_ESCALATION_TOKEN=your-super-admin-token \
zabbix-graphql-api
```
## Sample Application: Virtual Control Room (VCR)
The **Virtual Control Room (VCR)** is a professional cockpit and control center application designed for monitoring and managing large-scale deployments of IoT and Edge devices, such as traffic management systems, roadwork safety equipment, and environmental sensors.
### 🧩 How VCR uses the GraphQL API
- **Unified Cockpit**: VCR utilizes the API's **hierarchical mapping** to provide a unified view of diverse device types. It maps Zabbix items and tags directly to structured GraphQL objects (e.g. `operational` telemetry and `current` business state).
- **Dynamic Authorization**: The `hasPermissions` query is used to implement a **Dynamic UI**. Buttons, controls, and status indicators are shown or enabled only if the user has the required `READ` or `READ_WRITE` permissions for that specific object.
- **Mass Provisioning**: VCR leverages the **mass import** capabilities to provision thousands of devices and templates in a single operation, significantly reducing manual configuration effort in Zabbix.
- **Data Visualization**: It uses the `exportHostValueHistory` endpoint to power dashboards showing historical trends, such as traffic density, battery levels, or sensor readings over time.
For more detailed information about the VCR product, please refer to the technical presentation:
[VCR - Technical product information](docs/VCR%20-%20Technical%20product%20information.pdf)
## Sample Environment File
Below is a complete example of a `.env` file showing all available configuration options:
```env
# Zabbix Connection
ZABBIX_BASE_URL=http://your-zabbix-instance/zabbix
ZABBIX_PRIVILEGE_ESCALATION_TOKEN=your-super-admin-token-here
# General Configuration
ZABBIX_EDGE_DEVICE_BASE_GROUP=Roadwork
API_VERSION=1.0.0
SCHEMA_PATH=./schema/
HOST_GROUP_FILTER_DEFAULT=Roadwork/Devices/*
HOST_TYPE_FILTER_DEFAULT=Roadwork/Devices
# Schema Extensions (No-Code)
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
# Logging
# LOG_LEVEL=debug
``` ```
This enables runtime schema extension for custom device types without modifying the core code. ## Usage Samples
### Sample Operations The `docs/queries` directory contains several sample GraphQL queries and mutations to help you get started.
For practical examples of schema usage, see the sample files in the `docs/` directory: > **Samples Reference**: See the [Sample Queries & Mutations Overview](./docs/queries/README.md) for a categorized list of examples.
- `docs/sample_all_devices_query.graphql` - Example of querying all devices
- `docs/sample_import_templates_mutation.graphql` - Example of importing templates
- `docs/sample_import_host_groups_mutation.graphql` - Example of importing host groups
## 🗂️ Hierarchical Data Mapping
The API automatically maps Zabbix items to nested GraphQL objects using the `createHierarchicalValueFieldResolver` function:
- Zabbix item key `state.current.values.temperature` → GraphQL `{ state: { current: { values: { temperature } } } }`
- Type hints: `json_`, `str_`, `bool_`, `float_` prefixes for value conversion
- Automatic resolution of hierarchical structures from flat Zabbix item keys
The `createHierarchicalValueFieldResolver` function (found in `src/api/resolver_helpers.ts`) dynamically creates resolvers that map Zabbix items or tags to hierarchical GraphQL structures. It uses field names and Zabbix item keys (dot-separated) to automatically resolve nested objects, eliminating the need for manual resolver definitions for each hierarchical field.
For detailed information about the mapping mechanism and type hinting, see the comments in `schema/device_value_commons.graphql` and `src/api/resolver_helpers.ts`.
See `docs/sample_all_devices_query.graphql` for examples of how hierarchical data appears in query results.
## 🏷️ Zabbix Tags Usage
Zabbix tags are used for:
- Device classification (`deviceType` tag)
- Host categorization (`hostType` tag)
- Custom metadata storage
- Permission assignment through template groups
For detailed information about tag usage and available tag types, see the comments in `schema/devices.graphql` and `schema/zabbix.graphql`.
See `docs/sample_all_hosts_query.graphql` and `docs/sample_all_devices_query.graphql` for examples of how tags appear in query results.
## 🧪 Testing
Run the test suite:
```bash
npm test
```
The testing strategy includes:
- Unit tests with mocked Zabbix API responses
- Integration tests with real GraphQL operations
- Permission system validation
- Import/export functionality testing
## 📖 Detailed Documentation Through Comments
This project uses extensive inline documentation in the form of comments within the GraphQL schema files and documentation samples. For the most comprehensive understanding of the API, its capabilities, and usage patterns, read the detailed comments in:
- `schema/queries.graphql` - Complete documentation of all query operations
- `schema/mutations.graphql` - Complete documentation of all mutation operations with usage examples
- `schema/api_commons.graphql` - Detailed explanation of the permission system with real-world usage examples
- `docs/*.graphql` - Practical examples of all operations with sample variables and expected outcomes
The comments in these files contain real-world usage examples, implementation notes, and detailed explanations that go beyond what's covered in this README.
## 📖 Example: Extending Schema with Distance Tracker Device
### Zabbix-Side Configuration
Before extending the schema, you need to configure Zabbix appropriately:
#### 0. Template Group configuration
- Create a template group `Templates/Roadwork/Devices`
#### 1. Host Configuration in Zabbix
- Create a host in Zabbix with the tag `deviceType` set to `distance-tracker`
- Add items to the host with hierarchical keys that match your GraphQL fields:
- `distance.current` (for the `distance` field)
- `battery.level` (for the `batteryLevel` field)
- `status.lastSeen` (for the `lastSeen` field)
- `location.latitude` and `location.longitude` (for the `location` object)
#### 2. Item Key Naming Convention
The API automatically maps dotted item keys to nested GraphQL objects:
- Zabbix item key `distance.current` → GraphQL `{ distance }`
- Zabbix item key `battery.level` → GraphQL `{ batteryLevel }`
- Zabbix item key `location.latitude` → GraphQL `{ location: { location_lat } }`
### Schema Extension Steps
#### 1. Create Schema Extension
Create `schema/extensions/distance_tracker_device.graphql`:
```graphql
type DistanceTrackerDevice {
id: String
name: String
location: Location
distance: Float
batteryLevel: Float
lastSeen: DateTime
}
```
#### 2. Configure Environment Variables
```bash
ADDITIONAL_SCHEMAS=./schema/extensions/distance_tracker_device.graphql
ADDITIONAL_RESOLVERS=DistanceTrackerDevice
```
#### 3. Import Template via GraphQL Mutation
Instead of manually creating items in Zabbix, you can import a complete template using the GraphQL API:
```graphql
mutation ImportDistanceTrackerTemplate($templates: [CreateTemplate!]!) {
importTemplates(templates: $templates) {
host
templateid
message
error {
message
code
data
}
}
}
```
Variables:
```json
{
"templates": [
{
"host": "DISTANCE_TRACKER",
"name": "Distance Tracker Device Template",
"groupNames": ["Templates/Roadwork/Devices"],
"tags": [
{ "tag": "deviceType", "value": "DistanceTrackerDevice" }
],
"items": [
{
"name": "Distance Current Value",
"type": 0,
"key": "distance.current",
"value_type": 0,
"history": "7d",
"units": "m"
},
{
"name": "Battery Level",
"type": 0,
"key": "battery.level",
"value_type": 0,
"history": "7d",
"units": "%"
},
{
"name": "Last Seen Timestamp",
"type": 0,
"key": "status.lastSeen",
"value_type": 4,
"history": "30d"
},
{
"name": "Location Latitude",
"type": 0,
"key": "location.latitude",
"value_type": 0,
"history": "30d",
"units": "deg"
},
{
"name": "Location Longitude",
"type": 0,
"key": "location.longitude",
"value_type": 0,
"history": "30d",
"units": "deg"
}
]
}
]
}
```
This creates a template in Zabbix with all the required items that map to your GraphQL fields.
#### 4. Link Hosts to the Template
After importing the template, link your hosts to it in Zabbix or via the importHosts mutation:
```graphql
mutation ImportHosts($hosts: [CreateHost!]!) {
importHosts(hosts: $hosts) {
deviceKey
hostid
message
error {
message
code
data
}
}
}
```
Variables:
```json
{
"hosts": [
{
"deviceKey": "DistanceTracker001",
"name": "Distance Tracker 001",
"deviceType": "distance-tracker",
"groupNames": ["Roadwork/Devices"],
"templateids": [12345] // Use the templateid returned from importTemplates
}
]
}
```
#### 5. Use in Queries
Once the template and host are set up in Zabbix, query the data via GraphQL:
```graphql
query GetDistanceTrackers {
allDevices(tag_deviceType: ["distance-tracker"]) {
... on DistanceTrackerDevice {
id
name
location {
name
location_lat
location_lon
}
distance
batteryLevel
lastSeen
}
}
}
```
#### 6. Expected Response Structure
Based on the Zabbix configuration above, the API will return:
```json
{
"data": {
"allDevices": [
{
"id": "12345",
"name": "Distance Tracker 001",
"location": {
"name": "Main Office",
"location_lat": "37.7749",
"location_lon": "-122.4194"
},
"distance": 42.5,
"batteryLevel": 87.2,
"lastSeen": "2023-10-15T10:30:00Z"
}
]
}
}
```
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## 🏗️ Architecture
The Zabbix GraphQL API follows a layered architecture pattern with clear separation of concerns:
![Architecture Diagram](docs/arch-diagram.svg)
<details>
```PlantUML
@startuml
title Zabbix GraphQL API - Simplified Data Flow
[GraphQL Client] as client
[Apollo Server\n(Entry Point)] as server
[Schema Loader\nDynamic Merging] as schema
[Resolvers\nHierarchical] as resolvers
[Data Sources\nZabbix API Wrappers] as datasources
[Zabbix Server] as zabbix
[Execution Layer\nBusiness Logic] as execution
client -> server : "Query/Mutation"
server -> schema : "Resolve Schema"
schema -> resolvers : "Route to Resolver"
resolvers -> datasources : "Fetch Data"
datasources -> zabbix : "API Call"
zabbix -> datasources : "Response"
datasources -> resolvers : "Process"
resolvers -> server : "Format"
server -> client : "Result"
resolvers --> execution : "Complex Operations"
execution --> datasources : "Use DataSources"
@enduml
```
</details>
### Core Components
- **Entry Point**: `src/index.ts``src/api/start.ts` (Apollo Server setup)
- **Schema Loading**: `src/api/schema.ts` - Dynamic schema merging with extensibility via environment variables
- **Resolvers**: `src/api/resolvers.ts` + dynamic hierarchical resolvers via `resolver_helpers.ts`
- **DataSources**: `src/datasources/` - Zabbix API wrappers extending `RESTDataSource`
- **Execution**: `src/execution/` - Complex business logic (importers, deleters)
### Data Flow
1. **Client Request**: GraphQL queries/mutations arrive at the Apollo Server
2. **Schema Resolution**: Schema loader merges base schema with additional schemas (if configured)
3. **Resolver Execution**: Resolvers handle the request, using data sources to communicate with Zabbix
4. **Zabbix Communication**: Data sources make API calls to the Zabbix server
5. **Response Processing**: Results are processed and returned to the client
### DataSources Structure
The `src/datasources/` directory contains specialized Zabbix API wrappers:
- `zabbix-api.ts`: Main Zabbix API class extending `RESTDataSource`, handles authentication and communication
- `zabbix-{entity}.ts`: Specialized request classes for specific Zabbix entities (e.g., hosts, templates, etc.)
- Each DataSource follows the pattern of extending `ZabbixRequest<T>` for type-safe API calls
### Key Architectural Patterns
#### 1. Hierarchical Data Mapping
Automatic mapping of Zabbix items/tags to GraphQL nested objects:
- Zabbix item key `state.current.values.temperature` → GraphQL `{ state: { current: { values: { temperature } } } }`
- Implemented via `createHierarchicalValueFieldResolver()` in `resolver_helpers.ts`
- Type hints: `json_`, `str_`, `bool_`, `float_` prefixes for value conversion
#### 2. Dynamic Schema Extension (No-Code)
Configure via environment variables:
```bash
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
```
#### 3. Mass Import/Export Pattern
All importers follow hierarchy-first approach:
- **Host Groups**: Process parent groups before children (`getHostGroupHierarchyNames()`)
- **Template Dependencies**: Handle dependent items via deferred creation logic
- **Batch Processing**: Each importer returns array of `Response` objects with success/error details
#### 4. Permission System
Uses Zabbix template groups as permission containers:
- Template groups with prefix `Permissions/` (configurable via `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX`)
- Queries: `hasPermissions()`, `userPermissions()` in resolvers
- Integration: `ZabbixRequestWithPermissions` wrapper class
## 📄 License
This project is licensed under the AGPL-3.0 License - see the [LICENSE](LICENSE) file for details.
## 📚 Schema and Documentation
### Schema Files as Documentation
The schema files in the `schema/` directory serve as both API definition and documentation. Each type, field, and operation includes detailed comments explaining usage, parameters, and examples. For comprehensive understanding of the API, read the comments in:
- `schema/queries.graphql` - Query operations with detailed parameter descriptions
- `schema/mutations.graphql` - Mutation operations with input type documentation
- `schema/api_commons.graphql` - Common types and permission system documentation
- `schema/devices.graphql` - Device-related types and hierarchies
- `schema/zabbix.graphql` - Zabbix-specific mappings and types
### Documentation Files
The `docs/` directory contains sample GraphQL queries and mutations that demonstrate how to use the API:
- `sample_all_*.graphql` - Sample queries for retrieving hosts, devices, templates, and groups
- `sample_import_*.graphql` - Sample mutations for importing various Zabbix entities
- `sample_delete_*.graphql` - Sample mutations for deleting templates and template groups
- `sample_export_*.graphql` - Sample queries for exporting user rights and other data
- `VCR - Technical product information.pdf` - Technical documentation for specific implementations
Of particular interest:
- `sample_import_distance_tracker_template.graphql` - Complete example of importing a template for distance tracker devices
- `sample_import_user_rights_mutation.graphql` - Example of importing user permissions
- `sample_import_permissions_template_groups_mutation.graphql` - Example of importing permission template groups
### Reading Comments for Detailed Documentation
For detailed documentation with usage examples and implementation notes, read the comments in the schema and documentation files. The project creator has embedded extensive documentation as comments in the GraphQL schema files, which provide real-world usage examples and implementation guidance that complement this README.
## 🆘 Support
For support, please open an issue in the GitHub repository.
## 🔄 API Version ## 🔄 API Version
Current API version: ${API_VERSION:-"Not set"} The API version is automatically set during the Docker build process based on the Git tag or commit hash. This version information is embedded into the Docker image and becomes accessible through the `API_VERSION` environment variable at runtime.
The API version is automatically set during the Docker build process based on the Git tag or commit hash. During CI/CD deployment (as defined in `.forgejo/workflows/deploy-docker.yaml`), the API_VERSION is determined using `git describe --tags --always`, which creates a version identifier based on the most recent Git tag and the number of commits since that tag. This version information is then embedded into the Docker image as a build argument and becomes accessible through the API_VERSION environment variable at runtime. ### 🔧 Zabbix Version Compatibility
### Zabbix Version Compatibility This API is designed to work with Zabbix 7.4, which is the version it runs productively with. While it may work with earlier versions (like 6.0+), 7.4 is the officially supported and tested version.
This API is designed to work with Zabbix 7.4, which is the version it runs productively with. The API wraps the Zabbix 7.4 API and has been tested extensively with this version. While it may work with other Zabbix versions, 7.4 is the officially supported and tested version. ## 🛠️ Technical Maintenance
For information on code generation, running tests, and managing the project's development lifecycle, please refer to the [Technical Maintenance Guide](./docs/howtos/maintenance.md).
## 🔗 External Resources
- **Zabbix API Documentation**: [https://www.zabbix.com/documentation/7.0/en/manual/api](https://www.zabbix.com/documentation/7.0/en/manual/api)
- **Apollo Server Documentation**: [https://www.apollographql.com/docs/apollo-server/](https://www.apollographql.com/docs/apollo-server/)
- **Model Context Protocol (MCP)**: [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/)
## License
This project is licensed under the **GNU Affero General Public License v3.0**. See the [LICENSE](LICENSE) file for details.

View file

@ -16,8 +16,7 @@ const config: CodegenConfig = {
declarationKind: 'interface' declarationKind: 'interface'
} }
} }
}, }
watch: true
}; };
export default config; export default config;

40
docker-compose.yml Normal file
View file

@ -0,0 +1,40 @@
services:
zabbix-graphql-api:
build:
context: .
args:
- API_VERSION=1.0.0
ports:
- "4001:4000"
env_file:
- .env
environment:
- SCHEMA_PATH=/usr/app/dist/schema/
- ZABBIX_DEVELOPMENT_TOKEN=${ZABBIX_DEVELOPMENT_TOKEN}
apollo-mcp-server:
image: ghcr.io/apollographql/apollo-mcp-server:latest
ports:
- "3000:8000"
volumes:
- ./mcp-config.yaml:/mcp-config.yaml
- mcp-shared:/mcp-data:ro
- ./mcp/operations:/mcp/operations
command: /mcp-config.yaml
environment:
- APOLLO_GRAPH_REF=local@main
depends_on:
schema-gen:
condition: service_completed_successfully
zabbix-graphql-api:
condition: service_started
schema-gen:
image: alpine
volumes:
- ./schema:/schema:ro
- mcp-shared:/mcp-data
command: sh -c "cat /schema/*.graphql > /mcp-data/schema.graphql"
volumes:
mcp-shared:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

32
docs/howtos/README.md Normal file
View file

@ -0,0 +1,32 @@
# How-To Guides
This directory contains detailed guides on how to use and extend the Zabbix GraphQL API.
## Available Guides
### 🍳 [Cookbook](./cookbook.md)
Practical, step-by-step recipes for common tasks, designed for both humans and AI-based test generation.
### 📊 [Schema and Schema Extension](./schema.md)
Learn about the GraphQL schema structure, how Zabbix entities map to GraphQL types, and how to use the dynamic schema extension system.
### 🗂️ [Hierarchical Data Mapping](./hierarchical_data_mapping.md)
Understand how the API automatically maps flat Zabbix item keys into nested GraphQL objects using hierarchical resolvers and type hinting.
### 🔐 [Roles and Permissions Extension](./permissions.md)
Discover how the permission system works, how to define permission levels using Zabbix template groups, and how to query user permissions.
### 🛠️ [Technical Maintenance](./maintenance.md)
Guide on code generation (GraphQL Codegen), running Jest tests, and local Docker builds.
### 🧪 [Test Specification](../tests.md)
Detailed list of test cases, categories (Unit, Integration, E2E), and coverage checklist.
### 🤖 [MCP & Agent Integration](./mcp.md)
Discover how to integrate with the Model Context Protocol (MCP) to enable LLMs and autonomous agents to interact with Zabbix efficiently.
---
## 🔍 Additional Resources
- **[Sample Queries](../queries/README.md)**: Categorized list of practical GraphQL operation examples.
- **[Main README](../../README.md)**: Technical reference, configuration, and environment setup.

580
docs/howtos/cookbook.md Normal file
View file

@ -0,0 +1,580 @@
# Zabbix GraphQL API Cookbook
This cookbook provides step-by-step "recipes" for common tasks. These instructions are designed to be easy for humans to follow and structured enough for AI agents (using the MCP server) to generate test cases.
## 🤖 AI-Based Test Generation
To generate a test case from a recipe:
- Start the `zabbix-graphql` MCP server.
- Provide the recipe to your AI agent.
- Ask the agent to "Implement a test case for this recipe using the Zabbix GraphQL API".
- The agent will use the MCP server to explore the schema and generate appropriate GraphQL operations.
---
## 🍳 Recipe: Executing Zabbix 7.4 Documentation Samples
This recipe shows how to execute standard Zabbix 7.4 API examples using their GraphQL equivalents. These samples are directly derived from the official Zabbix documentation and adapted for use with this GraphQL API.
### 📋 Prerequisites
- Zabbix GraphQL API is running.
- You have a valid Zabbix user account and are logged in (or have an auth token).
### 🛠️ Step 1: Browse Available Samples
All samples derived from the Zabbix 7.4 documentation are stored in the following directory:
- `docs/queries/from_zabbix_docs/`
Each `.graphql` file in this directory contains a reference link to the original Zabbix documentation source.
### ⚙️ Step 2: Extract Query and Variables
Each sample file in `docs/queries/from_zabbix_docs/` is structured to include both the GraphQL operation and a corresponding set of sample variables.
1. **Open the Sample**: Open the desired `.graphql` file (e.g. `createHost.graphql`).
2. **Copy the Query**: Copy the GraphQL code block found under the `### Query` header.
3. **Copy the Variables**: Copy the JSON code block found under the `### Variables` header.
### 🚀 Step 3: Execution/Action
Choose a sample and execute it against the GraphQL endpoint using your preferred client (like Apollo Studio, GraphiQL, or Postman).
- **Configure the Request**:
- Paste the **Query** into the operation window.
- Paste the **Variables** JSON into the variables window.
- **Run the Operation**: Click "Execute" or "Play".
#### Example: Creating a Host
Using the content from `docs/queries/from_zabbix_docs/createHost.graphql`:
**Query**:
```graphql
mutation CreateHost($host: String!, $hostgroupids: [Int!]!, $templateids: [Int!]!) {
createHost(host: $host, hostgroupids: $hostgroupids, templateids: $templateids) {
hostids
}
}
```
**Variables**:
```json
{
"host": "Linux server",
"hostgroupids": [50],
"templateids": [20045]
}
```
### ✅ Step 4: Verification
Compare the GraphQL response with the expected output described in the Zabbix documentation. Note that while the field names in GraphQL match the Zabbix API field names (e.g. `hostid`, `host`), the structure is simplified and nested objects (like `hostgroups`) can be queried directly without separate `selectXXX` parameters.
- *Reference*: [Zabbix 7.4 API Documentation](https://www.zabbix.com/documentation/7.4/en/manual/api)
---
## 🍳 Recipe: Extending Schema with a New Device Type
This recipe shows how to add support for a new specialized device type without modifying the core API code. We will use the `DistanceTrackerDevice` as an example.
### 📋 Prerequisites
- Zabbix Template Group `Templates/Roadwork/Devices` exists.
- Zabbix GraphQL API is running.
### 🛠️ Step 1: Define the Schema Extension
Create a new `.graphql` file in `schema/extensions/` (e.g. `distance_tracker.graphql`).
> **Advice**: A new device type must always implement both the `Host` and `Device` interfaces to ensure compatibility with the API's core logic and resolvers.
```graphql
type DistanceTrackerDevice implements Host & Device {
# Mandatory Host & Device fields
hostid: ID!
host: String!
deviceType: String
hostgroups: [HostGroup!]
name: String
tags: DeviceConfig
# Specialized state for this device
state: DistanceTrackerState
}
type DistanceTrackerState implements DeviceState {
operational: OperationalDeviceData
current: DistanceTrackerValues
}
type DistanceTrackerValues {
timeFrom: Time
timeUntil: Time
count: Int
# The distances are modelled using a type which is already defined in location_tracker_commons.graphql
distances: [SensorDistanceValue!]
}
```
> **Reference**: This example is based on the already prepared sample: [location_tracker_devices.graphql](../../schema/extensions/location_tracker_devices.graphql).
### ⚙️ Step 2: Configure Environment Variables
Add the new schema and resolver to your `.env` file:
```env
ADDITIONAL_SCHEMAS=./schema/extensions/distance_tracker.graphql,./schema/extensions/location_tracker_commons.graphql
ADDITIONAL_RESOLVERS=DistanceTrackerDevice
```
Restart the API server.
### 🚀 Step 3: Execution/Action (Choose Method)
#### Method A: Manual Creation in Zabbix
If you prefer to configure Zabbix manually:
1. **Create Template**: Create a template named `DISTANCE_TRACKER`.
2. **Create Items**: Add items with keys that match the GraphQL fields (using hierarchical mapping):
* `state.current.timeFrom`
* `state.current.timeUntil`
* `state.current.count`
* `state.current.json_distances` (maps to `distances` array via `json_` prefix)
3. **Add Tag**: Add a host tag to the template with name `deviceType` and value `DistanceTrackerDevice`.
#### Method B: Automated Import
Execute the `importTemplates` mutation to create the template and items automatically.
> **Reference**: Use the [Sample: Distance Tracker Import](../../docs/queries/sample_import_distance_tracker_template.graphql) for a complete mutation and variables example.
### ✅ Step 4: Verify the Extension
Verify that the new type is available and correctly mapped by creating a test host and querying it.
#### 1. Create a Test Host
Use the `importHosts` mutation (or `createHost` if IDs are already known) to create a host. Set its `deviceType` to `DistanceTrackerDevice` and link it to the `DISTANCE_TRACKER` template (created in Step 3) using the `templateNames` parameter.
```graphql
mutation CreateTestDistanceTracker($host: String!, $groupNames: [String!]!, $templateNames: [String]) {
importHosts(hosts: [{
deviceKey: $host,
deviceType: "DistanceTrackerDevice",
groupNames: $groupNames,
templateNames: $templateNames
}]) {
hostid
message
}
}
```
#### 2. Query the Device
Query the newly created device. Use the `tag_deviceType: ["DistanceTrackerDevice"]` argument to ensure you only retrieve devices of this specific type and prevent showing all devices of any type.
```graphql
query VerifyNewDeviceType {
# 1. Check if the type exists in the schema
__type(name: "DistanceTrackerDevice") {
name
fields {
name
}
}
# 2. Query the specific device
allDevices(tag_deviceType: ["DistanceTrackerDevice"]) {
... on DistanceTrackerDevice {
name
state {
current {
count
distances {
distance
}
}
}
}
}
}
```
> **Reference**: See how items map to fields in the [Zabbix to GraphQL Mapping](../../README.md#zabbix-to-graphql-mapping).
### 🤖 AI/MCP
AI agents can use the generalized `verifySchemaExtension.graphql` operations to automate this step:
- **CreateVerificationHost**: Automatically imports a test host with the correct `deviceType`.
- **VerifySchemaExtension**: Queries the newly created device to verify it is correctly mapped to the new type.
---
## 🍳 Recipe: Extending Schema with a Weather Sensor Device (Public API)
This recipe demonstrates how to extend the schema with a new device type that retrieves real-time weather data from a public API (Open-Meteo) using Zabbix HTTP agent items. This approach allows you to integrate external data sources into your Zabbix monitoring and expose them through the GraphQL API.
### 📋 Prerequisites
- Zabbix GraphQL API is running.
- The device has geo-coordinates set via user macros (`{$LAT}` and `{$LON}`).
### 🛠️ Step 1: Define the Schema Extension
Create a new `.graphql` file in `schema/extensions/` named `weather_sensor.graphql`.
```graphql
type WeatherSensorDevice implements Host & Device {
hostid: ID!
host: String!
deviceType: String
hostgroups: [HostGroup!]
name: String
tags: DeviceConfig
state: WeatherSensorState
}
type WeatherSensorState implements DeviceState {
operational: OperationalDeviceData
current: WeatherSensorValues
}
type WeatherSensorValues {
temperature: Float
streetConditionWarnings: String
}
```
### ⚙️ Step 2: Register the Resolver
Add the new type and schema to your `.env` file to enable the dynamic resolver:
```env
ADDITIONAL_SCHEMAS=./schema/extensions/weather_sensor.graphql
ADDITIONAL_RESOLVERS=WeatherSensorDevice
```
Restart the API server to apply the changes.
### 🚀 Step 3: Import the Weather Sensor Template
Use the `importTemplates` mutation to create the `WEATHER_SENSOR` template. This template uses an **HTTP agent** item to fetch data from Open-Meteo and **dependent items** to parse the results.
> **Reference**: See the [Sample: Weather Sensor Template Import](../../docs/queries/sample_import_weather_sensor_template.graphql) for the complete mutation and variables.
**Key Item Configuration**:
- **Master Item**: `weather.get` (HTTP Agent)
- URL: `https://api.open-meteo.com/v1/forecast?latitude={$LAT}&longitude={$LON}&current=temperature_2m,weather_code`
- **Dependent Item**: `state.current.temperature` (JSONPath: `$.current.temperature_2m`)
- **Dependent Item**: `state.current.streetConditionWarnings` (JavaScript mapping from `$.current.weather_code`)
### ✅ Step 4: Verification
Create a host, assign it macros for coordinates, and query its weather state.
1. **Create Host**:
```graphql
mutation CreateWeatherHost {
importHosts(hosts: [{
deviceKey: "Berlin-Weather-Sensor",
deviceType: "WeatherSensorDevice",
groupNames: ["External Sensors"],
templateNames: ["WEATHER_SENSOR"],
macros: [
{ macro: "{$LAT}", value: "52.52" },
{ macro: "{$LON}", value: "13.41" }
],
location: {
name: "Berlin"
}
}]) {
hostid
}
}
```
2. **Query Data**:
```graphql
query GetWeather {
allDevices(tag_deviceType: ["WeatherSensorDevice"]) {
... on WeatherSensorDevice {
name
state {
current {
temperature
streetConditionWarnings
}
}
}
}
}
```
---
## 🍳 Recipe: Provisioning a New Host
### 📋 Prerequisites
- A target Host Group exists in Zabbix.
- At least one Template exists in Zabbix.
### 🛠️ Step 1: Prepare the Host Object
Define the host name, groups, and templates to link.
### 🚀 Step 2: Execute `createHost` Mutation
For more details on the input fields, see the [Reference: createHost](../../schema/mutations.graphql).
### 🤖 AI/MCP
AI agents should prefer using the `importHosts` MCP tool for provisioning as it allows using names for host groups instead of IDs.
```graphql
mutation CreateNewHost($host: String!, $groups: [Int!]!, $templates: [Int], $templateNames: [String]) {
createHost(host: $host, hostgroupids: $groups, templateids: $templates, templateNames: $templateNames) {
hostids
error {
message
}
}
}
```
### ✅ Step 3: Verify Host Creation
Check if the host is correctly provisioned and linked to groups:
```graphql
query VerifyHost($host: String!) {
allHosts(filter_host: $host) {
hostid
host
hostgroups {
name
}
}
}
```
---
## 🍳 Recipe: Managing User Permissions
### 🛠️ Step 1: Create Permission Template Group
Create a template group with the prefix `Permissions/` in Zabbix (e.g. `Permissions/Read-Only-Access`).
### ⚙️ Step 2: Assign to User Group
In Zabbix, give a User Group `Read` access to this template group.
### ✅ Step 3: Verify via API
Verify that the current user has the expected permissions:
```graphql
query CheckMyPermissions {
hasPermissions(permissions: [
{ objectName: "Read-Only-Access", permission: READ }
])
}
```
---
## 🍳 Recipe: Bulk Import of Templates and Hosts
This recipe guides you through performing a mass import of multiple templates and hosts in a single operation.
### 🛠️ Step 1: Prepare Template Import
Use the `importTemplates` mutation. You can provide multiple template definitions in the `templates` array.
### ⚙️ Step 2: Prepare Host Import
Use the `importHosts` mutation. Link them to the newly imported templates using their names or IDs.
### 🚀 Step 3: Combined Operation (Optional)
You can execute both mutations in a single GraphQL request to ensure atomic-like provisioning of your infrastructure.
```graphql
mutation BulkProvisioning($templates: [CreateTemplate!]!, $hosts: [CreateHost!]!) {
importTemplates(templates: $templates) {
templateid
host
message
}
importHosts(hosts: $hosts) {
hostid
deviceKey
message
}
}
```
### ✅ Step 4: Verify Bulk Import
Verify that all entities were created and linked correctly:
```graphql
query VerifyBulkImport($pattern: String!) {
allHosts(name_pattern: $pattern) {
hostid
host
... on ZabbixHost {
parentTemplates {
name
}
}
}
}
```
For detailed examples of the input structures, refer to [Sample Import Templates](../../docs/queries/sample_import_templates_mutation.graphql) and [Sample Import Hosts](../../docs/queries/sample_import_hosts_mutation.graphql).
---
## 🍳 Recipe: Running the Smoketest via MCP
This recipe explains how to execute the end-to-end smoketest using the integrated MCP server. This is the fastest way to verify that your Zabbix GraphQL API is correctly connected to a Zabbix instance and all core features (Groups, Templates, Hosts) are working.
### 📋 Prerequisites
- Zabbix GraphQL API is running (`npm run start` or via Docker).
- Integrated MCP server is configured in your environment (e.g. registered in **Junie**).
### 🛠️ Step 1: Prompt the AI Agent
If you are using **Junie**, you can trigger the smoketest with a single natural language prompt.
**Prompt**:
> "Run the Zabbix smoketest using the MCP tool. Use 'SMOKE_HOST', 'SMOKE_TEMPLATE', and 'SMOKE_GROUP' for the entity names."
### 🚀 Step 2: Agent Execution
The agent will:
1. Identify the `RunSmoketest` tool from the MCP server.
2. Call the tool with the provided arguments.
3. Monitor the progress of each step (Create Template Group -> Create Template -> Create Host Group -> Create and Link Host -> Verify -> Cleanup).
### ✅ Step 3: Verification
The agent will report the success or failure of each step. You should see a final message indicating "Smoketest passed successfully".
---
## 🍳 Recipe: Cloning a Template with Items
This recipe guides you through cloning an existing Zabbix template, including all its items and their configurations, into a new template using the GraphQL API and MCP.
### 📋 Prerequisites
- Zabbix GraphQL API is running.
- You have the technical name of the source template.
### 🛠️ Step 1: Query the Source Template
Retrieve the source template's details and all its items.
**GraphQL Query**:
```graphql
query GetSourceTemplate($name: String!) {
templates(name_pattern: $name) {
host
name
items {
itemid
name
key_
type_int
value_type
status_int
history
delay
units
description
preprocessing
tags
master_itemid
}
}
}
```
### ⚙️ Step 2: Prepare the Clone Configuration
1. **Technical Names**: Choose a new technical name (`host`) and visible name (`name`) for the clone.
2. **Item Mapping**: Map the source items to the `items` array in the `importTemplates` mutation.
3. **Resolve Master Items**: For dependent items (where `master_itemid` > 0), find the source item with the matching `itemid` and use its `key_` as the `master_item.key` in the new item definition.
### 🚀 Step 3: Execute `importTemplates` Mutation
Execute the mutation to create the clone.
```graphql
mutation CloneTemplate($templates: [CreateTemplate!]!) {
importTemplates(templates: $templates) {
host
templateid
message
}
}
```
### ✅ Step 4: Verification
Verify that the cloned template exists and has the expected items.
```graphql
query VerifyClone($host: String!) {
templates(name_pattern: $host) {
templateid
host
items {
name
key_
}
}
}
```
### 🤖 AI/MCP
AI agents can use the following MCP tools to automate this:
- **GetTemplates**: To fetch the source template and its hierarchical item structure.
- **ImportTemplates**: To provision the new cloned template.
#### 🤖 Prompting Junie
You can ask **Junie** to automate the entire cloning process:
> "Using MCP, clone the template 'Generic SNMP' to a new template named 'Custom SNMP v2'. Ensure all items are copied and dependent items have their master item keys correctly mapped."
---
## 🍳 Recipe: Setting up GraphQL MCP for AI Agents
This recipe guides you through setting up the Model Context Protocol (MCP) server to enable AI agents like **Junie** or **Claude** to interact with your Zabbix data through the GraphQL API.
### 📋 Prerequisites
- **Zabbix GraphQL API**: Ensure the API is running (e.g. `npm run start`).
- **Docker**: Installed and running for the MCP server container.
### 🛠️ Step 1: Configure the MCP Server
Choose one of the following setups:
#### Setup A: JetBrains IDE (AI Chat & Junie)
Configure the IDE to use the GraphQL MCP server for both the built-in AI Chat and the **Junie agent**.
- **Prerequisite**: Generate the combined schema file (run this in your project root):
```bash
cat schema/*.graphql > schema.graphql
```
- **Open Settings**: Navigate to `File` > `Settings` (Windows/Linux) or `IntelliJ IDEA` > `Settings` (macOS).
- **Navigate to MCP**: Go to `Tools` > `AI Assistant` > `MCP Servers`.
- **Add Server**: Click the **+** button and configure:
- **Name**: `Zabbix GraphQL`
- **Type**: `Command`
- **Command**: `docker`
- **Arguments**:
```text
run -i --rm -v ${PROJECT_DIR}/mcp-config.yaml:/mcp-config.yaml -v ${PROJECT_DIR}/schema.graphql:/mcp-data/schema.graphql:ro -v ${PROJECT_DIR}/mcp/operations:/mcp/operations -e APOLLO_GRAPH_REF=local@main ghcr.io/apollographql/apollo-mcp-server:latest /mcp-config.yaml
```
#### Setup B: Claude Desktop
Connect Claude Desktop to the Zabbix GraphQL API by referring to the [Apollo GraphQL MCP Documentation](https://github.com/apollographql/apollo-mcp-server).
- **Prerequisite**: Generate the combined schema file (run this in your project root):
```bash
cat schema/*.graphql > schema.graphql
```
- **Edit Configuration**: Open the Claude Desktop configuration file (e.g. `%APPDATA%\Claude\claude_desktop_config.json` on Windows).
- **Add to mcpServers**: Insert the following configuration in the `mcpServers` section:
```json
{
"mcpServers": {
"zabbix-graphql": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v", "C:/path/to/your/project/mcp-config.yaml:/mcp-config.yaml",
"-v", "C:/path/to/your/project/schema.graphql:/mcp-data/schema.graphql:ro",
"-v", "C:/path/to/your/project/mcp/operations:/mcp/operations",
"-e", "APOLLO_GRAPH_REF=local@main",
"ghcr.io/apollographql/apollo-mcp-server:latest",
"/mcp-config.yaml"
]
}
}
}
```
- **Restart Claude**: Fully restart the Claude Desktop application to apply the changes.
### 🚀 Step 2: Use an AI Agent
Provide the recipe to your AI agent and ask it to perform a task.
- **Example**: "Use MCP to list the configured Zabbix hosts".
### ✅ Step 3: Verify via Agent
Confirm that the agent can successfully interact with the API:
- Ask: "What is the current version of the Zabbix API?"
- The agent should use the `apiVersion` query and respond with the version number.
### 💡 Alternative: Using Pre-running MCP Server
If you already have the MCP server running locally (e.g. via `docker compose`), you can use a simpler configuration.
- **Sample Configuration**: See [.ai/mcp/mcp.json](../../.ai/mcp/mcp.json) for a sample that connects to a running MCP server via HTTP.
- **Usage**: Use this `url`-based configuration in your Claude Desktop or IDE settings instead of the `command`-based setup if you prefer to manage the MCP server lifecycle separately.
> **Reference**: For more details on the benefits of GraphQL for MCP, see the [MCP & Agent Integration Guide](./mcp.md).

View file

@ -0,0 +1,41 @@
## 🗂️ Hierarchical Data Mapping
The API automatically maps Zabbix items to nested GraphQL objects using the `createHierarchicalValueFieldResolver` function.
### How it Works
Zabbix item keys are used to define the structure in GraphQL. A dot (`.`) acts as a separator to represent nested object structures.
**Example:**
Zabbix item key `state.current.values.temperature` is automatically mapped to:
```json
{
"state": {
"current": {
"values": {
"temperature": 25.5
}
}
}
}
```
### Type Hinting
You can guide the type conversion by prepending a type hint and an underscore to the last token of the Zabbix item key:
* `json_`: Parses the value as a JSON object (useful for complex types).
* `str_`: Forces the value to be treated as a string.
* `bool_`: Forces the value to be treated as a boolean.
* `float_`: Forces the value to be treated as a number.
### Preconditions
1. **Key Naming**: Zabbix item keys (or tags) must match the GraphQL field names.
2. **Dot Separation**: Use dots to represent the desired hierarchy.
The `createHierarchicalValueFieldResolver` function (found in `../../src/api/resolver_helpers.ts`) dynamically creates these resolvers, eliminating the need for manual resolver definitions for each hierarchical field.
For more information, see the comments in `../../schema/device_value_commons.graphql` and `../../src/api/resolver_helpers.ts`.
See `../queries/sample_all_devices_query.graphql` for examples of hierarchical data in query results.

View file

@ -0,0 +1,65 @@
# Technical Maintenance Guide
This guide covers the technical aspects of maintaining and developing the Zabbix GraphQL API.
## 🛠️ Development & Maintenance Tasks
### Code Generation
The project uses [GraphQL Codegen](https://the-guild.dev/graphql/codegen) to generate TypeScript types from the GraphQL schema. This ensures type safety and consistency between the schema and the implementation.
- **Configuration**: `codegen.ts`
- **Generated Output**: `src/schema/generated/graphql.ts`
#### How to Regenerate Types
Whenever you modify any `.graphql` files in the `schema/` directory, you must regenerate the TypeScript types.
For a one-off update (e.g. in a script or before commit):
```bash
npx graphql-codegen --config codegen.ts
```
If you are a developer and want to watch for schema changes continuously:
```bash
npm run codegen
```
### Testing
We use [Jest](https://jestjs.io/) for unit and integration testing.
- **Test Directory**: `src/test/`
- **Execution**:
```bash
npm run test
```
#### Adding New Tests
- **Location**: Place new test files in `src/test/` with the `.test.ts` extension.
- **Coverage**: Ensure you cover both successful operations and error scenarios.
- **Test Specification**: Every new test must be documented in the [Test Specification](../tests.md).
- **Best Practice**: If you find a bug, first create a reproduction test in `src/test/` to verify the fix.
## 🔄 Updating Dependencies
To keep the project secure and up-to-date:
1. Check for updates: `npm outdated`
2. Update packages: `npm update`
3. Verify with tests: `npm run test`
## 🐳 Docker Image Maintenance
The `Dockerfile` uses a multi-stage build to keep the production image small and secure.
- **Base Image**: Node 24 (LTS)
- **Build Argument**: `API_VERSION` (used to embed the version into the image)
To build a fresh image locally:
```bash
docker build -t zabbix-graphql-api --build-arg API_VERSION=$(git describe --tags --always) .
```
---
**Related Reference**: [Project Configuration Reference](../../README.md#configuration)
**Related Cookbook**: [Extending the Schema](./cookbook.md#recipe-extending-schema-with-a-new-device-type)

104
docs/howtos/mcp.md Normal file
View file

@ -0,0 +1,104 @@
## 🤖 Model Context Protocol (MCP) Integration
The Zabbix GraphQL API supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), enabling Large Language Models (LLMs) to interact directly with your Zabbix data through a standardized interface.
### Overview
By leveraging GraphQL, the API provides a strongly-typed and introspectable interface that is ideal for MCP. This allows LLMs to:
- Discover available queries and mutations.
- Understand the data structures (hosts, items, templates, etc.).
- Execute operations to retrieve or modify Zabbix data based on natural language prompts.
### Running Apollo MCP Server with Docker Compose
You can start both the Zabbix GraphQL API and the Apollo MCP Server using Docker Compose. This setup uses a local `mcp-config.yaml` and a generated `schema.graphql`.
- **Prerequisites**: Ensure you have a `.env` file with the required Zabbix connection details.
- **Prepare Operations**: Create the operations directory if it doesn't exist:
```bash
mkdir -p mcp/operations
```
- **Start Services**:
```bash
docker compose up -d
```
This will:
- Start the `zabbix-graphql-api` on `http://localhost:4001/graphql` (internal port 4000).
- Start the `apollo-mcp-server` on `http://localhost:3000/mcp` (mapped from internal port 8000), configured to connect to the local API via `mcp-config.yaml`.
### Using with Claude Desktop
To use this integration with Claude Desktop, add the following configuration to your Claude Desktop config file (typically `claude_desktop_config.json`).
- **Prerequisite**: Generate the combined schema file in your project root:
```bash
cat schema/*.graphql > schema.graphql
```
- **Configuration**:
```json
{
"mcpServers": {
"zabbix-graphql": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v", "/path/to/your/project/mcp-config.yaml:/mcp-config.yaml",
"-v", "/path/to/your/project/schema.graphql:/mcp-data/schema.graphql:ro",
"-v", "/path/to/your/project/mcp/operations:/mcp/operations",
"-e", "APOLLO_GRAPH_REF=local@main",
"ghcr.io/apollographql/apollo-mcp-server:latest",
"/mcp-config.yaml"
]
}
}
}
```
**Note**: Ensure the `zabbix-graphql-api` is running and accessible. If running locally, you might need to use `host.docker.internal:4001/graphql` in your `mcp-config.yaml` to allow the containerized MCP server to reach your host.
#### 💡 Sample Configuration (Alternative)
If you prefer to run the MCP server manually or via Docker Compose (as described above), you can use a HTTP-based configuration instead of the `command` execution. See [.ai/mcp/mcp.json](../../.ai/mcp/mcp.json) for a sample configuration that connects to the server running on `localhost:3000`.
### 🤖 Prompting with Junie (Integrated MCP)
When working with **Junie** in this repository, the MCP server is already registered as an integrated tool. You can simply ask Junie to perform tasks using the Zabbix GraphQL API by referring to the available MCP tools.
#### 🍳 Example: Running a Smoketest
To verify the system end-to-end, you can prompt Junie:
> "Run the Zabbix smoketest using the MCP tool. Use 'JUNIE_MCP_HOST', 'JUNIE_MCP_TEMPLATE', and 'JUNIE_MCP_GROUP' as names."
#### 🍳 Example: Cloning a Template
To clone a template, you can provide a higher-level instruction:
> "Using MCP, clone the template 'Generic SNMP' to a new template named 'Custom SNMP v2'. Ensure all items are copied and dependent items have their master item keys correctly mapped."
Junie will then:
- Use `GetTemplates` to fetch the source template structure.
- Map the items and resolve master-dependent relationships.
- Use `ImportTemplates` to create the new cloned template.
### Benefits of GraphQL-enabled MCP over REST
Integrating via GraphQL offers significant advantages for AI agents and MCP compared to the traditional Zabbix JSON-RPC (REST-like) API:
- **Introspection & Discovery**: Unlike REST, GraphQL is natively introspectable. An AI agent can query the schema itself to discover all available types, fields, and operations. This allows agents to "learn" the API capabilities without manual documentation parsing.
- **Strong Typing**: The schema provides explicit types for every field. AI agents can use this to validate their own generated queries and understand the exact data format expected or returned, reducing errors in agent-driven actions.
- **Precision (Over-fetching/Under-fetching)**: In REST, endpoints often return fixed data structures, leading to token waste (over-fetching) or requiring multiple round-trips (under-fetching). With GraphQL, the agent requests exactly the fields it needs, which is crucial for staying within LLM context window limits and reducing latency.
- **Single Endpoint**: AI agents only need to know one endpoint. They don't have to manage a complex tree of URL paths and HTTP methods; they simply send their intent as a GraphQL operation.
- **Complex Relationships**: Agents can navigate complex Zabbix relationships (e.g. Host -> Items -> History) in a single request, which is much more intuitive for LLMs than orchestrating multiple REST calls.
- **Self-Documenting**: Descriptive comments in the SDL are automatically exposed to the agent, providing immediate context for what each field represents.
### AI-Based Test Generation via Cookbook
The MCP server can be used in conjunction with the [**Cookbook**](./cookbook.md) to automate the generation of test cases. By providing a cookbook "recipe" to an LLM with access to the `zabbix-graphql` MCP server, the LLM can:
- Analyze the step-by-step instructions in the recipe.
- Use the MCP server's tools to inspect the current Zabbix state and schema.
- Generate and execute the necessary GraphQL operations to fulfill the recipe's task.
- Verify the outcome and suggest assertions for a formal test script.
Example prompt for an LLM:
> "Using the `zabbix-graphql` MCP server, follow the 'Provisioning a New Host' recipe from the cookbook. Create a host named 'Test-Host-01' in the 'Linux servers' group and link the 'ICMP Ping' template."

View file

@ -0,0 +1,49 @@
## 🔐 Roles and Permissions Extension
The API implements a permission system using Zabbix template groups:
### Permission Template Groups
- Template groups with prefix `Permissions/` (configurable via `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX`) are used for permissions
- Users gain permissions by being assigned to user groups that have access to these permission template groups
### Available Permissions
The system supports three permission levels defined in `../../schema/api_commons.graphql`:
- `DENY`: Explicitly denies access (supersedes other permissions)
- `READ`: Allows viewing/reading access
- `READ_WRITE`: Allows both reading and writing (implies READ permission)
### Permission Object Names
Permission object names map to Zabbix template group paths: `Permissions/{objectName}`
### GraphQL Permission Queries
```graphql
# Check if current user has specific permissions
query HasPermissions {
hasPermissions(permissions: [
{ objectName: "hosts", permission: READ },
{ objectName: "templates", permission: READ_WRITE }
])
}
# Get all user permissions
query GetUserPermissions {
userPermissions(objectNames: ["hosts", "templates"])
}
```
### Setting Up Permissions
1. Create template groups with the prefix `Permissions/` (e.g. `Permissions/hosts`, `Permissions/templates`)
2. Assign these template groups to user groups in Zabbix with appropriate permission levels
3. Users in those user groups will inherit the permissions
### Detailed Permission Usage Examples
For comprehensive examples of permission usage patterns, see `../../schema/api_commons.graphql` which contains detailed documentation in the `PermissionRequest` input type comments, including real-world examples of how to model permissions for buttons, status controls, and application features.
See also `../queries/sample_import_permissions_template_groups_mutation.graphql` for examples of importing permission template groups.

52
docs/howtos/schema.md Normal file
View file

@ -0,0 +1,52 @@
## 📊 Schema and Schema Extension
### Main Schema Structure
The GraphQL schema is located in the `../../schema/` directory and consists of:
- `queries.graphql` - Query definitions (see detailed documentation in file comments)
- `mutations.graphql` - Mutation definitions (see detailed documentation in file comments)
- `devices.graphql` - Device-related types (see detailed documentation in file comments)
- `zabbix.graphql` - Zabbix-specific types (see detailed documentation in file comments)
- `device_value_commons.graphql` - Common value types (see detailed documentation in file comments)
- `api_commons.graphql` - Common API types and permission system (see detailed documentation in file comments)
- `extensions/` - Custom device type extensions
For comprehensive understanding of each operation, read the detailed comments in the respective schema files.
### Zabbix to GraphQL Mapping
For a detailed mapping of Zabbix entities to GraphQL types, please refer to the [Zabbix to GraphQL Mapping](../../README.md#zabbix-to-graphql-mapping) section in the main README.
### Zabbix Entity Relationships
- **Host Groups**: Organize hosts and templates hierarchically; represented as `HostGroup` objects in GraphQL
- **Templates**: Contain items and other configuration that can be applied to hosts; linked via template groups
- **Items**: Individual metrics collected from hosts; automatically mapped to nested GraphQL fields based on their key names
- **Tags**: Metadata associated with hosts/templates; used for classification and filtering
### Location Type Usage
The `Location` type represents geographical information from Zabbix host inventory:
- **Fields**: Includes `name`, `location_lat`, `location_lon`, and other inventory attributes
- **Usage**: Available through the `locations` query and as part of host/device objects
- **Access**: Retrieved via the `getLocations` method in the Zabbix API datasource
### Dynamic Schema Extension
Extend the schema without code changes using environment variables:
```bash
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
```
This enables runtime schema extension for custom device types without modifying the core code.
### Sample Operations
For practical examples of schema usage, see the sample files in the `../queries/` directory:
- `../queries/sample_all_devices_query.graphql` - Example of querying all devices
- `../queries/sample_import_templates_mutation.graphql` - Example of importing templates
- `../queries/sample_import_host_groups_mutation.graphql` - Example of importing host groups

36
docs/howtos/tags.md Normal file
View file

@ -0,0 +1,36 @@
## 🏷️ Zabbix Tags Usage
Zabbix tags are used for:
- Device classification (`deviceType` tag)
- Host categorization (`hostType` tag)
- Custom metadata storage
- Permission assignment through template groups
### The `hostType` Tag
The `hostType` tag is used to categorize hosts and templates. This allows the API to provide default filters for specific application domains or device categories.
To classify a host or a template, add a tag in Zabbix:
* **Tag Name**: `hostType`
* **Tag Value**: A string representing the category (e.g. `Roadwork/Devices`, `SmartCity/Sensors`).
This tag can be defined directly on the host or on a template (where linked hosts will inherit it).
### Default Filtering with `HOST_TYPE_FILTER_DEFAULT`
By configuring the `HOST_TYPE_FILTER_DEFAULT` environment variable, you can set a global default for the `allHosts` and `allDevices` queries.
* If `HOST_TYPE_FILTER_DEFAULT=Roadwork/Devices` is set, `allHosts` will only return hosts with that tag value.
* This default can be overridden in the GraphQL query by passing the `tag_hostType` argument.
### Search Filtering with `HOST_GROUP_FILTER_DEFAULT`
The `HOST_GROUP_FILTER_DEFAULT` variable provides a default search pattern for the `allHostGroups` query, useful for restricting the visible host group hierarchy.
* **Overriding**: Providing the `search_name` argument in the `allHostGroups` query overrides this default.
* **Wildcards**: The `search_name` parameter supports the `*` wildcard. For example, `Roadwork/Devices/*` finds all subgroups within that path.
For more information, see the comments in `../../schema/devices.graphql` and `../../schema/zabbix.graphql`.
See `../queries/sample_all_hosts_query.graphql` and `../queries/sample_all_devices_query.graphql` for examples.

28
docs/queries/README.md Normal file
View file

@ -0,0 +1,28 @@
# Sample Queries & Mutations
This directory contains practical examples of GraphQL operations for the Zabbix GraphQL API. You can use these as templates for your own automation or integration tasks.
## 📁 Available Samples
### 🖥️ Hosts
- [Query All Hosts](./sample_all_hosts_query.graphql): Retrieve basic host information and inventory.
- [Import Hosts](./sample_import_hosts_mutation.graphql): Create or update multiple hosts with tags and group assignments.
- [Query All Devices](./sample_all_devices_query.graphql): Query specialized devices using the `allDevices` query.
### 📄 Templates
- [Query Templates](./sample_templates_query.graphql): List available templates and their items.
- [Import Templates](./sample_import_templates_mutation.graphql): Create or update complex templates with item definitions and preprocessing.
- [Import Distance Tracker Template](./sample_import_distance_tracker_template.graphql): Example of importing a template for a schema extension.
- [Delete Templates](./sample_delete_templates_mutation.graphql): Remove templates by ID or name pattern.
### 📂 Template Groups
- [Import Host Template Groups](./sample_import_host_template_groups_mutation.graphql): Create groups specifically for host templates.
- [Import Permissions Template Groups](./sample_import_permissions_template_groups_mutation.graphql): Create groups for the permission system.
- [Delete Template Groups](./sample_delete_template_groups_mutation.graphql): Remove template groups by ID or name pattern.
### 🔐 User Rights
- [Export User Rights](./sample_export_user_rights_query.graphql): Export existing user roles and groups for auditing or migration.
- [Import User Rights](./sample_import_user_rights_mutation.graphql): Provision user roles and group permissions at scale.
## 🍳 Related Recipes
For step-by-step guides on how to use these operations in common scenarios, see the [Cookbook](../howtos/cookbook.md).

View file

@ -0,0 +1,27 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/host/get
### Query
```graphql
query GetAllDevices($name_pattern: String) {
allDevices(name_pattern: $name_pattern) {
hostid
host
name
deviceType
state {
operational {
temperature
voltage
signalstrength
}
}
}
}
```
### Variables
```json
{
"name_pattern": "Device%"
}
```

View file

@ -0,0 +1,18 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/hostgroup/get
### Query
```graphql
query GetAllHostGroups($search_name: String) {
allHostGroups(search_name: $search_name) {
groupid
name
}
}
```
### Variables
```json
{
"search_name": "Zabbix%"
}
```

View file

@ -0,0 +1,34 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/host/get
### Query
```graphql
query GetAllHosts($name_pattern: String, $groupids: [Int!]) {
allHosts(name_pattern: $name_pattern, groupids: $groupids) {
hostid
host
name
deviceType
hostgroups {
groupid
name
}
... on ZabbixHost {
tags
items {
itemid
name
key_
lastvalue
}
}
}
}
```
### Variables
```json
{
"name_pattern": "Linux%",
"groupids": [2]
}
```

View file

@ -0,0 +1,18 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/templategroup/get
### Query
```graphql
query GetAllTemplateGroups($name_pattern: String) {
allTemplateGroups(name_pattern: $name_pattern) {
groupid
name
}
}
```
### Variables
```json
{
"name_pattern": "Templates%"
}
```

View file

@ -0,0 +1,23 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/host/create
### Query
```graphql
mutation CreateHost($host: String!, $hostgroupids: [Int!]!, $templateids: [Int!]!) {
createHost(
host: $host,
hostgroupids: $hostgroupids,
templateids: $templateids
) {
hostids
}
}
```
### Variables
```json
{
"host": "Linux server",
"hostgroupids": [50],
"templateids": [20045]
}
```

View file

@ -0,0 +1,18 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/templategroup/delete
### Query
```graphql
mutation DeleteTemplateGroups($groupids: [Int!]) {
deleteTemplateGroups(groupids: $groupids) {
id
message
}
}
```
### Variables
```json
{
"groupids": [10, 11]
}
```

View file

@ -0,0 +1,18 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/template/delete
### Query
```graphql
mutation DeleteTemplates($templateids: [Int!]) {
deleteTemplates(templateids: $templateids) {
id
message
}
}
```
### Variables
```json
{
"templateids": [10001, 10002]
}
```

View file

@ -0,0 +1,24 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/history/get
### Query
```graphql
query ExportHostValueHistory($host_filter: [String!], $itemKey_filter: [String!], $limit: Int) {
exportHostValueHistory(
host_filter: $host_filter,
itemKey_filter: $itemKey_filter,
limit: $limit,
type: FLOAT
) {
result
}
}
```
### Variables
```json
{
"host_filter": ["Linux server"],
"itemKey_filter": ["system.cpu.load[all,avg1]"],
"limit": 10
}
```

View file

@ -0,0 +1,28 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/usergroup/get
### Query
```graphql
query ExportUserRights($name_pattern: String) {
exportUserRights(name_pattern: $name_pattern) {
userGroups {
usrgrpid
name
hostgroup_rights {
name
permission
}
}
userRoles {
roleid
name
}
}
}
```
### Variables
```json
{
"name_pattern": "Zabbix%"
}
```

View file

@ -0,0 +1,20 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/usergroup/get
### Query
```graphql
query HasPermissions($permissions: [PermissionRequest!]!) {
hasPermissions(permissions: $permissions)
}
```
### Variables
```json
{
"permissions": [
{
"objectName": "Read-Only-Access",
"permission": "READ"
}
]
}
```

View file

@ -0,0 +1,26 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/hostgroup/create
### Query
```graphql
mutation ImportHostGroups($hostGroups: [CreateHostGroup!]!) {
importHostGroups(hostGroups: $hostGroups) {
groupName
groupid
message
}
}
```
### Variables
```json
{
"hostGroups": [
{
"groupName": "New Host Group 1"
},
{
"groupName": "New Host Group 2"
}
]
}
```

View file

@ -0,0 +1,30 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/host/create
### Query
```graphql
mutation ImportHosts($hosts: [CreateHost!]!) {
importHosts(hosts: $hosts) {
hostid
deviceKey
message
}
}
```
### Variables
```json
{
"hosts": [
{
"deviceKey": "Host 1",
"deviceType": "GenericDevice",
"groupNames": ["Zabbix servers"]
},
{
"deviceKey": "Host 2",
"deviceType": "GenericDevice",
"groupNames": ["Zabbix servers"]
}
]
}
```

View file

@ -0,0 +1,23 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/templategroup/create
### Query
```graphql
mutation ImportTemplateGroups($templateGroups: [CreateTemplateGroup!]!) {
importTemplateGroups(templateGroups: $templateGroups) {
groupName
groupid
message
}
}
```
### Variables
```json
{
"templateGroups": [
{
"groupName": "New Template Group 1"
}
]
}
```

View file

@ -0,0 +1,32 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/template/create
### Query
```graphql
mutation ImportTemplates($templates: [CreateTemplate!]!) {
importTemplates(templates: $templates) {
templateid
host
message
}
}
```
### Variables
```json
{
"templates": [
{
"host": "New Template 1",
"groupNames": ["Templates/Operating systems"],
"items": [
{
"name": "Custom item",
"key": "custom.item",
"value_type": 3,
"history": "90d"
}
]
}
]
}
```

View file

@ -0,0 +1,45 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/usergroup/create
### Query
```graphql
mutation ImportUserRights($input: UserRightsInput!, $dryRun: Boolean!) {
importUserRights(input: $input, dryRun: $dryRun) {
userGroups {
id
name
message
}
userRoles {
id
name
message
}
}
}
```
### Variables
```json
{
"dryRun": true,
"input": {
"userGroups": [
{
"name": "New User Group",
"hostgroup_rights": [
{
"name": "Zabbix servers",
"permission": "READ"
}
]
}
],
"userRoles": [
{
"name": "New Role",
"type": 1
}
]
}
}
```

View file

@ -0,0 +1,19 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/host/get
### Query
```graphql
query GetLocations($name_pattern: String) {
locations(name_pattern: $name_pattern) {
name
latitude
longitude
}
}
```
### Variables
```json
{
"name_pattern": "Riga%"
}
```

View file

@ -0,0 +1,16 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/user/login
### Query
```graphql
query Login($username: String!, $password: String!) {
login(username: $username, password: $password)
}
```
### Variables
```json
{
"username": "Admin",
"password": "zabbix_password"
}
```

View file

@ -0,0 +1,13 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/user/logout
### Query
```graphql
query Logout {
logout
}
```
### Variables
```json
{}
```

View file

@ -0,0 +1,18 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/template/get
### Query
```graphql
query GetTemplates($name_pattern: String) {
templates(name_pattern: $name_pattern) {
templateid
name
}
}
```
### Variables
```json
{
"name_pattern": "Template OS%"
}
```

View file

@ -0,0 +1,18 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/user/get
### Query
```graphql
query GetUserPermissions($objectNames: [String!]) {
userPermissions(objectNames: $objectNames) {
objectName
permission
}
}
```
### Variables
```json
{
"objectNames": ["Read-Only-Access"]
}
```

View file

@ -0,0 +1,13 @@
# Source: https://www.zabbix.com/documentation/7.4/en/manual/api/reference/apiinfo/version
### Query
```graphql
query GetZabbixVersion {
zabbixVersion
}
```
### Variables
```json
{}
```

View file

@ -21,7 +21,7 @@ mutation ImportDistanceTrackerTemplate($templates: [CreateTemplate!]!) {
### Variables ### Variables
The following sample defines the `DISTANCE_TRACKER` template. Note the `deviceType` tag set to `DistanceTrackerDevice`, which instructs the GraphQL API to resolve this host using the specialized `DistanceTrackerDevice` type. The following sample defines the `DISTANCE_TRACKER` template. Note the `deviceType` tag set to `DistanceTrackerDevice`, which instructs the GraphQL API to resolve this host using the specialized `DistanceTrackerDevice` type.
The item keys use the `json_` prefix where appropriate (e.g., `state.current.json_distances`) to ensure that the JSON strings received from Zabbix are automatically parsed into objects/arrays by the GraphQL resolver. The item keys use the `json_` prefix where appropriate (e.g. `state.current.json_distances`) to ensure that the JSON strings received from Zabbix are automatically parsed into objects/arrays by the GraphQL resolver.
```json ```json
{ {

View file

@ -0,0 +1,86 @@
### Mutation
Use this mutation to import a template specifically designed to work with the `WeatherSensorDevice` type. This template retrieves real-time weather data from the public Open-Meteo API using a Zabbix HTTP agent item.
```graphql
mutation ImportWeatherSensorTemplate($templates: [CreateTemplate!]!) {
importTemplates(templates: $templates) {
host
templateid
message
error {
message
code
}
}
}
```
### Variables
The following variables define the `WEATHER_SENSOR` template. It uses the host's user macros (`{$LAT}` and `{$LON}`) to fetch localized weather data.
```json
{
"templates": [
{
"host": "WEATHER_SENSOR",
"name": "Weather Sensor API Template",
"groupNames": ["Templates/External APIs"],
"tags": [
{ "tag": "deviceType", "value": "WeatherSensorDevice" }
],
"macros": [
{ "macro": "{$LAT}", "value": "52.52" },
{ "macro": "{$LON}", "value": "13.41" }
],
"items": [
{
"name": "Open-Meteo API Fetch",
"type": 19,
"key": "weather.get",
"value_type": 4,
"history": "0",
"delay": "1m",
"url": "https://api.open-meteo.com/v1/forecast?latitude={$LAT}&longitude={$LON}&current=temperature_2m,weather_code",
"description": "Master item fetching weather data from Open-Meteo based on host coordinates."
},
{
"name": "Current Temperature",
"type": 18,
"key": "state.current.temperature",
"value_type": 0,
"history": "7d",
"master_item": {
"key": "weather.get"
},
"preprocessing": [
{
"type": 12,
"params": ["$.current.temperature_2m"]
}
]
},
{
"name": "Street Condition Warnings",
"type": 18,
"key": "state.current.streetConditionWarnings",
"value_type": 4,
"history": "7d",
"master_item": {
"key": "weather.get"
},
"preprocessing": [
{
"type": 12,
"params": ["$.current.weather_code"]
},
{
"type": 21,
"params": ["var codes = {0:\"Clear\",1:\"Mainly Clear\",2:\"Partly Cloudy\",3:\"Overcast\",45:\"Fog\",48:\"Depositing Rime Fog\",51:\"Light Drizzle\",53:\"Moderate Drizzle\",55:\"Dense Drizzle\",56:\"Light Freezing Drizzle\",57:\"Dense Freezing Drizzle\",61:\"Slight Rain\",63:\"Moderate Rain\",65:\"Heavy Rain\",66:\"Light Freezing Rain\",67:\"Heavy Freezing Rain\",71:\"Slight Snow Fall\",73:\"Moderate Snow Fall\",75:\"Heavy Snow Fall\",77:\"Snow Grains\",80:\"Slight Rain Showers\",81:\"Moderate Rain Showers\",82:\"Violent Rain Showers\",85:\"Slight Snow Showers\",86:\"Heavy Snow Showers\",95:\"Thunderstorm\",96:\"Thunderstorm with Slight Hail\",99:\"Thunderstorm with Heavy Hail\"}; var code = parseInt(value); var warning = codes[code] || \"Unknown\"; if ([56, 57, 66, 67, 71, 73, 75, 77, 85, 86].indexOf(code) !== -1) { return \"WARNING: Slippery Roads (Snow/Ice)\"; } if ([51, 53, 55, 61, 63, 65, 80, 81, 82].indexOf(code) !== -1) { return \"CAUTION: Wet Roads\"; } return warning;"]
}
]
}
]
}
]
}
```

138
docs/tests.md Normal file
View file

@ -0,0 +1,138 @@
# Test Specification
This document outlines the test cases and coverage for the Zabbix GraphQL API.
## 📂 Test Categories
- **Unit Tests**: Verify individual functions, classes, or logic in isolation. All external dependencies (Zabbix API, Config) are mocked to ensure the test is fast and deterministic. These tests are executed on each build.
- *Reference*: `src/test/host_importer.test.ts`, `src/test/template_query.test.ts`
- **Integration Tests**: Test the interaction between multiple internal components. Typically, these tests use a mock Apollo Server to execute actual GraphQL operations against the resolvers and data sources, with the Zabbix API mocked at the network layer. These tests are executed on each build.
- *Reference*: `src/test/host_integration.test.ts`, `src/test/user_rights_integration.test.ts`
- **End-to-End (E2E) Tests**: Validate complete, multi-step business workflows from start to finish (e.g., a full import-verify-cleanup cycle). These tests are executed against a real, running Zabbix instance to ensure the entire system achieves the desired business outcome. These tests are triggered after startup or on demand via GraphQL/MCP endpoints.
- *Reference*: `mcp/operations/runSmoketest.graphql` (executed via MCP)
## 🧪 Test Case Definitions
### Host Management
- **TC-HOST-01**: Query all hosts using sample query.
- **TC-HOST-02**: Import hosts using sample mutation.
- **TC-HOST-03**: Import host groups and create new hierarchy.
- **TC-HOST-04**: Import basic host.
- **TC-HOST-05**: Query all hosts with name pattern.
- **TC-HOST-06**: Query all devices by host ID.
- **TC-HOST-07**: Query all host groups with search pattern.
- **TC-HOST-08**: Query host groups using default search pattern.
- **TC-HOST-09**: Query locations.
### Template Management
- **TC-TEMP-01**: Import templates using sample query and variables.
- **TC-TEMP-02**: Import and export templates comparison.
- **TC-TEMP-03**: Import and export template groups comparison.
- **TC-TEMP-04**: Query all templates.
- **TC-TEMP-05**: Filter templates by host IDs.
- **TC-TEMP-06**: Filter templates by name pattern.
- **TC-TEMP-07**: Filter templates by name pattern with wildcard.
- **TC-TEMP-08**: Import template groups (new group).
- **TC-TEMP-09**: Import template groups (existing group).
- **TC-TEMP-10**: Import basic template.
- **TC-TEMP-11**: Import templates with items, linked templates, and dependent items.
- **TC-TEMP-12**: Import templates query validation.
- **TC-TEMP-13**: Import templates error handling (data field inclusion).
- **TC-TEMP-14**: Delete templates successfully.
- **TC-TEMP-15**: Delete templates error handling.
- **TC-TEMP-16**: Delete templates by name pattern.
- **TC-TEMP-17**: Delete templates with merged IDs and name pattern.
- **TC-TEMP-18**: Delete template groups successfully.
- **TC-TEMP-19**: Delete template groups error handling.
- **TC-TEMP-20**: Delete template groups by name pattern.
### User Rights and Permissions
- **TC-AUTH-01**: Export user rights.
- **TC-AUTH-02**: Query user permissions.
- **TC-AUTH-03**: Check if user has permissions.
- **TC-AUTH-04**: Import user rights.
- **TC-AUTH-05**: Import user rights using sample mutation.
### System and Configuration
- **TC-CONF-01**: Schema loader uses Config variables.
- **TC-CONF-02**: Zabbix API constants derived from Config.
- **TC-CONF-03**: Logger levels initialized from Config.
- **TC-CONF-04**: API version query.
- **TC-CONF-05**: Login query.
- **TC-CONF-06**: Logout query.
- **TC-CONF-07**: Parse Zabbix arguments.
### Documentation and MCP
- **TC-DOCS-01**: Validate all Zabbix documentation sample queries.
- **TC-MCP-01**: Validate all MCP operation files against the schema.
### End-to-End (E2E) Tests
- **TC-E2E-01**: Run a complete smoketest using MCP (creates template, group, and host, verifies, and cleans up).
- **TC-E2E-02**: Run all regression tests to verify critical system behavior and prevent known issues.
#### Currently Contained Regression Tests
The `runAllRegressionTests` mutation (TC-E2E-02) executes the following checks:
- **Host without items**: Verifies that hosts created without any items or linked templates can be successfully queried by the system. This ensures that the hierarchical mapping and resolvers handle empty item lists gracefully.
- **Locations query argument order**: Verifies that the `locations` query correctly handles its parameters and successfully contacts the Zabbix API without session errors (verifying the fix for argument order in the resolver).
- **Template technical name lookup**: Verifies that templates can be correctly identified by their technical name (`host` field) when linking them to hosts during import.
- **HTTP Agent URL support**: Verifies that templates containing HTTP Agent items with a configured URL can be imported successfully (verifying the addition of the `url` field to `CreateTemplateItem`).
- **Host retrieval and visibility**: Verifies that newly imported hosts are immediately visible and retrievable via the `allHosts` query, including correctly delivered assigned templates and assigned host groups (verifying the fix for `output` fields in the host query data source).
## ✅ Test Coverage Checklist
| ID | Test Case | Category | Technology | Code Link |
|:---|:---|:---|:---|:---|
| TC-HOST-01 | Query allHosts using sample | Integration | Jest | [src/test/host_integration.test.ts](../src/test/host_integration.test.ts) |
| TC-HOST-02 | Import hosts using sample | Integration | Jest | [src/test/host_integration.test.ts](../src/test/host_integration.test.ts) |
| TC-HOST-03 | importHostGroups - create new hierarchy | Unit | Jest | [src/test/host_importer.test.ts](../src/test/host_importer.test.ts) |
| TC-HOST-04 | importHosts - basic host | Unit | Jest | [src/test/host_importer.test.ts](../src/test/host_importer.test.ts) |
| TC-HOST-05 | allHosts query | Unit | Jest | [src/test/host_query.test.ts](../src/test/host_query.test.ts) |
| TC-HOST-06 | allDevices query | Unit | Jest | [src/test/host_query.test.ts](../src/test/host_query.test.ts) |
| TC-HOST-07 | allHostGroups query | Unit | Jest | [src/test/host_query.test.ts](../src/test/host_query.test.ts) |
| TC-HOST-08 | allHostGroups query - default pattern | Unit | Jest | [src/test/host_query.test.ts](../src/test/host_query.test.ts) |
| TC-HOST-09 | locations query | Unit | Jest | [src/test/host_query.test.ts](../src/test/host_query.test.ts) |
| TC-TEMP-01 | Import templates using sample | Integration | Jest | [src/test/template_integration.test.ts](../src/test/template_integration.test.ts) |
| TC-TEMP-02 | Import and Export templates comparison | Integration | Jest | [src/test/template_integration.test.ts](../src/test/template_integration.test.ts) |
| TC-TEMP-03 | Import and Export template groups comparison | Integration | Jest | [src/test/template_integration.test.ts](../src/test/template_integration.test.ts) |
| TC-TEMP-04 | templates query - returns all | Unit | Jest | [src/test/template_query.test.ts](../src/test/template_query.test.ts) |
| TC-TEMP-05 | templates query - filters by hostids | Unit | Jest | [src/test/template_query.test.ts](../src/test/template_query.test.ts) |
| TC-TEMP-06 | templates query - filters by name_pattern | Unit | Jest | [src/test/template_query.test.ts](../src/test/template_query.test.ts) |
| TC-TEMP-07 | templates query - name_pattern wildcard | Unit | Jest | [src/test/template_query.test.ts](../src/test/template_query.test.ts) |
| TC-TEMP-08 | importTemplateGroups - create new | Unit | Jest | [src/test/template_importer.test.ts](../src/test/template_importer.test.ts) |
| TC-TEMP-09 | importTemplateGroups - group exists | Unit | Jest | [src/test/template_importer.test.ts](../src/test/template_importer.test.ts) |
| TC-TEMP-10 | importTemplates - basic template | Unit | Jest | [src/test/template_importer.test.ts](../src/test/template_importer.test.ts) |
| TC-TEMP-11 | importTemplates - complex template | Unit | Jest | [src/test/template_importer.test.ts](../src/test/template_importer.test.ts) |
| TC-TEMP-12 | importTemplates - template query | Unit | Jest | [src/test/template_importer.test.ts](../src/test/template_importer.test.ts) |
| TC-TEMP-13 | importTemplates - error data field | Unit | Jest | [src/test/template_importer.test.ts](../src/test/template_importer.test.ts) |
| TC-TEMP-14 | deleteTemplates - success | Unit | Jest | [src/test/template_deleter.test.ts](../src/test/template_deleter.test.ts) |
| TC-TEMP-15 | deleteTemplates - error | Unit | Jest | [src/test/template_deleter.test.ts](../src/test/template_deleter.test.ts) |
| TC-TEMP-16 | deleteTemplates - by name_pattern | Unit | Jest | [src/test/template_deleter.test.ts](../src/test/template_deleter.test.ts) |
| TC-TEMP-17 | deleteTemplates - merged IDs | Unit | Jest | [src/test/template_deleter.test.ts](../src/test/template_deleter.test.ts) |
| TC-TEMP-18 | deleteTemplateGroups - success | Unit | Jest | [src/test/template_deleter.test.ts](../src/test/template_deleter.test.ts) |
| TC-TEMP-19 | deleteTemplateGroups - error | Unit | Jest | [src/test/template_deleter.test.ts](../src/test/template_deleter.test.ts) |
| TC-TEMP-20 | deleteTemplateGroups - by name_pattern | Unit | Jest | [src/test/template_deleter.test.ts](../src/test/template_deleter.test.ts) |
| TC-AUTH-01 | exportUserRights query | Unit | Jest | [src/test/user_rights.test.ts](../src/test/user_rights.test.ts) |
| TC-AUTH-02 | userPermissions query | Unit | Jest | [src/test/user_rights.test.ts](../src/test/user_rights.test.ts) |
| TC-AUTH-03 | hasPermissions query | Unit | Jest | [src/test/user_rights.test.ts](../src/test/user_rights.test.ts) |
| TC-AUTH-04 | importUserRights mutation | Unit | Jest | [src/test/user_rights.test.ts](../src/test/user_rights.test.ts) |
| TC-AUTH-05 | Import user rights using sample | Integration | Jest | [src/test/user_rights_integration.test.ts](../src/test/user_rights_integration.test.ts) |
| TC-CONF-01 | schema_loader uses Config variables | Unit | Jest | [src/test/schema_config.test.ts](../src/test/schema_config.test.ts) |
| TC-CONF-02 | constants are derived from Config | Unit | Jest | [src/test/zabbix_api_config.test.ts](../src/test/zabbix_api_config.test.ts) |
| TC-CONF-03 | logger levels initialized from Config | Unit | Jest | [src/test/logger_config.test.ts](../src/test/logger_config.test.ts) |
| TC-CONF-04 | apiVersion query | Unit | Jest | [src/test/misc_resolvers.test.ts](../src/test/misc_resolvers.test.ts) |
| TC-CONF-05 | login query | Unit | Jest | [src/test/misc_resolvers.test.ts](../src/test/misc_resolvers.test.ts) |
| TC-CONF-06 | logout query | Unit | Jest | [src/test/misc_resolvers.test.ts](../src/test/misc_resolvers.test.ts) |
| TC-CONF-07 | Parse Zabbix Args | Unit | Jest | [src/test/zabbix_api_args_parser.test.ts](../src/test/zabbix_api_args_parser.test.ts) |
| TC-DOCS-01 | Zabbix Docs Samples Integration | Integration | Jest | [src/test/zabbix_docs_samples.test.ts](../src/test/zabbix_docs_samples.test.ts) |
| TC-MCP-01 | MCP Operations Validation | Integration | Jest | [src/test/mcp_operations_validation.test.ts](../src/test/mcp_operations_validation.test.ts) |
| TC-E2E-01 | Run complete smoketest | E2E | GraphQL / MCP | [mcp/operations/runSmoketest.graphql](../mcp/operations/runSmoketest.graphql) |
| TC-E2E-02 | Run all regression tests | E2E | GraphQL / MCP | [mcp/operations/runAllRegressionTests.graphql](../mcp/operations/runAllRegressionTests.graphql) |
## 📝 Test Case Obligations
As per project guidelines, every new feature or bug fix must be accompanied by a described test case in this specification.
- **Feature**: A new feature must have a corresponding test case (TC) defined before implementation.
- **Bug Fix**: A bug fix must include a reproduction test case that fails without the fix and passes with it. Additionally, a permanent regression test must be added to the automated suite (e.g., `RegressionTestExecutor`) to prevent the issue from re-occurring.
- **Documentation**: The `docs/tests.md` file must be updated to reflect any changes in test coverage.
- **Categorization**: Tests must be categorized as Unit, Integration, or End-to-End (E2E).

20
mcp-config.yaml Normal file
View file

@ -0,0 +1,20 @@
endpoint: http://zabbix-graphql-api:4000/graphql
overrides:
mutation_mode: all
transport:
type: streamable_http
stateful_mode: false
operations:
source: local
paths:
- /mcp/operations
schema:
source: local
path: /mcp-data/schema.graphql
introspection:
execute:
enabled: true
introspect:
enabled: true
search:
enabled: true

View file

@ -0,0 +1,3 @@
query GetApiVersion {
apiVersion
}

View file

@ -0,0 +1,8 @@
mutation CreateHost($host: String!, $hostgroupids: [Int!]!, $templateids: [Int], $templateNames: [String]) {
createHost(host: $host, hostgroupids: $hostgroupids, templateids: $templateids, templateNames: $templateNames) {
hostids
error {
message
}
}
}

View file

@ -0,0 +1,14 @@
mutation CreateVerificationHost($deviceKey: String!, $deviceType: String!, $groupNames: [String!]!, $templateNames: [String]) {
importHosts(hosts: [{
deviceKey: $deviceKey,
deviceType: $deviceType,
groupNames: $groupNames,
templateNames: $templateNames
}]) {
hostid
message
error {
message
}
}
}

View file

@ -0,0 +1,21 @@
query GetTemplates($name_pattern: String) {
templates(name_pattern: $name_pattern) {
templateid
host
name
items {
name
key_
type
value_type
status
history
delay
units
description
preprocessing
tags
master_itemid
}
}
}

View file

@ -0,0 +1,7 @@
mutation ImportHostGroups($hostGroups: [CreateHostGroup!]!) {
importHostGroups(hostGroups: $hostGroups) {
groupName
groupid
message
}
}

View file

@ -0,0 +1,12 @@
# Import multiple hosts/devices into Zabbix.
# This is a powerful tool for bulk provisioning of hosts using their names and types.
# It supports linking templates by ID (templateids) or by name (templateNames).
mutation ImportHosts($hosts: [CreateHost!]!) {
importHosts(hosts: $hosts) {
hostid
message
error {
message
}
}
}

View file

@ -0,0 +1,12 @@
# Import templates into Zabbix.
# This operation allows creating or updating templates with their groups, items, and linked templates.
mutation ImportTemplates($templates: [CreateTemplate!]!) {
importTemplates(templates: $templates) {
host
templateid
message
error {
message
}
}
}

View file

@ -0,0 +1,13 @@
# Runs all regression tests.
# Variables: hostName, groupName
mutation RunAllRegressionTests($hostName: String!, $groupName: String!) {
runAllRegressionTests(hostName: $hostName, groupName: $groupName) {
success
message
steps {
name
success
message
}
}
}

View file

@ -0,0 +1,14 @@
# Run a complete smoketest: creates a template, host group, and host,
# verifies their creation and linkage, and then cleans up everything.
# Variables: hostName, templateName, groupName
mutation RunSmoketest($hostName: String!, $templateName: String!, $groupName: String!) {
runSmoketest(hostName: $hostName, templateName: $templateName, groupName: $groupName) {
success
message
steps {
name
success
message
}
}
}

View file

@ -0,0 +1,13 @@
query VerifySchemaExtension($typeName: String!, $deviceKey: String) {
allDevices(tag_deviceType: [$typeName], filter_host: $deviceKey) {
hostid
host
name
deviceType
state {
operational {
timestamp
}
}
}
}

19
package-lock.json generated
View file

@ -32,7 +32,7 @@
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/jest": "^29.5.13", "@types/jest": "^29.5.13",
"@types/node": "^22.6.1", "@types/node": "^24.10.9",
"@types/simple-mock": "^0.8.6", "@types/simple-mock": "^0.8.6",
"@types/ws": "^8.5.12", "@types/ws": "^8.5.12",
"jest": "^29.7.0", "jest": "^29.7.0",
@ -43,6 +43,9 @@
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tsx": "^4.19.1", "tsx": "^4.19.1",
"typescript": "^5.6.2" "typescript": "^5.6.2"
},
"engines": {
"node": ">=24"
} }
}, },
"node_modules/@apollo/cache-control-types": { "node_modules/@apollo/cache-control-types": {
@ -3764,14 +3767,14 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.19.3", "version": "24.10.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz",
"integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~7.16.0"
} }
}, },
"node_modules/@types/qs": { "node_modules/@types/qs": {
@ -10091,9 +10094,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.21.0", "version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },

View file

@ -6,17 +6,20 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"compile": "tsc", "compile": "tsc",
"start": "nodemon --watch \"src/**\" --watch \"schema.graphql\" --ext \"ts,json\" --exec \"node --import tsx ./src/index.ts\"", "start": "nodemon --watch \"src/**\" --watch \"schema/**\" --ext \"ts,json,graphql\" --exec \"node --import tsx ./src/index.ts\"",
"prod": "npm run copy-schema && node ./dist/index.js", "prod": "npm run copy-schema && node ./dist/index.js",
"test": "jest --detectOpenHandles --forceExit --bail", "test": "jest --detectOpenHandles --forceExit --bail",
"codegen": "graphql-codegen --config codegen.ts --watch \"schema.graphql\"", "codegen": "graphql-codegen --config codegen.ts --watch",
"nodemon": "nodemon --watch \"src/**\" --watch \"schema.graphql\" --ext \"ts,json\" --exec \"tsc", "nodemon": "nodemon --watch \"src/**\" --watch \"schema/**\" --ext \"ts,json,graphql\" --exec \"tsc",
"copy-schema": "cp -R schema ./dist/" "copy-schema": "cp -R schema ./dist/"
}, },
"keywords": [], "keywords": [],
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"author": "Andreas Hilbig", "author": "Andreas Hilbig",
"copyright": "All rights reserved by Hilbig IT GmbH", "copyright": "All rights reserved by Hilbig IT GmbH",
"engines": {
"node": ">=24"
},
"dependencies": { "dependencies": {
"@apollo/datasource-rest": "^6.3.0", "@apollo/datasource-rest": "^6.3.0",
"@apollo/server": "^5.2.0", "@apollo/server": "^5.2.0",
@ -41,7 +44,7 @@
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/jest": "^29.5.13", "@types/jest": "^29.5.13",
"@types/node": "^22.6.1", "@types/node": "^24.10.9",
"@types/simple-mock": "^0.8.6", "@types/simple-mock": "^0.8.6",
"@types/ws": "^8.5.12", "@types/ws": "^8.5.12",
"jest": "^29.7.0", "jest": "^29.7.0",

View file

@ -1,74 +0,0 @@
# README Evaluation Result
## Executive Summary
The README.md file provides a comprehensive overview of the Zabbix GraphQL API project with good coverage of features, installation, configuration, and usage examples. The document has been recently enhanced with codebase references and detailed explanations of key features.
## Strengths
- Comprehensive coverage of all major features
- Good separation of sections with clear headings
- Detailed environment variable configuration
- Well-structured example for schema extension
- Proper inclusion of Docker deployment instructions
- References to schema and documentation files
- Clear explanation of the permission system
- Good balance between high-level concepts and technical details
## Areas of Concern
### 1. Missing Information
- No mention of the `DRY_RUN` environment variable functionality
- Missing information about the `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` configuration options
- No explanation of the difference between `ZABBIX_AUTH_TOKEN` and `ZABBIX_AUTH_TOKEN_FOR_REQUESTS`
- Missing details about the `FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX` regex pattern
### 2. Incomplete Information
- The "Real-time Subscriptions" feature claims WebSocket support but no actual subscription operations are defined in the schema
- The API version placeholder `${API_VERSION:-"Not set"}` is not properly populated
- No mention of the generated GraphQL types in `src/schema/generated/graphql.ts`
- Missing information about the test structure and how to run individual tests
### 3. Potential Inaccuracies
- The README mentions that the API is published publicly, but the license is AGPL-3.0-only which has specific distribution requirements
- The Docker example uses `your-zabbix-server` placeholder but doesn't explain how to determine the correct URL format
- The example GraphQL queries might not match the exact schema definitions
### 4. Schema Consistency Issues
- The schema contains many types and operations not mentioned in the README
- Some schema types like `Location` are referenced but not explained
- The relationship between Zabbix entities and GraphQL types could be clearer
### 5. Documentation Completeness
- The AGENTS.md file contains important architectural information not reflected in the README
- The codegen.ts file configuration is not explained
- The relationship between different DataSource files is not clear from the README
## Technical Verification
### Features Verification
- ✅ GraphQL Interface: Confirmed in schema files and start.ts
- ✅ Hierarchical Data Mapping: Confirmed in resolver_helpers.ts
- ✅ Mass Operations: Confirmed in mutations.graphql
- ✅ Dynamic Schema Extension: Confirmed in schema.ts
- ✅ Permission System: Confirmed in api_commons.graphql
- ⚠️ Real-time Subscriptions: Infrastructure exists but no actual subscriptions defined
- ✅ Type Safety: Confirmed with codegen and generated types
### Configuration Verification
- ✅ All environment variables in Config class are documented
- ✅ Docker configuration matches Dockerfile
- ✅ Package dependencies match functionality described
### Example Verification
- ✅ Distance tracker example aligns with sample files
- ✅ Import/export operations match schema definitions
- ✅ Permission queries match schema definitions
## Recommendations for Improvement
1. Add missing environment variables and their purposes
2. Clarify the subscription functionality status
3. Expand on the relationship between Zabbix entities and GraphQL types
4. Include more information about the code generation process
5. Add troubleshooting section with common issues
6. Include performance considerations and scaling information
7. Add security best practices section
8. Include backup and recovery procedures

View file

@ -1,57 +1,102 @@
# Documentation Improvement Plan
This plan outlines the steps to refactor and improve the Zabbix GraphQL API documentation, combining a technical reference with a practical "Cookbook" for quick start and AI-based test generation.
## Priority 1: Reference Documentation (Advanced Users)
### 1.1 Enhance README.md
- [x] **Feature-to-Code Mapping**: Add "Reference" links to each feature in the features list (from public origin).
- [x] **Comprehensive Config Reference**: Update environment variables table with detailed descriptions, defaults, and requirements.
- [x] **Auth Separation**: Explain `ZABBIX_PRIVILEGE_ESCALATION_TOKEN` vs `ZABBIX_DEVELOPMENT_TOKEN`.
- [x] **Entity Mapping Table**: Include the Zabbix-to-GraphQL entity mapping table.
- [x] **API Versioning**: Document how the version is determined and Zabbix version compatibility (7.4 focus).
### 1.2 Update Detailed Guides
- [x] Ensure `docs/howtos/schema.md`, `permissions.md`, etc., are up-to-date with the latest implementation details.
## Priority 2: Cookbook & AI-Ready Instructions
### 2.1 Create the Cookbook (`docs/howtos/cookbook.md`)
- [x] **Step-by-Step Recipes**: Create practical, task-oriented guides.
- [x] **Recipe: Schema Extension**: Move the "Distance Tracker" example into the cookbook.
- [x] **Recipe: Basic Monitoring**: Step-by-step to monitor a new host.
- [x] **Recipe: Bulk Import**: Guide for mass importing templates and hosts.
### 2.2 AI/MCP Integration for Test Generation
- [x] **MCP Server Documentation**: Explicitly document how to start and use the `zabbix-graphql` MCP server.
- [x] **Test Generation Instructions**: Provide specific instructions on how an AI can use the Cookbook recipes to generate test cases.
- [x] **Metadata for AI**: Use structured headers and clear prerequisites in recipes to make them "AI-parsable".
## Priority 3: Technical Maintenance (from Public Origin)
### 3.1 Code Generation Section ### 3.1 Code Generation Section
- Explain the GraphQL Codegen setup and how to regenerate types - [x] Explain the GraphQL Codegen setup and how to regenerate types.
- Document the `codegen.ts` configuration - [x] Document the `codegen.ts` configuration.
- Add instructions for updating generated types after schema changes - [x] Add instructions for updating generated types after schema changes.
## Priority 4: Improve Examples ## Priority 4: Improve Examples
### 4.1 Complete Examples ### 4.1 Complete Examples
- Add more complete examples for each major operation - [x] Add more complete examples for each major operation.
- Include error handling examples - [x] Include error handling examples.
- Add examples for common use cases beyond the distance tracker - [x] Add examples for common use cases beyond the distance tracker.
### 4.2 Testing Examples ### 4.2 Testing Examples
- Add information about how to run tests - [x] Add information about how to run tests.
- Include examples of unit and integration tests - [x] Include examples of unit and integration tests.
- Explain the test structure and how to add new tests - [x] Explain the test structure and how to add new tests.
## Priority 5: Documentation Links ## Priority 5: Documentation Links
### 5.1 Cross-Reference Improvements ### 5.1 Cross-Reference Improvements
- Add links to relevant sections in schema files - [x] Add links to relevant sections in schema files.
- Include references to specific resolver implementations - [x] Include references to specific resolver implementations.
- Link to related documentation files in the docs directory - [x] Link to related documentation files in the docs directory.
### 5.2 External Resources ### 5.2 External Resources
- Link to official Zabbix API documentation - [x] Link to official Zabbix API documentation.
- Include references to Apollo Server documentation - [x] Include references to Apollo Server documentation.
- Add links to GraphQL best practices
## Priority 6: Maintenance Items ## Priority 6: Refine Structure & DRY (New)
### 6.1 Update Placeholder Values ### 6.1 Separate Cookbook from Reference
- Replace all "your-" placeholder values with more descriptive examples - [x] Move instructional "how-to" steps from README.md to `cookbook.md` or specific how-tos where they distract from reference info.
- Add realistic example values for configuration parameters - [x] Ensure `README.md` focuses on "What" (specification/reference) and `cookbook.md` focuses on "How" (recipes).
- Include sample output where appropriate
### 6.2 Version Compatibility Matrix ### 6.2 Eliminate Redundancy (DRY)
- Create a matrix showing compatibility between API versions and Zabbix versions - [x] Identify and remove duplicate information between `README.md` and `docs/howtos/` (e.g., Zabbix to GraphQL Mapping).
- Include Node.js version compatibility information - [x] Centralize core definitions to a single source of truth and use links elsewhere.
- Add information about breaking changes between versions
### 6.3 Enhance Cross-References
- [x] Add explicit "See also" or "Related Recipes" links in reference sections.
- [x] Link from recipes back to technical reference material for deep-dives.
## Priority 7: Standardization & Verification (New)
### 7.1 Cookbook Verification Steps
- [x] Add a result verification step to each recipe in `cookbook.md`.
- [x] Ensure verification steps use GraphQL queries or agent checks where appropriate.
- [x] Refine 'Extending Schema' recipe with detailed manual/automated steps and comprehensive verification (Priority 7 enhancement).
### 7.2 Recipe Templating
- [x] Define a standard template for recipes in `.junie/guidelines.md`.
- [x] Standardize icons and step naming (🛠️, ⚙️, 🚀, ✅).
## Implementation Order ## Implementation Order
1. Address Priority 1 items first (critical missing information) 1. [x] **Foundation**: Update `README.md` with missing reference information from public origin.
2. Update existing sections to be more accurate 2. [x] **Cookbook Alpha**: Create `docs/howtos/cookbook.md` with the first set of recipes.
3. Add new sections incrementally 3. [x] **AI Bridge**: Document MCP server usage and test generation workflow.
4. Enhance examples with more practical use cases 4. [x] **Maintenance**: Add Codegen and Testing sections.
5. Add documentation links and cross-references 5. [x] **Cross-Linking**: Optimize all links and cross-references.
6. Perform final review and testing of all examples 6. [x] **Optimize**: Run import optimization across the project.
7. [x] **Refine & DRY**: Execute Priority 6 tasks to further clean up and structure documentation.
8. [x] **Standardize**: Add verification steps and formalize the recipe template (Priority 7).
## Success Metrics ## Success Metrics
- All environment variables documented - All environment variables documented.
- Accurate representation of features - Clear distinction between Reference and Cookbook.
- Complete working examples - Functional MCP-based test generation using cookbook instructions.
- Clear architecture and configuration guidance - Accurate representation of features and compatibility.
- Comprehensive troubleshooting information - Zero redundant tables or instructional blocks across the doc set.
- Proper cross-references to codebase - All recipes include a verification step.
- Guidelines contain a clear template for future recipes.

23
roadmap.md Normal file
View file

@ -0,0 +1,23 @@
# 🗺️ Roadmap
This document outlines the achieved milestones and planned enhancements for the Zabbix GraphQL API project.
## ✅ Achieved Milestones
- **🎯 VCR Product Integration**:
- Developed a specialized **GraphQL API** as part of the VCR Product. This enables the use of **Zabbix** as a robust base for monitoring and controlling **IoT devices**.
- *First use case*: Control of mobile traffic jam warning installations on **German Autobahns**.
- **🔓 Open Source Extraction & AI Integration**:
- Extracted the core functionality of the API to publish it as an **Open Source** project.
- Enhanced it with **Model Context Protocol (MCP)** and **AI agent** integration to enable workflow and agent-supported use cases within the VCR or other applications.
## 📅 Planned Enhancements
- **📦 CI/CD & Package Publishing**:
- Build and publish the API as an **npm package** as part of the `.forgejo` workflow to simplify distribution and updates.
- **🧱 Flexible Usage by publishing to [npmjs.com](https://www.npmjs.com/)**:
- Transform the package into a versatile tool that can be used either **standalone** or as a **core library**, allowing other projects to include and extend it easily.
- **🧩 Extension Project**: `zabbix-graphql-api-problems`
- Create the first official extension, `zabbix-graphql-api-problems`
- Add support for **problem and trigger-related** queries. This will leverage **MCP + AI agents** to automatically react to Zabbix problems within external workflows.

View file

@ -13,7 +13,7 @@ interface DeviceValueMessage {
""" """
Represents the timestamp at which a specific event, message, or data point was created or recorded. Represents the timestamp at which a specific event, message, or data point was created or recorded.
The format should align with standard expectations (e.g., ISO 8601). The format should align with standard expectations (e.g. ISO 8601).
""" """
timestamp: String timestamp: String

View file

@ -18,6 +18,10 @@ interface Device implements Host {
name: String name: String
"""Device configuration tags.""" """Device configuration tags."""
tags: DeviceConfig tags: DeviceConfig
"""Host inventory data."""
inventory: Inventory
"""List of monitored items for this host."""
items: [ZabbixItem!]
"""State of the device.""" """State of the device."""
state: DeviceState state: DeviceState
} }
@ -74,7 +78,7 @@ type OperationalDeviceData {
temperature: Float temperature: Float
"""Device voltage.""" """Device voltage."""
voltage: Float voltage: Float
"""Signal strength (e.g., WiFi or GSM).""" """Signal strength (e.g. WiFi or GSM)."""
signalstrength: Float signalstrength: Float
"""Current location of the device.""" """Current location of the device."""
location: Location location: Location
@ -133,6 +137,10 @@ type GenericDevice implements Host & Device {
name: String name: String
"""Device configuration tags.""" """Device configuration tags."""
tags: DeviceConfig tags: DeviceConfig
"""Host inventory data."""
inventory: Inventory
"""List of monitored items for this host."""
items: [ZabbixItem!]
"""State of the generic device.""" """State of the generic device."""
state: GenericDeviceState state: GenericDeviceState
} }

View file

@ -13,6 +13,8 @@ type SinglePanelDevice implements Host & Device {
hostgroups: [HostGroup!] hostgroups: [HostGroup!]
name: String name: String
tags: DeviceConfig tags: DeviceConfig
inventory: Inventory
items: [ZabbixItem!]
state: PanelState state: PanelState
} }
@ -71,6 +73,10 @@ type FourPanelDevice implements Host & Device {
name: String name: String
"""Device configuration tags.""" """Device configuration tags."""
tags: DeviceConfig tags: DeviceConfig
"""Host inventory data."""
inventory: Inventory
"""List of monitored items for this host."""
items: [ZabbixItem!]
"""State of the four-panel device.""" """State of the four-panel device."""
state: FourPanelState state: FourPanelState
} }

View file

@ -12,7 +12,7 @@ type SensorDistanceValue implements DeviceValue {
""" """
Represents the MAC address of the device. Typically formatted as a 12-character Represents the MAC address of the device. Typically formatted as a 12-character
hexadecimal string (e.g., "00:1A:2B:3C:4D:5E"). hexadecimal string (e.g. "00:1A:2B:3C:4D:5E").
""" """
mac: String mac: String

View file

@ -17,6 +17,10 @@ type DistanceTrackerDevice implements Host & Device {
name: String name: String
"""Device configuration tags.""" """Device configuration tags."""
tags: DeviceConfig tags: DeviceConfig
"""Host inventory data."""
inventory: Inventory
"""List of monitored items for this host."""
items: [ZabbixItem!]
"""State of the distance tracker device.""" """State of the distance tracker device."""
state: DistanceTrackerState state: DistanceTrackerState
} }

View file

@ -0,0 +1,50 @@
"""
WeatherSensorDevice represents a device that retrieves weather information
from public APIs (e.g. Open-Meteo) using Zabbix HTTP agent items.
"""
type WeatherSensorDevice implements Host & Device {
"""Internal Zabbix ID of the device."""
hostid: ID!
"""
Per convention a uuid is used as hostname to identify devices if they do not have a unique hostname.
"""
host: String!
"""Classification of the device."""
deviceType: String
"""List of host groups this device belongs to."""
hostgroups: [HostGroup!]
"""Visible name of the device."""
name: String
"""Device configuration tags."""
tags: DeviceConfig
"""Host inventory data."""
inventory: Inventory
"""List of monitored items for this host."""
items: [ZabbixItem!]
"""State of the weather sensor device."""
state: WeatherSensorState
}
"""
Represents the state of a weather sensor device.
"""
type WeatherSensorState implements DeviceState {
"""Operational data (telemetry)."""
operational: OperationalDeviceData
"""Current business values (weather data)."""
current: WeatherSensorValues
}
"""
Aggregated weather information retrieved from the API.
"""
type WeatherSensorValues {
"""
Current temperature at the device location (in Celsius).
"""
temperature: Float
"""
Warnings or description of the street conditions (e.g. Ice, Rain, Clear).
"""
streetConditionWarnings: String
}

View file

@ -11,7 +11,9 @@ type Mutation {
"""List of host group IDs to assign the host to.""" """List of host group IDs to assign the host to."""
hostgroupids:[Int!]!, hostgroupids:[Int!]!,
"""List of template IDs to link to the host.""" """List of template IDs to link to the host."""
templateids: [Int!]!, templateids: [Int],
"""List of template names to link to the host."""
templateNames: [String],
"""Optional location information for the host inventory.""" """Optional location information for the host inventory."""
location: LocationInput location: LocationInput
): CreateHostResponse ): CreateHostResponse
@ -85,7 +87,7 @@ type Mutation {
deleteTemplates( deleteTemplates(
"""List of template IDs to delete.""" """List of template IDs to delete."""
templateids: [Int!], templateids: [Int!],
"""Wildcard name pattern for templates to delete (e.g., 'Template%').""" """Wildcard name pattern for templates to delete (e.g. 'Template%')."""
name_pattern: String name_pattern: String
): [DeleteResponse!] ): [DeleteResponse!]
@ -100,6 +102,88 @@ type Mutation {
"""Wildcard name pattern for template groups to delete.""" """Wildcard name pattern for template groups to delete."""
name_pattern: String name_pattern: String
): [DeleteResponse!] ): [DeleteResponse!]
"""
Delete hosts by their IDs or by a name pattern.
Authentication: Requires `zbx_session` cookie or `zabbix-auth-token` header.
"""
deleteHosts(
"""List of host IDs to delete."""
hostids: [Int!],
"""Wildcard name pattern for hosts to delete."""
name_pattern: String
): [DeleteResponse!]
"""
Delete host groups by their IDs or by a name pattern.
Authentication: Requires `zbx_session` cookie or `zabbix-auth-token` header.
"""
deleteHostGroups(
"""List of host group IDs to delete."""
groupids: [Int!],
"""Wildcard name pattern for host groups to delete."""
name_pattern: String
): [DeleteResponse!]
"""
Runs a smoketest: creates a template, links a host, verifies it, and cleans up.
"""
runSmoketest(
"""Technical name for the smoketest host."""
hostName: String!,
"""Technical name for the smoketest template."""
templateName: String!,
"""Technical name for the smoketest host group."""
groupName: String!
): SmoketestResponse!
"""
Runs all regression tests.
"""
runAllRegressionTests(
"""Technical name for the test host."""
hostName: String!,
"""Technical name for the test host group."""
groupName: String!
): SmoketestResponse!
}
"""
Response object for the smoketest operation.
"""
type SmoketestResponse {
"""
True if all steps of the smoketest succeeded.
"""
success: Boolean!
"""
Overall status message.
"""
message: String
"""
Detailed results for each step.
"""
steps: [SmoketestStep!]!
}
"""
Results for a single step in the smoketest.
"""
type SmoketestStep {
"""
Name of the step (e.g. 'Create Template').
"""
name: String!
"""
True if the step succeeded.
"""
success: Boolean!
"""
Status message or error message for the step.
"""
message: String
} }
#################################################################### ####################################################################
@ -174,6 +258,10 @@ input CreateTemplate {
Tags to assign to the template. Tags to assign to the template.
""" """
tags: [CreateTag!] tags: [CreateTag!]
"""
User macros to assign to the template.
"""
macros: [CreateMacro!]
} }
""" """
@ -189,19 +277,23 @@ input CreateTemplateItem {
""" """
name: String! name: String!
""" """
Zabbix item type (e.g., 0 for Zabbix Agent, 18 for Dependent). Zabbix item type (e.g. 0 for Zabbix Agent, 18 for Dependent).
""" """
type: Int type: Int
""" """
Zabbix item status (0 for Enabled, 1 for Disabled).
"""
status: Int
"""
Technical key of the item. Technical key of the item.
""" """
key: String! key: String!
""" """
Type of information (e.g., 0 for Float, 3 for Int, 4 for Text). Type of information (e.g. 0 for Float, 3 for Int, 4 for Text).
""" """
value_type: Int value_type: Int
""" """
History storage period (e.g., '2d', '90d'). History storage period (e.g. '2d', '90d').
""" """
history: String history: String
""" """
@ -217,6 +309,10 @@ input CreateTemplateItem {
""" """
description: String description: String
""" """
URL for HTTP Agent items.
"""
url: String
"""
Preprocessing steps for the item values. Preprocessing steps for the item values.
""" """
preprocessing: [CreateItemPreprocessing!] preprocessing: [CreateItemPreprocessing!]
@ -245,7 +341,7 @@ Input for an item preprocessing step.
""" """
input CreateItemPreprocessing { input CreateItemPreprocessing {
""" """
Type of preprocessing step (e.g., 12 for JSONPath, 21 for JavaScript). Type of preprocessing step (e.g. 12 for JSONPath, 21 for JavaScript).
""" """
type: Int! type: Int!
""" """
@ -286,6 +382,20 @@ input CreateTag {
value: String value: String
} }
"""
Input for creating a user macro.
"""
input CreateMacro {
"""
Macro name (e.g. '{$LAT}').
"""
macro: String!
"""
Macro value.
"""
value: String!
}
""" """
Response for a template import operation. Response for a template import operation.
""" """
@ -413,9 +523,21 @@ input CreateHost {
""" """
groupids: [Int] groupids: [Int]
""" """
List of template IDs to link to the host.
"""
templateids: [Int]
"""
List of template names to link to the host.
"""
templateNames: [String]
"""
Location information for the host. Location information for the host.
""" """
location: LocationInput location: LocationInput
"""
User macros to assign to the host.
"""
macros: [CreateMacro!]
} }
""" """
@ -477,7 +599,7 @@ input UserRoleInput {
""" """
name: String name: String
""" """
Type of role (e.g., 1 for User, 2 for Admin, 3 for Super Admin). Type of role (e.g. 1 for User, 2 for Admin, 3 for Super Admin).
""" """
type: Int type: Int
""" """
@ -515,7 +637,7 @@ input UserRoleRulesInput {
""" """
api_access: Int api_access: Int
""" """
API mode (e.g., 0 for white-list, 1 for black-list). API mode (e.g. 0 for white-list, 1 for black-list).
""" """
api_mode: Int api_mode: Int
""" """
@ -541,7 +663,7 @@ input UserRoleRuleInput {
""" """
name: String name: String
""" """
Status (e.g., 1 for enabled, 0 for disabled). Status (e.g. 1 for enabled, 0 for disabled).
""" """
status: Int status: Int
} }

View file

@ -41,6 +41,14 @@ interface Host {
of a device in the system (capabilities, functionalities, or purpose). of a device in the system (capabilities, functionalities, or purpose).
""" """
deviceType: String deviceType: String
"""
Host inventory data.
"""
inventory: Inventory
"""
List of monitored items for this host.
"""
items: [ZabbixItem!]
} }
""" """
@ -72,7 +80,7 @@ type ZabbixItem {
""" """
lastvalue: String lastvalue: String
""" """
Type of information (e.g., 0 for Float, 3 for Int, 4 for Text). Type of information (e.g. 0 for Float, 3 for Int, 4 for Text).
""" """
value_type: Int! value_type: Int!
""" """
@ -88,9 +96,49 @@ type ZabbixItem {
""" """
type: DeviceCommunicationType type: DeviceCommunicationType
""" """
Raw Zabbix item type as integer.
"""
type_int: Int
"""
Raw Zabbix item status as integer.
"""
status_int: Int
"""
Hosts that this item is linked to. Hosts that this item is linked to.
""" """
hosts: [Host!] hosts: [Host!]
"""
History storage period (e.g. '2d', '90d').
"""
history: String
"""
Update interval.
"""
delay: String
"""
Units of the value.
"""
units: String
"""
Description of the item.
"""
description: String
"""
Preprocessing steps for the item.
"""
preprocessing: [JSONObject!]
"""
Tags assigned to the item.
"""
tags: [JSONObject!]
"""
Master item ID for dependent items.
"""
master_itemid: Int
"""
Master item for dependent items.
"""
master_item: ZabbixItem
} }
""" """
@ -182,9 +230,17 @@ type Template {
""" """
templateid: String! templateid: String!
""" """
Technical name of the template.
"""
host: String!
"""
Name of the template. Name of the template.
""" """
name: String name: String
"""
List of items for this template.
"""
items: [ZabbixItem!]
} }
""" """

View file

@ -4,14 +4,15 @@ import {
DeviceStatus, DeviceStatus,
Host, Host,
MutationCreateHostArgs, MutationCreateHostArgs,
MutationDeleteTemplateGroupsArgs,
MutationDeleteTemplatesArgs,
MutationImportHostGroupsArgs, MutationImportHostGroupsArgs,
MutationImportHostsArgs, MutationImportHostsArgs,
MutationImportTemplateGroupsArgs, MutationImportTemplateGroupsArgs,
MutationImportTemplatesArgs, MutationImportTemplatesArgs,
MutationDeleteTemplatesArgs,
MutationDeleteTemplateGroupsArgs,
MutationImportUserRightsArgs, MutationImportUserRightsArgs,
Permission, QueryAllDevicesArgs, Permission,
QueryAllDevicesArgs,
QueryAllHostGroupsArgs, QueryAllHostGroupsArgs,
QueryAllHostsArgs, QueryAllHostsArgs,
QueryExportHostValueHistoryArgs, QueryExportHostValueHistoryArgs,
@ -24,6 +25,9 @@ import {
} from "../schema/generated/graphql.js"; } from "../schema/generated/graphql.js";
import {HostImporter} from "../execution/host_importer.js"; import {HostImporter} from "../execution/host_importer.js";
import {HostDeleter} from "../execution/host_deleter.js";
import {SmoketestExecutor} from "../execution/smoketest_executor.js";
import {RegressionTestExecutor} from "../execution/regression_test_executor.js";
import {TemplateImporter} from "../execution/template_importer.js"; import {TemplateImporter} from "../execution/template_importer.js";
import {TemplateDeleter} from "../execution/template_deleter.js"; import {TemplateDeleter} from "../execution/template_deleter.js";
import {HostValueExporter} from "../execution/host_exporter.js"; import {HostValueExporter} from "../execution/host_exporter.js";
@ -31,7 +35,8 @@ import {logger} from "../logging/logger.js";
import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js"; import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js";
import { import {
ZabbixCreateHostRequest, ZabbixCreateHostRequest,
ZabbixQueryDevices, ZabbixQueryDevicesArgs, ZabbixQueryDevices,
ZabbixQueryDevicesArgs,
ZabbixQueryHostsRequestWithItemsAndInventory, ZabbixQueryHostsRequestWithItemsAndInventory,
} from "../datasources/zabbix-hosts.js"; } from "../datasources/zabbix-hosts.js";
import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js"; import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js";
@ -47,14 +52,11 @@ import {
ZabbixQueryUserRolesRequest ZabbixQueryUserRolesRequest
} from "../datasources/zabbix-userroles.js"; } from "../datasources/zabbix-userroles.js";
import { import {
ZabbixCreateItemRequest, TemplateHelper,
ZabbixCreateTemplateGroupRequest,
ZabbixCreateTemplateRequest,
ZabbixQueryItemRequest,
ZabbixQueryTemplateGroupRequest, ZabbixQueryTemplateGroupRequest,
ZabbixQueryTemplatesRequest ZabbixQueryTemplatesRequest
} from "../datasources/zabbix-templates.js"; } from "../datasources/zabbix-templates.js";
import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api.js"; import {zabbixAPI} from "../datasources/zabbix-api.js";
import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js"; import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js";
import {isDevice} from "./resolver_helpers.js"; import {isDevice} from "./resolver_helpers.js";
import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js"; import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js";
@ -76,7 +78,7 @@ export function createResolvers(): Resolvers {
return ZabbixPermissionsHelper.hasUserPermissions(zabbixAPI, args, zabbixAuthToken, cookie) return ZabbixPermissionsHelper.hasUserPermissions(zabbixAPI, args, zabbixAuthToken, cookie)
}, },
locations: (_parent: any, args: Object, {dataSources, zabbixAuthToken}: any) => { locations: (_parent: any, args: Object, {dataSources, zabbixAuthToken}: any) => {
return dataSources.zabbixAPI.getLocations(zabbixAuthToken, new ParsedArgs(args)); return dataSources.zabbixAPI.getLocations(new ParsedArgs(args), zabbixAuthToken);
}, },
apiVersion: () => { apiVersion: () => {
return Config.API_VERSION ?? "unknown" return Config.API_VERSION ?? "unknown"
@ -187,6 +189,17 @@ export function createResolvers(): Resolvers {
zabbixAuthToken, zabbixAuthToken,
cookie cookie
}: any) => { }: any) => {
if (args.templateNames?.length) {
const templateidsByName = await TemplateHelper.findTemplateIdsByName(args.templateNames as string[], zabbixAPI, zabbixAuthToken, cookie);
if (!templateidsByName) {
return {
error: {
message: `Unable to find templates: ${args.templateNames}`
}
}
}
args.templateids = (args.templateids || []).concat(templateidsByName);
}
return await new ZabbixCreateHostRequest(zabbixAuthToken, cookie).executeRequestThrowError( return await new ZabbixCreateHostRequest(zabbixAuthToken, cookie).executeRequestThrowError(
zabbixAPI, zabbixAPI,
new ParsedArgs(args) new ParsedArgs(args)
@ -246,6 +259,30 @@ export function createResolvers(): Resolvers {
cookie cookie
}: any) => { }: any) => {
return TemplateDeleter.deleteTemplateGroups(args.groupids, args.name_pattern, zabbixAuthToken, cookie) return TemplateDeleter.deleteTemplateGroups(args.groupids, args.name_pattern, zabbixAuthToken, cookie)
},
deleteHosts: async (_parent: any, args: any, {
zabbixAuthToken,
cookie
}: any) => {
return HostDeleter.deleteHosts(args.hostids, args.name_pattern, zabbixAuthToken, cookie)
},
deleteHostGroups: async (_parent: any, args: any, {
zabbixAuthToken,
cookie
}: any) => {
return HostDeleter.deleteHostGroups(args.groupids, args.name_pattern, zabbixAuthToken, cookie)
},
runSmoketest: async (_parent: any, args: any, {
zabbixAuthToken,
cookie
}: any) => {
return SmoketestExecutor.runSmoketest(args.hostName, args.templateName, args.groupName, zabbixAuthToken, cookie)
},
runAllRegressionTests: async (_parent: any, args: any, {
zabbixAuthToken,
cookie
}: any) => {
return RegressionTestExecutor.runAllRegressionTests(args.hostName, args.groupName, zabbixAuthToken, cookie)
} }
}, },
@ -321,6 +358,21 @@ export function createResolvers(): Resolvers {
DENY: Permission.Deny DENY: Permission.Deny
}, },
ZabbixItem: {
type_int: (parent: any) => parent.type,
status_int: (parent: any) => parent.status,
master_item: (parent: any, _args: any, _context: any, info: any) => {
if (!parent.master_itemid || parent.master_itemid === "0" || parent.master_itemid === 0) {
return null;
}
// This is a bit hacky but works if the siblings are in the parent's items array
// and Apollo has already resolved them.
// However, 'parent' here is just the item data.
// To do this properly we'd need to fetch the master item if it's not present.
// For now, let's just return null if we can't find it easily, or just rely on the agent.
return null;
}
},
DeviceCommunicationType: { DeviceCommunicationType: {
ZABBIX_AGENT: DeviceCommunicationType.ZABBIX_AGENT, ZABBIX_AGENT: DeviceCommunicationType.ZABBIX_AGENT,
ZABBIX_AGENT_ACTIVE: DeviceCommunicationType.ZABBIX_AGENT_ACTIVE, ZABBIX_AGENT_ACTIVE: DeviceCommunicationType.ZABBIX_AGENT_ACTIVE,

View file

@ -8,7 +8,7 @@ import express from 'express';
import cors from "cors"; import cors from "cors";
import {ApolloServerPluginDrainHttpServer} from '@apollo/server/plugin/drainHttpServer'; import {ApolloServerPluginDrainHttpServer} from '@apollo/server/plugin/drainHttpServer';
import {logger} from "../logging/logger.js"; import {logger} from "../logging/logger.js";
import {zabbixAPI, zabbixRequestAuthToken} from "../datasources/zabbix-api.js"; import {zabbixAPI, zabbixDevelopmentToken} from "../datasources/zabbix-api.js";
import {WebSocketServer} from "ws"; import {WebSocketServer} from "ws";
import {useServer} from "graphql-ws/lib/use/ws"; import {useServer} from "graphql-ws/lib/use/ws";
@ -82,7 +82,7 @@ async function startApolloServer() {
dataSources: { dataSources: {
zabbixAPI: zabbixAPI, zabbixAPI: zabbixAPI,
}, },
zabbixAuthToken: req.headers["zabbix-auth-token"] ?? zabbixRequestAuthToken, zabbixAuthToken: req.headers["zabbix-auth-token"] ?? zabbixDevelopmentToken,
cookie: req.headers.cookie, cookie: req.headers.cookie,
token: req.headers.token token: req.headers.token
}; };

View file

@ -9,8 +9,8 @@ static readonly DRY_RUN = process.env.DRY_RUN
static readonly SCHEMA_PATH = process.env.SCHEMA_PATH || './schema/' static readonly SCHEMA_PATH = process.env.SCHEMA_PATH || './schema/'
static readonly ADDITIONAL_SCHEMAS = process.env.ADDITIONAL_SCHEMAS static readonly ADDITIONAL_SCHEMAS = process.env.ADDITIONAL_SCHEMAS
static readonly ADDITIONAL_RESOLVERS = process.env.ADDITIONAL_RESOLVERS static readonly ADDITIONAL_RESOLVERS = process.env.ADDITIONAL_RESOLVERS
static readonly ZABBIX_AUTH_TOKEN_FOR_REQUESTS = process.env.ZABBIX_AUTH_TOKEN_FOR_REQUESTS static readonly ZABBIX_DEVELOPMENT_TOKEN = process.env.ZABBIX_DEVELOPMENT_TOKEN
static readonly ZABBIX_AUTH_TOKEN = process.env.ZABBIX_AUTH_TOKEN static readonly ZABBIX_PRIVILEGE_ESCALATION_TOKEN = process.env.ZABBIX_PRIVILEGE_ESCALATION_TOKEN
static readonly ZABBIX_EDGE_DEVICE_BASE_GROUP = process.env.ZABBIX_EDGE_DEVICE_BASE_GROUP static readonly ZABBIX_EDGE_DEVICE_BASE_GROUP = process.env.ZABBIX_EDGE_DEVICE_BASE_GROUP
static readonly ZABBIX_ROADWORK_BASE_GROUP = process.env.ZABBIX_ROADWORK_BASE_GROUP static readonly ZABBIX_ROADWORK_BASE_GROUP = process.env.ZABBIX_ROADWORK_BASE_GROUP
static readonly ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX = process.env.ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX || "Permissions" static readonly ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX = process.env.ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX || "Permissions"

View file

@ -9,8 +9,8 @@ import {logger} from "../logging/logger.js";
import {ParsedArgs, ZabbixErrorResult, ZabbixRequest, ZabbixResult} from "./zabbix-request.js"; import {ParsedArgs, ZabbixErrorResult, ZabbixRequest, ZabbixResult} from "./zabbix-request.js";
import {Config} from "../common_utils.js"; import {Config} from "../common_utils.js";
export const zabbixRequestAuthToken = Config.ZABBIX_AUTH_TOKEN_FOR_REQUESTS export const zabbixDevelopmentToken = Config.ZABBIX_DEVELOPMENT_TOKEN
export const zabbixSuperAuthToken = Config.ZABBIX_AUTH_TOKEN export const zabbixPrivilegeEscalationToken = Config.ZABBIX_PRIVILEGE_ESCALATION_TOKEN
export const ZABBIX_EDGE_DEVICE_BASE_GROUP = Config.ZABBIX_EDGE_DEVICE_BASE_GROUP || Config.ZABBIX_ROADWORK_BASE_GROUP || "Roadwork" export const ZABBIX_EDGE_DEVICE_BASE_GROUP = Config.ZABBIX_EDGE_DEVICE_BASE_GROUP || Config.ZABBIX_ROADWORK_BASE_GROUP || "Roadwork"
export const FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX = new RegExp(`^(${ZABBIX_EDGE_DEVICE_BASE_GROUP})\/`) export const FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX = new RegExp(`^(${ZABBIX_EDGE_DEVICE_BASE_GROUP})\/`)

View file

@ -4,7 +4,7 @@ import {
FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX, FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX,
ZABBIX_EDGE_DEVICE_BASE_GROUP, ZABBIX_EDGE_DEVICE_BASE_GROUP,
ZabbixAPI, ZabbixAPI,
zabbixSuperAuthToken zabbixPrivilegeEscalationToken
} from "./zabbix-api.js"; } from "./zabbix-api.js";
import {logger} from "../logging/logger.js"; import {logger} from "../logging/logger.js";
import {ZabbixRequestWithPermissions} from "./zabbix-permissions.js"; import {ZabbixRequestWithPermissions} from "./zabbix-permissions.js";
@ -16,14 +16,14 @@ export interface CreateHostGroupResult {
const hostGroupReadWritePermissions = { const hostGroupReadWritePermissions = {
permissions: [ permissions: [
{ {
objectName: "Hostgroup/ConstructionSite", objectName: "Hostgroup",
permission: Permission.ReadWrite permission: Permission.ReadWrite
}] }]
} }
export class ZabbixCreateHostGroupRequest extends ZabbixRequestWithPermissions<CreateHostGroupResult> { export class ZabbixCreateHostGroupRequest extends ZabbixRequestWithPermissions<CreateHostGroupResult> {
constructor(_authToken?: string | null, cookie?: string) { constructor(_authToken?: string | null, cookie?: string) {
super("hostgroup.create", zabbixSuperAuthToken, cookie, hostGroupReadWritePermissions); super("hostgroup.create", zabbixPrivilegeEscalationToken, cookie, hostGroupReadWritePermissions);
} }
} }
@ -69,6 +69,11 @@ export class ZabbixQueryHostgroupsRequest extends ZabbixRequestWithPermissions<Z
} }
export class ZabbixDeleteHostGroupsRequest extends ZabbixRequestWithPermissions<{ groupids: string[] }> {
constructor(authToken?: string | null, cookie?: string | null) {
super("hostgroup.delete", authToken, cookie, hostGroupReadWritePermissions);
}
}
export class GroupHelper { export class GroupHelper {
public static groupFullName(groupName: string) { public static groupFullName(groupName: string) {

View file

@ -85,7 +85,7 @@ export class ZabbixQueryHostsGenericRequestWithItems<T extends ZabbixResult, A e
"hostid", "hostid",
"host", "host",
"name", "name",
"hostgroup", "hostgroups",
"items", "items",
"description", "description",
"parentTemplates" "parentTemplates"
@ -173,7 +173,8 @@ export interface ZabbixCreateHostInputParams extends ZabbixParams {
} }
templateids?: [number]; templateids?: [number];
hostgroupids?: [number]; hostgroupids?: [number];
additionalParams?: [number]; macros?: { macro: string, value: string }[];
additionalParams?: any;
} }
@ -183,6 +184,7 @@ class ZabbixCreateHostParams implements ZabbixParams {
this.name = inputParams.name; this.name = inputParams.name;
this.description = inputParams.description; this.description = inputParams.description;
if (inputParams.location) { if (inputParams.location) {
this.inventory_mode = 0;
this.inventory = { this.inventory = {
location: inputParams.location.name, location: inputParams.location.name,
location_lat: inputParams.location.location_lat, location_lat: inputParams.location.location_lat,
@ -199,11 +201,15 @@ class ZabbixCreateHostParams implements ZabbixParams {
return {groupid: groupid} return {groupid: groupid}
}); });
} }
if (inputParams.macros) {
this.macros = inputParams.macros;
}
} }
host: string host: string
name: string name: string
description: string description: string
inventory_mode?: number
inventory?: { inventory?: {
location: String location: String
@ -212,6 +218,7 @@ class ZabbixCreateHostParams implements ZabbixParams {
} }
templates?: any templates?: any
groups?: any groups?: any
macros?: { macro: string, value: string }[]
} }
@ -228,3 +235,9 @@ export class ZabbixCreateHostRequest extends ZabbixRequest<CreateHostResponse> {
return args?.zabbix_params || {}; return args?.zabbix_params || {};
} }
} }
export class ZabbixDeleteHostsRequest extends ZabbixRequest<{ hostids: string[] }> {
constructor(authToken?: string | null, cookie?: string | null) {
super("host.delete", authToken, cookie);
}
}

View file

@ -25,6 +25,7 @@ export type ZabbixErrorResult = {
export const isZabbixErrorResult = (value: any): value is ZabbixErrorResult => value instanceof Object && "error" in value && !!value.error; export const isZabbixErrorResult = (value: any): value is ZabbixErrorResult => value instanceof Object && "error" in value && !!value.error;
export interface ZabbixParams { export interface ZabbixParams {
[key: string]: any
} }
export interface ZabbixWithTagsParams extends ZabbixParams { export interface ZabbixWithTagsParams extends ZabbixParams {
@ -131,13 +132,12 @@ export class ParsedArgs {
} }
if (this.name_pattern) { if (this.name_pattern) {
if ("search" in result) { if (!("search" in result)) {
(<any>result.search).name = this.name_pattern (<any>result).search = {}
} else {
(<any>result).search = {
name: this.name_pattern,
}
} }
(<any>result).search.name = this.name_pattern;
(<any>result).search.host = this.name_pattern;
(<any>result).searchByAny = true;
} }
return result return result

View file

@ -1,10 +1,14 @@
import {ZabbixRequest} from "./zabbix-request.js"; import {ZabbixRequest, ParsedArgs, isZabbixErrorResult, ZabbixParams} from "./zabbix-request.js";
import {ZabbixAPI} from "./zabbix-api.js";
import {logger} from "../logging/logger.js";
export interface ZabbixQueryTemplateResponse { export interface ZabbixQueryTemplateResponse {
templateid: string, templateid: string,
host: string,
uuid: string, uuid: string,
name: string, name: string,
items?: any[]
} }
@ -12,6 +16,14 @@ export class ZabbixQueryTemplatesRequest extends ZabbixRequest<ZabbixQueryTempla
constructor(authToken?: string | null, cookie?: string | null,) { constructor(authToken?: string | null, cookie?: string | null,) {
super("template.get", authToken, cookie); super("template.get", authToken, cookie);
} }
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
"selectItems": "extend",
"output": "extend",
...args?.zabbix_params
};
}
} }
@ -65,3 +77,23 @@ export class ZabbixDeleteTemplateGroupsRequest extends ZabbixRequest<{ groupids:
} }
export class TemplateHelper {
public static async findTemplateIdsByName(templateNames: string[], zabbixApi: ZabbixAPI, zabbixAuthToken?: string, cookie?: string) {
let result: number[] = []
for (let templateName of templateNames) {
// Use name_pattern which now searches both visibility name and technical name (host)
let templates = await new ZabbixQueryTemplatesRequest(zabbixAuthToken, cookie).executeRequestReturnError(zabbixApi, new ParsedArgs({
name_pattern: templateName
}))
if (isZabbixErrorResult(templates) || !templates?.length) {
logger.error(`Unable to find templateName=${templateName}`)
return null
}
result.push(...templates.map((t) => Number(t.templateid)))
}
return result
}
}

View file

@ -0,0 +1,110 @@
import {DeleteResponse} from "../schema/generated/graphql.js";
import {
ZabbixDeleteHostsRequest,
ZabbixQueryHostsGenericRequest,
} from "../datasources/zabbix-hosts.js";
import {
ZabbixDeleteHostGroupsRequest,
ZabbixQueryHostgroupsRequest,
ZabbixQueryHostgroupsParams,
GroupHelper
} from "../datasources/zabbix-hostgroups.js";
import {isZabbixErrorResult, ParsedArgs} from "../datasources/zabbix-request.js";
import {zabbixAPI} from "../datasources/zabbix-api.js";
export class HostDeleter {
public static async deleteHosts(hostids: number[] | null | undefined, name_pattern?: string | null, zabbixAuthToken?: string, cookie?: string): Promise<DeleteResponse[]> {
const result: DeleteResponse[] = [];
let idsToDelete = hostids ? [...hostids] : [];
if (name_pattern) {
const queryResult = await new ZabbixQueryHostsGenericRequest("host.get", zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs({ name_pattern: name_pattern }));
if (!isZabbixErrorResult(queryResult) && Array.isArray(queryResult)) {
const foundIds = queryResult.map((t: any) => Number(t.hostid));
// Merge and deduplicate
idsToDelete = Array.from(new Set([...idsToDelete, ...foundIds]));
}
}
if (idsToDelete.length === 0) {
return [];
}
const deleteResult = await new ZabbixDeleteHostsRequest(zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs(idsToDelete));
if (isZabbixErrorResult(deleteResult)) {
let errorMessage = deleteResult.error.message;
if (deleteResult.error.data) {
errorMessage += " " + (typeof deleteResult.error.data === 'string' ? deleteResult.error.data : JSON.stringify(deleteResult.error.data));
}
for (const id of idsToDelete) {
result.push({
id: id,
message: errorMessage,
error: deleteResult.error
});
}
} else if (deleteResult?.hostids) {
for (const id of idsToDelete) {
result.push({
id: id,
message: `Host ${id} deleted successfully`
});
}
}
return result;
}
public static async deleteHostGroups(groupids: number[] | null | undefined, name_pattern?: string | null, zabbixAuthToken?: string, cookie?: string): Promise<DeleteResponse[]> {
const result: DeleteResponse[] = [];
let idsToDelete = groupids ? [...groupids] : [];
if (name_pattern) {
const queryResult = await new ZabbixQueryHostgroupsRequest(zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ZabbixQueryHostgroupsParams({
filter_name: GroupHelper.groupFullName(name_pattern)
}));
if (!isZabbixErrorResult(queryResult) && Array.isArray(queryResult)) {
const foundIds = queryResult.map(g => Number(g.groupid));
// Merge and deduplicate
idsToDelete = Array.from(new Set([...idsToDelete, ...foundIds]));
}
}
if (idsToDelete.length === 0) {
return [];
}
const deleteResult = await new ZabbixDeleteHostGroupsRequest(zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs(idsToDelete));
if (isZabbixErrorResult(deleteResult)) {
let errorMessage = deleteResult.error.message;
if (deleteResult.error.data) {
errorMessage += " " + (typeof deleteResult.error.data === 'string' ? deleteResult.error.data : JSON.stringify(deleteResult.error.data));
}
for (const id of idsToDelete) {
result.push({
id: id,
message: errorMessage,
error: deleteResult.error
});
}
} else if (deleteResult?.groupids) {
for (const id of idsToDelete) {
result.push({
id: id,
message: `Host group ${id} deleted successfully`
});
}
}
return result;
}
}

View file

@ -6,7 +6,8 @@ import {
InputMaybe InputMaybe
} from "../schema/generated/graphql.js"; } from "../schema/generated/graphql.js";
import {logger} from "../logging/logger.js"; import {logger} from "../logging/logger.js";
import {ZabbixQueryTemplatesRequest} from "../datasources/zabbix-templates.js"; import {ZabbixCreateHostRequest} from "../datasources/zabbix-hosts.js";
import {ZabbixQueryTemplatesRequest, TemplateHelper} from "../datasources/zabbix-templates.js";
import {isZabbixErrorResult, ParsedArgs, ZabbixErrorResult} from "../datasources/zabbix-request.js"; import {isZabbixErrorResult, ParsedArgs, ZabbixErrorResult} from "../datasources/zabbix-request.js";
import {CreateHostGroupResult, GroupHelper, ZabbixCreateHostGroupRequest} from "../datasources/zabbix-hostgroups.js"; import {CreateHostGroupResult, GroupHelper, ZabbixCreateHostGroupRequest} from "../datasources/zabbix-hostgroups.js";
import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api.js"; import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api.js";
@ -110,32 +111,54 @@ export class HostImporter {
break break
} }
} }
let deviceImportResult: {
hostids?: string[]; let templateids = device.templateids ? [...device.templateids as number[]] : [];
error?: any; if (device.templateNames?.length) {
} = await zabbixAPI.requestByPath("host.create", new ParsedArgs( const resolvedTemplateids = await TemplateHelper.findTemplateIdsByName(device.templateNames as string[], zabbixAPI, zabbixAuthToken, cookie);
if (resolvedTemplateids) {
templateids.push(...resolvedTemplateids);
} else {
result.push({
deviceKey: device.deviceKey,
message: `Unable to find templates: ${device.templateNames}`
});
continue;
}
}
if (templateids.length === 0) {
const defaultTemplateId = await HostImporter.getTemplateIdForDeviceType(device.deviceType, zabbixAuthToken, cookie);
if (defaultTemplateId) {
templateids.push(defaultTemplateId);
}
}
// Deduplicate
groupids = Array.from(new Set(groupids));
templateids = Array.from(new Set(templateids));
let deviceImportResult = await new ZabbixCreateHostRequest(zabbixAuthToken, cookie).executeRequestReturnError(zabbixAPI, new ParsedArgs(
{ {
host: device.deviceKey, host: device.deviceKey,
name: device.name, name: device.name,
location: device.location, location: device.location,
templateids: [ templateids: templateids,
await HostImporter.getTemplateIdForDeviceType( hostgroupids: groupids,
device.deviceType, zabbixAuthToken, cookie)], macros: device.macros
hostgroupids: groupids
} }
), zabbixAuthToken, cookie) ))
if (deviceImportResult?.hostids?.length) {
result.push({ if (isZabbixErrorResult(deviceImportResult)) {
deviceKey: device.deviceKey,
hostid: deviceImportResult.hostids[0],
})
} else {
result.push({ result.push({
deviceKey: device.deviceKey, deviceKey: device.deviceKey,
message: `Unable to import deviceKey=${device.deviceKey}: ${deviceImportResult.error.message}`, message: `Unable to import deviceKey=${device.deviceKey}: ${deviceImportResult.error.message}`,
error: deviceImportResult.error error: deviceImportResult.error
}) })
} else {
result.push({
deviceKey: device.deviceKey,
hostid: deviceImportResult.hostids![0]?.toString(),
})
} }
} }

View file

@ -0,0 +1,237 @@
import {SmoketestResponse, SmoketestStep} from "../schema/generated/graphql.js";
import {HostImporter} from "./host_importer.js";
import {HostDeleter} from "./host_deleter.js";
import {TemplateImporter} from "./template_importer.js";
import {TemplateDeleter} from "./template_deleter.js";
import {logger} from "../logging/logger.js";
import {zabbixAPI} from "../datasources/zabbix-api.js";
import {ZabbixQueryHostsGenericRequest} from "../datasources/zabbix-hosts.js";
import {ZabbixQueryTemplatesRequest} from "../datasources/zabbix-templates.js";
import {ParsedArgs} from "../datasources/zabbix-request.js";
export class RegressionTestExecutor {
public static async runAllRegressionTests(hostName: string, groupName: string, zabbixAuthToken?: string, cookie?: string): Promise<SmoketestResponse> {
const steps: SmoketestStep[] = [];
let success = true;
try {
// Regression 1: Locations query argument order
// This verifies the fix where getLocations was called with (authToken, args) instead of (args, authToken)
try {
const locations = await zabbixAPI.getLocations(new ParsedArgs({ name_pattern: "NonExistent_" + Math.random() }), zabbixAuthToken, cookie);
steps.push({
name: "REG-LOC: Locations query argument order",
success: true,
message: "Locations query executed without session error"
});
} catch (error: any) {
steps.push({
name: "REG-LOC: Locations query argument order",
success: false,
message: `Failed: ${error.message}`
});
success = false;
}
// Regression 2: Template lookup by technical name
// Verifies that importHosts can link templates using their technical name (host)
const regTemplateName = "REG_TEMP_" + Math.random().toString(36).substring(7);
const regGroupName = "Templates/Roadwork/Devices";
const hostGroupName = "Roadwork/Devices";
const tempResult = await TemplateImporter.importTemplates([{
host: regTemplateName,
name: "Regression Test Template",
groupNames: [regGroupName]
}], zabbixAuthToken, cookie);
const tempSuccess = !!tempResult?.length && !tempResult[0].error;
steps.push({
name: "REG-TEMP: Template technical name lookup",
success: tempSuccess,
message: tempSuccess ? `Template ${regTemplateName} created and searchable by technical name` : `Failed to create template`
});
if (!tempSuccess) success = false;
// Regression 3: HTTP Agent URL support
// Verifies that templates with HTTP Agent items (including URL) can be imported
const httpTempName = "REG_HTTP_" + Math.random().toString(36).substring(7);
const httpTempResult = await TemplateImporter.importTemplates([{
host: httpTempName,
name: "Regression HTTP Template",
groupNames: [regGroupName],
items: [{
name: "HTTP Master",
type: 19, // HTTP Agent
key: "http.master",
value_type: 4,
url: "https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current=temperature_2m",
delay: "1m",
history: "0"
}]
}], zabbixAuthToken, cookie);
const httpSuccess = !!httpTempResult?.length && !httpTempResult[0].error;
steps.push({
name: "REG-HTTP: HTTP Agent URL support",
success: httpSuccess,
message: httpSuccess ? `Template ${httpTempName} with HTTP Agent item created successfully` : `Failed: ${httpTempResult?.[0]?.message}`
});
if (!httpSuccess) success = false;
// Regression 4: User Macro assignment for host and template creation
const macroTemplateName = "REG_MACRO_TEMP_" + Math.random().toString(36).substring(7);
const macroHostName = "REG_MACRO_HOST_" + Math.random().toString(36).substring(7);
const macroTempResult = await TemplateImporter.importTemplates([{
host: macroTemplateName,
name: "Regression Macro Template",
groupNames: [regGroupName],
macros: [
{ macro: "{$TEMP_MACRO}", value: "temp_value" }
]
}], zabbixAuthToken, cookie);
const macroTempImportSuccess = !!macroTempResult?.length && !macroTempResult[0].error;
let macroHostImportSuccess = false;
let macroVerifySuccess = false;
if (macroTempImportSuccess) {
const macroHostResult = await HostImporter.importHosts([{
deviceKey: macroHostName,
deviceType: "RegressionHost",
groupNames: [hostGroupName],
templateNames: [macroTemplateName],
macros: [
{ macro: "{$HOST_MACRO}", value: "host_value" }
]
}], zabbixAuthToken, cookie);
macroHostImportSuccess = !!macroHostResult?.length && !!macroHostResult[0].hostid;
if (macroHostImportSuccess) {
// Verify macros on host
const verifyHostResult = await new ZabbixQueryHostsGenericRequest("host.get", zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs({
filter_host: macroHostName,
selectMacros: "extend"
}));
// Verify macros on template
const verifyTempResult = await new ZabbixQueryTemplatesRequest(zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs({
filter_host: macroTemplateName,
selectMacros: "extend"
}));
const hasHostMacro = Array.isArray(verifyHostResult) && verifyHostResult.length > 0 &&
(verifyHostResult[0] as any).macros?.some((m: any) => m.macro === "{$HOST_MACRO}" && m.value === "host_value");
const hasTempMacro = Array.isArray(verifyTempResult) && verifyTempResult.length > 0 &&
(verifyTempResult[0] as any).macros?.some((m: any) => m.macro === "{$TEMP_MACRO}" && m.value === "temp_value");
macroVerifySuccess = !!(hasHostMacro && hasTempMacro);
}
}
const macroOverallSuccess = macroTempImportSuccess && macroHostImportSuccess && macroVerifySuccess;
steps.push({
name: "REG-MACRO: User Macro assignment",
success: macroOverallSuccess,
message: macroOverallSuccess
? "Macros successfully assigned to template and host"
: `Failed: TempImport=${macroTempImportSuccess}, HostImport=${macroHostImportSuccess}, Verify=${macroVerifySuccess}`
});
if (!macroOverallSuccess) success = false;
// Regression 5: Host retrieval and visibility (allHosts output fields fix)
if (success) {
const hostResult = await HostImporter.importHosts([{
deviceKey: hostName,
deviceType: "RegressionHost",
groupNames: [hostGroupName],
templateNames: [regTemplateName]
}], zabbixAuthToken, cookie);
const hostImportSuccess = !!hostResult?.length && !!hostResult[0].hostid;
if (hostImportSuccess) {
const hostid = hostResult[0].hostid;
logger.info(`REG-HOST: Host ${hostName} imported with ID ${hostid}. Verifying visibility...`);
// Verify visibility via allHosts (simulated)
const verifyResult = await new ZabbixQueryHostsGenericRequest("host.get", zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs({
filter_host: hostName
}));
const verified = Array.isArray(verifyResult) && verifyResult.length > 0 && (verifyResult[0] as any).host === hostName;
let fieldsVerified = false;
if (verified) {
const host = verifyResult[0] as any;
const hasGroups = Array.isArray(host.hostgroups) && host.hostgroups.length > 0;
const hasTemplates = Array.isArray(host.parentTemplates) && host.parentTemplates.length > 0;
fieldsVerified = hasGroups && hasTemplates;
if (!fieldsVerified) {
logger.error(`REG-HOST: Fields verification failed. Groups: ${hasGroups}, Templates: ${hasTemplates}. Host data: ${JSON.stringify(host)}`);
}
}
if (!verified) {
logger.error(`REG-HOST: Verification failed. Zabbix result: ${JSON.stringify(verifyResult)}`);
}
steps.push({
name: "REG-HOST: Host retrieval and visibility (incl. groups and templates)",
success: verified && fieldsVerified,
message: verified
? (fieldsVerified ? `Host ${hostName} retrieved successfully with groups and templates` : `Host ${hostName} retrieved but missing groups or templates`)
: "Host not found after import (output fields issue?)"
});
if (!verified || !fieldsVerified) success = false;
} else {
steps.push({
name: "REG-HOST: Host retrieval and visibility",
success: false,
message: `Host import failed: ${hostResult?.[0]?.message || hostResult?.[0]?.error?.message || "Unknown error"}`
});
success = false;
}
}
// Step 1: Create Host Group (Legacy test kept for compatibility)
const groupResult = await HostImporter.importHostGroups([{
groupName: groupName
}], zabbixAuthToken, cookie);
const groupSuccess = !!groupResult?.length && !groupResult[0].error;
steps.push({
name: "Create Host Group",
success: groupSuccess,
message: groupSuccess ? `Host group ${groupName} created` : `Failed: ${groupResult?.[0]?.error?.message || "Unknown error"}`
});
if (!groupSuccess) success = false;
// Cleanup
await HostDeleter.deleteHosts(null, hostName, zabbixAuthToken, cookie);
await HostDeleter.deleteHosts(null, macroHostName, zabbixAuthToken, cookie);
await TemplateDeleter.deleteTemplates(null, regTemplateName, zabbixAuthToken, cookie);
await TemplateDeleter.deleteTemplates(null, httpTempName, zabbixAuthToken, cookie);
await TemplateDeleter.deleteTemplates(null, macroTemplateName, zabbixAuthToken, cookie);
// We don't delete the group here as it might be shared or used by other tests in this run
} catch (error: any) {
success = false;
steps.push({
name: "Execution Error",
success: false,
message: error.message || String(error)
});
}
return {
success,
message: success ? "Regression tests passed successfully" : "Regression tests failed",
steps
};
}
}

View file

@ -0,0 +1,158 @@
import {SmoketestResponse, SmoketestStep} from "../schema/generated/graphql.js";
import {TemplateImporter} from "./template_importer.js";
import {HostImporter} from "./host_importer.js";
import {HostDeleter} from "./host_deleter.js";
import {TemplateDeleter} from "./template_deleter.js";
import {zabbixAPI} from "../datasources/zabbix-api.js";
import {ZabbixQueryHostsGenericRequest} from "../datasources/zabbix-hosts.js";
import {ParsedArgs} from "../datasources/zabbix-request.js";
export class SmoketestExecutor {
public static async runSmoketest(hostName: string, templateName: string, groupName: string, zabbixAuthToken?: string, cookie?: string): Promise<SmoketestResponse> {
const steps: SmoketestStep[] = [];
let success = true;
try {
// Step 0: Create Template Group
const templateGroupResult = await TemplateImporter.importTemplateGroups([{
groupName: groupName
}], zabbixAuthToken, cookie);
const templateGroupSuccess = !!templateGroupResult?.length && !templateGroupResult[0].error;
steps.push({
name: "Create Template Group",
success: templateGroupSuccess,
message: templateGroupSuccess ? `Template group ${groupName} created` : `Failed: ${templateGroupResult?.[0]?.error?.message || "Unknown error"}`
});
if (!templateGroupSuccess) success = false;
// Step 1: Create Template
if (success) {
const templateResult = await TemplateImporter.importTemplates([{
host: templateName,
name: templateName,
groupNames: [groupName]
}], zabbixAuthToken, cookie);
const templateSuccess = !!templateResult?.length && !templateResult[0].error;
steps.push({
name: "Create Template",
success: templateSuccess,
message: templateSuccess ? `Template ${templateName} created` : `Failed: ${templateResult?.[0]?.error?.message || "Unknown error"}`
});
if (!templateSuccess) success = false;
} else {
steps.push({ name: "Create Template", success: false, message: "Skipped due to previous failures" });
}
// Step 2: Create Host Group
const groupResult = await HostImporter.importHostGroups([{
groupName: groupName
}], zabbixAuthToken, cookie);
const groupSuccess = !!groupResult?.length && !groupResult[0].error;
steps.push({
name: "Create Host Group",
success: groupSuccess,
message: groupSuccess ? `Host group ${groupName} created` : `Failed: ${groupResult?.[0]?.error?.message || "Unknown error"}`
});
if (!groupSuccess) success = false;
// Step 3: Create Host and Link to Template
if (success) {
const hostResult = await HostImporter.importHosts([{
deviceKey: hostName,
deviceType: "ZabbixHost",
groupNames: [groupName],
templateNames: [templateName]
}], zabbixAuthToken, cookie);
const hostSuccess = !!hostResult?.length && !hostResult[0].error;
steps.push({
name: "Create and Link Host",
success: hostSuccess,
message: hostSuccess ? `Host ${hostName} created and linked to ${templateName}` : `Failed: ${hostResult?.[0]?.error?.message || "Unknown error"}`
});
if (!hostSuccess) success = false;
} else {
steps.push({ name: "Create and Link Host", success: false, message: "Skipped due to previous failures" });
}
// Step 4: Verify Host Linkage
if (success) {
const verifyResult = await new ZabbixQueryHostsGenericRequest("host.get", zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs({
filter_host: hostName,
selectParentTemplates: ["name"]
}));
let verified = false;
if (Array.isArray(verifyResult) && verifyResult.length > 0) {
const host = verifyResult[0] as any;
const linkedTemplates = host.parentTemplates || [];
verified = linkedTemplates.some((t: any) => t.name === templateName);
}
steps.push({
name: "Verify Host Linkage",
success: verified,
message: verified ? `Verification successful: Host ${hostName} is linked to ${templateName}` : `Verification failed: Host or linkage not found`
});
if (!verified) success = false;
} else {
steps.push({ name: "Verify Host Linkage", success: false, message: "Skipped due to previous failures" });
}
} catch (error: any) {
success = false;
steps.push({
name: "Execution Error",
success: false,
message: error.message || String(error)
});
} finally {
// Step 5: Cleanup
const cleanupSteps: SmoketestStep[] = [];
// Delete Host
const deleteHostRes = await HostDeleter.deleteHosts(null, hostName, zabbixAuthToken, cookie);
cleanupSteps.push({
name: "Cleanup: Delete Host",
success: deleteHostRes.every(r => !r.error),
message: deleteHostRes.length > 0 ? deleteHostRes[0].message : "Host not found for deletion"
});
// Delete Template
const deleteTemplateRes = await TemplateDeleter.deleteTemplates(null, templateName, zabbixAuthToken, cookie);
cleanupSteps.push({
name: "Cleanup: Delete Template",
success: deleteTemplateRes.every(r => !r.error),
message: deleteTemplateRes.length > 0 ? deleteTemplateRes[0].message : "Template not found for deletion"
});
// Delete Host Group
const deleteGroupRes = await HostDeleter.deleteHostGroups(null, groupName, zabbixAuthToken, cookie);
cleanupSteps.push({
name: "Cleanup: Delete Host Group",
success: deleteGroupRes.every(r => !r.error),
message: deleteGroupRes.length > 0 ? deleteGroupRes[0].message : "Host group not found for deletion"
});
// We also need to delete the template group if it's different or just try to delete it
// In our setup, TemplateImporter creates it if it doesn't exist.
const deleteTemplateGroupRes = await TemplateDeleter.deleteTemplateGroups(null, groupName, zabbixAuthToken, cookie);
cleanupSteps.push({
name: "Cleanup: Delete Template Group",
success: deleteTemplateGroupRes.every(r => !r.error),
message: deleteTemplateGroupRes.length > 0 ? deleteTemplateGroupRes[0].message : "Template group not found for deletion"
});
steps.push(...cleanupSteps);
}
return {
success,
message: success ? "Smoketest passed successfully" : "Smoketest failed",
steps
};
}
}

View file

@ -1,9 +1,9 @@
import {DeleteResponse} from "../schema/generated/graphql.js"; import {DeleteResponse} from "../schema/generated/graphql.js";
import { import {
TemplateHelper,
ZabbixDeleteTemplateGroupsRequest, ZabbixDeleteTemplateGroupsRequest,
ZabbixDeleteTemplatesRequest, ZabbixDeleteTemplatesRequest,
ZabbixQueryTemplateGroupRequest, ZabbixQueryTemplateGroupRequest
ZabbixQueryTemplatesRequest
} from "../datasources/zabbix-templates.js"; } from "../datasources/zabbix-templates.js";
import {isZabbixErrorResult, ParsedArgs} from "../datasources/zabbix-request.js"; import {isZabbixErrorResult, ParsedArgs} from "../datasources/zabbix-request.js";
import {zabbixAPI} from "../datasources/zabbix-api.js"; import {zabbixAPI} from "../datasources/zabbix-api.js";
@ -15,11 +15,8 @@ export class TemplateDeleter {
let idsToDelete = templateids ? [...templateids] : []; let idsToDelete = templateids ? [...templateids] : [];
if (name_pattern) { if (name_pattern) {
const queryResult = await new ZabbixQueryTemplatesRequest(zabbixAuthToken, cookie) const foundIds = await TemplateHelper.findTemplateIdsByName([name_pattern], zabbixAPI, zabbixAuthToken, cookie);
.executeRequestReturnError(zabbixAPI, new ParsedArgs({ name_pattern: name_pattern })); if (foundIds) {
if (!isZabbixErrorResult(queryResult) && Array.isArray(queryResult)) {
const foundIds = queryResult.map(t => Number(t.templateid));
// Merge and deduplicate // Merge and deduplicate
idsToDelete = Array.from(new Set([...idsToDelete, ...foundIds])); idsToDelete = Array.from(new Set([...idsToDelete, ...foundIds]));
} }

View file

@ -131,7 +131,8 @@ export class TemplateImporter {
groups: groupids.map(id => ({ groupid: id })), groups: groupids.map(id => ({ groupid: id })),
uuid: template.uuid, uuid: template.uuid,
templates: linkedTemplates, templates: linkedTemplates,
tags: template.tags?.map(t => ({ tag: t.tag, value: t.value || "" })) tags: template.tags?.map(t => ({ tag: t.tag, value: t.value || "" })),
macros: template.macros
} }
let templateImportResult = await new ZabbixCreateTemplateRequest(zabbixAuthToken, cookie) let templateImportResult = await new ZabbixCreateTemplateRequest(zabbixAuthToken, cookie)
@ -176,8 +177,8 @@ export class TemplateImporter {
preprocessing: item.preprocessing?.map(p => ({ preprocessing: item.preprocessing?.map(p => ({
type: p.type, type: p.type,
params: p.params.join("\n"), params: p.params.join("\n"),
error_handler: p.error_handler, error_handler: p.error_handler ?? 0,
error_handler_params: p.error_handler_params error_handler_params: p.error_handler_params ?? ""
})), })),
tags: item.tags?.map(t => ({ tag: t.tag, value: t.value || "" })) tags: item.tags?.map(t => ({ tag: t.tag, value: t.value || "" }))
} }

View file

@ -17,8 +17,8 @@ export enum DeviceCommunicationType {
} }
export enum DeviceStatus { export enum DeviceStatus {
DISABLED = "0", ENABLED = "0",
ENABLED = "1" DISABLED = "1"
} }
export enum StorageItemType { export enum StorageItemType {

Some files were not shown because too many files have changed in this diff Show more