diff --git a/src/datasource/components/ItemCountWarning.tsx b/src/datasource/components/ItemCountWarning.tsx new file mode 100644 index 0000000..cf80aec --- /dev/null +++ b/src/datasource/components/ItemCountWarning.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Alert } from '@grafana/ui'; +import { ITEM_COUNT_WARNING_THRESHOLD } from '../constants'; + +interface ItemCountWarningProps { + itemCount: number; + threshold?: number; +} + +/** + * A warning banner that displays when the query matches too many items. + * This is a non-intrusive warning that doesn't block the query flow. + */ +export const ItemCountWarning: React.FC = ({ + itemCount, + threshold = ITEM_COUNT_WARNING_THRESHOLD, +}) => { + if (itemCount < threshold) { + return null; + } + + return ( + + This query matches {itemCount} items and may return a large amount of data. Consider using more specific filters. + + ); +}; diff --git a/src/datasource/components/QueryEditor.tsx b/src/datasource/components/QueryEditor.tsx index 111b1ad..646ec71 100644 --- a/src/datasource/components/QueryEditor.tsx +++ b/src/datasource/components/QueryEditor.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { QueryEditorProps } from '@grafana/data'; import { Combobox, ComboboxOption, InlineField, Stack } from '@grafana/ui'; import * as c from '../constants'; @@ -16,6 +16,7 @@ import { ServicesQueryEditor } from './QueryEditor/ServicesQueryEditor'; import { TriggersQueryEditor } from './QueryEditor/TriggersQueryEditor'; import { UserMacrosQueryEditor } from './QueryEditor/UserMacrosQueryEditor'; import { QueryEditorRow } from './QueryEditor/QueryEditorRow'; +import { ItemCountWarning } from './ItemCountWarning'; const zabbixQueryTypeOptions: Array> = [ { @@ -113,6 +114,7 @@ export interface ZabbixQueryEditorProps extends QueryEditorProps {} export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: ZabbixQueryEditorProps) => { + const [itemCount, setItemCount] = useState(0); const queryDefaults = getDefaultQuery(); query = { ...queryDefaults, ...query }; query.options = { ...queryDefaults.options, ...query.options }; @@ -152,7 +154,12 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: ZabbixQ const renderMetricsEditor = () => { return ( <> - + ); @@ -198,6 +205,7 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: ZabbixQ return ( + {queryType === c.MODE_METRICS && } diff --git a/src/datasource/components/QueryEditor/MetricsQueryEditor.tsx b/src/datasource/components/QueryEditor/MetricsQueryEditor.tsx index d416994..2907103 100644 --- a/src/datasource/components/QueryEditor/MetricsQueryEditor.tsx +++ b/src/datasource/components/QueryEditor/MetricsQueryEditor.tsx @@ -16,9 +16,10 @@ export interface Props { query: ZabbixMetricsQuery; datasource: ZabbixDatasource; onChange: (query: ZabbixMetricsQuery) => void; + onItemCountChange?: (count: number) => void; } -export const MetricsQueryEditor = ({ query, datasource, onChange }: Props) => { +export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountChange }: Props) => { const interpolatedQuery = useInterpolatedQuery(datasource, query); const loadGroupOptions = async () => { @@ -94,12 +95,30 @@ export const MetricsQueryEditor = ({ query, datasource, onChange }: Props) => { return options; }, [interpolatedQuery.group.filter, interpolatedQuery.host.filter]); - const loadItemOptions = async (group: string, host: string, app: string, itemTag: string) => { + const loadItemOptions = async (group: string, host: string, app: string, itemTag: string, itemFilter: string) => { const options = { itemtype: 'num', showDisabledItems: query.options.showDisabledItems, }; const items = await datasource.zabbix.getAllItems(group, host, app, itemTag, options); + + // Count items that match the current item filter for the warning + let matchingItemCount = items?.length || 0; + if (itemFilter && items?.length) { + // If there's an item filter, count how many items match it + const filterRegex = + itemFilter.startsWith('/') && itemFilter.endsWith('/') ? new RegExp(itemFilter.slice(1, -1)) : null; + if (filterRegex) { + matchingItemCount = items.filter((item) => filterRegex.test(item.name)).length; + } else if (itemFilter) { + // Exact match or partial match + matchingItemCount = items.filter((item) => item.name === itemFilter || item.name.includes(itemFilter)).length; + } + } + + // Report the matching item count + onItemCountChange?.(matchingItemCount); + let itemOptions: Array> = items?.map((item) => ({ value: item.name, label: item.name, @@ -114,7 +133,8 @@ export const MetricsQueryEditor = ({ query, datasource, onChange }: Props) => { interpolatedQuery.group.filter, interpolatedQuery.host.filter, interpolatedQuery.application.filter, - interpolatedQuery.itemTag.filter + interpolatedQuery.itemTag.filter, + interpolatedQuery.item.filter ); return options; }, [ @@ -122,6 +142,7 @@ export const MetricsQueryEditor = ({ query, datasource, onChange }: Props) => { interpolatedQuery.host.filter, interpolatedQuery.application.filter, interpolatedQuery.itemTag.filter, + interpolatedQuery.item.filter, ]); // Update suggestions on every metric change @@ -129,6 +150,7 @@ export const MetricsQueryEditor = ({ query, datasource, onChange }: Props) => { const hostFilter = interpolatedQuery.host?.filter; const appFilter = interpolatedQuery.application?.filter; const tagFilter = interpolatedQuery.itemTag?.filter; + const itemFilter = interpolatedQuery.item?.filter; useEffect(() => { fetchGroups(); @@ -148,7 +170,7 @@ export const MetricsQueryEditor = ({ query, datasource, onChange }: Props) => { useEffect(() => { fetchItems(); - }, [groupFilter, hostFilter, appFilter, tagFilter]); + }, [groupFilter, hostFilter, appFilter, tagFilter, itemFilter]); const onFilterChange = (prop: string) => { return (value: string) => { diff --git a/src/datasource/constants.ts b/src/datasource/constants.ts index a087a2c..29d4e98 100644 --- a/src/datasource/constants.ts +++ b/src/datasource/constants.ts @@ -49,3 +49,5 @@ export const MIN_SLA_INTERVAL = 3600; export const RANGE_VARIABLE_VALUE = 'range_series'; export const DEFAULT_ZABBIX_PROBLEMS_LIMIT = 1001; + +export const ITEM_COUNT_WARNING_THRESHOLD = 500;