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] Guided onboarding, alerts & cases #143598

Merged
merged 55 commits into from
Oct 27, 2022

Conversation

stephmilovic
Copy link
Contributor

@stephmilovic stephmilovic commented Oct 18, 2022

Dear reviewer, please read this whole description before reviewing the code...

ICYMI

Security currently have a product tour that needs to be removed. There is a large effort in the Onboarding team to bring a unified flow to onboard users to each solution in Kibana. For 8.6 the Security team needs to replace our old product tour with the new guidedOnboarding plugin framework. There are 3 steps for Security to implement: 1. Add Data 2. Turn on Rules 3. Alerts and Cases
Screen Shot 2022-10-24 at 6 15 46 PM

Summary

  1. Defines the new guidedOnboarding plugin dependency
  2. Creates state management via context around step types, SecurityStepId
  3. Implements the cases and alerts guided onboarding step, SecurityStepId.alertsCases
  4. Removes old security product tour

alertsCases

Still to do after merging

It is important to merge this work so that @xcrzx can use the guidedOnboarding plugin for his steps. Therefore this PR only implements the happy path. Still to do:

  1. Ensure unhappy paths are in order. If user clicks out of tour at unexpected time and comes back, is that handled smoothly? Right now, its smooth probably 80% of the time
  2. Cypress tests. We had a tour previously that this removes
  3. Gavin's design points
  4. Remove feature flag

I know there are some weird cases like this, please ignore:
Screen Shot 2022-10-24 at 6 14 18 PM

To test

This is only step 3 of the tour and the previous have not been done yet, so we need to manually put Kibana in the correct state to trigger step 3

  1. add feature flag to kibana.dev.yml: xpack.securitySolution.enableExperimental: ['guidedOnboarding']
  2. Run kibana with examples: yarn start --run-examples
  3. Ensure you have alerts
  4. Go to /app/guidedOnboardingExample
  5. Use the "Set guide state" section to set Guide to security and Step ID to alertsCases
  6. The Setup guide menu will automatically open to Security step 3. Hit the "Start" button
  7. You will be redirected to the alerts page and at this point, we will start on the Alerts and cases tour. Click through the steps to complete the tour
  8. Keep in mind you are testing HAPPY PATH ONLY. We need to get happy path in to unblock @xcrzx and then I will fix unhappy path

Brain dump

This work required some creativity for reasons. Allow me to explain some weirdness

The EuiTourStep component needs an anchor to attach on in the DOM. This can be defined in 2 ways:

type EuiTourStepAnchorProps = ExclusiveUnion<{
  //Element to which the tour step popover attaches when open
  children: ReactElement;
  // Selector or reference to the element to which the tour step popover attaches when open
  anchor?: never;
}, {
  children?: never;
  anchor: ElementTarget;
}>;

It was important that the EuiTourStep anchor is in the DOM when the tour step becomes active. Additionally, when the anchor leaves the DOM, we need EuiTourStep to leave the DOM as well.

