From 460c2ed90a24d929dc89c6337d6c0e4bb7494883 Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 14:15:28 +0100 Subject: [PATCH 01/21] feat: preparation for README recreation --- AGENTS.md | 124 +++++++++++++++++++++++++++++++++++++ NOTES.md | 14 +++++ README.md => README_old.md | 4 +- 3 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 AGENTS.md create mode 100644 NOTES.md rename README.md => README_old.md (99%) diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..916a811 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,124 @@ +# 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` +- 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 \ No newline at end of file diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..1464404 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,14 @@ +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 diff --git a/README.md b/README_old.md similarity index 99% rename from README.md rename to README_old.md index 71b50a7..ea8e536 100644 --- a/README.md +++ b/README_old.md @@ -77,7 +77,7 @@ npm run prod The API will be available at `http://localhost:4000/`. -## Running with Docker +#### Running with Docker ### Using the Pre-built Image @@ -98,7 +98,7 @@ docker run -d \ forgejo.tooling.hilbigit.com/zabbix/zabbix-graphql-api:latest ``` -### Building Locally +#### Building Locally If you prefer to build the image yourself using the provided `Dockerfile`: From 85a926e5bbb7c0134c32ee46df70618bbd9eb67c Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 15:01:28 +0100 Subject: [PATCH 02/21] fix: README enhancement --- README.md | 546 ++++++++++++++++++++++++++++++++++++++++++++++++++ README_old.md | 300 --------------------------- 2 files changed, 546 insertions(+), 300 deletions(-) create mode 100644 README.md delete mode 100644 README_old.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..2bb2500 --- /dev/null +++ b/README.md @@ -0,0 +1,546 @@ +# 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. + +## 🚀 Features + +- **GraphQL Interface**: Modern GraphQL API wrapping Zabbix functionality + - *Reference*: `schema/queries.graphql`, `schema/mutations.graphql`, `src/api/start.ts` + +- **Hierarchical Data Mapping**: Automatic mapping of Zabbix items/tags to nested GraphQL objects + - *Reference*: `src/api/resolver_helpers.ts`, `schema/device_value_commons.graphql`, `docs/sample_all_devices_query.graphql` + +- **Mass Operations**: Import/export capabilities for hosts, templates, and user rights + - *Reference*: `schema/mutations.graphql` (importHosts, importTemplates, importUserRights, etc.), `docs/sample_import_*.graphql` + +- **Dynamic Schema Extension**: Extend the schema without code changes using environment variables + - *Reference*: `src/api/schema.ts`, `schema/extensions/`, `src/common_utils.ts` (ADDITIONAL_SCHEMAS, ADDITIONAL_RESOLVERS) + +- **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` + +- **Real-time Subscriptions**: WebSocket support for GraphQL subscriptions (infrastructure implemented) + - *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 + - *Reference*: `codegen.ts`, `src/schema/generated/graphql.ts`, `tsconfig.json`, `package.json` (devDependencies for GraphQL Codegen) + +## 📋 Prerequisites + +Before you begin, ensure you have met the following requirements: + +- **Node.js** >= 18.x (tested with v21.5.0) +- **NPM** >= 8.x +- **Zabbix Server** with API access +- **Zabbix Super Admin Token** (for full functionality) + +## 🛠️ Installation + +### Clone the repository + +```bash +git clone https://github.com/your-repo/zabbix-graphql-api.git +cd zabbix-graphql-api +``` + +### Install dependencies + +```bash +npm install +``` + +## ⚙️ Configuration + +### Environment Variables + +Create a `.env` file in the project root with the following variables: + +```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 | +|----------|-------------|---------| +| `ZABBIX_BASE_URL` | URL to your Zabbix server (include `/zabbix` path) | - | +| `ZABBIX_AUTH_TOKEN` | Zabbix Super Admin API token | - | +| `ZABBIX_AUTH_TOKEN_FOR_REQUESTS` | Token used for automated requests | - | +| `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base group for edge devices | - | +| `ZABBIX_ROADWORK_BASE_GROUP` | Base group for roadwork devices | - | +| `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for permission template groups | `Permissions` | +| `SCHEMA_PATH` | Path to schema files | `./schema/` | +| `ADDITIONAL_SCHEMAS` | Comma-separated list of additional schema files | - | +| `ADDITIONAL_RESOLVERS` | Comma-separated list of resolver types to generate | - | +| `LOG_LEVELS` | Log level configuration | - | + +## ▶️ Running the Application + +### Development Mode + +```bash +# Start with hot reloading +npm run start +``` + +The GraphQL API will be available at `http://localhost:4000/` + +### 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. + +### 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 `docs/` directory: +- `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: + +#### 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 + +## 📄 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 + +Current API version: ${API_VERSION:-"Not set"} \ No newline at end of file diff --git a/README_old.md b/README_old.md deleted file mode 100644 index ea8e536..0000000 --- a/README_old.md +++ /dev/null @@ -1,300 +0,0 @@ -# Zabbix GraphQL API - -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. - -## Key Features & Enhancements - -Compared to the original Zabbix API, this GraphQL API provides several key enhancements: - -* **Mass Import/Export**: Robust support for importing and exporting templates, template groups, hosts, and host groups in bulk. -* **Hierarchical Host Groups**: Automatically handles the creation and resolution of nested host group hierarchies (e.g., `Parent/Child/Leaf`). -* **Template Management**: - * Full support for template items, including complex preprocessing steps and tags. - * **Dependent Item Support**: Intelligent deferred creation logic to handle item dependencies within a template. - * Linked template resolution by name. -* **Advanced Deletion**: Ability to delete templates and template groups not only by ID but also by **name patterns** (supporting Zabbix wildcards like `%`). -* **User Rights & Permissions**: - * Integrated management of user roles and user groups. - * Support for importing/exporting user rights with UUID-based matching for cross-instance consistency. - * On-the-fly permission checks (`hasPermissions`, `userPermissions`). -* **Improved Error Reporting**: Detailed error data from Zabbix is appended to GraphQL error messages, making debugging significantly easier. -* **Strongly Typed Schema**: Leverages GraphQL's type system for clear API documentation and client-side code generation. -* **Dynamic Schema Extensibility**: Easily extend the API with custom schema snippets and dynamic resolvers for specialized device types without modifying the core code. -* **CI/CD Integration**: Includes a ready-to-use Forgejo/Gitea/GitHub Actions workflow for automated building, testing, and deployment. -* **Sample Application (VCR)**: Designed to power the **Virtual Control Room**, a professional cockpit for managing thousands of IoT/Edge devices. - -## How to Install and Start - -### Prerequisites - -* **Node.js**: Version 18 or higher recommended. -* **Zabbix**: A running Zabbix instance (compatible with Zabbix 6.0+). - -### Installation - -1. Clone the repository: - ```bash - git clone - cd zabbix-graphql-api - ``` - -2. Install dependencies: - ```bash - npm install - ``` - -### Configuration - -The API is configured via environment variables. Create a `.env` file or set them in your environment: - -| Variable | Description | Default | -| :--- | :--- | :--- | -| `ZABBIX_BASE_URL` | URL to your Zabbix API (e.g., `http://zabbix.example.com/zabbix`) | | -| `ZABBIX_AUTH_TOKEN` | Zabbix Super Admin API token for administrative tasks | | -| `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base host group for devices | `Roadwork` | -| `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for template groups used as permissions | `Permissions` | -| `SCHEMA_PATH` | Path to the directory containing `.graphql` schema files | `./schema/` | -| `HOST_GROUP_FILTER_DEFAULT` | Default search pattern for `allHostGroups` query | | -| `HOST_TYPE_FILTER_DEFAULT` | Default value for `tag_hostType` filter in `allHosts` and `allDevices` queries | | - -### 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/`. - -#### Running with Docker - -### Using the Pre-built Image - -You can run the API without building it locally by pulling the latest image from the Hilbig IT Forgejo infrastructure: - -```bash -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_AUTH_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`: - -1. Build the image (ensure you provide an `API_VERSION`): - ```bash - docker build -t zabbix-graphql-api --build-arg API_VERSION=1.0.0 . - ``` - -2. 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_AUTH_TOKEN=your-super-admin-token \ - zabbix-graphql-api - ``` - -## Extending the Schema - -The Zabbix GraphQL API is designed to be highly extensible. You can add your own GraphQL schema snippets and have resolvers dynamically created for them. - -### Dynamic Resolvers with `createHierarchicalValueFieldResolver` - -The function `createHierarchicalValueFieldResolver` (found in `src/api/resolver_helpers.ts`) allows for the automatic creation of resolvers that map Zabbix items or tags to a hierarchical GraphQL structure. It uses field names and Zabbix item keys (dot-separated) to automatically resolve nested objects. - -### Zabbix Preconditions for Hierarchical Mapping - -In order for the dynamic resolvers to correctly map Zabbix data to your GraphQL schema, the following preconditions must be met in your Zabbix templates: - -* **Key Naming**: Zabbix item keys (or tags) must match the GraphQL field names. -* **Dot Separation**: Use a dot (`.`) as a separator to represent nested object structures. For example, a Zabbix item with the key `state.current.values.temperature` will be automatically mapped to the `temperature` field within the nested structure: `state` -> `current` -> `values` -> `temperature`. -* **Type Hinting**: You can guide the type conversion by prepending a type hint and an underscore to the last token of the 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. - -For a complete example of a Zabbix template designed for schema extension, see the [Distance Tracker Import Sample](docs/sample_import_distance_tracker_template.graphql). - -### No-Code Extension via Environment Variables - -You can extend the schema and add resolvers without writing any TypeScript code by using the following environment variables: - -* **`ADDITIONAL_SCHEMAS`**: A comma-separated list of paths to additional `.graphql` files. -* **`ADDITIONAL_RESOLVERS`**: A comma-separated list of GraphQL Type names for which dynamic hierarchical resolvers should be created. - -#### Example - -Suppose you have custom device definitions in `schema/extensions/`. You can load them and enable dynamic resolution by setting: - -```bash -ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql -ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice -``` - -The API will: -1. Load all provided schema files. -2. For each type listed in `ADDITIONAL_RESOLVERS`, it will automatically create a resolver that maps Zabbix items (e.g., an item with key `state.current.values.temperature`) to the corresponding GraphQL fields. - -## User Permissions & `hasPermission` - -The Zabbix GraphQL API provides a sophisticated way to manage and check application-level permissions using Zabbix's built-in user group and template group mechanisms. - -### Modeling Permissions with Template Groups - -Permissions can be modeled as **empty template groups** (groups with no templates or hosts attached) organized in a hierarchical structure. By convention, these groups start with a configurable prefix (default: `Permissions/`). - -#### Example Hierarchy: -* `Permissions/ConstructionSite`: General access to construction site data. -* `Permissions/Automatism`: Access to automation features. -* `Permissions/Automatism/Status`: Permission to view automation status. - -### Zabbix Preconditions - -1. **Template Groups**: Create template groups for each permission you want to manage (e.g., `Permissions/App1/FeatureA`). -2. **User Groups**: In Zabbix, assign these template groups to Zabbix User Groups with specific permission levels (`READ`, `READ_WRITE`, or `DENY`). -3. **Authentication**: The GraphQL API will check the permissions of the authenticated user (via token or session cookie) against these Zabbix assignments. - -### Using `hasPermission` and `userPermissions` - -The API provides two main queries for permission checking: - -* **`userPermissions`**: Returns a list of all permissions assigned to the current user. -* **`hasPermissions`**: Checks if the user has a specific set of required permissions (e.g., "Does the user have `READ_WRITE` access to `Automatism/Status`?"). - -This allows for fine-grained access control in your frontend or external applications, using Zabbix as the central authorization authority. - -For a complete example of how to import these permission groups, see the [Permissions Template Groups Import Sample](docs/sample_import_permissions_template_groups_mutation.graphql). - -## Host Classification & Filtering - -The API leverages Zabbix tags to classify hosts and devices, enabling efficient filtering and multi-tenancy support. - -### 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. - -#### How to set the Host Type in Zabbix: - -To classify a host or a template, simply add a tag in the Zabbix UI or via the API: -* **Tag Name**: `hostType` -* **Tag Value**: A string representing the category (e.g., `Roadwork/Devices`, `SmartCity/Sensors`). - -This tag can be defined: -1. **Directly on the Host**: Specific to that individual device. -2. **On a Template**: All hosts linked to this template will inherit the classification. - -### 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, a query like `allHosts { host }` will only return hosts that have the `hostType` tag set to `Roadwork/Devices`. -* This default can always be overridden in the GraphQL query by explicitly 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. This is particularly useful for restricting the visible host group hierarchy to a specific subtree by default. - -#### Overriding the Default Filter - -The default filter can be overridden by explicitly providing the `search_name` argument in the `allHostGroups` query. When `search_name` is present, the environment variable is ignored. - -#### Using Wildcards - -The `search_name` parameter supports the `*` wildcard (enabled via the Zabbix API's `searchWildcardsEnabled` feature). This allows you to search for all subgroups within a specific path. - -**Example**: To find all subgroups of `Roadwork/Devices/`, use the following query: - -```graphql -query { - allHostGroups(search_name: "Roadwork/Devices/*") { - groupid - name - } -} -``` - -## 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_AUTH_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 - -# Logging -# LOG_LEVEL=debug -``` - -## Usage Samples - -The `docs` directory contains several sample GraphQL queries and mutations to help you get started: - -* **Hosts**: - * [Query All Hosts](docs/sample_all_hosts_query.graphql) - * [Import Hosts](docs/sample_import_hosts_mutation.graphql) -* **Templates**: - * [Query Templates](docs/sample_templates_query.graphql) - * [Import Templates](docs/sample_import_templates_mutation.graphql) - * [Import Distance Tracker Template](docs/sample_import_distance_tracker_template.graphql) (Schema Extension Example) - * [Delete Templates](docs/sample_delete_templates_mutation.graphql) -* **Template Groups**: - * [Import Host Template Groups](docs/sample_import_host_template_groups_mutation.graphql) - * [Import Permissions Template Groups](docs/sample_import_permissions_template_groups_mutation.graphql) - * [Delete Template Groups](docs/sample_delete_template_groups_mutation.graphql) -* **User Rights**: - * [Export User Rights](docs/sample_export_user_rights_query.graphql) - * [Import User Rights](docs/sample_import_user_rights_mutation.graphql) - -## License - -This project is licensed under the **GNU Affero General Public License v3.0**. See the [LICENSE](LICENSE) file for details. From 944ae2bfbb1e337a264bbac264da9cacfea89235 Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 15:37:34 +0100 Subject: [PATCH 03/21] fix: README add initial query and mutations desciptions. --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 2bb2500..5f56c91 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,34 @@ npm run start The GraphQL API will be available at `http://localhost:4000/` +### Available Queries and Mutations + +#### 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 From 8eb2d2d58731278b4242c0eeca54dd2d24b314be Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 15:45:08 +0100 Subject: [PATCH 04/21] fix: API_VERSION description --- README.md | 6 +- readme.evaluation.result.md | 74 +++++++++++++++++++++++ readme.improvement.plan.md | 117 ++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 readme.evaluation.result.md create mode 100644 readme.improvement.plan.md diff --git a/README.md b/README.md index 5f56c91..ff766ee 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ npm run start The GraphQL API will be available at `http://localhost:4000/` -### Available Queries and Mutations +#### Available Queries and Mutations after start #### Queries - `apiVersion` - Returns the API build version @@ -571,4 +571,6 @@ For support, please open an issue in the GitHub repository. ## 🔄 API Version -Current API version: ${API_VERSION:-"Not set"} \ No newline at end of file +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. 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. \ No newline at end of file diff --git a/readme.evaluation.result.md b/readme.evaluation.result.md new file mode 100644 index 0000000..5783506 --- /dev/null +++ b/readme.evaluation.result.md @@ -0,0 +1,74 @@ +# 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 \ No newline at end of file diff --git a/readme.improvement.plan.md b/readme.improvement.plan.md new file mode 100644 index 0000000..c293276 --- /dev/null +++ b/readme.improvement.plan.md @@ -0,0 +1,117 @@ +# README Improvement Plan + +## Priority 1: Critical Missing Information + +### 1.1 Environment Variables +- Add documentation for `DRY_RUN` environment variable and its purpose +- Document `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` configuration options +- Explain the difference between `ZABBIX_AUTH_TOKEN` and `ZABBIX_AUTH_TOKEN_FOR_REQUESTS` +- Add information about `FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX` regex pattern + +### 1.2 API Version Information +- Fix the API version placeholder to properly display the current version +- Add information about version compatibility between the API and Zabbix server. + - Tested with Zabbix. 7.4 + +### 1.3 Subscription Clarification +- Either implement actual subscription operations in the schema or clarify that only infrastructure is provided +- If only infrastructure is provided, update the feature description to be more accurate + +## Priority 2: Enhance Existing Sections + +### 2.1 Architecture Section +- Add an architecture diagram or detailed explanation based on AGENTS.md +- Include information about the data flow between components +- Explain the role of each DataSource file in the datasources directory + +### 2.2 Configuration Section +- Add a complete table of all environment variables with descriptions +- Include default values and required/optional status +- Add examples of production vs development configurations + +### 2.3 Schema Relationship Section +- Create a mapping table between Zabbix entities and GraphQL types +- Explain how Zabbix host groups, templates, and items relate to GraphQL objects +- Add information about the Location type and its usage + +## Priority 3: Add New Sections + +### 3.1 Code Generation Section +- Explain the GraphQL Codegen setup and how to regenerate types +- Document the `codegen.ts` configuration +- Add instructions for updating generated types after schema changes + +### 3.2 Troubleshooting Section +- Common connection issues with Zabbix server +- Authentication problems and solutions +- Schema extension troubleshooting +- Performance issues and optimizations + +### 3.3 Security Best Practices +- Recommended authentication approaches +- Permission system best practices +- Network security considerations +- API rate limiting and protection + +### 3.4 Performance and Scaling +- Performance considerations for large Zabbix installations +- Caching strategies +- Connection pooling recommendations +- Monitoring and observability setup + +### 3.5 Backup and Recovery +- Configuration backup procedures +- Schema migration guidelines +- Disaster recovery procedures + +## Priority 4: Improve Examples + +### 4.1 Complete Examples +- Add more complete examples for each major operation +- Include error handling examples +- Add examples for common use cases beyond the distance tracker + +### 4.2 Testing Examples +- Add information about how to run tests +- Include examples of unit and integration tests +- Explain the test structure and how to add new tests + +## Priority 5: Documentation Links + +### 5.1 Cross-Reference Improvements +- Add links to relevant sections in schema files +- Include references to specific resolver implementations +- Link to related documentation files in the docs directory + +### 5.2 External Resources +- Link to official Zabbix API documentation +- Include references to Apollo Server documentation +- Add links to GraphQL best practices + +## Priority 6: Maintenance Items + +### 6.1 Update Placeholder Values +- Replace all "your-" placeholder values with more descriptive examples +- Add realistic example values for configuration parameters +- Include sample output where appropriate + +### 6.2 Version Compatibility Matrix +- Create a matrix showing compatibility between API versions and Zabbix versions +- Include Node.js version compatibility information +- Add information about breaking changes between versions + +## Implementation Order +1. Address Priority 1 items first (critical missing information) +2. Update existing sections to be more accurate +3. Add new sections incrementally +4. Enhance examples with more practical use cases +5. Add documentation links and cross-references +6. Perform final review and testing of all examples + +## Success Metrics +- All environment variables documented +- Accurate representation of features +- Complete working examples +- Clear architecture and configuration guidance +- Comprehensive troubleshooting information +- Proper cross-references to codebase \ No newline at end of file From e864d43981672f89f8287d4e163ee65a30d325d3 Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 15:51:45 +0100 Subject: [PATCH 05/21] fix: api version and env variables --- README.md | 25 +++++++++++++++++++++---- readme.improvement.plan.md | 13 +------------ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ff766ee..e7c00ec 100644 --- a/README.md +++ b/README.md @@ -80,15 +80,28 @@ LOG_LEVELS=info | Variable | Description | Default | |----------|-------------|---------| | `ZABBIX_BASE_URL` | URL to your Zabbix server (include `/zabbix` path) | - | -| `ZABBIX_AUTH_TOKEN` | Zabbix Super Admin API token | - | -| `ZABBIX_AUTH_TOKEN_FOR_REQUESTS` | Token used for automated requests | - | +| `ZABBIX_AUTH_TOKEN` | Zabbix Super Admin API token for administrative operations (full permissions needed for import/export operations) | - | +| `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) | - | | `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base group for edge devices | - | | `ZABBIX_ROADWORK_BASE_GROUP` | Base group for roadwork devices | - | -| `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for permission template groups | `Permissions` | +| `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for permission template groups (used to identify permission-related template groups in Zabbix) | `Permissions` | | `SCHEMA_PATH` | Path to schema files | `./schema/` | | `ADDITIONAL_SCHEMAS` | Comma-separated list of additional schema files | - | | `ADDITIONAL_RESOLVERS` | Comma-separated list of resolver types to generate | - | | `LOG_LEVELS` | Log level configuration | - | +| `DRY_RUN` | If set, runs in dry run mode without starting the server (exits immediately after initialization, useful for testing configuration) | - | +| `HOST_TYPE_FILTER_DEFAULT` | Default filter for host types | - | +| `HOST_GROUP_FILTER_DEFAULT` | Default filter for host groups | - | + +### Authentication Tokens Explanation + +The API supports two different authentication tokens for security separation: +- `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 + +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. ## ▶️ Running the Application @@ -573,4 +586,8 @@ For support, please open an issue in the GitHub repository. 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. 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. \ No newline at end of file +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 + +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. \ No newline at end of file diff --git a/readme.improvement.plan.md b/readme.improvement.plan.md index c293276..0549bd3 100644 --- a/readme.improvement.plan.md +++ b/readme.improvement.plan.md @@ -1,18 +1,7 @@ # README Improvement Plan ## Priority 1: Critical Missing Information - -### 1.1 Environment Variables -- Add documentation for `DRY_RUN` environment variable and its purpose -- Document `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` configuration options -- Explain the difference between `ZABBIX_AUTH_TOKEN` and `ZABBIX_AUTH_TOKEN_FOR_REQUESTS` -- Add information about `FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX` regex pattern - -### 1.2 API Version Information -- Fix the API version placeholder to properly display the current version -- Add information about version compatibility between the API and Zabbix server. - - Tested with Zabbix. 7.4 - + ### 1.3 Subscription Clarification - Either implement actual subscription operations in the schema or clarify that only infrastructure is provided - If only infrastructure is provided, update the feature description to be more accurate From fa1844ca54cb0a3989a97ccf972ec36c926ff5d2 Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 15:54:45 +0100 Subject: [PATCH 06/21] fix: subscriptions clarification --- README.md | 2 +- readme.improvement.plan.md | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index e7c00ec..14e8a3e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A GraphQL wrapper for the Zabbix API that provides enhanced mass import/export o - **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` -- **Real-time Subscriptions**: WebSocket support for GraphQL subscriptions (infrastructure implemented) +- **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 diff --git a/readme.improvement.plan.md b/readme.improvement.plan.md index 0549bd3..dea7054 100644 --- a/readme.improvement.plan.md +++ b/readme.improvement.plan.md @@ -2,16 +2,9 @@ ## Priority 1: Critical Missing Information -### 1.3 Subscription Clarification -- Either implement actual subscription operations in the schema or clarify that only infrastructure is provided -- If only infrastructure is provided, update the feature description to be more accurate ## Priority 2: Enhance Existing Sections -### 2.1 Architecture Section -- Add an architecture diagram or detailed explanation based on AGENTS.md -- Include information about the data flow between components -- Explain the role of each DataSource file in the datasources directory ### 2.2 Configuration Section - Add a complete table of all environment variables with descriptions From e5c1134eab7459d998b671973592c537767ea016 Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 16:10:49 +0100 Subject: [PATCH 07/21] fix: arch diagram --- README.md | 88 ++++++++++++++++++++++++++++++++++++++++++ docs/arch-diagram.png | Bin 0 -> 20036 bytes docs/arch-diagram.svg | 1 + 3 files changed, 89 insertions(+) create mode 100644 docs/arch-diagram.png create mode 100644 docs/arch-diagram.svg diff --git a/README.md b/README.md index 14e8a3e..b3ecb57 100644 --- a/README.md +++ b/README.md @@ -543,6 +543,94 @@ Based on the Zabbix configuration above, the API will return: 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request +## 🏗️ Architecture + +The Zabbix GraphQL API follows a layered architecture pattern with clear separation of concerns: + +![Architecture Diagram](docs/arch-diagram.svg) + +
+ +```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 +``` +
+ +### 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` 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. diff --git a/docs/arch-diagram.png b/docs/arch-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..66bca4b971874807e97c4ea7d2717d365e1f91f4 GIT binary patch literal 20036 zcmb5VV|1lK(>5Ag6HF$yHL>l<#C9^VZQHhO+qN;WCbsRIy`SfO*Ezq=`o3R#t=^65 ztE;-MyQ(@&PDUIN4jT>x1O!o1LPP-s1QHPh1hfeT9MEDINt_G#(UbeFBntS2gM&ju zL;LaL2O%LL4Gj%DJ3Bu=zqq)#l$4Z$f`W#IhLMqxjg5`1t*w`rS5Qz;XlQ6sQc^}n zMnOSASy@?KU0ru~_wexW%*@Q(+}zsQ+W!9j#l^+V&CS=>SI}zfAz%$^M^QCL0~=d6 zOCw`P5OE`GBYS;EBSXSJZiJ?ej9E4Y|iWcquNA>rdj)bUl$UM!OriaTw|Li{cCF=d-Nlh}@0z zZkx3y9gl+m+q^Os2n?2;jMSM$Y@4i7Rat{`*~_{BMdd{eFG`JVIS4-zs~(VXI+oe+ z->%|2^2KC{A8Y1TcE2tYHYRa5Q@7bT>c#{2AYKavIFo3~A)>3)L~L6b^A%7DM(L8& zjai7~_!3&uL*6GhsO&L@M8-qbm)9uC0MW@FoDB!rNMj-t;}$BnI~ znHA_;2c%X0yu+c@Z-x!*+iea9bLJZG$Rp;Cpu1*@}$`d?F`y>3P&9wS2$AS>R~) zF`@*$`vV?QD^7hd2gzh}%j~LiJwp+ti{LjntXU7K`vC}u)P$snppxs_MHj5WF2~$k*jU0}R_XE|a3(LGQ=PS0*O7xFb zj9k*CrQE0R9_l5rZ*-gbN@J1rJnUzh2-p4_pSiYSc~?&K21jWyd-z*&r)#CjTQWY2 zbI&*)x8o4QLDKn)?}p!me6PLMn z!4^UH>YBoe`e+#Ta1K>V`b$d*{EJ(`S}Q3J%cBpM{a!Bi7A1@Q;)d*3|E6t$@7u6| zD)_Pi1XABAcvW4|7rmhkqT+jm3(h2LaC}`;c$~P!Lc{7Kl$Aw2=PUfSt%8Y;q#St|NdLO13R`^r#H$dNvkE&UFH4&Qn6%J0?D)vZ;9an|Vg zD!&VnVGr^LP3KwtPEpX*e4A|@bnIJ)mE9#%D^78=X(z4nY`VvI_iNBvU`C6yf_a^t zzh#sBRlSXYT16JXTY~(vB?<~bO$vN}NszTuKN-!`)=7_cBx5jxGXUhkhQl z@-ugB@PybG)%w?mqljQ!oc$xHNfnF;rA)V2%>R1!gJDZ4oOK^5QYk15Y!0;%-0;Ja z36wnl*gs}j5}MTN=%m=}Pp}P{A&Zsr@46<)XBj^8VkOj?QDcZIG=Gg_kjhm6DkYvl zBJVS(tmm|rIQR8m>7$SUTTl*0;t>N^4e5(8Oq-Jtf0mmg@OlVGLlSs|<~Jb93pSd` zAT6PO!48LpBb`#`!luk*5u*qw7C!Z;-_O0aLUan>5bA-Gyi1Dzd zMX#}B{p^jL#Rp|^zzbEXUMZM@!;?q+vJ$9OGIE=iL%af08o&|NI{gCzYekPi;)H*N z_(=&bx7bfJJ*%qoT^Z_LRDlz>ZT~iBQZEO=P*< zi>>-3CzR8ywA|0Fb6`kuWY=J!%N&bGE$jReqF*d z6y^u~+JB8v=?0I&cFL-qEpW=Vm~=Fit=tM-mEXb4w{XQo@$^5wK!dVpgF+NC+FShY zsnS|RSFzKg>a$@_2tQeN|B444{z7<#O?mY^yHO_+9`kS<&9*OH>ouYg#9`Vld>!Kr zJ)2o8Ie%C|H`-yb;!GIF?f3GzheLvxf9~U>YoR)FSs6G#(N+bQ(_KmOzY(%t+Yci(2di&ap2eh<;&&ova7d7$NkpaOWW zM1L$(+Z7UV{`9DNoQMtR(qIYB0W+(lFL%{% zdJVVzUij0_C;lY&4y5Nh;;{tChkA*@js#X4LTT{mi#`#-6Vqo*55lf?_lnw@pK1=M z(-A7Oh#wTF*F4xP56IY~&|%`H<9;;2+fIYPgAaef55&T*qTP9;?tFoSCZfmPm*%QT z=IKfK?FDT|nIsD4pfafaO30NdQ+1Y&H`>jmY;~9;qDZ3%pcs zJ(rtrSaKO&9HprijU>bKmAV~YDYoxHupcQrsg|bLG)y^>J*^*sU?5ca`#*Vwhm|kP z{SrDGi7I|3ixsEY;O57a8Gg%?Hx#q{NE14Hq|vXWO|bpbQ03W}57w6smvD$$16@vp&`fkC;eFfGBe!nML29+)fWsF)8<;TBI?D>e7O?DJqeLQH$4sK~ zjzwTn*x{{SkV5ofQZuTCTeLiJaW(-I-|BN#2oTz~l!Tz~iijwno$u%sN_ccEOiL)=oVDpZ zITKg33|{j7dmQPO&R49Wzq#vCpz}WJ5*Pac>h~~mw6>H{GI>R1o`X2NZHbp?Slkg< zR_Ts-qrIyEp5+^`OA>QeMS^Nmyo~6k#RAk*QH^4$D?y&r3A_;-<^H z7+EJ?4Q7pvt@LlVytC|`%wQwUuurg8im>p75#lb#1SFb$5xdcPTCl>>HU<3XeLd(3 z6mdShdJstd-8)^|Yt1F6T(5!|KbP(B!f1%Sr8n41gP(kDxSK1_Qm6Hhv-h@ZU#0n<_c`) z9WZ@dhwvJ(7}2FqvkTySuZW!S5>fwaSN1Xt5Xzj#V$8hNR|~4zprbsR{bm!jG*~tG z2xoPoC`dt#eA3QRciHK;mjg*IwhrdfF>7`$D)_zzA|}TKd81XvDTiq{tQ=xZ_{)Op zc8~u7(_mZw`1b0b*~mI}Rpe?a0)MRJO|Uf-{t+otq1Hy80>w`_`xmj*7_E*43aVcT zoR2^E0}jDe(tu? z(HD1n6sX*KNoFG^1aogJY77tPcA95)G22`cUwviwTdFZj>E1b5fmWacHoR^8*zkW)bKr1R2Ol9QlNP$QE=FIj~FyQ z-Mw*IXRNq(rUE5D^U|Fi{d{f}%;L*ur@1yN^+mkdcefu_4`-MYZ}%8TxI46- zgyD5}7g<>iwB#r1#F;T-WLDR#-4=DbZFNpF6ie0HZGqqiHS&ez#c+Q~737Od5EO)Rx`E*V3ev={IB^iFx#p5yBHgDY4a|Coa@$B!qD(6t& zE4!`!chW7h%W)h?$x?r8e7;R?I3HBg$2Wc=6zcCEASq?4n4q&^=X#p{2t5BP|#e`zHfT6^|BJ~MjbW7HRVnZVxs zD)bdx4`(n>!4a%hOBh(T+-=x4km1hNcUV^z&e$f{hE?N;d?(? >dQ)<~x9vD5Y$ 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

