mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[9.0] [Security Solution] Fixes related integrations render performance on rule editing pages (#217254) (#218671)
# Backport This will backport the following commits from `main` to `9.0`: - [[Security Solution] Fixes related integrations render performance on rule editing pages (#217254)](https://github.com/elastic/kibana/pull/217254) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Davis Plumlee","email":"56367316+dplumlee@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-04-18T18:47:20Z","message":"[Security Solution] Fixes related integrations render performance on rule editing pages (#217254)\n\n## Summary\n\nFixes https://github.com/elastic/kibana/issues/183607\n\nAdds logic to fix the re-render performance issues caused by the related\nintegrations component on the rule edit and creation pages. This copies\na strategy used in https://github.com/elastic/kibana/pull/180682 to fix\na similar issue with required fields. Related integrations component now\ndoesn't re-render when there are updates to components that don't affect\nit.\n\n#### React Profile while typing in query field component\n\n\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n\n---------\n\nCo-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>","sha":"1b6376e9c9e09edb5b0e790bdccc7d14bd5801ca","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","Team:Detections and Resp","Team: SecuritySolution","Feature:Rule Management","Team:Detection Rule Management","backport:version","v9.1.0","v8.19.0","v8.18.1","v9.0.1"],"title":"[Security Solution] Fixes related integrations render performance on rule editing pages","number":217254,"url":"https://github.com/elastic/kibana/pull/217254","mergeCommit":{"message":"[Security Solution] Fixes related integrations render performance on rule editing pages (#217254)\n\n## Summary\n\nFixes https://github.com/elastic/kibana/issues/183607\n\nAdds logic to fix the re-render performance issues caused by the related\nintegrations component on the rule edit and creation pages. This copies\na strategy used in https://github.com/elastic/kibana/pull/180682 to fix\na similar issue with required fields. Related integrations component now\ndoesn't re-render when there are updates to components that don't affect\nit.\n\n#### React Profile while typing in query field component\n\n\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n\n---------\n\nCo-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>","sha":"1b6376e9c9e09edb5b0e790bdccc7d14bd5801ca"}},"sourceBranch":"main","suggestedTargetBranches":["8.19","8.18","9.0"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/217254","number":217254,"mergeCommit":{"message":"[Security Solution] Fixes related integrations render performance on rule editing pages (#217254)\n\n## Summary\n\nFixes https://github.com/elastic/kibana/issues/183607\n\nAdds logic to fix the re-render performance issues caused by the related\nintegrations component on the rule edit and creation pages. This copies\na strategy used in https://github.com/elastic/kibana/pull/180682 to fix\na similar issue with required fields. Related integrations component now\ndoesn't re-render when there are updates to components that don't affect\nit.\n\n#### React Profile while typing in query field component\n\n\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n\n---------\n\nCo-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>","sha":"1b6376e9c9e09edb5b0e790bdccc7d14bd5801ca"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.0","label":"v9.0.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Davis Plumlee <56367316+dplumlee@users.noreply.github.com>
This commit is contained in:
parent
f9f11a5e4b
commit
01d7f190db
5 changed files with 112 additions and 60 deletions
|
@ -8,28 +8,74 @@
|
|||
import React from 'react';
|
||||
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSpacer } from '@elastic/eui';
|
||||
import { UseArray, useFormData } from '../../../../shared_imports';
|
||||
import type { FormHook, ArrayItem } from '../../../../shared_imports';
|
||||
import { RelatedIntegrationsHelpInfo } from './related_integrations_help_info';
|
||||
import { RelatedIntegrationFieldRow } from './related_integration_field_row';
|
||||
import * as i18n from './translations';
|
||||
import { OptionalFieldLabel } from '../optional_field_label';
|
||||
import { getFlattenedArrayFieldNames } from '../utils';
|
||||
|
||||
interface RelatedIntegrationsProps {
|
||||
path: string;
|
||||
dataTestSubj?: string;
|
||||
}
|
||||
|
||||
export function RelatedIntegrations({ path, dataTestSubj }: RelatedIntegrationsProps): JSX.Element {
|
||||
function RelatedIntegrationsComponent({
|
||||
path,
|
||||
dataTestSubj,
|
||||
}: RelatedIntegrationsProps): JSX.Element {
|
||||
return (
|
||||
<UseArray path={path} initialNumberOfItems={0}>
|
||||
{({ items, addItem, removeItem, form }) => (
|
||||
<RelatedIntegrationsList
|
||||
items={items}
|
||||
addItem={addItem}
|
||||
removeItem={removeItem}
|
||||
path={path}
|
||||
form={form}
|
||||
dataTestSubj={dataTestSubj}
|
||||
/>
|
||||
)}
|
||||
</UseArray>
|
||||
);
|
||||
}
|
||||
|
||||
interface RelatedIntegrationsListProps {
|
||||
items: ArrayItem[];
|
||||
addItem: () => void;
|
||||
removeItem: (id: number) => void;
|
||||
path: string;
|
||||
form: FormHook;
|
||||
dataTestSubj?: string;
|
||||
}
|
||||
|
||||
const RelatedIntegrationsList = ({
|
||||
items,
|
||||
addItem,
|
||||
removeItem,
|
||||
path,
|
||||
form,
|
||||
dataTestSubj,
|
||||
}: RelatedIntegrationsListProps) => {
|
||||
const flattenedFieldNames = getFlattenedArrayFieldNames(form, path);
|
||||
|
||||
/*
|
||||
Not using "watch" for the initial render, to let row components render and initialize form fields.
|
||||
Then we can use the "watch" feature to track their changes.
|
||||
*/
|
||||
const hasRenderedInitially = flattenedFieldNames.length > 0;
|
||||
const fieldsToWatch = hasRenderedInitially ? flattenedFieldNames : [];
|
||||
|
||||
const [formData] = useFormData({ watch: fieldsToWatch });
|
||||
|
||||
const label = (
|
||||
<>
|
||||
{i18n.RELATED_INTEGRATIONS_LABEL}
|
||||
<RelatedIntegrationsHelpInfo />
|
||||
</>
|
||||
);
|
||||
const [formData] = useFormData();
|
||||
|
||||
return (
|
||||
<UseArray path={path} initialNumberOfItems={0}>
|
||||
{({ items, addItem, removeItem }) => (
|
||||
<EuiFormRow
|
||||
label={label}
|
||||
labelAppend={OptionalFieldLabel}
|
||||
|
@ -56,7 +102,7 @@ export function RelatedIntegrations({ path, dataTestSubj }: RelatedIntegrationsP
|
|||
</EuiButtonEmpty>
|
||||
</>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
</UseArray>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const RelatedIntegrations = React.memo(RelatedIntegrationsComponent);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { RequiredFieldInput } from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen';
|
||||
import type { ERROR_CODE, FormData, FormHook, ValidationFunc } from '../../../../shared_imports';
|
||||
import * as i18n from './translations';
|
||||
import { getFlattenedArrayFieldNames } from './utils';
|
||||
import { getFlattenedArrayFieldNames } from '../utils';
|
||||
|
||||
export function makeValidateRequiredField(parentFieldPath: string) {
|
||||
return function validateRequiredField(
|
||||
|
|
|
@ -16,7 +16,7 @@ import { RequiredFieldsHelpInfo } from './required_fields_help_info';
|
|||
import * as defineRuleI18n from '../../../rule_creation_ui/components/step_define_rule/translations';
|
||||
import { OptionalFieldLabel } from '../optional_field_label';
|
||||
import { RequiredFieldRow } from './required_fields_row';
|
||||
import { getFlattenedArrayFieldNames } from './utils';
|
||||
import { getFlattenedArrayFieldNames } from '../utils';
|
||||
import * as i18n from './translations';
|
||||
|
||||
interface RequiredFieldsComponentProps {
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { FormHook, ArrayItem } from '../../../../shared_imports';
|
||||
|
||||
interface PickTypeForNameParameters {
|
||||
name: string;
|
||||
type: string;
|
||||
|
@ -28,26 +26,3 @@ export function pickTypeForName({ name, type, typesByFieldName = {} }: PickTypeF
|
|||
*/
|
||||
return typesAvailableForName[0] ?? type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of flattened field names for a given array field of a form.
|
||||
* Flattened field name is a string that represents the path to an item in an array field.
|
||||
* For example, a field "myArrayField" can be represented as "myArrayField[0]", "myArrayField[1]", etc.
|
||||
*
|
||||
* Flattened field names are useful:
|
||||
* - when you need to subscribe to changes in an array field using `useFormData` "watch" option
|
||||
* - when you need to retrieve form data before serializer function is applied
|
||||
*
|
||||
* @param {Object} form - Form object.
|
||||
* @param {string} arrayFieldName - Path to the array field.
|
||||
* @returns {string[]} - Flattened array field names.
|
||||
*/
|
||||
export function getFlattenedArrayFieldNames(
|
||||
form: { getFields: FormHook['getFields'] },
|
||||
arrayFieldName: string
|
||||
): string[] {
|
||||
const internalField = form.getFields()[`${arrayFieldName}__array__`] ?? {};
|
||||
const internalFieldValue = (internalField?.value ?? []) as ArrayItem[];
|
||||
|
||||
return internalFieldValue.map((item) => item.path);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ArrayItem, FormHook } from '../../../shared_imports';
|
||||
|
||||
/**
|
||||
* Returns a list of flattened field names for a given array field of a form.
|
||||
* Flattened field name is a string that represents the path to an item in an array field.
|
||||
* For example, a field "myArrayField" can be represented as "myArrayField[0]", "myArrayField[1]", etc.
|
||||
*
|
||||
* Flattened field names are useful:
|
||||
* - when you need to subscribe to changes in an array field using `useFormData` "watch" option
|
||||
* - when you need to retrieve form data before serializer function is applied
|
||||
*
|
||||
* @param {Object} form - Form object.
|
||||
* @param {string} arrayFieldName - Path to the array field.
|
||||
* @returns {string[]} - Flattened array field names.
|
||||
*/
|
||||
export function getFlattenedArrayFieldNames(
|
||||
form: { getFields: FormHook['getFields'] },
|
||||
arrayFieldName: string
|
||||
): string[] {
|
||||
const internalField = form.getFields()[`${arrayFieldName}__array__`] ?? {};
|
||||
const internalFieldValue = (internalField?.value ?? []) as ArrayItem[];
|
||||
|
||||
return internalFieldValue.map((item) => item.path);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue