diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 8f6b5aa..d1b9822 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,34 +5,30 @@
-
-
-
+
+
+
-
-
+
-
-
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
+
@@ -43,7 +39,7 @@
-
+
@@ -111,7 +107,7 @@
"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/docs",
+ "last_opened_file_path": "//wsl.localhost/Ubuntu/home/ahilbig/git/vcr/zabbix-graphql-api/docs/use-cases",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
@@ -139,11 +135,11 @@
+
-
@@ -195,9 +191,9 @@
-
+
@@ -235,6 +231,7 @@
+
@@ -504,7 +501,7 @@
file://$PROJECT_DIR$/src/datasources/zabbix-request.ts
- 253
+ 254
diff --git a/README.md b/README.md
index b8e8c2b..9fd837f 100644
--- a/README.md
+++ b/README.md
@@ -99,7 +99,11 @@ The API is configured via environment variables. Create a `.env` file or set the
| `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 |
+| `LOG_LEVELS` | Comma-separated list of log levels to enable (e.g. `DEBUG,INFO,ERROR`) | - | No |
+| `VERBOSITY` | Verbosity level for GraphQL logging (0=off, 1=parameters, 2=parameters+responses) | `0` | No |
+| `MCP_LOG_LEVEL` | Log level for the MCP server | `info` | No |
+| `MCP_LOG_PARAMETERS` | Whether to log parameters of MCP calls | `false` | No |
+| `MCP_LOG_RESPONSES` | Whether to log responses of MCP calls | `false` | No |
| `HOST_TYPE_FILTER_DEFAULT` | Default filter for host types | - | No |
| `HOST_GROUP_FILTER_DEFAULT` | Default filter for host groups | - | No |
@@ -194,7 +198,7 @@ The **Virtual Control Room (VCR)** is a professional cockpit and control center
- **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)
+[VCR - Technical product information](docs/use-cases/VCR%20-%20Technical%20product%20information.pdf)
## Sample Environment File
diff --git a/docker-compose.yml b/docker-compose.yml
index afe92f9..4a56b5c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,6 +11,8 @@ services:
environment:
- SCHEMA_PATH=/usr/app/dist/schema/
- ZABBIX_DEVELOPMENT_TOKEN=${ZABBIX_DEVELOPMENT_TOKEN}
+ - VERBOSITY=${VERBOSITY:-0}
+ - LOG_LEVELS=${LOG_LEVELS:-INFO}
volumes:
- ./samples:/usr/app/dist/samples
@@ -25,6 +27,9 @@ services:
command: /mcp-config.yaml
environment:
- APOLLO_GRAPH_REF=local@main
+ - MCP_LOG_LEVEL=${MCP_LOG_LEVEL:-info}
+ - MCP_LOG_PARAMETERS=${MCP_LOG_PARAMETERS:-false}
+ - MCP_LOG_RESPONSES=${MCP_LOG_RESPONSES:-false}
depends_on:
schema-gen:
condition: service_completed_successfully
diff --git a/docs/howtos/cookbook.md b/docs/howtos/cookbook.md
index 44d3a39..b3fe469 100644
--- a/docs/howtos/cookbook.md
+++ b/docs/howtos/cookbook.md
@@ -346,6 +346,91 @@ Create a host, assign it macros for coordinates, and query its state.
---
+## 🍳 Recipe: Extending Schema with a Simulated Device (Zabbix Trap)
+
+This recipe demonstrates how to create a simulated device type that receives data via Zabbix Trapper items. This is useful for testing or for devices that push their state (like Path/GeoJSON data) to Zabbix instead of being polled.
+
+### 📋 Prerequisites
+- Zabbix GraphQL API is running.
+- You have an external script or system capable of pushing data to Zabbix (e.g. using `zabbix_sender` or the `pushHistory` mutation).
+
+### 🛠️ Step 1: Define the Schema Extension
+Add the `TrackedDevice` type to `samples/extensions/location_tracker_devices.graphql`:
+
+```graphql
+type TrackedDevice implements Host & Device {
+ hostid: ID!
+ host: String!
+ deviceType: String
+ hostgroups: [HostGroup!]
+ name: String
+ tags: DeviceConfig
+ inventory: Inventory
+ items: [ZabbixItem!]
+ state: TrackedState
+}
+
+type TrackedState implements DeviceState {
+ operational: OperationalDeviceData
+ current: TrackedValues
+}
+
+type TrackedValues {
+ """
+ GeoJSON representation of the tracked device's location or path.
+ """
+ geojson: JSONObject
+}
+```
+
+### ⚙️ Step 2: Register the Resolver
+Add `TrackedDevice` to your `.env` file:
+```env
+ADDITIONAL_RESOLVERS=...,TrackedDevice
+```
+Restart the API server.
+
+### 🚀 Step 3: Import the Simulated Device Template
+Use the `importTemplates` mutation to create a template with a **Zabbix Trapper** item. We use the `json_` prefix in the item key to ensure the JSON string is automatically parsed into a `JSONObject`.
+
+> **Reference**: Use the [Sample: Import Simulated BT Template](../../docs/queries/sample_import_simulated_bt_template.graphql) for a complete mutation and variables example.
+
+### 🚀 Step 4: Push History Data
+Push GeoJSON data to your simulated device using the `pushHistory` mutation. This allows you to simulate a device moving along a path by providing multiple data points with different timestamps.
+
+> **Reference**: See the [Sample: Push GeoJSON History](../../docs/queries/sample_push_geojson_history.graphql) for a complete example of pushing historical data.
+
+### ✅ Step 5: Verification
+Verify that the device correctly resolves to the new type and that both the current state and historical data are accessible.
+
+- **Create Host**: Use the `importHosts` mutation to create a host (e.g. `Vehicle1`) and link it to the simulated template.
+- **Query Current State**: Use the `allDevices` query to verify the latest pushed position.
+ - *Reference*: See the [Sample: Tracked Device Query](../../docs/queries/sample_tracked_device_query.graphql).
+- **Query Historical Data**: Use the `exportHostValueHistory` query to retrieve the path history.
+
+```graphql
+query GetVehicleHistory($host: [String!], $key: [String!]) {
+ exportHostValueHistory(
+ host_filter: $host,
+ itemKey_filter: $key,
+ type: TEXT,
+ limit: 100
+ ) {
+ result
+ }
+}
+```
+
+**Variables**:
+```json
+{
+ "host": ["Vehicle1"],
+ "key": ["state.current.json_geojson"]
+}
+```
+
+---
+
## 🍳 Recipe: Testing Specialized Device Types
This recipe shows how to execute a comprehensive query to verify the state and configuration of specialized device types, such as the `DistanceTrackerDevice`. This is useful for validating that your schema extensions and hierarchical mappings are working correctly.
@@ -586,6 +671,77 @@ You can ask **Junie** to automate the entire cloning process:
---
+## 🍳 Recipe: Pushing History Data to Trapper Items
+
+This recipe shows how to push data into Zabbix items of type `ZABBIX_TRAP` using the `pushHistory` mutation. This is particularly useful for IoT devices or external systems that push data to Zabbix instead of being polled.
+
+### 📋 Prerequisites
+- Zabbix GraphQL API is running.
+- A host exists in Zabbix.
+- An item of type `ZABBIX_TRAP` (type 2) exists on that host.
+
+### 🛠️ Step 1: Preparation
+Identify the `itemid` or the combination of `host` and `key` for the target item.
+
+### 🚀 Step 2: Execution/Action
+Execute the `pushHistory` mutation. You can provide multiple values with different timestamps. The `value` field accepts a `JSONObject`, which will be automatically stringified before being sent to Zabbix.
+
+```graphql
+mutation PushDeviceData($host: String, $key: String, $itemid: Int, $values: [HistoryPushInput!]!) {
+ pushHistory(host: $host, key: $key, itemid: $itemid, values: $values) {
+ message
+ data {
+ itemid
+ error {
+ message
+ }
+ }
+ }
+}
+```
+
+**Sample Variables**:
+```json
+{
+ "host": "IoT-Sensor-01",
+ "key": "sensor.data.json",
+ "values": [
+ {
+ "timestamp": "2024-01-01T12:00:00Z",
+ "value": {
+ "temperature": 22.5,
+ "humidity": 45
+ }
+ },
+ {
+ "timestamp": "2024-01-01T12:01:00Z",
+ "value": {
+ "temperature": 22.6,
+ "humidity": 44
+ }
+ }
+ ]
+}
+```
+
+### ✅ Step 3: Verification
+Verify that the data was successfully pushed by querying the item's last value:
+
+```graphql
+query VerifyPushedData($host: String!) {
+ allHosts(filter_host: $host) {
+ items {
+ name
+ key_
+ lastvalue
+ lastclock
+ }
+ }
+}
+```
+
+---
+
## 🍳 Recipe: Setting up GraphQL MCP for AI Agents
This recipe guides you through setting up the Model Context Protocol (MCP) server to enable AI agents like **Junie** or **Claude** to interact with your Zabbix data through the GraphQL API.
diff --git a/docs/howtos/mcp.md b/docs/howtos/mcp.md
index 58ffb21..1c3752f 100644
--- a/docs/howtos/mcp.md
+++ b/docs/howtos/mcp.md
@@ -102,3 +102,17 @@ The MCP server can be used in conjunction with the [**Cookbook**](./cookbook.md)
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."
+
+### 📝 Logging & Verbosity
+
+You can control the logging level and verbosity of both the GraphQL API and the MCP server via environment variables. This is particularly useful for debugging MCP calls and seeing the exact parameters and responses.
+
+- **GraphQL API Verbosity**:
+ - `VERBOSITY=1`: Logs GraphQL operation names and parameters (variables).
+ - `VERBOSITY=2`: Logs operation names, parameters, and the full response body.
+- **MCP Server Logging**:
+ - `MCP_LOG_LEVEL`: Sets the log level for the Apollo MCP server (`debug`, `info`, `warn`, `error`).
+ - `MCP_LOG_PARAMETERS=true`: Enables logging of parameters in the MCP server.
+ - `MCP_LOG_RESPONSES=true`: Enables logging of responses in the MCP server.
+
+When running via Docker Compose, these can be set in your `.env` file.
diff --git a/docs/queries/README.md b/docs/queries/README.md
index 4e664c7..6cfd6e7 100644
--- a/docs/queries/README.md
+++ b/docs/queries/README.md
@@ -8,12 +8,15 @@ This directory contains practical examples of GraphQL operations for the Zabbix
- [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.
+- [Tracked Device Query](./sample_tracked_device_query.graphql): Query simulated or tracked devices.
+- [Push GeoJSON History](./sample_push_geojson_history.graphql): Push multiple GeoJSON data points to a tracked device.
- [Distance Tracker Test Query](./sample_distance_tracker_test_query.graphql): Comprehensive query for testing specialized `DistanceTrackerDevice` types.
### 📄 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.
+- [Import Simulated BT Template](./sample_import_simulated_bt_template.graphql): Example of importing a template for a simulated device.
- [Delete Templates](./sample_delete_templates_mutation.graphql): Remove templates by ID or name pattern.
### 📂 Template Groups
diff --git a/docs/queries/sample_import_simulated_bt_template.graphql b/docs/queries/sample_import_simulated_bt_template.graphql
new file mode 100644
index 0000000..59d77db
--- /dev/null
+++ b/docs/queries/sample_import_simulated_bt_template.graphql
@@ -0,0 +1,46 @@
+### Mutation
+Use this mutation to import a template for a simulated device that pushes GeoJSON data via Zabbix Trapper items.
+
+```graphql
+mutation ImportSimulatedBTTemplate($templates: [CreateTemplate!]!) {
+ importTemplates(templates: $templates) {
+ host
+ templateid
+ message
+ error {
+ message
+ }
+ }
+}
+```
+
+### Variables
+The following sample defines the `SIMULATED_BT_DEVICE` template. Note the `deviceType` tag set to `TrackedDevice`, which instructs the GraphQL API to resolve this host using the specialized `TrackedDevice` type.
+
+We use the `state.current.json_geojson` key for the trapper item. The `json_` prefix ensures that the JSON string received from Zabbix is automatically parsed into a `JSONObject` by the GraphQL resolver.
+
+```json
+{
+ "templates": [
+ {
+ "host": "SIMULATED_BT_DEVICE",
+ "name": "Simulated BT Device",
+ "groupNames": ["Templates/Roadwork/Devices"],
+ "tags": [
+ { "tag": "class", "value": "roadwork" },
+ { "tag": "deviceType", "value": "TrackedDevice" }
+ ],
+ "items": [
+ {
+ "name": "GeoJSON Data",
+ "type": 2,
+ "key": "state.current.json_geojson",
+ "value_type": 4,
+ "history": "7d",
+ "description": "Trapper item receiving GeoJSON payloads"
+ }
+ ]
+ }
+ ]
+}
+```
diff --git a/docs/queries/sample_push_geojson_history.graphql b/docs/queries/sample_push_geojson_history.graphql
new file mode 100644
index 0000000..9b5ff9b
--- /dev/null
+++ b/docs/queries/sample_push_geojson_history.graphql
@@ -0,0 +1,470 @@
+### Mutation
+Use this mutation to push multiple GeoJSON data points to a simulated device. Each point is pushed with its own timestamp extracted from the GeoJSON properties.
+
+```graphql
+mutation PushGeoJsonHistory($host: String!, $key: String!, $values: [HistoryPushInput!]!) {
+ pushHistory(host: $host, key: $key, values: $values) {
+ message
+ data {
+ itemid
+ error {
+ message
+ }
+ }
+ }
+}
+```
+
+### Variables
+The following variables push 20 GeoJSON features to the `Vehicle1` host.
+
+Note that we use the technical Zabbix key `state.current.json_geojson` (including the `json_` prefix) to target the correct trapper item.
+
+```json
+{
+ "host": "Vehicle1",
+ "key": "state.current.json_geojson",
+ "values": [
+ {
+ "timestamp": "2026-02-02T16:00:00.000Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:00:00.000Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.980238638943689,
+ 50.94213786322479
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:00:12.838Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:00:12.838Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.979929916761165,
+ 50.9418828739594
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:00:24.413Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:00:24.413Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.97947100540884,
+ 50.9418828739594
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:00:38.910Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:00:38.910Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.978962030999412,
+ 50.94205111445655
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:00:48.218Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:00:48.218Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.97871171571623,
+ 50.94222198308785
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:00:55.942Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:00:55.942Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.978469744275316,
+ 50.9423402763876
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:01:07.048Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:01:07.048Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.978269492048469,
+ 50.94209317448525
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:01:18.399Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:01:18.399Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.97871171571623,
+ 50.94214574946821
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:01:27.785Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:01:27.785Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.9789870625279775,
+ 50.942303474059884
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:01:38.408Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:01:38.408Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.979103876326803,
+ 50.94255846101797
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:01:50.930Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:01:50.930Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.979337503923659,
+ 50.94283447625244
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:01:58.600Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:01:58.600Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.979475177329533,
+ 50.94300534200477
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:02:15.429Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:02:15.429Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.979212346282509,
+ 50.942618921637944
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:02:30.260Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:02:30.260Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.97896620292093,
+ 50.94228244414538
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:02:36.242Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:02:36.242Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.978816013750361,
+ 50.942166779444534
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:02:46.091Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:02:46.091Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.978582386152624,
+ 50.94196962304159
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:02:56.752Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:02:56.752Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.979003750213366,
+ 50.941948592976075
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:03:15.630Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:03:15.630Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.979721320691965,
+ 50.94181452608393
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:03:24.823Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:03:24.823Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.980071762088585,
+ 50.9418776164344
+ ]
+ }
+ }
+ },
+ {
+ "timestamp": "2026-02-02T16:03:36.019Z",
+ "value": {
+ "type": "Feature",
+ "properties": {
+ "time": "2026-02-02T16:03:36.019Z",
+ "deviceType": "locationTracker",
+ "parent": {
+ "type": "vehicle",
+ "subType": "forklift",
+ "name": "vehicle1"
+ }
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.980234467022115,
+ 50.94213786322479
+ ]
+ }
+ }
+ }
+ ]
+}
+```
diff --git a/docs/queries/sample_tracked_device_query.graphql b/docs/queries/sample_tracked_device_query.graphql
new file mode 100644
index 0000000..20dc45f
--- /dev/null
+++ b/docs/queries/sample_tracked_device_query.graphql
@@ -0,0 +1,19 @@
+### Query
+Retrieve the state of tracked devices.
+
+```graphql
+query GetSimulatedState {
+ allDevices(tag_deviceType: ["TrackedDevice"]) {
+ name
+ host
+ deviceType
+ ... on TrackedDevice {
+ state {
+ current {
+ geojson
+ }
+ }
+ }
+ }
+}
+```
diff --git a/docs/tests.md b/docs/tests.md
index 72fad66..9051c41 100644
--- a/docs/tests.md
+++ b/docs/tests.md
@@ -53,6 +53,9 @@ This document outlines the test cases and coverage for the Zabbix GraphQL API.
- **TC-AUTH-04**: Import user rights.
- **TC-AUTH-05**: Import user rights using sample mutation.
+### History and Data Pushing
+- **TC-HIST-01**: Push history data using `pushHistory` mutation.
+
### Query Optimization
- **TC-OPT-01**: Verify that GraphQL queries only fetch requested fields from Zabbix (reduced output).
- **TC-OPT-02**: Verify that skippable Zabbix parameters (like selectItems) are omitted if not requested in GraphQL.
@@ -91,6 +94,7 @@ The `runAllRegressionTests` mutation (TC-E2E-02) executes the following checks:
- **State sub-properties**: Verifies that requesting device state sub-properties correctly triggers the retrieval of required Zabbix items, even if `items` is not explicitly requested (verifying the indirect dependency logic).
- **Negative Optimization (allDevices)**: Verifies that items are NOT requested from Zabbix if neither `items` nor `state` (or state sub-properties) are requested within the `allDevices` query.
- **allDevices deviceType filter**: Verifies that the `allDevices` query only returns hosts that have a `deviceType` tag, and that the `deviceType` field is populated for all results.
+- **pushHistory mutation**: Verifies that the `pushHistory` mutation correctly pushes data to ZABBIX_TRAP items, using either item ID or a combination of host and item key.
## ✅ Test Coverage Checklist
diff --git a/docs/VCR - Technical product information.pdf b/docs/use-cases/VCR - Technical product information.pdf
similarity index 76%
rename from docs/VCR - Technical product information.pdf
rename to docs/use-cases/VCR - Technical product information.pdf
index 9cd7c57..93a0f68 100644
--- a/docs/VCR - Technical product information.pdf
+++ b/docs/use-cases/VCR - Technical product information.pdf
@@ -14,25 +14,25 @@
endobj
7 0 obj
<<
-/Title (VCR - Technical product information)
+/Title (VCR - Technical product information - 20260203)
/Creator (Canva)
/Producer (Canva)
-/CreationDate (D:20260127114130+00'00')
-/ModDate (D:20260127114130+00'00')
-/Keywords (DAG8VLZFaS4,BAE0LxBesj0,0)
+/CreationDate (D:20260203115237+00'00')
+/ModDate (D:20260203115236+00'00')
+/Keywords (DAHAQ-Pwduk,BAE0LxBesj0,0)
/Author (Andreas Hilbig)
>>
endobj
2 0 obj
<<
/Type /Pages
-/Kids [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R]
-/Count 10
+/Kids [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R]
+/Count 9
>>
endobj
3 0 obj
<<
-/Length 2715
+/Length 2736
/Type /Metadata
/Subtype /XML
>>
@@ -45,11 +45,11 @@ stream
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
dc:language="de-DE"
pdf:Producer="Canva"
- xmp:CreateDate="2026-01-27T11:41:30.717Z">
+ xmp:CreateDate="2026-02-03T11:52:37.56Z">
- VCR - Technical product information
- VCR - Technical product information
+ VCR - Technical product information - 20260203
+ VCR - Technical product information - 20260203
@@ -81,9 +81,9 @@ endobj
4 0 obj
<<
/Type /StructTreeRoot
-/ParentTree 18 0 R
+/ParentTree 17 0 R
/ParentTreeNextKey 100009
-/K 19 0 R
+/K 18 0 R
>>
endobj
5 0 obj
@@ -103,16 +103,16 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 20 0 R
+/ExtGState 19 0 R
/XObject <<
-/X5 21 0 R
-/X6 22 0 R
-/X12 23 0 R
+/X5 20 0 R
+/X6 21 0 R
+/X12 22 0 R
>>
-/Font 24 0 R
+/Font 23 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 25 0 R
+/Contents 24 0 R
/StructParents 0
/Tabs /S
/Parent 2 0 R
@@ -128,18 +128,18 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 26 0 R
+/ExtGState 25 0 R
/XObject <<
-/X38 27 0 R
-/X40 28 0 R
-/X42 29 0 R
-/X44 30 0 R
+/X38 26 0 R
+/X40 27 0 R
+/X42 28 0 R
+/X44 29 0 R
>>
-/Font 31 0 R
+/Font 30 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Annots [32 0 R 33 0 R]
-/Contents 34 0 R
+/Annots [31 0 R 32 0 R]
+/Contents 33 0 R
/StructParents 1
/Tabs /S
/Parent 2 0 R
@@ -154,14 +154,14 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 35 0 R
+/ExtGState 34 0 R
/XObject <<
-/X29 36 0 R
+/X29 35 0 R
>>
-/Font 37 0 R
+/Font 36 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 38 0 R
+/Contents 37 0 R
/StructParents 100002
/Tabs /S
/Parent 2 0 R
@@ -177,11 +177,11 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 39 0 R
-/Font 40 0 R
+/ExtGState 38 0 R
+/Font 39 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 41 0 R
+/Contents 40 0 R
/StructParents 100003
/Tabs /S
/Parent 2 0 R
@@ -197,14 +197,14 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 42 0 R
+/ExtGState 41 0 R
/XObject <<
-/X29 43 0 R
+/X29 42 0 R
>>
-/Font 44 0 R
+/Font 43 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 45 0 R
+/Contents 44 0 R
/StructParents 100004
/Tabs /S
/Parent 2 0 R
@@ -220,11 +220,11 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 46 0 R
-/Font 47 0 R
+/ExtGState 45 0 R
+/Font 46 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 48 0 R
+/Contents 47 0 R
/StructParents 100005
/Tabs /S
/Parent 2 0 R
@@ -240,11 +240,11 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 49 0 R
-/Font 50 0 R
+/ExtGState 48 0 R
+/Font 49 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 51 0 R
+/Contents 50 0 R
/StructParents 100006
/Tabs /S
/Parent 2 0 R
@@ -260,16 +260,16 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 52 0 R
+/ExtGState 51 0 R
/XObject <<
-/X37 53 0 R
-/X46 54 0 R
-/X55 55 0 R
+/X37 52 0 R
+/X46 53 0 R
+/X55 54 0 R
>>
-/Font 56 0 R
+/Font 55 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 57 0 R
+/Contents 56 0 R
/StructParents 100007
/Tabs /S
/Parent 2 0 R
@@ -285,10 +285,10 @@ endobj
/Type /Page
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState 58 0 R
+/ExtGState 57 0 R
>>
/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 59 0 R
+/Contents 58 0 R
/StructParents 100008
/Tabs /S
/Parent 2 0 R
@@ -301,60 +301,45 @@ endobj
endobj
17 0 obj
<<
-/Type /Page
-/Resources 60 0 R
-/MediaBox [0.0 0.0 595.5 841.92]
-/Contents 61 0 R
-/Tabs /S
-/Parent 2 0 R
-/BleedBox [0.0 0.0 595.5 841.92]
-/TrimBox [0.0 0.0 595.5 841.92]
-/CropBox [0.0 0.0 595.5 841.92]
-/Rotate 0
-/Annots []
->>
-endobj
-18 0 obj
-<<
/Limits [0 100008]
-/Nums [0 [62 0 R 63 0 R 64 0 R 65 0 R 66 0 R 67 0 R 68 0 R 69 0 R 70 0 R 71 0 R
-72 0 R 73 0 R 74 0 R 75 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R
-82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R]
- 1 [62 0 R 63 0 R 92 0 R 93 0 R 94 0 R 95 0 R 96 0 R 97 0 R 98 0 R 99 0 R
-100 0 R 101 0 R 102 0 R 103 0 R]
- 100000 104 0 R 100001 104 0 R 100002 [105 0 R 106 0 R 107 0 R 108 0 R 109 0 R 110 0 R 111 0 R 112 0 R 113 0 R 114 0 R
-115 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R 121 0 R 122 0 R 123 0 R 124 0 R
-125 0 R 126 0 R 127 0 R 128 0 R 129 0 R 130 0 R 131 0 R 132 0 R 133 0 R 134 0 R
-135 0 R 136 0 R 137 0 R 138 0 R]
-100003 [105 0 R 106 0 R 139 0 R 140 0 R 141 0 R 142 0 R 143 0 R 144 0 R 145 0 R 146 0 R
-147 0 R 148 0 R 149 0 R 150 0 R 151 0 R 152 0 R 153 0 R 154 0 R 155 0 R 156 0 R
-157 0 R 158 0 R 159 0 R 160 0 R 161 0 R 162 0 R 163 0 R 164 0 R]
- 100004 [165 0 R 166 0 R 167 0 R 168 0 R 169 0 R 170 0 R 171 0 R 172 0 R 173 0 R 174 0 R
-175 0 R]
- 100005 [165 0 R 166 0 R 175 0 R 176 0 R 177 0 R 178 0 R 179 0 R 180 0 R 181 0 R 182 0 R
-183 0 R 184 0 R 185 0 R 186 0 R 187 0 R 188 0 R 189 0 R 190 0 R]
- 100006 [165 0 R 166 0 R 190 0 R 191 0 R 192 0 R 193 0 R 194 0 R 195 0 R 196 0 R 197 0 R
-198 0 R 199 0 R]
+/Nums [0 [59 0 R 60 0 R 61 0 R 62 0 R 63 0 R 64 0 R 65 0 R 66 0 R 67 0 R 68 0 R
+69 0 R 70 0 R 71 0 R 72 0 R 73 0 R 74 0 R 75 0 R 76 0 R 77 0 R 78 0 R
+79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R]
+ 1 [59 0 R 60 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R 95 0 R 96 0 R
+97 0 R 98 0 R 99 0 R 100 0 R]
+ 100000 101 0 R 100001 101 0 R 100002 [102 0 R 103 0 R 104 0 R 105 0 R 106 0 R 107 0 R 108 0 R 109 0 R 110 0 R 111 0 R
+112 0 R 113 0 R 114 0 R 115 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R 121 0 R
+122 0 R 123 0 R 124 0 R 125 0 R 126 0 R 127 0 R 128 0 R 129 0 R 130 0 R 131 0 R
+132 0 R 133 0 R 134 0 R 135 0 R]
+100003 [102 0 R 103 0 R 136 0 R 137 0 R 138 0 R 139 0 R 140 0 R 141 0 R 142 0 R 143 0 R
+144 0 R 145 0 R 146 0 R 147 0 R 148 0 R 149 0 R 150 0 R 151 0 R 152 0 R 153 0 R
+154 0 R 155 0 R 156 0 R 157 0 R 158 0 R 159 0 R 160 0 R 161 0 R]
+ 100004 [162 0 R 163 0 R 164 0 R 165 0 R 166 0 R 167 0 R 168 0 R 169 0 R 170 0 R 171 0 R
+172 0 R]
+ 100005 [162 0 R 163 0 R 173 0 R 174 0 R 175 0 R 176 0 R 177 0 R 178 0 R 179 0 R 180 0 R
+181 0 R 182 0 R 183 0 R 184 0 R 185 0 R 186 0 R 187 0 R]
+ 100006 [162 0 R 163 0 R 188 0 R 189 0 R 190 0 R 191 0 R 192 0 R 193 0 R 194 0 R 195 0 R
+196 0 R 197 0 R 198 0 R 199 0 R]
100007 [200 0 R 201 0 R 202 0 R 203 0 R 204 0 R 205 0 R 206 0 R 207 0 R 208 0 R 209 0 R
210 0 R 211 0 R 212 0 R 213 0 R 214 0 R]
100008 [200 0 R 201 0 R]
]
>>
endobj
-19 0 obj
+18 0 obj
<<
-/K [62 0 R 105 0 R 165 0 R 200 0 R]
+/K [59 0 R 102 0 R 162 0 R 200 0 R]
/P 4 0 R
/S /Document
>>
endobj
-20 0 obj
+19 0 obj
<<
/G3 215 0 R
/G11 216 0 R
>>
endobj
-21 0 obj
+20 0 obj
<<
/Length 299
/Type /XObject
@@ -373,7 +358,7 @@ x
$>`8MDo*r # )^ T ;T| VsL#>)p_sá42C:I@|0A
endstream
endobj
-23 0 obj
+22 0 obj
<<
/Length 33
/Type /XObject
@@ -416,14 +401,14 @@ stream
endstream
endobj
-24 0 obj
+23 0 obj
<<
/F13 226 0 R
/F25 227 0 R
/F35 228 0 R
>>
endobj
-25 0 obj
+24 0 obj
<<
/Length 5904
/Filter /FlateDecode
@@ -451,12 +436,12 @@ V
d
qrD>(!rEԶ ]2V%cp@%ڋ$mNr6K_h}/~We;YI<"hQyH"9Zf&v`{x_PzEk9X*oL<#@G֣I)⑽φ
N\&z[FLY})梽n=~(RwQݧM^)v{xf4SzdÒ#*=Y0q R*5Y䫼͵`?gWl+&Fs)QMsҁNe;DEzcmf~.-:|8j7wSy'e%ng-b~hh>=>\K|llu^+UɍM܅p-dzݥ;A18( & IՐ.z^;Ug\כ}˧B"X}ܢW\Dl=\cSS
X2BA
endstream
endobj
-26 0 obj
+25 0 obj
<<
/G3 215 0 R
>>
endobj
-27 0 obj
+26 0 obj
<<
/Length 5042
/Type /XObject
@@ -486,7 +471,7 @@ G
dpD)?2C9+3qǖ]{ĢE\w``[T,U_[e3Jw | `Zd
endstream
endobj
-28 0 obj
+27 0 obj
<<
/Length 363979
/Type /XObject
@@ -1880,7 +1865,7 @@ j
mK.-7<][ݪ:_L^۟U_ԃeSAU:JJ˸ڹ|t6k-dW+=V?yeѮsc, t'??}{V*Mv?O~O\M.q٤9efYz*jMA7HY\Fze;Yũ߇^;kjyL? FhÇo:YtsLxtZ2}=~Ÿu!Ύv^&2ƣՐW;~)*4~xܗ|gs :i6\Q../8]_\dtךstFrFrf