Compare commits
12 commits
40c6bed639
...
41e4c4da1f
| Author | SHA1 | Date | |
|---|---|---|---|
| 41e4c4da1f | |||
| 5da4a17e36 | |||
| b84e4c0734 | |||
| ef7afe65ab | |||
| 67357d0bc3 | |||
| b56255ffaa | |||
| 9a79fc8e4c | |||
| a9940063e9 | |||
| bbf7357e93 | |||
| 91a1523d71 | |||
| a01bfabfba | |||
| 4ec61ffba1 |
113 changed files with 3746 additions and 1079 deletions
7
.ai/mcp/mcp.json
Normal file
7
.ai/mcp/mcp.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zabbix-graphql": {
|
||||||
|
"url": "http://localhost:3000/mcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -130,3 +130,6 @@ dist
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
|
||||||
|
# Generated schema for MCP
|
||||||
|
schema.graphql
|
||||||
|
|
|
||||||
2
.idea/runConfigurations/index_ts.xml
generated
2
.idea/runConfigurations/index_ts.xml
generated
|
|
@ -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
99
.idea/workspace.xml
generated
|
|
@ -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 - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - 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 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.">
|
||||||
<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 - Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. - 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 - 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`.">
|
||||||
|
<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 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.">
|
||||||
|
<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 ".js" 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 - Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class. - Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided. - Added corresponding tests to verify default behavior in host and host group queries. - Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query. - 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 - Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class. - Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided. - Added corresponding tests to verify default behavior in host and host group queries. - Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query. - 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 - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files" />
|
<MESSAGE value="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files" />
|
||||||
<MESSAGE value="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - 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 - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - 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 - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - 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 - Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. - 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" - Moved query files to a centralized `docs/queries/` directory for better organization. - Added example files under `mcp/operations` to support MCP integration. - Introduced a `docker-compose.yml` file for easier local development and deployment of services. - Updated tests to reflect the relocation of query files. - Enhanced IntelliJ `.idea/workspace.xml` settings for project consistency. " />
|
||||||
|
<MESSAGE value="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`." />
|
||||||
|
<MESSAGE value="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." />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="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." />
|
||||||
<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">
|
||||||
|
|
|
||||||
|
|
@ -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
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
24
|
||||||
124
AGENTS.md
124
AGENTS.md
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
14
NOTES.md
14
NOTES.md
|
|
@ -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
829
README.md
|
|
@ -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:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<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.
|
||||||
|
|
|
||||||
|
|
@ -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
40
docker-compose.yml
Normal 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
32
docs/howtos/README.md
Normal 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
580
docs/howtos/cookbook.md
Normal 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}¤t=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).
|
||||||
41
docs/howtos/hierarchical_data_mapping.md
Normal file
41
docs/howtos/hierarchical_data_mapping.md
Normal 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.
|
||||||
65
docs/howtos/maintenance.md
Normal file
65
docs/howtos/maintenance.md
Normal 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
104
docs/howtos/mcp.md
Normal 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."
|
||||||
49
docs/howtos/permissions.md
Normal file
49
docs/howtos/permissions.md
Normal 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
52
docs/howtos/schema.md
Normal 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
36
docs/howtos/tags.md
Normal 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
28
docs/queries/README.md
Normal 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).
|
||||||
27
docs/queries/from_zabbix_docs/allDevices.graphql
Normal file
27
docs/queries/from_zabbix_docs/allDevices.graphql
Normal 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%"
|
||||||
|
}
|
||||||
|
```
|
||||||
18
docs/queries/from_zabbix_docs/allHostGroups.graphql
Normal file
18
docs/queries/from_zabbix_docs/allHostGroups.graphql
Normal 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%"
|
||||||
|
}
|
||||||
|
```
|
||||||
34
docs/queries/from_zabbix_docs/allHosts.graphql
Normal file
34
docs/queries/from_zabbix_docs/allHosts.graphql
Normal 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]
|
||||||
|
}
|
||||||
|
```
|
||||||
18
docs/queries/from_zabbix_docs/allTemplateGroups.graphql
Normal file
18
docs/queries/from_zabbix_docs/allTemplateGroups.graphql
Normal 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%"
|
||||||
|
}
|
||||||
|
```
|
||||||
23
docs/queries/from_zabbix_docs/createHost.graphql
Normal file
23
docs/queries/from_zabbix_docs/createHost.graphql
Normal 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]
|
||||||
|
}
|
||||||
|
```
|
||||||
18
docs/queries/from_zabbix_docs/deleteTemplateGroups.graphql
Normal file
18
docs/queries/from_zabbix_docs/deleteTemplateGroups.graphql
Normal 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]
|
||||||
|
}
|
||||||
|
```
|
||||||
18
docs/queries/from_zabbix_docs/deleteTemplates.graphql
Normal file
18
docs/queries/from_zabbix_docs/deleteTemplates.graphql
Normal 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]
|
||||||
|
}
|
||||||
|
```
|
||||||
24
docs/queries/from_zabbix_docs/exportHostValueHistory.graphql
Normal file
24
docs/queries/from_zabbix_docs/exportHostValueHistory.graphql
Normal 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
|
||||||
|
}
|
||||||
|
```
|
||||||
28
docs/queries/from_zabbix_docs/exportUserRights.graphql
Normal file
28
docs/queries/from_zabbix_docs/exportUserRights.graphql
Normal 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%"
|
||||||
|
}
|
||||||
|
```
|
||||||
20
docs/queries/from_zabbix_docs/hasPermissions.graphql
Normal file
20
docs/queries/from_zabbix_docs/hasPermissions.graphql
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
26
docs/queries/from_zabbix_docs/importHostGroups.graphql
Normal file
26
docs/queries/from_zabbix_docs/importHostGroups.graphql
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
30
docs/queries/from_zabbix_docs/importHosts.graphql
Normal file
30
docs/queries/from_zabbix_docs/importHosts.graphql
Normal 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"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
23
docs/queries/from_zabbix_docs/importTemplateGroups.graphql
Normal file
23
docs/queries/from_zabbix_docs/importTemplateGroups.graphql
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
32
docs/queries/from_zabbix_docs/importTemplates.graphql
Normal file
32
docs/queries/from_zabbix_docs/importTemplates.graphql
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
45
docs/queries/from_zabbix_docs/importUserRights.graphql
Normal file
45
docs/queries/from_zabbix_docs/importUserRights.graphql
Normal 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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
19
docs/queries/from_zabbix_docs/locations.graphql
Normal file
19
docs/queries/from_zabbix_docs/locations.graphql
Normal 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%"
|
||||||
|
}
|
||||||
|
```
|
||||||
16
docs/queries/from_zabbix_docs/login.graphql
Normal file
16
docs/queries/from_zabbix_docs/login.graphql
Normal 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"
|
||||||
|
}
|
||||||
|
```
|
||||||
13
docs/queries/from_zabbix_docs/logout.graphql
Normal file
13
docs/queries/from_zabbix_docs/logout.graphql
Normal 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
|
||||||
|
{}
|
||||||
|
```
|
||||||
18
docs/queries/from_zabbix_docs/templates.graphql
Normal file
18
docs/queries/from_zabbix_docs/templates.graphql
Normal 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%"
|
||||||
|
}
|
||||||
|
```
|
||||||
18
docs/queries/from_zabbix_docs/userPermissions.graphql
Normal file
18
docs/queries/from_zabbix_docs/userPermissions.graphql
Normal 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"]
|
||||||
|
}
|
||||||
|
```
|
||||||
13
docs/queries/from_zabbix_docs/zabbixVersion.graphql
Normal file
13
docs/queries/from_zabbix_docs/zabbixVersion.graphql
Normal 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
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
86
docs/queries/sample_import_weather_sensor_template.graphql
Normal file
86
docs/queries/sample_import_weather_sensor_template.graphql
Normal 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}¤t=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
138
docs/tests.md
Normal 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
20
mcp-config.yaml
Normal 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
|
||||||
3
mcp/operations/apiVersion.graphql
Normal file
3
mcp/operations/apiVersion.graphql
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
query GetApiVersion {
|
||||||
|
apiVersion
|
||||||
|
}
|
||||||
8
mcp/operations/createHost.graphql
Normal file
8
mcp/operations/createHost.graphql
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
mcp/operations/createVerificationHost.graphql
Normal file
14
mcp/operations/createVerificationHost.graphql
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
mcp/operations/getTemplates.graphql
Normal file
21
mcp/operations/getTemplates.graphql
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
mcp/operations/importHostGroups.graphql
Normal file
7
mcp/operations/importHostGroups.graphql
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
mutation ImportHostGroups($hostGroups: [CreateHostGroup!]!) {
|
||||||
|
importHostGroups(hostGroups: $hostGroups) {
|
||||||
|
groupName
|
||||||
|
groupid
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
12
mcp/operations/importHosts.graphql
Normal file
12
mcp/operations/importHosts.graphql
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
mcp/operations/importTemplates.graphql
Normal file
12
mcp/operations/importTemplates.graphql
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
mcp/operations/runAllRegressionTests.graphql
Normal file
13
mcp/operations/runAllRegressionTests.graphql
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
mcp/operations/runSmoketest.graphql
Normal file
14
mcp/operations/runSmoketest.graphql
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
mcp/operations/verifySchemaExtension.graphql
Normal file
13
mcp/operations/verifySchemaExtension.graphql
Normal 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
19
package-lock.json
generated
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
11
package.json
11
package.json
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
23
roadmap.md
Normal 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.
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
schema/extensions/weather_sensor.graphql
Normal file
50
schema/extensions/weather_sensor.graphql
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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!]
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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})\/`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
110
src/execution/host_deleter.ts
Normal file
110
src/execution/host_deleter.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
237
src/execution/regression_test_executor.ts
Normal file
237
src/execution/regression_test_executor.ts
Normal 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¤t=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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
158
src/execution/smoketest_executor.ts
Normal file
158
src/execution/smoketest_executor.ts
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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]));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 || "" }))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue