add status to detail page with failure history (#54812)
This commit is contained in:
parent
c3430fefd9
commit
daeddfdd78
|
@ -51,6 +51,7 @@ export const DETECTION_ENGINE_PREPACKAGED_URL = `${DETECTION_ENGINE_RULES_URL}/p
|
|||
export const DETECTION_ENGINE_PRIVILEGES_URL = `${DETECTION_ENGINE_URL}/privileges`;
|
||||
export const DETECTION_ENGINE_INDEX_URL = `${DETECTION_ENGINE_URL}/index`;
|
||||
export const DETECTION_ENGINE_TAGS_URL = `${DETECTION_ENGINE_URL}/tags`;
|
||||
export const DETECTION_ENGINE_RULES_STATUS = `${DETECTION_ENGINE_URL}/rules/_find_statuses`;
|
||||
|
||||
/**
|
||||
* Default signals index key for kibana.dev.yml
|
||||
|
|
|
@ -19,12 +19,14 @@ import {
|
|||
ImportRulesProps,
|
||||
ExportRulesProps,
|
||||
RuleError,
|
||||
RuleStatus,
|
||||
ImportRulesResponse,
|
||||
} from './types';
|
||||
import { throwIfNotOk } from '../../../hooks/api/api';
|
||||
import {
|
||||
DETECTION_ENGINE_RULES_URL,
|
||||
DETECTION_ENGINE_PREPACKAGED_URL,
|
||||
DETECTION_ENGINE_RULES_STATUS,
|
||||
} from '../../../../common/constants';
|
||||
import * as i18n from '../../../pages/detection_engine/rules/translations';
|
||||
|
||||
|
@ -302,3 +304,36 @@ export const exportRules = async ({
|
|||
await throwIfNotOk(response);
|
||||
return response.blob();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Rule Status provided Rule ID
|
||||
*
|
||||
* @param id string of Rule ID's (not rule_id)
|
||||
*
|
||||
* @throws An error if response is not OK
|
||||
*/
|
||||
export const getRuleStatusById = async ({
|
||||
id,
|
||||
signal,
|
||||
}: {
|
||||
id: string;
|
||||
signal: AbortSignal;
|
||||
}): Promise<Record<string, RuleStatus[]>> => {
|
||||
const response = await fetch(
|
||||
`${chrome.getBasePath()}${DETECTION_ENGINE_RULES_STATUS}?ids=${encodeURIComponent(
|
||||
JSON.stringify([id])
|
||||
)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
'kbn-xsrf': 'true',
|
||||
},
|
||||
signal,
|
||||
}
|
||||
);
|
||||
|
||||
await throwIfNotOk(response);
|
||||
return response.json();
|
||||
};
|
||||
|
|
|
@ -78,8 +78,12 @@ export const RuleSchema = t.intersection([
|
|||
updated_by: t.string,
|
||||
}),
|
||||
t.partial({
|
||||
last_failure_at: t.string,
|
||||
last_failure_message: t.string,
|
||||
output_index: t.string,
|
||||
saved_id: t.string,
|
||||
status: t.string,
|
||||
status_date: t.string,
|
||||
timeline_id: t.string,
|
||||
timeline_title: t.string,
|
||||
version: t.number,
|
||||
|
@ -175,3 +179,13 @@ export interface ExportRulesProps {
|
|||
excludeExportDetails?: boolean;
|
||||
signal: AbortSignal;
|
||||
}
|
||||
|
||||
export interface RuleStatus {
|
||||
alert_id: string;
|
||||
status_date: string;
|
||||
status: string;
|
||||
last_failure_at: string | null;
|
||||
last_success_at: string | null;
|
||||
last_failure_message: string | null;
|
||||
last_success_message: string | null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useStateToaster } from '../../../components/toasters';
|
||||
import { errorToToaster } from '../../../components/ml/api/error_to_toaster';
|
||||
import { getRuleStatusById } from './api';
|
||||
import * as i18n from './translations';
|
||||
import { RuleStatus } from './types';
|
||||
|
||||
type Return = [boolean, RuleStatus[] | null];
|
||||
|
||||
/**
|
||||
* Hook for using to get a Rule from the Detection Engine API
|
||||
*
|
||||
* @param id desired Rule ID's (not rule_id)
|
||||
*
|
||||
*/
|
||||
export const useRuleStatus = (id: string | undefined | null): Return => {
|
||||
const [ruleStatus, setRuleStatus] = useState<RuleStatus[] | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [, dispatchToaster] = useStateToaster();
|
||||
|
||||
useEffect(() => {
|
||||
let isSubscribed = true;
|
||||
const abortCtrl = new AbortController();
|
||||
|
||||
async function fetchData(idToFetch: string) {
|
||||
try {
|
||||
setLoading(true);
|
||||
const ruleStatusResponse = await getRuleStatusById({
|
||||
id: idToFetch,
|
||||
signal: abortCtrl.signal,
|
||||
});
|
||||
|
||||
if (isSubscribed) {
|
||||
setRuleStatus(ruleStatusResponse[id ?? '']);
|
||||
}
|
||||
} catch (error) {
|
||||
if (isSubscribed) {
|
||||
setRuleStatus(null);
|
||||
errorToToaster({ title: i18n.RULE_FETCH_FAILURE, error, dispatchToaster });
|
||||
}
|
||||
}
|
||||
if (isSubscribed) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
if (id != null) {
|
||||
fetchData(id);
|
||||
}
|
||||
return () => {
|
||||
isSubscribed = false;
|
||||
abortCtrl.abort();
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
return [loading, ruleStatus];
|
||||
};
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getOr } from 'lodash/fp';
|
||||
import { isEmpty, getOr } from 'lodash/fp';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
|
@ -79,15 +79,17 @@ class TimelineQueryComponent extends QueryTemplate<
|
|||
sourceId,
|
||||
sortField,
|
||||
} = this.props;
|
||||
const defaultIndex =
|
||||
indexPattern == null || isEmpty(indexPattern)
|
||||
? kibana.services.uiSettings.get<string[]>(DEFAULT_INDEX_KEY)
|
||||
: indexPattern?.title.split(',');
|
||||
const variables: GetTimelineQuery.Variables = {
|
||||
fieldRequested: fields,
|
||||
filterQuery: createFilter(filterQuery),
|
||||
sourceId,
|
||||
pagination: { limit, cursor: null, tiebreaker: null },
|
||||
sortField,
|
||||
defaultIndex:
|
||||
indexPattern?.title.split(',') ??
|
||||
kibana.services.uiSettings.get<string[]>(DEFAULT_INDEX_KEY),
|
||||
defaultIndex,
|
||||
inspect: isInspected,
|
||||
};
|
||||
return (
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiPanel, EuiLoadingContent } from '@elastic/eui';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
|
@ -299,7 +300,7 @@ export const SignalsTableComponent = React.memo<SignalsTableComponentProps>(
|
|||
[additionalActions, canUserCRUD, selectAll]
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
if (loading || isEmpty(signalsIndex)) {
|
||||
return (
|
||||
<EuiPanel>
|
||||
<HeaderSection title={i18n.SIGNALS_TABLE_TITLE} />
|
||||
|
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
|
||||
import {
|
||||
EuiBasicTable,
|
||||
EuiPanel,
|
||||
EuiLoadingContent,
|
||||
EuiHealth,
|
||||
EuiBasicTableColumn,
|
||||
} from '@elastic/eui';
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import { useRuleStatus } from '../../../../containers/detection_engine/rules/use_rule_status';
|
||||
import { RuleStatus } from '../../../../containers/detection_engine/rules';
|
||||
import { HeaderSection } from '../../../../components/header_section';
|
||||
import * as i18n from './translations';
|
||||
import { FormattedDate } from '../../../../components/formatted_date';
|
||||
|
||||
interface FailureHistoryProps {
|
||||
id?: string | null;
|
||||
}
|
||||
|
||||
const FailureHistoryComponent: React.FC<FailureHistoryProps> = ({ id }) => {
|
||||
const [loading, ruleStatus] = useRuleStatus(id);
|
||||
if (loading) {
|
||||
return (
|
||||
<EuiPanel>
|
||||
<HeaderSection title={i18n.LAST_FIVE_ERRORS} />
|
||||
<EuiLoadingContent />
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
||||
const columns: Array<EuiBasicTableColumn<RuleStatus>> = [
|
||||
{
|
||||
name: i18n.COLUMN_STATUS_TYPE,
|
||||
render: () => <EuiHealth color="danger">{i18n.TYPE_FAILED}</EuiHealth>,
|
||||
truncateText: false,
|
||||
width: '16%',
|
||||
},
|
||||
{
|
||||
field: 'last_failure_at',
|
||||
name: i18n.COLUMN_FAILED_AT,
|
||||
render: (value: string) => <FormattedDate value={value} fieldName="last_failure_at" />,
|
||||
sortable: false,
|
||||
truncateText: false,
|
||||
width: '24%',
|
||||
},
|
||||
{
|
||||
field: 'last_failure_message',
|
||||
name: i18n.COLUMN_FAILED_MSG,
|
||||
render: (value: string) => <>{value}</>,
|
||||
sortable: false,
|
||||
truncateText: false,
|
||||
width: '60%',
|
||||
},
|
||||
];
|
||||
return (
|
||||
<EuiPanel>
|
||||
<HeaderSection title={i18n.LAST_FIVE_ERRORS} />
|
||||
<EuiBasicTable
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
items={ruleStatus != null ? ruleStatus?.filter(rs => rs.last_failure_at != null) : []}
|
||||
sorting={{ sort: { field: 'status_date', direction: 'desc' } }}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
|
||||
export const FailureHistory = memo(FailureHistoryComponent);
|
|
@ -4,9 +4,17 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiButton, EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiLoadingSpinner,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiHealth,
|
||||
EuiTab,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import React, { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { Redirect, useParams } from 'react-router-dom';
|
||||
import { StickyContainer } from 'react-sticky';
|
||||
|
||||
|
@ -52,6 +60,9 @@ import { inputsSelectors } from '../../../../store/inputs';
|
|||
import { State } from '../../../../store';
|
||||
import { InputsRange } from '../../../../store/inputs/model';
|
||||
import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../../../store/inputs/actions';
|
||||
import { getEmptyTagValue } from '../../../../components/empty_value';
|
||||
import { RuleStatusFailedCallOut } from './status_failed_callout';
|
||||
import { FailureHistory } from './failure_history';
|
||||
|
||||
interface ReduxProps {
|
||||
filters: esFilters.Filter[];
|
||||
|
@ -66,6 +77,19 @@ export interface DispatchProps {
|
|||
}>;
|
||||
}
|
||||
|
||||
const ruleDetailTabs = [
|
||||
{
|
||||
id: 'signal',
|
||||
name: detectionI18n.SIGNAL,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: 'failure',
|
||||
name: i18n.FAILURE_HISTORY_TAB,
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
type RuleDetailsComponentProps = ReduxProps & DispatchProps;
|
||||
|
||||
const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
|
||||
|
@ -81,6 +105,7 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
|
|||
} = useUserInfo();
|
||||
const { ruleId } = useParams();
|
||||
const [isLoading, rule] = useRule(ruleId);
|
||||
const [ruleDetailTab, setRuleDetailTab] = useState('signal');
|
||||
const { aboutRuleData, defineRuleData, scheduleRuleData } = getStepsData({
|
||||
rule,
|
||||
detailsView: true,
|
||||
|
@ -149,6 +174,42 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
|
|||
filters,
|
||||
]);
|
||||
|
||||
const statusColor =
|
||||
rule?.status == null
|
||||
? 'subdued'
|
||||
: rule?.status === 'succeeded'
|
||||
? 'success'
|
||||
: rule?.status === 'failed'
|
||||
? 'danger'
|
||||
: rule?.status === 'executing'
|
||||
? 'warning'
|
||||
: 'subdued';
|
||||
|
||||
const tabs = useMemo(
|
||||
() =>
|
||||
ruleDetailTabs.map(tab => (
|
||||
<EuiTab
|
||||
onClick={() => setRuleDetailTab(tab.id)}
|
||||
isSelected={tab.id === ruleDetailTab}
|
||||
disabled={tab.disabled}
|
||||
key={tab.name}
|
||||
>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
)),
|
||||
[ruleDetailTabs, ruleDetailTab, setRuleDetailTab]
|
||||
);
|
||||
const ruleError = useMemo(
|
||||
() =>
|
||||
rule?.status === 'failed' && ruleDetailTab === 'signal' && rule?.last_failure_at != null ? (
|
||||
<RuleStatusFailedCallOut
|
||||
message={rule?.last_failure_message ?? ''}
|
||||
date={rule?.last_failure_at}
|
||||
/>
|
||||
) : null,
|
||||
[rule, ruleDetailTab]
|
||||
);
|
||||
|
||||
const updateDateRangeCallback = useCallback(
|
||||
(min: number, max: number) => {
|
||||
setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
|
||||
|
@ -180,14 +241,43 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
|
|||
border
|
||||
subtitle={subTitle}
|
||||
subtitle2={[
|
||||
lastSignals != null ? (
|
||||
<>
|
||||
{detectionI18n.LAST_SIGNAL}
|
||||
{': '}
|
||||
{lastSignals}
|
||||
</>
|
||||
) : null,
|
||||
'Status: Comming Soon',
|
||||
...(lastSignals != null
|
||||
? [
|
||||
<>
|
||||
{detectionI18n.LAST_SIGNAL}
|
||||
{': '}
|
||||
{lastSignals}
|
||||
</>,
|
||||
]
|
||||
: []),
|
||||
<EuiFlexGroup
|
||||
gutterSize="xs"
|
||||
alignItems="center"
|
||||
justifyContent="flexStart"
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
{i18n.STATUS}
|
||||
{':'}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiHealth color={statusColor}>
|
||||
{rule?.status ?? getEmptyTagValue()}
|
||||
</EuiHealth>
|
||||
</EuiFlexItem>
|
||||
{rule?.status_date && (
|
||||
<>
|
||||
<EuiFlexItem grow={false}>
|
||||
<>{i18n.STATUS_AT}</>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<FormattedDate
|
||||
value={rule?.status_date}
|
||||
fieldName={i18n.STATUS_DATE}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
)}
|
||||
</EuiFlexGroup>,
|
||||
]}
|
||||
title={title}
|
||||
>
|
||||
|
@ -216,73 +306,75 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</HeaderPage>
|
||||
|
||||
{ruleError}
|
||||
{tabs}
|
||||
<EuiSpacer />
|
||||
{ruleDetailTab === 'signal' && (
|
||||
<>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem component="section" grow={1}>
|
||||
<StepPanel loading={isLoading} title={ruleI18n.DEFINITION}>
|
||||
{defineRuleData != null && (
|
||||
<StepDefineRule
|
||||
descriptionDirection="column"
|
||||
isReadOnlyView={true}
|
||||
isLoading={false}
|
||||
defaultValues={defineRuleData}
|
||||
/>
|
||||
)}
|
||||
</StepPanel>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem component="section" grow={1}>
|
||||
<StepPanel loading={isLoading} title={ruleI18n.DEFINITION}>
|
||||
{defineRuleData != null && (
|
||||
<StepDefineRule
|
||||
descriptionDirection="column"
|
||||
isReadOnlyView={true}
|
||||
isLoading={false}
|
||||
defaultValues={defineRuleData}
|
||||
/>
|
||||
)}
|
||||
</StepPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem component="section" grow={2}>
|
||||
<StepPanel loading={isLoading} title={ruleI18n.ABOUT}>
|
||||
{aboutRuleData != null && (
|
||||
<StepAboutRule
|
||||
descriptionDirection="row"
|
||||
isReadOnlyView={true}
|
||||
isLoading={false}
|
||||
defaultValues={aboutRuleData}
|
||||
/>
|
||||
)}
|
||||
</StepPanel>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem component="section" grow={2}>
|
||||
<StepPanel loading={isLoading} title={ruleI18n.ABOUT}>
|
||||
{aboutRuleData != null && (
|
||||
<StepAboutRule
|
||||
descriptionDirection="row"
|
||||
isReadOnlyView={true}
|
||||
isLoading={false}
|
||||
defaultValues={aboutRuleData}
|
||||
/>
|
||||
)}
|
||||
</StepPanel>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem component="section" grow={1}>
|
||||
<StepPanel loading={isLoading} title={ruleI18n.SCHEDULE}>
|
||||
{scheduleRuleData != null && (
|
||||
<StepScheduleRule
|
||||
descriptionDirection="column"
|
||||
isReadOnlyView={true}
|
||||
isLoading={false}
|
||||
defaultValues={scheduleRuleData}
|
||||
/>
|
||||
)}
|
||||
</StepPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer />
|
||||
<SignalsHistogramPanel
|
||||
filters={signalMergedFilters}
|
||||
query={query}
|
||||
from={from}
|
||||
stackByOptions={signalsHistogramOptions}
|
||||
to={to}
|
||||
updateDateRange={updateDateRangeCallback}
|
||||
/>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
{ruleId != null && (
|
||||
<SignalsTable
|
||||
canUserCRUD={canUserCRUD ?? false}
|
||||
defaultFilters={signalDefaultFilters}
|
||||
hasIndexWrite={hasIndexWrite ?? false}
|
||||
from={from}
|
||||
loading={loading}
|
||||
signalsIndex={signalIndexName ?? ''}
|
||||
to={to}
|
||||
/>
|
||||
<EuiFlexItem component="section" grow={1}>
|
||||
<StepPanel loading={isLoading} title={ruleI18n.SCHEDULE}>
|
||||
{scheduleRuleData != null && (
|
||||
<StepScheduleRule
|
||||
descriptionDirection="column"
|
||||
isReadOnlyView={true}
|
||||
isLoading={false}
|
||||
defaultValues={scheduleRuleData}
|
||||
/>
|
||||
)}
|
||||
</StepPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer />
|
||||
<SignalsHistogramPanel
|
||||
filters={signalMergedFilters}
|
||||
query={query}
|
||||
from={from}
|
||||
stackByOptions={signalsHistogramOptions}
|
||||
to={to}
|
||||
updateDateRange={updateDateRangeCallback}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
{ruleId != null && (
|
||||
<SignalsTable
|
||||
canUserCRUD={canUserCRUD ?? false}
|
||||
defaultFilters={signalDefaultFilters}
|
||||
hasIndexWrite={hasIndexWrite ?? false}
|
||||
from={from}
|
||||
loading={loading}
|
||||
signalsIndex={signalIndexName ?? ''}
|
||||
to={to}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{ruleDetailTab === 'failure' && <FailureHistory id={rule?.id} />}
|
||||
</WrapperPage>
|
||||
</StickyContainer>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import { FormattedDate } from '../../../../components/formatted_date';
|
||||
import * as i18n from './translations';
|
||||
|
||||
interface RuleStatusFailedCallOutComponentProps {
|
||||
date: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
const RuleStatusFailedCallOutComponent: React.FC<RuleStatusFailedCallOutComponentProps> = ({
|
||||
date,
|
||||
message,
|
||||
}) => (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<EuiFlexGroup gutterSize="xs" alignItems="center" justifyContent="flexStart">
|
||||
<EuiFlexItem grow={false}>{i18n.ERROR_CALLOUT_TITLE}</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<FormattedDate value={date} fieldName="last_failure_at" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
>
|
||||
<p>{message}</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
|
||||
export const RuleStatusFailedCallOut = memo(RuleStatusFailedCallOutComponent);
|
|
@ -34,3 +34,70 @@ export const ACTIVATE_RULE = i18n.translate(
|
|||
export const UNKNOWN = i18n.translate('xpack.siem.detectionEngine.ruleDetails.unknownDescription', {
|
||||
defaultMessage: 'Unknown',
|
||||
});
|
||||
|
||||
export const STATUS = i18n.translate('xpack.siem.detectionEngine.ruleDetails.statusDescription', {
|
||||
defaultMessage: 'Status',
|
||||
});
|
||||
|
||||
export const STATUS_AT = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.statusAtDescription',
|
||||
{
|
||||
defaultMessage: 'at',
|
||||
}
|
||||
);
|
||||
|
||||
export const STATUS_DATE = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.statusDateDescription',
|
||||
{
|
||||
defaultMessage: 'Status date',
|
||||
}
|
||||
);
|
||||
|
||||
export const ERROR_CALLOUT_TITLE = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.errorCalloutTitle',
|
||||
{
|
||||
defaultMessage: 'Rule failure at',
|
||||
}
|
||||
);
|
||||
|
||||
export const FAILURE_HISTORY_TAB = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.failureHistoryTab',
|
||||
{
|
||||
defaultMessage: 'Failure History',
|
||||
}
|
||||
);
|
||||
|
||||
export const LAST_FIVE_ERRORS = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.lastFiveErrorsTitle',
|
||||
{
|
||||
defaultMessage: 'Last five errors',
|
||||
}
|
||||
);
|
||||
|
||||
export const COLUMN_STATUS_TYPE = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.statusTypeColumn',
|
||||
{
|
||||
defaultMessage: 'Type',
|
||||
}
|
||||
);
|
||||
|
||||
export const COLUMN_FAILED_AT = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.statusFailedAtColumn',
|
||||
{
|
||||
defaultMessage: 'Failed at',
|
||||
}
|
||||
);
|
||||
|
||||
export const COLUMN_FAILED_MSG = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.statusFailedMsgColumn',
|
||||
{
|
||||
defaultMessage: 'Failed message',
|
||||
}
|
||||
);
|
||||
|
||||
export const TYPE_FAILED = i18n.translate(
|
||||
'xpack.siem.detectionEngine.ruleDetails.statusFailedDescription',
|
||||
{
|
||||
defaultMessage: 'Failed',
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue