chore: add default filters for host and host group queries
- Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class. - Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided. - Added corresponding tests to verify default behavior in host and host group queries. - Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query. - Explained the usage of the '*' wildcard in 'search_name' with a concrete example for subgroup matching.
This commit is contained in:
parent
2a8ff989f3
commit
5e41aa5cc4
5 changed files with 107 additions and 16 deletions
26
.idea/workspace.xml
generated
26
.idea/workspace.xml
generated
|
|
@ -4,17 +4,12 @@
|
||||||
<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: update base host group name from "Baustellen-Devices" to "Roadwork" in code, tests, and documentation">
|
<list default="true" id="d7a71994-2699-4ae4-9fd2-ee13b7f33d35" name="Changes" comment="chore: centralize configuration management using a new `Config` class - Replaced all direct `process.env` references with `Config` class constants. - Added `dotenv` package to manage environment variables. - Updated affected files, including schema loader, Zabbix API, resolvers, logging system, and integration points. - Improved maintainability and consistency in environment variable handling.">
|
||||||
<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$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="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/api/resolvers.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/api/resolvers.ts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/api/schema.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/api/schema.ts" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/common_utils.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/common_utils.ts" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/common_utils.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/common_utils.ts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/datasources/zabbix-api.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/datasources/zabbix-api.ts" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/test/host_query.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/host_query.test.ts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/datasources/zabbix-permissions.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/datasources/zabbix-permissions.ts" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.ts" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/logging/logger.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/logging/logger.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" />
|
||||||
|
|
@ -120,7 +115,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" />
|
||||||
|
|
@ -353,7 +348,15 @@
|
||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1769525714791</updated>
|
<updated>1769525714791</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="20" />
|
<task id="LOCAL-00020" summary="chore: centralize configuration management using a new `Config` class - Replaced all direct `process.env` references with `Config` class constants. - Added `dotenv` package to manage environment variables. - Updated affected files, including schema loader, Zabbix API, resolvers, logging system, and integration points. - Improved maintainability and consistency in environment variable handling.">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1769531308340</created>
|
||||||
|
<option name="number" value="00020" />
|
||||||
|
<option name="presentableId" value="LOCAL-00020" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1769531308340</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="21" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
|
@ -393,7 +396,8 @@
|
||||||
<MESSAGE value="chore: update Docker image path in workflows and README" />
|
<MESSAGE value="chore: update Docker image path in workflows and README" />
|
||||||
<MESSAGE value="docs: add Virtual Control Room (VCR) sample application info to README.md - Added a new section describing the Virtual Control Room (VCR) as a sample application. - Explained how VCR utilizes key API features like hierarchical mapping, dynamic authorization, mass provisioning, and data visualization. - Included a link to the technical product information PDF in the docs folder. - Added VCR to the Key Features list." />
|
<MESSAGE value="docs: add Virtual Control Room (VCR) sample application info to README.md - Added a new section describing the Virtual Control Room (VCR) as a sample application. - Explained how VCR utilizes key API features like hierarchical mapping, dynamic authorization, mass provisioning, and data visualization. - Included a link to the technical product information PDF in the docs folder. - Added VCR to the Key Features list." />
|
||||||
<MESSAGE value="chore: update base host group name from "Baustellen-Devices" to "Roadwork" in code, tests, and documentation" />
|
<MESSAGE value="chore: update base host group name from "Baustellen-Devices" to "Roadwork" in code, tests, and documentation" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="chore: update base host group name from "Baustellen-Devices" to "Roadwork" in code, tests, and documentation" />
|
<MESSAGE value="chore: centralize configuration management using a new `Config` class - Replaced all direct `process.env` references with `Config` class constants. - Added `dotenv` package to manage environment variables. - Updated affected files, including schema loader, Zabbix API, resolvers, logging system, and integration points. - Improved maintainability and consistency in environment variable handling." />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="chore: centralize configuration management using a new `Config` class - Replaced all direct `process.env` references with `Config` class constants. - Added `dotenv` package to manage environment variables. - Updated affected files, including schema loader, Zabbix API, resolvers, logging system, and integration points. - Improved maintainability and consistency in environment variable handling." />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
|
|
|
||||||
52
README.md
52
README.md
|
|
@ -58,6 +58,8 @@ The API is configured via environment variables. Create a `.env` file or set the
|
||||||
| `ZABBIX_EDGE_DEVICE_BASE_GROUP` | Base host group for devices | `Roadwork` |
|
| `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` |
|
| `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/` |
|
| `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
|
### Starting the API
|
||||||
|
|
||||||
|
|
@ -187,6 +189,54 @@ This allows for fine-grained access control in your frontend or external applica
|
||||||
|
|
||||||
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).
|
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.
|
||||||
|
|
@ -214,6 +264,8 @@ ZABBIX_AUTH_TOKEN=your-super-admin-token-here
|
||||||
ZABBIX_EDGE_DEVICE_BASE_GROUP=Roadwork
|
ZABBIX_EDGE_DEVICE_BASE_GROUP=Roadwork
|
||||||
API_VERSION=1.0.0
|
API_VERSION=1.0.0
|
||||||
SCHEMA_PATH=./schema/
|
SCHEMA_PATH=./schema/
|
||||||
|
HOST_GROUP_FILTER_DEFAULT=Roadwork/Devices/*
|
||||||
|
HOST_TYPE_FILTER_DEFAULT=Roadwork/Devices
|
||||||
|
|
||||||
# Schema Extensions (No-Code)
|
# Schema Extensions (No-Code)
|
||||||
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql
|
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,9 @@ export function createResolvers(): Resolvers {
|
||||||
zabbixAuthToken,
|
zabbixAuthToken,
|
||||||
cookie, dataSources
|
cookie, dataSources
|
||||||
}: any) => {
|
}: any) => {
|
||||||
args.tag_hostType ??= [ZABBIX_EDGE_DEVICE_BASE_GROUP];
|
if (Config.HOST_TYPE_FILTER_DEFAULT) {
|
||||||
|
args.tag_hostType ??= [Config.HOST_TYPE_FILTER_DEFAULT];
|
||||||
|
}
|
||||||
return await new ZabbixQueryHostsRequestWithItemsAndInventory(zabbixAuthToken, cookie)
|
return await new ZabbixQueryHostsRequestWithItemsAndInventory(zabbixAuthToken, cookie)
|
||||||
.executeRequestThrowError(
|
.executeRequestThrowError(
|
||||||
dataSources.zabbixAPI, new ParsedArgs(args)
|
dataSources.zabbixAPI, new ParsedArgs(args)
|
||||||
|
|
@ -107,8 +109,9 @@ export function createResolvers(): Resolvers {
|
||||||
zabbixAuthToken,
|
zabbixAuthToken,
|
||||||
cookie, dataSources
|
cookie, dataSources
|
||||||
}: any) => {
|
}: any) => {
|
||||||
args.tag_hostType ??= [ZABBIX_EDGE_DEVICE_BASE_GROUP];
|
if (Config.HOST_TYPE_FILTER_DEFAULT) {
|
||||||
|
args.tag_hostType ??= [Config.HOST_TYPE_FILTER_DEFAULT];
|
||||||
|
}
|
||||||
return await new ZabbixQueryDevices(zabbixAuthToken, cookie)
|
return await new ZabbixQueryDevices(zabbixAuthToken, cookie)
|
||||||
.executeRequestThrowError(
|
.executeRequestThrowError(
|
||||||
dataSources.zabbixAPI, new ZabbixQueryDevicesArgs(args)
|
dataSources.zabbixAPI, new ZabbixQueryDevicesArgs(args)
|
||||||
|
|
@ -118,8 +121,8 @@ export function createResolvers(): Resolvers {
|
||||||
zabbixAuthToken,
|
zabbixAuthToken,
|
||||||
cookie
|
cookie
|
||||||
}: any) => {
|
}: any) => {
|
||||||
if (!args.search_name) {
|
if (!args.search_name && Config.HOST_GROUP_FILTER_DEFAULT) {
|
||||||
args.search_name = ZABBIX_EDGE_DEVICE_BASE_GROUP + "/*"
|
args.search_name = Config.HOST_GROUP_FILTER_DEFAULT
|
||||||
}
|
}
|
||||||
return await new ZabbixQueryHostgroupsRequest(zabbixAuthToken, cookie).executeRequestThrowError(
|
return await new ZabbixQueryHostgroupsRequest(zabbixAuthToken, cookie).executeRequestThrowError(
|
||||||
zabbixAPI, new ZabbixQueryHostgroupsParams(args)
|
zabbixAPI, new ZabbixQueryHostgroupsParams(args)
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,6 @@ static readonly DRY_RUN = process.env.DRY_RUN
|
||||||
static readonly ZABBIX_ROADWORK_BASE_GROUP = process.env.ZABBIX_ROADWORK_BASE_GROUP
|
static readonly ZABBIX_ROADWORK_BASE_GROUP = process.env.ZABBIX_ROADWORK_BASE_GROUP
|
||||||
static readonly ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX = process.env.ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX || "Permissions"
|
static readonly ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX = process.env.ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX || "Permissions"
|
||||||
static readonly LOG_LEVELS = process.env.LOG_LEVELS
|
static readonly LOG_LEVELS = process.env.LOG_LEVELS
|
||||||
|
static readonly HOST_TYPE_FILTER_DEFAULT = process.env.HOST_TYPE_FILTER_DEFAULT;
|
||||||
|
static readonly HOST_GROUP_FILTER_DEFAULT = process.env.HOST_GROUP_FILTER_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import {createResolvers} from "../api/resolvers.js";
|
import {createResolvers} from "../api/resolvers.js";
|
||||||
import {zabbixAPI, ZABBIX_EDGE_DEVICE_BASE_GROUP} from "../datasources/zabbix-api.js";
|
import {zabbixAPI, ZABBIX_EDGE_DEVICE_BASE_GROUP} from "../datasources/zabbix-api.js";
|
||||||
import {QueryAllHostsArgs, QueryAllDevicesArgs, QueryAllHostGroupsArgs} from "../schema/generated/graphql.js";
|
import {QueryAllHostsArgs, QueryAllDevicesArgs, QueryAllHostGroupsArgs} from "../schema/generated/graphql.js";
|
||||||
|
import {Config} from "../common_utils.js";
|
||||||
|
|
||||||
// Mocking ZabbixAPI
|
// Mocking ZabbixAPI
|
||||||
jest.mock("../datasources/zabbix-api.js", () => ({
|
jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
|
|
@ -14,6 +15,14 @@ jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
ZABBIX_EDGE_DEVICE_BASE_GROUP: "Roadwork"
|
ZABBIX_EDGE_DEVICE_BASE_GROUP: "Roadwork"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Mocking Config
|
||||||
|
jest.mock("../common_utils.js", () => ({
|
||||||
|
Config: {
|
||||||
|
HOST_TYPE_FILTER_DEFAULT: "Roadwork",
|
||||||
|
HOST_GROUP_FILTER_DEFAULT: "Roadwork/%"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
describe("Host and HostGroup Resolvers", () => {
|
describe("Host and HostGroup Resolvers", () => {
|
||||||
let resolvers: any;
|
let resolvers: any;
|
||||||
|
|
||||||
|
|
@ -94,6 +103,27 @@ describe("Host and HostGroup Resolvers", () => {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("allHostGroups query - uses default search pattern", async () => {
|
||||||
|
const mockGroups = [{ groupid: "10", name: "Roadwork/Group 1" }];
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce(mockGroups);
|
||||||
|
|
||||||
|
const args: QueryAllHostGroupsArgs = {};
|
||||||
|
const context = {
|
||||||
|
zabbixAuthToken: "test-token"
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await resolvers.Query.allHostGroups(null, args, context);
|
||||||
|
|
||||||
|
expect(result).toEqual(mockGroups);
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("hostgroup.get", expect.objectContaining({
|
||||||
|
body: expect.objectContaining({
|
||||||
|
params: expect.objectContaining({
|
||||||
|
search: { name: ["Roadwork/%"] }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
test("locations query", async () => {
|
test("locations query", async () => {
|
||||||
const mockLocations = [{ name: "Loc 1", latitude: 1.0, longitude: 2.0 }];
|
const mockLocations = [{ name: "Loc 1", latitude: 1.0, longitude: 2.0 }];
|
||||||
(zabbixAPI.getLocations as jest.Mock).mockResolvedValueOnce(mockLocations);
|
(zabbixAPI.getLocations as jest.Mock).mockResolvedValueOnce(mockLocations);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue