Skip to content

Commit d6fa86b

Browse files
authored
NETOBSERV-2441: fix page loading triggering autocompletion (#1060)
There was an abusive use of the autocompletion mechanism in filters. It's primarily used to get a list of filter options out of a search value. But it was also used, during page loading, to transform a filter textual value (read from URL) into a normalized filter option. This is bad, because triggers unnecessary API calls on page load. The change consists in splitting concerns between autocompletion and text mapping to normalized filter option.
1 parent 7ad804c commit d6fa86b

File tree

6 files changed

+174
-121
lines changed

6 files changed

+174
-121
lines changed

web/src/components/drawer/record/record-panel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ export const RecordPanel: React.FC<RecordDrawerProps> = ({
235235
const values = [
236236
{
237237
v: Array.isArray(value) ? value.join(value.length == 2 ? '.' : ':') : valueStr,
238-
display: (await def.getOptions(valueStr)).find(o => o.value === valueStr)?.name
238+
display: (await def.autocomplete(valueStr)).find(o => o.value === valueStr)?.name
239239
}
240240
];
241241
// TODO: is it relevant to show composed columns?

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export const AutocompleteFilter: React.FC<AutocompleteFilterProps> = ({
8686
(newValue: string) => {
8787
setCurrentValue(newValue);
8888
filterDefinition
89-
.getOptions(newValue)
89+
.autocomplete(newValue)
9090
.then(setOptions)
9191
.catch(err => {
9292
const errorMessage = getHTTPErrorDetails(err);
@@ -147,11 +147,10 @@ export const AutocompleteFilter: React.FC<AutocompleteFilterProps> = ({
147147
return;
148148
}
149149

150-
createFilterValue(filterDefinition, validation.val!).then(v => {
151-
if (addFilterParent(v)) {
152-
resetFilterValue();
153-
}
154-
});
150+
const fv = createFilterValue(filterDefinition, validation.val!);
151+
if (addFilterParent(fv)) {
152+
resetFilterValue();
153+
}
155154
}, [
156155
options,
157156
filterDefinition,

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,10 @@ export const TextFilter: React.FC<TextFilterProps> = ({
7474
return;
7575
}
7676

77-
createFilterValue(filterDefinition, validation.val!).then(v => {
78-
if (addFilter(v)) {
79-
resetFilterValue();
80-
}
81-
});
77+
const fv = createFilterValue(filterDefinition, validation.val!);
78+
if (addFilter(fv)) {
79+
resetFilterValue();
80+
}
8281
}, [currentValue, allowEmpty, filterDefinition, setMessageWithDelay, setIndicator, addFilter, resetFilterValue]);
8382

8483
return (

web/src/model/filters.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ export interface FilterDefinition {
6767
name: string;
6868
component: FilterComponent;
6969
category?: FilterCategory;
70-
getOptions: (value: string) => Promise<FilterOption[]>;
70+
findOption: (value: string) => FilterOption | undefined;
71+
autocomplete: (value: string) => Promise<FilterOption[]>;
7172
validate: (value: string) => { val?: string; err?: string };
7273
checkCompletion?: (value: string, selected: string) => { completed: boolean; option: FilterOption };
7374
autoCompleteAddsQuotes?: boolean;
@@ -101,22 +102,11 @@ export interface FilterOption {
101102
value: string;
102103
}
103104

104-
export const createFilterValue = (def: FilterDefinition, value: string): Promise<FilterValue> => {
105-
return (
106-
def
107-
.getOptions(value)
108-
.then(opts => {
109-
const option = opts.find(opt => opt.name === value || opt.value === value);
110-
return option
111-
? { v: option.value, display: option.name }
112-
: { v: value, display: value === undefinedValue ? 'n/a' : undefined };
113-
})
114-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
115-
.catch(_ => {
116-
// In case of error, still create the minimal possible FilterValue
117-
return { v: value };
118-
})
119-
);
105+
export const createFilterValue = (def: FilterDefinition, value: string): FilterValue => {
106+
const option = def.findOption(value);
107+
return option
108+
? { v: option.value, display: option.name }
109+
: { v: value, display: value === undefinedValue ? 'n/a' : undefined };
120110
};
121111

122112
export const hasEnabledFilterValues = (filter: Filter) => {

web/src/utils/filter-definitions.ts

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,30 @@ import { joinResource, SplitResource, splitResource, SplitStage } from '../model
1717
import { getPort } from '../utils/port';
1818
import { ColumnConfigDef } from './columns';
1919
import {
20+
autocompleteCluster,
21+
autocompleteDirection,
22+
autocompleteDnsErrorCode,
23+
autocompleteDnsResponseCode,
24+
autocompleteDropCause,
25+
autocompleteDropState,
26+
autocompleteDSCP,
27+
autocompleteEmpty,
28+
autocompleteKind,
29+
autocompleteNamespace,
30+
autocompletePort,
31+
autocompleteProtocol,
32+
autocompleteResource,
33+
autocompleteTCPFlags,
34+
autocompleteUDN,
35+
autocompleteZone,
2036
findDirectionOption,
37+
findDnsErrorCodeOption,
38+
findDnsResponseCodeOption,
39+
findDropCauseOption,
40+
findDropStateOption,
41+
findDSCPOption,
2142
findProtocolOption,
22-
getClusterOptions,
23-
getDirectionOptionsAsync,
24-
getDnsErrorCodeOptions,
25-
getDnsResponseCodeOptions,
26-
getDropCauseOptions,
27-
getDropStateOptions,
28-
getDSCPOptions,
29-
getKindOptions,
30-
getNamespaceOptions,
31-
getPortOptions,
32-
getProtocolOptions,
33-
getResourceOptions,
34-
getTCPFlagsOptions,
35-
getUDNOptions,
36-
getZoneOptions,
37-
noOption
43+
portValueToOption
3844
} from './filter-options';
3945
import { validateIPFilter } from './ip';
4046
import { validateK8SName, validateStrictK8SName } from './label';
@@ -243,7 +249,7 @@ export const getFilterDefinitions = (
243249
return invalid(t('Value is empty'));
244250
}
245251
//allow 0 / 1 or Ingress / Egress
246-
const found = findDirectionOption(value, t);
252+
const found = findDirectionOption(t, true, value);
247253
if (found) {
248254
return valid(found.name);
249255
}
@@ -254,29 +260,30 @@ export const getFilterDefinitions = (
254260
const isSrc = d.id.includes('src');
255261
const colConfig = columnsDefs.find(c => c.filter === d.id);
256262

257-
let getOptions: (value: string) => Promise<FilterOption[]> = noOption;
263+
let autocomplete: (value: string) => Promise<FilterOption[]> = autocompleteEmpty;
264+
let findOption: (value: string) => FilterOption | undefined = () => undefined;
258265
let validate: (value: string) => { val?: string; err?: string } = rejectEmptyValue;
259266
let encoder: FiltersEncoder = simpleFiltersEncoder(colConfig?.field as Field);
260267
let checkCompletion:
261268
| ((value: string, selected: string) => { completed: boolean; option: FilterOption })
262269
| undefined = undefined;
263270

264271
if (d.id.includes('namespace')) {
265-
getOptions = getNamespaceOptions;
272+
autocomplete = autocompleteNamespace;
266273
validate = k8sNameValidation;
267274
} else if (d.id.includes('cluster')) {
268-
getOptions = getClusterOptions;
275+
autocomplete = autocompleteCluster;
269276
} else if (d.id.includes('udn')) {
270-
getOptions = getUDNOptions;
277+
autocomplete = autocompleteUDN;
271278
} else if (d.id.includes('zone')) {
272-
getOptions = getZoneOptions;
279+
autocomplete = autocompleteZone;
273280
} else if (d.id.includes('name')) {
274281
validate = k8sNameValidation;
275282
} else if (d.id.includes('kind')) {
276-
getOptions = getKindOptions;
283+
autocomplete = autocompleteKind;
277284
encoder = kindFiltersEncoder(`${isSrc ? 'Src' : 'Dst'}K8S_Type`, `${isSrc ? 'Src' : 'Dst'}K8S_OwnerType`);
278285
} else if (d.id.includes('resource')) {
279-
getOptions = getResourceOptions;
286+
autocomplete = autocompleteResource;
280287
validate = k8sResourceValidation;
281288
checkCompletion = k8sResourceCompletion;
282289
encoder = k8sResourceFiltersEncoder(
@@ -289,33 +296,41 @@ export const getFilterDefinitions = (
289296
} else if (d.id.includes('address')) {
290297
validate = addressValidation;
291298
} else if (d.id.includes('port')) {
292-
getOptions = getPortOptions;
299+
autocomplete = autocompletePort;
300+
findOption = portValueToOption;
293301
validate = portValidation;
294302
} else if (d.id.includes('mac')) {
295303
validate = macValidation;
296304
} else if (d.id.includes('proto')) {
297-
getOptions = getProtocolOptions;
305+
autocomplete = autocompleteProtocol;
306+
findOption = findProtocolOption;
298307
validate = protoValidation;
299308
} else if (d.id.includes('direction')) {
300-
getOptions = v => getDirectionOptionsAsync(v, t, d.id === 'nodedirection');
309+
autocomplete = v => autocompleteDirection(t, d.id === 'nodedirection', v);
310+
findOption = v => findDirectionOption(t, d.id === 'nodedirection', v);
301311
validate = dirValidation;
302312
} else if (d.id.includes('drop_state')) {
303-
getOptions = getDropStateOptions;
313+
autocomplete = autocompleteDropState;
314+
findOption = findDropStateOption;
304315
encoder = simpleFiltersEncoder('PktDropLatestState');
305316
} else if (d.id.includes('drop_cause')) {
306-
getOptions = getDropCauseOptions;
317+
autocomplete = autocompleteDropCause;
318+
findOption = findDropCauseOption;
307319
encoder = simpleFiltersEncoder('PktDropLatestDropCause');
308320
} else if (d.id.includes('dns_flag_response_code')) {
309-
getOptions = getDnsResponseCodeOptions;
321+
autocomplete = autocompleteDnsResponseCode;
322+
findOption = findDnsResponseCodeOption;
310323
} else if (d.id.includes('dns_errno')) {
311-
getOptions = getDnsErrorCodeOptions;
324+
autocomplete = autocompleteDnsErrorCode;
325+
findOption = findDnsErrorCodeOption;
312326
} else if (d.id.includes('dscp')) {
313-
getOptions = getDSCPOptions;
327+
autocomplete = autocompleteDSCP;
328+
findOption = findDSCPOption;
314329
} else if (d.id.includes('flags')) {
315-
getOptions = getTCPFlagsOptions;
330+
autocomplete = autocompleteTCPFlags;
316331
}
317332

318-
return { getOptions, validate, encoder, checkCompletion };
333+
return { autocomplete, findOption, validate, encoder, checkCompletion };
319334
};
320335

321336
return filterDefs.map(d => {

0 commit comments

Comments
 (0)