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] Reintroduce ML Jobs warning popover on rule upgrade #159868

Merged
merged 5 commits into from
Jun 20, 2023

Conversation

jpdjere
Copy link
Contributor

@jpdjere jpdjere commented Jun 16, 2023

Summary

Reintroduces ML Jobs warning popover that was removed during new install/upgrade initial implementation.

ML Jobs warning popover that appears when user has legacy ML jobs installed, and the user attempts to update their prebuilt rules.

Modified behaviour so that the popover now appears in any of the three cases: upgrading all rules, upgrading specific rules and upgrading a single rule.

Testing

In the state returned by UpgradePrebuiltRulesTableContextProvider, replace the value of legacyJobsInstalled for a mock value. See below:

    return {
      state: {
        rules,
        tags,
        isFetched,
        isLoading: isLoading && loadingJobs,
        isRefetching,
        selectedRules,
        loadingRules,
        lastUpdated: dataUpdatedAt,
        legacyJobsInstalled: [
          {
            id: 'rc-rare-process-windows-5',
            description:
              'Looks for rare and anomalous processes on a Windows host. Requires process execution events from Sysmon.',
            groups: ['host'],
            processed_record_count: 8577,
            memory_status: 'ok',
            jobState: 'closed',
            hasDatafeed: true,
            datafeedId: 'datafeed-rc-rare-process-windows-5',
            datafeedIndices: ['winlogbeat-*'],
            datafeedState: 'stopped',
            latestTimestampMs: 1561402325194,
            earliestTimestampMs: 1554327458406,
            isSingleMetricViewerJob: true,
            awaitingNodeAssignment: false,
            jobTags: {},
            bucketSpanSeconds: 900,
          },
          {
            id: 'siem-api-rare_process_linux_ecs',
            description: 'SIEM Auditbeat: Detect unusually rare processes on Linux (beta)',
            groups: ['siem'],
            processed_record_count: 582251,
            memory_status: 'hard_limit',
            jobState: 'closed',
            hasDatafeed: true,
            datafeedId: 'datafeed-siem-api-rare_process_linux_ecs',
            datafeedIndices: ['auditbeat-*'],
            datafeedState: 'stopped',
            latestTimestampMs: 1557434782207,
            earliestTimestampMs: 1557353420495,
            isSingleMetricViewerJob: true,
            awaitingNodeAssignment: false,
            jobTags: {},
            bucketSpanSeconds: 900,
          },
        ],
        isUpgradeModalVisible,
        ruleIdToUpgrade,
        modalConfirmationUpdateMethod,
      },
      actions,
    };

Checklist

Delete any items that are not applicable to this PR.

For maintainers

@jpdjere jpdjere added 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. Feature:Rule Management Security Solution Detection Rule Management Team:Detection Rule Management Security Detection Rule Management Team v8.9.0 labels Jun 16, 2023
@jpdjere jpdjere requested a review from a team as a code owner June 16, 2023 16:50
@jpdjere jpdjere self-assigned this Jun 16, 2023
@jpdjere jpdjere requested a review from spong June 16, 2023 16:50
@elasticmachine
Copy link
Contributor

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

@elasticmachine
Copy link
Contributor

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

@jpdjere jpdjere requested review from xcrzx and removed request for spong June 16, 2023 16:55
@jpdjere
Copy link
Contributor Author

jpdjere commented Jun 16, 2023

@xcrzx Reintroduced the ML Popover with this approach, but not completely satisfied with what I had to do with the Upgrade context, seems kinda hacky to me.

Can you take a look on Monday and tell me what you think?

@maximpn maximpn self-requested a review June 19, 2023 09:56
Copy link
Contributor

@xcrzx xcrzx left a comment

Choose a reason for hiding this comment

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

I've tested it locally, and the functionality is working as expected. Thanks, @jpdjere! 👍 However, I do have a few suggestions about structuring the business logic within the context. If necessary, we can sync up on this over a Zoom call.