How to use components (for D&R step engineers)

  • Define your steps in ../public/common/components/guided_onboarding_tour/tour_config.ts in the securityTourConfig const
  • For each step, implement the GuidedOnboardingTourStep component at the location of the anchor. As stated in the Brain Dump section, there are two ways to define the anchor. I will explain examples of both methods:
  1. Method 1 - as children. Looking at step 1 of the SecurityStepId.alertsCases tour. In the alertsCasesConfig you can see the config for this step looks like:

    {
      ...defaultConfig,
      step: 1,
      title: i18n.translate('xpack.securitySolution.guided_onboarding.tour.ruleNameStep.tourTitle', {
        defaultMessage: 'Test alert for practice',
      }),
      content: i18n.translate(
        'xpack.securitySolution.guided_onboarding.tour.ruleNameStep.tourContent',
        {
          defaultMessage:
            'To help you practice triaging alerts, we enabled a rule to create your first alert.',
        }
      ),
      anchorPosition: 'downCenter',
      dataTestSubj: getTourAnchor(1, SecurityStepId.alertsCases),
    }
    

    Notice that no anchor prop is defined in the step 1 config.
    As you can see pictured below, the tour step anchor is the Rule name of the first alert.

    1

    The component for this anchor is RenderCellValue which returns DefaultCellRenderer. We wrap DefaultCellRenderer with GuidedOnboardingTourStep, passing step={1} stepId={SecurityStepId.alertsCases} to indicate the step. Since there are many other iterations of this component on the page, we also need to pass the isTourAnchor property to determine which of these components should be the anchor. In the code, this looks something like:

    export const RenderCellValue = (props) => {
      const { columnId, rowIndex, scopeId } = props;
      const isTourAnchor = useMemo(
        () =>
          columnId === SIGNAL_RULE_NAME_FIELD_NAME &&
          isDetectionsAlertsTable(scopeId) &&
          rowIndex === 0,
        [columnId, rowIndex, scopeId]
      );
    
      return (
        <GuidedOnboardingTourStep
          isTourAnchor={isTourAnchor}
          step={1}
          stepId={SecurityStepId.alertsCases}
        >
          <DefaultCellRenderer {...props} />
        </GuidedOnboardingTourStep>
      );
    };
    
  2. Method 2 - as anchor props. Looking at step 5 of the SecurityStepId.alertsCases tour. In the alertsCasesConfig you can see the config for this step looks like:

    {
      ...defaultConfig,
      step: 5,
      title: i18n.translate('xpack.securitySolution.guided_onboarding.tour.createCase.tourTitle', {
        defaultMessage: `Add details`,
      }),
      content: i18n.translate(
        'xpack.securitySolution.guided_onboarding.tour.createCase.tourContent',
        {
          defaultMessage: `In addition to the alert, you can add any relevant information you need to the case.`,
        }
      ),
      anchor: `[data-test-subj="create-case-flyout"]`,
      anchorPosition: 'leftUp',
      dataTestSubj: getTourAnchor(5, SecurityStepId.alertsCases),
      hideNextButton: true,
    }
    

    Notice that the anchor prop is defined as [data-test-subj="create-case-flyout"] in the step 5 config. There is also a hideNextButton boolean utilized here.
    As you can see pictured below, the tour step anchor is the create case flyout and the next button is hidden.

    5

    HINT: @cnasikas and @jonathan-buttner @elastic/response-ops-cases this is where your part of the review comes in

    Since cases is its own plugin and we are using a method to generate the flyout, we cannot wrap the flyout as children of the GuidedOnboardingTourStep. We do however need the EuiTourStep component to mount in the same location as the anchor. Therefore, I had to pass a new optional property to the case component called headerContent that simply accepts and renders React.ReactNode at the top of the flyout. In the code, this looks something like:

    createCaseFlyout.open({
      attachments: caseAttachments,
      ...(isTourShown(SecurityStepId.alertsCases) && activeStep === 4
        ? {
            headerContent: (
              // isTourAnchor=true no matter what in order to
              // force active guide step outside of security solution (cases)
              <GuidedOnboardingTourStep isTourAnchor step={5} stepId={SecurityStepId.alertsCases} />
            ),
          }
        : {}),
    });
    
  • The useTourContext is used within anchor components, returning the state of the security tour

    export interface TourContextValue {
      activeStep: number;
      endTourStep: (stepId: SecurityStepId) => void;
      incrementStep: (stepId: SecurityStepId, step?: number) => void;
      isTourShown: (stepId: SecurityStepId) => boolean;
    }
    

    When the tour step does not have a next button, the anchor component will need to call incrementStep after an action is taken. For example, in SecurityStepId.alertsCases step 4, the user needs to click the "Add to case" button to advance the tour.

    4

    So we utilize the useTourContext to do the following check and increment the step in handleAddToNewCaseClick:

    if (isTourShown(SecurityStepId.alertsCases) && activeStep === 4) {
      incrementStep(SecurityStepId.alertsCases);
    }
    

    In SecurityStepId.alertsCases step 5, the user needs to fill out the form and hit the "Create case" button in order to end the alertsCases portion the tour, so with the afterCaseCreated method we call endTourStep(SecurityStepId.alertsCases).

@stephmilovic stephmilovic added release_note:enhancement Team:Threat Hunting Security Solution Threat Hunting Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.6.0 labels Oct 18, 2022
@@ -83,12 +82,6 @@ export const GlobalHeader = React.memo(
};
}, [portalNode, setHeaderActionMenu, theme.theme$]);

const { isTourShown, endTour } = useTourContext();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure why this was here, but we are changing the tour entirely so i think safe to remove?

@stephmilovic
Copy link
Contributor Author

stephmilovic commented Oct 27, 2022

I have one question, by "happy path" do you also mean not closing the flyout when there's a tour tooltip on it (the last two steps)?

Yes. Happy path means the user clicked through exactly as we wanted them to like a good little user. I need to go back and handle the cases when the user clicks out of the tour

Copy link
Contributor

@semd semd left a comment

Choose a reason for hiding this comment

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

LGTM! 🚀

@stephmilovic
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Contributor

@yuliacech yuliacech left a comment

Choose a reason for hiding this comment

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

Thanks a lot for working on this, @stephmilovic!
I tested the happy path locally and it worked as expected 👍

const [loading, setLoading] = useState(false);

