-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Actionable Observability] Add rule details page (#130330)
* Add rule details page * Fix route * Fix route * Add useBreadcrumbs * Add rule summary * Complete rule data summary * Update styling * Add update rule * Add edit role * Update desgin * Add conditions * Add connectors icons * Fix more button * Remove unused FelxBox * Add fetch alerts * Move to items to components folder * Format dates * Add tabs * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Use the shared getRuleStatusDropdown * Add permissions * Better handling errors * Fix styling * Fix tag style * Add tags component * Use tags component from triggers aciton ui * Add last24hAlerts hook * Fix last 24h alerts count hook * Fix large font * Fix font size * Fix size Actions * Fix fontsize page header * Fix conditions size * Fix text move vertically on small screen * Update style * Update alerts counts style * Cleanup * Add formatter for the interval * Add edit button on the definition section * Add delete modal * Add loader * Fix conditions panctuation * Fix size * Use the healthColor function from rule component * Add loading while deleting a rule * Use connectors name to show actions * Fix type * Fix rule page * Fix types * Use common RULES_PAGE_LINK var * Fix checks * Better error handling * Better i18n * Code review * Fix checks i18n * Use abort signal * Revert signal for loadRule as there is no tests * Fix style * Fixing tests * Reduce bundle size * Fix i18n * Bump limits
- Loading branch information
Showing
18 changed files
with
1,110 additions
and
4 deletions.
There are no files selected for viewing
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
159 changes: 159 additions & 0 deletions
159
x-pack/plugins/observability/public/hooks/use_fetch_last24h_alerts.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,159 @@ | ||
/* | ||
* 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. | ||
*/ | ||
/* | ||
* 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, useRef } from 'react'; | ||
import { AsApiContract } from '@kbn/actions-plugin/common'; | ||
import { HttpSetup } from '@kbn/core/public'; | ||
import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common/constants'; | ||
import { RULE_LOAD_ERROR } from '../pages/rule_details/translations'; | ||
|
||
interface UseFetchLast24hAlertsProps { | ||
http: HttpSetup; | ||
features: string; | ||
ruleId: string; | ||
} | ||
interface FetchLast24hAlerts { | ||
isLoadingLast24hAlerts: boolean; | ||
last24hAlerts: number; | ||
errorLast24hAlerts: string | undefined; | ||
} | ||
|
||
export function useFetchLast24hAlerts({ http, features, ruleId }: UseFetchLast24hAlertsProps) { | ||
const [last24hAlerts, setLast24hAlerts] = useState<FetchLast24hAlerts>({ | ||
isLoadingLast24hAlerts: true, | ||
last24hAlerts: 0, | ||
errorLast24hAlerts: undefined, | ||
}); | ||
const isCancelledRef = useRef(false); | ||
const abortCtrlRef = useRef(new AbortController()); | ||
const fetchLast24hAlerts = useCallback(async () => { | ||
isCancelledRef.current = false; | ||
abortCtrlRef.current.abort(); | ||
abortCtrlRef.current = new AbortController(); | ||
try { | ||
if (!features) return; | ||
const { index } = await fetchIndexNameAPI({ | ||
http, | ||
features, | ||
}); | ||
const { error, alertsCount } = await fetchLast24hAlertsAPI({ | ||
http, | ||
index, | ||
ruleId, | ||
signal: abortCtrlRef.current.signal, | ||
}); | ||
if (error) throw error; | ||
if (!isCancelledRef.current) { | ||
setLast24hAlerts((oldState: FetchLast24hAlerts) => ({ | ||
...oldState, | ||
last24hAlerts: alertsCount, | ||
isLoading: false, | ||
})); | ||
} | ||
} catch (error) { | ||
if (!isCancelledRef.current) { | ||
if (error.name !== 'AbortError') { | ||
setLast24hAlerts((oldState: FetchLast24hAlerts) => ({ | ||
...oldState, | ||
isLoading: false, | ||
errorLast24hAlerts: RULE_LOAD_ERROR( | ||
error instanceof Error ? error.message : typeof error === 'string' ? error : '' | ||
), | ||
})); | ||
} | ||
} | ||
} | ||
}, [http, features, ruleId]); | ||
useEffect(() => { | ||
fetchLast24hAlerts(); | ||
}, [fetchLast24hAlerts]); | ||
|
||
return last24hAlerts; | ||
} | ||
|
||
interface IndexName { | ||
index: string; | ||
} | ||
|
||
export async function fetchIndexNameAPI({ | ||
http, | ||
features, | ||
}: { | ||
http: HttpSetup; | ||
features: string; | ||
}): Promise<IndexName> { | ||
const res = await http.get<{ index_name: string[] }>(`${BASE_RAC_ALERTS_API_PATH}/index`, { | ||
query: { features }, | ||
}); | ||
return { | ||
index: res.index_name[0], | ||
}; | ||
} | ||
export async function fetchLast24hAlertsAPI({ | ||
http, | ||
index, | ||
ruleId, | ||
signal, | ||
}: { | ||
http: HttpSetup; | ||
index: string; | ||
ruleId: string; | ||
signal: AbortSignal; | ||
}): Promise<{ | ||
error: string | null; | ||
alertsCount: number; | ||
}> { | ||
try { | ||
const res = await http.post<AsApiContract<any>>(`${BASE_RAC_ALERTS_API_PATH}/find`, { | ||
signal, | ||
body: JSON.stringify({ | ||
index, | ||
query: { | ||
bool: { | ||
must: [ | ||
{ | ||
term: { | ||
'kibana.alert.rule.uuid': ruleId, | ||
}, | ||
}, | ||
{ | ||
range: { | ||
'@timestamp': { | ||
gte: 'now-24h', | ||
lt: 'now', | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
aggs: { | ||
alerts_count: { | ||
cardinality: { | ||
field: 'kibana.alert.uuid', | ||
}, | ||
}, | ||
}, | ||
}), | ||
}); | ||
return { | ||
error: null, | ||
alertsCount: res.aggregations.alerts_count.value, | ||
}; | ||
} catch (error) { | ||
return { | ||
error, | ||
alertsCount: 0, | ||
}; | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
x-pack/plugins/observability/public/hooks/use_fetch_rule.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,46 @@ | ||
/* | ||
* 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 { loadRule } from '@kbn/triggers-actions-ui-plugin/public'; | ||
import { FetchRuleProps, FetchRule } from '../pages/rule_details/types'; | ||
import { RULE_LOAD_ERROR } from '../pages/rule_details/translations'; | ||
|
||
export function useFetchRule({ ruleId, http }: FetchRuleProps) { | ||
const [ruleSummary, setRuleSummary] = useState<FetchRule>({ | ||
isRuleLoading: true, | ||
rule: undefined, | ||
errorRule: undefined, | ||
}); | ||
const fetchRuleSummary = useCallback(async () => { | ||
try { | ||
const rule = await loadRule({ | ||
http, | ||
ruleId, | ||
}); | ||
|
||
setRuleSummary((oldState: FetchRule) => ({ | ||
...oldState, | ||
isRuleLoading: false, | ||
rule, | ||
})); | ||
} catch (error) { | ||
setRuleSummary((oldState: FetchRule) => ({ | ||
...oldState, | ||
isRuleLoading: false, | ||
errorRule: RULE_LOAD_ERROR( | ||
error instanceof Error ? error.message : typeof error === 'string' ? error : '' | ||
), | ||
})); | ||
} | ||
}, [ruleId, http]); | ||
useEffect(() => { | ||
fetchRuleSummary(); | ||
}, [fetchRuleSummary]); | ||
|
||
return { ...ruleSummary, reloadRule: fetchRuleSummary }; | ||
} |
51 changes: 51 additions & 0 deletions
51
x-pack/plugins/observability/public/hooks/use_fetch_rule_actions.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,51 @@ | ||
/* | ||
* 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 { ActionConnector, loadAllActions } from '@kbn/triggers-actions-ui-plugin/public'; | ||
import { FetchRuleActionsProps } from '../pages/rule_details/types'; | ||
import { ACTIONS_LOAD_ERROR } from '../pages/rule_details/translations'; | ||
|
||
interface FetchActions { | ||
isLoadingActions: boolean; | ||
allActions: Array<ActionConnector<Record<string, unknown>>>; | ||
errorActions: string | undefined; | ||
} | ||
|
||
export function useFetchRuleActions({ http }: FetchRuleActionsProps) { | ||
const [ruleActions, setRuleActions] = useState<FetchActions>({ | ||
isLoadingActions: true, | ||
allActions: [] as Array<ActionConnector<Record<string, unknown>>>, | ||
errorActions: undefined, | ||
}); | ||
|
||
const fetchRuleActions = useCallback(async () => { | ||
try { | ||
const response = await loadAllActions({ | ||
http, | ||
}); | ||
setRuleActions((oldState: FetchActions) => ({ | ||
...oldState, | ||
isLoadingActions: false, | ||
allActions: response, | ||
})); | ||
} catch (error) { | ||
setRuleActions((oldState: FetchActions) => ({ | ||
...oldState, | ||
isLoadingActions: false, | ||
errorActions: ACTIONS_LOAD_ERROR( | ||
error instanceof Error ? error.message : typeof error === 'string' ? error : '' | ||
), | ||
})); | ||
} | ||
}, [http]); | ||
useEffect(() => { | ||
fetchRuleActions(); | ||
}, [fetchRuleActions]); | ||
|
||
return { ...ruleActions, reloadRuleActions: fetchRuleActions }; | ||
} |
48 changes: 48 additions & 0 deletions
48
x-pack/plugins/observability/public/hooks/use_fetch_rule_summary.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,48 @@ | ||
/* | ||
* 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 { loadRuleSummary } from '@kbn/triggers-actions-ui-plugin/public'; | ||
import { FetchRuleSummaryProps, FetchRuleSummary } from '../pages/rule_details/types'; | ||
import { RULE_LOAD_ERROR } from '../pages/rule_details/translations'; | ||
|
||
export function useFetchRuleSummary({ ruleId, http }: FetchRuleSummaryProps) { | ||
const [ruleSummary, setRuleSummary] = useState<FetchRuleSummary>({ | ||
isLoadingRuleSummary: true, | ||
ruleSummary: undefined, | ||
errorRuleSummary: undefined, | ||
}); | ||
|
||
const fetchRuleSummary = useCallback(async () => { | ||
setRuleSummary((oldState: FetchRuleSummary) => ({ ...oldState, isLoading: true })); | ||
|
||
try { | ||
const response = await loadRuleSummary({ | ||
http, | ||
ruleId, | ||
}); | ||
setRuleSummary((oldState: FetchRuleSummary) => ({ | ||
...oldState, | ||
isLoading: false, | ||
ruleSummary: response, | ||
})); | ||
} catch (error) { | ||
setRuleSummary((oldState: FetchRuleSummary) => ({ | ||
...oldState, | ||
isLoading: false, | ||
errorRuleSummary: RULE_LOAD_ERROR( | ||
error instanceof Error ? error.message : typeof error === 'string' ? error : '' | ||
), | ||
})); | ||
} | ||
}, [ruleId, http]); | ||
useEffect(() => { | ||
fetchRuleSummary(); | ||
}, [fetchRuleSummary]); | ||
|
||
return { ...ruleSummary, reloadRuleSummary: fetchRuleSummary }; | ||
} |
61 changes: 61 additions & 0 deletions
61
x-pack/plugins/observability/public/pages/rule_details/components/actions.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,61 @@ | ||
/* | ||
* 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 { | ||
EuiText, | ||
EuiSpacer, | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiIcon, | ||
IconType, | ||
EuiLoadingSpinner, | ||
} from '@elastic/eui'; | ||
import { intersectionBy } from 'lodash'; | ||
import { ActionsProps } from '../types'; | ||
import { useFetchRuleActions } from '../../../hooks/use_fetch_rule_actions'; | ||
import { useKibana } from '../../../utils/kibana_react'; | ||
|
||
interface MapActionTypeIcon { | ||
[key: string]: string | IconType; | ||
} | ||
const mapActionTypeIcon: MapActionTypeIcon = { | ||
/* TODO: Add the rest of the application logs (SVGs ones) */ | ||
'.server-log': 'logsApp', | ||
'.email': 'email', | ||
'.pagerduty': 'apps', | ||
'.index': 'indexOpen', | ||
'.slack': 'logoSlack', | ||
'.webhook': 'logoWebhook', | ||
}; | ||
export function Actions({ ruleActions }: ActionsProps) { | ||
const { | ||
http, | ||
notifications: { toasts }, | ||
} = useKibana().services; | ||
const { isLoadingActions, allActions, errorActions } = useFetchRuleActions({ http }); | ||
if (ruleActions && ruleActions.length <= 0) return <EuiText size="s">0</EuiText>; | ||
const actions = intersectionBy(allActions, ruleActions, 'actionTypeId'); | ||
if (isLoadingActions) return <EuiLoadingSpinner size="s" />; | ||
return ( | ||
<EuiFlexGroup direction="column"> | ||
{actions.map((action) => ( | ||
<> | ||
<EuiFlexGroup alignItems="baseline"> | ||
<EuiFlexItem grow={false}> | ||
<EuiIcon size="m" type={mapActionTypeIcon[action.actionTypeId] ?? 'apps'} /> | ||
</EuiFlexItem> | ||
<EuiFlexItem style={{ margin: '0px' }}> | ||
<EuiText size="s">{action.name}</EuiText> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
<EuiSpacer size="s" /> | ||
</> | ||
))} | ||
{errorActions && toasts.addDanger({ title: errorActions })} | ||
</EuiFlexGroup> | ||
); | ||
} |
11 changes: 11 additions & 0 deletions
11
x-pack/plugins/observability/public/pages/rule_details/components/index.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,11 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
export { PageTitle } from './page_title'; | ||
export { ItemTitleRuleSummary } from './item_title_rule_summary'; | ||
export { ItemValueRuleSummary } from './item_value_rule_summary'; | ||
export { Actions } from './actions'; |
Oops, something went wrong.