Skip to content

Mutate Graph filters from flyout actions#250105

Open
albertoblaz wants to merge 20 commits intoelastic:mainfrom
albertoblaz:graph-popovers-impl
Open

Mutate Graph filters from flyout actions#250105
albertoblaz wants to merge 20 commits intoelastic:mainfrom
albertoblaz:graph-popovers-impl

Conversation

@albertoblaz
Copy link
Contributor

@albertoblaz albertoblaz commented Jan 22, 2026

Summary

Closes #239954.

PRs involved:

  1. Reorganize popover files #249622
  2. Pin individual entities in graph #251299
  3. Mutate Graph filters from flyout actions #250105 <-- This PR

Key Changes

  1. New Actions Buttons for Grouped Items
  2. Filter State Management with FilterStore
  3. Refactored Popover Item Generation
  4. Clickable Item Titles

Screenshots

Actions in graph node - enabled actions (entity available in Entity Store) Screenshot 2026-02-26 at 16 38 23 Screenshot 2026-02-26 at 16 38 33
Actions in flyout - disabled entity relationships action Screenshot 2026-02-26 at 16 37 46
Actions in graph node - disabled actions (entity unavailable) Screenshot 2026-02-26 at 16 37 58

How to test

  1. Deploy a local env using the following command:
    node scripts/es snapshot --license trial -E path.data=../default -E reindex.remote.whitelist=kfir-graph-viz-wip-ba715e.es.eu-west-1.aws.qa.elastic.cloud:443 -E xpack.security.authc.api_key.enabled=true
  2. run kibana using yarn start
  3. Go to Advanced settings and make suresecuritySolution:enableGraphVisualization and securitySolution:enableAssetInventory features are toggled on.
  4. Got to Security -> inventory -> click on 'Enable Asset Inventory'.
  5. Load events and entities data:

node scripts/es_archiver load x-pack/solutions/security/test/cloud_security_posture_functional/es_archives/logs_gcp_audit --es-url http://elastic:changeme@localhost:9200 --kibana-url http://elastic:changeme@localhost:5601

node scripts/es_archiver load x-pack/solutions/security/test/cloud_security_posture_functional/es_archives/entity_store_v2 --es-url http://elastic:changeme@localhost:9200 --kibana-url http://elastic:changeme@localhost:5601

  1. Navigate to Security -> Explore -> User
    extend the time range to 2-3 years and check how the events and rendered, few examples:

  2. Toggle the graph searchbar (blue primary button at the top-right corner within the graph).

  3. Click popover items i.e. "Show actions done to this entity" from single nodes in the graph. Verify filters update properly and graph re-renders

  4. Click on "show entity details" / "show event details" from grouped nodes in the graph. Flyout must open on the right-side, then click the 3-dots button on a grouped item. The same popover as in graph nodes must open. Try clicking any of the popover items. Verify filters update and graph re-renders.

  5. For testing the "Show entity relationships" option: Try one event by typing in the search bar event.id: "rel-hierarchy-event-id-ftr-12345" and check the graph. Click show entity relationships and actions of different nodes and validate that everything is rendered and works correctly.

Checklist

  • Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support
  • Documentation was added for features that require explanation or tutorials
  • Unit or functional tests were updated or added to match the most common scenarios
  • If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the docker list
  • This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The release_note:breaking label should be applied in these situations.
  • Flaky Test Runner was used on any tests changed
  • The PR description includes the appropriate Release Notes section, and the correct release_note:* label is applied per the guidelines
  • Review the backport guidelines and apply applicable backport:* labels.

Identify risks

Risk of breaking filters in graph investigation after major refactor.