// loading = false initial state causes flashes of empty tables
const [loading, setLoading] = useState(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

why does purely cosmetic onboarding elements being added cause tables to flash empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it was already flashing, it just became obvious with the addition of the EuiTourStep. if it starts as not loading, then loading, the element is there briefly and then disappears. EuiTourStep uses EuiPortal to mount. So it mounts to the flyout when it shows briefly, hides when the flyout flashes blank and goes into loading state, and comes back when flyout is actually available

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
securitySolution 3232 3231 -1

Async chunks

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

id before after diff
cases 374.6KB 374.6KB +66.0B
securitySolution 9.6MB 9.6MB +15.2KB
total +15.2KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
cases 126.1KB 126.2KB +32.0B
securitySolution 50.6KB 50.6KB +20.0B
total +52.0B
Unknown metric groups

ESLint disabled line counts

id before after diff
securitySolution 442 443 +1

miscellaneous assets size

id before after diff
securitySolution 4.4MB 3.5MB -925.6KB

Total ESLint disabled count

id before after diff
securitySolution 519 520 +1

History

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

@stephmilovic stephmilovic merged commit fd1ad82 into elastic:main Oct 27, 2022
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Oct 27, 2022
jloleysens added a commit to jloleysens/kibana that referenced this pull request Oct 27, 2022
* main: (24 commits)
  [Files] Add file upload to file picker (elastic#143969)
  [Security solution] Guided onboarding, alerts & cases (elastic#143598)
  [APM] Critical path for a single trace (elastic#143735)
  skip failing test suite (elastic#143933)
  [Fleet] Update GH Projects automation (elastic#144123)
  [APM] Performance fix for 'cardinality' telemetry task (elastic#144061)
  [Enterprise Search] Attach ML Inference Pipeline - Pipeline re-use (elastic#143979)
  [8.5][DOCS] Add support for differential logs (elastic#143242)
  Bump nwsapi from v2.2.0 to v2.2.2 (elastic#144001)
  [APM] Add waterfall to dependency operations (elastic#143257)
  [Shared UX] Add deprecation message to kibana react Markdown (elastic#143766)
  [Security Solution][Endpoint] Adds RBAC API checks for Blocklist (elastic#144047)
  Improve `needs-team` auto labeling regex (elastic#143787)
  [Reporting/CSV Export] _id field can not be formatted (elastic#143807)
  Adds SavedObjectsWarning to analytics results pages. (elastic#144109)
  Bump chromedriver to 107 (elastic#144073)
  Update cypress (elastic#143755)
  [Maps] nest security layers in layer group (elastic#144055)
  [Lens][Agg based Heatmap] Navigate to Lens Agg based Heatmap. (elastic#143820)
  Added support of saved search (elastic#144095)
  ...
angorayc added a commit that referenced this pull request May 28, 2024
## Summary

Fixes #181190
Relevant PRs: #143598 |
#163739


### Steps to Verify: 
1. Entering
[these](https://p.elstc.co/paste/sEmk++Tb#mjwuX7IN8hIN+kOy5gdtfweQNi9sUl+4lVRAewc6hR+)
in your kibana.dev.yml
2. Execute this command to set the Guided onboarding steps to
alertsCases
```
curl --location --request PUT 'http://localhost:5601/internal/guided_onboarding/state' \
--header 'kbn-xsrf: cypress-creds' \
--header 'x-elastic-internal-origin: security-solution' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {YOUR_AUTH_TOKEN}' \.    // If you are using Postman just fill in the Auth tab
--data '{
  "status": "in_progress",
  "guide": {
    "isActive": true,
    "status": "in_progress",
    "steps": [
      { "id": "add_data", "status": "complete" },
      { "id": "rules", "status": "complete" },
      { "id": "alertsCases", "status": "active" }
    ],
    "guideId": "siem"
  }
}'
```
3. Make sure you have alerts available.
4. To test the old flyout with Guided onboarding tour, please go to
Stack Management > Advanced settings > Expandable flyout **OFF**

### It compatible with the new expandable flyout:
1. It shows `expandable flyout tour` when the guided onboarding tour is
**not enabled**.
3. The first two steps should be `hidden` when the `left` expandable is
opened.
5. Most of the guided onboarding tour steps should be hidden when `Add
to new case` flyout or `Add to existing case` modal opened.
6. Once the test case is created, the `insight section` and `correlation
tab` should be opened automatically to fetch cases.
7. `expandable flyout tour` should be visible again after the guided
onboarding tour is finished.






https://github.com/elastic/kibana/assets/6295984/b19bfce9-ec02-4291-b616-e24d3e984a03






### It compatible with the old flyout: 




https://github.com/elastic/kibana/assets/6295984/b10b8bdf-e159-4663-b455-1f4541358a11


### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Sergi Massaneda <sergi.massaneda@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
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:enhancement Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. Team:Threat Hunting Security Solution Threat Hunting Team v8.6.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants