Mutate Graph filters from flyout actions#250105
Mutate Graph filters from flyout actions#250105albertoblaz wants to merge 20 commits intoelastic:mainfrom
Conversation
| openPreviewPanel({ | ||
| id: GenericEntityPanelKey, | ||
| params: { | ||
| entityId: item.id, | ||
| scopeId, | ||
| isPreviewMode: true, | ||
| banner: GENERIC_ENTITY_PREVIEW_BANNER, | ||
| isEngineMetadataExist: !!item.availableInEntityStore, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
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 renderGraphInvestigation - Co-locates button and correct preview panel i.e. opens entity preview from entity and button and event/alert previews from event button
There was a problem hiding this comment.
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.tsxandevent_actions_button.tsx - the hooks in graph i.e.
use_entity_node_expand_popover.tsanduse_label_node_expand_popover.ts
- the new buttons in flyout i.e.
Then, use_graph_filters.ts observes mutations in filters and re-renders the entire graph.
There was a problem hiding this comment.
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
| 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]); |
There was a problem hiding this comment.
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
| // 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] | ||
| ); |
There was a problem hiding this comment.
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.
| 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, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
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
| 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', | ||
| }; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
|
Pinging @elastic/contextual-security-apps (Team:Cloud Security) |
|
@elasticmachine run docs-build |
## 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
57f2277 to
6736fd0
Compare
💛 Build succeeded, but was flaky
Failed CI StepsTest Failures
Metrics [docs]Module Count
Public APIs missing comments
Async chunks
Unknown metric groupsAPI count
ESLint disabled line counts
Total ESLint disabled count
History
cc @albertoblaz |
|
While testing it, few questions comes to my mind:
Issues found:
Screen.Recording.2026-03-01.at.15.11.25.mov |
Summary
Closes #239954.
PRs involved:
Key Changes
Screenshots
Actions in graph node - enabled actions (entity available in Entity Store)
Actions in flyout - disabled entity relationships action
Actions in graph node - disabled actions (entity unavailable)
How to test
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=trueyarn startAdvanced settingsand make suresecuritySolution:enableGraphVisualizationandsecuritySolution:enableAssetInventoryfeatures are toggled on.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:5601node 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:5601Navigate to Security -> Explore -> User
extend the time range to 2-3 years and check how the events and rendered, few examples:
Toggle the graph searchbar (blue primary button at the top-right corner within the graph).
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
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.
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
release_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.Identify risks
Risk of breaking filters in graph investigation after major refactor.