[8.x] [Cloud Security] Collecting telemetry of graph visualization usage (#207154) (#208808)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Cloud Security] Collecting telemetry of graph visualization usage
(#207154)](https://github.com/elastic/kibana/pull/207154)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Kfir
Peled","email":"61654899+kfirpeled@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-01-29T17:31:20Z","message":"[Cloud
Security] Collecting telemetry of graph visualization usage
(#207154)\n\n## Summary\r\n\r\nCollects two events of graph
visualization usage\r\n\r\n1. When graph preview is shown - allows to
determine how many users\r\ningested data that is graph compliant\r\n2.
When graph investigation component is visible - allows to
determine\r\nhow many users entered the investigation user
flow\r\n\r\n**How to test:**\r\n\r\n- Enable the feature flag
\r\n\r\n`kibana.dev.yml`:\r\n\r\n```yaml\r\nuiSettings.overrides.securitySolution:enableVisualizationsInFlyout:
true\r\nuiSettings.overrides.securitySolution:enableGraphVisualization:
true\r\n```\r\n\r\n- Load mocked data:\r\n\r\n```bash\r\nnode
scripts/es_archiver load
x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit
\\ \r\n --es-url http://elastic:changeme@localhost:9200 \\\r\n
--kibana-url http://elastic:changeme@localhost:5601\r\n\r\nnode
scripts/es_archiver load
x-pack/test/cloud_security_posture_functional/es_archives/security_alerts
\\\r\n --es-url http://elastic:changeme@localhost:9200 \\\r\n
--kibana-url http://elastic:changeme@localhost:5601\r\n```\r\n\r\n- Make
sure you include data from Oct 13 2024. (in the video I use
Last\r\nyear)\r\n- Run in dev tools:\r\n```\r\nPOST
kbn:/internal/telemetry/clusters/_stats?pretty=true&apiVersion=2\r\n{\r\n
\"unencrypted\": true,\r\n \"refreshCache\": true\r\n}\r\n```\r\n- Check
if the ui_counters were reported\r\n\r\n![Screenshot 2025-01-28 at 23
22\r\n08](https://github.com/user-attachments/assets/b09d4870-80f1-4026-bc98-28ad1d495ffb)","sha":"7ac553ce89e987cb08128afff4f9fdcefc48e0c1","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Cloud
Security","backport:prev-minor"],"title":"[Cloud Security] Collecting
telemetry of graph visualization
usage","number":207154,"url":"https://github.com/elastic/kibana/pull/207154","mergeCommit":{"message":"[Cloud
Security] Collecting telemetry of graph visualization usage
(#207154)\n\n## Summary\r\n\r\nCollects two events of graph
visualization usage\r\n\r\n1. When graph preview is shown - allows to
determine how many users\r\ningested data that is graph compliant\r\n2.
When graph investigation component is visible - allows to
determine\r\nhow many users entered the investigation user
flow\r\n\r\n**How to test:**\r\n\r\n- Enable the feature flag
\r\n\r\n`kibana.dev.yml`:\r\n\r\n```yaml\r\nuiSettings.overrides.securitySolution:enableVisualizationsInFlyout:
true\r\nuiSettings.overrides.securitySolution:enableGraphVisualization:
true\r\n```\r\n\r\n- Load mocked data:\r\n\r\n```bash\r\nnode
scripts/es_archiver load
x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit
\\ \r\n --es-url http://elastic:changeme@localhost:9200 \\\r\n
--kibana-url http://elastic:changeme@localhost:5601\r\n\r\nnode
scripts/es_archiver load
x-pack/test/cloud_security_posture_functional/es_archives/security_alerts
\\\r\n --es-url http://elastic:changeme@localhost:9200 \\\r\n
--kibana-url http://elastic:changeme@localhost:5601\r\n```\r\n\r\n- Make
sure you include data from Oct 13 2024. (in the video I use
Last\r\nyear)\r\n- Run in dev tools:\r\n```\r\nPOST
kbn:/internal/telemetry/clusters/_stats?pretty=true&apiVersion=2\r\n{\r\n
\"unencrypted\": true,\r\n \"refreshCache\": true\r\n}\r\n```\r\n- Check
if the ui_counters were reported\r\n\r\n![Screenshot 2025-01-28 at 23
22\r\n08](https://github.com/user-attachments/assets/b09d4870-80f1-4026-bc98-28ad1d495ffb)","sha":"7ac553ce89e987cb08128afff4f9fdcefc48e0c1"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/207154","number":207154,"mergeCommit":{"message":"[Cloud
Security] Collecting telemetry of graph visualization usage
(#207154)\n\n## Summary\r\n\r\nCollects two events of graph
visualization usage\r\n\r\n1. When graph preview is shown - allows to
determine how many users\r\ningested data that is graph compliant\r\n2.
When graph investigation component is visible - allows to
determine\r\nhow many users entered the investigation user
flow\r\n\r\n**How to test:**\r\n\r\n- Enable the feature flag
\r\n\r\n`kibana.dev.yml`:\r\n\r\n```yaml\r\nuiSettings.overrides.securitySolution:enableVisualizationsInFlyout:
true\r\nuiSettings.overrides.securitySolution:enableGraphVisualization:
true\r\n```\r\n\r\n- Load mocked data:\r\n\r\n```bash\r\nnode
scripts/es_archiver load
x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit
\\ \r\n --es-url http://elastic:changeme@localhost:9200 \\\r\n
--kibana-url http://elastic:changeme@localhost:5601\r\n\r\nnode
scripts/es_archiver load
x-pack/test/cloud_security_posture_functional/es_archives/security_alerts
\\\r\n --es-url http://elastic:changeme@localhost:9200 \\\r\n
--kibana-url http://elastic:changeme@localhost:5601\r\n```\r\n\r\n- Make
sure you include data from Oct 13 2024. (in the video I use
Last\r\nyear)\r\n- Run in dev tools:\r\n```\r\nPOST
kbn:/internal/telemetry/clusters/_stats?pretty=true&apiVersion=2\r\n{\r\n
\"unencrypted\": true,\r\n \"refreshCache\": true\r\n}\r\n```\r\n- Check
if the ui_counters were reported\r\n\r\n![Screenshot 2025-01-28 at 23
22\r\n08](https://github.com/user-attachments/assets/b09d4870-80f1-4026-bc98-28ad1d495ffb)","sha":"7ac553ce89e987cb08128afff4f9fdcefc48e0c1"}}]}]
BACKPORT-->

Co-authored-by: Kfir Peled <61654899+kfirpeled@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2025-01-30 06:22:24 +11:00 committed by GitHub
parent 273630d3a7
commit 89fe701d70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 171 additions and 63 deletions

View file

@ -10,35 +10,45 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
export const APP_NAME = 'cloud-security';
export const MISCONFIGURATION_INSIGHT = 'misconfiguration-insight';
export const VULNERABILITIES_INSIGHT = 'vulnerabilities-insight';
export const MISCONFIGURATION_INSIGHT_HOST_DETAILS = `${MISCONFIGURATION_INSIGHT}-host-details`;
export const MISCONFIGURATION_INSIGHT_USER_DETAILS = `${MISCONFIGURATION_INSIGHT}-user-details`;
export const MISCONFIGURATION_INSIGHT_HOST_ENTITY_OVERVIEW = `${MISCONFIGURATION_INSIGHT}-host-entity-overview`;
export const MISCONFIGURATION_INSIGHT_USER_ENTITY_OVERVIEW = `${MISCONFIGURATION_INSIGHT}-user-entity-overview`;
export const VULNERABILITIES_INSIGHT_HOST_DETAILS = `${VULNERABILITIES_INSIGHT}-host-details`;
export const VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW = `${VULNERABILITIES_INSIGHT}-host-entity-overview`;
export const MISCONFIGURATION_INSIGHT = 'misconfiguration-insight' as const;
export const VULNERABILITIES_INSIGHT = 'vulnerabilities-insight' as const;
export const MISCONFIGURATION_INSIGHT_HOST_DETAILS =
`${MISCONFIGURATION_INSIGHT}-host-details` as const;
export const MISCONFIGURATION_INSIGHT_USER_DETAILS =
`${MISCONFIGURATION_INSIGHT}-user-details` as const;
export const MISCONFIGURATION_INSIGHT_HOST_ENTITY_OVERVIEW =
`${MISCONFIGURATION_INSIGHT}-host-entity-overview` as const;
export const MISCONFIGURATION_INSIGHT_USER_ENTITY_OVERVIEW =
`${MISCONFIGURATION_INSIGHT}-user-entity-overview` as const;
export const VULNERABILITIES_INSIGHT_HOST_DETAILS =
`${VULNERABILITIES_INSIGHT}-host-details` as const;
export const VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW =
`${VULNERABILITIES_INSIGHT}-host-entity-overview` as const;
export const ENTITY_FLYOUT_WITH_MISCONFIGURATION_VISIT =
'entity-flyout-with-misconfiguration-visits';
'entity-flyout-with-misconfiguration-visits' as const;
export const ENTITY_FLYOUT_WITH_VULNERABILITY_PREVIEW =
'entity-flyout-with-vulnerability-preview-visits';
'entity-flyout-with-vulnerability-preview-visits' as const;
export const ENTITY_FLYOUT_EXPAND_MISCONFIGURATION_VIEW_VISITS =
'entity-flyout-expand-misconfiguration-view-visits';
'entity-flyout-expand-misconfiguration-view-visits' as const;
export const ENTITY_FLYOUT_EXPAND_VULNERABILITY_VIEW_VISITS =
'entity-flyout-expand-vulnerability-view-visits';
'entity-flyout-expand-vulnerability-view-visits' as const;
export const NAV_TO_FINDINGS_BY_HOST_NAME_FRPOM_ENTITY_FLYOUT =
'nav-to-findings-by-host-name-from-entity-flyout';
'nav-to-findings-by-host-name-from-entity-flyout' as const;
export const NAV_TO_FINDINGS_BY_RULE_NAME_FRPOM_ENTITY_FLYOUT =
'nav-to-findings-by-rule-name-from-entity-flyout';
export const CREATE_DETECTION_RULE_FROM_FLYOUT = 'create-detection-rule-from-flyout';
export const CREATE_DETECTION_FROM_TABLE_ROW_ACTION = 'create-detection-from-table-row-action';
export const VULNERABILITIES_FLYOUT_VISITS = 'vulnerabilities-flyout-visits';
export const OPEN_FINDINGS_FLYOUT = 'open-findings-flyout';
export const GROUP_BY_CLICK = 'group-by-click';
export const CHANGE_RULE_STATE = 'change-rule-state';
'nav-to-findings-by-rule-name-from-entity-flyout' as const;
export const CREATE_DETECTION_RULE_FROM_FLYOUT = 'create-detection-rule-from-flyout' as const;
export const CREATE_DETECTION_FROM_TABLE_ROW_ACTION =
'create-detection-from-table-row-action' as const;
export const VULNERABILITIES_FLYOUT_VISITS = 'vulnerabilities-flyout-visits' as const;
export const OPEN_FINDINGS_FLYOUT = 'open-findings-flyout' as const;
export const GROUP_BY_CLICK = 'group-by-click' as const;
export const CHANGE_RULE_STATE = 'change-rule-state' as const;
type CloudSecurityUiCounters =
export const GRAPH_PREVIEW = 'graph-preview' as const;
export const GRAPH_INVESTIGATION = 'graph-investigation' as const;
export type CloudSecurityUiCounters =
| typeof ENTITY_FLYOUT_WITH_MISCONFIGURATION_VISIT
| typeof ENTITY_FLYOUT_WITH_VULNERABILITY_PREVIEW
| typeof ENTITY_FLYOUT_EXPAND_MISCONFIGURATION_VIEW_VISITS
@ -56,7 +66,9 @@ type CloudSecurityUiCounters =
| typeof MISCONFIGURATION_INSIGHT_HOST_ENTITY_OVERVIEW
| typeof MISCONFIGURATION_INSIGHT_USER_ENTITY_OVERVIEW
| typeof VULNERABILITIES_INSIGHT_HOST_DETAILS
| typeof VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW;
| typeof VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW
| typeof GRAPH_PREVIEW
| typeof GRAPH_INVESTIGATION;
export class UiMetricService {
private usageCollection: UsageCollectionSetup | undefined;

View file

@ -25,6 +25,10 @@ import type { EuiBasicTableColumn } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import {
MISCONFIGURATION_INSIGHT_HOST_DETAILS,
VULNERABILITIES_INSIGHT_HOST_DETAILS,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import type { RelatedUser } from '../../../../../common/search_strategy/security_solution/related_entities/related_users';
import type { RiskSeverity } from '../../../../../common/search_strategy';
@ -346,13 +350,13 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s
name={hostName}
direction="column"
data-test-subj={HOST_DETAILS_MISCONFIGURATIONS_TEST_ID}
telemetrySuffix={'host-details'}
telemetryKey={MISCONFIGURATION_INSIGHT_HOST_DETAILS}
/>
<VulnerabilitiesInsight
hostName={hostName}
direction="column"
data-test-subj={HOST_DETAILS_VULNERABILITIES_TEST_ID}
telemetrySuffix={'host-details'}
telemetryKey={VULNERABILITIES_INSIGHT_HOST_DETAILS}
/>
</EuiFlexGrid>
<EuiSpacer size="l" />

View file

@ -25,6 +25,7 @@ import type { EuiBasicTableColumn } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { MISCONFIGURATION_INSIGHT_USER_DETAILS } from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import type { RelatedHost } from '../../../../../common/search_strategy/security_solution/related_entities/related_hosts';
import type { RiskSeverity } from '../../../../../common/search_strategy';
@ -346,7 +347,7 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s
name={userName}
direction="column"
data-test-subj={USER_DETAILS_MISCONFIGURATIONS_TEST_ID}
telemetrySuffix={'user-details'}
telemetryKey={MISCONFIGURATION_INSIGHT_USER_DETAILS}
/>
</EuiFlexGrid>
<EuiSpacer size="l" />

View file

@ -11,6 +11,10 @@ import type { EuiButtonGroupOptionProps } from '@elastic/eui/src/components/butt
import { useExpandableFlyoutApi, useExpandableFlyoutState } from '@kbn/expandable-flyout';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import {
uiMetricService,
GRAPH_INVESTIGATION,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { useDocumentDetailsContext } from '../../shared/context';
import { useWhichFlyout } from '../../shared/hooks/use_which_flyout';
@ -31,6 +35,7 @@ import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions';
import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction';
import { GRAPH_ID, GraphVisualization } from '../components/graph_visualization';
import { useGraphPreview } from '../../shared/hooks/use_graph_preview';
import { METRIC_TYPE } from '../../../../common/lib/telemetry';
import { ENABLE_GRAPH_VISUALIZATION_SETTING } from '../../../../../common/constants';
const visualizeButtons: EuiButtonGroupOptionProps[] = [
@ -109,6 +114,8 @@ export const VisualizeTab = memo(() => {
banner: ANALYZER_PREVIEW_BANNER,
},
});
} else if (optionId === GRAPH_ID) {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, GRAPH_INVESTIGATION);
}
},
[startTransaction, openPreviewPanel, key, scopeId]
@ -117,6 +124,10 @@ export const VisualizeTab = memo(() => {
useEffect(() => {
if (panels.left?.path?.subTab) {
setActiveVisualizationId(panels.left?.path?.subTab);
if (panels.left?.path?.subTab === GRAPH_ID) {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, GRAPH_INVESTIGATION);
}
}
}, [panels.left?.path?.subTab]);

View file

@ -5,10 +5,15 @@
* 2.0.
*/
import React from 'react';
import { render } from '@testing-library/react';
import { useFetchGraphData } from '@kbn/cloud-security-posture-graph/src/hooks';
import {
uiMetricService,
GRAPH_PREVIEW,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import { TestProviders } from '../../../../common/mock';
import React from 'react';
import { DocumentDetailsContext } from '../../shared/context';
import { mockContextValue } from '../../shared/mocks/mock_context';
import { GraphPreviewContainer } from './graph_preview_container';
@ -23,6 +28,14 @@ import {
} from '../../../shared/components/test_ids';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
jest.mock('@kbn/cloud-security-posture-common/utils/ui_metrics', () => ({
uiMetricService: {
trackUiMetric: jest.fn(),
},
}));
const uiMetricServiceMock = uiMetricService as jest.Mocked<typeof uiMetricService>;
const mockUseUiSetting = jest.fn().mockReturnValue([true]);
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
@ -173,6 +186,10 @@ describe('<GraphPreviewContainer />', () => {
refetchOnWindowFocus: false,
},
});
expect(uiMetricServiceMock.trackUiMetric).toHaveBeenCalledWith(
METRIC_TYPE.LOADED,
GRAPH_PREVIEW
);
});
it('should render component for event', async () => {
@ -224,6 +241,10 @@ describe('<GraphPreviewContainer />', () => {
refetchOnWindowFocus: false,
},
});
expect(uiMetricServiceMock.trackUiMetric).toHaveBeenCalledWith(
METRIC_TYPE.LOADED,
GRAPH_PREVIEW
);
});
it('should render component and without link in header in preview panel', async () => {
@ -437,5 +458,7 @@ describe('<GraphPreviewContainer />', () => {
refetchOnWindowFocus: false,
},
});
expect(uiMetricServiceMock.trackUiMetric).not.toHaveBeenCalled();
});
});

View file

@ -5,12 +5,17 @@
* 2.0.
*/
import React from 'react';
import React, { useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { EuiBetaBadge } from '@elastic/eui';
import { EuiBetaBadge, useGeneratedHtmlId } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useFetchGraphData } from '@kbn/cloud-security-posture-graph/src/hooks';
import {
uiMetricService,
GRAPH_PREVIEW,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants';
import { useDocumentDetailsContext } from '../../shared/context';
import { GRAPH_PREVIEW_TEST_ID } from './test_ids';
@ -23,6 +28,7 @@ import { ExpandablePanel } from '../../../shared/components/expandable_panel';
* Graph preview under Overview, Visualizations. It shows a graph representation of entities.
*/
export const GraphPreviewContainer: React.FC = () => {
const renderingId = useGeneratedHtmlId();
const {
dataAsNestedObject,
getFieldsData,
@ -72,6 +78,12 @@ export const GraphPreviewContainer: React.FC = () => {
},
});
useEffect(() => {
if (hasGraphRepresentation) {
uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, GRAPH_PREVIEW);
}
}, [hasGraphRepresentation, renderingId]);
return (
<ExpandablePanel
header={{

View file

@ -18,6 +18,10 @@ import {
import { css } from '@emotion/react';
import { getOr } from 'lodash/fp';
import { i18n } from '@kbn/i18n';
import {
MISCONFIGURATION_INSIGHT_HOST_ENTITY_OVERVIEW,
VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { buildHostNamesFilter } from '../../../../../common/search_strategy';
import { HOST_NAME_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants';
import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
@ -253,12 +257,12 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
fieldName={'host.name'}
name={hostName}
data-test-subj={ENTITIES_HOST_OVERVIEW_MISCONFIGURATIONS_TEST_ID}
telemetrySuffix={'host-entity-overview'}
telemetryKey={MISCONFIGURATION_INSIGHT_HOST_ENTITY_OVERVIEW}
/>
<VulnerabilitiesInsight
hostName={hostName}
data-test-subj={ENTITIES_HOST_OVERVIEW_VULNERABILITIES_TEST_ID}
telemetrySuffix={'host-entity-overview'}
telemetryKey={VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW}
/>
</EuiFlexGroup>
);

View file

@ -18,6 +18,7 @@ import {
import { css } from '@emotion/react';
import { getOr } from 'lodash/fp';
import { i18n } from '@kbn/i18n';
import { MISCONFIGURATION_INSIGHT_USER_ENTITY_OVERVIEW } from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { buildUserNamesFilter } from '../../../../../common/search_strategy';
import { useDocumentDetailsContext } from '../../shared/context';
import type { DescriptionList } from '../../../../../common/utility_types';
@ -250,7 +251,7 @@ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName
fieldName={'user.name'}
name={userName}
data-test-subj={ENTITIES_USER_OVERVIEW_MISCONFIGURATIONS_TEST_ID}
telemetrySuffix={'user-entity-overview'}
telemetryKey={MISCONFIGURATION_INSIGHT_USER_ENTITY_OVERVIEW}
/>
</EuiFlexGroup>
);

View file

@ -9,6 +9,11 @@ import React from 'react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { render } from '@testing-library/react';
import { useFetchGraphData } from '@kbn/cloud-security-posture-graph/src/hooks';
import {
uiMetricService,
GRAPH_PREVIEW,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import {
ANALYZER_PREVIEW_TEST_ID,
SESSION_PREVIEW_TEST_ID,
@ -84,6 +89,14 @@ jest.mock('@kbn/cloud-security-posture-graph/src/hooks', () => ({
const mockUseFetchGraphData = useFetchGraphData as jest.Mock;
jest.mock('@kbn/cloud-security-posture-common/utils/ui_metrics', () => ({
uiMetricService: {
trackUiMetric: jest.fn(),
},
}));
const uiMetricServiceMock = uiMetricService as jest.Mocked<typeof uiMetricService>;
const panelContextValue = {
...mockContextValue,
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
@ -182,6 +195,10 @@ describe('<VisualizationsSection />', () => {
const { getByTestId } = renderVisualizationsSection();
expect(getByTestId(`${GRAPH_PREVIEW_TEST_ID}LeftSection`)).toBeInTheDocument();
expect(uiMetricServiceMock.trackUiMetric).toHaveBeenCalledWith(
METRIC_TYPE.LOADED,
GRAPH_PREVIEW
);
});
it('should not render the graph preview component if the graph feature is disabled', () => {

View file

@ -6,14 +6,14 @@
*/
import React, { useEffect, useMemo } from 'react';
import { EuiFlexItem, type EuiFlexGroupProps, useEuiTheme } from '@elastic/eui';
import { EuiFlexItem, type EuiFlexGroupProps, useEuiTheme, useGeneratedHtmlId } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview';
import { buildGenericEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common';
import {
MISCONFIGURATION_INSIGHT,
uiMetricService,
type CloudSecurityUiCounters,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import { InsightDistributionBar } from './insight_distribution_bar';
@ -42,7 +42,7 @@ interface MisconfigurationsInsightProps {
/**
* used to track the instance of this component, prefer kebab-case
*/
telemetrySuffix?: string;
telemetryKey?: CloudSecurityUiCounters;
}
/*
@ -53,8 +53,9 @@ export const MisconfigurationsInsight: React.FC<MisconfigurationsInsightProps> =
fieldName,
direction,
'data-test-subj': dataTestSubj,
telemetrySuffix,
telemetryKey,
}) => {
const renderingId = useGeneratedHtmlId();
const { scopeId, isPreview } = useDocumentDetailsContext();
const { euiTheme } = useEuiTheme();
const { data } = useMisconfigurationPreview({
@ -65,12 +66,10 @@ export const MisconfigurationsInsight: React.FC<MisconfigurationsInsightProps> =
});
useEffect(() => {
uiMetricService.trackUiMetric(
METRIC_TYPE.COUNT,
`${MISCONFIGURATION_INSIGHT}-${telemetrySuffix}`
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (telemetryKey) {
uiMetricService.trackUiMetric(METRIC_TYPE.COUNT, telemetryKey);
}
}, [telemetryKey, renderingId]);
const passedFindings = data?.count.passed || 0;
const failedFindings = data?.count.failed || 0;

View file

@ -6,7 +6,7 @@
*/
import React, { useEffect, useMemo } from 'react';
import { EuiFlexItem, type EuiFlexGroupProps, useEuiTheme } from '@elastic/eui';
import { EuiFlexItem, type EuiFlexGroupProps, useEuiTheme, useGeneratedHtmlId } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview';
@ -14,7 +14,7 @@ import { buildGenericEntityFlyoutPreviewQuery } from '@kbn/cloud-security-postur
import { getVulnerabilityStats, hasVulnerabilitiesData } from '@kbn/cloud-security-posture';
import {
uiMetricService,
VULNERABILITIES_INSIGHT,
type CloudSecurityUiCounters,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import { InsightDistributionBar } from './insight_distribution_bar';
@ -38,7 +38,7 @@ interface VulnerabilitiesInsightProps {
/**
* used to track the instance of this component, prefer kebab-case
*/
telemetrySuffix?: string;
telemetryKey?: CloudSecurityUiCounters;
}
/*
@ -48,8 +48,9 @@ export const VulnerabilitiesInsight: React.FC<VulnerabilitiesInsightProps> = ({
hostName,
direction,
'data-test-subj': dataTestSubj,
telemetrySuffix,
telemetryKey,
}) => {
const renderingId = useGeneratedHtmlId();
const { scopeId, isPreview } = useDocumentDetailsContext();
const { euiTheme } = useEuiTheme();
const { data } = useVulnerabilitiesPreview({
@ -60,12 +61,10 @@ export const VulnerabilitiesInsight: React.FC<VulnerabilitiesInsightProps> = ({
});
useEffect(() => {
uiMetricService.trackUiMetric(
METRIC_TYPE.COUNT,
`${VULNERABILITIES_INSIGHT}-${telemetrySuffix}`
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (telemetryKey) {
uiMetricService.trackUiMetric(METRIC_TYPE.COUNT, telemetryKey);
}
}, [telemetryKey, renderingId]);
const { CRITICAL = 0, HIGH = 0, MEDIUM = 0, LOW = 0, NONE = 0 } = data?.count || {};
const totalVulnerabilities = useMemo(

View file

@ -6,10 +6,25 @@
*/
import { resolve } from 'path';
import type { FtrConfigProviderContext } from '@kbn/test';
import type { FtrConfigProviderContext, GenericFtrProviderContext } from '@kbn/test';
import { CLOUD_SECURITY_PLUGIN_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants';
import {
KibanaEBTServerProvider,
KibanaEBTUIProvider,
} from '@kbn/test-suites-src/analytics/services/kibana_ebt';
import { pageObjects } from './page_objects';
import { services } from './services';
import type { services as inheritedServices } from '../functional/services';
type SecurityTelemetryServices = typeof inheritedServices &
typeof services & {
kibana_ebt_ui: typeof KibanaEBTUIProvider;
};
export type SecurityTelemetryFtrProviderContext = GenericFtrProviderContext<
SecurityTelemetryServices,
typeof pageObjects
>;
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const xpackFunctionalConfig = await readConfigFile(
@ -21,6 +36,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
services: {
...xpackFunctionalConfig.get('services'),
...services,
kibana_ebt_server: KibanaEBTServerProvider,
kibana_ebt_ui: KibanaEBTUIProvider,
},
pageObjects,
testFiles: [resolve(__dirname, './pages')],
@ -50,6 +67,11 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
// `--xpack.fleet.registryUrl=https://localhost:8080`,
`--xpack.fleet.agents.fleet_server.hosts=["https://ftr.kibana:8220"]`,
`--xpack.fleet.internal.fleetServerStandalone=true`,
// Required for telemetry e2e tests
`--plugin-path=${resolve(
__dirname,
'../../../test/analytics/plugins/analytics_ftr_helpers'
)}`,
],
},
};

View file

@ -6,10 +6,11 @@
*/
import expect from '@kbn/expect';
import { GenericFtrService } from '@kbn/test';
import type { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
import type { FilterBarService } from '@kbn/test-suites-src/functional/services/filter_bar';
import { FtrService } from '../../functional/ftr_provider_context';
import type { QueryBarProvider } from '../services/query_bar_provider';
import type { SecurityTelemetryFtrProviderContext } from '../config';
const GRAPH_PREVIEW_TITLE_LINK_TEST_ID = 'securitySolutionFlyoutGraphPreviewTitleLink';
const NODE_EXPAND_BUTTON_TEST_ID = 'cloudSecurityGraphNodeExpandButton';
@ -24,7 +25,7 @@ const GRAPH_ACTIONS_TOGGLE_SEARCH_ID = `${GRAPH_INVESTIGATION_TEST_ID}ToggleSear
const GRAPH_ACTIONS_INVESTIGATE_IN_TIMELINE_ID = `${GRAPH_INVESTIGATION_TEST_ID}InvestigateInTimeline`;
type Filter = Parameters<FilterBarService['addFilter']>[0];
export class ExpandedFlyoutGraph extends FtrService {
export class ExpandedFlyoutGraph extends GenericFtrService<SecurityTelemetryFtrProviderContext> {
private readonly pageObjects = this.ctx.getPageObjects(['common', 'header']);
private readonly testSubjects = this.ctx.getService('testSubjects');
private readonly filterBar = this.ctx.getService('filterBar');
@ -154,7 +155,6 @@ export class ExpandedFlyoutGraph extends FtrService {
}
async setKqlQuery(kql: string): Promise<void> {
// @ts-expect-error queryBarProvider is not a public service
const queryBarProvider: QueryBarProvider = this.ctx.getService('queryBarProvider');
const queryBar = queryBarProvider.getQueryBar(GRAPH_INVESTIGATION_TEST_ID);

View file

@ -6,14 +6,15 @@
*/
import { waitForPluginInitialized } from '../../cloud_security_posture_api/utils';
import type { FtrProviderContext } from '../ftr_provider_context';
import type { SecurityTelemetryFtrProviderContext } from '../config';
// eslint-disable-next-line import/no-default-export
export default function ({ getPageObjects, getService }: FtrProviderContext) {
export default function ({ getPageObjects, getService }: SecurityTelemetryFtrProviderContext) {
const retry = getService('retry');
const logger = getService('log');
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const ebtUIHelper = getService('kibana_ebt_ui');
const pageObjects = getPageObjects([
'common',
'header',
@ -49,8 +50,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
);
await alertsPage.waitForListToHaveAlerts();
await alertsPage.flyout.expandVisualizations();
await ebtUIHelper.setOptIn(true); // starts the recording of events from this moment
});
after(async () => {
@ -63,6 +63,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('expanded flyout - filter by node', async () => {
await alertsPage.flyout.expandVisualizations();
await alertsPage.flyout.assertGraphPreviewVisible();
await alertsPage.flyout.assertGraphNodesNumber(3);

View file

@ -6,14 +6,15 @@
*/
import { waitForPluginInitialized } from '../../cloud_security_posture_api/utils';
import type { FtrProviderContext } from '../ftr_provider_context';
import type { SecurityTelemetryFtrProviderContext } from '../config';
// eslint-disable-next-line import/no-default-export
export default function ({ getPageObjects, getService }: FtrProviderContext) {
export default function ({ getPageObjects, getService }: SecurityTelemetryFtrProviderContext) {
const retry = getService('retry');
const logger = getService('log');
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const ebtUIHelper = getService('kibana_ebt_ui');
const pageObjects = getPageObjects([
'common',
'header',
@ -44,8 +45,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
);
await networkEventsPage.waitForListToHaveEvents();
await networkEventsPage.flyout.expandVisualizations();
await ebtUIHelper.setOptIn(true); // starts the recording of events from this moment
});
after(async () => {
@ -55,6 +55,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('expanded flyout - filter by node', async () => {
await networkEventsPage.flyout.expandVisualizations();
await networkEventsPage.flyout.assertGraphPreviewVisible();
await networkEventsPage.flyout.assertGraphNodesNumber(3);