[RAM] Fix blank rule details page when displaying SIEM rules and small alignment issue (#138659)

* Fix rule details page erroring while displaying SIEM rules

* Add unit test to verify fix

* Address comment and add generic SIEM rule description for now

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Faisal Kanout <faisal.kanout@elastic.co>
This commit is contained in:
Jiawei Wu 2022-08-25 15:36:23 -06:00 committed by GitHub
parent 2187087c44
commit 568c260369
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 19 deletions

View file

@ -46,9 +46,9 @@ export function RuleActions({ ruleActions, actionTypeRegistry }: RuleActionsProp
}
if (isLoadingActionConnectors) return <EuiLoadingSpinner size="s" />;
return (
<EuiFlexGroup direction="column">
<EuiFlexGroup direction="column" gutterSize="none">
{actionConnectors.map(({ actionTypeId, name }) => (
<React.Fragment key={actionTypeId}>
<EuiFlexItem key={actionTypeId}>
<EuiFlexGroup alignItems="center" gutterSize="s" component="span">
<EuiFlexItem grow={false}>
<EuiIcon size="m" type={getActionIconClass(actionTypeId) ?? 'apps'} />
@ -60,7 +60,7 @@ export function RuleActions({ ruleActions, actionTypeRegistry }: RuleActionsProp
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
</React.Fragment>
</EuiFlexItem>
))}
</EuiFlexGroup>
);

View file

@ -69,19 +69,28 @@ const mockedRuleTypeIndex = new Map(
})
);
interface SetupProps {
ruleOverwrite?: any;
}
describe('Rule Definition', () => {
let wrapper: ReactWrapper;
async function setup() {
async function setup({ ruleOverwrite }: SetupProps = {}) {
const actionTypeRegistry = actionTypeRegistryMock.create();
const ruleTypeRegistry = ruleTypeRegistryMock.create();
const mockedRule = mockRule();
const mockedRule = mockRule(ruleOverwrite);
jest.mock('../../../lib/capabilities', () => ({
hasAllPrivilege: jest.fn(() => true),
hasSaveRulesCapability: jest.fn(() => true),
hasExecuteActionsCapability: jest.fn(() => true),
hasManageApiKeysCapability: jest.fn(() => true),
}));
ruleTypeRegistry.has.mockReturnValue(true);
ruleTypeRegistry.has.mockImplementation((id) => {
if (id === 'siem_rule') {
return false;
}
return true;
});
const ruleTypeR: RuleTypeModel = {
id: 'my-rule-type',
iconClass: 'test',
@ -93,7 +102,12 @@ describe('Rule Definition', () => {
ruleParamsExpression: jest.fn(),
requiresAppContext: false,
};
ruleTypeRegistry.get.mockReturnValue(ruleTypeR);
ruleTypeRegistry.get.mockImplementation((id) => {
if (id === 'siem_rule') {
throw new Error('error');
}
return ruleTypeR;
});
actionTypeRegistry.list.mockReturnValue([
{ id: '.server-log', iconClass: 'logsApp' },
{ id: '.slack', iconClass: 'logoSlack' },
@ -117,7 +131,10 @@ describe('Rule Definition', () => {
});
}
beforeAll(async () => await setup());
beforeEach(async () => await setup());
afterEach(() => {
jest.clearAllMocks();
});
it('renders rule definition ', async () => {
expect(wrapper.find('[data-test-subj="ruleSummaryRuleDefinition"]')).toBeTruthy();
@ -138,6 +155,18 @@ describe('Rule Definition', () => {
expect(ruleDescription.find('div.euiText').text()).toEqual('Rule when testing');
});
it('show SIEM rule type description "', async () => {
await setup({
ruleOverwrite: {
consumer: 'siem',
ruleTypeId: 'siem_rule',
},
});
const ruleDescription = wrapper.find('[data-test-subj="ruleSummaryRuleDescription"]');
expect(ruleDescription).toBeTruthy();
expect(ruleDescription.find('div.euiText').text()).toEqual('Security detection rule');
});
it('show rule conditions "', async () => {
const ruleConditions = wrapper.find('[data-test-subj="ruleSummaryRuleConditions"]');
expect(ruleConditions).toBeTruthy();
@ -166,7 +195,7 @@ describe('Rule Definition', () => {
expect(editButton).toMatchObject({});
});
});
function mockRule(): Rule {
function mockRule(overwrite = {}): Rule {
return {
id: '1',
name: 'test rule',
@ -175,7 +204,7 @@ function mockRule(): Rule {
ruleTypeId: 'test_rule_type',
schedule: { interval: '1s' },
actions: [],
params: { name: 'test rule type name' },
params: { name: 'test rule type name', description: 'siem description' },
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
@ -218,5 +247,6 @@ function mockRule(): Rule {
},
},
},
...overwrite,
};
}

View file

@ -15,6 +15,7 @@ import {
EuiTitle,
EuiLoadingSpinner,
} from '@elastic/eui';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { i18n } from '@kbn/i18n';
import { formatDuration } from '@kbn/alerting-plugin/common';
import { RuleDefinitionProps } from '../../../../types';
@ -83,6 +84,20 @@ export const RuleDefinition: React.FunctionComponent<RuleDefinitionProps> = ({
: false)
);
}, [hideEditButton, canSaveRule, ruleTypeRegistry, rule]);
const ruleDescription = useMemo(() => {
if (ruleTypeRegistry.has(rule.ruleTypeId)) {
return ruleTypeRegistry.get(rule.ruleTypeId).description;
}
// TODO: Replace this generic description with proper SIEM rule descriptions
if (rule.consumer === AlertConsumers.SIEM) {
return i18n.translate('xpack.triggersActionsUI.ruleDetails.securityDetectionRule', {
defaultMessage: 'Security detection rule',
});
}
return '';
}, [rule, ruleTypeRegistry]);
return (
<EuiFlexItem data-test-subj="ruleSummaryRuleDefinition" grow={3}>
<EuiPanel color="subdued" hasBorder={false} paddingSize={'m'}>
@ -143,27 +158,33 @@ export const RuleDefinition: React.FunctionComponent<RuleDefinitionProps> = ({
</ItemTitleRuleSummary>
<ItemValueRuleSummary
data-test-subj="ruleSummaryRuleDescription"
itemValue={ruleTypeRegistry.get(rule.ruleTypeId).description}
itemValue={ruleDescription}
/>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexGroup alignItems="center">
<ItemTitleRuleSummary>
{i18n.translate('xpack.triggersActionsUI.ruleDetails.conditionsTitle', {
defaultMessage: 'Conditions',
})}
</ItemTitleRuleSummary>
<EuiFlexItem grow={3}>
<EuiFlexGroup data-test-subj="ruleSummaryRuleConditions" alignItems="center">
{hasEditButton ? (
<EuiButtonEmpty onClick={() => setEditFlyoutVisible(true)}>
<EuiFlexGroup
data-test-subj="ruleSummaryRuleConditions"
alignItems="center"
gutterSize="none"
>
<EuiFlexItem grow={false}>
{hasEditButton ? (
<EuiButtonEmpty onClick={() => setEditFlyoutVisible(true)} flush="left">
<EuiText size="s">{getRuleConditionsWording()}</EuiText>
</EuiButtonEmpty>
) : (
<EuiText size="s">{getRuleConditionsWording()}</EuiText>
</EuiButtonEmpty>
) : (
<EuiText size="s">{getRuleConditionsWording()}</EuiText>
)}
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>