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`.
This commit is contained in:
Andreas Hilbig 2026-01-30 00:47:02 +01:00
parent 2a82fe6cf2
commit 4ec61ffba1
33 changed files with 439 additions and 165 deletions

65
.idea/workspace.xml generated
View file

@ -4,9 +4,30 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="d7a71994-2699-4ae4-9fd2-ee13b7f33d35" name="Changes" comment="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file"> <list default="true" id="d7a71994-2699-4ae4-9fd2-ee13b7f33d35" name="Changes" comment="chore: update `.idea/workspace.xml` and project guidelines &#10;&#10;- Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. &#10;- Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows.">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.junie/guidelines.md" beforeDir="false" afterPath="$PROJECT_DIR$/.junie/guidelines.md" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.junie/guidelines.md" beforeDir="false" afterPath="$PROJECT_DIR$/.junie/guidelines.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_all_devices_query.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_all_host_groups_query.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_all_hosts_query.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_all_template_groups_query.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_delete_template_groups_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_delete_templates_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_export_user_rights_query.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_import_distance_tracker_template.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_import_host_groups_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_import_host_template_groups_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_import_hosts_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_import_permissions_template_groups_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_import_templates_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_import_user_rights_mutation.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/docs/sample_templates_query.graphql" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/api/resolvers.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/api/resolvers.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/datasources/zabbix-hostgroups.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/datasources/zabbix-hostgroups.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/test/host_integration.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/host_integration.test.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/test/template_integration.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/template_integration.test.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/test/user_rights_integration.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/user_rights_integration.test.ts" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -17,7 +38,7 @@
<execution /> <execution />
</component> </component>
<component name="EmbeddingIndexingInfo"> <component name="EmbeddingIndexingInfo">
<option name="cachedIndexableFilesCount" value="70" /> <option name="cachedIndexableFilesCount" value="106" />
<option name="fileBasedEmbeddingIndicesEnabled" value="true" /> <option name="fileBasedEmbeddingIndicesEnabled" value="true" />
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
@ -29,6 +50,7 @@
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
<option name="RESET_MODE" value="HARD" /> <option name="RESET_MODE" value="HARD" />
<option name="SWAP_SIDES_IN_COMPARE_BRANCHES" value="true" /> <option name="SWAP_SIDES_IN_COMPARE_BRANCHES" value="true" />
<option name="UPDATE_TYPE" value="REBASE" />
</component> </component>
<component name="GitRewordedCommitMessages"> <component name="GitRewordedCommitMessages">
<option name="commitMessagesMapping"> <option name="commitMessagesMapping">
@ -90,8 +112,8 @@
"npm.copy-schema.executor": "Run", "npm.copy-schema.executor": "Run",
"npm.prod.executor": "Run", "npm.prod.executor": "Run",
"npm.test.executor": "Run", "npm.test.executor": "Run",
"settings.editor.selected.configurable": "ml.llm.LLMConfigurable", "settings.editor.selected.configurable": "junie.mcp",
"settings.editor.splitter.proportion": "0.3839406", "settings.editor.splitter.proportion": "0.28812414",
"to.speed.mode.migration.done": "true", "to.speed.mode.migration.done": "true",
"ts.external.directory.path": "\\\\wsl.localhost\\Ubuntu\\home\\ahilbig\\git\\vcr\\zabbix-graphql-api\\node_modules\\typescript\\lib", "ts.external.directory.path": "\\\\wsl.localhost\\Ubuntu\\home\\ahilbig\\git\\vcr\\zabbix-graphql-api\\node_modules\\typescript\\lib",
"vue.rearranger.settings.migration": "true" "vue.rearranger.settings.migration": "true"
@ -116,7 +138,7 @@
<recent name="\\wsl.localhost\Ubuntu\home\ahilbig\git\vcr\zabbix-graphql-api\schema" /> <recent name="\\wsl.localhost\Ubuntu\home\ahilbig\git\vcr\zabbix-graphql-api\schema" />
</key> </key>
</component> </component>
<component name="RunManager" selected="npm.test"> <component name="RunManager" selected="Node.js.index.ts">
<configuration name="copy-schema" type="js.build_tools.npm" temporary="true" nameIsGenerated="true"> <configuration name="copy-schema" type="js.build_tools.npm" temporary="true" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" /> <package-json value="$PROJECT_DIR$/package.json" />
<command value="run" /> <command value="run" />
@ -179,7 +201,7 @@
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-WS-253.29346.242" /> <option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-WS-253.30387.83" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>
@ -196,6 +218,9 @@
<workItem from="1768913192173" duration="14627000" /> <workItem from="1768913192173" duration="14627000" />
<workItem from="1769095609607" duration="1390000" /> <workItem from="1769095609607" duration="1390000" />
<workItem from="1769256682556" duration="8928000" /> <workItem from="1769256682556" duration="8928000" />
<workItem from="1769699975260" duration="75000" />
<workItem from="1769700092648" duration="5212000" />
<workItem from="1769724930397" duration="3476000" />
</task> </task>
<task id="LOCAL-00001" summary="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment"> <task id="LOCAL-00001" summary="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment">
<option name="closed" value="true" /> <option name="closed" value="true" />
@ -373,7 +398,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1769582068426</updated> <updated>1769582068426</updated>
</task> </task>
<option name="localTasksCounter" value="23" /> <task id="LOCAL-00023" summary="chore: update `.idea/workspace.xml` and project guidelines &#10;&#10;- Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. &#10;- Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows.">
<option name="closed" value="true" />
<created>1769597550348</created>
<option name="number" value="00023" />
<option name="presentableId" value="LOCAL-00023" />
<option name="project" value="LOCAL" />
<updated>1769597550348</updated>
</task>
<option name="localTasksCounter" value="24" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@ -384,14 +417,25 @@
<map> <map>
<entry key="MAIN"> <entry key="MAIN">
<value> <value>
<State /> <State>
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="public/main" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value> </value>
</entry> </entry>
</map> </map>
</option> </option>
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="refactor!: Update Node.js version to 24.12.0, enhance GraphQL schema structure, and improve dynamic schema loading logic" />
<MESSAGE value="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment" /> <MESSAGE value="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment" />
<MESSAGE value="chore: Add missing &quot;.js&quot; extensions to imports and improve Node.js compatibility for dynamic schema loading" /> <MESSAGE value="chore: Add missing &quot;.js&quot; extensions to imports and improve Node.js compatibility for dynamic schema loading" />
<MESSAGE value="chore: Update IntelliJ workspace and fix Docker image tag in workflow" /> <MESSAGE value="chore: Update IntelliJ workspace and fix Docker image tag in workflow" />
@ -416,7 +460,8 @@
<MESSAGE value="chore: add default filters for host and host group queries&#10;&#10;- Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class.&#10;- Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided.&#10;- Added corresponding tests to verify default behavior in host and host group queries.&#10;- Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query.&#10;- Explained the usage of the '*' wildcard in 'search_name' with a concrete example for subgroup matching." /> <MESSAGE value="chore: add default filters for host and host group queries&#10;&#10;- Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class.&#10;- Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided.&#10;- Added corresponding tests to verify default behavior in host and host group queries.&#10;- Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query.&#10;- Explained the usage of the '*' wildcard in 'search_name' with a concrete example for subgroup matching." />
<MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files" /> <MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files" />
<MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file" /> <MESSAGE value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file" />
<option name="LAST_COMMIT_MESSAGE" value="chore: add tests for schema and API config mocking&#10;&#10;- Added unit tests for schema loader, mocking Config variables and resolvers.&#10;- Added unit tests for Zabbix API configuration, verifying constants derived from Config.&#10;- Mocked relevant modules and filesystem behaviors to enable isolated testing.&#10;- Optimized imports on all files and include this within a new .junie/guidelines.md file" /> <MESSAGE value="chore: update `.idea/workspace.xml` and project guidelines &#10;&#10;- Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. &#10;- Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows." />
<option name="LAST_COMMIT_MESSAGE" value="chore: update `.idea/workspace.xml` and project guidelines &#10;&#10;- Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. &#10;- Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows." />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" /> <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">

View file

@ -2,6 +2,11 @@
This document provides concise information and best practices for developers working on the Zabbix GraphQL API project. This document provides concise information and best practices for developers working on the Zabbix GraphQL API project.
### Environment
- **Operating System:** Windows with WSL + Ubuntu installed.
- **Commands:** Always execute Linux commands (e.g., use `ls` instead of `dir`).
## Tech Stack ## Tech Stack
- **Runtime**: Node.js (v18+) - **Runtime**: Node.js (v18+)
- **Language**: TypeScript (ESM) - **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`. - **Modular Datasources**: When adding support for new Zabbix features, create a new datasource class in `src/datasources/` extending `ZabbixRESTDataSource`.
- **Schema Organization**: Place GraphQL SDL files in the `schema/` directory. Use descriptive comments in SDL as they are used for API documentation. - **Schema Organization**: Place GraphQL SDL files in the `schema/` directory. Use descriptive comments in SDL as they are used for API documentation.
- **Testing**: Write reproduction tests for bugs and cover new features with both unit and integration tests in `src/test/`. - **Testing**: Write reproduction tests for bugs and cover new features with both unit and integration tests in `src/test/`.
### Git Standards
- **Commit Messages:** Use [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `style:`).

157
README.md
View file

@ -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. * **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. * **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. * **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. * **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 ## How to Install and Start
### Prerequisites ### Prerequisites
@ -117,126 +130,6 @@ If you prefer to build the image yourself using the provided `Dockerfile`:
zabbix-graphql-api 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) ## 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. 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 ## 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**: * **Hosts**:
* [Query All Hosts](docs/sample_all_hosts_query.graphql) * [Query All Hosts](docs/queries/sample_all_hosts_query.graphql)
* [Import Hosts](docs/sample_import_hosts_mutation.graphql) * [Import Hosts](docs/queries/sample_import_hosts_mutation.graphql)
* **Templates**: * **Templates**:
* [Query Templates](docs/sample_templates_query.graphql) * [Query Templates](docs/queries/sample_templates_query.graphql)
* [Import Templates](docs/sample_import_templates_mutation.graphql) * [Import Templates](docs/queries/sample_import_templates_mutation.graphql)
* [Import Distance Tracker Template](docs/sample_import_distance_tracker_template.graphql) (Schema Extension Example) * [Import Distance Tracker Template](docs/queries/sample_import_distance_tracker_template.graphql) (Schema Extension Example)
* [Delete Templates](docs/sample_delete_templates_mutation.graphql) * [Delete Templates](docs/queries/sample_delete_templates_mutation.graphql)
* **Template Groups**: * **Template Groups**:
* [Import Host Template Groups](docs/sample_import_host_template_groups_mutation.graphql) * [Import Host Template Groups](docs/queries/sample_import_host_template_groups_mutation.graphql)
* [Import Permissions Template Groups](docs/sample_import_permissions_template_groups_mutation.graphql) * [Import Permissions Template Groups](docs/queries/sample_import_permissions_template_groups_mutation.graphql)
* [Delete Template Groups](docs/sample_delete_template_groups_mutation.graphql) * [Delete Template Groups](docs/queries/sample_delete_template_groups_mutation.graphql)
* **User Rights**: * **User Rights**:
* [Export User Rights](docs/sample_export_user_rights_query.graphql) * [Export User Rights](docs/queries/sample_export_user_rights_query.graphql)
* [Import User Rights](docs/sample_import_user_rights_mutation.graphql) * [Import User Rights](docs/queries/sample_import_user_rights_mutation.graphql)
## License ## License

