Security Solution: reimplement filterBrowserFieldsByFieldName (#128779)

The function filterBrowserFieldsByFieldName is being run 4+ times when loading pages in the Security app. With a large number of fields, such as is found in production environment, this function can take 10+ seconds to completed. With this implementation, it should run a bit quicker.
This commit is contained in:
Robert Austin 2022-03-29 15:40:33 -04:00 committed by GitHub
parent 7d94876df4
commit 4ce6f20fec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -37,42 +37,63 @@ export const getFieldCount = (category: Partial<BrowserField> | undefined): numb
* Filters the specified `BrowserFields` to return a new collection where every
* category contains at least one field name that matches the specified substring.
*/
export const filterBrowserFieldsByFieldName = ({
export function filterBrowserFieldsByFieldName({
browserFields,
substring,
}: {
browserFields: BrowserFields;
substring: string;
}): BrowserFields => {
}): BrowserFields {
const trimmedSubstring = substring.trim();
// an empty search param will match everything, so return the original browserFields
if (trimmedSubstring === '') {
return browserFields;
}
const result: Record<string, Partial<BrowserField>> = {};
for (const [categoryName, categoryDescriptor] of Object.entries(browserFields)) {
if (!categoryDescriptor.fields) {
// ignore any category that is missing fields. This is not expected to happen.
// eslint-disable-next-line no-continue
continue;
}
// filter each category such that it only contains fields with field names
// that contain the specified substring:
const filteredBrowserFields: BrowserFields = Object.keys(browserFields).reduce(
(filteredCategories, categoryId) => ({
...filteredCategories,
[categoryId]: {
...browserFields[categoryId],
fields: pickBy(
({ name }) => name != null && name.includes(trimmedSubstring),
browserFields[categoryId].fields
),
},
}),
{}
);
// keep track of whether this category had a matching field, if so, we should emit it into the result
let hadAMatch = false;
// only pick non-empty categories from the filtered browser fields
const nonEmptyCategories: BrowserFields = pickBy(
(category) => categoryHasFields(category),
filteredBrowserFields
);
// The fields that matched, for this `categoryName`
const filteredFields: Record<string, Partial<BrowserField>> = {};
return nonEmptyCategories;
};
for (const [fieldName, fieldDescriptor] of Object.entries(categoryDescriptor.fields)) {
// For historical reasons, we consider the name as it appears on the field descriptor, not the `fieldName` (attribute name) itself.
// It is unclear if there is any point in continuing to do this.
const fieldNameFromDescriptor = fieldDescriptor.name;
if (!fieldNameFromDescriptor) {
// Ignore any field that is missing a name in its descriptor. This is not expected to happen.
// eslint-disable-next-line no-continue
continue;
}
// Check if this field matches (via substring comparison) the passed substring
if (fieldNameFromDescriptor !== null && fieldNameFromDescriptor.includes(trimmedSubstring)) {
// this field is a match, so we should emit this category into the result object.
hadAMatch = true;
// emit this field
filteredFields[fieldName] = fieldDescriptor;
}
}
if (hadAMatch) {
// if at least one field matches, emit the category, but replace the `fields` attribute with the filtered fields
result[categoryName] = {
...browserFields[categoryName],
fields: filteredFields,
};
}
}
return result;
}
/**
* Filters the selected `BrowserFields` to return a new collection where every