Skip to content

Commit 516972d

Browse files
openshift-cherrypick-robotjpinsonneaujotak
authored
[release-1.11] NETOBSERV-2566 & NETOBSERV-2604 selecting "either" removes the filter (#1266)
* handle endpoint filters for prometheus only * improve default filter & direction handling * Apply suggestion from @jotak --------- Co-authored-by: Julien Pinsonneau <jpinsonn@redhat.com> Co-authored-by: Joel Takvorian <joel.takvorian@homeblocks.net>
1 parent e640e0b commit 516972d

File tree

9 files changed

+599
-21
lines changed

9 files changed

+599
-21
lines changed

web/src/components/netflow-traffic.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,12 @@ export const NetflowTraffic: React.FC<NetflowTrafficProps> = ({
195195
}, [getAvailableColumns]);
196196

197197
const getFilterDefs = React.useCallback(() => {
198-
return getFilterDefinitions(model.config.filters, model.config.columns, t).filter(fd => {
198+
const allFilterDefs = getFilterDefinitions(model.config.filters, model.config.columns, t);
199+
return allFilterDefs.filter(fd => {
199200
if (fd.id === 'id') {
200201
return isConnectionTracking();
201202
}
202-
return checkFilterAvailable(fd, model.config, model.dataSource);
203+
return checkFilterAvailable(fd, model.config, model.dataSource, allFilterDefs);
203204
});
204205
// eslint-disable-next-line react-hooks/exhaustive-deps
205206
}, [model.config, model.dataSource]);

web/src/components/toolbar/filters-toolbar.tsx

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,57 @@ export const FiltersToolbar: React.FC<FiltersToolbarProps> = ({
6464

6565
const [searchInputValue, setSearchInputValue] = React.useState('');
6666

67-
const [direction, setDirection] = React.useState<Direction>();
68-
const [filter, setFilter] = React.useState<FilterDefinition | null>(
69-
findFilter(filterDefinitions, 'src_namespace') || filterDefinitions.length ? filterDefinitions[0] : null
67+
const getDefaultFilter = React.useCallback((defs: FilterDefinition[]): FilterDefinition => {
68+
// Parent guarantees non-empty definitions
69+
return findFilter(defs, 'src_namespace') || defs[0];
70+
}, []);
71+
72+
const getDefaultDirection = React.useCallback((filter: FilterDefinition): Direction => {
73+
// Set direction based on filter's category or id prefix
74+
if (filter.category === 'source' || filter.id.startsWith('src_')) {
75+
return 'source';
76+
} else if (filter.category === 'destination' || filter.id.startsWith('dst_')) {
77+
return 'destination';
78+
}
79+
return undefined;
80+
}, []);
81+
82+
const [filter, setFilter] = React.useState<FilterDefinition>(() => getDefaultFilter(filterDefinitions));
83+
const [direction, setDirection] = React.useState<Direction>(() =>
84+
getDefaultDirection(getDefaultFilter(filterDefinitions))
7085
);
7186
const [compare, setCompare] = React.useState<FilterCompare>(FilterCompare.equal);
7287
const [value, setValue] = React.useState<string>('');
7388

7489
const [showFilters, setShowFilters] = useLocalStorage<boolean>(localStorageShowFiltersKey, true);
7590

91+
// Safe filter setter that validates the filter exists in filterDefinitions
92+
const setSafeFilter = React.useCallback(
93+
(f: FilterDefinition | null | undefined) => {
94+
if (!f) {
95+
// If null/undefined, use default filter
96+
setFilter(getDefaultFilter(filterDefinitions));
97+
} else if (filterDefinitions.find(fd => fd.id === f.id)) {
98+
// If filter exists in definitions, use it
99+
setFilter(f);
100+
} else {
101+
// If filter doesn't exist, fallback to default
102+
setFilter(getDefaultFilter(filterDefinitions));
103+
}
104+
},
105+
[filterDefinitions, getDefaultFilter]
106+
);
107+
108+
// Update filter if filterDefinitions changes and current filter is no longer valid
109+
React.useEffect(() => {
110+
if (!filterDefinitions.find(fd => fd.id === filter.id)) {
111+
const newFilter = getDefaultFilter(filterDefinitions);
112+
setFilter(newFilter);
113+
// Sync direction with the new filter
114+
setDirection(getDefaultDirection(newFilter));
115+
}
116+
}, [filterDefinitions, filter, getDefaultFilter, getDefaultDirection]);
117+
76118
// reset and delay message state to trigger tooltip properly
77119
const setMessageWithDelay = React.useCallback(
78120
(m: string | undefined) => {
@@ -93,9 +135,6 @@ export const FiltersToolbar: React.FC<FiltersToolbarProps> = ({
93135
}, []);
94136

95137
const getFilterToolbar = React.useCallback(() => {
96-
if (!filter) {
97-
return <></>;
98-
}
99138
return (
100139
<ToolbarItem className="flex-start">
101140
<Tooltip
@@ -118,7 +157,7 @@ export const FiltersToolbar: React.FC<FiltersToolbarProps> = ({
118157
value={value}
119158
setValue={setValue}
120159
setCompare={setCompare}
121-
setFilter={setFilter}
160+
setFilter={setSafeFilter}
122161
setDirection={setDirection}
123162
setIndicator={setIndicator}
124163
searchInputValue={searchInputValue}
@@ -141,6 +180,7 @@ export const FiltersToolbar: React.FC<FiltersToolbarProps> = ({
141180
searchInputValue,
142181
setFilters,
143182
setMessageWithDelay,
183+
setSafeFilter,
144184
value
145185
]);
146186

web/src/components/toolbar/filters/filter-search-input.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export interface FilterSearchInputProps {
4848
value: string;
4949
setValue: (v: string) => void;
5050
setCompare: (v: FilterCompare) => void;
51-
setFilter: (v: FilterDefinition) => void;
51+
setFilter: (v: FilterDefinition | null | undefined) => void;
5252
setDirection: (v: Direction) => void;
5353
setIndicator: (v: Indicator) => void;
5454
setSearchInputValue: (v: string) => void;

web/src/components/toolbar/filters/filter-search-panel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface FilterSearchPanelProps {
2020
direction: Direction;
2121
setDirection: (direction: Direction) => void;
2222
filter: FilterDefinition;
23-
setFilter: (filter: FilterDefinition) => void;
23+
setFilter: (filter: FilterDefinition | null | undefined) => void;
2424
filters?: Filters;
2525
compare: FilterCompare;
2626
setCompare: (compare: FilterCompare) => void;

web/src/components/toolbar/filters/filters-dropdown.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export interface FiltersDropdownProps {
1616
selectedDirection: Direction;
1717
setSelectedDirection: (v: Direction) => void;
1818
selectedFilter: FilterDefinition;
19-
setSelectedFilter: (f: FilterDefinition) => void;
19+
setSelectedFilter: (f: FilterDefinition | null | undefined) => void;
2020
match?: Match;
2121
}
2222

@@ -79,7 +79,7 @@ export const FiltersDropdown: React.FC<FiltersDropdownProps> = ({
7979
}
8080
} else if (selectedFilter.id.startsWith('src_') || selectedFilter.id.startsWith('dst_')) {
8181
const id = selectedFilter.id.replace('src_', '').replace('dst_', '') as FilterId;
82-
setSelectedFilter(findFilter(filterDefinitions, id)!);
82+
setSelectedFilter(findFilter(filterDefinitions, id));
8383
}
8484
}, [filterDefinitions, selectedDirection, selectedFilter, setSelectedDirection, setSelectedFilter]);
8585
return (

web/src/utils/__tests__/filter-definitions.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,48 @@ describe('Check availability against features', () => {
205205
expect(available).toBe(true);
206206
});
207207
});
208+
209+
describe('Check endpoint filters availability with Prometheus only', () => {
210+
const getConfig = (promLabels: string[], dataSources: string[] = ['prom']): Config => {
211+
return { promLabels, dataSources, columns: ColumnConfigSampleDefs, filters: FilterConfigSampleDefs } as Config;
212+
};
213+
214+
it('should be available when both src_ and dst_ variants are available', () => {
215+
const namespaceFilter = findFilter(FilterDefinitionSample, 'namespace')!;
216+
const nameFilter = findFilter(FilterDefinitionSample, 'name')!;
217+
218+
// Both src and dst variants have their labels available
219+
const available = checkFilterAvailable(
220+
namespaceFilter,
221+
getConfig(['SrcK8S_Namespace', 'DstK8S_Namespace']),
222+
'prom',
223+
FilterDefinitionSample
224+
);
225+
expect(available).toBe(true);
226+
227+
const nameAvailable = checkFilterAvailable(
228+
nameFilter,
229+
getConfig(['SrcK8S_Name', 'DstK8S_Name']),
230+
'prom',
231+
FilterDefinitionSample
232+
);
233+
expect(nameAvailable).toBe(true);
234+
});
235+
236+
it('should not be available when variants are missing', () => {
237+
const namespaceFilter = findFilter(FilterDefinitionSample, 'namespace')!;
238+
239+
// Neither src_ nor dst_ variant available
240+
const available = checkFilterAvailable(namespaceFilter, getConfig([]), 'prom', FilterDefinitionSample);
241+
expect(available).toBe(false);
242+
});
243+
244+
it('should be available with Loki enabled', () => {
245+
const namespaceFilter = findFilter(FilterDefinitionSample, 'namespace')!;
246+
247+
// With Loki, endpoint filters follow the default behavior
248+
const available = checkFilterAvailable(namespaceFilter, getConfig([], ['loki']), 'auto', FilterDefinitionSample);
249+
// Should fall through to default "allow by default" behavior
250+
expect(available).toBe(true);
251+
});
252+
});

0 commit comments

Comments
 (0)