27
docker-compose.yml Normal file
View file

@ -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

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

@ -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.

View file

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

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

@ -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.

View file

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

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

@ -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

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

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

20
mcp-config.yaml Normal file
View file

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

View file

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

View file

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

View file

@ -4,14 +4,15 @@ import {
DeviceStatus, DeviceStatus,
Host, Host,
MutationCreateHostArgs, MutationCreateHostArgs,
MutationDeleteTemplateGroupsArgs,
MutationDeleteTemplatesArgs,
MutationImportHostGroupsArgs, MutationImportHostGroupsArgs,
MutationImportHostsArgs, MutationImportHostsArgs,
MutationImportTemplateGroupsArgs, MutationImportTemplateGroupsArgs,
MutationImportTemplatesArgs, MutationImportTemplatesArgs,
MutationDeleteTemplatesArgs,
MutationDeleteTemplateGroupsArgs,
MutationImportUserRightsArgs, MutationImportUserRightsArgs,
Permission, QueryAllDevicesArgs, Permission,
QueryAllDevicesArgs,
QueryAllHostGroupsArgs, QueryAllHostGroupsArgs,
QueryAllHostsArgs, QueryAllHostsArgs,
QueryExportHostValueHistoryArgs, QueryExportHostValueHistoryArgs,
@ -31,7 +32,8 @@ import {logger} from "../logging/logger.js";
import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js"; import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js";
import { import {
ZabbixCreateHostRequest, ZabbixCreateHostRequest,
ZabbixQueryDevices, ZabbixQueryDevicesArgs, ZabbixQueryDevices,
ZabbixQueryDevicesArgs,
ZabbixQueryHostsRequestWithItemsAndInventory, ZabbixQueryHostsRequestWithItemsAndInventory,
} from "../datasources/zabbix-hosts.js"; } from "../datasources/zabbix-hosts.js";
import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js"; import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js";
@ -46,15 +48,8 @@ import {
ZabbixImportUserRolesRequest, ZabbixImportUserRolesRequest,
ZabbixQueryUserRolesRequest ZabbixQueryUserRolesRequest
} from "../datasources/zabbix-userroles.js"; } from "../datasources/zabbix-userroles.js";
import { import {ZabbixQueryTemplateGroupRequest, ZabbixQueryTemplatesRequest} from "../datasources/zabbix-templates.js";
ZabbixCreateItemRequest, import {zabbixAPI} from "../datasources/zabbix-api.js";
ZabbixCreateTemplateGroupRequest,
ZabbixCreateTemplateRequest,
ZabbixQueryItemRequest,
ZabbixQueryTemplateGroupRequest,
ZabbixQueryTemplatesRequest
} from "../datasources/zabbix-templates.js";
import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api.js";
import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js"; import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js";
import {isDevice} from "./resolver_helpers.js"; import {isDevice} from "./resolver_helpers.js";
import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js"; import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js";

View file

@ -16,7 +16,7 @@ export interface CreateHostGroupResult {
const hostGroupReadWritePermissions = { const hostGroupReadWritePermissions = {
permissions: [ permissions: [
{ {
objectName: "Hostgroup/ConstructionSite", objectName: "Hostgroup",
permission: Permission.ReadWrite permission: Permission.ReadWrite
}] }]
} }

View file

@ -27,7 +27,7 @@ describe("Host Integration Tests", () => {
}); });
test("Query allHosts using sample", async () => { 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 mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
const variablesMatch = sampleFile.match(/```json\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 () => { 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 mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/); const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);

View file

@ -24,7 +24,7 @@ describe("Template Integration Tests", () => {
}); });
test("Import templates using sample query and variables", async () => { 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 // Extract mutation and variables from the doc file
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/); const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
@ -65,7 +65,7 @@ describe("Template Integration Tests", () => {
test("Import and Export templates comparison", async () => { test("Import and Export templates comparison", async () => {
// 1. Import // 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 importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
const importVariables = JSON.parse(importSample.match(/```json\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) // 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 query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1];
const queryVariables = JSON.parse(querySample.match(/```json\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 () => { test("Import and Export template groups comparison", async () => {
// 1. Import (Host Template Groups) // 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 importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
const importVariables = JSON.parse(importSample.match(/```json\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); expect(importResult.data.importTemplateGroups).toHaveLength(importVariables.templateGroups.length);
// 2. Import (Permissions Template Groups) // 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 permImportMutation = permImportSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
const permImportVariables = JSON.parse(permImportSample.match(/```json\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); expect(permImportResult.data.importTemplateGroups).toHaveLength(permImportVariables.templateGroups.length);
// 3. Export (Query) // 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 query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1];
const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]); const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]);

View file

@ -26,7 +26,7 @@ describe("User Rights Integration Tests", () => {
}); });
test("Import user rights using sample", async () => { 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 mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/); const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);