9zur{ 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 Zc$iyc)gv!T0wdNabhQm{muo%>`w#Di(t7{^ literal 0 HcmV?d00001 diff --git a/docs/arch-diagram.svg b/docs/arch-diagram.svg new file mode 100644 index 0000000..e53b0e1 --- /dev/null +++ b/docs/arch-diagram.svg @@ -0,0 +1 @@ +Zabbix GraphQL API - Simplified Data FlowZabbix GraphQL API - Simplified Data FlowGraphQL ClientApollo Server(Entry Point)Schema LoaderDynamic MergingResolversHierarchicalData SourcesZabbix API WrappersZabbix ServerExecution LayerBusiness LogicQuery/MutationResultResolve SchemaRoute to ResolverFetch DataProcessAPI CallResponseFormatComplex OperationsUse DataSources \ No newline at end of file From b7c56d11f2aea594613ad952001207b75aa87452 Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 16:13:32 +0100 Subject: [PATCH 08/21] fix: additional found env variables added --- README.md | 76 ++++++++++++++++++++++++++++++-------- readme.improvement.plan.md | 11 +----- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index b3ecb57..8214a5c 100644 --- a/README.md +++ b/README.md @@ -77,21 +77,23 @@ LOG_LEVELS=info ### Environment Variable Details -| Variable | Description | Default | -|----------|-------------|---------| -| `ZABBIX_BASE_URL` | URL to your Zabbix server (include `/zabbix` path) | - | -| `ZABBIX_AUTH_TOKEN` | Zabbix Super Admin API token for administrative operations (full permissions needed for import/export operations) | - | -| `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) | - | -| `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base group for edge devices | - | -| `ZABBIX_ROADWORK_BASE_GROUP` | Base group for roadwork devices | - | -| `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for permission template groups (used to identify permission-related template groups in Zabbix) | `Permissions` | -| `SCHEMA_PATH` | Path to schema files | `./schema/` | -| `ADDITIONAL_SCHEMAS` | Comma-separated list of additional schema files | - | -| `ADDITIONAL_RESOLVERS` | Comma-separated list of resolver types to generate | - | -| `LOG_LEVELS` | Log level configuration | - | -| `DRY_RUN` | If set, runs in dry run mode without starting the server (exits immediately after initialization, useful for testing configuration) | - | -| `HOST_TYPE_FILTER_DEFAULT` | Default filter for host types | - | -| `HOST_GROUP_FILTER_DEFAULT` | Default filter for host groups | - | +| Variable | Description | Default | Required | +|----------|-------------|---------|----------| +| `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_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_EDGE_DEVICE_BASE_GROUP` | Base group for edge devices | - | 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 | +| `SCHEMA_PATH` | Path to schema files | `./schema/` | No | +| `ADDITIONAL_SCHEMAS` | Comma-separated list of additional schema files | - | No | +| `ADDITIONAL_RESOLVERS` | Comma-separated list of resolver types to generate | - | No | +| `LOG_LEVELS` | Log level configuration | - | 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_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. ### Authentication Tokens Explanation @@ -103,6 +105,50 @@ The API supports two different authentication tokens for security separation: 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. +### Configuration Examples + +#### Development Configuration (.env.development) +```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_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) +ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql +ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice +``` + +#### Production Configuration (.env.production) +```bash +# 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_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 diff --git a/readme.improvement.plan.md b/readme.improvement.plan.md index dea7054..fb31dc0 100644 --- a/readme.improvement.plan.md +++ b/readme.improvement.plan.md @@ -5,16 +5,7 @@ ## Priority 2: Enhance Existing Sections - -### 2.2 Configuration Section -- Add a complete table of all environment variables with descriptions -- Include default values and required/optional status -- Add examples of production vs development configurations - -### 2.3 Schema Relationship Section -- Create a mapping table between Zabbix entities and GraphQL types -- Explain how Zabbix host groups, templates, and items relate to GraphQL objects -- Add information about the Location type and its usage + ## Priority 3: Add New Sections From 40c6bed639689e296a246b0dd400414674f6180d Mon Sep 17 00:00:00 2001 From: Vladimir Svacko Date: Wed, 28 Jan 2026 16:45:46 +0100 Subject: [PATCH 09/21] feat: README enhancement --- README.md | 31 +++++++++++++++++++++++++++++++ readme.improvement.plan.md | 33 --------------------------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 8214a5c..95671ac 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,35 @@ The GraphQL schema is located in the `schema/` directory and consists of: 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: + +| Zabbix Entity | GraphQL Type | Description | +|---------------|--------------|-------------| +| Host | `Host` / `Device` | Represents a Zabbix host; `Device` is a specialized `Host` with a `deviceType` tag | +| Host Group | `HostGroup` | Represents a Zabbix host group | +| Template | `Template` | Represents a Zabbix template | +| 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 | +| Tag | `Tag` | Represents a Zabbix tag associated with a host or template | +| Inventory | `Location` | Host inventory information maps to location data | + +### 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: @@ -383,6 +412,8 @@ The comments in these files contain real-world usage examples, implementation no ### 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` diff --git a/readme.improvement.plan.md b/readme.improvement.plan.md index fb31dc0..783a3bd 100644 --- a/readme.improvement.plan.md +++ b/readme.improvement.plan.md @@ -1,42 +1,9 @@ -# README Improvement Plan - -## Priority 1: Critical Missing Information - -## Priority 2: Enhance Existing Sections - - - -## Priority 3: Add New Sections - ### 3.1 Code Generation Section - Explain the GraphQL Codegen setup and how to regenerate types - Document the `codegen.ts` configuration - Add instructions for updating generated types after schema changes -### 3.2 Troubleshooting Section -- Common connection issues with Zabbix server -- Authentication problems and solutions -- Schema extension troubleshooting -- Performance issues and optimizations - -### 3.3 Security Best Practices -- Recommended authentication approaches -- Permission system best practices -- Network security considerations -- API rate limiting and protection - -### 3.4 Performance and Scaling -- Performance considerations for large Zabbix installations -- Caching strategies -- Connection pooling recommendations -- Monitoring and observability setup - -### 3.5 Backup and Recovery -- Configuration backup procedures -- Schema migration guidelines -- Disaster recovery procedures - ## Priority 4: Improve Examples ### 4.1 Complete Examples From 4ec61ffba171c75051647cad423fe9780b4ecbe6 Mon Sep 17 00:00:00 2001 From: Andreas Hilbig Date: Fri, 30 Jan 2026 00:47:02 +0100 Subject: [PATCH 10/21] 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`. --- .idea/workspace.xml | 65 ++++++-- .junie/guidelines.md | 8 + README.md | 157 +++--------------- docker-compose.yml | 27 +++ docs/howtos/README.md | 24 +++ docs/howtos/hierarchical_data_mapping.md | 41 +++++ docs/howtos/mcp.md | 65 ++++++++ docs/howtos/permissions.md | 49 ++++++ docs/howtos/schema.md | 62 +++++++ docs/howtos/tags.md | 36 ++++ .../sample_all_devices_query.graphql | 0 .../sample_all_host_groups_query.graphql | 0 .../sample_all_hosts_query.graphql | 0 .../sample_all_template_groups_query.graphql | 0 ...le_delete_template_groups_mutation.graphql | 0 .../sample_delete_templates_mutation.graphql | 0 .../sample_export_user_rights_query.graphql | 0 ...e_import_distance_tracker_template.graphql | 0 ...sample_import_host_groups_mutation.graphql | 0 ...port_host_template_groups_mutation.graphql | 0 .../sample_import_hosts_mutation.graphql | 0 ...rmissions_template_groups_mutation.graphql | 0 .../sample_import_templates_mutation.graphql | 0 ...sample_import_user_rights_mutation.graphql | 0 .../sample_templates_query.graphql | 0 mcp-config.yaml | 20 +++ mcp/operations/apiVersion.graphql | 3 + mcp/operations/createHost.graphql | 6 + src/api/resolvers.ts | 21 +-- src/datasources/zabbix-hostgroups.ts | 2 +- src/test/host_integration.test.ts | 4 +- src/test/template_integration.test.ts | 12 +- src/test/user_rights_integration.test.ts | 2 +- 33 files changed, 439 insertions(+), 165 deletions(-) create mode 100644 docker-compose.yml create mode 100644 docs/howtos/README.md create mode 100644 docs/howtos/hierarchical_data_mapping.md create mode 100644 docs/howtos/mcp.md create mode 100644 docs/howtos/permissions.md create mode 100644 docs/howtos/schema.md create mode 100644 docs/howtos/tags.md rename docs/{ => queries}/sample_all_devices_query.graphql (100%) rename docs/{ => queries}/sample_all_host_groups_query.graphql (100%) rename docs/{ => queries}/sample_all_hosts_query.graphql (100%) rename docs/{ => queries}/sample_all_template_groups_query.graphql (100%) rename docs/{ => queries}/sample_delete_template_groups_mutation.graphql (100%) rename docs/{ => queries}/sample_delete_templates_mutation.graphql (100%) rename docs/{ => queries}/sample_export_user_rights_query.graphql (100%) rename docs/{ => queries}/sample_import_distance_tracker_template.graphql (100%) rename docs/{ => queries}/sample_import_host_groups_mutation.graphql (100%) rename docs/{ => queries}/sample_import_host_template_groups_mutation.graphql (100%) rename docs/{ => queries}/sample_import_hosts_mutation.graphql (100%) rename docs/{ => queries}/sample_import_permissions_template_groups_mutation.graphql (100%) rename docs/{ => queries}/sample_import_templates_mutation.graphql (100%) rename docs/{ => queries}/sample_import_user_rights_mutation.graphql (100%) rename docs/{ => queries}/sample_templates_query.graphql (100%) create mode 100644 mcp-config.yaml create mode 100644 mcp/operations/apiVersion.graphql create mode 100644 mcp/operations/createHost.graphql diff --git a/.idea/workspace.xml b/.idea/workspace.xml index a135d93..2f3b3b9 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,9 +4,30 @@ - @@ -416,7 +460,8 @@ - diff --git a/.junie/guidelines.md b/.junie/guidelines.md index da28a59..820f2d4 100644 --- a/.junie/guidelines.md +++ b/.junie/guidelines.md @@ -2,6 +2,11 @@ This document provides concise information and best practices for developers working on the Zabbix GraphQL API project. +### Environment +- **Operating System:** Windows with WSL + Ubuntu installed. +- **Commands:** Always execute Linux commands (e.g., use `ls` instead of `dir`). + + ## Tech Stack - **Runtime**: Node.js (v18+) - **Language**: TypeScript (ESM) @@ -35,3 +40,6 @@ This document provides concise information and best practices for developers wor - **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. - **Testing**: Write reproduction tests for bugs and cover new features with both unit and integration tests in `src/test/`. + +### Git Standards +- **Commit Messages:** Use [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `style:`). diff --git a/README.md b/README.md index 71b50a7..e6d55af 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,21 @@ Compared to the original Zabbix API, this GraphQL API provides several key enhan * **Strongly Typed Schema**: Leverages GraphQL's type system for clear API documentation and client-side code generation. * **Dynamic Schema Extensibility**: Easily extend the API with custom schema snippets and dynamic resolvers for specialized device types without modifying the core code. * **CI/CD Integration**: Includes a ready-to-use Forgejo/Gitea/GitHub Actions workflow for automated building, testing, and deployment. +* **MCP Integration**: Native support for the **Model Context Protocol (MCP)**, enabled by GraphQL's introspectable schema, allowing LLMs to seamlessly interact with Zabbix data. * **Sample Application (VCR)**: Designed to power the **Virtual Control Room**, a professional cockpit for managing thousands of IoT/Edge devices. +## How-To Guides + +For detailed information on specific topics, please refer to our how-to guides: + +* [**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. +* [**Zabbix Tags Usage**](./docs/howtos/tags.md): Using tags for classification and metadata. +* [**MCP Integration**](./docs/howtos/mcp.md): Connecting LLMs to Zabbix 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 @@ -117,126 +130,6 @@ If you prefer to build the image yourself using the provided `Dockerfile`: zabbix-graphql-api ``` -## Extending the Schema - -The Zabbix GraphQL API is designed to be highly extensible. You can add your own GraphQL schema snippets and have resolvers dynamically created for them. - -### Dynamic Resolvers with `createHierarchicalValueFieldResolver` - -The function `createHierarchicalValueFieldResolver` (found in `src/api/resolver_helpers.ts`) allows for the automatic creation of resolvers that map Zabbix items or tags to a hierarchical GraphQL structure. It uses field names and Zabbix item keys (dot-separated) to automatically resolve nested objects. - -### Zabbix Preconditions for Hierarchical Mapping - -In order for the dynamic resolvers to correctly map Zabbix data to your GraphQL schema, the following preconditions must be met in your Zabbix templates: - -* **Key Naming**: Zabbix item keys (or tags) must match the GraphQL field names. -* **Dot Separation**: Use a dot (`.`) as a separator to represent nested object structures. For example, a Zabbix item with the key `state.current.values.temperature` will be automatically mapped to the `temperature` field within the nested structure: `state` -> `current` -> `values` -> `temperature`. -* **Type Hinting**: You can guide the type conversion by prepending a type hint and an underscore to the last token of the 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. - -For a complete example of a Zabbix template designed for schema extension, see the [Distance Tracker Import Sample](docs/sample_import_distance_tracker_template.graphql). - -### No-Code Extension via Environment Variables - -You can extend the schema and add resolvers without writing any TypeScript code by using the following environment variables: - -* **`ADDITIONAL_SCHEMAS`**: A comma-separated list of paths to additional `.graphql` files. -* **`ADDITIONAL_RESOLVERS`**: A comma-separated list of GraphQL Type names for which dynamic hierarchical resolvers should be created. - -#### Example - -Suppose you have custom device definitions in `schema/extensions/`. You can load them and enable dynamic resolution by setting: - -```bash -ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql -ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice -``` - -The API will: -1. Load all provided schema files. -2. For each type listed in `ADDITIONAL_RESOLVERS`, it will automatically create a resolver that maps Zabbix items (e.g., an item with key `state.current.values.temperature`) to the corresponding GraphQL fields. - -## User Permissions & `hasPermission` - -The Zabbix GraphQL API provides a sophisticated way to manage and check application-level permissions using Zabbix's built-in user group and template group mechanisms. - -### Modeling Permissions with Template Groups - -Permissions can be modeled as **empty template groups** (groups with no templates or hosts attached) organized in a hierarchical structure. By convention, these groups start with a configurable prefix (default: `Permissions/`). - -#### Example Hierarchy: -* `Permissions/ConstructionSite`: General access to construction site data. -* `Permissions/Automatism`: Access to automation features. -* `Permissions/Automatism/Status`: Permission to view automation status. - -### Zabbix Preconditions - -1. **Template Groups**: Create template groups for each permission you want to manage (e.g., `Permissions/App1/FeatureA`). -2. **User Groups**: In Zabbix, assign these template groups to Zabbix User Groups with specific permission levels (`READ`, `READ_WRITE`, or `DENY`). -3. **Authentication**: The GraphQL API will check the permissions of the authenticated user (via token or session cookie) against these Zabbix assignments. - -### Using `hasPermission` and `userPermissions` - -The API provides two main queries for permission checking: - -* **`userPermissions`**: Returns a list of all permissions assigned to the current user. -* **`hasPermissions`**: Checks if the user has a specific set of required permissions (e.g., "Does the user have `READ_WRITE` access to `Automatism/Status`?"). - -This allows for fine-grained access control in your frontend or external applications, using Zabbix as the central authorization authority. - -For a complete example of how to import these permission groups, see the [Permissions Template Groups Import Sample](docs/sample_import_permissions_template_groups_mutation.graphql). - -## Host Classification & Filtering - -The API leverages Zabbix tags to classify hosts and devices, enabling efficient filtering and multi-tenancy support. - -### 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. - -#### How to set the Host Type in Zabbix: - -To classify a host or a template, simply add a tag in the Zabbix UI or via the API: -* **Tag Name**: `hostType` -* **Tag Value**: A string representing the category (e.g., `Roadwork/Devices`, `SmartCity/Sensors`). - -This tag can be defined: -1. **Directly on the Host**: Specific to that individual device. -2. **On a Template**: All hosts linked to this template will inherit the classification. - -### 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, a query like `allHosts { host }` will only return hosts that have the `hostType` tag set to `Roadwork/Devices`. -* This default can always be overridden in the GraphQL query by explicitly 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. This is particularly useful for restricting the visible host group hierarchy to a specific subtree by default. - -#### Overriding the Default Filter - -The default filter can be overridden by explicitly providing the `search_name` argument in the `allHostGroups` query. When `search_name` is present, the environment variable is ignored. - -#### Using Wildcards - -The `search_name` parameter supports the `*` wildcard (enabled via the Zabbix API's `searchWildcardsEnabled` feature). This allows you to search for all subgroups within a specific path. - -**Example**: To find all subgroups of `Roadwork/Devices/`, use the following query: - -```graphql -query { - allHostGroups(search_name: "Roadwork/Devices/*") { - groupid - name - } -} -``` - ## 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. @@ -277,23 +170,23 @@ ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice ## Usage Samples -The `docs` directory contains several sample GraphQL queries and mutations to help you get started: +The `docs/queries` directory contains several sample GraphQL queries and mutations to help you get started: * **Hosts**: - * [Query All Hosts](docs/sample_all_hosts_query.graphql) - * [Import Hosts](docs/sample_import_hosts_mutation.graphql) + * [Query All Hosts](docs/queries/sample_all_hosts_query.graphql) + * [Import Hosts](docs/queries/sample_import_hosts_mutation.graphql) * **Templates**: - * [Query Templates](docs/sample_templates_query.graphql) - * [Import Templates](docs/sample_import_templates_mutation.graphql) - * [Import Distance Tracker Template](docs/sample_import_distance_tracker_template.graphql) (Schema Extension Example) - * [Delete Templates](docs/sample_delete_templates_mutation.graphql) + * [Query Templates](docs/queries/sample_templates_query.graphql) + * [Import Templates](docs/queries/sample_import_templates_mutation.graphql) + * [Import Distance Tracker Template](docs/queries/sample_import_distance_tracker_template.graphql) (Schema Extension Example) + * [Delete Templates](docs/queries/sample_delete_templates_mutation.graphql) * **Template Groups**: - * [Import Host Template Groups](docs/sample_import_host_template_groups_mutation.graphql) - * [Import Permissions Template Groups](docs/sample_import_permissions_template_groups_mutation.graphql) - * [Delete Template Groups](docs/sample_delete_template_groups_mutation.graphql) + * [Import Host Template Groups](docs/queries/sample_import_host_template_groups_mutation.graphql) + * [Import Permissions Template Groups](docs/queries/sample_import_permissions_template_groups_mutation.graphql) + * [Delete Template Groups](docs/queries/sample_delete_template_groups_mutation.graphql) * **User Rights**: - * [Export User Rights](docs/sample_export_user_rights_query.graphql) - * [Import User Rights](docs/sample_import_user_rights_mutation.graphql) + * [Export User Rights](docs/queries/sample_export_user_rights_query.graphql) + * [Import User Rights](docs/queries/sample_import_user_rights_mutation.graphql) ## License diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1b7d32e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +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_AUTH_TOKEN_FOR_REQUESTS=${ZABBIX_AUTH_TOKEN_FOR_REQUESTS} + + apollo-mcp-server: + image: ghcr.io/apollographql/apollo-mcp-server:latest + ports: + - "3000:8000" + volumes: + - ./mcp-config.yaml:/mcp-config.yaml + - ./schema.graphql:/schema.graphql + - ./mcp/operations:/mcp/operations + command: /mcp-config.yaml + environment: + - APOLLO_GRAPH_REF=local@main + depends_on: + - zabbix-graphql-api diff --git a/docs/howtos/README.md b/docs/howtos/README.md new file mode 100644 index 0000000..978d1fb --- /dev/null +++ b/docs/howtos/README.md @@ -0,0 +1,24 @@ +# How-To Guides + +This directory contains detailed guides on how to use and extend the Zabbix GraphQL API. + +## Available Guides + +### 📊 [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. + +### 🏷️ [Zabbix Tags Usage](./tags.md) +Learn how Zabbix tags are used for device classification, host categorization, and as metadata within the GraphQL API. + +### 🤖 [MCP Integration](./mcp.md) +Discover how to integrate the Zabbix GraphQL API with the Model Context Protocol (MCP) to enable LLMs to interact with your Zabbix data. + +--- + +For practical examples of GraphQL operations, check the [Sample Queries](../queries/) directory. diff --git a/docs/howtos/hierarchical_data_mapping.md b/docs/howtos/hierarchical_data_mapping.md new file mode 100644 index 0000000..c722652 --- /dev/null +++ b/docs/howtos/hierarchical_data_mapping.md @@ -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. diff --git a/docs/howtos/mcp.md b/docs/howtos/mcp.md new file mode 100644 index 0000000..6943e5d --- /dev/null +++ b/docs/howtos/mcp.md @@ -0,0 +1,65 @@ +## 🤖 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`. + +1. **Prerequisites**: Ensure you have a `.env` file with the required Zabbix connection details. +2. **Generate Schema**: Generate the combined schema file required by the MCP server: + ```bash + cat schema/*.graphql > schema.graphql + ``` +3. **Prepare Operations**: Create the operations directory if it doesn't exist: + ```bash + mkdir -p mcp/operations + ``` +4. **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 `appflowy.json` or similar depending on OS, but usually `claude_desktop_config.json`): + +```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:/schema.graphql", + "-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. + +### Benefits of GraphQL-enabled MCP + +- **Self-Documenting**: The GraphQL schema provides all necessary metadata for the LLM to understand how to use the tools. +- **Efficient**: LLMs can request exactly the data they need, reducing token usage and improving response speed. +- **Secure**: Uses the same authentication and permission model as the rest of the API. diff --git a/docs/howtos/permissions.md b/docs/howtos/permissions.md new file mode 100644 index 0000000..d521dc0 --- /dev/null +++ b/docs/howtos/permissions.md @@ -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. diff --git a/docs/howtos/schema.md b/docs/howtos/schema.md new file mode 100644 index 0000000..838eb97 --- /dev/null +++ b/docs/howtos/schema.md @@ -0,0 +1,62 @@ +## 📊 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: + +| Zabbix Entity | GraphQL Type | Description | +|---------------|--------------|-------------| +| Host | `Host` / `Device` | Represents a Zabbix host; `Device` is a specialized `Host` with a `deviceType` tag | +| Host Group | `HostGroup` | Represents a Zabbix host group | +| Template | `Template` | Represents a Zabbix template | +| 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 | +| Tag | `Tag` | Represents a Zabbix tag associated with a host or template | +| Inventory | `Location` | Host inventory information maps to location data | + +### 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 diff --git a/docs/howtos/tags.md b/docs/howtos/tags.md new file mode 100644 index 0000000..6b8e9a3 --- /dev/null +++ b/docs/howtos/tags.md @@ -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. diff --git a/docs/sample_all_devices_query.graphql b/docs/queries/sample_all_devices_query.graphql similarity index 100% rename from docs/sample_all_devices_query.graphql rename to docs/queries/sample_all_devices_query.graphql diff --git a/docs/sample_all_host_groups_query.graphql b/docs/queries/sample_all_host_groups_query.graphql similarity index 100% rename from docs/sample_all_host_groups_query.graphql rename to docs/queries/sample_all_host_groups_query.graphql diff --git a/docs/sample_all_hosts_query.graphql b/docs/queries/sample_all_hosts_query.graphql similarity index 100% rename from docs/sample_all_hosts_query.graphql rename to docs/queries/sample_all_hosts_query.graphql diff --git a/docs/sample_all_template_groups_query.graphql b/docs/queries/sample_all_template_groups_query.graphql similarity index 100% rename from docs/sample_all_template_groups_query.graphql rename to docs/queries/sample_all_template_groups_query.graphql diff --git a/docs/sample_delete_template_groups_mutation.graphql b/docs/queries/sample_delete_template_groups_mutation.graphql similarity index 100% rename from docs/sample_delete_template_groups_mutation.graphql rename to docs/queries/sample_delete_template_groups_mutation.graphql diff --git a/docs/sample_delete_templates_mutation.graphql b/docs/queries/sample_delete_templates_mutation.graphql similarity index 100% rename from docs/sample_delete_templates_mutation.graphql rename to docs/queries/sample_delete_templates_mutation.graphql diff --git a/docs/sample_export_user_rights_query.graphql b/docs/queries/sample_export_user_rights_query.graphql similarity index 100% rename from docs/sample_export_user_rights_query.graphql rename to docs/queries/sample_export_user_rights_query.graphql diff --git a/docs/sample_import_distance_tracker_template.graphql b/docs/queries/sample_import_distance_tracker_template.graphql similarity index 100% rename from docs/sample_import_distance_tracker_template.graphql rename to docs/queries/sample_import_distance_tracker_template.graphql diff --git a/docs/sample_import_host_groups_mutation.graphql b/docs/queries/sample_import_host_groups_mutation.graphql similarity index 100% rename from docs/sample_import_host_groups_mutation.graphql rename to docs/queries/sample_import_host_groups_mutation.graphql diff --git a/docs/sample_import_host_template_groups_mutation.graphql b/docs/queries/sample_import_host_template_groups_mutation.graphql similarity index 100% rename from docs/sample_import_host_template_groups_mutation.graphql rename to docs/queries/sample_import_host_template_groups_mutation.graphql diff --git a/docs/sample_import_hosts_mutation.graphql b/docs/queries/sample_import_hosts_mutation.graphql similarity index 100% rename from docs/sample_import_hosts_mutation.graphql rename to docs/queries/sample_import_hosts_mutation.graphql diff --git a/docs/sample_import_permissions_template_groups_mutation.graphql b/docs/queries/sample_import_permissions_template_groups_mutation.graphql similarity index 100% rename from docs/sample_import_permissions_template_groups_mutation.graphql rename to docs/queries/sample_import_permissions_template_groups_mutation.graphql diff --git a/docs/sample_import_templates_mutation.graphql b/docs/queries/sample_import_templates_mutation.graphql similarity index 100% rename from docs/sample_import_templates_mutation.graphql rename to docs/queries/sample_import_templates_mutation.graphql diff --git a/docs/sample_import_user_rights_mutation.graphql b/docs/queries/sample_import_user_rights_mutation.graphql similarity index 100% rename from docs/sample_import_user_rights_mutation.graphql rename to docs/queries/sample_import_user_rights_mutation.graphql diff --git a/docs/sample_templates_query.graphql b/docs/queries/sample_templates_query.graphql similarity index 100% rename from docs/sample_templates_query.graphql rename to docs/queries/sample_templates_query.graphql diff --git a/mcp-config.yaml b/mcp-config.yaml new file mode 100644 index 0000000..389a203 --- /dev/null +++ b/mcp-config.yaml @@ -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: /schema.graphql +introspection: + execute: + enabled: true + introspect: + enabled: true + search: + enabled: true diff --git a/mcp/operations/apiVersion.graphql b/mcp/operations/apiVersion.graphql new file mode 100644 index 0000000..d46a89a --- /dev/null +++ b/mcp/operations/apiVersion.graphql @@ -0,0 +1,3 @@ +query GetApiVersion { + apiVersion +} diff --git a/mcp/operations/createHost.graphql b/mcp/operations/createHost.graphql new file mode 100644 index 0000000..c078a0f --- /dev/null +++ b/mcp/operations/createHost.graphql @@ -0,0 +1,6 @@ +mutation CreateHost($host: String!, $hostgroupids: [Int!]!, $templateids: [Int!]) { + createHost(host: $host, hostgroupids: $hostgroupids, templateids: $templateids) { + hostids + error + } +} diff --git a/src/api/resolvers.ts b/src/api/resolvers.ts index e55c45a..ed83dc2 100644 --- a/src/api/resolvers.ts +++ b/src/api/resolvers.ts @@ -4,14 +4,15 @@ import { DeviceStatus, Host, MutationCreateHostArgs, + MutationDeleteTemplateGroupsArgs, + MutationDeleteTemplatesArgs, MutationImportHostGroupsArgs, MutationImportHostsArgs, MutationImportTemplateGroupsArgs, MutationImportTemplatesArgs, - MutationDeleteTemplatesArgs, - MutationDeleteTemplateGroupsArgs, MutationImportUserRightsArgs, - Permission, QueryAllDevicesArgs, + Permission, + QueryAllDevicesArgs, QueryAllHostGroupsArgs, QueryAllHostsArgs, QueryExportHostValueHistoryArgs, @@ -31,7 +32,8 @@ import {logger} from "../logging/logger.js"; import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js"; import { ZabbixCreateHostRequest, - ZabbixQueryDevices, ZabbixQueryDevicesArgs, + ZabbixQueryDevices, + ZabbixQueryDevicesArgs, ZabbixQueryHostsRequestWithItemsAndInventory, } from "../datasources/zabbix-hosts.js"; import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js"; @@ -46,15 +48,8 @@ import { ZabbixImportUserRolesRequest, ZabbixQueryUserRolesRequest } from "../datasources/zabbix-userroles.js"; -import { - ZabbixCreateItemRequest, - ZabbixCreateTemplateGroupRequest, - ZabbixCreateTemplateRequest, - ZabbixQueryItemRequest, - ZabbixQueryTemplateGroupRequest, - ZabbixQueryTemplatesRequest -} from "../datasources/zabbix-templates.js"; -import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api.js"; +import {ZabbixQueryTemplateGroupRequest, ZabbixQueryTemplatesRequest} from "../datasources/zabbix-templates.js"; +import {zabbixAPI} from "../datasources/zabbix-api.js"; import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js"; import {isDevice} from "./resolver_helpers.js"; import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js"; diff --git a/src/datasources/zabbix-hostgroups.ts b/src/datasources/zabbix-hostgroups.ts index 03d0664..65e9c6d 100644 --- a/src/datasources/zabbix-hostgroups.ts +++ b/src/datasources/zabbix-hostgroups.ts @@ -16,7 +16,7 @@ export interface CreateHostGroupResult { const hostGroupReadWritePermissions = { permissions: [ { - objectName: "Hostgroup/ConstructionSite", + objectName: "Hostgroup", permission: Permission.ReadWrite }] } diff --git a/src/test/host_integration.test.ts b/src/test/host_integration.test.ts index 963df37..b7be340 100644 --- a/src/test/host_integration.test.ts +++ b/src/test/host_integration.test.ts @@ -27,7 +27,7 @@ describe("Host Integration Tests", () => { }); test("Query allHosts using sample", async () => { - const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_all_hosts_query.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_all_hosts_query.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/); const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/); @@ -51,7 +51,7 @@ describe("Host Integration Tests", () => { }); test("Import hosts using sample", async () => { - const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_hosts_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_hosts_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/); const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/); diff --git a/src/test/template_integration.test.ts b/src/test/template_integration.test.ts index 58b2571..22858bb 100644 --- a/src/test/template_integration.test.ts +++ b/src/test/template_integration.test.ts @@ -24,7 +24,7 @@ describe("Template Integration Tests", () => { }); test("Import templates using sample query and variables", async () => { - const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); // Extract mutation and variables from the doc file const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/); @@ -65,7 +65,7 @@ describe("Template Integration Tests", () => { test("Import and Export templates comparison", async () => { // 1. Import - const importSample = readFileSync(join(process.cwd(), 'docs', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const importSample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1]; const importVariables = JSON.parse(importSample.match(/```json\n([\s\S]*?)\n```/)![1]); @@ -84,7 +84,7 @@ describe("Template Integration Tests", () => { }); // 2. Export (Query) - const querySample = readFileSync(join(process.cwd(), 'docs', 'sample_templates_query.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const querySample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_templates_query.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1]; const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]); @@ -137,7 +137,7 @@ describe("Template Integration Tests", () => { test("Import and Export template groups comparison", async () => { // 1. Import (Host Template Groups) - const importSample = readFileSync(join(process.cwd(), 'docs', 'sample_import_host_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const importSample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_host_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1]; const importVariables = JSON.parse(importSample.match(/```json\n([\s\S]*?)\n```/)![1]); @@ -164,7 +164,7 @@ describe("Template Integration Tests", () => { expect(importResult.data.importTemplateGroups).toHaveLength(importVariables.templateGroups.length); // 2. Import (Permissions Template Groups) - const permImportSample = readFileSync(join(process.cwd(), 'docs', 'sample_import_permissions_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const permImportSample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_permissions_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const permImportMutation = permImportSample.match(/```graphql\n([\s\S]*?)\n```/)![1]; const permImportVariables = JSON.parse(permImportSample.match(/```json\n([\s\S]*?)\n```/)![1]); @@ -189,7 +189,7 @@ describe("Template Integration Tests", () => { expect(permImportResult.data.importTemplateGroups).toHaveLength(permImportVariables.templateGroups.length); // 3. Export (Query) - const querySample = readFileSync(join(process.cwd(), 'docs', 'sample_all_template_groups_query.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const querySample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_all_template_groups_query.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1]; const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]); diff --git a/src/test/user_rights_integration.test.ts b/src/test/user_rights_integration.test.ts index afb9663..a7e401c 100644 --- a/src/test/user_rights_integration.test.ts +++ b/src/test/user_rights_integration.test.ts @@ -26,7 +26,7 @@ describe("User Rights Integration Tests", () => { }); test("Import user rights using sample", async () => { - const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_user_rights_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); + const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_user_rights_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n'); const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/); const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/); From a01bfabfbaaa9902a428b9207002523beeb02de8 Mon Sep 17 00:00:00 2001 From: Andreas Hilbig Date: Fri, 30 Jan 2026 14:34:09 +0100 Subject: [PATCH 11/21] 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. --- .idea/runConfigurations/index_ts.xml | 2 +- .idea/workspace.xml | 125 ++++++++---------- .junie/guidelines.md | 11 +- .nvmrc | 1 + Dockerfile | 4 +- README.md | 120 ++++++++++++----- codegen.ts | 3 +- docker-compose.yml | 2 +- docs/howtos/README.md | 7 +- docs/howtos/cookbook.md | 87 ++++++++++++ docs/howtos/mcp.md | 25 +++- docs/howtos/permissions.md | 2 +- docs/howtos/tags.md | 2 +- ...e_import_distance_tracker_template.graphql | 2 +- package-lock.json | 19 +-- package.json | 7 +- readme.improvement.plan.md | 72 ++++++++++ schema/device_value_commons.graphql | 2 +- schema/devices.graphql | 2 +- .../location_tracker_commons.graphql | 2 +- schema/mutations.graphql | 16 +-- schema/zabbix.graphql | 2 +- src/api/start.ts | 4 +- src/common_utils.ts | 4 +- src/datasources/zabbix-api.ts | 4 +- src/datasources/zabbix-hostgroups.ts | 4 +- src/schema/generated/graphql.ts | 28 ++-- src/test/zabbix_api_config.test.ts | 6 +- 28 files changed, 395 insertions(+), 170 deletions(-) create mode 100644 .nvmrc create mode 100644 docs/howtos/cookbook.md create mode 100644 readme.improvement.plan.md diff --git a/.idea/runConfigurations/index_ts.xml b/.idea/runConfigurations/index_ts.xml index 1c32029..e23e69f 100644 --- a/.idea/runConfigurations/index_ts.xml +++ b/.idea/runConfigurations/index_ts.xml @@ -4,7 +4,7 @@ - + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 2f3b3b9..c153fa3 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,30 +4,9 @@ - + + - - - - - - - - - - - - - - - - - - - - - - - @@ -80,45 +59,45 @@ - { + "keyToString": { + "NIXITCH_NIXPKGS_CONFIG": "", + "NIXITCH_NIX_CONF_DIR": "", + "NIXITCH_NIX_OTHER_STORES": "", + "NIXITCH_NIX_PATH": "", + "NIXITCH_NIX_PROFILES": "", + "NIXITCH_NIX_REMOTE": "", + "NIXITCH_NIX_USER_PROFILE_DIR": "", + "Node.js.index.ts.executor": "Run", + "RunOnceActivity.MCP Project settings loaded": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", + "RunOnceActivity.git.unshallow": "true", + "RunOnceActivity.typescript.service.memoryLimit.init": "true", + "com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true", + "git-widget-placeholder": "main", + "go.import.settings.migrated": "true", + "javascript.preferred.runtime.type.id": "node", + "junie.onboarding.icon.badge.shown": "true", + "last_opened_file_path": "//wsl.localhost/Ubuntu/home/ahilbig/git/vcr/zabbix-graphql-api/src", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_interpreter_path": "wsl://Ubuntu@/home/ahilbig/.nvm/versions/node/v24.12.0/bin/node", + "nodejs_package_manager_path": "npm", + "npm.codegen.executor": "Run", + "npm.compile.executor": "Run", + "npm.copy-schema.executor": "Run", + "npm.prod.executor": "Run", + "npm.test.executor": "Run", + "settings.editor.selected.configurable": "ml.llm.mcp", + "settings.editor.splitter.proportion": "0.28812414", + "to.speed.mode.migration.done": "true", + "ts.external.directory.path": "\\\\wsl.localhost\\Ubuntu\\home\\ahilbig\\git\\vcr\\zabbix-graphql-api\\node_modules\\typescript\\lib", + "vue.rearranger.settings.migration": "true" } -}]]> +} @@ -436,9 +423,6 @@ - - - @@ -461,7 +445,10 @@ - diff --git a/.junie/guidelines.md b/.junie/guidelines.md index 820f2d4..80d96a7 100644 --- a/.junie/guidelines.md +++ b/.junie/guidelines.md @@ -4,11 +4,11 @@ This document provides concise information and best practices for developers wor ### Environment - **Operating System:** Windows with WSL + Ubuntu installed. -- **Commands:** Always execute Linux commands (e.g., use `ls` instead of `dir`). +- **Commands:** Always execute Linux commands (e.g. use `ls` instead of `dir`). ## Tech Stack -- **Runtime**: Node.js (v18+) +- **Runtime**: Node.js (v24+) - **Language**: TypeScript (ESM) - **API**: GraphQL (Apollo Server 4) - **Testing**: Jest @@ -30,16 +30,17 @@ This document provides concise information and best practices for developers wor - `npm run prod`: Prepares the schema and runs the compiled production build. ## 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. - **Type Safety**: Leverage types generated via `npm run codegen` for resolvers and data handling to ensure consistency with the schema. - **Import Optimization**: - Always optimize imports before committing. - 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`. - **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/`. +- **Grammar & Style**: Avoid using a comma after "e.g." or "i.e." (e.g. use "e.g. example" instead of "e.g., example"). ### Git Standards -- **Commit Messages:** Use [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `style:`). +- **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. diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/Dockerfile b/Dockerfile index 25e9476..3b8f336 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # 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 -ARG node_version=21.5.0 +ARG node_version=24 #stage1 FROM node:${node_version} as builder diff --git a/README.md b/README.md index e6d55af..dd8a992 100644 --- a/README.md +++ b/README.md @@ -6,37 +6,39 @@ A modern GraphQL interface for Zabbix, providing enhanced features and easier in 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. -## Key Features & Enhancements +## 🚀 Features -Compared to the original Zabbix API, this GraphQL API provides several key enhancements: +- **GraphQL Interface**: Modern GraphQL API wrapping Zabbix functionality + - *Reference*: `schema/queries.graphql`, `schema/mutations.graphql`, `src/api/start.ts` -* **Mass Import/Export**: Robust support for importing and exporting templates, template groups, hosts, and host groups in bulk. -* **Hierarchical Host Groups**: Automatically handles the creation and resolution of nested host group hierarchies (e.g., `Parent/Child/Leaf`). -* **Template Management**: - * Full support for template items, including complex preprocessing steps and tags. - * **Dependent Item Support**: Intelligent deferred creation logic to handle item dependencies within a template. - * Linked template resolution by name. -* **Advanced Deletion**: Ability to delete templates and template groups not only by ID but also by **name patterns** (supporting Zabbix wildcards like `%`). -* **User Rights & Permissions**: - * Integrated management of user roles and user groups. - * Support for importing/exporting user rights with UUID-based matching for cross-instance consistency. - * On-the-fly permission checks (`hasPermissions`, `userPermissions`). -* **Improved Error Reporting**: Detailed error data from Zabbix is appended to GraphQL error messages, making debugging significantly easier. -* **Strongly Typed Schema**: Leverages GraphQL's type system for clear API documentation and client-side code generation. -* **Dynamic Schema Extensibility**: Easily extend the API with custom schema snippets and dynamic resolvers for specialized device types without modifying the core code. -* **CI/CD Integration**: Includes a ready-to-use Forgejo/Gitea/GitHub Actions workflow for automated building, testing, and deployment. -* **MCP Integration**: Native support for the **Model Context Protocol (MCP)**, enabled by GraphQL's introspectable schema, allowing LLMs to seamlessly interact with Zabbix data. -* **Sample Application (VCR)**: Designed to power the **Virtual Control Room**, a professional cockpit for managing thousands of IoT/Edge devices. +- **Hierarchical Data Mapping**: Automatic mapping of Zabbix items/tags to nested GraphQL objects + - *Reference*: `src/api/resolver_helpers.ts`, `schema/device_value_commons.graphql`, `docs/sample_all_devices_query.graphql` + +- **Mass Operations**: Import/export capabilities for hosts, templates, and user rights + - *Reference*: `schema/mutations.graphql` (importHosts, importTemplates, importUserRights, etc.), `docs/sample_import_*.graphql` + +- **Dynamic Schema Extension**: Extend the schema without code changes using environment variables + - *Reference*: `src/api/schema.ts`, `schema/extensions/`, `src/common_utils.ts` (ADDITIONAL_SCHEMAS, ADDITIONAL_RESOLVERS) + +- **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` + +- **Type Safety**: Full TypeScript support with generated types + - *Reference*: `codegen.ts`, `src/schema/generated/graphql.ts`, `tsconfig.json`, `package.json` (devDependencies for GraphQL Codegen) + +- **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`, `mcp-config.yaml`, `docker-compose.yml` (MCP service) ## How-To Guides For detailed information on specific topics, please refer to our how-to guides: +* [**Cookbook**](./docs/howtos/cookbook.md): Practical, step-by-step 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. * [**Zabbix Tags Usage**](./docs/howtos/tags.md): Using tags for classification and metadata. -* [**MCP Integration**](./docs/howtos/mcp.md): Connecting LLMs to Zabbix via Model Context Protocol. +* [**MCP & Agent Integration**](./docs/howtos/mcp.md): Connecting LLMs and autonomous agents to Zabbix via Model Context Protocol. See the [How-To Overview](./docs/howtos/README.md) for a complete list of documentation. @@ -44,7 +46,7 @@ See the [How-To Overview](./docs/howtos/README.md) for a complete list of docume ### Prerequisites -* **Node.js**: Version 18 or higher recommended. +* **Node.js**: Version 24 (LTS) or higher recommended. * **Zabbix**: A running Zabbix instance (compatible with Zabbix 6.0+). ### Installation @@ -60,19 +62,59 @@ See the [How-To Overview](./docs/howtos/README.md) for a complete list of docume npm install ``` -### Configuration +## ⚙️ Configuration + +### Environment Variables The API is configured via environment variables. Create a `.env` file or set them in your environment: -| Variable | Description | Default | -| :--- | :--- | :--- | -| `ZABBIX_BASE_URL` | URL to your Zabbix API (e.g., `http://zabbix.example.com/zabbix`) | | -| `ZABBIX_AUTH_TOKEN` | Zabbix Super Admin API token for administrative tasks | | -| `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base host group for devices | `Roadwork` | -| `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX` | Prefix for template groups used as permissions | `Permissions` | -| `SCHEMA_PATH` | Path to the directory containing `.graphql` schema files | `./schema/` | -| `HOST_GROUP_FILTER_DEFAULT` | Default search pattern for `allHostGroups` query | | -| `HOST_TYPE_FILTER_DEFAULT` | Default value for `tag_hostType` filter in `allHosts` and `allDevices` queries | | +| Variable | Description | Default | Required | +|----------|-------------|---------|----------| +| `ZABBIX_BASE_URL` | URL to your Zabbix server (include `/zabbix` path) | - | Yes | +| `ZABBIX_PRIVILEGE_ESCALATION_TOKEN` | Zabbix Super Admin API token used for privilege escalation (e.g. during import 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 | `Roadwork` | 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 | +| `ADDITIONAL_SCHEMAS` | Comma-separated list of additional schema files | - | No | +| `ADDITIONAL_RESOLVERS` | Comma-separated list of resolver types to generate | - | No | +| `LOG_LEVEL` | Log level configuration (e.g. `debug`, `info`, `warn`, `error`) | `info` | No | +| `HOST_TYPE_FILTER_DEFAULT` | Default filter for host types | - | No | +| `HOST_GROUP_FILTER_DEFAULT` | Default filter for host groups | - | No | + +## 🔐 Authorization + +The application operates with different authentication and authorization mechanisms depending on the scenario: + +### 1. Production Use + +In production environments, the `ZABBIX_DEVELOPMENT_TOKEN` should **always be unset** to ensure proper security. + +* **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. + +### 2. Privilege Escalation + +* **`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. + +### 3. Local Development and Testing + +* **`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. + * **Warning**: This should be avoided in production as it undermines security by bypassing per-request authentication. + +### Zabbix to GraphQL Mapping + +The API maps Zabbix entities to GraphQL types as follows: + +| Zabbix Entity | GraphQL Type | Description | +|---------------|--------------|-------------| +| Host | `Host` / `Device` | Represents a Zabbix host; `Device` is a specialized `Host` with a `deviceType` tag | +| Host Group | `HostGroup` | Represents a Zabbix host group | +| Template | `Template` | Represents a Zabbix template | +| Template Group | `HostGroup` | Represents a Zabbix template group | +| 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 | +| Inventory | `Location` | Host inventory information maps to location data | ### Starting the API @@ -107,7 +149,7 @@ docker run -d \ --name zabbix-graphql-api \ -p 4000:4000 \ -e ZABBIX_BASE_URL=http://your-zabbix-instance/zabbix \ - -e ZABBIX_AUTH_TOKEN=your-super-admin-token \ + -e ZABBIX_PRIVILEGE_ESCALATION_TOKEN=your-super-admin-token \ forgejo.tooling.hilbigit.com/zabbix/zabbix-graphql-api:latest ``` @@ -126,7 +168,7 @@ If you prefer to build the image yourself using the provided `Dockerfile`: --name zabbix-graphql-api \ -p 4000:4000 \ -e ZABBIX_BASE_URL=http://your-zabbix-instance/zabbix \ - -e ZABBIX_AUTH_TOKEN=your-super-admin-token \ + -e ZABBIX_PRIVILEGE_ESCALATION_TOKEN=your-super-admin-token \ zabbix-graphql-api ``` @@ -136,7 +178,7 @@ The **Virtual Control Room (VCR)** is a professional cockpit and control center ### 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). +* **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. @@ -151,7 +193,7 @@ Below is a complete example of a `.env` file showing all available configuration ```env # Zabbix Connection ZABBIX_BASE_URL=http://your-zabbix-instance/zabbix -ZABBIX_AUTH_TOKEN=your-super-admin-token-here +ZABBIX_PRIVILEGE_ESCALATION_TOKEN=your-super-admin-token-here # General Configuration ZABBIX_EDGE_DEVICE_BASE_GROUP=Roadwork @@ -188,6 +230,14 @@ The `docs/queries` directory contains several sample GraphQL queries and mutatio * [Export User Rights](docs/queries/sample_export_user_rights_query.graphql) * [Import User Rights](docs/queries/sample_import_user_rights_mutation.graphql) +## 🔄 API Version + +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. + +### 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. + ## License This project is licensed under the **GNU Affero General Public License v3.0**. See the [LICENSE](LICENSE) file for details. diff --git a/codegen.ts b/codegen.ts index 4496883..35e153c 100644 --- a/codegen.ts +++ b/codegen.ts @@ -16,8 +16,7 @@ const config: CodegenConfig = { declarationKind: 'interface' } } - }, - watch: true + } }; export default config; diff --git a/docker-compose.yml b/docker-compose.yml index 1b7d32e..f39e29d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: - .env environment: - SCHEMA_PATH=/usr/app/dist/schema/ - - ZABBIX_AUTH_TOKEN_FOR_REQUESTS=${ZABBIX_AUTH_TOKEN_FOR_REQUESTS} + - ZABBIX_DEVELOPMENT_TOKEN=${ZABBIX_DEVELOPMENT_TOKEN} apollo-mcp-server: image: ghcr.io/apollographql/apollo-mcp-server:latest diff --git a/docs/howtos/README.md b/docs/howtos/README.md index 978d1fb..2a50708 100644 --- a/docs/howtos/README.md +++ b/docs/howtos/README.md @@ -4,6 +4,9 @@ This directory contains detailed guides on how to use and extend the Zabbix Grap ## 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. @@ -16,8 +19,8 @@ Discover how the permission system works, how to define permission levels using ### 🏷️ [Zabbix Tags Usage](./tags.md) Learn how Zabbix tags are used for device classification, host categorization, and as metadata within the GraphQL API. -### 🤖 [MCP Integration](./mcp.md) -Discover how to integrate the Zabbix GraphQL API with the Model Context Protocol (MCP) to enable LLMs to interact with your Zabbix data. +### 🤖 [MCP & Agent Integration](./mcp.md) +Discover how to integrate the Zabbix GraphQL API with the Model Context Protocol (MCP) to enable LLMs and autonomous agents to interact with your Zabbix data efficiently. --- diff --git a/docs/howtos/cookbook.md b/docs/howtos/cookbook.md new file mode 100644 index 0000000..e50bc93 --- /dev/null +++ b/docs/howtos/cookbook.md @@ -0,0 +1,87 @@ +# 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: +1. Start the `zabbix-graphql` MCP server. +2. Provide the recipe to your AI agent. +3. Ask the agent to "Implement a test case for this recipe using the Zabbix GraphQL API". +4. The agent will use the MCP server to explore the schema and generate appropriate GraphQL operations. + +--- + +## 🍳 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. + +### 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`): +```graphql +type DistanceTrackerDevice { + id: String + name: String + location: Location + distance: Float + batteryLevel: Float + lastSeen: DateTime +} +``` + +### Step 2: Configure Environment Variables +Add the new schema and resolver to your `.env` file: +```env +ADDITIONAL_SCHEMAS=./schema/extensions/distance_tracker.graphql +ADDITIONAL_RESOLVERS=DistanceTrackerDevice +``` +Restart the API server. + +### Step 3: Import the Template +Execute the `importTemplates` mutation to create the template in Zabbix. Use Zabbix item keys that match your GraphQL fields (e.g. `distance.current` for `distance`). + +--- + +## 🍳 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 +```graphql +mutation CreateNewHost($host: String!, $groups: [Int!]!, $templates: [Int!]!) { + createHost(host: $host, hostgroupids: $groups, templateids: $templates) { + hostids + error { + message + } + } +} +``` + +--- + +## 🍳 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 +```graphql +query CheckMyPermissions { + hasPermissions(permissions: [ + { objectName: "Read-Only-Access", permission: READ } + ]) +} +``` diff --git a/docs/howtos/mcp.md b/docs/howtos/mcp.md index 6943e5d..9d089a9 100644 --- a/docs/howtos/mcp.md +++ b/docs/howtos/mcp.md @@ -58,8 +58,25 @@ To use this integration with Claude Desktop, add the following configuration to **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. -### Benefits of GraphQL-enabled MCP +### Benefits of GraphQL-enabled MCP over REST -- **Self-Documenting**: The GraphQL schema provides all necessary metadata for the LLM to understand how to use the tools. -- **Efficient**: LLMs can request exactly the data they need, reducing token usage and improving response speed. -- **Secure**: Uses the same authentication and permission model as the rest of the API. +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: + +1. Analyze the step-by-step instructions in the recipe. +2. Use the MCP server's tools to inspect the current Zabbix state and schema. +3. Generate and execute the necessary GraphQL operations to fulfill the recipe's task. +4. 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." diff --git a/docs/howtos/permissions.md b/docs/howtos/permissions.md index d521dc0..7cea864 100644 --- a/docs/howtos/permissions.md +++ b/docs/howtos/permissions.md @@ -38,7 +38,7 @@ query GetUserPermissions { ### Setting Up Permissions -1. Create template groups with the prefix `Permissions/` (e.g., `Permissions/hosts`, `Permissions/templates`) +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 diff --git a/docs/howtos/tags.md b/docs/howtos/tags.md index 6b8e9a3..8d65307 100644 --- a/docs/howtos/tags.md +++ b/docs/howtos/tags.md @@ -13,7 +13,7 @@ The `hostType` tag is used to categorize hosts and templates. This allows the AP 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`). +* **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). diff --git a/docs/queries/sample_import_distance_tracker_template.graphql b/docs/queries/sample_import_distance_tracker_template.graphql index 62dd415..b47014e 100644 --- a/docs/queries/sample_import_distance_tracker_template.graphql +++ b/docs/queries/sample_import_distance_tracker_template.graphql @@ -21,7 +21,7 @@ mutation ImportDistanceTrackerTemplate($templates: [CreateTemplate!]!) { ### 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 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 { diff --git a/package-lock.json b/package-lock.json index 5ffedf7..91ff0f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "@types/cors": "^2.8.17", "@types/express": "^5.0.6", "@types/jest": "^29.5.13", - "@types/node": "^22.6.1", + "@types/node": "^24.10.9", "@types/simple-mock": "^0.8.6", "@types/ws": "^8.5.12", "jest": "^29.7.0", @@ -43,6 +43,9 @@ "ts-node": "^10.9.2", "tsx": "^4.19.1", "typescript": "^5.6.2" + }, + "engines": { + "node": ">=24" } }, "node_modules/@apollo/cache-control-types": { @@ -3764,14 +3767,14 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/qs": { @@ -10091,9 +10094,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 2e5ed1a..9a0abae 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "nodemon --watch \"src/**\" --watch \"schema.graphql\" --ext \"ts,json\" --exec \"node --import tsx ./src/index.ts\"", "prod": "npm run copy-schema && node ./dist/index.js", "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", "copy-schema": "cp -R schema ./dist/" }, @@ -17,6 +17,9 @@ "license": "AGPL-3.0-only", "author": "Andreas Hilbig", "copyright": "All rights reserved by Hilbig IT GmbH", + "engines": { + "node": ">=24" + }, "dependencies": { "@apollo/datasource-rest": "^6.3.0", "@apollo/server": "^5.2.0", @@ -41,7 +44,7 @@ "@types/cors": "^2.8.17", "@types/express": "^5.0.6", "@types/jest": "^29.5.13", - "@types/node": "^22.6.1", + "@types/node": "^24.10.9", "@types/simple-mock": "^0.8.6", "@types/ws": "^8.5.12", "jest": "^29.7.0", diff --git a/readme.improvement.plan.md b/readme.improvement.plan.md new file mode 100644 index 0000000..c552fc9 --- /dev/null +++ b/readme.improvement.plan.md @@ -0,0 +1,72 @@ +# 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 +- **Feature-to-Code Mapping**: Add "Reference" links to each feature in the features list (from public origin). +- **Comprehensive Config Reference**: Update environment variables table with detailed descriptions, defaults, and requirements. +- **Auth Separation**: Explain `ZABBIX_PRIVILEGE_ESCALATION_TOKEN` vs `ZABBIX_DEVELOPMENT_TOKEN`. +- **Entity Mapping Table**: Include the Zabbix-to-GraphQL entity mapping table. +- **API Versioning**: Document how the version is determined and Zabbix version compatibility (7.4 focus). + +### 1.2 Update Detailed Guides +- 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`) +- **Step-by-Step Recipes**: Create practical, task-oriented guides. +- **Recipe: Schema Extension**: Move the "Distance Tracker" example into the cookbook. +- **Recipe: Basic Monitoring**: Step-by-step to monitor a new host. +- **Recipe: Bulk Import**: Guide for mass importing templates and hosts. + +### 2.2 AI/MCP Integration for Test Generation +- **MCP Server Documentation**: Explicitly document how to start and use the `zabbix-graphql` MCP server. +- **Test Generation Instructions**: Provide specific instructions on how an AI can use the Cookbook recipes to generate test cases. +- **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 +- Explain the GraphQL Codegen setup and how to regenerate types. +- Document the `codegen.ts` configuration. +- Add instructions for updating generated types after schema changes. + +## Priority 4: Improve Examples + +### 4.1 Complete Examples +- Add more complete examples for each major operation. +- Include error handling examples. +- Add examples for common use cases beyond the distance tracker. + +### 4.2 Testing Examples +- Add information about how to run tests. +- Include examples of unit and integration tests. +- Explain the test structure and how to add new tests. + +## Priority 5: Documentation Links + +### 5.1 Cross-Reference Improvements +- Add links to relevant sections in schema files. +- Include references to specific resolver implementations. +- Link to related documentation files in the docs directory. + +### 5.2 External Resources +- Link to official Zabbix API documentation. +- Include references to Apollo Server documentation. + +## Implementation Order +1. **Foundation**: Update `README.md` with missing reference information from public origin. +2. **Cookbook Alpha**: Create `docs/howtos/cookbook.md` with the first set of recipes. +3. **AI Bridge**: Document MCP server usage and test generation workflow. +4. **Maintenance**: Add Codegen and Testing sections. +5. **Cross-Linking**: Optimize all links and cross-references. +6. **Optimize**: Run import optimization across the project. + +## Success Metrics +- All environment variables documented. +- Clear distinction between Reference and Cookbook. +- Functional MCP-based test generation using cookbook instructions. +- Accurate representation of features and compatibility. diff --git a/schema/device_value_commons.graphql b/schema/device_value_commons.graphql index eb3e7fc..322ebf9 100644 --- a/schema/device_value_commons.graphql +++ b/schema/device_value_commons.graphql @@ -13,7 +13,7 @@ interface DeviceValueMessage { """ 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 diff --git a/schema/devices.graphql b/schema/devices.graphql index b7e259f..682a9c9 100644 --- a/schema/devices.graphql +++ b/schema/devices.graphql @@ -74,7 +74,7 @@ type OperationalDeviceData { temperature: Float """Device voltage.""" voltage: Float - """Signal strength (e.g., WiFi or GSM).""" + """Signal strength (e.g. WiFi or GSM).""" signalstrength: Float """Current location of the device.""" location: Location diff --git a/schema/extensions/location_tracker_commons.graphql b/schema/extensions/location_tracker_commons.graphql index 6157aa0..2ac018e 100644 --- a/schema/extensions/location_tracker_commons.graphql +++ b/schema/extensions/location_tracker_commons.graphql @@ -12,7 +12,7 @@ type SensorDistanceValue implements DeviceValue { """ 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 diff --git a/schema/mutations.graphql b/schema/mutations.graphql index 1d63759..1a41ffb 100644 --- a/schema/mutations.graphql +++ b/schema/mutations.graphql @@ -85,7 +85,7 @@ type Mutation { deleteTemplates( """List of template IDs to delete.""" 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 ): [DeleteResponse!] @@ -189,7 +189,7 @@ input CreateTemplateItem { """ 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 """ @@ -197,11 +197,11 @@ input CreateTemplateItem { """ 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 """ - History storage period (e.g., '2d', '90d'). + History storage period (e.g. '2d', '90d'). """ history: String """ @@ -245,7 +245,7 @@ Input for an item preprocessing step. """ 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! """ @@ -477,7 +477,7 @@ input UserRoleInput { """ 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 """ @@ -515,7 +515,7 @@ input UserRoleRulesInput { """ 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 """ @@ -541,7 +541,7 @@ input UserRoleRuleInput { """ name: String """ - Status (e.g., 1 for enabled, 0 for disabled). + Status (e.g. 1 for enabled, 0 for disabled). """ status: Int } diff --git a/schema/zabbix.graphql b/schema/zabbix.graphql index eafdaf9..f7a5ad0 100644 --- a/schema/zabbix.graphql +++ b/schema/zabbix.graphql @@ -72,7 +72,7 @@ type ZabbixItem { """ 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! """ diff --git a/src/api/start.ts b/src/api/start.ts index 8c436fa..f490258 100644 --- a/src/api/start.ts +++ b/src/api/start.ts @@ -8,7 +8,7 @@ import express from 'express'; import cors from "cors"; import {ApolloServerPluginDrainHttpServer} from '@apollo/server/plugin/drainHttpServer'; 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 {useServer} from "graphql-ws/lib/use/ws"; @@ -82,7 +82,7 @@ async function startApolloServer() { dataSources: { zabbixAPI: zabbixAPI, }, - zabbixAuthToken: req.headers["zabbix-auth-token"] ?? zabbixRequestAuthToken, + zabbixAuthToken: req.headers["zabbix-auth-token"] ?? zabbixDevelopmentToken, cookie: req.headers.cookie, token: req.headers.token }; diff --git a/src/common_utils.ts b/src/common_utils.ts index 4fcb46d..056193a 100644 --- a/src/common_utils.ts +++ b/src/common_utils.ts @@ -9,8 +9,8 @@ static readonly DRY_RUN = process.env.DRY_RUN static readonly SCHEMA_PATH = process.env.SCHEMA_PATH || './schema/' static readonly ADDITIONAL_SCHEMAS = process.env.ADDITIONAL_SCHEMAS 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_AUTH_TOKEN = process.env.ZABBIX_AUTH_TOKEN + static readonly ZABBIX_DEVELOPMENT_TOKEN = process.env.ZABBIX_DEVELOPMENT_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_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" diff --git a/src/datasources/zabbix-api.ts b/src/datasources/zabbix-api.ts index 3d73f62..fae182c 100644 --- a/src/datasources/zabbix-api.ts +++ b/src/datasources/zabbix-api.ts @@ -9,8 +9,8 @@ import {logger} from "../logging/logger.js"; import {ParsedArgs, ZabbixErrorResult, ZabbixRequest, ZabbixResult} from "./zabbix-request.js"; import {Config} from "../common_utils.js"; -export const zabbixRequestAuthToken = Config.ZABBIX_AUTH_TOKEN_FOR_REQUESTS -export const zabbixSuperAuthToken = Config.ZABBIX_AUTH_TOKEN +export const zabbixDevelopmentToken = Config.ZABBIX_DEVELOPMENT_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 FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX = new RegExp(`^(${ZABBIX_EDGE_DEVICE_BASE_GROUP})\/`) diff --git a/src/datasources/zabbix-hostgroups.ts b/src/datasources/zabbix-hostgroups.ts index 65e9c6d..2881db9 100644 --- a/src/datasources/zabbix-hostgroups.ts +++ b/src/datasources/zabbix-hostgroups.ts @@ -4,7 +4,7 @@ import { FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX, ZABBIX_EDGE_DEVICE_BASE_GROUP, ZabbixAPI, - zabbixSuperAuthToken + zabbixPrivilegeEscalationToken } from "./zabbix-api.js"; import {logger} from "../logging/logger.js"; import {ZabbixRequestWithPermissions} from "./zabbix-permissions.js"; @@ -23,7 +23,7 @@ const hostGroupReadWritePermissions = { export class ZabbixCreateHostGroupRequest extends ZabbixRequestWithPermissions { constructor(_authToken?: string | null, cookie?: string) { - super("hostgroup.create", zabbixSuperAuthToken, cookie, hostGroupReadWritePermissions); + super("hostgroup.create", zabbixPrivilegeEscalationToken, cookie, hostGroupReadWritePermissions); } } diff --git a/src/schema/generated/graphql.ts b/src/schema/generated/graphql.ts index f198540..c630ddd 100644 --- a/src/schema/generated/graphql.ts +++ b/src/schema/generated/graphql.ts @@ -1,6 +1,8 @@ -import {DeviceCommunicationType, DeviceStatus, Permission, StorageItemType} from '../../model/model_enum_values.js'; -import {GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig} from 'graphql'; - +import { DeviceCommunicationType } from '../../model/model_enum_values.js'; +import { StorageItemType } from '../../model/model_enum_values.js'; +import { DeviceStatus } from '../../model/model_enum_values.js'; +import { Permission } from '../../model/model_enum_values.js'; +import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -94,7 +96,7 @@ export interface CreateItemPreprocessing { error_handler_params?: InputMaybe; /** Parameters for the preprocessing step. */ params: Array; - /** 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: Scalars['Int']['input']; } @@ -165,7 +167,7 @@ export interface CreateTemplateItem { delay?: InputMaybe; /** Description of the item. */ description?: InputMaybe; - /** History storage period (e.g., '2d', '90d'). */ + /** History storage period (e.g. '2d', '90d'). */ history?: InputMaybe; /** Technical key of the item. */ key: Scalars['String']['input']; @@ -177,13 +179,13 @@ export interface CreateTemplateItem { preprocessing?: InputMaybe>; /** Tags to assign to the item. */ tags?: InputMaybe>; - /** 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?: InputMaybe; /** Units of the value. */ units?: InputMaybe; /** Internally used unique id. */ uuid?: InputMaybe; - /** 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?: InputMaybe; } @@ -266,7 +268,7 @@ export interface DeviceValueMessage { deviceType?: Maybe; /** * 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?: Maybe; /** @@ -581,7 +583,7 @@ export interface OperationalDeviceData { error?: Maybe>; /** Current location of the device. */ location?: Maybe; - /** Signal strength (e.g., WiFi or GSM). */ + /** Signal strength (e.g. WiFi or GSM). */ signalstrength?: Maybe; /** Device temperature. */ temperature?: Maybe; @@ -843,7 +845,7 @@ export interface UserRoleInput { readonly?: InputMaybe; /** Specific rules for the role. */ rules?: InputMaybe; - /** 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?: InputMaybe; } @@ -883,7 +885,7 @@ export interface UserRoleRule { export interface UserRoleRuleInput { /** Name of the rule/element. */ name?: InputMaybe; - /** Status (e.g., 1 for enabled, 0 for disabled). */ + /** Status (e.g. 1 for enabled, 0 for disabled). */ status?: InputMaybe; } @@ -920,7 +922,7 @@ export interface UserRoleRulesInput { api?: InputMaybe>; /** Whether API access is enabled (1) or not (0). */ api_access?: InputMaybe; - /** 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?: InputMaybe; /** Module access rules. */ modules?: InputMaybe>; @@ -1020,7 +1022,7 @@ export interface ZabbixItem { status?: Maybe; /** Communication type used by the item. */ type?: Maybe; - /** 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: Scalars['Int']['output']; } diff --git a/src/test/zabbix_api_config.test.ts b/src/test/zabbix_api_config.test.ts index d0b564d..3581c3e 100644 --- a/src/test/zabbix_api_config.test.ts +++ b/src/test/zabbix_api_config.test.ts @@ -1,11 +1,11 @@ // Import after mocking Config -import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI, zabbixSuperAuthToken} from "../datasources/zabbix-api.js"; +import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI, zabbixPrivilegeEscalationToken} from "../datasources/zabbix-api.js"; // Mocking Config jest.mock("../common_utils.js", () => ({ Config: { ZABBIX_EDGE_DEVICE_BASE_GROUP: "CustomEdgeGroup", - ZABBIX_AUTH_TOKEN: "super-secret-token", + ZABBIX_PRIVILEGE_ESCALATION_TOKEN: "super-secret-token", ZABBIX_BASE_URL: "http://custom-zabbix" } })); @@ -13,7 +13,7 @@ jest.mock("../common_utils.js", () => ({ describe("Zabbix API Config Mocking", () => { test("constants are derived from Config", () => { expect(ZABBIX_EDGE_DEVICE_BASE_GROUP).toBe("CustomEdgeGroup"); - expect(zabbixSuperAuthToken).toBe("super-secret-token"); + expect(zabbixPrivilegeEscalationToken).toBe("super-secret-token"); expect(zabbixAPI.baseURL).toBe("http://custom-zabbix"); }); }); From 91a1523d717dc6822e26dec84ed2a8f9f4a4a14f Mon Sep 17 00:00:00 2001 From: Andreas Hilbig Date: Fri, 30 Jan 2026 15:07:14 +0100 Subject: [PATCH 12/21] docs: complete documentation refactoring and structure optimization This commit finalizes the documentation improvement plan by: - Centralizing reference material in README.md. - Creating a dedicated Technical Maintenance guide (docs/howtos/maintenance.md). - Creating a categorized Sample Queries & Mutations overview (docs/queries/README.md). - Eliminating redundant information across the doc set (DRY principle). - Optimizing cross-references between reference documentation and the Cookbook. - Updating the improvement plan to reflect all tasks as completed. --- .idea/workspace.xml | 16 ++++++-- README.md | 76 ++++++++++++++++++----------------- docs/howtos/README.md | 11 +++-- docs/howtos/cookbook.md | 36 +++++++++++++++++ docs/howtos/maintenance.md | 58 +++++++++++++++++++++++++++ docs/howtos/schema.md | 12 +----- docs/queries/README.md | 28 +++++++++++++ readme.improvement.plan.md | 82 +++++++++++++++++++++++--------------- 8 files changed, 231 insertions(+), 88 deletions(-) create mode 100644 docs/howtos/maintenance.md create mode 100644 docs/queries/README.md diff --git a/.idea/workspace.xml b/.idea/workspace.xml index c153fa3..9f7d5a4 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,9 +4,9 @@ - - + + diff --git a/README.md b/README.md index dd8a992..2e47315 100644 --- a/README.md +++ b/README.md @@ -31,25 +31,29 @@ The Zabbix GraphQL API acts as a wrapper and enhancer for the native Zabbix JSON ## How-To Guides -For detailed information on specific topics, please refer to our 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, step-by-step recipes for quick start and AI test generation. +* [**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. -* [**Zabbix Tags Usage**](./docs/howtos/tags.md): Using tags for classification and metadata. -* [**MCP & Agent Integration**](./docs/howtos/mcp.md): Connecting LLMs and autonomous agents to Zabbix via Model Context Protocol. +* [**Technical Maintenance**](./docs/howtos/maintenance.md): Guide on code generation, testing, and Docker maintenance. +* [**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 +### 📋 Prerequisites +Before you begin, ensure you have met the following requirements: * **Node.js**: Version 24 (LTS) or higher recommended. * **Zabbix**: A running Zabbix instance (compatible with Zabbix 6.0+). + with API access +* **Zabbix Super Admin Token** (for full functionality / privilege escalation) +* **Zabbix User Access** (groups and roles depending on your use case) -### Installation +### 🛠️ Installation 1. Clone the repository: ```bash @@ -61,6 +65,21 @@ See the [How-To Overview](./docs/howtos/README.md) for a complete list of docume ```bash 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 @@ -93,6 +112,8 @@ In production environments, the `ZABBIX_DEVELOPMENT_TOKEN` should **always be un * **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. +> **Recipe**: See [Managing User Permissions](./docs/howtos/cookbook.md#recipe-managing-user-permissions) for setup instructions. + ### 2. Privilege Escalation * **`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. @@ -116,21 +137,8 @@ The API maps Zabbix entities to GraphQL types as follows: | Tag | `Tag` | Represents a Zabbix tag associated with a host or template | | Inventory | `Location` | Host inventory information maps to location data | -### Starting the API +> **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). -#### 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/`. ## Running with Docker @@ -212,23 +220,9 @@ ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice ## Usage Samples -The `docs/queries` directory contains several sample GraphQL queries and mutations to help you get started: +The `docs/queries` directory contains several sample GraphQL queries and mutations to help you get started. -* **Hosts**: - * [Query All Hosts](docs/queries/sample_all_hosts_query.graphql) - * [Import Hosts](docs/queries/sample_import_hosts_mutation.graphql) -* **Templates**: - * [Query Templates](docs/queries/sample_templates_query.graphql) - * [Import Templates](docs/queries/sample_import_templates_mutation.graphql) - * [Import Distance Tracker Template](docs/queries/sample_import_distance_tracker_template.graphql) (Schema Extension Example) - * [Delete Templates](docs/queries/sample_delete_templates_mutation.graphql) -* **Template Groups**: - * [Import Host Template Groups](docs/queries/sample_import_host_template_groups_mutation.graphql) - * [Import Permissions Template Groups](docs/queries/sample_import_permissions_template_groups_mutation.graphql) - * [Delete Template Groups](docs/queries/sample_delete_template_groups_mutation.graphql) -* **User Rights**: - * [Export User Rights](docs/queries/sample_export_user_rights_query.graphql) - * [Import User Rights](docs/queries/sample_import_user_rights_mutation.graphql) +> **Samples Reference**: See the [Sample Queries & Mutations Overview](./docs/queries/README.md) for a categorized list of examples. ## 🔄 API Version @@ -238,6 +232,16 @@ The API version is automatically set during the Docker build process based on th 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. +## 🛠️ 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. diff --git a/docs/howtos/README.md b/docs/howtos/README.md index 2a50708..c67e860 100644 --- a/docs/howtos/README.md +++ b/docs/howtos/README.md @@ -16,12 +16,15 @@ Understand how the API automatically maps flat Zabbix item keys into nested Grap ### 🔐 [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. -### 🏷️ [Zabbix Tags Usage](./tags.md) -Learn how Zabbix tags are used for device classification, host categorization, and as metadata within the GraphQL API. +### 🛠️ [Technical Maintenance](./maintenance.md) +Guide on code generation (GraphQL Codegen), running Jest tests, and local Docker builds. ### 🤖 [MCP & Agent Integration](./mcp.md) -Discover how to integrate the Zabbix GraphQL API with the Model Context Protocol (MCP) to enable LLMs and autonomous agents to interact with your Zabbix data efficiently. +Discover how to integrate with the Model Context Protocol (MCP) to enable LLMs and autonomous agents to interact with Zabbix efficiently. --- -For practical examples of GraphQL operations, check the [Sample Queries](../queries/) directory. +## 🔍 Additional Resources + +- **[Sample Queries](../queries/README.md)**: Categorized list of practical GraphQL operation examples. +- **[Main README](../../README.md)**: Technical reference, configuration, and environment setup. diff --git a/docs/howtos/cookbook.md b/docs/howtos/cookbook.md index e50bc93..c807958 100644 --- a/docs/howtos/cookbook.md +++ b/docs/howtos/cookbook.md @@ -44,6 +44,8 @@ Restart the API server. ### Step 3: Import the Template Execute the `importTemplates` mutation to create the template in Zabbix. Use Zabbix item keys that match your GraphQL fields (e.g. `distance.current` for `distance`). +> **Reference**: See how items map to fields in the [Zabbix to GraphQL Mapping](../../README.md#zabbix-to-graphql-mapping). + --- ## 🍳 Recipe: Provisioning a New Host @@ -56,6 +58,8 @@ Execute the `importTemplates` mutation to create the template in Zabbix. Use Zab 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). + ```graphql mutation CreateNewHost($host: String!, $groups: [Int!]!, $templates: [Int!]!) { createHost(host: $host, hostgroupids: $groups, templateids: $templates) { @@ -85,3 +89,35 @@ query CheckMyPermissions { ]) } ``` + +--- + +## 🍳 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 + } +} +``` + +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). diff --git a/docs/howtos/maintenance.md b/docs/howtos/maintenance.md new file mode 100644 index 0000000..5dcbfd3 --- /dev/null +++ b/docs/howtos/maintenance.md @@ -0,0 +1,58 @@ +# 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: + +```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. +- **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) diff --git a/docs/howtos/schema.md b/docs/howtos/schema.md index 838eb97..c121a4d 100644 --- a/docs/howtos/schema.md +++ b/docs/howtos/schema.md @@ -16,17 +16,7 @@ For comprehensive understanding of each operation, read the detailed comments in ### Zabbix to GraphQL Mapping -The API maps Zabbix entities to GraphQL types as follows: - -| Zabbix Entity | GraphQL Type | Description | -|---------------|--------------|-------------| -| Host | `Host` / `Device` | Represents a Zabbix host; `Device` is a specialized `Host` with a `deviceType` tag | -| Host Group | `HostGroup` | Represents a Zabbix host group | -| Template | `Template` | Represents a Zabbix template | -| 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 | -| Tag | `Tag` | Represents a Zabbix tag associated with a host or template | -| Inventory | `Location` | Host inventory information maps to location data | +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 diff --git a/docs/queries/README.md b/docs/queries/README.md new file mode 100644 index 0000000..e08de52 --- /dev/null +++ b/docs/queries/README.md @@ -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). diff --git a/readme.improvement.plan.md b/readme.improvement.plan.md index c552fc9..ec3cba6 100644 --- a/readme.improvement.plan.md +++ b/readme.improvement.plan.md @@ -5,68 +5,84 @@ This plan outlines the steps to refactor and improve the Zabbix GraphQL API docu ## Priority 1: Reference Documentation (Advanced Users) ### 1.1 Enhance README.md -- **Feature-to-Code Mapping**: Add "Reference" links to each feature in the features list (from public origin). -- **Comprehensive Config Reference**: Update environment variables table with detailed descriptions, defaults, and requirements. -- **Auth Separation**: Explain `ZABBIX_PRIVILEGE_ESCALATION_TOKEN` vs `ZABBIX_DEVELOPMENT_TOKEN`. -- **Entity Mapping Table**: Include the Zabbix-to-GraphQL entity mapping table. -- **API Versioning**: Document how the version is determined and Zabbix version compatibility (7.4 focus). +- [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 -- Ensure `docs/howtos/schema.md`, `permissions.md`, etc., are up-to-date with the latest implementation details. +- [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`) -- **Step-by-Step Recipes**: Create practical, task-oriented guides. -- **Recipe: Schema Extension**: Move the "Distance Tracker" example into the cookbook. -- **Recipe: Basic Monitoring**: Step-by-step to monitor a new host. -- **Recipe: Bulk Import**: Guide for mass importing templates and hosts. +- [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 -- **MCP Server Documentation**: Explicitly document how to start and use the `zabbix-graphql` MCP server. -- **Test Generation Instructions**: Provide specific instructions on how an AI can use the Cookbook recipes to generate test cases. -- **Metadata for AI**: Use structured headers and clear prerequisites in recipes to make them "AI-parsable". +- [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 -- Explain the GraphQL Codegen setup and how to regenerate types. -- Document the `codegen.ts` configuration. -- Add instructions for updating generated types after schema changes. +- [x] Explain the GraphQL Codegen setup and how to regenerate types. +- [x] Document the `codegen.ts` configuration. +- [x] Add instructions for updating generated types after schema changes. ## Priority 4: Improve Examples ### 4.1 Complete Examples -- Add more complete examples for each major operation. -- Include error handling examples. -- Add examples for common use cases beyond the distance tracker. +- [x] Add more complete examples for each major operation. +- [x] Include error handling examples. +- [x] Add examples for common use cases beyond the distance tracker. ### 4.2 Testing Examples -- Add information about how to run tests. -- Include examples of unit and integration tests. -- Explain the test structure and how to add new tests. +- [x] Add information about how to run tests. +- [x] Include examples of unit and integration tests. +- [x] Explain the test structure and how to add new tests. ## Priority 5: Documentation Links ### 5.1 Cross-Reference Improvements -- Add links to relevant sections in schema files. -- Include references to specific resolver implementations. -- Link to related documentation files in the docs directory. +- [x] Add links to relevant sections in schema files. +- [x] Include references to specific resolver implementations. +- [x] Link to related documentation files in the docs directory. ### 5.2 External Resources -- Link to official Zabbix API documentation. -- Include references to Apollo Server documentation. +- [x] Link to official Zabbix API documentation. +- [x] Include references to Apollo Server documentation. + +## Priority 6: Refine Structure & DRY (New) + +### 6.1 Separate Cookbook from Reference +- [x] Move instructional "how-to" steps from README.md to `cookbook.md` or specific how-tos where they distract from reference info. +- [x] Ensure `README.md` focuses on "What" (specification/reference) and `cookbook.md` focuses on "How" (recipes). + +### 6.2 Eliminate Redundancy (DRY) +- [x] Identify and remove duplicate information between `README.md` and `docs/howtos/` (e.g., Zabbix to GraphQL Mapping). +- [x] Centralize core definitions to a single source of truth and use links elsewhere. + +### 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. ## Implementation Order -1. **Foundation**: Update `README.md` with missing reference information from public origin. -2. **Cookbook Alpha**: Create `docs/howtos/cookbook.md` with the first set of recipes. -3. **AI Bridge**: Document MCP server usage and test generation workflow. -4. **Maintenance**: Add Codegen and Testing sections. -5. **Cross-Linking**: Optimize all links and cross-references. -6. **Optimize**: Run import optimization across the project. +1. [x] **Foundation**: Update `README.md` with missing reference information from public origin. +2. [x] **Cookbook Alpha**: Create `docs/howtos/cookbook.md` with the first set of recipes. +3. [x] **AI Bridge**: Document MCP server usage and test generation workflow. +4. [x] **Maintenance**: Add Codegen and Testing sections. +5. [x] **Cross-Linking**: Optimize all links and cross-references. +6. [x] **Optimize**: Run import optimization across the project. +7. [x] **Refine & DRY**: Execute Priority 6 tasks to further clean up and structure documentation. ## Success Metrics - All environment variables documented. - Clear distinction between Reference and Cookbook. - Functional MCP-based test generation using cookbook instructions. - Accurate representation of features and compatibility. +- Zero redundant tables or instructional blocks across the doc set. From bbf7357e9395105f4f4fbaa43385c3ea22fde60f Mon Sep 17 00:00:00 2001 From: Andreas Hilbig Date: Fri, 30 Jan 2026 19:04:05 +0100 Subject: [PATCH 13/21] docs: enhance documentation and automate MCP setup This commit introduces several improvements to the project's documentation, roadmap, and AI agent integration (MCP). Key changes: - Created and styled roadmap.md to track project milestones and future plans. - Updated .junie/guidelines.md with strict documentation style standards. - Automated GraphQL schema concatenation for the MCP server using a schema-gen init-container. - Updated MCP setup recipes in cookbook.md and mcp.md to reflect the new automation. - Added .ai/mcp/mcp.json for connecting to existing MCP services via HTTP. - Improved development workflow by updating package.json to watch .graphql files. - Cleaned up the root directory by moving schema.graphql to .gitignore and removing redundant files. - Standardized visual style and formatting across all markdown files. --- .ai/mcp/mcp.json | 7 +++ .gitignore | 3 ++ .idea/workspace.xml | 20 ++++++++- .junie/guidelines.md | 16 ++++++- README.md | 82 +++++++++++++++++----------------- docker-compose.yml | 17 ++++++- docs/howtos/README.md | 1 - docs/howtos/cookbook.md | 99 ++++++++++++++++++++++++++++++++++------- docs/howtos/mcp.md | 43 ++++++++++-------- mcp-config.yaml | 2 +- package.json | 4 +- roadmap.md | 23 ++++++++++ 12 files changed, 230 insertions(+), 87 deletions(-) create mode 100644 .ai/mcp/mcp.json create mode 100644 roadmap.md diff --git a/.ai/mcp/mcp.json b/.ai/mcp/mcp.json new file mode 100644 index 0000000..fdccbec --- /dev/null +++ b/.ai/mcp/mcp.json @@ -0,0 +1,7 @@ +{ + "mcpServers": { + "zabbix-graphql": { + "url": "http://localhost:3000/mcp" + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 381cdbc..78ded7b 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,6 @@ dist .pnp.* .vscode/settings.json + +# Generated schema for MCP +schema.graphql diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 9f7d5a4..c161fad 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,8 +5,17 @@ + + + + + + + + + - @@ -43,7 +52,13 @@ - + + + + - - - - - - - - - - + + + + + - @@ -215,7 +210,8 @@ - + +