Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] Remove extra data structures in create/edit rule forms #157749

Merged
merged 23 commits into from
Jun 5, 2023

Conversation

marshallmain
Copy link
Contributor

@marshallmain marshallmain commented May 15, 2023

Summary

This PR refactors the forms used by the Detection Engine create and edit rule pages. The existing forms have a lot of complexity in how data is managed: each of the 4 steps (Define, About, Schedule, and Actions) map to a separate component, and these individual components own the form for that step. However, some steps have dependencies on data in other steps. In addition, Rule Preview and submitting the rule to the API require the data from all steps. To resolve this, I moved the form ownership from the individual step components to the Create/Edit Rule page components and passed the appropriate form into each individual step component.

With the forms owned at the Page level, many of the additional data structures we previously needed to pass form data around are no longer necessary.

Removed

  • formHooks, setFormHooks - These hooks were used to let step components pass the form's validate function back up to the Page component. With the forms created in the Page component, the validate functions are available without additional structures.
  • stepsData, setStepsData - This data structure held the form data from each step and the validation status. We now use the live form data through useFormData without additional sidecar data structures.
  • defineRuleData/aboutRuleData/scheduleRuleData - Sidecar data structures for rule preview, also held the form data from each step. We now use the live form data through useFormData without additional sidecar data structures.

Simplified

  • editStep() no longer relies on formHooks
  • submitStep() no longer relies on formHooks
  • Step forms no longer need watch and onChange configurations

Key Implementation Notes

In order for useFormData to be reliable, the forms for all steps must stay in the DOM at all times while on the Create/Edit pages. Thus the "read only" view has been separated from the editable view, and the visibility of each view is toggled by hiding elements with CSS rather than removing them from the DOM.

If the form were to be removed from the DOM when switching to a read only view, the values from useFormData would reset to the default values so we would need a sidecar object to hold the form values when the form is not rendered. It would also require re-loading the values from the sidecar into the form when the form gets re-added to the DOM. Keeping the form in the DOM at all times allows us to rely on useFormData consistently, which simplifies the data model.

Bug Fixes

  • When moving back to step 1 from step 2 by clicking "Edit", data is no longer lost in step 2

@marshallmain marshallmain marked this pull request as ready for review May 23, 2023 14:23
@marshallmain marshallmain requested review from a team as code owners May 23, 2023 14:23
@marshallmain marshallmain requested a review from banderror May 23, 2023 14:23
@marshallmain marshallmain added Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. labels May 23, 2023
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-detections-response (Team:Detections and Resp)

@elasticmachine
Copy link
Contributor

Pinging @elastic/security-solution (Team: SecuritySolution)

@marshallmain marshallmain added release_note:skip Skip the PR/issue when compiling release notes v8.9.0 labels May 23, 2023
@banderror banderror requested review from maximpn and removed request for banderror May 29, 2023 12:11
Copy link
Contributor

@maximpn maximpn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marshallmain Thank you for the long awaited refactoring 🙏 It made the rule's form much cleaner and it's a great step towards simplicity.

Tested locally and it seems working fine though I haven't performed complex manual testing scenarios.

It's clear that some code is just coming through but there is no hard to improve it a bit. As it doesn't look critical I approve the PR and it's up to you to react on the nit comments.

/>
<div
style={{
display: activeStep === RuleStep.defineRule ? undefined : 'none',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious if there is already an utility component or something exposed by EUI (I wasn't able to find it in the docs) to improve readability. Something like

<DisplayOnCondition visible={activeStep === RuleStep.defineRule}>
  ...
</DisplayOnCondition>

or it could be a component specific for this functionality only accepting a step name.

</StepContentWrapper>
);
};
export const StepAboutRuleReadOnly = memo(StepAboutRuleReadOnlyComponent);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see memo() utility is used for the other steps as well though it's unclear if it does a real performance improvement it's here just "in case".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ I think only a shallow comparison is being made, so it the props may nullify the memo each time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I haven't checked to see if we're getting a benefit here or not - the forms lib doesn't seem great about providing the same object on each render so defaultValues might be changing a lot and taking away the memo benefits. But, the "Read only" view was memoized before when it was combined with the edit component, so I just memoized it here to keep the behavior as similar as possible.

I have another branch where I was working on improving the memoization around the create rules page, but I wasn't seeing a noticeable difference in user experience so I shelved it to work on this refactor instead. But, it could be worth picking it up again next.

it('Shows validation error on rule edit when saved query can not be loaded', function () {
// TODO: this error depended on the schema validation running. Can we show the error
// based on the saved query failing to load instead of relying on the schema validation?
it.skip('Shows validation error on rule edit when saved query can not be loaded', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you create an issue to record this as being skipped if we're unable to skip it before merging?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: #159060

</StepContentWrapper>
);
};
export const StepAboutRuleReadOnly = memo(StepAboutRuleReadOnlyComponent);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ I think only a shallow comparison is being made, so it the props may nullify the memo each time.

Copy link
Contributor

@yctercero yctercero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a much awaited refactor! LGTM! I pulled down to test creating and editing rules. I tried to fill out pretty much every field to test it out. All seemed good. There's some existing validation bugs with the actions section but unrelated to any of these changes.

@kibana-ci
Copy link
Collaborator

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] FTR Configs #31 / ObservabilityApp Observability alerts / Add to case > When user has read permissions for cases "after all" hook for "does not render case options in the overflow menu"
  • [job] [logs] FTR Configs #31 / ObservabilityApp Observability alerts / Add to case > When user has read permissions for cases "before all" hook for "does not render case options in the overflow menu"

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
securitySolution 4103 4104 +1

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
securitySolution 10.7MB 10.7MB -306.0B
Unknown metric groups

ESLint disabled line counts

id before after diff
enterpriseSearch 19 21 +2
securitySolution 414 417 +3
total +5

References to deprecated APIs

id before after diff
securitySolution 606 603 -3

Total ESLint disabled count

id before after diff
enterpriseSearch 20 22 +2
securitySolution 498 501 +3
total +5

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@marshallmain marshallmain merged commit 9abed02 into elastic:main Jun 5, 2023
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Jun 5, 2023
maximpn added a commit that referenced this pull request Aug 11, 2023
**Addresses:** #159349

## Summary

This PR fixes and re-enables rule snoozing Cypress test `Rule editing page / actions tab - adds an action to a snoozed rule`. 

## Details

Basically the test wasn't flaky it just failed on the `main` branch and was skipped. The cause lays in interlacing [Rule snooze tests PR](#158195) with [Rule Editing page refactoring PR](#157749). Two PRs were merged independently to the `main` branch (while the tests PR was merged the last) so combining them lead to a test failure while each one separately didn't cause any problems.

### The root cause

The root cause is in a combination of factors.

[useForm](https://github.com/elastic/kibana/blob/main/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts#L33) hook has a flaw it can't update its state when new `defaultValue` comes in. The same issue also in [useField](https://github.com/elastic/kibana/blob/main/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts#L77) hook. Rule Editing page fetched a fresh rule's data every time it's rendered via [useRule](https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx#L581). As `useRule` hook is based on `React Query` it returns stale data during the first render while firing an HTTP request for the fresh data. Obviously the problem happens if the stale data is passed to `useForm` hook as `defaultValue` and when fresh data is fetched and passed again to `useForm` hook the latter just ignores it.

Staying longer on the Rule Details page helps to avoid the problem as fetched rule's data is cached and returned as stale data on the Rule Editing page after transition. As stale and fresh data are the same the test would pass. Anyway this behaviour can be reproduced in prod with a slow internet connection.

### What was done to fix the problem?

Functionality has been added to update the cached rule's data (React Query cache) upon mutation successful update rule mutation. The mutation if fired by pressing "Save changes" button on the Rule Editing page. It is possible as update rule endpoint returns an updated rule in its response.

Along the way it turned out update rule endpoint's and read rule endpoint's responses weren't properly typed so it lead to types mismatch. To fix it `updateRule`'s and `UseMutationOptions`'s return types were changed to `Rule` instead of  `RuleResponse`.

### Misc

Along the way it turned out `cy.get(RULE_INDICES).should('be.visible');` isn't required anymore to access rule actions form.
e40pud added a commit that referenced this pull request Aug 14, 2023
…163050)

## Summary

Original ticket: #159060 

This PR un-skips test which was disabled after the Rule Editing page
[refactoring](#157749). There we
stopped fields validation on page loading. To be able to show the
"failed to load saved query" error on page loading we force the field
validation when we failed to load a saved query.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting release_note:skip Skip the PR/issue when compiling release notes Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.9.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants