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
@@ -0,0 +1,135 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { render } from '@testing-library/react';
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', () => {
const alert: Alert = {
_id: '_id',
_index: '_index',
field1: 'value1',
};
const field = 'field';

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

expect(getByText(getEmptyValue())).toBeInTheDocument();
});

it('should handle string value', () => {
const alert: Alert = {
_id: '_id',
_index: '_index',
field1: 'value1',
};
const field = 'field1';

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

expect(getByText('value1')).toBeInTheDocument();
});

it('should handle number value', () => {
const alert: Alert = {
_id: '_id',
_index: '_index',
field1: 123,
};
const columnId = 'field1';

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

expect(getByText('123')).toBeInTheDocument();
});

it('should handle array of booleans', () => {
const alert: Alert = {
_id: '_id',
_index: '_index',
field1: [true, false],
};
const field = 'field1';

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

expect(getByText('true, false')).toBeInTheDocument();
});

it('should handle array of numbers', () => {
const alert: Alert = {
_id: '_id',
_index: '_index',
field1: [1, 2],
};
const field = 'field1';

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

expect(getByText('1, 2')).toBeInTheDocument();
});

it('should handle array of null', () => {
const alert: Alert = {
_id: '_id',
_index: '_index',
field1: [null, null],
};
const field = 'field1';

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

expect(getByText(',')).toBeInTheDocument();
});

it('should join array of JsonObjects', () => {
const alert: Alert = {
_id: '_id',
_index: '_index',
field1: [{ subField1: 'value1', subField2: 'value2' }],
};
const field = 'field1';

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

expect(getByText('[object Object]')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { memo, useMemo } from 'react';
import type { Alert } from '@kbn/alerting-types';
import { getOrEmptyTagFromValue } from '../../../../common/components/empty_value';
import { getAlertFieldValueAsStringOrNull } from '../../../utils/type_utils';

export interface BasicCellRendererProps {
/**
* Alert data passed from the renderCellValue callback via the AlertWithLegacyFormats interface
*/
alert: Alert;
/**
* Column id passed from the renderCellValue callback via EuiDataGridProps['renderCellValue'] interface
*/
field: string;
}

/**
* Renders all the basic table cell values.
* Component used in the AI for SOC alert summary table.
*/
export const BasicCellRenderer = memo(({ alert, field }: BasicCellRendererProps) => {
const displayValue: string | null = useMemo(
() => getAlertFieldValueAsStringOrNull(alert, field),
[alert, field]
);

return <>{getOrEmptyTagFromValue(displayValue)}</>;
});

BasicCellRenderer.displayName = 'BasicCellRenderer';
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { render } from '@testing-library/react';
import type { Alert } from '@kbn/alerting-types';
import {
ICON_TEST_ID,
KibanaAlertRelatedIntegrationsCellRenderer,
SKELETON_TEST_ID,
} from './kibana_alert_related_integrations_cell_renderer';
import { useGetIntegrationFromPackageName } from '../../../hooks/alert_summary/use_get_integration_from_package_name';
import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils';

jest.mock('../../../hooks/alert_summary/use_get_integration_from_package_name');

describe('KibanaAlertRelatedIntegrationsCellRenderer', () => {
it('should handle missing field', () => {
(useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
integration: null,
isLoading: false,
});

const alert: Alert = {
_id: '_id',
_index: '_index',
};

const { queryByTestId } = render(<KibanaAlertRelatedIntegrationsCellRenderer alert={alert} />);

expect(queryByTestId(SKELETON_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(ICON_TEST_ID)).not.toBeInTheDocument();
});

it('should handle not finding matching integration', () => {
(useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
integration: null,
isLoading: false,
});

const alert: Alert = {
_id: '_id',
_index: '_index',
[ALERT_RULE_PARAMETERS]: ['splunk'],
};

const { queryByTestId } = render(<KibanaAlertRelatedIntegrationsCellRenderer alert={alert} />);

expect(queryByTestId(SKELETON_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(ICON_TEST_ID)).not.toBeInTheDocument();
});

it('should show loading', () => {
(useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
integration: null,
isLoading: true,
});

const alert: Alert = {
_id: '_id',
_index: '_index',
[ALERT_RULE_PARAMETERS]: ['splunk'],
};

const { getByTestId, queryByTestId } = render(
<KibanaAlertRelatedIntegrationsCellRenderer alert={alert} />
);

expect(getByTestId(SKELETON_TEST_ID)).toBeInTheDocument();
expect(queryByTestId(ICON_TEST_ID)).not.toBeInTheDocument();
});

it('should show integration icon', () => {
(useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
integration: { name: 'Splunk', icon: ['icon'] },
isLoading: false,
});

const alert: Alert = {
_id: '_id',
_index: '_index',
[ALERT_RULE_PARAMETERS]: ['splunk'],
};

const { getByTestId, queryByTestId } = render(
<KibanaAlertRelatedIntegrationsCellRenderer alert={alert} />
);

expect(queryByTestId(SKELETON_TEST_ID)).not.toBeInTheDocument();
expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { memo, useMemo } from 'react';
import type { JsonValue } from '@kbn/utility-types';
import { CardIcon } from '@kbn/fleet-plugin/public';
import { EuiSkeletonText } from '@elastic/eui';
import type { Alert } from '@kbn/alerting-types';
import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils';
import { useGetIntegrationFromPackageName } from '../../../hooks/alert_summary/use_get_integration_from_package_name';
import { getAlertFieldValueAsStringOrNull, isJsonObjectValue } from '../../../utils/type_utils';

export const SKELETON_TEST_ID = 'alert-summary-table-related-integrations-cell-renderer-skeleton';
export const ICON_TEST_ID = 'alert-summary-table-related-integrations-cell-renderer-icon';

const RELATED_INTEGRATIONS_FIELD = 'related_integrations';
const PACKAGE_FIELD = 'package';

// function is_string(value: unknown): value is string {}

export interface KibanaAlertRelatedIntegrationsCellRendererProps {
/**
* Alert data passed from the renderCellValue callback via the AlertWithLegacyFormats interface
*/
alert: Alert;
}

/**
* Renders an integration/package icon. Retrieves the package name from the kibana.alert.rule.parameters field in the alert,
* fetches all integrations/packages and use the icon from the one that matches by name.
* Used in AI for SOC alert summary table.
*/
export const KibanaAlertRelatedIntegrationsCellRenderer = memo(
({ alert }: KibanaAlertRelatedIntegrationsCellRendererProps) => {
const packageName: string | null = useMemo(() => {
const values: JsonValue[] | undefined = alert[ALERT_RULE_PARAMETERS];

if (Array.isArray(values) && values.length === 1) {
const value: JsonValue = values[0];
if (!isJsonObjectValue(value)) return null;

const relatedIntegration = value[RELATED_INTEGRATIONS_FIELD];
if (!isJsonObjectValue(relatedIntegration)) return null;

return getAlertFieldValueAsStringOrNull(relatedIntegration as Alert, PACKAGE_FIELD);
}

return null;
}, [alert]);

const { integration, isLoading } = useGetIntegrationFromPackageName({ packageName });

return (
<EuiSkeletonText data-test-subj={SKELETON_TEST_ID} isLoading={isLoading} lines={1}>
{integration ? (
<CardIcon
data-test-subj={ICON_TEST_ID}
icons={integration.icons}
integrationName={integration.title}
packageName={integration.name}
size="l"
version={integration.version}
/>
) : null}
</EuiSkeletonText>
);
}
);

KibanaAlertRelatedIntegrationsCellRenderer.displayName =
'KibanaAlertRelatedIntegrationsCellRenderer';
Loading