@albertoblaz albertoblaz self-assigned this Jan 22, 2026
@albertoblaz albertoblaz added release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting Team:Cloud Security Cloud Security team related v9.4.0 labels Jan 22, 2026
Comment on lines +51 to +60
openPreviewPanel({
id: GenericEntityPanelKey,
params: {
entityId: item.id,
scopeId,
isPreviewMode: true,
banner: GENERIC_ENTITY_PREVIEW_BANNER,
isEngineMetadataExist: !!item.availableInEntityStore,
},
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Everything under graph_grouped_node_preview_panel/ is meant to be rendered in a flyout context. So we don't need the tiny pub-sub we had before. We can invoke the Flyout API right away. Benefits of this:

  • Established pattern in other flyout panels
  • Simplifies the code
  • Reduces complexity in GraphVisualization, which in future, should just reuse and render GraphInvestigation
  • Co-locates button and correct preview panel i.e. opens entity preview from entity and button and event/alert previews from event button

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This whole file is a combination of:

  • A filters store that acts as a single source of truth for graph filters
  • A pub-sub channel that listens to events produced from:
    • the new buttons in flyout i.e. entity_actions_button.tsx and event_actions_button.tsx
    • the hooks in graph i.e. use_entity_node_expand_popover.ts and use_label_node_expand_popover.ts

Then, use_graph_filters.ts observes mutations in filters and re-renders the entire graph.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved this file and its tests to be co-located with filter functionality added. They were mixed with non-related stuff in graph_investigation/ folder.

I kept this file since it contains only pure functions to add, remove or check filters. filter_store.ts mutates state using these functions

Comment on lines +31 to +43
const store = useMemo(() => getOrCreateFilterStore(scopeId), [scopeId]);

// Update dataViewId when it changes
useEffect(() => {
store.setDataViewId(dataViewId);
}, [store, dataViewId]);

// Clean up store on unmount or when scopeId changes
useEffect(() => {
return () => {
destroyFilterStore(scopeId);
};
}, [scopeId]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we might have multiple graph instances in Kibana, each graph has access to its own filter store instance through the scopeId. This prevents from cross-publishing or cross-listening to events from other graphs

Comment on lines 57 to 66
// Use React 18's useSyncExternalStore for optimal concurrent rendering support
const searchFilters = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);

// Callback for SearchBar's onFiltersUpdated - sets filters directly in store
const setSearchFilters = useCallback(
(filters: Filter[]) => {
store.setFilters(filters);
},
[store]
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the recommended way to observe and get in sync a non-React-based store and React rendering, preventing from concurrency issues at rendering time.

Comment on lines 64 to 77
const items = getEntityExpandItems({
nodeId: item.id,
sourceNamespace: item.ecsParentField,
onShowEntityDetails: item.availableInEntityStore ? handleShowEntityDetails : undefined,
onClose: closePopover,
isFilterActive: (field, value) => isFilterActiveForScope(scopeId, field, value),
toggleFilter: (field, value, action) => emitFilterToggle(scopeId, field, value, action),
shouldRender: {
showActionsByEntity: true,
showActionsOnEntity: true,
showRelatedEvents: true,
showEntityDetails: !!item.availableInEntityStore,
},
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

getEntityExpandItems gives us a single source of truth for popover items. Each item is rendered in an opt-in way via the shouldRender prop.

getEntityExpandItems is also invoked from the graph hooks but in different conditions (graph can render single or grouped nodes while flyout only renders single items). To keep it simple, the function provides with enough callbacks and boolean arguments

Comment on lines +32 to +54
export const GENERIC_ENTITY_PREVIEW_BANNER = {
title: i18n.translate(`${i18nNamespaceKey}.entityPreviewBannerTitle`, {
defaultMessage: 'Entity preview',
}),
backgroundColor: 'primary',
textColor: 'default',
};

export const ALERT_PREVIEW_BANNER = {
title: i18n.translate(`${i18nNamespaceKey}.alertPreviewBannerTitle`, {
defaultMessage: 'Alert preview',
}),
backgroundColor: 'danger',
textColor: 'danger',
};

export const EVENT_PREVIEW_BANNER = {
title: i18n.translate(`${i18nNamespaceKey}.eventPreviewBannerTitle`, {
defaultMessage: 'Event preview',
}),
backgroundColor: 'warning',
textColor: 'warning',
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're duplicating now these banner configs that already exist in x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/preview/constants.ts. Reason is, obviously, they live in a separate package. In an ideal world, this could be common config across packages. Let me know if I should tackle it now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This used to be the tiny pub-sub to open entity/event/alert previews when clicking on the grouped-item title. Replaced with a direct Flyout API call

@albertoblaz albertoblaz marked this pull request as ready for review January 23, 2026 09:00
@albertoblaz albertoblaz requested review from a team as code owners January 23, 2026 09:00
@elasticmachine
Copy link
Contributor

Pinging @elastic/contextual-security-apps (Team:Cloud Security)

@albertoblaz
Copy link
Contributor Author

@elasticmachine run docs-build

@albertoblaz albertoblaz marked this pull request as draft February 18, 2026 10:16
albertoblaz added a commit that referenced this pull request Feb 19, 2026
## Summary

Part of the fixes needed to close
#239954

PRs:
1. #251299 <-- this PR
2. #250105

API allows for pinning events/alerts by document ID but we don't have a
proper way to represent that in the UI so we're skipping for now

### Screenshots

<details><summary>Pinning no entities from the group</summary>
<img width="1660" height="842" alt="Screenshot 2026-02-02 at 19 18 56"
src="https://github.com/user-attachments/assets/407c4b99-f0a2-441f-8731-e5b5a1101734"
/>
</details> 

<details><summary>Pinning "admin2@example.com" entity</summary>
<img width="1665" height="952" alt="Screenshot 2026-02-02 at 19 22 15"
src="https://github.com/user-attachments/assets/b45f8bbf-cd11-4db9-ae9a-37813b4919fa"
/>
</details> 

<details><summary>Pinning "admin2@example.com" and
"admin-user2@example.com" entities</summary>
<img width="1659" height="1023" alt="Screenshot 2026-02-02 at 19 23 11"
src="https://github.com/user-attachments/assets/f701d0ea-debb-40d5-ade3-1c0d356e2d62"
/>
</details> 

<details><summary>Pinning all entities in actor node</summary>
<img width="1661" height="1479" alt="Screenshot 2026-02-02 at 19 28 10"
src="https://github.com/user-attachments/assets/20938268-0161-4767-a9d5-904bb6cde446"
/>
</details> 

<details><summary>pinnedIds field sent in the request payload</summary>
<img width="927" height="311" alt="Screenshot 2026-02-02 at 19 35 56"
src="https://github.com/user-attachments/assets/c9052483-5dff-4e1d-a1cb-b979c3bbf5b5"
/>
</details> 

### How to test

1. Deploy a local env using the following command:
`yarn es snapshot --license trial -E
xpack.security.authc.api_key.enabled=true`
2. Run kibana using `yarn start --no-base-path`
3. Go to `Advanced settings` and make sure these toggles are on:
    - `securitySolution:enableGraphVisualization`
    - `securitySolution:enableAssetInventory`
4. Run these commands:
    ```bash
node scripts/es_archiver load
x-pack/solutions/security/test/cloud_security_posture_functional/es_archives/logs_gcp_audit
--es-url http://elastic:changeme@localhost:9200 --kibana-url
http://elastic:changeme@localhost:5601
    
node scripts/es_archiver load
x-pack/solutions/security/test/cloud_security_posture_functional/es_archives/security_alerts_modified_mappings
--es-url http://elastic:changeme@localhost:9200 --kibana-url
http://elastic:changeme@localhost:5601
    
node scripts/es_archiver load
x-pack/solutions/security/test/cloud_security_posture_functional/es_archives/entity_store
--es-url http://elastic:changeme@localhost:9200 --kibana-url
http://elastic:changeme@localhost:5601
    
node scripts/es_archiver load
x-pack/solutions/security/test/cloud_security_posture_functional/es_archives/entity_store_v2
--es-url http://elastic:changeme@localhost:9200 --kibana-url
http://elastic:changeme@localhost:5601
    ```
5. Go to Security -> Explore -> Network/Users/Hosts
6. Open an event's flyout, then expand Graph Visualization. Apply
filters to see events from September, 1st 2017 till Now. Add an
`event.action` filter set to "google.iam.admin.v1.CreateUser", then keep
adding `user.entity.id` or `event.id` filters set to the IDs in the
entity node using the `OR` operator
7. Individual entities/events/alerts should be pinned correctly in the
graph, outside the entity/label groups

### Checklist

- [ ] 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/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [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
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [x] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [x] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks
@elasticmachine
Copy link
Contributor

elasticmachine commented Feb 24, 2026

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] Jest Tests #8 / Header QueryTabHeader should render the immutable timeline call out with correct message

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
securitySolution 8795 8800 +5

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/cloud-security-posture-graph 58 57 -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 11.1MB 11.1MB +7.0KB
Unknown metric groups

API count

id before after diff
@kbn/cloud-security-posture-graph 91 89 -2

ESLint disabled line counts

id before after diff
@kbn/cloud-security-posture-graph 30 31 +1
securitySolution 717 716 -1
total -0

Total ESLint disabled count

id before after diff
@kbn/cloud-security-posture-graph 32 33 +1
securitySolution 822 821 -1
total -0

History

cc @albertoblaz

@albertoblaz albertoblaz marked this pull request as ready for review February 26, 2026 15:42
@kfirpeled
Copy link
Contributor

While testing it, few questions comes to my mind:

  1. When we show related events, we don't separate/pin the entity. Worth checking with design whether to keep it consistent with show actions done on this entity or by this entity
  2. We don't show Show entity details option on the preview flyout of grouped entities. I'd expect to show it disabled when it is not available. To keep it consistent. wdyt?

Issues found:

  1. Show entity details not working
Screen.Recording.2026-03-01.at.15.11.25.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting ci:build-serverless-image release_note:skip Skip the PR/issue when compiling release notes Team:Cloud Security Cloud Security team related v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update graph visualization through actions in grouped entities/events flyout

3 participants