Comment on lines 116 to 125
const upgradeRule = useCallback(
(ruleId: string) => {
if (legacyJobsInstalled.length > 0) {
showUpgradeModal();
setModalConfirmationUpgradeMethod('SINGLE_RULE');
setRuleIdToUpgrade(ruleId);
} else {
upgradeOneRule(ruleId);
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's encapsulate this business logic within the rules upgrade context. This is the very purpose of the context – to accommodate all rule-upgrade-related logic, while minimizing its presence in the UI as much as possible. So, when we talk about a single rule upgrade, we could expose a single method from the context that is aware of the context state, performs all the necessary checks for the upgrade process, and can manage basic state manipulations, like setting loading rules, displaying intermediary modal windows, and so on. That way we'll have atomic operations exposed from the context that holds all related business logic instead of assembling it from many pieces in the UI.

Comment on lines 42 to 70
const upgradeRulesWrapper = (
upgradeMethod: ModalConfirmationUpgradeMethod,
upgradeRuleMethod: () => Promise<void>
) => {
if (legacyJobsInstalled.length > 0) {
showUpgradeModal();
setModalConfirmationUpgradeMethod(upgradeMethod);
} else {
upgradeRuleMethod();
}
};

const mlJobUpgradeModalConfirm = useCallback(() => {
if (modalConfirmationUpdateMethod === 'ALL_RULES') {
upgradeAllRules();
} else if (modalConfirmationUpdateMethod === 'SPECIFIC_RULES') {
upgradeSelectedRules();
} else if (modalConfirmationUpdateMethod === 'SINGLE_RULE') {
upgradeOneRule(ruleIdToUpgrade);
}
hideUpgradeModal();
}, [
hideUpgradeModal,
modalConfirmationUpdateMethod,
ruleIdToUpgrade,
upgradeAllRules,
upgradeOneRule,
upgradeSelectedRules,
]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, we should move this business logic to the context as much as possible.

Comment on lines 83 to 86
setModalConfirmationUpgradeMethod: Dispatch<SetStateAction<ModalConfirmationUpgradeMethod>>;
setRuleIdToUpgrade: Dispatch<SetStateAction<string>>;
showUpgradeModal: () => void;
hideUpgradeModal: () => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

Those are very low-level methods. Let's try to hide them inside the context and implement rule upgrade logic without exposing them.

Copy link
Contributor

@xcrzx xcrzx left a comment

Choose a reason for hiding this comment

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

I'm approving the PR now so we can merge it before the feature freeze. It's perfectly fine to address the comments in a follow-up.

Added popover

Delete mock

Refactor code to live in context
@jpdjere
Copy link
Contributor Author

jpdjere commented Jun 19, 2023

@xcrzx Thanks for the review and the approval.

I refactored the code following the feedback, centralizing all code in the context. I think it's much cleaner now. Take a look if you wish, I'll leave the CI running during the night and merge it as soon as it's green tomorrow.

Comment on lines 260 to 262
upgradeSingleRuleFromRowCTA,
upgradeSelectedRulesCTAClick,
upgradeAllRulesCTAClick,
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be ideal to keep the method names neutral and avoid referencing specific use cases. For example, we should remove terms like Row, Click, or CTA from the method names.

Comment on lines 81 to 84
/**
* Rule ID of single rule to upgrade when the ML jobs modal is confirmed
* */
ruleIdToUpgrade: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this state still used?

Comment on lines 69 to 80
/**
* Legacy ML Jobs that cause modal to pop up before performing rule ugprades
*/
legacyJobsInstalled: MlSummaryJob[];
/**
* Upgrade method to execute when the user confirms the modal
*/
modalConfirmationUpdateMethod: ModalConfirmationUpgradeMethod | null;
/**
* Is true when the modal is visible
* */
isUpgradeModalVisible: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

I propose we take this a step further and make those state props internal. The rules upgrade context should handle modal confirmation internally without revealing anything to the external environment. I might not have been clear enough about handling that state internally, so let's please sync up on this topic separately.

Comment on lines 196 to 208
// Wrapper around upgrade rules methods to display ML Jobs warning modal when necessary
const upgradeRulesWrapper = useCallback(
(upgradeMethod: ModalConfirmationUpgradeMethod, upgradeRuleMethod: () => Promise<void>) =>
() => {
if (legacyJobsInstalled.length > 0) {
showUpgradeModal();
setModalConfirmationUpgradeMethod(upgradeMethod);
} else {
upgradeRuleMethod();
}
},
[legacyJobsInstalled.length, showUpgradeModal]
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Utilizing the wrapper does add a bit of extra complexity to the upgrade logic. For this specific use case, we have a hook named useAsyncConfirmation. By using it, we can significantly improve the readability of the business logic. Consequently, a single rule upgrade method might look like the following:

const [confirmUpgrade, onConfirm, onCancel] = useAsyncConfirmation({
  onInit: showUpgradeModal,
  onFinish: hideUpgradeModal,
});

const upgradeOneRule = useCallback(
  async (ruleId: RuleSignatureId) => {
    try {
      // 1. Set loading state
      setLoadingRules((prev) => [...prev, ruleId]);

      // 2. Confirm upgrade
      if (await confirmUpgrade()) {
        // 3. Perform upgrade
        await upgradeSpecificRulesRequest({});
      }
    } finally {
      // 4. Remove loading state
      setLoadingRules((prev) => prev.filter((id) => id !== ruleId));
    }
  },
  [confirmUpgrade, upgradeSpecificRulesRequest]
);

As you can see, all required upgrade steps are located in the method body and are easily readable, no need to split the process into several functions.

@jpdjere
Copy link
Contributor Author

jpdjere commented Jun 20, 2023

Thanks @xcrzx, this is so much easier and tidier.

useAsyncConfirmation for the win :)

Copy link
Contributor

@xcrzx xcrzx left a comment

Choose a reason for hiding this comment

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

Thank you, @jpdjere, for implementing the suggestions 👍 Verified locally, the modal window works as expected 🎉

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
securitySolution 4197 4199 +2

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.9MB 10.9MB +2.6KB
Unknown metric groups

ESLint disabled line counts

id before after diff
enterpriseSearch 13 15 +2
securitySolution 411 415 +4
total +6

Total ESLint disabled count

id before after diff
enterpriseSearch 14 16 +2
securitySolution 494 498 +4
total +6

History

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

cc @jpdjere

@jpdjere jpdjere merged commit 21dbbd0 into elastic:main Jun 20, 2023
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Jun 20, 2023
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 Feature:Rule Management Security Solution Detection Rule Management release_note:skip Skip the PR/issue when compiling release notes Team:Detection Rule Management Security Detection Rule Management Team 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.

5 participants