-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Actionable Observability] o11y rules page (#127406)
* style rules table, refactor useFetchRules hook, edit_rule_flyout component,delete, pagination, sorting * remove unused import * fix translations * change name column to rule and statusFilter to lastResponse filter * remove unused code * embed create rule flyout & create loadRuleTypesHook that accepts a filteredSolutions param * fix failing tests * fix last failing test * Show rule type name in the Rule column * PR review comments * useMemo for panelItems on status_context * close status context popover when clicking outside of the context menu * refactor useFetchRules to get rid of react-hooks/exhaustive-deps warning * remove console log statement * useCallback for EuiBasicTable onChange * cleanup async fetchRuleTypes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
- Loading branch information
1 parent
3dbaf8a
commit ad5c67b
Showing
20 changed files
with
1,326 additions
and
283 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
x-pack/plugins/observability/public/hooks/use_fetch_rules.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* 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 { useEffect, useState, useCallback } from 'react'; | ||
import { loadRules, Rule } from '../../../triggers_actions_ui/public'; | ||
import { RULES_LOAD_ERROR } from '../pages/rules/translations'; | ||
import { FetchRulesProps } from '../pages/rules/types'; | ||
import { OBSERVABILITY_RULE_TYPES } from '../pages/rules/config'; | ||
import { useKibana } from '../utils/kibana_react'; | ||
|
||
interface RuleState { | ||
isLoading: boolean; | ||
data: Rule[]; | ||
error: string | null; | ||
totalItemCount: number; | ||
} | ||
|
||
export function useFetchRules({ ruleLastResponseFilter, page, sort }: FetchRulesProps) { | ||
const { http } = useKibana().services; | ||
|
||
const [rulesState, setRulesState] = useState<RuleState>({ | ||
isLoading: false, | ||
data: [], | ||
error: null, | ||
totalItemCount: 0, | ||
}); | ||
|
||
const fetchRules = useCallback(async () => { | ||
setRulesState((oldState) => ({ ...oldState, isLoading: true })); | ||
|
||
try { | ||
const response = await loadRules({ | ||
http, | ||
page, | ||
typesFilter: OBSERVABILITY_RULE_TYPES, | ||
ruleStatusesFilter: ruleLastResponseFilter, | ||
sort, | ||
}); | ||
setRulesState((oldState) => ({ | ||
...oldState, | ||
isLoading: false, | ||
data: response.data, | ||
totalItemCount: response.total, | ||
})); | ||
} catch (_e) { | ||
setRulesState((oldState) => ({ ...oldState, isLoading: false, error: RULES_LOAD_ERROR })); | ||
} | ||
}, [http, page, ruleLastResponseFilter, sort]); | ||
useEffect(() => { | ||
fetchRules(); | ||
}, [fetchRules]); | ||
|
||
return { | ||
rulesState, | ||
reload: fetchRules, | ||
setRulesState, | ||
}; | ||
} |
93 changes: 93 additions & 0 deletions
93
x-pack/plugins/observability/public/pages/rules/components/delete_modal_confirmation.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
* 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 { EuiConfirmModal } from '@elastic/eui'; | ||
import React, { useEffect, useState } from 'react'; | ||
import { HttpSetup } from 'kibana/public'; | ||
import { useKibana } from '../../../utils/kibana_react'; | ||
import { | ||
confirmModalText, | ||
confirmButtonText, | ||
cancelButtonText, | ||
deleteSuccessText, | ||
deleteErrorText, | ||
} from '../translations'; | ||
|
||
export function DeleteModalConfirmation({ | ||
idsToDelete, | ||
apiDeleteCall, | ||
onDeleted, | ||
onCancel, | ||
onErrors, | ||
singleTitle, | ||
multipleTitle, | ||
setIsLoadingState, | ||
}: { | ||
idsToDelete: string[]; | ||
apiDeleteCall: ({ | ||
ids, | ||
http, | ||
}: { | ||
ids: string[]; | ||
http: HttpSetup; | ||
}) => Promise<{ successes: string[]; errors: string[] }>; | ||
onDeleted: (deleted: string[]) => void; | ||
onCancel: () => void; | ||
onErrors: () => void; | ||
singleTitle: string; | ||
multipleTitle: string; | ||
setIsLoadingState: (isLoading: boolean) => void; | ||
}) { | ||
const [deleteModalFlyoutVisible, setDeleteModalVisibility] = useState<boolean>(false); | ||
|
||
useEffect(() => { | ||
setDeleteModalVisibility(idsToDelete.length > 0); | ||
}, [idsToDelete]); | ||
|
||
const { | ||
http, | ||
notifications: { toasts }, | ||
} = useKibana().services; | ||
const numIdsToDelete = idsToDelete.length; | ||
if (!deleteModalFlyoutVisible) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<EuiConfirmModal | ||
buttonColor="danger" | ||
data-test-subj="deleteIdsConfirmation" | ||
title={confirmButtonText(numIdsToDelete, singleTitle, multipleTitle)} | ||
onCancel={() => { | ||
setDeleteModalVisibility(false); | ||
onCancel(); | ||
}} | ||
onConfirm={async () => { | ||
setDeleteModalVisibility(false); | ||
setIsLoadingState(true); | ||
const { successes, errors } = await apiDeleteCall({ ids: idsToDelete, http }); | ||
setIsLoadingState(false); | ||
|
||
const numSuccesses = successes.length; | ||
const numErrors = errors.length; | ||
if (numSuccesses > 0) { | ||
toasts.addSuccess(deleteSuccessText(numSuccesses, singleTitle, multipleTitle)); | ||
} | ||
|
||
if (numErrors > 0) { | ||
toasts.addDanger(deleteErrorText(numErrors, singleTitle, multipleTitle)); | ||
await onErrors(); | ||
} | ||
await onDeleted(successes); | ||
}} | ||
cancelButtonText={cancelButtonText} | ||
confirmButtonText={confirmButtonText(numIdsToDelete, singleTitle, multipleTitle)} | ||
> | ||
{confirmModalText(numIdsToDelete, singleTitle, multipleTitle)} | ||
</EuiConfirmModal> | ||
); | ||
} |
31 changes: 31 additions & 0 deletions
31
x-pack/plugins/observability/public/pages/rules/components/edit_rule_flyout.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* 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, { useState, useMemo, useEffect } from 'react'; | ||
import { useKibana } from '../../../utils/kibana_react'; | ||
import { EditFlyoutProps } from '../types'; | ||
|
||
export function EditRuleFlyout({ currentRule, onSave }: EditFlyoutProps) { | ||
const { triggersActionsUi } = useKibana().services; | ||
const [editFlyoutVisible, setEditFlyoutVisibility] = useState<boolean>(false); | ||
|
||
useEffect(() => { | ||
setEditFlyoutVisibility(true); | ||
}, [currentRule]); | ||
const EditAlertFlyout = useMemo( | ||
() => | ||
triggersActionsUi.getEditAlertFlyout({ | ||
initialRule: currentRule, | ||
onClose: () => { | ||
setEditFlyoutVisibility(false); | ||
}, | ||
onSave, | ||
}), | ||
[currentRule, setEditFlyoutVisibility, triggersActionsUi, onSave] | ||
); | ||
return <>{editFlyoutVisible && EditAlertFlyout}</>; | ||
} |
43 changes: 43 additions & 0 deletions
43
x-pack/plugins/observability/public/pages/rules/components/execution_status.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* 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 { EuiHealth, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; | ||
import { AlertExecutionStatusErrorReasons } from '../../../../../alerting/common'; | ||
import { getHealthColor, rulesStatusesTranslationsMapping } from '../config'; | ||
import { RULE_STATUS_LICENSE_ERROR } from '../translations'; | ||
import { ExecutionStatusProps } from '../types'; | ||
|
||
export function ExecutionStatus({ executionStatus }: ExecutionStatusProps) { | ||
const healthColor = getHealthColor(executionStatus.status); | ||
const tooltipMessage = | ||
executionStatus.status === 'error' ? `Error: ${executionStatus?.error?.message}` : null; | ||
const isLicenseError = executionStatus.error?.reason === AlertExecutionStatusErrorReasons.License; | ||
const statusMessage = isLicenseError | ||
? RULE_STATUS_LICENSE_ERROR | ||
: rulesStatusesTranslationsMapping[executionStatus.status]; | ||
|
||
const health = ( | ||
<EuiHealth data-test-subj={`ruleStatus-${executionStatus.status}`} color={healthColor}> | ||
{statusMessage} | ||
</EuiHealth> | ||
); | ||
|
||
const healthWithTooltip = tooltipMessage ? ( | ||
<EuiToolTip data-test-subj="ruleStatus-error-tooltip" position="top" content={tooltipMessage}> | ||
{health} | ||
</EuiToolTip> | ||
) : ( | ||
health | ||
); | ||
|
||
return ( | ||
<EuiFlexGroup gutterSize="none"> | ||
<EuiFlexItem>{healthWithTooltip}</EuiFlexItem> | ||
</EuiFlexGroup> | ||
); | ||
} |
87 changes: 87 additions & 0 deletions
87
x-pack/plugins/observability/public/pages/rules/components/last_response_filter.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
/* eslint-disable react/function-component-definition */ | ||
|
||
import React, { useEffect, useState } from 'react'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import { | ||
EuiFilterGroup, | ||
EuiPopover, | ||
EuiFilterButton, | ||
EuiFilterSelectItem, | ||
EuiHealth, | ||
} from '@elastic/eui'; | ||
import { AlertExecutionStatuses, AlertExecutionStatusValues } from '../../../../../alerting/common'; | ||
import { getHealthColor, rulesStatusesTranslationsMapping } from '../config'; | ||
import { StatusFilterProps } from '../types'; | ||
|
||
export const LastResponseFilter: React.FunctionComponent<StatusFilterProps> = ({ | ||
selectedStatuses, | ||
onChange, | ||
}: StatusFilterProps) => { | ||
const [selectedValues, setSelectedValues] = useState<string[]>(selectedStatuses); | ||
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false); | ||
|
||
useEffect(() => { | ||
if (onChange) { | ||
onChange(selectedValues); | ||
} | ||
}, [selectedValues, onChange]); | ||
|
||
useEffect(() => { | ||
setSelectedValues(selectedStatuses); | ||
}, [selectedStatuses]); | ||
|
||
return ( | ||
<EuiFilterGroup> | ||
<EuiPopover | ||
isOpen={isPopoverOpen} | ||
closePopover={() => setIsPopoverOpen(false)} | ||
button={ | ||
<EuiFilterButton | ||
iconType="arrowDown" | ||
hasActiveFilters={selectedValues.length > 0} | ||
numActiveFilters={selectedValues.length} | ||
numFilters={selectedValues.length} | ||
onClick={() => setIsPopoverOpen(!isPopoverOpen)} | ||
data-test-subj="ruleStatusFilterButton" | ||
> | ||
<FormattedMessage | ||
id="xpack.observability.rules.ruleLastResponseFilterLabel" | ||
defaultMessage="Last response" | ||
/> | ||
</EuiFilterButton> | ||
} | ||
> | ||
<div className="euiFilterSelect__items"> | ||
{[...AlertExecutionStatusValues].sort().map((item: AlertExecutionStatuses) => { | ||
const healthColor = getHealthColor(item); | ||
return ( | ||
<EuiFilterSelectItem | ||
key={item} | ||
style={{ textTransform: 'capitalize' }} | ||
onClick={() => { | ||
const isPreviouslyChecked = selectedValues.includes(item); | ||
if (isPreviouslyChecked) { | ||
setSelectedValues(selectedValues.filter((val) => val !== item)); | ||
} else { | ||
setSelectedValues(selectedValues.concat(item)); | ||
} | ||
}} | ||
checked={selectedValues.includes(item) ? 'on' : undefined} | ||
data-test-subj={`ruleStatus${item}FilerOption`} | ||
> | ||
<EuiHealth color={healthColor}>{rulesStatusesTranslationsMapping[item]}</EuiHealth> | ||
</EuiFilterSelectItem> | ||
); | ||
})} | ||
</div> | ||
</EuiPopover> | ||
</EuiFilterGroup> | ||
); | ||
}; |
24 changes: 24 additions & 0 deletions
24
x-pack/plugins/observability/public/pages/rules/components/last_run.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* 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 { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; | ||
import moment from 'moment'; | ||
import { LastRunProps } from '../types'; | ||
|
||
export function LastRun({ date }: LastRunProps) { | ||
return ( | ||
<> | ||
<EuiFlexGroup direction="column" gutterSize="none"> | ||
<EuiFlexItem grow={false}> | ||
<EuiText color="subdued" size="xs"> | ||
{moment(date).fromNow()} | ||
</EuiText> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</> | ||
); | ||
} |
50 changes: 50 additions & 0 deletions
50
x-pack/plugins/observability/public/pages/rules/components/name.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* 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 { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiBadge } from '@elastic/eui'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import { RuleNameProps } from '../types'; | ||
import { useKibana } from '../../../utils/kibana_react'; | ||
|
||
export function Name({ name, rule }: RuleNameProps) { | ||
const { http } = useKibana().services; | ||
const detailsLink = http.basePath.prepend( | ||
`/app/management/insightsAndAlerting/triggersActions/rule/${rule.id}` | ||
); | ||
const link = ( | ||
<EuiFlexGroup direction="column" gutterSize="xs"> | ||
<EuiFlexItem grow={false}> | ||
<EuiFlexGroup gutterSize="xs"> | ||
<EuiFlexItem grow={false}> | ||
<EuiLink title={name} href={detailsLink}> | ||
{name} | ||
</EuiLink> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiText color="subdued" size="xs"> | ||
{rule.ruleType} | ||
</EuiText> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
); | ||
return ( | ||
<> | ||
{link} | ||
{rule.enabled && rule.muteAll && ( | ||
<EuiBadge data-test-subj="mutedActionsBadge" color="hollow"> | ||
<FormattedMessage | ||
id="xpack.observability.rules.rulesTable.columns.mutedBadge" | ||
defaultMessage="Muted" | ||
/> | ||
</EuiBadge> | ||
)} | ||
</> | ||
); | ||
} |
Oops, something went wrong.