Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,63 @@
import { render } from '@testing-library/react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import React from 'react';
import { useGetIntegrationFromRuleId } from '../../../hooks/alert_summary/use_get_integration_from_rule_id';
import { usePackageIconType } from '@kbn/fleet-plugin/public/hooks';
import {
INTEGRATION_INTEGRATION_ICON_TEST_ID,
INTEGRATION_ICON_TEST_ID,
INTEGRATION_LOADING_SKELETON_TEST_ID,
IntegrationIcon,
} from './integration_icon';
import type { PackageListItem } from '@kbn/fleet-plugin/common';
import { installationStatuses } from '@kbn/fleet-plugin/common/constants';
import { usePackageIconType } from '@kbn/fleet-plugin/public/hooks';

jest.mock('../../../hooks/alert_summary/use_get_integration_from_rule_id');
jest.mock('@kbn/fleet-plugin/public/hooks');

const testId = 'testid';
const integration: PackageListItem = {
id: 'splunk',
icons: [{ src: 'icon.svg', path: 'mypath/icon.svg', type: 'image/svg+xml' }],
name: 'splunk',
status: installationStatuses.NotInstalled,
title: 'Splunk',
version: '0.1.0',
};

describe('IntegrationIcon', () => {
it('should return a single integration icon', () => {
(useGetIntegrationFromRuleId as jest.Mock).mockReturnValue({
integration: {
title: 'title',
icons: [{ type: 'type', src: 'src' }],
name: 'name',
version: 'version',
},
isLoading: false,
});
beforeEach(() => {
jest.clearAllMocks();
(usePackageIconType as jest.Mock).mockReturnValue('iconType');
});

it('should render a single integration icon', () => {
const { getByTestId } = render(
<IntlProvider locale="en">
<IntegrationIcon ruleId={'name'} />
<IntegrationIcon data-test-subj={testId} integration={integration} />
</IntlProvider>
);

expect(getByTestId(INTEGRATION_INTEGRATION_ICON_TEST_ID)).toBeInTheDocument();
expect(getByTestId(`${testId}-${INTEGRATION_ICON_TEST_ID}`)).toBeInTheDocument();
});

it('should return a single integration loading', () => {
(useGetIntegrationFromRuleId as jest.Mock).mockReturnValue({
integration: {},
isLoading: true,
});

it('should render the loading skeleton', () => {
const { getByTestId } = render(
<IntlProvider locale="en">
<IntegrationIcon ruleId={''} />
<IntegrationIcon data-test-subj={testId} integration={undefined} isLoading={true} />
</IntlProvider>
);

expect(getByTestId(`${testId}-${INTEGRATION_LOADING_SKELETON_TEST_ID}`)).toBeInTheDocument();
});

it('should not render skeleton or icon', () => {
const { queryByTestId } = render(
<IntlProvider locale="en">
<IntegrationIcon data-test-subj={testId} integration={undefined} />
</IntlProvider>
);

expect(getByTestId(INTEGRATION_LOADING_SKELETON_TEST_ID)).toBeInTheDocument();
expect(queryByTestId(`${testId}-${INTEGRATION_ICON_TEST_ID}`)).not.toBeInTheDocument();
expect(
queryByTestId(`${testId}-${INTEGRATION_LOADING_SKELETON_TEST_ID}`)
).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,49 @@ import React, { memo } from 'react';
import { EuiSkeletonText } from '@elastic/eui';
import { CardIcon } from '@kbn/fleet-plugin/public';
import type { IconSize } from '@elastic/eui/src/components/icon/icon';
import { useGetIntegrationFromRuleId } from '../../../hooks/alert_summary/use_get_integration_from_rule_id';
import type { PackageListItem } from '@kbn/fleet-plugin/common';

export const INTEGRATION_LOADING_SKELETON_TEST_ID = 'ai-for-soc-alert-integration-loading-skeleton';
export const INTEGRATION_INTEGRATION_ICON_TEST_ID = 'ai-for-soc-alert-integration-icon';
export const INTEGRATION_LOADING_SKELETON_TEST_ID = 'integration-loading-skeleton';
export const INTEGRATION_ICON_TEST_ID = 'integration-icon';

interface IntegrationProps {
/**
* Id of the rule the alert was generated by
* Optional data test subject string
*/
ruleId: string;
'data-test-subj'?: string;
/**
* Changes the size of the icon. Uses the Eui IconSize interface.
* Defaults to s
*/
iconSize?: IconSize;
/**
* Id of the rule the alert was generated by
*/
integration: PackageListItem | undefined;
/**
* If true, renders a EuiSkeletonText
*/
isLoading?: boolean;
}

/**
* Renders the icon for the integration that matches the rule id.
* In AI for SOC, we can retrieve the integration/package that matches a specific rule, via the related_integrations field on the rule.
* Renders the icon for the integration. Renders a EuiSkeletonText if loading.
*/
export const IntegrationIcon = memo(({ ruleId, iconSize = 's' }: IntegrationProps) => {
const { integration, isLoading } = useGetIntegrationFromRuleId({ ruleId });

return (
export const IntegrationIcon = memo(
({
'data-test-subj': dataTestSubj,
iconSize = 's',
integration,
isLoading = false,
}: IntegrationProps) => (
<EuiSkeletonText
data-test-subj={INTEGRATION_LOADING_SKELETON_TEST_ID}
data-test-subj={`${dataTestSubj}-${INTEGRATION_LOADING_SKELETON_TEST_ID}`}
isLoading={isLoading}
lines={1}
>
{integration ? (
<CardIcon
data-test-subj={INTEGRATION_INTEGRATION_ICON_TEST_ID}
data-test-subj={`${dataTestSubj}-${INTEGRATION_ICON_TEST_ID}`}
icons={integration.icons}
integrationName={integration.title}
packageName={integration.name}
Expand All @@ -50,6 +60,7 @@ export const IntegrationIcon = memo(({ ruleId, iconSize = 's' }: IntegrationProp
/>
) : null}
</EuiSkeletonText>
);
});
)
);

IntegrationIcon.displayName = 'IntegrationIcon';
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import type { PackageListItem } from '@kbn/fleet-plugin/common';
import { CardIcon } from '@kbn/fleet-plugin/public';
import { IntegrationIcon } from '../common/integration_icon';
import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date';

const LAST_SYNCED = i18n.translate(
Expand Down Expand Up @@ -70,12 +70,10 @@ export const IntegrationCard = memo(
>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<CardIcon
icons={integration.icons}
integrationName={integration.title}
packageName={integration.name}
size="xl"
version={integration.version}
<IntegrationIcon
data-test-subj={dataTestSubj}
iconSize="xl"
integration={integration}
/>
</EuiFlexItem>
<EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { css } from '@emotion/react';
import { EuiBadge, EuiCard } from '@elastic/eui';
import type { PackageListItem } from '@kbn/fleet-plugin/common';
import { INTEGRATIONS_PLUGIN_ID } from '@kbn/fleet-plugin/common';
import { CardIcon, useLink } from '@kbn/fleet-plugin/public';
import { useLink } from '@kbn/fleet-plugin/public';
import { i18n } from '@kbn/i18n';
import { IntegrationIcon } from '../common/integration_icon';
import { useKibana } from '../../../../common/lib/kibana';

const SIEM_BADGE = i18n.translate('xpack.securitySolution.alertSummary.integrations.siemBadge', {
Expand Down Expand Up @@ -65,13 +66,7 @@ export const IntegrationCard = memo(
display="plain"
hasBorder
icon={
<CardIcon
icons={integration.icons}
integrationName={integration.title}
packageName={integration.name}
size="xl"
version={integration.version}
/>
<IntegrationIcon data-test-subj={dataTestSubj} iconSize="xl" integration={integration} />
}
layout="horizontal"
onClick={onClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,16 @@ const packages: PackageListItem[] = [
version: '',
},
];
const ruleResponse = {
rules: [],
isLoading: false,
};

describe('<SearchBarSection />', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should render all components', () => {
(useIntegrations as jest.Mock).mockReturnValue({
isLoading: false,
Expand All @@ -49,7 +57,7 @@ describe('<SearchBarSection />', () => {
});

const { getByTestId, queryByTestId } = render(
<SearchBarSection dataView={dataView} packages={packages} />
<SearchBarSection dataView={dataView} packages={packages} ruleResponse={ruleResponse} />
);

expect(getByTestId(SEARCH_BAR_TEST_ID)).toBeInTheDocument();
Expand All @@ -64,7 +72,7 @@ describe('<SearchBarSection />', () => {
});

const { getByTestId, queryByTestId } = render(
<SearchBarSection dataView={dataView} packages={packages} />
<SearchBarSection dataView={dataView} packages={packages} ruleResponse={ruleResponse} />
);

expect(getByTestId(INTEGRATION_BUTTON_LOADING_TEST_ID)).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React, { memo, useMemo } from 'react';
import type { DataView } from '@kbn/data-views-plugin/common';
import { EuiFlexGroup, EuiFlexItem, EuiSkeletonRectangle } from '@elastic/eui';
import type { PackageListItem } from '@kbn/fleet-plugin/common';
import type { RuleResponse } from '../../../../../common/api/detection_engine';
import { useIntegrations } from '../../../hooks/alert_summary/use_integrations';
import { SiemSearchBar } from '../../../../common/components/search_bar';
import { IntegrationFilterButton } from './integrations_filter_button';
Expand All @@ -29,6 +30,19 @@ export interface SearchBarSectionProps {
* List of installed AI for SOC integrations
*/
packages: PackageListItem[];
/**
* Result from the useQuery to fetch all rules
*/
ruleResponse: {
/**
* Result from fetching all rules
*/
rules: RuleResponse[];
/**
* True while rules are being fetched
*/
isLoading: boolean;
};
}

/**
Expand All @@ -38,34 +52,36 @@ export interface SearchBarSectionProps {
* For the AI for SOC effort, each integration has one rule associated with.
* This means that deselecting an integration is equivalent to filtering out by the rule for that integration.
*/
export const SearchBarSection = memo(({ dataView, packages }: SearchBarSectionProps) => {
const { isLoading, integrations } = useIntegrations({ packages });
export const SearchBarSection = memo(
({ dataView, packages, ruleResponse }: SearchBarSectionProps) => {
const { isLoading, integrations } = useIntegrations({ packages, ruleResponse });

const dataViewSpec = useMemo(() => dataView.toSpec(), [dataView]);
const dataViewSpec = useMemo(() => dataView.toSpec(), [dataView]);

return (
<EuiFlexGroup gutterSize="none" alignItems="center">
<EuiFlexItem grow={false}>
<EuiSkeletonRectangle
data-test-subj={INTEGRATION_BUTTON_LOADING_TEST_ID}
isLoading={isLoading}
width={INTEGRATION_BUTTON_LOADING_WIDTH}
height={INTEGRATION_BUTTON_LOADING_HEIGHT}
>
<IntegrationFilterButton integrations={integrations} />
</EuiSkeletonRectangle>
</EuiFlexItem>
<EuiFlexItem>
<SiemSearchBar
dataTestSubj={SEARCH_BAR_TEST_ID}
hideFilterBar
hideQueryMenu
id={InputsModelId.global}
sourcererDataView={dataViewSpec}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
});
return (
<EuiFlexGroup gutterSize="none" alignItems="center">
<EuiFlexItem grow={false}>
<EuiSkeletonRectangle
data-test-subj={INTEGRATION_BUTTON_LOADING_TEST_ID}
isLoading={isLoading}
width={INTEGRATION_BUTTON_LOADING_WIDTH}
height={INTEGRATION_BUTTON_LOADING_HEIGHT}
>
<IntegrationFilterButton integrations={integrations} />
</EuiSkeletonRectangle>
</EuiFlexItem>
<EuiFlexItem>
<SiemSearchBar
dataTestSubj={SEARCH_BAR_TEST_ID}
hideFilterBar
hideQueryMenu
id={InputsModelId.global}
sourcererDataView={dataViewSpec}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
}
);

SearchBarSection.displayName = 'SearchBarSection';
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import type { Alert } from '@kbn/alerting-types';
import { BasicCellRenderer } from './basic_cell_renderer';
import { TestProviders } from '../../../../common/mock';
import { getEmptyValue } from '../../../../common/components/empty_value';
import { CellValue } from './render_cell';

describe('BasicCellRenderer', () => {
it('should handle missing field', () => {
Expand Down Expand Up @@ -58,7 +57,7 @@ describe('BasicCellRenderer', () => {

const { getByText } = render(
<TestProviders>
<CellValue alert={alert} columnId={columnId} />
<BasicCellRenderer alert={alert} field={columnId} />
</TestProviders>
);

Expand Down
Loading